Another way for 2D Rotation?

Started by
29 comments, last by nerco666 14 years, 1 month ago
Quote:Original post by nerco666
Thank you guys for your help.

Btw assuming "yi" is the difference between the 2 points on the y axis should i negate it since "i = negative exponent"

so a.y - - b.y = a.y + b.y..

Thanks!


I have no idea what you mean by that. I don't know what "2 points" you are talking about, and there is nothing negative about i... Perhaps you can try to reformulate your question more clearly.
Advertisement
here is how I do simple rotation. Keep the cos and sin calcs out of the loop for speed. More speed could be gained if you put the cos and sin values into tables.

Don't try to setup a matrix to do this because it would be much slower.

cos1 = cos(ang);
sin1 = sin(ang);

for ( c = 0 ; c < vertcount ; c++ ) {
newx = vertx[c] * cos1 + verty[c] * sin1;
newy = verty[c] * cos1 - vertx[c] * sin1;
// store newx and newy here
}
Quote:Original post by ejthayer
Don't try to setup a matrix to do this because it would be much slower.

That is completely untrue. The matrix multiplication will perform the exact same calculations, and would be very similar in speed if this is the only transformation being done. If more than one or two transformations is being done their matrices can be concatenated and several operations can be done at one time with a single matrix multiplication, which will be much faster.

http://en.wikipedia.org/wiki/Transformation_matrix
If we're discussing a purely mathematical exercise, the previous posters have explained things better than I can.

If there's a specific reason why you're trying to speed up rotation, it would be useful to know some more details. Depending on what you're using this code for, there is plenty of room for optimization at higher levels.

For example, if you're rotating a bitmap, you can calculate some x and y deltas at the start, then most of the actual pixels getting rotated will only require addition and table lookups. Other situations will have different optimization paths.

sox is right. In most cases, you can avoid using angles. The resulting code is often cleaner and faster.

For arbitrary rotations there is nothing faster than a matrix for rotating a vector.

The only possible optimization to a matrix can be made if you know that certain elements of a matrix are either 1, -1, or 0, by employing the simple, yet fundamental rule








The case of having matrix elements with 1's, -1's or 0's only happens when the angle is at 90o intervals. In such cases the matrix multiplication reduces to

Some rotation problems involve knowing the rotation angle to begin with - for instance, the user may input the angle and the power of the gun on a tank before firing. However, most rotation problems don't initially involve angles.

The most common case is when you want to align two vectors on an object. For instance, you want to point a cannon at a duck. There is a vector that points from the center of the cannon down the muzzle of the cannon, and there is a vector that points from the center of the cannon to the duck. You want to rotate the cannon so that these vectors are aligned.

Here is a function that finds the value of the sin and cosine without all of the messy trig and inverse trig functions, using as input the initial vector i (the cannon muzzle), and the final vector f (the duck).

void findSinCos(const vec2& i, const vec2& f, float& sin, float& cos){     float xx = i.x*f.x;     float xy = i.x*f.y;     float yx = i.y*f.x;     float yy = i.y*f.y;     cos = xx + yy;  //vector dot product     sin = xy - yx;  //vector cross product in 2D     //find normalization constant     float norm = xx*xx + xy*xy + yx*yx + yy*yy;     norm = 1.0f / sqrt(norm);     //apply normalization constant     cos *= norm;     sin *= norm;}


More than half of the cost of this funciton is performing normalization, but it is cheaper to do it here rather than normalizing the vectors externally and passing them in. If you have the normalized vectors sitting around for other purposes anyway, just make a similar function that leaves out the normalization step.

Now make a function to construct the matrix

void findRotMatrix(const vec2& i, const vec2& f, mat22& mat){     float s, c;     findSinCos(i, f, s, c);     mat.m00 = c;     mat.m11 = c;     mat.m01 = -s;     mat.m10 = s;}
Since 2D rotation matrices are all of the form

(p q)
(-q p)

you can simply store p and q. I would call that an optimization. In other words, use unit-length complex numbers the way I described earlier. :)

The equivalent of findSinCos() and findRotMatrix() in terms of complex numbers is simply this:
typedef std::complex<double> Complex;// Find the rotation that when applied to a yields something aligned with bComplex find_rotation(Complex a, Complex b) {  Complex d = b/a;  return d / d.abs();}
Cast the two vectors I and F as complex numbers




Now divide F by I



Now, if you want to take into account the fact that I and F might not be normalized, you would multiply this by the fraction |I|/|F|. The result is



If I and F are unit length, then the denominator is 1.

Notice that the real and imaginary parts correspond exactly with the cosine and sine terms in the function findSinCos. Thus - it is the exact same amount of computational work. The complex version only has less code because you've overloaded the divide operator.

Not that I am against using complex numbers to represent rotations. Complex numbers are basically the 2D version of quaternions. If you would like to see how to extend your find_rotation function to 3D - i.e. using quaternions instead of complex numbers - you can look at an article on my blog. Look toward the bottom. The function at the bottom is the quaternion equivalent of your find_rotation function, (without overloaded operators.)

[Edited by - Eric_Brown on March 17, 2010 8:34:27 PM]
Quote:Original post by Eric_Brown
[...]

Notice that the real and imaginary parts correspond exactly with the cosine and sine terms in the function findSinCos. Thus - it is the exact same amount of computational work. The complex version only has less code because you've overloaded the divide operator.


Of course it's the same amount of computation. The only possible performance difference is that your code stores the result using four floating-point numbers, and mine uses only two.

But the real advantage of expressing it as operations on complex numbers is that it's much much shorter and easier to get right. I didn't overload the divide operator: The standard C++ library did it for me. As I said, fewer opportunities for me to mess up.

As you mention, using complex numbers to represent 2D rotations is similar in spirit to using quaternions for 3D rotations, but it's a lot easier to fit in your head. Perhaps that is really the biggest reason to learn how to use complex numbers in this fashion.

This topic is closed to new replies.

Advertisement