Skip to content

Commit e426c08

Browse files
authored
honor closest distance in quaternion slerp
Fix the quaternion spherical linear interpolation to make it choose the shortest linear path, which is the intended behavior in graphical animations. GitHub-Pull-Request: #90
1 parent 45f44e1 commit e426c08

File tree

4 files changed

+16
-2
lines changed

4 files changed

+16
-2
lines changed

mgl32/quat.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,20 @@ func (q1 Quat) OrientationEqualThreshold(q2 Quat, epsilon float32) bool {
210210
}
211211

212212
// QuatSlerp is *S*pherical *L*inear Int*erp*olation, a method of interpolating
213-
// between two quaternions. This always takes the straightest path on the sphere between
213+
// between two quaternions. This always takes the straightest and shortest path on the sphere between
214214
// the two quaternions, and maintains constant velocity.
215215
//
216216
// However, it's expensive and QuatSlerp(q1,q2) is not the same as QuatSlerp(q2,q1)
217217
func QuatSlerp(q1, q2 Quat, amount float32) Quat {
218218
q1, q2 = q1.Normalize(), q2.Normalize()
219219
dot := q1.Dot(q2)
220220

221+
// Make sure we take the shortest path in case dot product is negative.
222+
if dot < 0.0 {
223+
q2 = q2.Scale(-1)
224+
dot = -dot
225+
}
226+
221227
// If the inputs are too close for comfort, linearly interpolate and normalize the result.
222228
if dot > 0.9995 {
223229
return QuatNlerp(q1, q2, amount)

mgl32/quat_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@ func TestQuatSlerp(t *testing.T) {
702702
{Quat{0.5, Vec3{-0.5, -0.5, 0.5}}, Quat{0.996, Vec3{-0.080, -0.080, 0}}, 0.2, Quat{0.6553097459373098, Vec3{-0.44231939784548874, -0.44231939784548874, 0.4237176207195655}}},
703703
{Quat{0.996, Vec3{-0.080, -0.080, 0}}, Quat{0.5, Vec3{-0.5, -0.5, 0.5}}, 0.8, Quat{0.6553097459373098, Vec3{-0.44231939784548874, -0.44231939784548874, 0.4237176207195655}}},
704704
{Quat{1, Vec3{0, 0, 0}}, Quat{-0.9999999, Vec3{0, 0, 0}}, 0, Quat{1, Vec3{0, 0, 0}}},
705+
{Quat{-0.707, Vec3{0, 0, 0.707}}, Quat{1, Vec3{0, 0, 0}}, 0.12, Quat{-0.7705132, Vec3{0, 0, 0.637424}}},
705706
}
706707

707708
for _, c := range tests {

mgl64/quat.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,20 @@ func (q1 Quat) OrientationEqualThreshold(q2 Quat, epsilon float64) bool {
212212
}
213213

214214
// QuatSlerp is *S*pherical *L*inear Int*erp*olation, a method of interpolating
215-
// between two quaternions. This always takes the straightest path on the sphere between
215+
// between two quaternions. This always takes the straightest and shortest path on the sphere between
216216
// the two quaternions, and maintains constant velocity.
217217
//
218218
// However, it's expensive and QuatSlerp(q1,q2) is not the same as QuatSlerp(q2,q1)
219219
func QuatSlerp(q1, q2 Quat, amount float64) Quat {
220220
q1, q2 = q1.Normalize(), q2.Normalize()
221221
dot := q1.Dot(q2)
222222

223+
// Make sure we take the shortest path in case dot product is negative.
224+
if dot < 0.0 {
225+
q2 = q2.Scale(-1)
226+
dot = -dot
227+
}
228+
223229
// If the inputs are too close for comfort, linearly interpolate and normalize the result.
224230
if dot > 0.9995 {
225231
return QuatNlerp(q1, q2, amount)

mgl64/quat_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ func TestQuatSlerp(t *testing.T) {
704704
{Quat{0.5, Vec3{-0.5, -0.5, 0.5}}, Quat{0.996, Vec3{-0.080, -0.080, 0}}, 0.2, Quat{0.6553097459373098, Vec3{-0.44231939784548874, -0.44231939784548874, 0.4237176207195655}}},
705705
{Quat{0.996, Vec3{-0.080, -0.080, 0}}, Quat{0.5, Vec3{-0.5, -0.5, 0.5}}, 0.8, Quat{0.6553097459373098, Vec3{-0.44231939784548874, -0.44231939784548874, 0.4237176207195655}}},
706706
{Quat{1, Vec3{0, 0, 0}}, Quat{-0.9999999, Vec3{0, 0, 0}}, 0, Quat{1, Vec3{0, 0, 0}}},
707+
{Quat{-0.707, Vec3{0, 0, 0.707}}, Quat{1, Vec3{0, 0, 0}}, 0.12, Quat{-0.7705132, Vec3{0, 0, 0.637424}}},
707708
}
708709

709710
for _, c := range tests {

0 commit comments

Comments
 (0)