mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Rotation api extension (#15169)
# Objective - Another way of specifying rotations was requested in https://github.com/bevyengine/bevy/issues/11132#issuecomment-2344603178 ## Solution - Add methods on `Rot2` - `turn_fraction(fraction: f32) -> Self` - `as_turn_fraction(self) -> f32` - Also add some documentation on range of rotation ## Testing - extended existing tests - added new tests ## Showcase ```rust let rotation1 = Rot2::degrees(90.0); let rotation2 = Rot2::turn_fraction(0.25); // rotations should be equal assert_relative_eq!(rotation1, rotation2); // The rotation should be 90 degrees assert_relative_eq!(rotation2.as_radians(), FRAC_PI_2); assert_relative_eq!(rotation2.as_degrees(), 90.0); ``` --------- Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
This commit is contained in:
parent
17b1bcde95
commit
29c4c79342
1 changed files with 90 additions and 3 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
use glam::FloatExt;
|
use glam::FloatExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -100,6 +102,26 @@ impl Rot2 {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Creates a [`Rot2`] from a counterclockwise angle in radians.
|
/// Creates a [`Rot2`] from a counterclockwise angle in radians.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The input rotation will always be clamped to the range `(-π, π]` by design.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_math::Rot2;
|
||||||
|
/// # use approx::assert_relative_eq;
|
||||||
|
/// # use std::f32::consts::{FRAC_PI_2, PI};
|
||||||
|
///
|
||||||
|
/// let rot1 = Rot2::radians(3.0 * FRAC_PI_2);
|
||||||
|
/// let rot2 = Rot2::radians(-FRAC_PI_2);
|
||||||
|
/// assert_relative_eq!(rot1, rot2);
|
||||||
|
///
|
||||||
|
/// let rot3 = Rot2::radians(PI);
|
||||||
|
/// assert_relative_eq!(rot1 * rot1, rot3);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn radians(radians: f32) -> Self {
|
pub fn radians(radians: f32) -> Self {
|
||||||
let (sin, cos) = ops::sin_cos(radians);
|
let (sin, cos) = ops::sin_cos(radians);
|
||||||
|
@ -107,11 +129,55 @@ impl Rot2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`Rot2`] from a counterclockwise angle in degrees.
|
/// Creates a [`Rot2`] from a counterclockwise angle in degrees.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The input rotation will always be clamped to the range `(-180°, 180°]` by design.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_math::Rot2;
|
||||||
|
/// # use approx::assert_relative_eq;
|
||||||
|
///
|
||||||
|
/// let rot1 = Rot2::degrees(270.0);
|
||||||
|
/// let rot2 = Rot2::degrees(-90.0);
|
||||||
|
/// assert_relative_eq!(rot1, rot2);
|
||||||
|
///
|
||||||
|
/// let rot3 = Rot2::degrees(180.0);
|
||||||
|
/// assert_relative_eq!(rot1 * rot1, rot3);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrees(degrees: f32) -> Self {
|
pub fn degrees(degrees: f32) -> Self {
|
||||||
Self::radians(degrees.to_radians())
|
Self::radians(degrees.to_radians())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Rot2`] from a counterclockwise fraction of a full turn of 360 degrees.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The input rotation will always be clamped to the range `(-50%, 50%]` by design.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_math::Rot2;
|
||||||
|
/// # use approx::assert_relative_eq;
|
||||||
|
///
|
||||||
|
/// let rot1 = Rot2::turn_fraction(0.75);
|
||||||
|
/// let rot2 = Rot2::turn_fraction(-0.25);
|
||||||
|
/// assert_relative_eq!(rot1, rot2);
|
||||||
|
///
|
||||||
|
/// let rot3 = Rot2::turn_fraction(0.5);
|
||||||
|
/// assert_relative_eq!(rot1 * rot1, rot3);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn turn_fraction(fraction: f32) -> Self {
|
||||||
|
Self::radians(TAU * fraction)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [`Rot2`] from the sine and cosine of an angle in radians.
|
/// 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`.
|
/// The rotation is only valid if `sin * sin + cos * cos == 1.0`.
|
||||||
|
@ -141,6 +207,12 @@ impl Rot2 {
|
||||||
self.as_radians().to_degrees()
|
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.
|
/// Returns the sine and cosine of the rotation angle in radians.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn sin_cos(self) -> (f32, f32) {
|
pub const fn sin_cos(self) -> (f32, f32) {
|
||||||
|
@ -437,25 +509,31 @@ impl approx::UlpsEq for Rot2 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
use approx::assert_relative_eq;
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
use crate::{Dir2, Rot2, Vec2};
|
use crate::{Dir2, Rot2, Vec2};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn creation() {
|
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 rotation2 = Rot2::degrees(90.0);
|
||||||
let rotation3 = Rot2::from_sin_cos(1.0, 0.0);
|
let rotation3 = Rot2::from_sin_cos(1.0, 0.0);
|
||||||
|
let rotation4 = Rot2::turn_fraction(0.25);
|
||||||
|
|
||||||
// All three rotations should be equal
|
// All three rotations should be equal
|
||||||
assert_relative_eq!(rotation1.sin, rotation2.sin);
|
assert_relative_eq!(rotation1.sin, rotation2.sin);
|
||||||
assert_relative_eq!(rotation1.cos, rotation2.cos);
|
assert_relative_eq!(rotation1.cos, rotation2.cos);
|
||||||
assert_relative_eq!(rotation1.sin, rotation3.sin);
|
assert_relative_eq!(rotation1.sin, rotation3.sin);
|
||||||
assert_relative_eq!(rotation1.cos, rotation3.cos);
|
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
|
// 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_degrees(), 90.0);
|
||||||
|
assert_relative_eq!(rotation1.as_turn_fraction(), 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -466,12 +544,21 @@ mod tests {
|
||||||
assert_relative_eq!(rotation * Dir2::Y, Dir2::NEG_X);
|
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
|
||||||
|
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]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let rotation1 = Rot2::degrees(90.0);
|
let rotation1 = Rot2::degrees(90.0);
|
||||||
let rotation2 = Rot2::degrees(180.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);
|
assert_eq!((rotation1 * rotation2).as_degrees(), -90.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue