0

I'm trying to implement a general shearing transform in the Unity game engine. Since Unity transforms are limited to TRS (translate, rotate, scale) the best way I found to do this is to simply chain together 2 rotation-scale transformations to achieve a net shearing. As far as I can tell this can be done generally, but I've been struggling to work out the math.

More formally I want to find any set of $s^x_1, s^y_1, s^x_2, s^y_2, \theta_1, \theta_2$ for a given $c_x$ and $c_y$ that solves the following equation:

$$ \begin{pmatrix} 1 && c_x \\ c_y && 1 \end{pmatrix} = \begin{pmatrix} s^x_1\cdot\cos(\theta_1) && -s^y_1\cdot\sin(\theta_1) \\ s^x_1\cdot\sin(\theta_1) && s^y_1\cdot\cos(\theta_1) \end{pmatrix} \cdot \begin{pmatrix} s^x_2\cdot\cos(\theta_2) && -s^y_2\cdot\sin(\theta_2) \\ s^x_2\cdot\sin(\theta_2) && s^y_2\cdot\cos(\theta_2) \end{pmatrix} $$

My goal is here is to write code that makes this calculation automatically. A numerical solution would also be fine for my use case.

WARdd
  • 1
  • Denote the equation by $A=BC$. In principle, you may perform a singular value decomposition $A=USV^T$ and set $B=US$ and $C=V^T$. If $\det A$ is guaranteed to be positive and you want the determinants of $B$ and $C$ to be positive as well, you may take $B=USD$ and $C=DV^T$ instead, where $D=\operatorname{diag}(1,\det V)$. It is possible to express $B$ and $C$ in terms of $c_x$ and $c_y$ explicitly, but the resulting formulae will be quite complicated. – user1551 Nov 19 '24 at 14:00

1 Answers1

0

Thanks to @user1551 for setting me on the direction of singular value decomposition, I managed to find a practical solution.

The full solution in Unity now looks like this:

using UnityEngine;

public class ShearTransform : MonoBehaviour {

public Transform parent, pivot; public Vector2 shear;

private void OnValidate() { ApplyTransform(); }

private void ApplyTransform() { if (parent == null | pivot == null) return;

float lambda = 1f - shear.x * shear.y;
if (Mathf.Abs(lambda) < 1e-4f)
    return;

float cx = shear.x;
float cy = shear.y;
float cx2 = cx * cx;
float cy2 = cy * cy;

float r1 = .5f * Mathf.Rad2Deg * Mathf.Atan2(2f * cy + 2f * cx, cx2 - cy2);
float ss1 = 2f + cx2 + cy2;
float ss2 = Mathf.Sqrt((cx2 - cy2) * (cx2 - cy2) + 4f * (cx + cy) * (cx + cy));
float s1 = Mathf.Sqrt(.5f * (ss1 + ss2));
float s2 = Mathf.Sqrt(.5f * (ss1 - ss2));
float r2 = .5f * Mathf.Rad2Deg * Mathf.Atan2(2f * cy + 2f * cx, cy2 - cx2);

parent.localRotation = Quaternion.Euler(0f, 0f, r1);
parent.localScale = new Vector3(s1, s2, 1f);
pivot.localRotation = Quaternion.Euler(0f, 0f, -r2);
pivot.localScale = Vector3.one;

}

}

WARdd
  • 1