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

Add Radians and Degrees types and Angle trait #11148

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
40 changes: 26 additions & 14 deletions crates/bevy_gizmos/src/arcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use crate::circles::DEFAULT_CIRCLE_SEGMENTS;
use crate::prelude::Gizmos;
use bevy_math::Vec2;
use bevy_math::{Angle, Radians, Vec2};
use bevy_render::color::Color;
use std::f32::consts::TAU;

Expand All @@ -17,9 +17,9 @@ impl<'s> Gizmos<'s> {
/// # Arguments
/// - `position` sets the center of this circle.
/// - `radius` controls the distance from `position` to this arc, and thus its curvature.
/// - `direction_angle` sets the clockwise angle in radians between `Vec2::Y` and
/// - `direction_angle` sets the clockwise angle between `Vec2::Y` and
/// the vector from `position` to the midpoint of the arc.
/// - `arc_angle` sets the length of this arc, in radians.
/// - `arc_angle` sets the length of this arc.
///
/// # Example
/// ```
Expand All @@ -28,12 +28,24 @@ impl<'s> Gizmos<'s> {
/// # use bevy_math::prelude::*;
/// # use std::f32::consts::PI;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.arc_2d(Vec2::ZERO, 0., PI / 4., 1., Color::GREEN);
/// gizmos.arc_2d(
/// Vec2::ZERO,
/// Radians::ZERO,
/// Radians::FRAC_PI_4,
/// 1.,
/// Color::GREEN
/// );
///
/// // Arcs have 32 line-segments by default.
/// // You may want to increase this for larger arcs.
/// gizmos
/// .arc_2d(Vec2::ZERO, 0., PI / 4., 5., Color::RED)
/// .arc_2d(
/// Vec2::ZERO,
/// Radians::ZERO,
/// Radians::FRAC_PI_4,
/// 5.,
/// Color::RED
/// )
/// .segments(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
Expand All @@ -42,16 +54,16 @@ impl<'s> Gizmos<'s> {
pub fn arc_2d(
&mut self,
position: Vec2,
direction_angle: f32,
arc_angle: f32,
direction_angle: impl Into<Radians>,
arc_angle: impl Into<Radians>,
radius: f32,
color: Color,
) -> Arc2dBuilder<'_, 's> {
Arc2dBuilder {
gizmos: self,
position,
direction_angle,
arc_angle,
direction_angle: direction_angle.into(),
arc_angle: arc_angle.into(),
radius,
color,
segments: None,
Expand All @@ -63,8 +75,8 @@ impl<'s> Gizmos<'s> {
pub struct Arc2dBuilder<'a, 's> {
gizmos: &'a mut Gizmos<'s>,
position: Vec2,
direction_angle: f32,
arc_angle: f32,
direction_angle: Radians,
arc_angle: Radians,
radius: f32,
color: Color,
segments: Option<usize>,
Expand All @@ -84,7 +96,7 @@ impl Drop for Arc2dBuilder<'_, '_> {
Some(segments) => segments,
// Do a linear interpolation between 1 and `DEFAULT_CIRCLE_SEGMENTS`
// using the arc angle as scalar.
None => ((self.arc_angle.abs() / TAU) * DEFAULT_CIRCLE_SEGMENTS as f32).ceil() as usize,
None => ((self.arc_angle.0 / TAU) * DEFAULT_CIRCLE_SEGMENTS as f32).ceil() as usize,
Jondolf marked this conversation as resolved.
Show resolved Hide resolved
};

let positions = arc_inner(self.direction_angle, self.arc_angle, self.radius, segments)
Expand All @@ -94,8 +106,8 @@ impl Drop for Arc2dBuilder<'_, '_> {
}

fn arc_inner(
direction_angle: f32,
arc_angle: f32,
direction_angle: Radians,
arc_angle: Radians,
radius: f32,
segments: usize,
) -> impl Iterator<Item = Vec2> {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_gizmos/src/circles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
//! and assorted support items.

use crate::prelude::Gizmos;
use bevy_math::{Quat, Vec2, Vec3};
use bevy_math::{Angle, Quat, Radians, Vec2, Vec3};
use bevy_render::color::Color;
use std::f32::consts::TAU;

pub(crate) const DEFAULT_CIRCLE_SEGMENTS: usize = 32;

fn circle_inner(radius: f32, segments: usize) -> impl Iterator<Item = Vec2> {
(0..segments + 1).map(move |i| {
let angle = i as f32 * TAU / segments as f32;
let angle = Radians(i as f32 * TAU / segments as f32);
Jondolf marked this conversation as resolved.
Show resolved Hide resolved
Vec2::from(angle.sin_cos()) * radius
})
}
Expand Down
240 changes: 240 additions & 0 deletions crates/bevy_math/src/angle/degrees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use std::{
fmt::Debug,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
};

use crate::{Angle, Radians};

/// An angle in degrees.
///
/// # Example
///
/// ```
/// use std::f32::consts::PI;
/// use bevy_math::{Angle, Degrees, Radians};
///
/// // Create angles from radians or degrees
/// let alpha = Degrees(180.0);
/// let beta = Degrees::from_radians(PI);
/// assert_eq!(alpha, beta);
/// assert_eq!(beta, Radians(PI));
///
/// // Add degrees and radians together (result is always radians)
/// assert_eq!(Radians(PI) + Degrees(180.0), Radians(2.0 * PI));
///
/// // Get float values
/// assert_eq!(alpha.0, 180.0);
/// assert_eq!(alpha.to_radians().0, PI);
///
/// // Use trigonometric operations
/// assert_eq!(alpha.cos(), -1.0);
///
/// // Normalize 540 degrees to range [0, 360) to get 180 degrees
/// let gamma = 3.0 * alpha;
/// let normalized = gamma.normalized();
///
/// // Small threshold for floating point error
/// assert!((normalized - alpha).abs() < Degrees(0.000001));
/// ```
// TODO: Reflect
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Degrees(pub f32);

impl Degrees {
/// Creates an angle in [`Degrees`] from radians.
#[inline]
pub fn from_radians(radians: f32) -> Self {
Self(radians.to_degrees())
}

/// Returns the angle in [`Radians`].
#[inline]
pub fn to_radians(self) -> Radians {
Radians::from_degrees(self.0)
}
}

impl Angle for Degrees {
const ZERO: Self = Self(0.0);
const EIGHTH: Self = Self(45.0);
const QUARTER: Self = Self(90.0);
const HALF: Self = Self(180.0);
const FULL: Self = Self(360.0);

#[inline]
fn new(angle: f32) -> Self {
Self(angle)
}

#[inline]
fn value(self) -> f32 {
self.0
}

#[inline]
fn sin(self) -> f32 {
self.to_radians().sin()
}

#[inline]
fn cos(self) -> f32 {
self.to_radians().cos()
}

#[inline]
fn tan(self) -> f32 {
self.to_radians().tan()
}

#[inline]
fn sin_cos(self) -> (f32, f32) {
self.to_radians().sin_cos()
}
}

impl From<Radians> for Degrees {
fn from(angle: Radians) -> Self {
angle.to_degrees()
}
}

impl Add for Degrees {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}

impl Sub for Degrees {
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}

impl Mul for Degrees {
type Output = Self;

fn mul(self, rhs: Degrees) -> Self::Output {
Self(self.0 * rhs.0)
}
}

impl Mul<f32> for Degrees {
type Output = Self;

fn mul(self, rhs: f32) -> Self::Output {
Self(self.0 * rhs)
}
}

impl Mul<Degrees> for f32 {
type Output = Degrees;

fn mul(self, rhs: Degrees) -> Self::Output {
Degrees(self * rhs.0)
}
}

impl Div for Degrees {
type Output = Self;

fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}

impl Div<f32> for Degrees {
type Output = Self;

fn div(self, rhs: f32) -> Self::Output {
Self(self.0 / rhs)
}
}

impl Rem for Degrees {
type Output = Self;

fn rem(self, rhs: Self) -> Self::Output {
Self(self.0 % rhs.0)
}
}

impl Rem<f32> for Degrees {
type Output = Self;

fn rem(self, rhs: f32) -> Self::Output {
Self(self.0 % rhs)
}
}

impl Neg for Degrees {
type Output = Self;

fn neg(self) -> Self::Output {
Self(-self.0)
}
}

impl AddAssign for Degrees {
fn add_assign(&mut self, rhs: Degrees) {
self.0 += rhs.0;
}
}

impl AddAssign<f32> for Degrees {
fn add_assign(&mut self, rhs: f32) {
self.0 += rhs;
}
}

impl SubAssign for Degrees {
fn sub_assign(&mut self, rhs: Degrees) {
self.0 -= rhs.0;
}
}

impl SubAssign<f32> for Degrees {
fn sub_assign(&mut self, rhs: f32) {
self.0 -= rhs;
}
}

impl MulAssign for Degrees {
fn mul_assign(&mut self, rhs: Degrees) {
self.0 *= rhs.0;
}
}

impl MulAssign<f32> for Degrees {
fn mul_assign(&mut self, rhs: f32) {
self.0 *= rhs;
}
}

impl DivAssign for Degrees {
fn div_assign(&mut self, rhs: Degrees) {
self.0 /= rhs.0;
}
}

impl DivAssign<f32> for Degrees {
fn div_assign(&mut self, rhs: f32) {
self.0 /= rhs;
}
}

impl RemAssign for Degrees {
fn rem_assign(&mut self, rhs: Degrees) {
self.0 %= rhs.0;
}
}

impl RemAssign<f32> for Degrees {
fn rem_assign(&mut self, rhs: f32) {
self.0 %= rhs;
}
}
Loading