Math: Calculating angle between vectors (PyMEL)

I’m trying to calculate the angle between two vectors and get it in degrees, and then rotate an object by this angle. Problem is that in some cases, I get an angle that is off by exactly 180 degrees. PyMEL has datatypes for Vectors and Matrices, and I would prefer to use these if possible instead of writing the formulas by hand in Python.

# Calculate vectors
vectorA = pm.datatypes.Vector(targetP0[0] - targetP1[0], targetP0[1] - targetP1[1], 0.0)
vectorB = pm.datatypes.Vector(sourceP1[0] - sourceP0[0], sourceP1[1] - sourceP0[1], 0.0)

# Returned unsigned angle in degrees
angle = math.degrees(vectorB.angle(vectorA)) 

# Calculate dot product
dot = (vectorB.x * vectorA.y) - (vectorB.y * vectorA.x)

if dot > 0: # Same
    sign = 1
elif dot < 0: # Opposite
    sign = -1
else: # Perpendicular
    sign = 0
    
# Signed angle
angle = (angle * sign)

Gives correct rotation
targetP0: [0.674, 0.825]
targetP1: [0.557, 0.8463]
sourceP0: [0.514, 0.901]
sourceP1: [0.372, 0.901]

Does not give correct rotation (off by precisely 180 degrees)
targetP0: [0.514, 0.901]
targetP1: [0.372, 0.901]
sourceP0: [0.674, 0.825]
sourceP1: [0.557, 0.846]

It’s probably something stupid and obvious that I’m missing - but I don’t see what that would be.

v1 = pm.datatypes.Vector(1, 0, 0)
v2 = pm.datatypes.Vector(1, 1, 0)

revert vector direction if dot(v1, v2) < 0 and calculate sign for angle value

v2, sign = (-v2, -1) if v1.dot(v2) < 0 else (v2, 1)

print math.degrees(v1.angle(v2)) * sign

[QUOTE=Styler;29836]v1 = pm.datatypes.Vector(1, 0, 0)
v2 = pm.datatypes.Vector(1, 1, 0)

revert vector direction if dot(v1, v2) < 0 and calculate sign for angle value

v2, sign = (-v2, -1) if v1.dot(v2) < 0 else (v2, 1)

print math.degrees(v1.angle(v2)) * sign[/QUOTE]
Thanks a lot. Flipping v2 was what I had missed to do!

Another similiar problem:
I’m trying to get the offset vector between two line segments (normalized vectors) like in the picture below. I know the positions of the points in the coordinate system if that is of any help:

Problem is that I get random end-to-end vectors like in the picture below, instead of getting the vector from and to the midpoints.

targetVector = pm.datatypes.Vector(targetP0[0] - targetP1[0], targetP0[1] - targetP1[1], 0.0)
sourceVector = pm.datatypes.Vector(sourceP1[0] - sourceP0[0], sourceP1[1] - sourceP0[1], 0.0)
offsetVector = pm.datatypes.Vector(targetP1[0] - sourceP0[0], targetP1[1] - sourceP0[1], 0.0)

But yea, what I need is a vector for the midpoints.

If you need to find exactly the same vector as drawn on the picture, you can do next:
v1 = pm.Vector(p2[0] - p0[0], p2[1] - p0[1], 0)
v2 = pm.Vector(p3[0] - p1[0], p3[1] - p1[1], 0)
v3 = (v1 + v2) * 0.5

This will give you a midpoint vector

[QUOTE=Styler;29842]If you need to find exactly the same vector as drawn on the picture, you can do next:
v1 = pm.Vector(p2[0] - p0[0], p2[1] - p0[1], 0)
v2 = pm.Vector(p3[0] - p1[0], p3[1] - p1[1], 0)
v3 = (v1 + v2) * 0.5

This will give you a midpoint vector

[/QUOTE]
Actually it’s the KM vector that I’m after.
Btw where did you find that picture?

I just attached it as example… Here is the website
I hope that answered your question =)

I was after the other bimedian and not the one you specified. However I got the formula right now:


# targetP0 - target vector point 0, in the form of pm.datatypes.Point()
# sourceP1 - source vector point 1...
# etc...

# Calculate midpoints for the offset vector
targetMid = []
targetMid.append((targetP0[0] + targetP1[0]) / 2)
targetMid.append((targetP0[1] + targetP1[1]) / 2)
sourceMid = []
sourceMid.append((sourceP0[0] + sourceP1[0]) / 2)
sourceMid.append((sourceP0[1] + sourceP1[1]) / 2)
        
offsetVector = pm.datatypes.Vector(targetMid[0] - sourceMid[0], targetMid[1] - sourceMid[1], 0.0)
# Win

Either way, that was off-topic actually. So to get back to the original problem: It turns out that the problem I have in my original post of this thread is still there. I still don’t get the correct rotation angle for the vectors!! :frowning:


# targetP0 - target vector point 0, in the form of pm.datatypes.Point()
# sourceP1 - source vector point 1...
# etc...

# Create vectors
sourceVector = pm.datatypes.Vector(sourceP1[0] - sourceP0[0], sourceP1[1] - sourceP0[1], 0.0)
targetVector = pm.datatypes.Vector(targetP0[0] - targetP1[0], targetP0[1] - targetP1[1], 0.0)

# Calculate dot product and get sign for the angle
temp = sourceVector.dot(targetVector)
if temp > 0: # Same
    sign = 1
elif temp < 0: # Opposite
    sign = -1
    sourceVector = -sourceVector

else: # Perpendicular
    sign = 0

# Calculate angle - returns unsigned angle as degrees
angle = math.degrees(sourceVector.angle(targetVector))

# Set correct sign
angle = angle * sign

# Rotate by the value in angle

What end result I get is dependant on the directions of the targetVector and sourceVector. If I rotate one of them juuust enough, everything turns out all right. But if their directions happen to be “wrong” somehow, I get an error of either 180 degrees minus the angle, or the angle but with an inverted sign.

This is really frustrating. The math looks solid so what am I missing?
Maybe the function angle() is incorrectly used? The docs says it returns the angle in radians, and that the returned value is unsigned and that axis() should be used to find the sign. But I’ve already calculated the dot product so why is this even needed?

This will be much easier if you post your code snippet with full description what you want. You can write me PM.