diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index 106f0ba08d..31ff134cf3 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -9,6 +9,7 @@ mod affine3; pub mod cubic_splines; +pub mod primitives; mod ray; mod rects; @@ -24,8 +25,8 @@ pub mod prelude { CubicBSpline, CubicBezier, CubicCardinalSpline, CubicGenerator, CubicHermite, CubicSegment, }, - BVec2, BVec3, BVec4, EulerRot, IRect, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Ray, - Rect, URect, UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles, Vec4, + primitives, BVec2, BVec3, BVec4, EulerRot, IRect, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, + Quat, Ray, Rect, URect, UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles, }; } diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs new file mode 100644 index 0000000000..e1e051c6b3 --- /dev/null +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -0,0 +1,185 @@ +use super::Primitive2d; +use crate::Vec2; + +/// A normalized vector pointing in a direction in 2D space +#[derive(Clone, Copy, Debug)] +pub struct Direction2d(Vec2); + +impl From for Direction2d { + fn from(value: Vec2) -> Self { + Self(value.normalize()) + } +} + +impl Direction2d { + /// Create a direction from a [`Vec2`] that is already normalized + pub fn from_normalized(value: Vec2) -> Self { + debug_assert!(value.is_normalized()); + Self(value) + } +} + +impl std::ops::Deref for Direction2d { + type Target = Vec2; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// A circle primitive +#[derive(Clone, Copy, Debug)] +pub struct Circle { + /// The radius of the circle + pub radius: f32, +} +impl Primitive2d for Circle {} + +/// An unbounded plane in 2D space. It forms a separating surface through the origin, +/// stretching infinitely far +#[derive(Clone, Copy, Debug)] +pub struct Plane2d { + /// The normal of the plane. The plane will be placed perpendicular to this direction + pub normal: Direction2d, +} +impl Primitive2d for Plane2d {} + +/// An infinite line along a direction in 2D space. +/// +/// For a finite line: [`Segment2d`] +#[derive(Clone, Copy, Debug)] +pub struct Line2d { + /// The direction of the line. The line extends infinitely in both the given direction + /// and its opposite direction + pub direction: Direction2d, +} +impl Primitive2d for Line2d {} + +/// A segment of a line along a direction in 2D space. +#[doc(alias = "LineSegment2d")] +#[derive(Clone, Debug)] +pub struct Segment2d { + /// The direction of the line segment + pub direction: Direction2d, + /// 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, +} +impl Primitive2d for Segment2d {} + +impl Segment2d { + /// Create a line segment from a direction and full length of the segment + pub fn new(direction: Direction2d, length: f32) -> Self { + Self { + direction, + half_length: length / 2., + } + } + + /// Get a line segment and translation from two points at each end of a line segment + /// + /// Panics if point1 == point2 + pub fn from_points(point1: Vec2, point2: Vec2) -> (Self, Vec2) { + let diff = point2 - point1; + let length = diff.length(); + ( + Self::new(Direction2d::from_normalized(diff / length), length), + (point1 + point2) / 2., + ) + } + + /// Get the position of the first point on the line segment + pub fn point1(&self) -> Vec2 { + *self.direction * -self.half_length + } + + /// Get the position of the second point on the line segment + pub fn point2(&self) -> Vec2 { + *self.direction * self.half_length + } +} + +/// A series of connected line segments in 2D space. +/// +/// For a version without generics: [`BoxedPolyline2d`] +#[derive(Clone, Debug)] +pub struct Polyline2d { + /// The vertices of the polyline + pub vertices: [Vec2; N], +} +impl Primitive2d for Polyline2d {} + +/// A series of connected line segments in 2D space, allocated on the heap +/// in a `Box<[Vec2]>`. +/// +/// For a version without alloc: [`Polyline2d`] +#[derive(Clone, Debug)] +pub struct BoxedPolyline2d { + /// The vertices of the polyline + pub vertices: Box<[Vec2]>, +} +impl Primitive2d for BoxedPolyline2d {} + +/// A triangle in 2D space +#[derive(Clone, Debug)] +pub struct Triangle2d { + /// The vertices of the triangle + pub vertices: [Vec2; 3], +} +impl Primitive2d for Triangle2d {} + +/// A rectangle primitive +#[doc(alias = "Quad")] +#[derive(Clone, Copy, Debug)] +pub struct Rectangle { + /// The half width of the rectangle + pub half_width: f32, + /// The half height of the rectangle + pub half_height: f32, +} +impl Primitive2d for Rectangle {} + +impl Rectangle { + /// Create a rectangle from a full width and height + pub fn new(width: f32, height: f32) -> Self { + Self::from_size(Vec2::new(width, height)) + } + + /// Create a rectangle from a given full size + pub fn from_size(size: Vec2) -> Self { + Self { + half_width: size.x / 2., + half_height: size.y / 2., + } + } +} + +/// A polygon with N vertices. +/// +/// For a version without generics: [`BoxedPolygon`] +#[derive(Clone, Debug)] +pub struct Polygon { + /// The vertices of the polygon + pub vertices: [Vec2; N], +} +impl Primitive2d for Polygon {} + +/// A polygon with a variable number of vertices, allocated on the heap +/// in a `Box<[Vec2]>`. +/// +/// For a version without alloc: [`Polygon`] +#[derive(Clone, Debug)] +pub struct BoxedPolygon { + /// The vertices of the polygon + pub vertices: Box<[Vec2]>, +} +impl Primitive2d for BoxedPolygon {} + +/// A polygon where all vertices lie on a circle, equally far apart +#[derive(Clone, Copy, Debug)] +pub struct RegularPolygon { + /// The circumcircle on which all vertices lie + pub circumcircle: Circle, + /// The number of sides + pub sides: usize, +} +impl Primitive2d for RegularPolygon {} diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs new file mode 100644 index 0000000000..0d40821dc9 --- /dev/null +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -0,0 +1,173 @@ +use super::Primitive3d; +use crate::Vec3; + +/// A normalized vector pointing in a direction in 3D space +#[derive(Clone, Copy, Debug)] +pub struct Direction3d(Vec3); + +impl From for Direction3d { + fn from(value: Vec3) -> Self { + Self(value.normalize()) + } +} + +impl Direction3d { + /// Create a direction from a [`Vec3`] that is already normalized + pub fn from_normalized(value: Vec3) -> Self { + debug_assert!(value.is_normalized()); + Self(value) + } +} + +impl std::ops::Deref for Direction3d { + type Target = Vec3; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// A sphere primitive +#[derive(Clone, Copy, Debug)] +pub struct Sphere { + /// The radius of the sphere + pub radius: f32, +} +impl Primitive3d for Sphere {} + +/// An unbounded plane in 3D space. It forms a separating surface through the origin, +/// stretching infinitely far +#[derive(Clone, Copy, Debug)] +pub struct Plane3d { + /// The normal of the plane. The plane will be placed perpendicular to this direction + pub normal: Direction3d, +} +impl Primitive3d for Plane3d {} + +/// An infinite line along a direction in 3D space. +/// +/// For a finite line: [`Segment3d`] +#[derive(Clone, Copy, Debug)] +pub struct Line3d { + /// The direction of the line + pub direction: Direction3d, +} +impl Primitive3d for Line3d {} + +/// A segment of a line along a direction in 3D space. +#[doc(alias = "LineSegment3d")] +#[derive(Clone, Debug)] +pub struct Segment3d { + /// The direction of the line + pub direction: Direction3d, + /// 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, +} +impl Primitive3d for Segment3d {} + +impl Segment3d { + /// Create a line segment from a direction and full length of the segment + pub fn new(direction: Direction3d, length: f32) -> Self { + Self { + direction, + half_length: length / 2., + } + } + + /// Get a line segment and translation from two points at each end of a line segment + /// + /// Panics if point1 == point2 + pub fn from_points(point1: Vec3, point2: Vec3) -> (Self, Vec3) { + let diff = point2 - point1; + let length = diff.length(); + ( + Self::new(Direction3d::from_normalized(diff / length), length), + (point1 + point2) / 2., + ) + } + + /// Get the position of the first point on the line segment + pub fn point1(&self) -> Vec3 { + *self.direction * -self.half_length + } + + /// Get the position of the second point on the line segment + pub fn point2(&self) -> Vec3 { + *self.direction * self.half_length + } +} + +/// A series of connected line segments in 3D space. +/// +/// For a version without generics: [`BoxedPolyline3d`] +#[derive(Clone, Debug)] +pub struct Polyline3d { + /// The vertices of the polyline + pub vertices: [Vec3; N], +} +impl Primitive3d for Polyline3d {} + +/// A series of connected line segments in 3D space, allocated on the heap +/// in a `Box<[Vec3]>`. +/// +/// For a version without alloc: [`Polyline3d`] +#[derive(Clone, Debug)] +pub struct BoxedPolyline3d { + /// The vertices of the polyline + pub vertices: Box<[Vec3]>, +} +impl Primitive3d for BoxedPolyline3d {} + +/// A cuboid primitive, more commonly known as a box. +#[derive(Clone, Copy, Debug)] +pub struct Cuboid { + /// Half of the width, height and depth of the cuboid + pub half_extents: Vec3, +} +impl Primitive3d for Cuboid {} + +impl Cuboid { + /// Create a cuboid from a full x, y and z length + pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Self { + Self::from_size(Vec3::new(x_length, y_length, z_length)) + } + + /// Create a cuboid from a given full size + pub fn from_size(size: Vec3) -> Self { + Self { + half_extents: size / 2., + } + } +} + +/// A cylinder primitive +#[derive(Clone, Copy, Debug)] +pub struct Cylinder { + /// The radius of the cylinder + pub radius: f32, + /// The half height of the cylinder + pub half_height: f32, +} +impl Primitive3d for Cylinder {} + +impl Cylinder { + /// Create a cylinder from a radius and full height + pub fn new(radius: f32, height: f32) -> Self { + Self { + radius, + half_height: height / 2., + } + } +} + +/// A capsule primitive. +/// A capsule is defined as a surface at a distance (radius) from a line +#[derive(Clone, Copy, Debug)] +pub struct Capsule { + /// The radius of the capsule + pub radius: f32, + /// Half the height of the capsule, excluding the hemispheres + pub half_length: f32, +} +impl super::Primitive2d for Capsule {} +impl Primitive3d for Capsule {} diff --git a/crates/bevy_math/src/primitives/mod.rs b/crates/bevy_math/src/primitives/mod.rs new file mode 100644 index 0000000000..a3e98d0397 --- /dev/null +++ b/crates/bevy_math/src/primitives/mod.rs @@ -0,0 +1,14 @@ +//! This module defines primitive shapes. +//! The origin is (0, 0) for 2D primitives and (0, 0, 0) for 3D primitives, +//! unless stated otherwise. + +mod dim2; +pub use dim2::*; +mod dim3; +pub use dim3::*; + +/// A marker trait for 2D primitives +pub trait Primitive2d {} + +/// A marker trait for 3D primitives +pub trait Primitive3d {}