Some weirdness with Vec3.angleDeg

I’m having some problems with this function. Made a quick test and got some weird results:

v1 = Vec3(1,0,0)
v2 = Vec3(1,1,0)
v3 = Vec3(1,-1,0)

The vectors are normalized like the function description says. These methods are supposed to be faster if we work with already normalized vectors right?

v2.normalize()
v3.normalize()

The test results are:

v1.angleDeg(v2) = 45 which is ok
v1.angleDeg(v3) = 45 which is not ok, should be 315

The method v1.relativeAngleDeg(v3) gives -45 which is the right result when we normalize the angle value to 360-45=315.

I think this might be a bug, otherwise angleDeg is just a confusing way to do the same thing the dot product already does.

im not soo well into the maths but in my opition, the angle between those 2 vecs is 45°, it doesnt matter if it’s on the x+ or x- direction (because it’s 3d)…

But your are right that relativeAngleDeg is more useful then angleDeg.

i think this is more curious:

v1 = Vec3(1,0,0)
v4 = Vec3(1,1,1)
v4.normalize()

v1.relativeAngleDeg( v4 ) is also 45, it just drop’s the z vector part.

in my (noob) opinion a the angle between those 2 Vec’s should return a Vec2 (vec3 is unfortunately not possible, but afaik vec2 would be)

and actually i hope the current behavior will not change, because of backwards compatiblity…

"im not soo well into the maths but in my opition, the angle between those 2 vecs is 45°, it doesnt matter if it’s on the x+ or x- direction (because it’s 3d)… "

I think that’s the other way around. I matters because it is 3d. :wink:

Image you need to make a script to follow someone, but you don’t want to use the lookAt function, you want to turn slowly towards him instead. If you don’t get the correct angle you end up turning in the opposite direction.

I just though it would be worth mentioning because the method description says it computes the angle between two vectors including 3d vectors.

PS: Unfortunately many cg books and even some math books give a wrong or ambiguous answer to this. The dot product is used to find the component of a vector in relation to another vector and not to obtain an angle. Even the usual mentioned method to convert a vector from rectangular coordinates (x,y) to polar coordinates (r,a) that says the angle a=tan(y/x) is incorrect for x<=0. That’s why the atan2(y,x) function exists.

Check this out in python math functions:
docs.python.org/lib/module-math.html

atan2(y, x)
    Return atan(y / x), in radians. The result is between -pi and pi. The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4.

The angle between two vectors is always just that, a single numeric angle. The angle is computed in the plane that contains both vectors. This angle has many uses, but one of them is not as a replacement for lookAt(), at least not by itself. And in fact, angleDeg() is computed via the dot product in the traditional way, though it has some special cases to deal with numerical imprecision close to the edge conditions.

relativeAngleDeg() also has many uses, a slightly different set than angleDeg(), but again it doesn’t really serve as a replacement for lookAt(). You would need another piece of information–the axis about which to rotate, which you can compute with a cross product of the two vectors.

But the easiest way to do a smooth lookAt() operation is to use a LerpQuatInterval. Quaternions have several nice properties that make them ideal for this sort of operation. In particular: (a) lerps always go the shortest way around, and (b) lerps are linearly smooth.

The easiest way to compute the appropriate quaternion is to call lookAt, and then see what the resulting quaternion is. Then reset to the original orientation and lerp from there.

Try code like this:

startQuat = source.getQuat()
source.lookAt(target)
endQuat = source.getQuat()
ival = LerpQuatInterval(source, 2, quat = endQuat, startQuat = startQuat)
ival.start()

David

I only pointed this out because the method description may be misleading. The angleDeg method only returns angles between 0 and 180 degrees and not the full range from 0 to 360. At least some mention of this would be useful.

Wouldn’t it be possible to build the rotation matrix or an axis angle from the angle returned by relativeAngleDeg and the cross product vector of the two vectors?

Someone did it in this thread (another game engine):
gamedev.net/community/forums … _id=488746

vector3 axis = cross(v1, v2);
float len = length(axis);
float angle = atan2(len, dot(v1,v2));
matrix33 rotation = identity33();
if (len > epsilon) {
    rotation.make_rotation(normalize(axis), angle));
}

I will take a look at it. The lookAt function maintains our model aligned with an up vector. Sometimes this restriction is not necessary, for missiles and similar stuff.

It’s a good point–there are lots of different things that might be meant by “the angle between two vectors”. Certainly the comment isn’t very informative.

Certainly. You can build a quaternion up with setFromAxisAngle(), which allows you to specify the axis determined by the cross product, and the angle determined by the dot product, of your two vectors. Then you can use this quat in a LerpQuatInterval.

Or, you can use the higher-level functions like lookAt(), which are designed to save you from having to think about trigonometry at all.

Without an up vector, “rotate A to look at B” is ambiguous. If you don’t care about the ambiguity, like for missiles and similar stuff, then it doesn’t matter what your up vector is, so you can just let it default. If it’s important to you that the up vector doesn’t change when you do the rotation, then you can pass in the current up vector to lookAt().

David