Skip to content

Commit

Permalink
move images
Browse files Browse the repository at this point in the history
  • Loading branch information
rakita committed Jul 9, 2023
1 parent 1c3bf13 commit 72a13a3
Show file tree
Hide file tree
Showing 24 changed files with 22 additions and 21 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Access blog at https://rakita.github.io/blog/
6 changes: 3 additions & 3 deletions content/blog/2d_transformations.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ Rotation is little bit more complex (it has little bit more to do) but in same r

And we come to scaling, it is best part of this post (it has pictures) :D. We will gradually introducing few things that needs to be done in scaling, we will see how we handle rotation, and shift controls (shift is usually used for aspect ration lock).

![Naive Scale](/2d_transformations/naive_scale.png)
![Naive Scale](./naive_scale.png)

Nice, lets start with basic example where our element is not rotated or translated and we just want to scale it. We will use `ref_point` (corner or side usually), and its `anchor_point` and of course we will need `current_point` to tell us where we want to scale to. We calculate `diff=current_point-anchor_point`, get scale as `s=scale(diff/element_size)` and we are done, we have scale matrix that can add to our transformation.

Okay, lets now look on example where we want to take top left corner `ref_point` ( you can follow picture below), in that case our `anchor_point` is positioned at bottom and if we want to scale it properly, to top and left. First difference from previous example is that we will need to move our object so that `anchor_point` is in `(0.0)` coordinate! We still need `diff` and we are calculating it same as before, but because now our axis are flipped, this is second difference, we need to reverse sign of `diff_new=Vector(-diff.x,-diff.y)`. Note, reversing `y` is needed for top side `ref_point` and reversing `x` for left side `ref_point`. We get scale as `s=scale(diff_new/element_size)` . And final third difference from previous example is that after all this we need to take translation of anchor `T=translate(anchor_point)`, calculate inverse `Tinv=inverse(T)` and bind it all together (from left to right) `S=T*s*Tin`.

![Scale](/2d_transformations/scale.png)
![Scale](./scale.png)

As you can see diff vector is oriented to negative in reference to our axis, this is reason why we need to flip it, if we didn't do flipping you would get small scale when moving away from top left corner.

Expand All @@ -62,7 +62,7 @@ That's great, but how to append scale in current matrix, when scale is something

Shift scale is scaling where aspect ration is not changed. This means that scale on both axis is equal and we need to choose which axis orientation we will take as primary. We could make it simple and depending on which corner_id is selected that take modulo of two and chose x or y scale, this will work but will be unintuitive. For better solution where depending on position of mouse relative to diagonal of element we will get smother transition between x and y orientation. See picture below:

![Naive Scale](/2d_transformations/shyft_scale.png)
![Naive Scale](./shyft_scale.png)

With transparent colors we can see zones where we want to take only `x` ( blue color) or take only `y` (marked with red). As noticeable our object is in original position that means our `original_points` is calculated same as in example with rotated object. Slope of diagonals that make these zones are calculated from `original_size` with equation `line_slope = original_size.y/original_size.x` . for second diagonal it is enough to just flip sign and we will get second slope. what we want to check is if point is in blue or red space and we can do that following if statement: (for abbreviate: `op` is `original_point` , `ls` is `line_slope` ): `(op.y < ls**op.x && op.y > -ls**op.x) || (op.y > op.x**ls && op.y < -ls**op.x)`, and if this if statement is true do `scale.y=scale.x` if it is false do opposite. And lastly don't forget that when you are overriding one scale to not override its sign, in example from picture we are taking `y` scale and overriding `x` scale but we need to preserve `x` sign to properly scale our element `x=sign(x)*abs(y)`.

Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
6 changes: 3 additions & 3 deletions content/blog/parallel_evm_claim.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Running transactions in parallel is more implementation detail and will depend o

The second example is having a third transaction that depends on the first one.

![](/parallel_evm_claim/example_chain.png)
![](./example_chain.png)


