Skip to content

Numerically stable implementation of axis, angle and scaled_axis for Rotation3 and UnitQuaternion#1595

Open
nmayorov wants to merge 7 commits intodimforge:mainfrom
nmayorov:robust_axis_angle
Open

Numerically stable implementation of axis, angle and scaled_axis for Rotation3 and UnitQuaternion#1595
nmayorov wants to merge 7 commits intodimforge:mainfrom
nmayorov:robust_axis_angle

Conversation

@nmayorov
Copy link
Copy Markdown

@nmayorov nmayorov commented Mar 28, 2026

Hi and thank you for a great library!

In this PR I wan to resolve couple of issues regarding axis-angle representation of rotations.

Conceptual change

The conceptual idea is that we want to use "scaled_axis" aka "rotation vector" as the core representation, without splitting it into axis and angle in internal computations. This is the numerical stable way to work with such representation and it gracefully handles the case of zero or close to zero rotation angles without introducing special cases, singularities and epsilon tresholds.

What issues does it fix

It fixes the following practical issues and bugs:

  • Loss of precision when creating from scaled_axis , i.e creating rotation from Vector3::new(5.0e-20, 1e-16, -1.0e-17) and then querying scaled_axis representation will return this vector, not vector of zeros
  • Possibility of angle returned as nan from Rotation3 due to acos argument going out [-1, 1] bounds after some minor round-off error accumulation (the issue I encountered myself)
  • Unstable computation of rotation axis from Rotation3 for angles close to pi (example in issue Numerically vulnerable axis calculation in Rotation3 #1382 )

Basically all the operations regarding scaled_axis and angle are 100% robust, the axis extraction can be controlled by the user (i.e. for how small angles to extract rotation axis).

How does it achieves that

The core change is in Quaternion::exp and UnitQuaternion::scaled_axis where scaling of the form sin(theta) / theta and its inverse is implemented via sinc without any epsilon threshold checking. After that UnitQuaternion functionality is solid.

And Rotation methods are delegated to UnitQuaternion as rotation matrix -> unit quaternion -> scaled axis. There are not many reasons or savings doing it in any other way, because scaled axis / rotation vector is directly connected to quaternion representation.

Code concerns

I had to change trait bound of T to RealField in Rotation3::angle. I'm not an expert in Rust and don't know exact implications of that, but it seems reasonable to do, because all other functions related to axis and angle have this specification.


I plan to add more tests on the functionality, meanwhile, hopefully, someone review and consider these changes.

Closes #1382.

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.

Numerically vulnerable axis calculation in Rotation3

3 participants