[Math] Determine shift between scaled rotated object and additional scale step

analytic geometrygeometrymatricestransformationtrigonometry

I am trying to find the amount to move an object so that, when it rotates and resizes, the resize would be smooth.

I have a Qt program, where I have to rotate objects around center, and resize based on top left anchor.
Unfortunately, built-in transformations combine anchors, so I can either do both transformations around center, or top left, or any unique point.

The item coordinates are based on top-left corner. To perform operations around center, I translate item by cx, cy, perform rotation, translate by -cx, -cy.

The transform used by the program is calculated by multiplying scale, then rotation then translation, is I think:

$$ t=
\begin{bmatrix}
sx & 0 & 0 \\
0 & sy & 0 \\
0 & 0 & 1 \\
\end{bmatrix}
.
\begin{bmatrix}
cos(\theta) & sin(\theta) & 0 \\
-sin(\theta) & cos(\theta) & 0 \\
0 & 0 & 1 \\
\end{bmatrix}
.
\begin{bmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
tx & ty & 1 \\
\end{bmatrix}
$$

(Based on documentation … $$ t= \begin{bmatrix}
scaleX& shearX & projX \\
shearY & scaleY & projY \\
transX & transY & 1 \\
\end{bmatrix}
$$
)

To rotate around center, I translate item to its (scaled) center, rotate, translate back, then resize. The result is correct, but the item appears to move.

After much struggle, I came to the solution that I have to adjust the item position on resize (or include the shift in the transform, but seems a position change is better since it allows the user to revert it).
I just can't figure out by how much.

I have tried to determine the item top-left coordinates before the resize, apply the transformation (which involves replacing current item transform with the transform that has the new parameters), determine the item top-left coordinates again, and shift by the change. The result was… horrific. Item still moves on resize after rotate, but the rotation anchor changed to bottom-right… I don't understand why. (I have tried many other ways to offset the move, none worked).

Assume a rectangular object that has been rotated (perhaps also initially resized), around its center. (in image, the green text). If I resize it, I would like to keep the top left point fixed (like the blue text).
Because of the fact that the transformations are compounded, creating a transformation containing resize and rotation would first resize (yellow text) and then rotate around the new center – resulting in the red text.
Visually, there is a jump on resizing a rotated text…

enter image description here
Legend: green = initial, blue = desired, red = actual, yellow = intermediate step to get red

Note – I cannot apply rotation ater resize, because the way transforms work, I would end up with a skewed object if my scaling factors on x and y are different:

enter image description here

So, my only option is to determine the shift between the previously rotated (and perhaps stretched) item top left, and the resulting stretched item top left points.

How can I determine the dx and dy difference between the two points ? (in first image, top left of green text and top left of red text)

I have seen questions that seem similar, but they attempt transformation around a unique anchor and have identical x and y scale factors… It is the anchor change that gives me headaches.

Edit – I found a temporary solution – I am storing the "old" t31 and t32, and adjusting position by the difference between the "old" and "new" values.
I would prefer calculated values, since knowledge of the "old" item introduces more complications…

Best Answer

If you begin with a horizontal object whose initial dimensions are $L_x$ and $L_y$ (before scaling), then the offset between blue and red text should be $$ \begin{align} &\Delta x={K_x-1\over2}L_x(1-\cos\alpha)+{K_y-1\over2}L_y\sin\alpha,\cr &\Delta y=-{K_y-1\over2}L_y(1-\cos\alpha)+{K_x-1\over2}L_x\sin\alpha,\cr \end{align} $$ where $\alpha$ is the rotation angle with respect to the horizontal and $K_x$, $K_y$ are the scaling factors.

The sign of $\Delta x$ and $\Delta y$ depends on how you measure $\alpha$, so you may need to find the correct sign by trial and error. Hope that helps.

EDIT

To show how those formulas can be derived, consider the black rectangle in the diagram below. Scaling it (with respect to up-left vertex $P$) and then rotating around the center $O'$ of the scaled rectangle yields a final brown rectangle, whose up-left vertex is $P'$.

If the black rectangle is first rotated around its center $O$ and then scaled with respect to $P''$, then we get instead the blue rectangle below. We are interested in finding the "offset" vector $\vec{P'P''}$.

If we denote by $R$ the rotation operator ($R(x,y)=(\cos\alpha x-\sin\alpha y, \cos\alpha y+\sin\alpha x)$, if positive angles represent a counterclockwise rotation) and by $S$ the scaling operator ($S(x,y)=(K_x x,K_y y)$), then we have:

$$ \vec{P'P''}=\vec{P'O'}+\vec{O'O}+\vec{OP''}= R\vec{PO'}+(S-1)\vec{OP}+R\vec{OP}= -R(S\vec{OP})+(S-1)\vec{OP}+R\vec{OP}, $$ that is: $$ \vec{P'P''}= (S-1)\vec{OP}-R(S-1)\vec{OP}. $$ Inserting here $\vec{OP}=(-L_x/2,L_y/2)$ one gets the desired result.

In comparing with my old formulas above, I see that $\vec{P'P''}=(-\Delta x,-\Delta y)$. In addition, I used there the opposite convention for the sign of the angle, but I hope this explanation is clear enough.

enter image description here

Related Question