dimforge / nalgebra

Linear algebra library for Rust.
https://nalgebra.org
Apache License 2.0
3.96k stars 473 forks source link

UnitDualQuaternion::sclerp() fails to interpolate equal rotations #1013

Closed Andlon closed 2 years ago

Andlon commented 2 years ago

Example:

let q1 = UnitDualQuaternion::from_rotation(UnitQuaternion::identity());
let q2 = UnitDualQuaternion::from_rotation(UnitQuaternion::identity());
q1.sclerp(&q2, 0.5);

panics with message:

DualQuaternion sclerp: ambiguous configuration.

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2d0788827c911f814132f41ce3e247d4

I realize that 180 degree rotation is ambiguous for interpolation, but 0 degrees ought to be unambiguous. I'd fix this myself but I'm not deep enough into the math to be confident about the fix (and I'm too involved with other things to get into it right now).

Maybe @tpdickso, @chinedufn or @ChristopherRabotin from #487 are able to chime in here.

ChristopherRabotin commented 2 years ago

I'm sorry, I've never worked with linear interpolation of quaternions before. My hunch however is that the interpolation of time between q_1 to itself should return q_1.

tpdickso commented 2 years ago

Good catch; I think the code that is used to check for an ambiguous configuration has a false positive if the quaternions are very close. I have a wild conjecture that this may be due to the fact that the helical trajectory that interpolates the two configurations would have an origin that approaches infinity (i.e., it's trying to produce a circular segment that is nearly-straight.)

I took a look at the code and I'm not quite sure how to differentiate the two cases. It probably is correct to handle the case in this ticket by just falling back to a split (slerp + lerp) for the rotation and translation components respectively, because the trajectory will be so close to linear. I can try to take a deeper dive this evening.

tpdickso commented 2 years ago

Oh, nevermind... the issue is actually quite a bit simpler. Multiplying two dual quaternions with the same rotational component should produce a difference with a magnitude of 1, not 0. Which it does. But I wrote vector(), instead of as_vector(), which just returns the imaginary components of the vector, when computing the magnitude. So a perfect unit quaternion for rotation has no imaginary components and will produce a zero magnitude when you're ignoring its real part. 🤦

I'll put up a patch to fix it!

tpdickso commented 2 years ago

No, I was right the first time, the formula just couldn't handle equal rotation components for totally numerical reasons. Putting up PR to fix it now!

sebcrozet commented 2 years ago

Closing this as the PR fixing it is merged.