gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > getting angle and scale from any given affine matrix

#169312 - vuurrobin - Wed Jul 01, 2009 11:58 pm

hello everybody,

is it possible to calculate the angle and scale from any given affine matrix? or is this only possible if you know for sure that the matrix is a rot/scale matrix and haven't sheared or anything.

I looked at tonc, but it only explains how to change an angle and scale to a matrix and not the other way around. I tried to do it myself, but I can't seem to wrap my mind around it, and google eather gives me links about 3d affine matrices, or a link to tonc.


can somebody help me?

greets vuurrobin.

#169313 - DensitY - Thu Jul 02, 2009 12:23 am

I'm assuming you want from matrix to Euler coords (yaw, pitch, roll), in that case the following link should be useful.

http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/

Scale not so sure about when its been multiplied in, only got a 20 minute lunch break today :p


Last edited by DensitY on Thu Jul 02, 2009 12:26 am; edited 1 time in total

#169314 - TwentySeven - Thu Jul 02, 2009 12:24 am

You can pull euler angles back out of a orthagonal matrix, but it's complicated and slow.

Most 3d work treats matrixes as a derived, write-only product of some sort of "source" angle and position information, typically eulers or a quaternion.

The question is, I guess, why do you need to do this?

#169315 - naleksiev - Thu Jul 02, 2009 12:51 am

It is not possible from any affine matrix. If you use shearing the the coordinates aren't perpendicular anymore so you will loose the rotation. If you know that you sheared only one of the coordinates you can still get the rotation.

I didn't understand if you are interested in 2D or 3D matrices I'll try to explain how to decompose 2D matrix hopefully you can do 3D yourself if you need.

First I haven't used 2D on DS so I don't know if the matrix is row or column based... I'll work with column based which looks like this
Xx Xy
Yx Yy
If DS is using row based all the matrices need to be transposed.

The matrix I'm using is 2x2 which include only rotation and scale. In your case you might have translation as well so it will be 3x2
Xx Xy
Yx Yy
Tx Ty

Why did I give such complex names to my cells? As you can see first and second columns have repeating capital letter which is X and Y. These are the vectors X and Y of your new coordinate system. Just make some transformation and try to draw vector X(Xx, Xy) and vector Y(Yx, Yy).
(I don't know how to explain this better without drawing it).

Now when you have the vectors X and Y you can just check their size to get the scale.
scaleX = length(vec2(Xx, Xy));
scaleY = length(vec2(Yx, Yy));


For rotation you need to find out how much one of the vectors is rotated from it's original position which you can do by using arc tangent.
rot = atn2(Xy, Xx)
_________________
http://raynds.blogspot.com/

#169316 - vuurrobin - Thu Jul 02, 2009 2:04 am

I'm creating a library for the ds and you can set the affine matrix of a sprite by either setting the 4 matrix elements directly or by calling a function that will calculate it from a given angle and scale. I also wanted to return the angle, even if the user setted the elements on its own.

its for 2d sprite transformation using a 2*2 matrix, so no 3d stuff.


@DensitY
I looked at the link for a bit, but I didn't understand most of it (I'm not that good at math, especially if its in a foreign language). I'll look at it in the morning and see is I can make something out of it.


@naleksiev
I don't know if only 1 point has sheared if the user setted the matrix him/her self, so that wont work. however getting the scale is usefull, so thanks.


thanks for the posts, everybody :)

#169321 - naleksiev - Fri Jul 03, 2009 12:34 am

I finally got what you are trying to do.

Either consider to detect that the matrix is sheared and show that to the user instead of angle and scale or show the sheared values.

I think you can assume that X isn't sheared and get it's rotation and scale. Then for Y compute shearing value, and scale.
You will end up with scaleX, scaleY, rotation, shearY parameters that you can show on the screen.

Or maybe another solution and my favorite is to show scaleX, scaleY, rotateX, rotateY. Where you can have a separate rotation for X and Y. So for example if you crate rotation matrix on 30? the rotationX and rotationY will both show 30?. But if you shear Y so the angle between X and Y is 45? rotateX will be equal to 0 and rotateY will be -45? (because it's relative from it's original 90?)

You can accomplish this by using the explanation from my previews post. Just use
rotX = atn2(Xy, Xx)
rotY = atn2(Yy, Yx)

_________________
http://raynds.blogspot.com/

