chaomodus/pyeuclid

Quaternion interpolation not working

Opened this issue · 4 comments

What steps will reproduce the problem?
q1 = Quaternion(.15,0, 0.99,0).normalized()
q2 = Quaternion(.15,0,-0.99,0).normalized()
print Quaternion.new_interpolate(q1,q2,0.0)
print Quaternion.new_interpolate(q1,q2,0.25)
print Quaternion.new_interpolate(q1,q2,0.5)
print Quaternion.new_interpolate(q1,q2,0.75)
print Quaternion.new_interpolate(q1,q2,1.0)

What is the expected output? What do you see instead?
I get the following output which does not appear to interpolate between the two 
quaternions:
Quaternion(real=0.15, imag=<0.00, -0.99, 0.00>)
Quaternion(real=0.15, imag=<0.00, -1.00, 0.00>)
Quaternion(real=0.15, imag=<0.00, -1.00, 0.00>)
Quaternion(real=0.15, imag=<0.00, -1.00, 0.00>)
Quaternion(real=0.15, imag=<0.00, -0.99, 0.00>)

This might be occurring when the angle between the two quaternions is obtuse?? 
I haven't dug into it enough yet to figure out the reason for failure.

What version of the product are you using? On what operating system?
Revision 36 on Ubuntu 10.04

Please provide any additional information below.


Original issue reported on code.google.com by chris.fl...@gmail.com on 19 Dec 2011 at 9:10

The error seems to have been introduced in revision 26. Revision 25 works fine 
on this example but revision 26 fails.

Original comment by chris.fl...@gmail.com on 19 Dec 2011 at 9:21

  • Added labels: ****
  • Removed labels: ****
Here is the diff between -r25 and -r26:

@@ -1287,6 +1346,12 @@
         Q = cls()

         costheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z
+        if costheta < 0.:
+            costheta = -costheta
+            q1 = q1.conjugated()
+        elif costheta > 1:
+            costheta = 1
+
         theta = math.acos(costheta)
         if abs(theta) < 0.01:
             Q.w = q2.w

It just seems like reverting the new_interpolate function back to -r25 would 
fix things. I'm not sure why the following lines are necessary (assuming both 
quaternions are normalized):
+        elif costheta > 1:
+            costheta = 1

I guess in case of rounding errors?

Original comment by chris.fl...@gmail.com on 19 Dec 2011 at 9:32

  • Added labels: ****
  • Removed labels: ****
Probably related to bug #16

Original comment by rodrigo....@gmail.com on 6 Aug 2014 at 9:45

  • Added labels: ****
  • Removed labels: ****
I ended up making some other mods to this function, here is currently working 
for me. I needed to have very precise interpolation for my application, the 
existing code will skip interpolation altogether if the quaternion angle 
difference is lower than an arbitrary threshold.

    def new_interpolate(cls, q1, q2, t):
        assert isinstance(q1, Quaternion) and isinstance(q2, Quaternion)
        Q = cls()

        costheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z
        if costheta < 0.:
            costheta = -costheta
            q1 = q1.conjugated()
        elif costheta > 1:
            costheta = 1
        elif costheta == 1.0:
            Q.w = q1.w
            Q.x = q1.x
            Q.y = q1.y
            Q.z = q1.z
            return Q
        theta = math.acos(costheta)
        sintheta = math.sqrt(1.0 - costheta * costheta)
        try:
            ratio1 = math.sin((1 - t) * theta) / sintheta
        except ZeroDivisionError:
            Q.w = q1.w
            Q.x = q1.x
            Q.y = q1.y
            Q.z = q1.z
            return Q
        ratio2 = math.sin(t * theta) / sintheta

        Q.w = q1.w * ratio1 + q2.w * ratio2
        Q.x = q1.x * ratio1 + q2.x * ratio2
        Q.y = q1.y * ratio1 + q2.y * ratio2
        Q.z = q1.z * ratio1 + q2.z * ratio2
        return Q
    new_interpolate = classmethod(new_interpolate)

Original comment by chris.fl...@gmail.com on 6 Aug 2014 at 10:33

  • Added labels: ****
  • Removed labels: ****