[Graph](https://mermaid.live/edit#pako:eNpdjjEOwjAMRa8SeUTNQMuUgYmViZEwWI0LkZoEpU4Fqnp3DC1CwtPX-7b1JmiTIzAwMDIdPF4zBj3WNiqZ8-aitN4rfmwXIGEFzRc0K9j9n9RQQaAc0Dv5P71rC3yjQBaMREcdlp4t2DjLKhZOp2dswXAuVEG5u58RmA77QSg5zykfF-eP-vwC_v88KA)
Expand All @@ -61,7 +61,7 @@ This example show's us that marking of state can be done by chain ids that this

Modelling dependency can be tricky but in parallel execution, there are only two synchronizations that can happen. And those are forks and joins and both of them can be seen in the picture.

![](/parallel_evm_claim/example_fork_join.png)
![](./example_fork_join.png)


[Graph](https://mermaid.live/edit#pako:eNpdj7EOwjAMRH-l8oiagRSWDEysTIwNg9W4EKlJUOogUNV_J9BWFXg6vTtZdwM0wRAo6BmZjhavEZ14SO2LfPXmUghxKPi5nUAWM6gWUM1g95_YL0D-gvWphBIcRYfW5AbDx9bAN3KkQWVpqMXUsQbtxxzFxOH88g0ojolKSHezdgbVYtdnSsZyiKdp1Xfc-AaEXkTp)
Expand All @@ -77,7 +77,7 @@ This is a good example that tests our initial mechanism of marking of accessed s

[Graph](https://mermaid.live/edit#pako:eNpd0D0PgjAQBuC_Qm40MMiHJB2cXJ0crcOFHkpCKSlXoyH8d6uUmPSmy3PvcHczNEYRCJgYmU4d3i3q7JnLIfF13d2SLDsm_Nqv4JsAxQZFgDJOVBvkMZQBDhtUAeo4Ucd75JCCJquxU37p-TuWwA_SJEH4VlGLrmcJclh8FB2by3toQLB1lIIb1f9MEC32k1dSHRt7Xh_x-8fyAQIhUhg)

![](/parallel_evm_claim/example_diamont.png)
![](./example_diamont.png)


All previous statements should be valid here.
Expand Down
File renamed without changes.
File renamed without changes
12 changes: 6 additions & 6 deletions public/atom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@
&lt;p&gt;Running transactions in parallel is more implementation detail and will depend on the programming language.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-2-chains-transactions-dependencies&quot;&gt;Example 2: Chains, transactions dependencies.&lt;&#x2F;h3&gt;
&lt;p&gt;The second example is having a third transaction that depends on the first one.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;parallel_evm_claim&#x2F;example_chain.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;parallel-evm-claim&#x2F;.&#x2F;example_chain.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mermaid.live&#x2F;edit#pako:eNpdjjEOwjAMRa8SeUTNQMuUgYmViZEwWI0LkZoEpU4Fqnp3DC1CwtPX-7b1JmiTIzAwMDIdPF4zBj3WNiqZ8-aitN4rfmwXIGEFzRc0K9j9n9RQQaAc0Dv5P71rC3yjQBaMREcdlp4t2DjLKhZOp2dswXAuVEG5u58RmA77QSg5zykfF-eP-vwC_v88KA&quot;&gt;Graph&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is the first example of dependent transactions and&lt;code&gt;tx3&lt;&#x2F;code&gt; can access only accounts that are in the original state or touched by &lt;code&gt;tx1&lt;&#x2F;code&gt;, if both &lt;code&gt;tx3&lt;&#x2F;code&gt; and &lt;code&gt;tx2&lt;&#x2F;code&gt; access the same account this would make the parallelism claim invalid.&lt;&#x2F;p&gt;
&lt;p&gt;This example show&#x27;s us that marking of state can be done by chain ids that this tx belongs to and we would get the same outcome. Without this &lt;code&gt;tx4&lt;&#x2F;code&gt; would need to check if the account state is original or marked by &lt;code&gt;tx1&lt;&#x2F;code&gt; or marked by &lt;code&gt;tx3&lt;&#x2F;code&gt; and that wouldn&#x27;t be efficient. I will use the terms chain and transaction interchangeably.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-3-chain-forks-and-joins&quot;&gt;Example 3: Chain forks and joins&lt;&#x2F;h2&gt;
&lt;p&gt;Modelling dependency can be tricky but in parallel execution, there are only two synchronizations that can happen. And those are forks and joins and both of them can be seen in the picture.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;parallel_evm_claim&#x2F;example_fork_join.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;parallel-evm-claim&#x2F;.&#x2F;example_fork_join.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mermaid.live&#x2F;edit#pako:eNpdj7EOwjAMRH-l8oiagRSWDEysTIwNg9W4EKlJUOogUNV_J9BWFXg6vTtZdwM0wRAo6BmZjhavEZ14SO2LfPXmUghxKPi5nUAWM6gWUM1g95_YL0D-gvWphBIcRYfW5AbDx9bAN3KkQWVpqMXUsQbtxxzFxOH88g0ojolKSHezdgbVYtdnSsZyiKdp1Xfc-AaEXkTp&quot;&gt;Graph&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is one fork here, and can be seen in the example of &lt;code&gt;tx1&lt;&#x2F;code&gt; that forks its state to chains of &lt;code&gt;tx5&lt;&#x2F;code&gt; and &lt;code&gt;tx3&lt;&#x2F;code&gt;. This means that there is a dependency between &lt;code&gt;tx5&lt;&#x2F;code&gt; and &lt;code&gt;tx1&lt;&#x2F;code&gt;, &lt;code&gt;tx3&lt;&#x2F;code&gt; and &lt;code&gt;tx1&lt;&#x2F;code&gt; but there are no dependencies on &lt;code&gt;tx3&lt;&#x2F;code&gt; and &lt;code&gt;tx5&lt;&#x2F;code&gt; and they can be run in parallel.&lt;&#x2F;p&gt;
&lt;p&gt;The mechanism of marking the state works the same as in the first example. &lt;code&gt;tx5&lt;&#x2F;code&gt; can now access the account of the original or &lt;code&gt;tx1&lt;&#x2F;code&gt; or &lt;code&gt;tx2&lt;&#x2F;code&gt; accounts if it accessed the state of &lt;code&gt;tx3&lt;&#x2F;code&gt; or &lt;code&gt;tx3&lt;&#x2F;code&gt; this would make parallel claim invalid.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-4-diamond-pattern&quot;&gt;Example 4: Diamond pattern&lt;&#x2F;h2&gt;
&lt;p&gt;This is a good example that tests our initial mechanism of marking of accessed state.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mermaid.live&#x2F;edit#pako:eNpd0D0PgjAQBuC_Qm40MMiHJB2cXJ0crcOFHkpCKSlXoyH8d6uUmPSmy3PvcHczNEYRCJgYmU4d3i3q7JnLIfF13d2SLDsm_Nqv4JsAxQZFgDJOVBvkMZQBDhtUAeo4Ucd75JCCJquxU37p-TuWwA_SJEH4VlGLrmcJclh8FB2by3toQLB1lIIb1f9MEC32k1dSHRt7Xh_x-8fyAQIhUhg&quot;&gt;Graph&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;parallel_evm_claim&#x2F;example_diamont.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;parallel-evm-claim&#x2F;.&#x2F;example_diamont.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;All previous statements should be valid here.&lt;&#x2F;p&gt;
&lt;p&gt;For example &lt;code&gt;tx7&lt;&#x2F;code&gt; can only touch original state or &lt;code&gt;tx1&lt;&#x2F;code&gt;,&lt;code&gt;tx2&lt;&#x2F;code&gt;,&lt;code&gt;tx3&lt;&#x2F;code&gt;,&lt;code&gt;tx4&lt;&#x2F;code&gt;,&lt;code&gt;tx5&lt;&#x2F;code&gt; but not &lt;code&gt;tx6&lt;&#x2F;code&gt;, and same with &lt;code&gt;tx6&lt;&#x2F;code&gt; it can&#x27;t touch state of &lt;code&gt;tx7&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-check-marks&quot;&gt;How to check marks&lt;&#x2F;h2&gt;
Expand Down Expand Up @@ -119,16 +119,16 @@
&lt;p&gt;Rotation is little bit more complex (it has little bit more to do) but in same rank as translation. We need reference point &lt;code&gt;ref_point&lt;&#x2F;code&gt; for selected element. Rotation is usually, not to say always, done around element center, for this we need &lt;code&gt;center_point&lt;&#x2F;code&gt;. And lastly we have &lt;code&gt;current_point&lt;&#x2F;code&gt;. As you can guest we need to find the angle between these two vectors &lt;code&gt;x=ref_point-center_point&lt;&#x2F;code&gt; and &lt;code&gt;y=current_point-center_point&lt;&#x2F;code&gt;. After consulting internet we get this equation:&lt;code&gt;angle = atan2(norm(cross(x,y)), dot(x,y))&lt;&#x2F;code&gt;. With angle found we can call function for creating matrix, something like &lt;code&gt;R=rotation(angle)&lt;&#x2F;code&gt;. Appending &lt;code&gt;R&lt;&#x2F;code&gt; to transformation matrix &lt;code&gt;M&lt;&#x2F;code&gt; is done with this simple but very used and important trick: We create another matrix of translation from elements center &lt;code&gt;T=translation(center_point)&lt;&#x2F;code&gt;, and it&#x27;s inverse&lt;code&gt;Tinv = inverse(T)&lt;&#x2F;code&gt;. We get matrix that we can use to append transformation to already present points &lt;code&gt;Ra = Tinv*R*T&lt;&#x2F;code&gt; and final transformation is &lt;code&gt;M=M*Ra&lt;&#x2F;code&gt;. Basically (with &lt;code&gt;Tinv&lt;&#x2F;code&gt; we just nullify translation, we then rotate our element around center and apply T to put it back into old position).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scale&quot;&gt;Scale&lt;&#x2F;h2&gt;
&lt;p&gt;And we come to scaling, it is best part of this post (it has pictures) :D. We will gradually introducing few things that needs to be done in scaling, we will see how we handle rotation, and shift controls (shift is usually used for aspect ration lock).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;2d_transformations&#x2F;naive_scale.png&quot; alt=&quot;Naive Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;2d-transformations&#x2F;.&#x2F;naive_scale.png&quot; alt=&quot;Naive Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Nice, lets start with basic example where our element is not rotated or translated and we just want to scale it. We will use &lt;code&gt;ref_point&lt;&#x2F;code&gt; (corner or side usually), and its &lt;code&gt;anchor_point&lt;&#x2F;code&gt; and of course we will need &lt;code&gt;current_point&lt;&#x2F;code&gt; to tell us where we want to scale to. We calculate &lt;code&gt;diff=current_point-anchor_point&lt;&#x2F;code&gt;, get scale as &lt;code&gt;s=scale(diff&#x2F;element_size)&lt;&#x2F;code&gt; and we are done, we have scale matrix that can add to our transformation.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, lets now look on example where we want to take top left corner &lt;code&gt;ref_point&lt;&#x2F;code&gt; ( you can follow picture below), in that case our &lt;code&gt;anchor_point&lt;&#x2F;code&gt; is positioned at bottom and if we want to scale it properly, to top and left. First difference from previous example is that we will need to move our object so that &lt;code&gt;anchor_point&lt;&#x2F;code&gt; is in &lt;code&gt;(0.0)&lt;&#x2F;code&gt; coordinate! We still need &lt;code&gt;diff&lt;&#x2F;code&gt; and we are calculating it same as before, but because now our axis are flipped, this is second difference, we need to reverse sign of &lt;code&gt;diff_new=Vector(-diff.x,-diff.y)&lt;&#x2F;code&gt;. Note, reversing &lt;code&gt;y&lt;&#x2F;code&gt; is needed for top side &lt;code&gt;ref_point&lt;&#x2F;code&gt; and reversing &lt;code&gt;x&lt;&#x2F;code&gt; for left side &lt;code&gt;ref_point&lt;&#x2F;code&gt;. We get scale as &lt;code&gt;s=scale(diff_new&#x2F;element_size)&lt;&#x2F;code&gt; . And final third difference from previous example is that after all this we need to take translation of anchor &lt;code&gt;T=translate(anchor_point)&lt;&#x2F;code&gt;, calculate inverse &lt;code&gt;Tinv=inverse(T)&lt;&#x2F;code&gt; and bind it all together (from left to right) &lt;code&gt;S=T*s*Tin&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;2d_transformations&#x2F;scale.png&quot; alt=&quot;Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;2d-transformations&#x2F;.&#x2F;scale.png&quot; alt=&quot;Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can see diff vector is oriented to negative in reference to our axis, this is reason why we need to flip it, if we didn&#x27;t do flipping you would get small scale when moving away from top left corner.&lt;&#x2F;p&gt;
&lt;p&gt;This will all work just fine if element is not in any way rotated (Yey rotation!), with rotation we are now in bind how to calculate our diff and extract scale information. But, don&#x27;t despair, we can use same trick as we did with rotation in a way that we will take &lt;code&gt;current_position&lt;&#x2F;code&gt; and inverse of current transformation matrix &lt;code&gt;Minv = inverse(M)&lt;&#x2F;code&gt; and get &lt;code&gt;relative_position=Minv*current_position&lt;&#x2F;code&gt;. Relative position now presents point relative to our &lt;strong&gt;original&lt;&#x2F;strong&gt; element. We get corner of original element as: &lt;code&gt;original_anchor_point=original_corners[handler_id]&lt;&#x2F;code&gt; (take care to select correct corner, it is probably jumbled up with rotation, I had something like &lt;code&gt;handler_id&lt;&#x2F;code&gt; to help me with that) and do same as we did in our last example, calculate diff as &lt;code&gt;diff=relative_position-original_corners[handler_id]&lt;&#x2F;code&gt;, and if needed invert its axis. Calculate scale as &lt;code&gt;s=scale(diff_new&#x2F;element_original_size)&lt;&#x2F;code&gt; and now similarly as previous scale example we need to move our original element to anchor before we do scaling, bear in mind that that translation represent anchor when our element is &lt;strong&gt;not&lt;&#x2F;strong&gt; transformation. We get &lt;code&gt;T=translate(original_anchor_point)&lt;&#x2F;code&gt; and its inverse &lt;code&gt;Tinv&lt;&#x2F;code&gt; and we get &lt;code&gt;S=T*s*Tinv&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s great, but how to append scale in current matrix, when scale is something that is done before rotation and translation? We could always prepend scale to &lt;code&gt;M&lt;&#x2F;code&gt;, and this is only way to properly add scale, mostly because we are using &lt;code&gt;S*R*T&lt;&#x2F;code&gt; order. But how to get matrix to apply directly on already transformed points? Hah, just take &lt;code&gt;Minv = inverse(M)&lt;&#x2F;code&gt; and get transformation that we can append on present points as &lt;code&gt;Sa=Minv*S*M&lt;&#x2F;code&gt;, and final matrix is &lt;code&gt;M=M*Sa&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shift-scale&quot;&gt;Shift scale&lt;&#x2F;h2&gt;
&lt;p&gt;Shift scale is scaling where aspect ration is not changed. This means that scale on both axis is equal and we need to choose which axis orientation we will take as primary. We could make it simple and depending on which corner_id is selected that take modulo of two and chose x or y scale, this will work but will be unintuitive. For better solution where depending on position of mouse relative to diagonal of element we will get smother transition between x and y orientation. See picture below:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;2d_transformations&#x2F;shyft_scale.png&quot; alt=&quot;Naive Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;rakita.github.io&#x2F;blog&#x2F;blog&#x2F;2d-transformations&#x2F;.&#x2F;shyft_scale.png&quot; alt=&quot;Naive Scale&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With transparent colors we can see zones where we want to take only &lt;code&gt;x&lt;&#x2F;code&gt; ( blue color) or take only &lt;code&gt;y&lt;&#x2F;code&gt; (marked with red). As noticeable our object is in original position that means our &lt;code&gt;original_points&lt;&#x2F;code&gt; is calculated same as in example with rotated object. Slope of diagonals that make these zones are calculated from &lt;code&gt;original_size&lt;&#x2F;code&gt; with equation &lt;code&gt;line_slope = original_size.y&#x2F;original_size.x&lt;&#x2F;code&gt; . for second diagonal it is enough to just flip sign and we will get second slope. what we want to check is if point is in blue or red space and we can do that following if statement: (for abbreviate: &lt;code&gt;op&lt;&#x2F;code&gt; is &lt;code&gt;original_point&lt;&#x2F;code&gt; , &lt;code&gt;ls&lt;&#x2F;code&gt; is &lt;code&gt;line_slope&lt;&#x2F;code&gt; ): &lt;code&gt;(op.y &amp;lt; ls**op.x &amp;amp;&amp;amp; op.y &amp;gt; -ls**op.x) || (op.y &amp;gt; op.x**ls &amp;amp;&amp;amp; op.y &amp;lt; -ls**op.x)&lt;&#x2F;code&gt;, and if this if statement is true do &lt;code&gt;scale.y=scale.x&lt;&#x2F;code&gt; if it is false do opposite. And lastly don&#x27;t forget that when you are overriding one scale to not override its sign, in example from picture we are taking &lt;code&gt;y&lt;&#x2F;code&gt; scale and overriding &lt;code&gt;x&lt;&#x2F;code&gt; scale but we need to preserve &lt;code&gt;x&lt;&#x2F;code&gt; sign to properly scale our element &lt;code&gt;x=sign(x)*abs(y)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tldr&quot;&gt;TLDR&lt;&#x2F;h2&gt;
&lt;p&gt;Summary of functions that were called throughout the text:&lt;&#x2F;p&gt;
Expand Down
Loading

0 comments on commit 72a13a3

Please sign in to comment.