Dir2 -> Rotation2d conversions (#13670)

# Objective

Filling a hole in the API: Previously, there was no particularly
ergonomic way to go from, e.g., a pair of directions to the rotation
that links them.

## Solution

We introduce a small suite of API methods to `Dir2` to address this:
```rust
/// Get the rotation that rotates this direction to `other`.
pub fn rotation_to(self, other: Self) -> Rotation2d { //... }

/// Get the rotation that rotates `other` to this direction.
pub fn rotation_from(self, other: Self) -> Rotation2d { //... }

/// Get the rotation that rotates the X-axis to this direction.
pub fn rotation_from_x(self) -> Rotation2d { //... }

/// Get the rotation that rotates this direction to the X-axis.
pub fn rotation_to_x(self) -> Rotation2d { //... }

/// Get the rotation that rotates this direction to the Y-axis.
pub fn rotation_from_y(self) -> Rotation2d { //... }

/// Get the rotation that rotates the Y-axis to this direction.
pub fn rotation_to_y(self) -> Rotation2d { //... }
```

I also removed some language from the `Rotation2d` docs that is
misleading: the radian and angle conversion functions are already clear
about which angles they spit out, and `Rotation2d` itself doesn't have
any bounds on angles or anything.
This commit is contained in:
Matty 2024-06-04 10:44:29 -04:00 committed by GitHub
parent 49338245ea
commit 39609f1708
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 3 deletions

View File

@ -202,6 +202,47 @@ impl Dir2 {
let angle = self.angle_between(rhs.0);
Rotation2d::radians(angle * s) * self
}
/// Get the rotation that rotates this direction to `other`.
#[inline]
pub fn rotation_to(self, other: Self) -> Rotation2d {
// Rotate `self` to X-axis, then X-axis to `other`:
other.rotation_from_x() * self.rotation_to_x()
}
/// Get the rotation that rotates `other` to this direction.
#[inline]
pub fn rotation_from(self, other: Self) -> Rotation2d {
other.rotation_to(self)
}
/// Get the rotation that rotates the X-axis to this direction.
#[inline]
pub fn rotation_from_x(self) -> Rotation2d {
Rotation2d::from_sin_cos(self.0.y, self.0.x)
}
/// Get the rotation that rotates this direction to the X-axis.
#[inline]
pub fn rotation_to_x(self) -> Rotation2d {
// (This is cheap, it just negates one component.)
self.rotation_from_x().inverse()
}
/// Get the rotation that rotates this direction to the Y-axis.
#[inline]
pub fn rotation_from_y(self) -> Rotation2d {
// `x <- y`, `y <- -x` correspond to rotating clockwise by pi/2;
// this transforms the Y-axis into the X-axis, maintaining the relative position
// of our direction. Then we just use the same technique as `rotation_from_x`.
Rotation2d::from_sin_cos(-self.0.x, self.0.y)
}
/// Get the rotation that rotates the Y-axis to this direction.
#[inline]
pub fn rotation_to_y(self) -> Rotation2d {
self.rotation_from_y().inverse()
}
}
impl TryFrom<Vec2> for Dir2 {
@ -764,6 +805,25 @@ mod tests {
);
}
#[test]
fn dir2_to_rotation2d() {
assert_relative_eq!(
Dir2::EAST.rotation_to(Dir2::NORTH_EAST),
Rotation2d::FRAC_PI_4
);
assert_relative_eq!(
Dir2::NORTH.rotation_from(Dir2::NORTH_EAST),
Rotation2d::FRAC_PI_4
);
assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rotation2d::FRAC_PI_2);
assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rotation2d::PI);
assert_relative_eq!(
Dir2::NORTH_WEST.rotation_from_x(),
Rotation2d::degrees(135.0)
);
assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rotation2d::FRAC_PI_4);
}
#[test]
fn dir3_creation() {
assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));

View File

@ -7,9 +7,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// A counterclockwise 2D rotation in radians.
///
/// The rotation angle is wrapped to be within the `(-pi, pi]` range.
/// A counterclockwise 2D rotation.
///
/// # Example
///