Page 1 of 1

Finding the smallest arc of rotation for a vector

Posted: Sun Feb 25, 2007 10:00 pm
by Baiame
Hello. For a while now, I've been trying to figure out (with reference to a couple threads on the GameDev forums) how to rotate vectors. I can't get it right. I'm doing:

Code: Select all

 vector3df direction;
vector3df newDirection;
double product;

newDirection.X=mousePointAt.X-nodePos.X;
 newDirection.Y=mousePointAt.Y-nodePos.Y;
 newDirection.normalize();
 direction.normalize();
 product=(direction.dotProduct(newDirection));
 direction.rotateXYBy(acos(product)*delta*100.0, direction.crossProduct(newDirection));
Note that some of the code is probably unnecessary, due to the number of ways I've tried solving this problem, and the variables I've used for debugging, etc.

I found that the acos of product is always positive. I followed the advice of a thread on GD, and did the same but set the centre of rotation to the cross product of the two vectors, and got the same result. Also, I think that when product=0, acos(product) returns a non-number.

All I want to do is find out the shortest arc of rotation between one point and another point. Any help would be greatly appreciated.

Posted: Sun Feb 25, 2007 10:28 pm
by Acki
So you get the angle, but it's alway positive (0 to 359.99999) ???
Then you just need to check if the angle is more than 180°...
If it's over 180° than simply use angle-360 as angle...
Or did I misunderstand your question ???

Posted: Sun Feb 25, 2007 10:47 pm
by Baiame
I think what I said I needed was misleading. Basically, all I need to know is whether a clockwise or counterclockwise rotation is shortest (between 2 unit vectors). Becuase acos(product), the way it is now, returns absolute values, I don't think if(angle>180)angle-360 will help (even if I set up the code to work with angles, it'd still be the same for both directions of rotation).

EDIT- It's kinda hard to describe. I'm not really working with angles. When I did try to do so, I didn't seem to get any closer to a solution.

I have, by the way, read about dot products on Wikipedia. Apparently the dot product of two perpendicular vectors is 0. So to just rotate the vector by the dot product alone won't work; the scene node translated according to the vector will "orbit" the position it's supposed to reach. I need a bunch of functions that return 0 when the two vectors in question are parallel, not perpendicular.

EDIT2- I kinda know how I would do it. Find out the operation needed to map the to-vector onto vector(1.0, 0.0), perform the same operation on the from-vector (the node's orientation), then look at the x component of the returned vector: if it is positive, rotate clockwise, if negative, rotate counter-clockwise. Not sure how exactly at the moment (especially on the first part).
EDIT3- I tried to find the angle that one must rotate the to-vector by to get it to equal (0.0, 1.0), then rotate another temp-vector (set equal to the node's current orientation vector) by that angle, and finally do a check on the Y component of the resulting vector. I thought it was a sound concept, but it doesn't work at all.

Code: Select all

 tempVector1.X=newDirection.X;
 tempVector1.Y=newDirection.Y;
 tempVector2=direction;
 tempVector2.rotateXYBy(-tempVector1.getAngle(), vector3df(0.0,0.0,0.0));
 
 if (tempVector2.Y>=0.0)
 {
 direction.rotateXYBy(10.0*delta, vector3df(0.0, 0.0, 0.0));
 }
 else
 {
  direction.rotateXYBy(-10.0*delta, vector3df(0.0, 0.0, 0.0));
 }
Can someone please enlighten me?

EDIT4(haha, the edits are getting ridiculous) I actually got it working using that method (I should've used the angle that you rotate the to-vector to itself, not the negative of it). However, my solution is a bit hackish and presumably not very efficient. I'd still be grateful for input on a simpler method.

Posted: Tue Mar 06, 2007 4:20 am
by Gato
To find the signed angle between two (2D) vectors, you can do something like the following:

Code: Select all

// the angle will be in RADIANS, from -Pi to Pi
 core::vector3df perp(-tempVector1.Y,tempVector1.X, 0.0);
 angle = atan2(perp.dotProduct(tempVector2), tempVector1.dotProduct(tempVector2));

To rotate a 2D vector, you can do the following:

Code: Select all

// the angle in RADIANS
double s = sin(angle); 
double c = cos(angle);
core::vector3df vrot(v1.X * c -v1.Y * s, v1.X * s + v1.Y * c);
Or using the rotateXYBy method:

Code: Select all

// the angle is in DEGREES
vec.rotateXYBy(angle, core::vector3df(0,0,0));
I haven't tested these, but they should work.

Posted: Tue Mar 06, 2007 8:20 pm
by Baiame
Thanks Gato, but I revisited this problem a few days ago, and I think I found a faster solution. Something like this:

Code: Select all

 newDirection.X=target.X-nodePos.X;
 newDirection.Y=target.Y-nodePos.Y;
 
 newDirection.normalize();
 direction.normalize();
 
 toAngle=direction.getAngle()+(360.0-newDirection.getAngle());
 if (toAngle>360.0) toAngle-=360.0;
 if (toAngle>180.0) toAngle=-(360.0-toAngle);
It may (or may not, I don't know the workings of the Irrlicht functions you used) use more logical operators, but it certainly uses less assignment and function calls. I don't even know if the normalisation is necessary, I'll have to check.