pub struct Rotation<From, To> { /* private fields */ }
Expand description
Defines a rotation transform between two CoordinateSystem
s.
There are generally two ways to construct a rotation:
- by using
engineering::Orientation::map_as_zero_in
when you already have anengineering::Orientation
; - by using Tait-Bryan angles to construct arbitrary rotations.
Mathematically speaking, this is a type-safe wrapper around a unit quaternion.
Note that this type implements Deserialize
despite having unsafe
constructors – this is
because doing otherwise would be extremely unergonomic. However, when deserializing, the
coordinate system of the deserialized value is not checked, so this is a foot-gun to be
mindful of.
Rotations can be chained with other transformations using *
(ie, the Mul
trait). However,
note that the order of the operands to *
matter, and do not match the mathematical
convention. Specifically, matrix multiply for transforms (like rotations) traditionally have
the transform on the left and the vector to transform on the right. However, doing so here
would lead to a type signature of
let _: Coordinate<To> = Rotation<From, To> * Coordinate<From>;
Which violates the expectation that a matrix multiply eliminates the “middle” component (ie,
(m × n)(n × p) = (m × p)). So, we require that the rotation is on the right to go from
From
into To
, and that the rotation is on the left to go from To
into From
(ie, for
the inverse transform).
Implementations§
Source§impl<From, To> Rotation<From, To>where
To: CoordinateSystem<Convention = EnuLike>,
impl<From, To> Rotation<From, To>where
To: CoordinateSystem<Convention = EnuLike>,
Sourcepub unsafe fn into_ned_equivalent<NedTo>(self) -> Rotation<From, NedTo>where
NedTo: CoordinateSystem<Convention = NedLike>,
pub unsafe fn into_ned_equivalent<NedTo>(self) -> Rotation<From, NedTo>where
NedTo: CoordinateSystem<Convention = NedLike>,
Converts a rotation from an EnuLike
coordinate system into the equivalent rotation in the
NedLike
coordinate system that shares the same origin.
§Safety
This function only provides a valid rotation if the EnuLike
and NedLike
coordinate
systems share the same origin. If this is not the case, the resulting rotation will lead to
incorrect transformations.
Source§impl<From, To> Rotation<From, To>where
To: CoordinateSystem<Convention = NedLike>,
impl<From, To> Rotation<From, To>where
To: CoordinateSystem<Convention = NedLike>,
Sourcepub unsafe fn into_enu_equivalent<EnuTo>(self) -> Rotation<From, EnuTo>where
EnuTo: CoordinateSystem<Convention = EnuLike>,
pub unsafe fn into_enu_equivalent<EnuTo>(self) -> Rotation<From, EnuTo>where
EnuTo: CoordinateSystem<Convention = EnuLike>,
Converts a rotation from a NedLike
coordinate system into the equivalent rotation in the
EnuLike
coordinate system that shares the same origin.
§Safety
This function only provides a valid rotation if the NedLike
and EnuLike
coordinate
systems share the same origin. If this is not the case, the resulting rotation will lead to
incorrect transformations.
Source§impl<From, To> Rotation<From, To>
impl<From, To> Rotation<From, To>
Sourcepub unsafe fn from_tait_bryan_angles(
yaw: impl Into<Angle>,
pitch: impl Into<Angle>,
roll: impl Into<Angle>,
) -> Self
👎Deprecated: Prefer tait_bryan_builder
to avoid argument-order confusion
pub unsafe fn from_tait_bryan_angles( yaw: impl Into<Angle>, pitch: impl Into<Angle>, roll: impl Into<Angle>, ) -> Self
tait_bryan_builder
to avoid argument-order confusionConstructs a rotation between two CoordinateSystem
s using (intrinsic) yaw, pitch, and
roll Tait-Bryan angles.
Perhaps counter-intuitively, this is the rotation of From
in To
. That is, it is the
rotation that must be applied to points in To
to place them in From
.
If your brain is more engineering-oriented, prefer the alternatives listed in the type docs.
Perhaps counter-intuitively, the angles provided here should represent the rotation that
must be applied to an object with zero Bearing
in To
to arrivate at a Bearing
with the given angles in From
. This is because we take the inverse of this rotation
to go from From
into To
, which in turn stems from mathematical norms.
The three rotations are defined as follows:
- yaw is rotation about the Z axis of
In
; - pitch is rotation about the Y axis; and
- roll is rotation about the X axis.
Since we are using intrinsic rotations (by convention given the naming of the arguments), pitch is with respect to the Y axis after applying the yaw, and roll is with respect to X after applying both yaw and pitch.
To determine the direction of rotation (ie, in which direction a positive angle goes), you can use the right-hand rule for rotations: curl your fingers and stick your thumb out in the positive direction of the axis you want to check rotation around (eg, positive Z for yaw). The direction your fingers curl is the direction of (positive) rotation.
Be aware that rotational angles have high ambiguities in literature and are easy to use wrong, especially because different fields tend to use the same term with different meanings (eg, “Euler angles” mean something else in aerospace than in mathematics).
See also engineering::Orientation::from_tait_bryan_angles
.
§Safety
Calling this method asserts that the provided rotational angles represent a correct way to
transform any input from From
to To
(and crucially, that no translation is needed). If
this is not the correct transform, then this allows moving values between different
coordinate system types without adjusting the values correctly, leading to a defeat of
their type safety.
Sourcepub unsafe fn identity() -> Self
pub unsafe fn identity() -> Self
Asserts that the rotational transform from From
to To
is one that returns the original
point or vector when applied.
§Safety
Like RigidBodyTransform::identity
, this allows you to claim that the “correct”
rotational transform between the coordinate systems From
and To
is the identity
function (and that no translation is needed). If this is not the correct transform, then
this allows moving values between different coordinate system types without adjusting the
values correctly, leading to a defeat of their type safety.
Sourcepub unsafe fn is_also_into<NewTo>(self) -> Rotation<From, NewTo>
pub unsafe fn is_also_into<NewTo>(self) -> Rotation<From, NewTo>
Changes the target coordinate system of the rotation to <NewTo>
with no changes to the
rotational angles.
This is useful when you have two coordinate systems that you know have exactly equivalent axes, and you need the types to “work out”. This can be the case, for instance, when two crates have both separately declared a NED-like coordinate system centered on the same object, and you have a rotation from some coordinate system into one of them, but you want the end type to be in the other.
This is not how you generally want to convert between coordinate systems. For that,
you’ll want to use RigidBodyTransform
.
This is exactly equivalent to re-constructing the rotation with the same rotational angles
using <NewTo>
instead of <To>
, just more concise and legible. That is, it is exactly
equal to:
use sguaba::{system, math::Rotation};
use uom::si::f64::Angle;
use uom::si::angle::degree;
use approx::assert_relative_eq;
system!(struct PlaneFrd using NED);
system!(struct PlaneNedFromCrate1 using NED);
system!(struct PlaneNedFromCrate2 using NED);
// SAFETY
// we're claiming, without any factual basis for doing so,
// that this _is_ the plane's orientation.
let rotation_into_1 = unsafe {
Rotation::<PlaneFrd, PlaneNedFromCrate1>::from_tait_bryan_angles(
Angle::new::<degree>(30.),
Angle::new::<degree>(45.),
Angle::new::<degree>(90.),
)
};
let (yaw, pitch, roll) = rotation_into_1.to_tait_bryan_angles();
assert_relative_eq!(
unsafe { Rotation::<PlaneFrd, PlaneNedFromCrate2>::from_tait_bryan_angles(yaw, pitch, roll) },
unsafe { rotation_into_1.is_also_into::<PlaneNedFromCrate2>() }
);
§Safety
This asserts that the transform From
to To
is the same as the transform from From
to
NewTo
. However, if that is not the case, the resulting transform would allow violating
type safety by moving, say, a coordinate into NewTo
without correctly adjusting its
values.
Sourcepub unsafe fn is_also_from<NewFrom>(self) -> Rotation<NewFrom, To>
pub unsafe fn is_also_from<NewFrom>(self) -> Rotation<NewFrom, To>
Changes the origin coordinate system of the rotation to <NewFrom>
with no changes to the
rotational angles.
This is useful when you have two coordinate systems that you know have exactly equivalent axes, and you need the types to “work out”. This can be the case, for instance, when two crates have both separately declared a NED-like coordinate system centered on the same object, and you have a rotation into some coordinate system from one of them, but need to transform from something typed to be in the other.
This is not how you generally want to convert between coordinate systems. For that,
you’ll want to use RigidBodyTransform
.
This is exactly equivalent to re-constructing the rotation with the same rotational angles
using <NewFrom>
instead of <From>
, just more concise and legible. That is, it is exactly
equal to:
use sguaba::{system, math::Rotation};
use uom::si::f64::Angle;
use uom::si::angle::degree;
use approx::assert_relative_eq;
system!(struct PlaneFrd using NED);
system!(struct PlaneNedFromCrate1 using NED);
system!(struct PlaneNedFromCrate2 using NED);
// SAFETY
// we're claiming, without any factual basis for doing so,
// that this _is_ the plane's orientation.
let rotation_into_1 = unsafe {
Rotation::<PlaneNedFromCrate1, PlaneFrd>::from_tait_bryan_angles(
Angle::new::<degree>(30.),
Angle::new::<degree>(45.),
Angle::new::<degree>(90.),
)
};
let (yaw, pitch, roll) = rotation_into_1.to_tait_bryan_angles();
assert_relative_eq!(
unsafe { Rotation::<PlaneNedFromCrate2, PlaneFrd>::from_tait_bryan_angles(yaw, pitch, roll) },
unsafe { rotation_into_1.is_also_from::<PlaneNedFromCrate2>() }
);
§Safety
This asserts that the transform From
to To
is the same as the transform from NewFrom
to To
. However, if that is not the case, the resulting transform would allow violating
type safety by moving, say, a coordinate from NewFrom
without correctly adjusting its
values.
Sourcepub fn cast_type_of_from<AlsoFrom>(self) -> Rotation<AlsoFrom, To>where
From: EquivalentTo<AlsoFrom>,
pub fn cast_type_of_from<AlsoFrom>(self) -> Rotation<AlsoFrom, To>where
From: EquivalentTo<AlsoFrom>,
Casts the coordinate system type parameter From
of the rotation to the equivalent
coordinate system AlsoFrom
.
See EquivalentTo
for details on when this is useful (and safe).
Note that this performs no modifications to the transform itself, as that should be
unnecessary when EquivalentTo
is implemented.
Sourcepub fn cast_type_of_to<AlsoTo>(self) -> Rotation<From, AlsoTo>where
To: EquivalentTo<AlsoTo>,
pub fn cast_type_of_to<AlsoTo>(self) -> Rotation<From, AlsoTo>where
To: EquivalentTo<AlsoTo>,
Casts the coordinate system type parameter To
of the rotation to the equivalent
coordinate system AlsoTo
.
See EquivalentTo
for details on when this is useful (and safe).
Note that this performs no modifications to the rotation itself, as that should be
unnecessary when EquivalentTo
is implemented.
Sourcepub fn inverse(&self) -> Rotation<To, From>
pub fn inverse(&self) -> Rotation<To, From>
Returns the equal-but-opposite transform to this one.
That is, a rotation from the CoordinateSystem
To
into the coordinate system
From
.
Sourcepub fn nlerp(&self, rhs: &Self, t: f64) -> Self
pub fn nlerp(&self, rhs: &Self, t: f64) -> Self
Linearly interpolate between this rotation and another one.
Conceptually returns self * (1.0 - t) + rhs * t
, i.e., the linear blend of the two
rotations using the scalar value t
.
Note that this function inherently normalizes the underlying rotation such that it remains a unit quaternion.
The value for t
is not restricted to the range [0, 1].
Sourcepub fn to_tait_bryan_angles(&self) -> (Angle, Angle, Angle)
pub fn to_tait_bryan_angles(&self) -> (Angle, Angle, Angle)
Returns the yaw-pitch-roll Tait-Bryan angles that describe this rotation.
See Rotation::from_tait_bryan_angles
for documentation about the exact meaning of yaw,
pitch, and roll here.
The angles returned are, perhaps counter-intuitively but also in alignment with
Rotation::from_tait_bryan_angles
, the rotations that must be performed to go from To
into From
.
Sourcepub fn euler_angles(&self) -> (Angle, Angle, Angle)
pub fn euler_angles(&self) -> (Angle, Angle, Angle)
Returns the Euler angles describing this rotation.
The use of this method is discouraged as Eulers angles are both hard to work with and do not have a single, canonical definition across domains (eg, in aerospace).
The angles returned are, perhaps counter-intuitively but in alignment with
Rotation::from_tait_bryan_angles
, the rotations that must be performed to go
from To
into From
.
Sourcepub fn tait_bryan_builder() -> TaitBryanBuilder<NeedsYaw, Rotation<From, To>>
pub fn tait_bryan_builder() -> TaitBryanBuilder<NeedsYaw, Rotation<From, To>>
Provides a type-safe builder for constructing a rotation from Tait-Bryan angles.
This builder enforces the correct intrinsic order (yaw → pitch → roll) at compile time and provides named parameters to prevent argument order confusion.
§Examples
use sguaba::{system, math::Rotation};
use uom::si::{f64::Angle, angle::degree};
system!(struct PlaneNed using NED);
system!(struct PlaneFrd using FRD);
let rotation = unsafe {
Rotation::<PlaneNed, PlaneFrd>::tait_bryan_builder()
.yaw(Angle::new::<degree>(90.0))
.pitch(Angle::new::<degree>(45.0))
.roll(Angle::new::<degree>(5.0))
.build()
};
The following examples should fail to compile because the angles are not provided in the correct order:
// Cannot call pitch before yaw - pitch() method doesn't exist on NeedsYaw state
let rotation = unsafe {
Rotation::<PlaneNed, PlaneFrd>::tait_bryan_builder()
.pitch(Angle::new::<degree>(45.0))
.yaw(Angle::new::<degree>(90.0))
.roll(Angle::new::<degree>(5.0))
.build()
};
// Cannot call build before setting all angles - build() method doesn't exist on NeedsPitch state
let rotation = unsafe {
Rotation::<PlaneNed, PlaneFrd>::tait_bryan_builder()
.yaw(Angle::new::<degree>(90.0))
.build()
};
// Cannot call roll before pitch - roll() method doesn't exist on NeedsPitch state
let rotation = unsafe {
Rotation::<PlaneNed, PlaneFrd>::tait_bryan_builder()
.yaw(Angle::new::<degree>(90.0))
.roll(Angle::new::<degree>(5.0))
.pitch(Angle::new::<degree>(45.0))
.build()
};
§Safety
The builder’s build()
method is unsafe
for the same reasons as
from_tait_bryan_angles
: you are asserting
a relationship between coordinate systems From
and To
.
Source§impl<From, To> Rotation<From, To>
impl<From, To> Rotation<From, To>
Sourcepub fn transform<T>(&self, in_from: T) -> <T as Mul<Self>>::Outputwhere
T: Mul<Self>,
pub fn transform<T>(&self, in_from: T) -> <T as Mul<Self>>::Outputwhere
T: Mul<Self>,
Transforms an element in CoordinateSystem
From
into To
.
Sourcepub fn inverse_transform<T>(&self, in_to: T) -> <Self as Mul<T>>::Outputwhere
Self: Mul<T>,
pub fn inverse_transform<T>(&self, in_to: T) -> <Self as Mul<T>>::Outputwhere
Self: Mul<T>,
Transforms an element in CoordinateSystem
To
into From
.
This is equivalent to (but more efficient than) first inverting the transform with
RigidBodyTransform::inverse
and then calling RigidBodyTransform::transform
.
Trait Implementations§
Source§impl<From, To> AbsDiffEq for Rotation<From, To>
impl<From, To> AbsDiffEq for Rotation<From, To>
Source§fn default_epsilon() -> Self::Epsilon
fn default_epsilon() -> Self::Epsilon
Source§fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool
Source§fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool
fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool
AbsDiffEq::abs_diff_eq
.Source§impl<'de, From, To> Deserialize<'de> for Rotation<From, To>
impl<'de, From, To> Deserialize<'de> for Rotation<From, To>
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<From, To> Mul<Bearing<To>> for Rotation<From, To>where
From: BearingDefined,
To: BearingDefined,
impl<From, To> Mul<Bearing<To>> for Rotation<From, To>where
From: BearingDefined,
To: BearingDefined,
Source§impl<From, To> Mul<Coordinate<To>> for Rotation<From, To>
impl<From, To> Mul<Coordinate<To>> for Rotation<From, To>
Source§type Output = Coordinate<From>
type Output = Coordinate<From>
*
operator.Source§impl<From, To> Mul<Orientation<To>> for Rotation<From, To>
impl<From, To> Mul<Orientation<To>> for Rotation<From, To>
Source§type Output = Orientation<From>
type Output = Orientation<From>
*
operator.Source§impl<From, To> Mul<Rotation<From, To>> for Bearing<From>where
From: BearingDefined,
To: BearingDefined,
impl<From, To> Mul<Rotation<From, To>> for Bearing<From>where
From: BearingDefined,
To: BearingDefined,
Source§impl<From, To> Mul<Rotation<From, To>> for Coordinate<From>
impl<From, To> Mul<Rotation<From, To>> for Coordinate<From>
Source§impl<From, To> Mul<Rotation<From, To>> for Orientation<From>
impl<From, To> Mul<Rotation<From, To>> for Orientation<From>
Source§impl<From, Over, To> Mul<Rotation<Over, To>> for RigidBodyTransform<From, Over>
impl<From, Over, To> Mul<Rotation<Over, To>> for RigidBodyTransform<From, Over>
Source§impl<From, To> RelativeEq for Rotation<From, To>
impl<From, To> RelativeEq for Rotation<From, To>
Source§fn default_max_relative() -> Self::Epsilon
fn default_max_relative() -> Self::Epsilon
Source§fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool
fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool
Source§fn relative_ne(
&self,
other: &Rhs,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool
fn relative_ne( &self, other: &Rhs, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool
RelativeEq::relative_eq
.impl<From, To> Copy for Rotation<From, To>
Auto Trait Implementations§
impl<From, To> Freeze for Rotation<From, To>
impl<From, To> RefUnwindSafe for Rotation<From, To>where
From: RefUnwindSafe,
To: RefUnwindSafe,
impl<From, To> Send for Rotation<From, To>
impl<From, To> Sync for Rotation<From, To>
impl<From, To> Unpin for Rotation<From, To>
impl<From, To> UnwindSafe for Rotation<From, To>where
From: UnwindSafe,
To: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self
from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self
is actually part of its subset T
(and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset
but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self
to the equivalent element of its superset.