diff --git a/crates/bevy_gizmos/src/circles.rs b/crates/bevy_gizmos/src/circles.rs index f8b48753b46a1..f1bfa80f7ddc8 100644 --- a/crates/bevy_gizmos/src/circles.rs +++ b/crates/bevy_gizmos/src/circles.rs @@ -5,7 +5,7 @@ use crate::prelude::{GizmoConfigGroup, Gizmos}; use bevy_math::Mat2; -use bevy_math::{primitives::Direction3d, Quat, Vec2, Vec3}; +use bevy_math::{Dir3, Quat, Vec2, Vec3}; use bevy_render::color::Color; use std::f32::consts::TAU; @@ -106,12 +106,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { /// # use bevy_render::prelude::*; /// # use bevy_math::prelude::*; /// fn system(mut gizmos: Gizmos) { - /// gizmos.circle(Vec3::ZERO, Direction3d::Z, 1., Color::GREEN); + /// gizmos.circle(Vec3::ZERO, Dir3::Z, 1., Color::GREEN); /// /// // Circles have 32 line-segments by default. /// // You may want to increase this for larger circles. /// gizmos - /// .circle(Vec3::ZERO, Direction3d::Z, 5., Color::RED) + /// .circle(Vec3::ZERO, Dir3::Z, 5., Color::RED) /// .segments(64); /// } /// # bevy_ecs::system::assert_is_system(system); @@ -120,7 +120,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { pub fn circle( &mut self, position: Vec3, - normal: Direction3d, + normal: Dir3, radius: f32, color: Color, ) -> EllipseBuilder<'_, 'w, 's, T> { diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 729d53b40fe12..ca41e969e2c62 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ system::{Deferred, ReadOnlySystemParam, Res, Resource, SystemBuffer, SystemMeta, SystemParam}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -use bevy_math::{primitives::Direction3d, Mat2, Quat, Vec2, Vec3}; +use bevy_math::{Dir3, Mat2, Quat, Vec2, Vec3}; use bevy_render::color::Color; use bevy_transform::TransformPoint; @@ -622,7 +622,7 @@ impl Drop for SphereBuilder<'_, '_, '_, T> { self.gizmos .circle( self.position, - Direction3d::new_unchecked(self.rotation * axis), + Dir3::new_unchecked(self.rotation * axis), self.radius, self.color, ) diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_gizmos/src/primitives/dim2.rs index 1922a9de6c932..f08988a18b5fd 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_gizmos/src/primitives/dim2.rs @@ -5,10 +5,10 @@ use std::f32::consts::PI; use super::helpers::*; use bevy_math::primitives::{ - BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Direction2d, Ellipse, Line2d, Plane2d, - Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, + BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon, + Polyline2d, Primitive2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, }; -use bevy_math::{Mat2, Vec2}; +use bevy_math::{Dir2, Mat2, Vec2}; use bevy_render::color::Color; use crate::prelude::{GizmoConfigGroup, Gizmos}; @@ -38,12 +38,12 @@ pub trait GizmoPrimitive2d { // direction 2d -impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d for Gizmos<'w, 's, T> { +impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d for Gizmos<'w, 's, T> { type Output<'a> = () where Self : 'a; fn primitive_2d( &mut self, - primitive: Direction2d, + primitive: Dir2, position: Vec2, angle: f32, color: Color, @@ -163,7 +163,7 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d for Gizmos<'w, 's, pub struct Line2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { gizmos: &'a mut Gizmos<'w, 's, T>, - direction: Direction2d, // Direction of the line + direction: Dir2, // Direction of the line position: Vec2, // position of the center of the line rotation: Mat2, // rotation of the line @@ -262,7 +262,7 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d for Gizmos<'w, 's, T .draw_arrow(true); // draw the plane line - let direction = Direction2d::new_unchecked(-normal.perp()); + let direction = Dir2::new_unchecked(-normal.perp()); self.primitive_2d(Line2d { direction }, position, angle, color) .draw_arrow(false); @@ -282,8 +282,8 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d for Gizmos<'w, 's, T pub struct Segment2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { gizmos: &'a mut Gizmos<'w, 's, T>, - direction: Direction2d, // Direction of the line segment - half_length: f32, // Half-length of the line segment + direction: Dir2, // Direction of the line segment + half_length: f32, // Half-length of the line segment position: Vec2, // position of the center of the line segment rotation: Mat2, // rotation of the line segment diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 3fd3bfdafba90..2eaba5a5dbab1 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -4,10 +4,10 @@ use super::helpers::*; use std::f32::consts::TAU; use bevy_math::primitives::{ - BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d, - Plane3d, Polyline3d, Primitive3d, Segment3d, Sphere, Torus, + BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, + Polyline3d, Primitive3d, Segment3d, Sphere, Torus, }; -use bevy_math::{Quat, Vec3}; +use bevy_math::{Dir3, Quat, Vec3}; use bevy_render::color::Color; use crate::prelude::{GizmoConfigGroup, Gizmos}; @@ -35,12 +35,12 @@ pub trait GizmoPrimitive3d { // direction 3d -impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d for Gizmos<'w, 's, T> { +impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d for Gizmos<'w, 's, T> { type Output<'a> = () where Self: 'a; fn primitive_3d( &mut self, - primitive: Direction3d, + primitive: Dir3, position: Vec3, rotation: Quat, color: Color, @@ -139,7 +139,7 @@ pub struct Plane3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { gizmos: &'a mut Gizmos<'w, 's, T>, // direction of the normal orthogonal to the plane - normal: Direction3d, + normal: Dir3, // Rotation of the sphere around the origin in 3D space rotation: Quat, @@ -218,7 +218,7 @@ impl Drop for Plane3dBuilder<'_, '_, '_, T> { .map(|angle| Quat::from_axis_angle(normal, angle)) .for_each(|quat| { let axis_direction = quat * normals_normal; - let direction = Direction3d::new_unchecked(axis_direction); + let direction = Dir3::new_unchecked(axis_direction); // for each axis draw dotted line (0..) diff --git a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs index ee942e52dd73e..583314072bdd9 100644 --- a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs @@ -1,10 +1,11 @@ //! Contains [`Bounded2d`] implementations for [geometric primitives](crate::primitives). -use glam::{Mat2, Vec2}; - -use crate::primitives::{ - BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Direction2d, Ellipse, Line2d, Plane2d, - Polygon, Polyline2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, +use crate::{ + primitives::{ + BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon, + Polyline2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, + }, + {Dir2, Mat2, Vec2}, }; use super::{Aabb2d, Bounded2d, BoundingCircle}; @@ -235,7 +236,7 @@ impl Bounded2d for Capsule2d { // Get the line segment between the hemicircles of the rotated capsule let segment = Segment2d { // Multiplying a normalized vector (Vec2::Y) with a rotation returns a normalized vector. - direction: Direction2d::new_unchecked(Mat2::from_angle(rotation) * Vec2::Y), + direction: Dir2::new_unchecked(Mat2::from_angle(rotation) * Vec2::Y), half_length: self.half_length, }; let (a, b) = (segment.point1(), segment.point2()); @@ -262,9 +263,10 @@ mod tests { use crate::{ bounding::Bounded2d, primitives::{ - Capsule2d, Circle, Direction2d, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, - Rectangle, RegularPolygon, Segment2d, Triangle2d, + Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Rectangle, + RegularPolygon, Segment2d, Triangle2d, }, + Dir2, }; #[test] @@ -320,31 +322,22 @@ mod tests { fn line() { let translation = Vec2::new(2.0, 1.0); - let aabb1 = Line2d { - direction: Direction2d::Y, - } - .aabb_2d(translation, 0.0); + let aabb1 = Line2d { direction: Dir2::Y }.aabb_2d(translation, 0.0); assert_eq!(aabb1.min, Vec2::new(2.0, -f32::MAX / 2.0)); assert_eq!(aabb1.max, Vec2::new(2.0, f32::MAX / 2.0)); - let aabb2 = Line2d { - direction: Direction2d::X, - } - .aabb_2d(translation, 0.0); + let aabb2 = Line2d { direction: Dir2::X }.aabb_2d(translation, 0.0); assert_eq!(aabb2.min, Vec2::new(-f32::MAX / 2.0, 1.0)); assert_eq!(aabb2.max, Vec2::new(f32::MAX / 2.0, 1.0)); let aabb3 = Line2d { - direction: Direction2d::from_xy(1.0, 1.0).unwrap(), + direction: Dir2::from_xy(1.0, 1.0).unwrap(), } .aabb_2d(translation, 0.0); assert_eq!(aabb3.min, Vec2::new(-f32::MAX / 2.0, -f32::MAX / 2.0)); assert_eq!(aabb3.max, Vec2::new(f32::MAX / 2.0, f32::MAX / 2.0)); - let bounding_circle = Line2d { - direction: Direction2d::Y, - } - .bounding_circle(translation, 0.0); + let bounding_circle = Line2d { direction: Dir2::Y }.bounding_circle(translation, 0.0); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), f32::MAX / 2.0); } diff --git a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs index 6730254793877..d3a8d02e9637a 100644 --- a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs @@ -5,9 +5,10 @@ use glam::{Mat3, Quat, Vec2, Vec3}; use crate::{ bounding::{Bounded2d, BoundingCircle}, primitives::{ - BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d, - Plane3d, Polyline3d, Segment3d, Sphere, Torus, Triangle2d, + BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, + Polyline3d, Segment3d, Sphere, Torus, Triangle2d, }, + Dir3, }; use super::{Aabb3d, Bounded3d, BoundingSphere}; @@ -151,7 +152,7 @@ impl Bounded3d for Capsule3d { // Get the line segment between the hemispheres of the rotated capsule let segment = Segment3d { // Multiplying a normalized vector (Vec3::Y) with a rotation returns a normalized vector. - direction: Direction3d::new_unchecked(rotation * Vec3::Y), + direction: Dir3::new_unchecked(rotation * Vec3::Y), half_length: self.half_length, }; let (a, b) = (segment.point1(), segment.point2()); @@ -311,9 +312,10 @@ mod tests { use crate::{ bounding::Bounded3d, primitives::{ - Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d, Plane3d, - Polyline3d, Segment3d, Sphere, Torus, + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d, + Segment3d, Sphere, Torus, }, + Dir3, }; #[test] @@ -359,38 +361,27 @@ mod tests { fn line() { let translation = Vec3::new(2.0, 1.0, 0.0); - let aabb1 = Line3d { - direction: Direction3d::Y, - } - .aabb_3d(translation, Quat::IDENTITY); + let aabb1 = Line3d { direction: Dir3::Y }.aabb_3d(translation, Quat::IDENTITY); assert_eq!(aabb1.min, Vec3::new(2.0, -f32::MAX / 2.0, 0.0)); assert_eq!(aabb1.max, Vec3::new(2.0, f32::MAX / 2.0, 0.0)); - let aabb2 = Line3d { - direction: Direction3d::X, - } - .aabb_3d(translation, Quat::IDENTITY); + let aabb2 = Line3d { direction: Dir3::X }.aabb_3d(translation, Quat::IDENTITY); assert_eq!(aabb2.min, Vec3::new(-f32::MAX / 2.0, 1.0, 0.0)); assert_eq!(aabb2.max, Vec3::new(f32::MAX / 2.0, 1.0, 0.0)); - let aabb3 = Line3d { - direction: Direction3d::Z, - } - .aabb_3d(translation, Quat::IDENTITY); + let aabb3 = Line3d { direction: Dir3::Z }.aabb_3d(translation, Quat::IDENTITY); assert_eq!(aabb3.min, Vec3::new(2.0, 1.0, -f32::MAX / 2.0)); assert_eq!(aabb3.max, Vec3::new(2.0, 1.0, f32::MAX / 2.0)); let aabb4 = Line3d { - direction: Direction3d::from_xyz(1.0, 1.0, 1.0).unwrap(), + direction: Dir3::from_xyz(1.0, 1.0, 1.0).unwrap(), } .aabb_3d(translation, Quat::IDENTITY); assert_eq!(aabb4.min, Vec3::splat(-f32::MAX / 2.0)); assert_eq!(aabb4.max, Vec3::splat(f32::MAX / 2.0)); - let bounding_sphere = Line3d { - direction: Direction3d::Y, - } - .bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = + Line3d { direction: Dir3::Y }.bounding_sphere(translation, Quat::IDENTITY); assert_eq!(bounding_sphere.center, translation); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0); } diff --git a/crates/bevy_math/src/bounding/raycast2d.rs b/crates/bevy_math/src/bounding/raycast2d.rs index ca7bce52f585c..97e77924908dd 100644 --- a/crates/bevy_math/src/bounding/raycast2d.rs +++ b/crates/bevy_math/src/bounding/raycast2d.rs @@ -1,5 +1,5 @@ use super::{Aabb2d, BoundingCircle, IntersectsVolume}; -use crate::{primitives::Direction2d, Ray2d, Vec2}; +use crate::{Dir2, Ray2d, Vec2}; /// A raycast intersection test for 2D bounding volumes #[derive(Clone, Debug)] @@ -13,8 +13,8 @@ pub struct RayCast2d { } impl RayCast2d { - /// Construct a [`RayCast2d`] from an origin, [`Direction2d`], and max distance. - pub fn new(origin: Vec2, direction: Direction2d, max: f32) -> Self { + /// Construct a [`RayCast2d`] from an origin, [`Dir2`], and max distance. + pub fn new(origin: Vec2, direction: Dir2, max: f32) -> Self { Self::from_ray(Ray2d { origin, direction }, max) } @@ -108,8 +108,8 @@ pub struct AabbCast2d { } impl AabbCast2d { - /// Construct an [`AabbCast2d`] from an [`Aabb2d`], origin, [`Direction2d`], and max distance. - pub fn new(aabb: Aabb2d, origin: Vec2, direction: Direction2d, max: f32) -> Self { + /// Construct an [`AabbCast2d`] from an [`Aabb2d`], origin, [`Dir2`], and max distance. + pub fn new(aabb: Aabb2d, origin: Vec2, direction: Dir2, max: f32) -> Self { Self::from_ray(aabb, Ray2d { origin, direction }, max) } @@ -145,8 +145,8 @@ pub struct BoundingCircleCast { } impl BoundingCircleCast { - /// Construct a [`BoundingCircleCast`] from a [`BoundingCircle`], origin, [`Direction2d`], and max distance. - pub fn new(circle: BoundingCircle, origin: Vec2, direction: Direction2d, max: f32) -> Self { + /// Construct a [`BoundingCircleCast`] from a [`BoundingCircle`], origin, [`Dir2`], and max distance. + pub fn new(circle: BoundingCircle, origin: Vec2, direction: Dir2, max: f32) -> Self { Self::from_ray(circle, Ray2d { origin, direction }, max) } @@ -183,37 +183,37 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of a centered bounding circle - RayCast2d::new(Vec2::Y * -5., Direction2d::Y, 90.), + RayCast2d::new(Vec2::Y * -5., Dir2::Y, 90.), BoundingCircle::new(Vec2::ZERO, 1.), 4., ), ( // Hit the center of a centered bounding circle, but from the other side - RayCast2d::new(Vec2::Y * 5., -Direction2d::Y, 90.), + RayCast2d::new(Vec2::Y * 5., -Dir2::Y, 90.), BoundingCircle::new(Vec2::ZERO, 1.), 4., ), ( // Hit the center of an offset circle - RayCast2d::new(Vec2::ZERO, Direction2d::Y, 90.), + RayCast2d::new(Vec2::ZERO, Dir2::Y, 90.), BoundingCircle::new(Vec2::Y * 3., 2.), 1., ), ( // Just barely hit the circle before the max distance - RayCast2d::new(Vec2::X, Direction2d::Y, 1.), + RayCast2d::new(Vec2::X, Dir2::Y, 1.), BoundingCircle::new(Vec2::ONE, 0.01), 0.99, ), ( // Hit a circle off-center - RayCast2d::new(Vec2::X, Direction2d::Y, 90.), + RayCast2d::new(Vec2::X, Dir2::Y, 90.), BoundingCircle::new(Vec2::Y * 5., 2.), 3.268, ), ( // Barely hit a circle on the side - RayCast2d::new(Vec2::X * 0.99999, Direction2d::Y, 90.), + RayCast2d::new(Vec2::X * 0.99999, Dir2::Y, 90.), BoundingCircle::new(Vec2::Y * 5., 1.), 4.996, ), @@ -241,17 +241,17 @@ mod tests { for (test, volume) in &[ ( // The ray doesn't go in the right direction - RayCast2d::new(Vec2::ZERO, Direction2d::X, 90.), + RayCast2d::new(Vec2::ZERO, Dir2::X, 90.), BoundingCircle::new(Vec2::Y * 2., 1.), ), ( // Ray's alignment isn't enough to hit the circle - RayCast2d::new(Vec2::ZERO, Direction2d::from_xy(1., 1.).unwrap(), 90.), + RayCast2d::new(Vec2::ZERO, Dir2::from_xy(1., 1.).unwrap(), 90.), BoundingCircle::new(Vec2::Y * 2., 1.), ), ( // The ray's maximum distance isn't high enough - RayCast2d::new(Vec2::ZERO, Direction2d::Y, 0.5), + RayCast2d::new(Vec2::ZERO, Dir2::Y, 0.5), BoundingCircle::new(Vec2::Y * 2., 1.), ), ] { @@ -268,12 +268,7 @@ mod tests { fn test_ray_intersection_circle_inside() { let volume = BoundingCircle::new(Vec2::splat(0.5), 1.); for origin in &[Vec2::X, Vec2::Y, Vec2::ONE, Vec2::ZERO] { - for direction in &[ - Direction2d::X, - Direction2d::Y, - -Direction2d::X, - -Direction2d::Y, - ] { + for direction in &[Dir2::X, Dir2::Y, -Dir2::X, -Dir2::Y] { for max in &[0., 1., 900.] { let test = RayCast2d::new(*origin, *direction, *max); @@ -295,37 +290,37 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of a centered aabb - RayCast2d::new(Vec2::Y * -5., Direction2d::Y, 90.), + RayCast2d::new(Vec2::Y * -5., Dir2::Y, 90.), Aabb2d::new(Vec2::ZERO, Vec2::ONE), 4., ), ( // Hit the center of a centered aabb, but from the other side - RayCast2d::new(Vec2::Y * 5., -Direction2d::Y, 90.), + RayCast2d::new(Vec2::Y * 5., -Dir2::Y, 90.), Aabb2d::new(Vec2::ZERO, Vec2::ONE), 4., ), ( // Hit the center of an offset aabb - RayCast2d::new(Vec2::ZERO, Direction2d::Y, 90.), + RayCast2d::new(Vec2::ZERO, Dir2::Y, 90.), Aabb2d::new(Vec2::Y * 3., Vec2::splat(2.)), 1., ), ( // Just barely hit the aabb before the max distance - RayCast2d::new(Vec2::X, Direction2d::Y, 1.), + RayCast2d::new(Vec2::X, Dir2::Y, 1.), Aabb2d::new(Vec2::ONE, Vec2::splat(0.01)), 0.99, ), ( // Hit an aabb off-center - RayCast2d::new(Vec2::X, Direction2d::Y, 90.), + RayCast2d::new(Vec2::X, Dir2::Y, 90.), Aabb2d::new(Vec2::Y * 5., Vec2::splat(2.)), 3., ), ( // Barely hit an aabb on corner - RayCast2d::new(Vec2::X * -0.001, Direction2d::from_xy(1., 1.).unwrap(), 90.), + RayCast2d::new(Vec2::X * -0.001, Dir2::from_xy(1., 1.).unwrap(), 90.), Aabb2d::new(Vec2::Y * 2., Vec2::ONE), 1.414, ), @@ -353,17 +348,17 @@ mod tests { for (test, volume) in &[ ( // The ray doesn't go in the right direction - RayCast2d::new(Vec2::ZERO, Direction2d::X, 90.), + RayCast2d::new(Vec2::ZERO, Dir2::X, 90.), Aabb2d::new(Vec2::Y * 2., Vec2::ONE), ), ( // Ray's alignment isn't enough to hit the aabb - RayCast2d::new(Vec2::ZERO, Direction2d::from_xy(1., 0.99).unwrap(), 90.), + RayCast2d::new(Vec2::ZERO, Dir2::from_xy(1., 0.99).unwrap(), 90.), Aabb2d::new(Vec2::Y * 2., Vec2::ONE), ), ( // The ray's maximum distance isn't high enough - RayCast2d::new(Vec2::ZERO, Direction2d::Y, 0.5), + RayCast2d::new(Vec2::ZERO, Dir2::Y, 0.5), Aabb2d::new(Vec2::Y * 2., Vec2::ONE), ), ] { @@ -380,12 +375,7 @@ mod tests { fn test_ray_intersection_aabb_inside() { let volume = Aabb2d::new(Vec2::splat(0.5), Vec2::ONE); for origin in &[Vec2::X, Vec2::Y, Vec2::ONE, Vec2::ZERO] { - for direction in &[ - Direction2d::X, - Direction2d::Y, - -Direction2d::X, - -Direction2d::Y, - ] { + for direction in &[Dir2::X, Dir2::Y, -Dir2::X, -Dir2::Y] { for max in &[0., 1., 900.] { let test = RayCast2d::new(*origin, *direction, *max); @@ -407,12 +397,7 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of the aabb, that a ray would've also hit - AabbCast2d::new( - Aabb2d::new(Vec2::ZERO, Vec2::ONE), - Vec2::ZERO, - Direction2d::Y, - 90., - ), + AabbCast2d::new(Aabb2d::new(Vec2::ZERO, Vec2::ONE), Vec2::ZERO, Dir2::Y, 90.), Aabb2d::new(Vec2::Y * 5., Vec2::ONE), 3., ), @@ -421,7 +406,7 @@ mod tests { AabbCast2d::new( Aabb2d::new(Vec2::ZERO, Vec2::ONE), Vec2::Y * 10., - -Direction2d::Y, + -Dir2::Y, 90., ), Aabb2d::new(Vec2::Y * 5., Vec2::ONE), @@ -432,7 +417,7 @@ mod tests { AabbCast2d::new( Aabb2d::new(Vec2::ZERO, Vec2::ONE), Vec2::X * 1.5, - Direction2d::Y, + Dir2::Y, 90., ), Aabb2d::new(Vec2::Y * 5., Vec2::ONE), @@ -443,7 +428,7 @@ mod tests { AabbCast2d::new( Aabb2d::new(Vec2::X * -2., Vec2::ONE), Vec2::X * 3., - Direction2d::Y, + Dir2::Y, 90., ), Aabb2d::new(Vec2::Y * 5., Vec2::ONE), @@ -477,7 +462,7 @@ mod tests { BoundingCircleCast::new( BoundingCircle::new(Vec2::ZERO, 1.), Vec2::ZERO, - Direction2d::Y, + Dir2::Y, 90., ), BoundingCircle::new(Vec2::Y * 5., 1.), @@ -488,7 +473,7 @@ mod tests { BoundingCircleCast::new( BoundingCircle::new(Vec2::ZERO, 1.), Vec2::Y * 10., - -Direction2d::Y, + -Dir2::Y, 90., ), BoundingCircle::new(Vec2::Y * 5., 1.), @@ -499,7 +484,7 @@ mod tests { BoundingCircleCast::new( BoundingCircle::new(Vec2::ZERO, 1.), Vec2::X * 1.5, - Direction2d::Y, + Dir2::Y, 90., ), BoundingCircle::new(Vec2::Y * 5., 1.), @@ -510,7 +495,7 @@ mod tests { BoundingCircleCast::new( BoundingCircle::new(Vec2::X * -1.5, 1.), Vec2::X * 3., - Direction2d::Y, + Dir2::Y, 90., ), BoundingCircle::new(Vec2::Y * 5., 1.), diff --git a/crates/bevy_math/src/bounding/raycast3d.rs b/crates/bevy_math/src/bounding/raycast3d.rs index 377d4646ee858..a8282e5de53ae 100644 --- a/crates/bevy_math/src/bounding/raycast3d.rs +++ b/crates/bevy_math/src/bounding/raycast3d.rs @@ -1,5 +1,5 @@ use super::{Aabb3d, BoundingSphere, IntersectsVolume}; -use crate::{primitives::Direction3d, Ray3d, Vec3}; +use crate::{Dir3, Ray3d, Vec3}; /// A raycast intersection test for 3D bounding volumes #[derive(Clone, Debug)] @@ -13,8 +13,8 @@ pub struct RayCast3d { } impl RayCast3d { - /// Construct a [`RayCast3d`] from an origin, [`Direction3d`], and max distance. - pub fn new(origin: Vec3, direction: Direction3d, max: f32) -> Self { + /// Construct a [`RayCast3d`] from an origin, [`Dir3`], and max distance. + pub fn new(origin: Vec3, direction: Dir3, max: f32) -> Self { Self::from_ray(Ray3d { origin, direction }, max) } @@ -115,8 +115,8 @@ pub struct AabbCast3d { } impl AabbCast3d { - /// Construct an [`AabbCast3d`] from an [`Aabb3d`], origin, [`Direction3d`], and max distance. - pub fn new(aabb: Aabb3d, origin: Vec3, direction: Direction3d, max: f32) -> Self { + /// Construct an [`AabbCast3d`] from an [`Aabb3d`], origin, [`Dir3`], and max distance. + pub fn new(aabb: Aabb3d, origin: Vec3, direction: Dir3, max: f32) -> Self { Self::from_ray(aabb, Ray3d { origin, direction }, max) } @@ -152,8 +152,8 @@ pub struct BoundingSphereCast { } impl BoundingSphereCast { - /// Construct a [`BoundingSphereCast`] from a [`BoundingSphere`], origin, [`Direction3d`], and max distance. - pub fn new(sphere: BoundingSphere, origin: Vec3, direction: Direction3d, max: f32) -> Self { + /// Construct a [`BoundingSphereCast`] from a [`BoundingSphere`], origin, [`Dir3`], and max distance. + pub fn new(sphere: BoundingSphere, origin: Vec3, direction: Dir3, max: f32) -> Self { Self::from_ray(sphere, Ray3d { origin, direction }, max) } @@ -190,37 +190,37 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of a centered bounding sphere - RayCast3d::new(Vec3::Y * -5., Direction3d::Y, 90.), + RayCast3d::new(Vec3::Y * -5., Dir3::Y, 90.), BoundingSphere::new(Vec3::ZERO, 1.), 4., ), ( // Hit the center of a centered bounding sphere, but from the other side - RayCast3d::new(Vec3::Y * 5., -Direction3d::Y, 90.), + RayCast3d::new(Vec3::Y * 5., -Dir3::Y, 90.), BoundingSphere::new(Vec3::ZERO, 1.), 4., ), ( // Hit the center of an offset sphere - RayCast3d::new(Vec3::ZERO, Direction3d::Y, 90.), + RayCast3d::new(Vec3::ZERO, Dir3::Y, 90.), BoundingSphere::new(Vec3::Y * 3., 2.), 1., ), ( // Just barely hit the sphere before the max distance - RayCast3d::new(Vec3::X, Direction3d::Y, 1.), + RayCast3d::new(Vec3::X, Dir3::Y, 1.), BoundingSphere::new(Vec3::new(1., 1., 0.), 0.01), 0.99, ), ( // Hit a sphere off-center - RayCast3d::new(Vec3::X, Direction3d::Y, 90.), + RayCast3d::new(Vec3::X, Dir3::Y, 90.), BoundingSphere::new(Vec3::Y * 5., 2.), 3.268, ), ( // Barely hit a sphere on the side - RayCast3d::new(Vec3::X * 0.99999, Direction3d::Y, 90.), + RayCast3d::new(Vec3::X * 0.99999, Dir3::Y, 90.), BoundingSphere::new(Vec3::Y * 5., 1.), 4.996, ), @@ -248,17 +248,17 @@ mod tests { for (test, volume) in &[ ( // The ray doesn't go in the right direction - RayCast3d::new(Vec3::ZERO, Direction3d::X, 90.), + RayCast3d::new(Vec3::ZERO, Dir3::X, 90.), BoundingSphere::new(Vec3::Y * 2., 1.), ), ( // Ray's alignment isn't enough to hit the sphere - RayCast3d::new(Vec3::ZERO, Direction3d::from_xyz(1., 1., 1.).unwrap(), 90.), + RayCast3d::new(Vec3::ZERO, Dir3::from_xyz(1., 1., 1.).unwrap(), 90.), BoundingSphere::new(Vec3::Y * 2., 1.), ), ( // The ray's maximum distance isn't high enough - RayCast3d::new(Vec3::ZERO, Direction3d::Y, 0.5), + RayCast3d::new(Vec3::ZERO, Dir3::Y, 0.5), BoundingSphere::new(Vec3::Y * 2., 1.), ), ] { @@ -275,14 +275,7 @@ mod tests { fn test_ray_intersection_sphere_inside() { let volume = BoundingSphere::new(Vec3::splat(0.5), 1.); for origin in &[Vec3::X, Vec3::Y, Vec3::ONE, Vec3::ZERO] { - for direction in &[ - Direction3d::X, - Direction3d::Y, - Direction3d::Z, - -Direction3d::X, - -Direction3d::Y, - -Direction3d::Z, - ] { + for direction in &[Dir3::X, Dir3::Y, Dir3::Z, -Dir3::X, -Dir3::Y, -Dir3::Z] { for max in &[0., 1., 900.] { let test = RayCast3d::new(*origin, *direction, *max); @@ -304,41 +297,37 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of a centered aabb - RayCast3d::new(Vec3::Y * -5., Direction3d::Y, 90.), + RayCast3d::new(Vec3::Y * -5., Dir3::Y, 90.), Aabb3d::new(Vec3::ZERO, Vec3::ONE), 4., ), ( // Hit the center of a centered aabb, but from the other side - RayCast3d::new(Vec3::Y * 5., -Direction3d::Y, 90.), + RayCast3d::new(Vec3::Y * 5., -Dir3::Y, 90.), Aabb3d::new(Vec3::ZERO, Vec3::ONE), 4., ), ( // Hit the center of an offset aabb - RayCast3d::new(Vec3::ZERO, Direction3d::Y, 90.), + RayCast3d::new(Vec3::ZERO, Dir3::Y, 90.), Aabb3d::new(Vec3::Y * 3., Vec3::splat(2.)), 1., ), ( // Just barely hit the aabb before the max distance - RayCast3d::new(Vec3::X, Direction3d::Y, 1.), + RayCast3d::new(Vec3::X, Dir3::Y, 1.), Aabb3d::new(Vec3::new(1., 1., 0.), Vec3::splat(0.01)), 0.99, ), ( // Hit an aabb off-center - RayCast3d::new(Vec3::X, Direction3d::Y, 90.), + RayCast3d::new(Vec3::X, Dir3::Y, 90.), Aabb3d::new(Vec3::Y * 5., Vec3::splat(2.)), 3., ), ( // Barely hit an aabb on corner - RayCast3d::new( - Vec3::X * -0.001, - Direction3d::from_xyz(1., 1., 1.).unwrap(), - 90., - ), + RayCast3d::new(Vec3::X * -0.001, Dir3::from_xyz(1., 1., 1.).unwrap(), 90.), Aabb3d::new(Vec3::Y * 2., Vec3::ONE), 1.732, ), @@ -366,21 +355,17 @@ mod tests { for (test, volume) in &[ ( // The ray doesn't go in the right direction - RayCast3d::new(Vec3::ZERO, Direction3d::X, 90.), + RayCast3d::new(Vec3::ZERO, Dir3::X, 90.), Aabb3d::new(Vec3::Y * 2., Vec3::ONE), ), ( // Ray's alignment isn't enough to hit the aabb - RayCast3d::new( - Vec3::ZERO, - Direction3d::from_xyz(1., 0.99, 1.).unwrap(), - 90., - ), + RayCast3d::new(Vec3::ZERO, Dir3::from_xyz(1., 0.99, 1.).unwrap(), 90.), Aabb3d::new(Vec3::Y * 2., Vec3::ONE), ), ( // The ray's maximum distance isn't high enough - RayCast3d::new(Vec3::ZERO, Direction3d::Y, 0.5), + RayCast3d::new(Vec3::ZERO, Dir3::Y, 0.5), Aabb3d::new(Vec3::Y * 2., Vec3::ONE), ), ] { @@ -397,14 +382,7 @@ mod tests { fn test_ray_intersection_aabb_inside() { let volume = Aabb3d::new(Vec3::splat(0.5), Vec3::ONE); for origin in &[Vec3::X, Vec3::Y, Vec3::ONE, Vec3::ZERO] { - for direction in &[ - Direction3d::X, - Direction3d::Y, - Direction3d::Z, - -Direction3d::X, - -Direction3d::Y, - -Direction3d::Z, - ] { + for direction in &[Dir3::X, Dir3::Y, Dir3::Z, -Dir3::X, -Dir3::Y, -Dir3::Z] { for max in &[0., 1., 900.] { let test = RayCast3d::new(*origin, *direction, *max); @@ -426,12 +404,7 @@ mod tests { for (test, volume, expected_distance) in &[ ( // Hit the center of the aabb, that a ray would've also hit - AabbCast3d::new( - Aabb3d::new(Vec3::ZERO, Vec3::ONE), - Vec3::ZERO, - Direction3d::Y, - 90., - ), + AabbCast3d::new(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Vec3::ZERO, Dir3::Y, 90.), Aabb3d::new(Vec3::Y * 5., Vec3::ONE), 3., ), @@ -440,7 +413,7 @@ mod tests { AabbCast3d::new( Aabb3d::new(Vec3::ZERO, Vec3::ONE), Vec3::Y * 10., - -Direction3d::Y, + -Dir3::Y, 90., ), Aabb3d::new(Vec3::Y * 5., Vec3::ONE), @@ -451,7 +424,7 @@ mod tests { AabbCast3d::new( Aabb3d::new(Vec3::ZERO, Vec3::ONE), Vec3::X * 1.5, - Direction3d::Y, + Dir3::Y, 90., ), Aabb3d::new(Vec3::Y * 5., Vec3::ONE), @@ -462,7 +435,7 @@ mod tests { AabbCast3d::new( Aabb3d::new(Vec3::X * -2., Vec3::ONE), Vec3::X * 3., - Direction3d::Y, + Dir3::Y, 90., ), Aabb3d::new(Vec3::Y * 5., Vec3::ONE), @@ -496,7 +469,7 @@ mod tests { BoundingSphereCast::new( BoundingSphere::new(Vec3::ZERO, 1.), Vec3::ZERO, - Direction3d::Y, + Dir3::Y, 90., ), BoundingSphere::new(Vec3::Y * 5., 1.), @@ -507,7 +480,7 @@ mod tests { BoundingSphereCast::new( BoundingSphere::new(Vec3::ZERO, 1.), Vec3::Y * 10., - -Direction3d::Y, + -Dir3::Y, 90., ), BoundingSphere::new(Vec3::Y * 5., 1.), @@ -518,7 +491,7 @@ mod tests { BoundingSphereCast::new( BoundingSphere::new(Vec3::ZERO, 1.), Vec3::X * 1.5, - Direction3d::Y, + Dir3::Y, 90., ), BoundingSphere::new(Vec3::Y * 5., 1.), @@ -529,7 +502,7 @@ mod tests { BoundingSphereCast::new( BoundingSphere::new(Vec3::X * -1.5, 1.), Vec3::X * 3., - Direction3d::Y, + Dir3::Y, 90., ), BoundingSphere::new(Vec3::Y * 5., 1.), diff --git a/crates/bevy_math/src/direction.rs b/crates/bevy_math/src/direction.rs new file mode 100644 index 0000000000000..698b476605c15 --- /dev/null +++ b/crates/bevy_math/src/direction.rs @@ -0,0 +1,522 @@ +use crate::{ + primitives::{Primitive2d, Primitive3d}, + Quat, Vec2, Vec3, Vec3A, +}; + +/// An error indicating that a direction is invalid. +#[derive(Debug, PartialEq)] +pub enum InvalidDirectionError { + /// The length of the direction vector is zero or very close to zero. + Zero, + /// The length of the direction vector is `std::f32::INFINITY`. + Infinite, + /// The length of the direction vector is `NaN`. + NaN, +} + +impl InvalidDirectionError { + /// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector. + pub fn from_length(length: f32) -> Self { + if length.is_nan() { + InvalidDirectionError::NaN + } else if !length.is_finite() { + // If the direction is non-finite but also not NaN, it must be infinite + InvalidDirectionError::Infinite + } else { + // If the direction is invalid but neither NaN nor infinite, it must be zero + InvalidDirectionError::Zero + } + } +} + +impl std::fmt::Display for InvalidDirectionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Direction can not be zero (or very close to zero), or non-finite." + ) + } +} + +/// A normalized vector pointing in a direction in 2D space +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub struct Dir2(Vec2); +impl Primitive2d for Dir2 {} + +impl Dir2 { + /// A unit vector pointing along the positive X axis. + pub const X: Self = Self(Vec2::X); + /// A unit vector pointing along the positive Y axis. + pub const Y: Self = Self(Vec2::Y); + /// A unit vector pointing along the negative X axis. + pub const NEG_X: Self = Self(Vec2::NEG_X); + /// A unit vector pointing along the negative Y axis. + pub const NEG_Y: Self = Self(Vec2::NEG_Y); + + /// Create a direction from a finite, nonzero [`Vec2`]. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new(value: Vec2) -> Result { + Self::new_and_length(value).map(|(dir, _)| dir) + } + + /// Create a [`Dir2`] from a [`Vec2`] that is already normalized. + /// + /// # Warning + /// + /// `value` must be normalized, i.e it's length must be `1.0`. + pub fn new_unchecked(value: Vec2) -> Self { + debug_assert!(value.is_normalized()); + + Self(value) + } + + /// Create a direction from a finite, nonzero [`Vec2`], also returning its original length. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> { + let length = value.length(); + let direction = (length.is_finite() && length > 0.0).then_some(value / length); + + direction + .map(|dir| (Self(dir), length)) + .ok_or(InvalidDirectionError::from_length(length)) + } + + /// Create a direction from its `x` and `y` components. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`. + pub fn from_xy(x: f32, y: f32) -> Result { + Self::new(Vec2::new(x, y)) + } +} + +impl TryFrom for Dir2 { + type Error = InvalidDirectionError; + + fn try_from(value: Vec2) -> Result { + Self::new(value) + } +} + +impl std::ops::Deref for Dir2 { + type Target = Vec2; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::Neg for Dir2 { + type Output = Self; + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +#[cfg(feature = "approx")] +impl approx::AbsDiffEq for Dir2 { + type Epsilon = f32; + fn default_epsilon() -> f32 { + f32::EPSILON + } + fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool { + self.as_ref().abs_diff_eq(other.as_ref(), epsilon) + } +} + +#[cfg(feature = "approx")] +impl approx::RelativeEq for Dir2 { + fn default_max_relative() -> f32 { + f32::EPSILON + } + fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { + self.as_ref() + .relative_eq(other.as_ref(), epsilon, max_relative) + } +} + +#[cfg(feature = "approx")] +impl approx::UlpsEq for Dir2 { + fn default_max_ulps() -> u32 { + 4 + } + fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { + self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) + } +} + +/// A normalized vector pointing in a direction in 3D space +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub struct Dir3(Vec3); +impl Primitive3d for Dir3 {} + +impl Dir3 { + /// A unit vector pointing along the positive X axis. + pub const X: Self = Self(Vec3::X); + /// A unit vector pointing along the positive Y axis. + pub const Y: Self = Self(Vec3::Y); + /// A unit vector pointing along the positive Z axis. + pub const Z: Self = Self(Vec3::Z); + /// A unit vector pointing along the negative X axis. + pub const NEG_X: Self = Self(Vec3::NEG_X); + /// A unit vector pointing along the negative Y axis. + pub const NEG_Y: Self = Self(Vec3::NEG_Y); + /// A unit vector pointing along the negative Z axis. + pub const NEG_Z: Self = Self(Vec3::NEG_Z); + + /// Create a direction from a finite, nonzero [`Vec3`]. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new(value: Vec3) -> Result { + Self::new_and_length(value).map(|(dir, _)| dir) + } + + /// Create a [`Dir3`] from a [`Vec3`] that is already normalized. + /// + /// # Warning + /// + /// `value` must be normalized, i.e it's length must be `1.0`. + pub fn new_unchecked(value: Vec3) -> Self { + debug_assert!(value.is_normalized()); + + Self(value) + } + + /// Create a direction from a finite, nonzero [`Vec3`], also returning its original length. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> { + let length = value.length(); + let direction = (length.is_finite() && length > 0.0).then_some(value / length); + + direction + .map(|dir| (Self(dir), length)) + .ok_or(InvalidDirectionError::from_length(length)) + } + + /// Create a direction from its `x`, `y`, and `z` components. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`. + pub fn from_xyz(x: f32, y: f32, z: f32) -> Result { + Self::new(Vec3::new(x, y, z)) + } +} + +impl TryFrom for Dir3 { + type Error = InvalidDirectionError; + + fn try_from(value: Vec3) -> Result { + Self::new(value) + } +} + +impl From for Vec3 { + fn from(value: Dir3) -> Self { + value.0 + } +} + +impl std::ops::Deref for Dir3 { + type Target = Vec3; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::Neg for Dir3 { + type Output = Self; + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl std::ops::Mul for Dir3 { + type Output = Vec3; + fn mul(self, rhs: f32) -> Self::Output { + self.0 * rhs + } +} + +impl std::ops::Mul for Quat { + type Output = Dir3; + + /// Rotates the [`Dir3`] using a [`Quat`]. + fn mul(self, direction: Dir3) -> Self::Output { + let rotated = self * *direction; + + // Make sure the result is normalized. + // This can fail for non-unit quaternions. + debug_assert!(rotated.is_normalized()); + + Dir3::new_unchecked(rotated) + } +} + +#[cfg(feature = "approx")] +impl approx::AbsDiffEq for Dir3 { + type Epsilon = f32; + fn default_epsilon() -> f32 { + f32::EPSILON + } + fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool { + self.as_ref().abs_diff_eq(other.as_ref(), epsilon) + } +} + +#[cfg(feature = "approx")] +impl approx::RelativeEq for Dir3 { + fn default_max_relative() -> f32 { + f32::EPSILON + } + fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { + self.as_ref() + .relative_eq(other.as_ref(), epsilon, max_relative) + } +} + +#[cfg(feature = "approx")] +impl approx::UlpsEq for Dir3 { + fn default_max_ulps() -> u32 { + 4 + } + fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { + self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) + } +} + +/// A normalized SIMD vector pointing in a direction in 3D space. +/// +/// This type stores a 16 byte aligned [`Vec3A`]. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub struct Dir3A(Vec3A); +impl Primitive3d for Dir3A {} + +impl Dir3A { + /// A unit vector pointing along the positive X axis. + pub const X: Self = Self(Vec3A::X); + /// A unit vector pointing along the positive Y axis. + pub const Y: Self = Self(Vec3A::Y); + /// A unit vector pointing along the positive Z axis. + pub const Z: Self = Self(Vec3A::Z); + /// A unit vector pointing along the negative X axis. + pub const NEG_X: Self = Self(Vec3A::NEG_X); + /// A unit vector pointing along the negative Y axis. + pub const NEG_Y: Self = Self(Vec3A::NEG_Y); + /// A unit vector pointing along the negative Z axis. + pub const NEG_Z: Self = Self(Vec3A::NEG_Z); + + /// Create a direction from a finite, nonzero [`Vec3A`]. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new(value: Vec3A) -> Result { + Self::new_and_length(value).map(|(dir, _)| dir) + } + + /// Create a [`Dir3A`] from a [`Vec3A`] that is already normalized. + /// + /// # Warning + /// + /// `value` must be normalized, i.e it's length must be `1.0`. + pub fn new_unchecked(value: Vec3A) -> Self { + debug_assert!(value.is_normalized()); + + Self(value) + } + + /// Create a direction from a finite, nonzero [`Vec3A`], also returning its original length. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> { + let length = value.length(); + let direction = (length.is_finite() && length > 0.0).then_some(value / length); + + direction + .map(|dir| (Self(dir), length)) + .ok_or(InvalidDirectionError::from_length(length)) + } + + /// Create a direction from its `x`, `y`, and `z` components. + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`. + pub fn from_xyz(x: f32, y: f32, z: f32) -> Result { + Self::new(Vec3A::new(x, y, z)) + } +} + +impl TryFrom for Dir3A { + type Error = InvalidDirectionError; + + fn try_from(value: Vec3A) -> Result { + Self::new(value) + } +} + +impl From for Vec3A { + fn from(value: Dir3A) -> Self { + value.0 + } +} + +impl std::ops::Deref for Dir3A { + type Target = Vec3A; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::Neg for Dir3A { + type Output = Self; + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl std::ops::Mul for Dir3A { + type Output = Vec3A; + fn mul(self, rhs: f32) -> Self::Output { + self.0 * rhs + } +} + +impl std::ops::Mul for Quat { + type Output = Dir3A; + + /// Rotates the [`Dir3A`] using a [`Quat`]. + fn mul(self, direction: Dir3A) -> Self::Output { + let rotated = self * *direction; + + // Make sure the result is normalized. + // This can fail for non-unit quaternions. + debug_assert!(rotated.is_normalized()); + + Dir3A::new_unchecked(rotated) + } +} + +#[cfg(feature = "approx")] +impl approx::AbsDiffEq for Dir3A { + type Epsilon = f32; + fn default_epsilon() -> f32 { + f32::EPSILON + } + fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool { + self.as_ref().abs_diff_eq(other.as_ref(), epsilon) + } +} + +#[cfg(feature = "approx")] +impl approx::RelativeEq for Dir3A { + fn default_max_relative() -> f32 { + f32::EPSILON + } + fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { + self.as_ref() + .relative_eq(other.as_ref(), epsilon, max_relative) + } +} + +#[cfg(feature = "approx")] +impl approx::UlpsEq for Dir3A { + fn default_max_ulps() -> u32 { + 4 + } + fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { + self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::InvalidDirectionError; + + #[test] + fn dir2_creation() { + assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X)); + assert_eq!( + Dir2::new(Vec2::new(0.0, 0.0)), + Err(InvalidDirectionError::Zero) + ); + assert_eq!( + Dir2::new(Vec2::new(f32::INFINITY, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir2::new(Vec2::new(f32::NAN, 0.0)), + Err(InvalidDirectionError::NaN) + ); + assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5))); + } + + #[test] + fn dir3_creation() { + assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X)); + assert_eq!( + Dir3::new(Vec3::new(0.0, 0.0, 0.0)), + Err(InvalidDirectionError::Zero) + ); + assert_eq!( + Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)), + Err(InvalidDirectionError::NaN) + ); + assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5))); + + // Test rotation + assert!( + (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Dir3::X) + .abs_diff_eq(Vec3::Y, 10e-6) + ); + } + + #[test] + fn dir3a_creation() { + assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X)); + assert_eq!( + Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)), + Err(InvalidDirectionError::Zero) + ); + assert_eq!( + Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)), + Err(InvalidDirectionError::Infinite) + ); + assert_eq!( + Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)), + Err(InvalidDirectionError::NaN) + ); + assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5))); + + // Test rotation + assert!( + (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Dir3A::X) + .abs_diff_eq(Vec3A::Y, 10e-6) + ); + } +} diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index d4ac8b6158ee3..99af7de62813b 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -8,12 +8,14 @@ mod affine3; mod aspect_ratio; pub mod bounding; pub mod cubic_splines; +mod direction; pub mod primitives; mod ray; mod rects; pub use affine3::*; pub use aspect_ratio::AspectRatio; +pub use direction::*; pub use ray::{Ray2d, Ray3d}; pub use rects::*; @@ -25,6 +27,7 @@ pub mod prelude { CubicBSpline, CubicBezier, CubicCardinalSpline, CubicGenerator, CubicHermite, CubicSegment, }, + direction::{Dir2, Dir3, Dir3A}, primitives::*, BVec2, BVec3, BVec4, EulerRot, FloatExt, IRect, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Ray2d, Ray3d, Rect, URect, UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec3, diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index 09eb970200857..a6157504fe862 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -1,118 +1,14 @@ use std::f32::consts::PI; -use super::{InvalidDirectionError, Primitive2d, WindingOrder}; -use crate::Vec2; +use super::{Primitive2d, WindingOrder}; +use crate::{Dir2, Vec2}; /// A normalized vector pointing in a direction in 2D space -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct Direction2d(Vec2); -impl Primitive2d for Direction2d {} - -impl Direction2d { - /// A unit vector pointing along the positive X axis. - pub const X: Self = Self(Vec2::X); - /// A unit vector pointing along the positive Y axis. - pub const Y: Self = Self(Vec2::Y); - /// A unit vector pointing along the negative X axis. - pub const NEG_X: Self = Self(Vec2::NEG_X); - /// A unit vector pointing along the negative Y axis. - pub const NEG_Y: Self = Self(Vec2::NEG_Y); - - /// Create a direction from a finite, nonzero [`Vec2`]. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the given vector is zero (or very close to zero), infinite, or `NaN`. - pub fn new(value: Vec2) -> Result { - Self::new_and_length(value).map(|(dir, _)| dir) - } - - /// Create a [`Direction2d`] from a [`Vec2`] that is already normalized. - /// - /// # Warning - /// - /// `value` must be normalized, i.e it's length must be `1.0`. - pub fn new_unchecked(value: Vec2) -> Self { - debug_assert!(value.is_normalized()); - - Self(value) - } - - /// Create a direction from a finite, nonzero [`Vec2`], also returning its original length. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the given vector is zero (or very close to zero), infinite, or `NaN`. - pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> { - let length = value.length(); - let direction = (length.is_finite() && length > 0.0).then_some(value / length); - - direction - .map(|dir| (Self(dir), length)) - .ok_or(InvalidDirectionError::from_length(length)) - } - - /// Create a direction from its `x` and `y` components. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`. - pub fn from_xy(x: f32, y: f32) -> Result { - Self::new(Vec2::new(x, y)) - } -} - -impl TryFrom for Direction2d { - type Error = InvalidDirectionError; - - fn try_from(value: Vec2) -> Result { - Self::new(value) - } -} - -impl std::ops::Deref for Direction2d { - type Target = Vec2; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::Neg for Direction2d { - type Output = Self; - fn neg(self) -> Self::Output { - Self(-self.0) - } -} - -#[cfg(feature = "approx")] -impl approx::AbsDiffEq for Direction2d { - type Epsilon = f32; - fn default_epsilon() -> f32 { - f32::EPSILON - } - fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool { - self.as_ref().abs_diff_eq(other.as_ref(), epsilon) - } -} - -#[cfg(feature = "approx")] -impl approx::RelativeEq for Direction2d { - fn default_max_relative() -> f32 { - f32::EPSILON - } - fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { - self.as_ref() - .relative_eq(other.as_ref(), epsilon, max_relative) - } -} - -#[cfg(feature = "approx")] -impl approx::UlpsEq for Direction2d { - fn default_max_ulps() -> u32 { - 4 - } - fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { - self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) - } -} +#[deprecated( + since = "0.14.0", + note = "`Direction2d` has been renamed. Please use `Dir2` instead." +)] +pub type Direction2d = Dir2; /// A circle primitive #[derive(Clone, Copy, Debug, PartialEq)] @@ -242,16 +138,14 @@ impl Ellipse { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Plane2d { /// The normal of the plane. The plane will be placed perpendicular to this direction - pub normal: Direction2d, + pub normal: Dir2, } impl Primitive2d for Plane2d {} impl Default for Plane2d { /// Returns the default [`Plane2d`] with a normal pointing in the `+Y` direction. fn default() -> Self { - Self { - normal: Direction2d::Y, - } + Self { normal: Dir2::Y } } } @@ -264,7 +158,7 @@ impl Plane2d { #[inline(always)] pub fn new(normal: Vec2) -> Self { Self { - normal: Direction2d::new(normal).expect("normal must be nonzero and finite"), + normal: Dir2::new(normal).expect("normal must be nonzero and finite"), } } } @@ -277,7 +171,7 @@ impl Plane2d { pub struct Line2d { /// The direction of the line. The line extends infinitely in both the given direction /// and its opposite direction - pub direction: Direction2d, + pub direction: Dir2, } impl Primitive2d for Line2d {} @@ -287,7 +181,7 @@ impl Primitive2d for Line2d {} #[doc(alias = "LineSegment2d")] pub struct Segment2d { /// The direction of the line segment - pub direction: Direction2d, + pub direction: Dir2, /// Half the length of the line segment. The segment extends by this amount in both /// the given direction and its opposite direction pub half_length: f32, @@ -297,7 +191,7 @@ impl Primitive2d for Segment2d {} impl Segment2d { /// Create a new `Segment2d` from a direction and full length of the segment #[inline(always)] - pub fn new(direction: Direction2d, length: f32) -> Self { + pub fn new(direction: Dir2, length: f32) -> Self { Self { direction, half_length: length / 2.0, @@ -316,7 +210,7 @@ impl Segment2d { ( // We are dividing by the length here, so the vector is normalized. - Self::new(Direction2d::new_unchecked(diff / length), length), + Self::new(Dir2::new_unchecked(diff / length), length), (point1 + point2) / 2., ) } @@ -806,31 +700,6 @@ mod tests { use super::*; use approx::assert_relative_eq; - #[test] - fn direction_creation() { - assert_eq!(Direction2d::new(Vec2::X * 12.5), Ok(Direction2d::X)); - assert_eq!( - Direction2d::new(Vec2::new(0.0, 0.0)), - Err(InvalidDirectionError::Zero) - ); - assert_eq!( - Direction2d::new(Vec2::new(f32::INFINITY, 0.0)), - Err(InvalidDirectionError::Infinite) - ); - assert_eq!( - Direction2d::new(Vec2::new(f32::NEG_INFINITY, 0.0)), - Err(InvalidDirectionError::Infinite) - ); - assert_eq!( - Direction2d::new(Vec2::new(f32::NAN, 0.0)), - Err(InvalidDirectionError::NaN) - ); - assert_eq!( - Direction2d::new_and_length(Vec2::X * 6.5), - Ok((Direction2d::X, 6.5)) - ); - } - #[test] fn rectangle_closest_point() { let rectangle = Rectangle::new(2.0, 2.0); diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs index 05b682f096903..044f2f499e473 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -1,150 +1,14 @@ use std::f32::consts::{FRAC_PI_3, PI}; -use super::{Circle, InvalidDirectionError, Primitive3d}; -use crate::{Quat, Vec3}; +use super::{Circle, Primitive3d}; +use crate::{Dir3, Vec3}; /// A normalized vector pointing in a direction in 3D space -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct Direction3d(Vec3); -impl Primitive3d for Direction3d {} - -impl Direction3d { - /// A unit vector pointing along the positive X axis. - pub const X: Self = Self(Vec3::X); - /// A unit vector pointing along the positive Y axis. - pub const Y: Self = Self(Vec3::Y); - /// A unit vector pointing along the positive Z axis. - pub const Z: Self = Self(Vec3::Z); - /// A unit vector pointing along the negative X axis. - pub const NEG_X: Self = Self(Vec3::NEG_X); - /// A unit vector pointing along the negative Y axis. - pub const NEG_Y: Self = Self(Vec3::NEG_Y); - /// A unit vector pointing along the negative Z axis. - pub const NEG_Z: Self = Self(Vec3::NEG_Z); - - /// Create a direction from a finite, nonzero [`Vec3`]. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the given vector is zero (or very close to zero), infinite, or `NaN`. - pub fn new(value: Vec3) -> Result { - Self::new_and_length(value).map(|(dir, _)| dir) - } - - /// Create a [`Direction3d`] from a [`Vec3`] that is already normalized. - /// - /// # Warning - /// - /// `value` must be normalized, i.e it's length must be `1.0`. - pub fn new_unchecked(value: Vec3) -> Self { - debug_assert!(value.is_normalized()); - - Self(value) - } - - /// Create a direction from a finite, nonzero [`Vec3`], also returning its original length. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the given vector is zero (or very close to zero), infinite, or `NaN`. - pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> { - let length = value.length(); - let direction = (length.is_finite() && length > 0.0).then_some(value / length); - - direction - .map(|dir| (Self(dir), length)) - .ok_or(InvalidDirectionError::from_length(length)) - } - - /// Create a direction from its `x`, `y`, and `z` components. - /// - /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length - /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`. - pub fn from_xyz(x: f32, y: f32, z: f32) -> Result { - Self::new(Vec3::new(x, y, z)) - } -} - -impl TryFrom for Direction3d { - type Error = InvalidDirectionError; - - fn try_from(value: Vec3) -> Result { - Self::new(value) - } -} - -impl From for Vec3 { - fn from(value: Direction3d) -> Self { - value.0 - } -} - -impl std::ops::Deref for Direction3d { - type Target = Vec3; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::Neg for Direction3d { - type Output = Self; - fn neg(self) -> Self::Output { - Self(-self.0) - } -} - -impl std::ops::Mul for Direction3d { - type Output = Vec3; - fn mul(self, rhs: f32) -> Self::Output { - self.0 * rhs - } -} - -impl std::ops::Mul for Quat { - type Output = Direction3d; - - /// Rotates the [`Direction3d`] using a [`Quat`]. - fn mul(self, direction: Direction3d) -> Self::Output { - let rotated = self * *direction; - - // Make sure the result is normalized. - // This can fail for non-unit quaternions. - debug_assert!(rotated.is_normalized()); - - Direction3d::new_unchecked(rotated) - } -} - -#[cfg(feature = "approx")] -impl approx::AbsDiffEq for Direction3d { - type Epsilon = f32; - fn default_epsilon() -> f32 { - f32::EPSILON - } - fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool { - self.as_ref().abs_diff_eq(other.as_ref(), epsilon) - } -} - -#[cfg(feature = "approx")] -impl approx::RelativeEq for Direction3d { - fn default_max_relative() -> f32 { - f32::EPSILON - } - fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { - self.as_ref() - .relative_eq(other.as_ref(), epsilon, max_relative) - } -} - -#[cfg(feature = "approx")] -impl approx::UlpsEq for Direction3d { - fn default_max_ulps() -> u32 { - 4 - } - fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { - self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) - } -} +#[deprecated( + since = "0.14.0", + note = "`Direction3d` has been renamed. Please use `Dir3` instead." +)] +pub type Direction3d = Dir3; /// A sphere primitive #[derive(Clone, Copy, Debug, PartialEq)] @@ -213,16 +77,14 @@ impl Sphere { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Plane3d { /// The normal of the plane. The plane will be placed perpendicular to this direction - pub normal: Direction3d, + pub normal: Dir3, } impl Primitive3d for Plane3d {} impl Default for Plane3d { /// Returns the default [`Plane3d`] with a normal pointing in the `+Y` direction. fn default() -> Self { - Self { - normal: Direction3d::Y, - } + Self { normal: Dir3::Y } } } @@ -235,7 +97,7 @@ impl Plane3d { #[inline(always)] pub fn new(normal: Vec3) -> Self { Self { - normal: Direction3d::new(normal).expect("normal must be nonzero and finite"), + normal: Dir3::new(normal).expect("normal must be nonzero and finite"), } } @@ -251,7 +113,7 @@ impl Plane3d { /// are *collinear* and lie on the same line. #[inline(always)] pub fn from_points(a: Vec3, b: Vec3, c: Vec3) -> (Self, Vec3) { - let normal = Direction3d::new((b - a).cross(c - a)) + let normal = Dir3::new((b - a).cross(c - a)) .expect("plane must be defined by three finite points that don't lie on the same line"); let translation = (a + b + c) / 3.0; @@ -266,7 +128,7 @@ impl Plane3d { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Line3d { /// The direction of the line - pub direction: Direction3d, + pub direction: Dir3, } impl Primitive3d for Line3d {} @@ -276,7 +138,7 @@ impl Primitive3d for Line3d {} #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct Segment3d { /// The direction of the line - pub direction: Direction3d, + pub direction: Dir3, /// Half the length of the line segment. The segment extends by this amount in both /// the given direction and its opposite direction pub half_length: f32, @@ -286,7 +148,7 @@ impl Primitive3d for Segment3d {} impl Segment3d { /// Create a new `Segment3d` from a direction and full length of the segment #[inline(always)] - pub fn new(direction: Direction3d, length: f32) -> Self { + pub fn new(direction: Dir3, length: f32) -> Self { Self { direction, half_length: length / 2.0, @@ -305,7 +167,7 @@ impl Segment3d { ( // We are dividing by the length here, so the vector is normalized. - Self::new(Direction3d::new_unchecked(diff / length), length), + Self::new(Dir3::new_unchecked(diff / length), length), (point1 + point2) / 2., ) } @@ -779,35 +641,33 @@ mod tests { // Reference values were computed by hand and/or with external tools use super::*; + use crate::{InvalidDirectionError, Quat}; use approx::assert_relative_eq; #[test] fn direction_creation() { - assert_eq!(Direction3d::new(Vec3::X * 12.5), Ok(Direction3d::X)); + assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X)); assert_eq!( - Direction3d::new(Vec3::new(0.0, 0.0, 0.0)), + Dir3::new(Vec3::new(0.0, 0.0, 0.0)), Err(InvalidDirectionError::Zero) ); assert_eq!( - Direction3d::new(Vec3::new(f32::INFINITY, 0.0, 0.0)), + Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)), Err(InvalidDirectionError::Infinite) ); assert_eq!( - Direction3d::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)), + Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)), Err(InvalidDirectionError::Infinite) ); assert_eq!( - Direction3d::new(Vec3::new(f32::NAN, 0.0, 0.0)), + Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)), Err(InvalidDirectionError::NaN) ); - assert_eq!( - Direction3d::new_and_length(Vec3::X * 6.5), - Ok((Direction3d::X, 6.5)) - ); + assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5))); // Test rotation assert!( - (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Direction3d::X) + (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Dir3::X) .abs_diff_eq(Vec3::Y, 10e-6) ); } diff --git a/crates/bevy_math/src/primitives/mod.rs b/crates/bevy_math/src/primitives/mod.rs index db5f380897572..34754ad89bd1f 100644 --- a/crates/bevy_math/src/primitives/mod.rs +++ b/crates/bevy_math/src/primitives/mod.rs @@ -15,41 +15,6 @@ pub trait Primitive2d {} /// A marker trait for 3D primitives pub trait Primitive3d {} -/// An error indicating that a direction is invalid. -#[derive(Debug, PartialEq)] -pub enum InvalidDirectionError { - /// The length of the direction vector is zero or very close to zero. - Zero, - /// The length of the direction vector is `std::f32::INFINITY`. - Infinite, - /// The length of the direction vector is `NaN`. - NaN, -} - -impl InvalidDirectionError { - /// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector. - pub fn from_length(length: f32) -> Self { - if length.is_nan() { - InvalidDirectionError::NaN - } else if !length.is_finite() { - // If the direction is non-finite but also not NaN, it must be infinite - InvalidDirectionError::Infinite - } else { - // If the direction is invalid but neither NaN nor infinite, it must be zero - InvalidDirectionError::Zero - } - } -} - -impl std::fmt::Display for InvalidDirectionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Direction can not be zero (or very close to zero), or non-finite." - ) - } -} - /// The winding order for a set of points #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum WindingOrder { diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs index bc9c45fa669dd..d615fbd7ecc8b 100644 --- a/crates/bevy_math/src/ray.rs +++ b/crates/bevy_math/src/ray.rs @@ -1,6 +1,6 @@ use crate::{ - primitives::{Direction2d, Direction3d, Plane2d, Plane3d}, - Vec2, Vec3, + primitives::{Plane2d, Plane3d}, + Dir2, Dir3, Vec2, Vec3, }; /// An infinite half-line starting at `origin` and going in `direction` in 2D space. @@ -10,7 +10,7 @@ pub struct Ray2d { /// The origin of the ray. pub origin: Vec2, /// The direction of the ray. - pub direction: Direction2d, + pub direction: Dir2, } impl Ray2d { @@ -23,8 +23,7 @@ impl Ray2d { pub fn new(origin: Vec2, direction: Vec2) -> Self { Self { origin, - direction: Direction2d::new(direction) - .expect("ray direction must be nonzero and finite"), + direction: Dir2::new(direction).expect("ray direction must be nonzero and finite"), } } @@ -55,7 +54,7 @@ pub struct Ray3d { /// The origin of the ray. pub origin: Vec3, /// The direction of the ray. - pub direction: Direction3d, + pub direction: Dir3, } impl Ray3d { @@ -68,8 +67,7 @@ impl Ray3d { pub fn new(origin: Vec3, direction: Vec3) -> Self { Self { origin, - direction: Direction3d::new(direction) - .expect("ray direction must be nonzero and finite"), + direction: Dir3::new(direction).expect("ray direction must be nonzero and finite"), } } diff --git a/crates/bevy_reflect/src/impls/math/direction.rs b/crates/bevy_reflect/src/impls/math/direction.rs new file mode 100644 index 0000000000000..abfd9ff0f8394 --- /dev/null +++ b/crates/bevy_reflect/src/impls/math/direction.rs @@ -0,0 +1,7 @@ +use crate as bevy_reflect; +use crate::{ReflectDeserialize, ReflectSerialize}; +use bevy_reflect_derive::impl_reflect_value; + +impl_reflect_value!(::bevy_math::Dir2(Debug, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(::bevy_math::Dir3(Debug, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(::bevy_math::Dir3A(Debug, PartialEq, Serialize, Deserialize)); diff --git a/crates/bevy_reflect/src/impls/math/primitives2d.rs b/crates/bevy_reflect/src/impls/math/primitives2d.rs index 29f93eb5611a0..03364fb5f2fbd 100644 --- a/crates/bevy_reflect/src/impls/math/primitives2d.rs +++ b/crates/bevy_reflect/src/impls/math/primitives2d.rs @@ -1,14 +1,7 @@ use crate as bevy_reflect; use crate::{ReflectDeserialize, ReflectSerialize}; -use bevy_math::{primitives::*, Vec2}; -use bevy_reflect_derive::{impl_reflect, impl_reflect_value}; - -impl_reflect_value!(::bevy_math::primitives::Direction2d( - Debug, - PartialEq, - Serialize, - Deserialize -)); +use bevy_math::{primitives::*, Dir2, Vec2}; +use bevy_reflect_derive::impl_reflect; impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] @@ -30,7 +23,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Plane2d { - normal: Direction2d, + normal: Dir2, } ); @@ -38,7 +31,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Line2d { - direction: Direction2d, + direction: Dir2, } ); @@ -46,7 +39,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Segment2d { - direction: Direction2d, + direction: Dir2, half_length: f32, } ); diff --git a/crates/bevy_reflect/src/impls/math/primitives3d.rs b/crates/bevy_reflect/src/impls/math/primitives3d.rs index 3c986fc1a63fc..e9b025529cfd8 100644 --- a/crates/bevy_reflect/src/impls/math/primitives3d.rs +++ b/crates/bevy_reflect/src/impls/math/primitives3d.rs @@ -1,14 +1,7 @@ use crate as bevy_reflect; use crate::{ReflectDeserialize, ReflectSerialize}; -use bevy_math::{primitives::*, Vec3}; -use bevy_reflect_derive::{impl_reflect, impl_reflect_value}; - -impl_reflect_value!(::bevy_math::primitives::Direction3d( - Debug, - PartialEq, - Serialize, - Deserialize -)); +use bevy_math::{primitives::*, Dir3, Vec3}; +use bevy_reflect_derive::impl_reflect; impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] @@ -22,7 +15,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Plane3d { - normal: Direction3d, + normal: Dir3, } ); @@ -30,7 +23,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Line3d { - direction: Direction3d, + direction: Dir3, } ); @@ -38,7 +31,7 @@ impl_reflect!( #[reflect(Debug, PartialEq, Serialize, Deserialize)] #[type_path = "bevy_math::primitives"] struct Segment3d { - direction: Direction3d, + direction: Dir3, half_length: f32, } ); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index b11bbbf6ac1ab..5fc5920b754ed 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -486,6 +486,7 @@ mod impls { mod glam; #[cfg(feature = "bevy_math")] mod math { + mod direction; mod primitives2d; mod primitives3d; mod rect; diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index c2e25686b1fe5..a8c6f6f75b1df 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -21,9 +21,7 @@ use bevy_ecs::{ system::{Commands, Query, Res, ResMut, Resource}, }; use bevy_log::warn; -use bevy_math::{ - primitives::Direction3d, vec2, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, Vec3, -}; +use bevy_math::{vec2, Dir3, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_render_macros::ExtractComponent; use bevy_transform::components::GlobalTransform; @@ -392,7 +390,7 @@ impl Camera { let world_far_plane = ndc_to_world.project_point3(ndc.extend(f32::EPSILON)); // The fallible direction constructor ensures that world_near_plane and world_far_plane aren't NaN. - Direction3d::new(world_far_plane - world_near_plane).map_or(None, |direction| { + Dir3::new(world_far_plane - world_near_plane).map_or(None, |direction| { Some(Ray3d { origin: world_near_plane, direction, diff --git a/crates/bevy_render/src/mesh/primitives/dim3/plane.rs b/crates/bevy_render/src/mesh/primitives/dim3/plane.rs index 02043acf48abd..b00b498038e5d 100644 --- a/crates/bevy_render/src/mesh/primitives/dim3/plane.rs +++ b/crates/bevy_render/src/mesh/primitives/dim3/plane.rs @@ -1,7 +1,4 @@ -use bevy_math::{ - primitives::{Direction3d, Plane3d}, - Quat, Vec2, Vec3, -}; +use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3}; use wgpu::PrimitiveTopology; use crate::{ @@ -30,7 +27,7 @@ impl Default for PlaneMeshBuilder { impl PlaneMeshBuilder { /// Creates a new [`PlaneMeshBuilder`] from a given normal and size. #[inline] - pub fn new(normal: Direction3d, size: Vec2) -> Self { + pub fn new(normal: Dir3, size: Vec2) -> Self { Self { plane: Plane3d { normal }, half_size: size / 2.0, @@ -59,7 +56,7 @@ impl PlaneMeshBuilder { /// Sets the normal of the plane, aka the direction the plane is facing. #[inline] #[doc(alias = "facing")] - pub fn normal(mut self, normal: Direction3d) -> Self { + pub fn normal(mut self, normal: Dir3) -> Self { self.plane = Plane3d { normal }; self } diff --git a/crates/bevy_transform/src/components/transform.rs b/crates/bevy_transform/src/components/transform.rs index 46dcceb68e5cf..ed8b004074f9a 100644 --- a/crates/bevy_transform/src/components/transform.rs +++ b/crates/bevy_transform/src/components/transform.rs @@ -1,7 +1,6 @@ use super::GlobalTransform; use bevy_ecs::{component::Component, reflect::ReflectComponent}; -use bevy_math::primitives::Direction3d; -use bevy_math::{Affine3A, Mat3, Mat4, Quat, Vec3}; +use bevy_math::{Affine3A, Dir3, Mat3, Mat4, Quat, Vec3}; use bevy_reflect::prelude::*; use bevy_reflect::Reflect; use std::ops::Mul; @@ -186,58 +185,58 @@ impl Transform { /// Get the unit vector in the local `X` direction. #[inline] - pub fn local_x(&self) -> Direction3d { - // Direction3d::new(x) panics if x is of invalid length, but quat * unit vector is length 1 - Direction3d::new(self.rotation * Vec3::X).unwrap() + pub fn local_x(&self) -> Dir3 { + // Dir3::new(x) panics if x is of invalid length, but quat * unit vector is length 1 + Dir3::new(self.rotation * Vec3::X).unwrap() } /// Equivalent to [`-local_x()`][Transform::local_x()] #[inline] - pub fn left(&self) -> Direction3d { + pub fn left(&self) -> Dir3 { -self.local_x() } /// Equivalent to [`local_x()`][Transform::local_x()] #[inline] - pub fn right(&self) -> Direction3d { + pub fn right(&self) -> Dir3 { self.local_x() } /// Get the unit vector in the local `Y` direction. #[inline] - pub fn local_y(&self) -> Direction3d { - // Direction3d::new(x) panics if x is of invalid length, but quat * unit vector is length 1 - Direction3d::new(self.rotation * Vec3::Y).unwrap() + pub fn local_y(&self) -> Dir3 { + // Dir3::new(x) panics if x is of invalid length, but quat * unit vector is length 1 + Dir3::new(self.rotation * Vec3::Y).unwrap() } /// Equivalent to [`local_y()`][Transform::local_y] #[inline] - pub fn up(&self) -> Direction3d { + pub fn up(&self) -> Dir3 { self.local_y() } /// Equivalent to [`-local_y()`][Transform::local_y] #[inline] - pub fn down(&self) -> Direction3d { + pub fn down(&self) -> Dir3 { -self.local_y() } /// Get the unit vector in the local `Z` direction. #[inline] - pub fn local_z(&self) -> Direction3d { - // Direction3d::new(x) panics if x is of invalid length, but quat * unit vector is length 1 - Direction3d::new(self.rotation * Vec3::Z).unwrap() + pub fn local_z(&self) -> Dir3 { + // Dir3::new(x) panics if x is of invalid length, but quat * unit vector is length 1 + Dir3::new(self.rotation * Vec3::Z).unwrap() } /// Equivalent to [`-local_z()`][Transform::local_z] #[inline] - pub fn forward(&self) -> Direction3d { + pub fn forward(&self) -> Dir3 { -self.local_z() } /// Equivalent to [`local_z()`][Transform::local_z] #[inline] - pub fn back(&self) -> Direction3d { + pub fn back(&self) -> Dir3 { self.local_z() } diff --git a/examples/2d/bounding_2d.rs b/examples/2d/bounding_2d.rs index ff121d7d8c258..e77874c4045d9 100644 --- a/examples/2d/bounding_2d.rs +++ b/examples/2d/bounding_2d.rs @@ -242,7 +242,7 @@ fn setup(mut commands: Commands, loader: Res) { transform: Transform::from_xyz(-OFFSET_X, -OFFSET_Y, 0.), ..default() }, - Shape::Line(Segment2d::new(Direction2d::from_xy(1., 0.3).unwrap(), 90.)), + Shape::Line(Segment2d::new(Dir2::from_xy(1., 0.3).unwrap(), 90.)), Spin, DesiredVolume::Circle, Intersects::default(), @@ -305,7 +305,7 @@ fn get_and_draw_ray(gizmos: &mut Gizmos, time: &Time) -> RayCast2d { let aabb_ray = Ray2d { origin: ray * 250., - direction: Direction2d::new_unchecked(-ray), + direction: Dir2::new_unchecked(-ray), }; let ray_cast = RayCast2d::from_ray(aabb_ray, dist - 20.); diff --git a/examples/3d/3d_gizmos.rs b/examples/3d/3d_gizmos.rs index 543db056c43ed..b99edd4a13349 100644 --- a/examples/3d/3d_gizmos.rs +++ b/examples/3d/3d_gizmos.rs @@ -118,10 +118,10 @@ fn draw_example_collection( .segments(10); // Circles have 32 line-segments by default. - my_gizmos.circle(Vec3::ZERO, Direction3d::Y, 3., Color::BLACK); + my_gizmos.circle(Vec3::ZERO, Dir3::Y, 3., Color::BLACK); // You may want to increase this for larger circles or spheres. my_gizmos - .circle(Vec3::ZERO, Direction3d::Y, 3.1, Color::NAVY) + .circle(Vec3::ZERO, Dir3::Y, 3.1, Color::NAVY) .segments(64); my_gizmos .sphere(Vec3::ZERO, Quat::IDENTITY, 3.2, Color::BLACK) diff --git a/examples/3d/3d_viewport_to_world.rs b/examples/3d/3d_viewport_to_world.rs index cb6bfe69d8482..4c9d1076d85a8 100644 --- a/examples/3d/3d_viewport_to_world.rs +++ b/examples/3d/3d_viewport_to_world.rs @@ -1,6 +1,6 @@ //! This example demonstrates how to use the `Camera::viewport_to_world` method. -use bevy::math::primitives::Direction3d; +use bevy::math::Dir3; use bevy::prelude::*; fn main() { @@ -39,7 +39,7 @@ fn draw_cursor( // Draw a circle just above the ground plane at that position. gizmos.circle( point + ground.up() * 0.01, - Direction3d::new_unchecked(ground.up()), // Up vector is already normalized. + Dir3::new_unchecked(ground.up()), // Up vector is already normalized. 0.2, Color::WHITE, ); diff --git a/examples/math/render_primitives.rs b/examples/math/render_primitives.rs index bfd1d38486747..e19c4bdef4d43 100644 --- a/examples/math/render_primitives.rs +++ b/examples/math/render_primitives.rs @@ -165,26 +165,18 @@ const TRIANGLE: Triangle2d = Triangle2d { ], }; -const PLANE_2D: Plane2d = Plane2d { - normal: Direction2d::Y, -}; -const PLANE_3D: Plane3d = Plane3d { - normal: Direction3d::Y, -}; +const PLANE_2D: Plane2d = Plane2d { normal: Dir2::Y }; +const PLANE_3D: Plane3d = Plane3d { normal: Dir3::Y }; -const LINE2D: Line2d = Line2d { - direction: Direction2d::X, -}; -const LINE3D: Line3d = Line3d { - direction: Direction3d::X, -}; +const LINE2D: Line2d = Line2d { direction: Dir2::X }; +const LINE3D: Line3d = Line3d { direction: Dir3::X }; const SEGMENT_2D: Segment2d = Segment2d { - direction: Direction2d::X, + direction: Dir2::X, half_length: BIG_2D, }; const SEGMENT_3D: Segment3d = Segment3d { - direction: Direction3d::X, + direction: Dir3::X, half_length: BIG_3D, };