Skip to content

Fix Unit/Vector slerp endpoints for antiparallel inputs - #1614

Open
gaoflow wants to merge 1 commit into
dimforge:mainfrom
gaoflow:fix-antiparallel-unit-slerp
Open

Fix Unit/Vector slerp endpoints for antiparallel inputs#1614
gaoflow wants to merge 1 commit into
dimforge:mainfrom
gaoflow:fix-antiparallel-unit-slerp

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 28, 2026

Copy link
Copy Markdown

The infallible Unit::slerp fell back to returning self for every t when self and rhs are antiparallel, so slerp(a, b, 1.0) returned -b instead of b, breaking its own doctest contract slerp(a, b, 1) == b. Vector::slerp inherited it. This is #657, and the spot of the old // TODO: … wrong when self and rhs are collinear with opposite direction.

Antiparallel is genuinely ambiguous (any great circle is valid), so only the endpoints are contractual. The degenerate branch now rotates through a deterministic axis orthogonal to self, making both endpoints exact and the path continuous; the interior geodesic is arbitrary-but-fixed.

Related: try_slerp returned Some(NaN) when rounding pushed self.dot(rhs) just past -1 (e.g. some Vector4 inputs), because acos/sqrt went out of domain — it now returns None, mirroring the existing parallel guard above it. UnitQuaternion/Rotation3::slerp (documented to panic at 180°) and UnitComplex/Rotation2::slerp (angle-based, already correct) have different contracts and are left as-is.

Tested with 6 new core::interpolation cases (2D/3D/4D antiparallel endpoints, unit-norm interior + orthogonal midpoint, generic control, Vector::slerp wrapper); the full feature-set cargo test suite is otherwise green (the one unrelated stack! trybuild UI failure is pre-existing toolchain drift, red on a clean checkout too).

The infallible Unit::slerp fell back to returning self for every t when self
and rhs are antiparallel, so slerp(a, b, 1.0) gave -b instead of b, breaking
the documented endpoint contract (dimforge#657). Vector::slerp inherited this.

Handle the degenerate branch by rotating through a deterministic axis
orthogonal to self, keeping the endpoints exact and the path continuous; the
interior geodesic is arbitrary since it is genuinely ambiguous there. Also make
try_slerp return None (instead of Some(NaN)) when rounding pushes the dot
product just past -1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant