Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotation api extension #15169

Merged
merged 4 commits into from
Sep 16, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions crates/bevy_math/src/rotation2d.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::f32::consts::TAU;

use glam::FloatExt;

use crate::{
Expand Down Expand Up @@ -100,18 +102,36 @@ impl Rot2 {
};

/// Creates a [`Rot2`] from a counterclockwise angle in radians.
///
/// # Note
///
/// The resulting rotation will always be clamped to the range (-PI, PI] by design
RobWalt marked this conversation as resolved.
Show resolved Hide resolved
#[inline]
pub fn radians(radians: f32) -> Self {
let (sin, cos) = ops::sin_cos(radians);
Self::from_sin_cos(sin, cos)
}

/// Creates a [`Rot2`] from a counterclockwise angle in degrees.
///
/// # Note
///
/// The resulting rotation will always be clamped to the range (-180.0, 180.0] by design
#[inline]
pub fn degrees(degrees: f32) -> Self {
Self::radians(degrees.to_radians())
}

/// Creates a [`Rot2`] from a counterclockwise fraction of a full turn of 360 degrees.
///
/// # Note
///
/// The resulting rotation will always be clamped to the range (-0.5, 0.5] by design
#[inline]
pub fn turn_fraction(fraction: f32) -> Self {
RobWalt marked this conversation as resolved.
Show resolved Hide resolved
Self::radians(TAU * fraction)
}

/// Creates a [`Rot2`] from the sine and cosine of an angle in radians.
///
/// The rotation is only valid if `sin * sin + cos * cos == 1.0`.
Expand Down Expand Up @@ -141,6 +161,12 @@ impl Rot2 {
self.as_radians().to_degrees()
}

/// Returns the rotation as a fraction of a full 360 degree turn.
#[inline]
pub fn as_turn_fraction(self) -> f32 {
self.as_radians() / TAU
}

/// Returns the sine and cosine of the rotation angle in radians.
#[inline]
pub const fn sin_cos(self) -> (f32, f32) {
Expand Down Expand Up @@ -437,25 +463,31 @@ impl approx::UlpsEq for Rot2 {

#[cfg(test)]
mod tests {
use std::f32::consts::FRAC_PI_2;

use approx::assert_relative_eq;

use crate::{Dir2, Rot2, Vec2};

#[test]
fn creation() {
let rotation1 = Rot2::radians(std::f32::consts::FRAC_PI_2);
let rotation1 = Rot2::radians(FRAC_PI_2);
let rotation2 = Rot2::degrees(90.0);
let rotation3 = Rot2::from_sin_cos(1.0, 0.0);
let rotation4 = Rot2::turn_fraction(0.25);

// All three rotations should be equal
assert_relative_eq!(rotation1.sin, rotation2.sin);
assert_relative_eq!(rotation1.cos, rotation2.cos);
assert_relative_eq!(rotation1.sin, rotation3.sin);
assert_relative_eq!(rotation1.cos, rotation3.cos);
assert_relative_eq!(rotation1.sin, rotation4.sin);
assert_relative_eq!(rotation1.cos, rotation4.cos);

// The rotation should be 90 degrees
assert_relative_eq!(rotation1.as_radians(), std::f32::consts::FRAC_PI_2);
assert_relative_eq!(rotation1.as_radians(), FRAC_PI_2);
assert_relative_eq!(rotation1.as_degrees(), 90.0);
assert_relative_eq!(rotation1.as_turn_fraction(), 0.25);
}

#[test]
Expand All @@ -466,12 +498,21 @@ mod tests {
assert_relative_eq!(rotation * Dir2::Y, Dir2::NEG_X);
}

#[test]
fn rotation_range() {
// the rotation range is `(-180, 180]` and the constructors normalize the rotations to that
// range
RobWalt marked this conversation as resolved.
Show resolved Hide resolved
assert_relative_eq!(Rot2::radians(3.0 * FRAC_PI_2), Rot2::radians(-FRAC_PI_2));
assert_relative_eq!(Rot2::degrees(270.0), Rot2::degrees(-90.0));
assert_relative_eq!(Rot2::turn_fraction(0.75), Rot2::turn_fraction(-0.25));
}

#[test]
fn add() {
let rotation1 = Rot2::degrees(90.0);
let rotation2 = Rot2::degrees(180.0);

// 90 deg + 180 deg becomes -90 deg after it wraps around to be within the ]-180, 180] range
// 90 deg + 180 deg becomes -90 deg after it wraps around to be within the `(-180, 180]` range
assert_eq!((rotation1 * rotation2).as_degrees(), -90.0);
}

Expand Down