#169372 - Cearn - Mon Jul 06, 2009 7:37 pm

naleksiev wrote:
First I haven't used 2D on DS so I don't know if the matrix is row or column based... I'll work with column based which looks like this
Xx Xy
Yx Yy

Since the vectors (capital X and Y) are in the rows, this matrix is actually row-based :P.

The rest of your post(s) explains the concepts and steps well, but unfortunately, the GBA/NDS 2D transformation matrix works a little different.

The 2D matrix has 4 elements, referred to in gbatek as pa, pb, pc, pd. A 2x2 matrix contains 2 vectors that define the axes of transformation. Call these u=(ux, uy) and v= (vx, vy) *. The layout of the matrix is as follows:
Code:
| pa  pb |  =  | ux  vx |
| pc  pd |     | uy  vy |

This is a column-based matrix, although that's really just a matter of notation. What does matter is how u and v map onto pa-pd, and that the matrix is stored in memory by rows.

u and v are vectors like any other: they have a magnitude and a direction. So you have two lengths (scaling factors) and two angles (directional factors). So you have u= ( U*cos(αu), U*sin(αu) ) and v= ( V*cos(αv), V*sin(αv) ). In a rotation, u and v are perpendicular, so there's a 90 degree difference between angles αu and αv, which basically means that one of the sines and cosines swap and one of them gets a minus sign. Renaming the lengths to X and Y scales and relating the angles to some angle a, you get a matrix that looks something like this:
Code:
| ux  vx |  =  | Sx*cos(a)  -Sy*sin(a) |
| uy  vy |     | Sx*sin(a)   Sy*cos(a) |

And if you have this kind of matrix, you can calculate the Sx, Sy and a terms back from the lengths of the vectors and either an arccos, arcsin or arctan. This is basically what naleksiev said. The problem, however, is that it's the wrong matrix.

If you just look at what you have on the screen, you can probably tell the principle axes of the transformation (and therefore the matrix). But the matrix you give to the hardware is the inverse of this (see the Tonc page and here for details). There are still u and v vectors, but they're vectors in a different space. If the result on screen looks like a Scale-then-Rotate operation, what you need to issue is something like this

Code:
| ux  vx |  =  |  Sx*cos(a)   Sx*sin(a) |
| uy  vy |     | -Sy*sin(a)   Sy*cos(a) |

Note that these Sx and Sy are actually different than the ones used before: in this case they're shrink-factors rather than enlargements.


You can still get the scales and angle back, but now it's
Code:
tan(a) = vx/ux                               ->  a = atan2(ux, vx)
ux² + vx² = Sx²*( cos²(a)+sin²(a) ) = Sx²    -> Sx = sqrt(ux² + vx²)
uy² + vy² = Sy²*(-sin²(a)+cos²(a) ) = Sy²    -> Sy = sqrt(uy² + vy²)



vuurrobin wrote:
I'm creating a library for the ds and you can set the affine matrix of a sprite by either setting the 4 matrix elements directly or by calling a function that will calculate it from a given angle and scale. I also wanted to return the angle, even if the user set the elements on its own.

In trying something like this, be very careful how you define your terms and what you want to get out of it. Dealing with matrices can be troublesome on its own, but when you're really working with inverse transformation matrices, things can get really hairy.

Think about this. If you use the following matrix:
Code:
| 2<<8  0    |
| 0     1<<8 |

This causes a horizontal scaling of the sprite. However, it doesn't scale by 2; it shrinks by 1/2. If you want to be able to reproduce the matrix, returning scale==2<<8 would be appropriate. If you want to be able to
do some sort of math with it (for collisions, for example), you'd need to return 1/2<<8.

Another thing is that there are always four parameters in the matrix, whether that's just vectors+coordinates (ux, uy, vx, vy), or any combination of scales, shears or angles. You can just pick one that's most convenient for you. The scaleX, scaleY, angleX, angleY that naleksiev suggested is probably the most useful, though you have to be careful with what those terms will actually mean.

Also, if it helps, there is an easy way to see whether you're dealing with shears or scale+rotations: in the latter the vectors are perpendicular. Technically, you'd need to check the inner product of u and v, but with taking the inverse matrix into account what you actually need to check is ux*uy+vx*vy. If that's zero, then the axes will appear perpendicular on screen.


* If possible, do not use letters x and y for both the vectors and their coordinates. Confuzzlement generally follows quickly after.