Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full 3D Helmert transformation #7

Open
axelpale opened this issue Sep 26, 2022 · 5 comments
Open

Full 3D Helmert transformation #7

axelpale opened this issue Sep 26, 2022 · 5 comments
Labels
enhancement New feature or request
Milestone

Comments

@axelpale
Copy link
Owner

axelpale commented Sep 26, 2022

In affineplane 2.x the helm3 is quite limited in 3D and more like 2.3 dimensional variant of the transformation.

The limitation brings complications: in order to make each dimension to respect the scale uniformly, we need to know the scale in almost every operation we make. For x and y, the scale is factored in the a and b properties. For z however, we would need to compute the scale explicitly with square root and squares. Computationally this is not a big deal, adding one square root, two multiplications and one addition to the necessary functions. From architectural perspective it just feels very wrong to compute the scale over and over again.

For a reference on full 3D Helmert matrices, see: https://proj.org/operations/transformations/helmert.html

See also 3D affine coordinate transformations by Constantin-Octavian 2006.

See also Helmert transformation in Wikipedia.

@axelpale
Copy link
Owner Author

axelpale commented Sep 26, 2022

Approach 1: to avoid recomputation of scale, introduce a latent property c so that helm3 and so plane3 becomes { a, b, c, x, y, z } and where c^2 = a^2 + b^2. This approach frees us from recomputing the scale in every occasion. Downsides are that this feels a dirty optimisation step for a rare special case, although necessary for Tapspace.

Approach 2: jump directly to full 3D Helmert with seven properties: { s, rx, ry, rz, x, y, z }. As in Approach 1, the scale is stored explicitly as s. The props rx, ry, and rz are the rotation angles. This parametrisation, especially if stored like this, requires huge number of trigonometric function computations which is not ideal. The rotation matrix, when opened, yields 9 numbers to store in addition to the scale and translation, implying the total of 13 properties.

Approach 3: introduce the explicit scale property and decompose the scale away from a and b. This takes us to five independent parameters { s, rz, x, y, z } which we could store conveniently with six properties { s, cz, sz, x, y, z } where cz = cos(rz) and sz = sin(rz). Alternative namings could be { s, a, b, x, y, z } or { a, b, c, x, y, z } where c is the scale and a = cos(rz) and b = sin(rz).

@axelpale
Copy link
Owner Author

axelpale commented Sep 26, 2022

Approach 4: Use quaternions to store rotations. A quaternion is more compact and still quite efficient, requiring only four properties for rotation computations without trigonometric functions. The 3D Helmert transformation could be represented as { s, a, b, c, d, x, y, z } or { s, r, rx, ry, rz, x, y, z }. Some research exists, for example Quaternion-based Helmert transformations by Milenkovic 2015.

@axelpale
Copy link
Owner Author

axelpale commented Sep 28, 2022

Approach 4.2: It seems possible to store the scale into the quaternion so that scale = sqrt(a*a+b*b+c*c+d*d). This way the object representation becomes { a, b, c, d, x, y, z } which is nicely in line with the 2D { a, b, x, y }. Actually, this 4-parameter 2D transform representation can be seen to be based on a complex number a + bi.

@axelpale
Copy link
Owner Author

Finding 1: When restricting to rotations around z-axis, we can derive that { a, b, x, y } is actually special case { a, 0, 0, b, x, y, 0 } of the full quaternion helmert. Therefore it would be more consistent if helm2 has properties { a, d, x, y } and the full helm3 has { a, b, c, d, x, y, z }. This way we would not need to change the meaning of b later when the lib might have some users.

@axelpale axelpale added the enhancement New feature or request label Oct 5, 2022
@axelpale
Copy link
Owner Author

After studying lots of quaternions, it seems common to denote scale with m for "magnitude" or "multiplier". Also, complex- and quaternion-based rotation usually requires unitary or orthogonal matrices for computation and algebra to work. In affineplane and tapspace code, it is common to compute the scale or magnitude from (a,b) with a square root, two multiplications and one addition. The scale is especially needed in inversions, that are very common operation in tapspace.

Therefore it seems practical to use 8-parameter { m, a, b, c, d, x, y, z } for full helm3 and also convert helm2 and helm23 to use m property: { m, a, b, x, y } and { m, a, b, x, y, z } where a*a + b*b = 1.

In the context of zoomable UIs and infinite scaling, it feels extremely practical to separate scale and rotation in order to extend the range of numerical stability.

These changes require new major version increment, affineplane v3.

@axelpale axelpale added this to the v3.0 milestone Oct 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant