From 39609f1708c1ec779e3b45cce6dfdaebfa592755 Mon Sep 17 00:00:00 2001 From: Matty Date: Tue, 4 Jun 2024 10:44:29 -0400 Subject: [PATCH] `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. --- crates/bevy_math/src/direction.rs | 60 ++++++++++++++++++++++++++++++ crates/bevy_math/src/rotation2d.rs | 4 +- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/crates/bevy_math/src/direction.rs b/crates/bevy_math/src/direction.rs index 9723d945f9..7ce5121fe0 100644 --- a/crates/bevy_math/src/direction.rs +++ b/crates/bevy_math/src/direction.rs @@ -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 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)); diff --git a/crates/bevy_math/src/rotation2d.rs b/crates/bevy_math/src/rotation2d.rs index e1cbcac399..6b41f67442 100644 --- a/crates/bevy_math/src/rotation2d.rs +++ b/crates/bevy_math/src/rotation2d.rs @@ -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 ///