Skip to content

Commit

Permalink
honor closest distance in quaternion slerp
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nitrix authored Apr 7, 2023
1 parent 45f44e1 commit e426c08
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 2 deletions.
8 changes: 7 additions & 1 deletion mgl32/quat.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,20 @@ func (q1 Quat) OrientationEqualThreshold(q2 Quat, epsilon float32) bool {
}

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

// Make sure we take the shortest path in case dot product is negative.
if dot < 0.0 {
q2 = q2.Scale(-1)
dot = -dot
}

// If the inputs are too close for comfort, linearly interpolate and normalize the result.
if dot > 0.9995 {
return QuatNlerp(q1, q2, amount)
Expand Down
1 change: 1 addition & 0 deletions mgl32/quat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ func TestQuatSlerp(t *testing.T) {
{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}}},
{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}}},
{Quat{1, Vec3{0, 0, 0}}, Quat{-0.9999999, Vec3{0, 0, 0}}, 0, Quat{1, Vec3{0, 0, 0}}},
{Quat{-0.707, Vec3{0, 0, 0.707}}, Quat{1, Vec3{0, 0, 0}}, 0.12, Quat{-0.7705132, Vec3{0, 0, 0.637424}}},
}

for _, c := range tests {
Expand Down
8 changes: 7 additions & 1 deletion mgl64/quat.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,20 @@ func (q1 Quat) OrientationEqualThreshold(q2 Quat, epsilon float64) bool {
}

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

// Make sure we take the shortest path in case dot product is negative.
if dot < 0.0 {
q2 = q2.Scale(-1)
dot = -dot
}

// If the inputs are too close for comfort, linearly interpolate and normalize the result.
if dot > 0.9995 {
return QuatNlerp(q1, q2, amount)
Expand Down
1 change: 1 addition & 0 deletions mgl64/quat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ func TestQuatSlerp(t *testing.T) {
{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}}},
{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}}},
{Quat{1, Vec3{0, 0, 0}}, Quat{-0.9999999, Vec3{0, 0, 0}}, 0, Quat{1, Vec3{0, 0, 0}}},
{Quat{-0.707, Vec3{0, 0, 0.707}}, Quat{1, Vec3{0, 0, 0}}, 0.12, Quat{-0.7705132, Vec3{0, 0, 0.637424}}},
}

for _, c := range tests {
Expand Down

0 comments on commit e426c08

Please sign in to comment.