diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_gizmos/src/primitives/dim2.rs index 9535c28fbd0ab..41b5e0a60a056 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_gizmos/src/primitives/dim2.rs @@ -7,9 +7,9 @@ use super::helpers::*; use bevy_color::Color; use bevy_math::{ primitives::{ - Annulus, Arc2d, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, - CircularSegment, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle, - RegularPolygon, Rhombus, Segment2d, Triangle2d, + Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, + Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, + Triangle2d, }, Dir2, Isometry2d, Rot2, Vec2, }; @@ -648,7 +648,7 @@ where // polyline 2d -impl GizmoPrimitive2d> for GizmoBuffer +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -660,42 +660,7 @@ where fn primitive_2d( &mut self, - primitive: &Polyline2d, - isometry: impl Into, - color: impl Into, - ) -> Self::Output<'_> { - if !self.enabled { - return; - } - - let isometry = isometry.into(); - - self.linestrip_2d( - primitive - .vertices - .iter() - .copied() - .map(|vec2| isometry * vec2), - color, - ); - } -} - -// boxed polyline 2d - -impl GizmoPrimitive2d for GizmoBuffer -where - Config: GizmoConfigGroup, - Clear: 'static + Send + Sync, -{ - type Output<'a> - = () - where - Self: 'a; - - fn primitive_2d( - &mut self, - primitive: &BoxedPolyline2d, + primitive: &Polyline2d, isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { @@ -784,7 +749,7 @@ where // polygon 2d -impl GizmoPrimitive2d> for GizmoBuffer +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -796,7 +761,7 @@ where fn primitive_2d( &mut self, - primitive: &Polygon, + primitive: &Polygon, isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { @@ -827,49 +792,6 @@ where } } -// boxed polygon 2d - -impl GizmoPrimitive2d for GizmoBuffer -where - Config: GizmoConfigGroup, - Clear: 'static + Send + Sync, -{ - type Output<'a> - = () - where - Self: 'a; - - fn primitive_2d( - &mut self, - primitive: &BoxedPolygon, - isometry: impl Into, - color: impl Into, - ) -> Self::Output<'_> { - if !self.enabled { - return; - } - - let isometry = isometry.into(); - - let closing_point = { - let first = primitive.vertices.first(); - (primitive.vertices.last() != first) - .then_some(first) - .flatten() - .cloned() - }; - self.linestrip_2d( - primitive - .vertices - .iter() - .copied() - .chain(closing_point) - .map(|vec2| isometry * vec2), - color, - ); - } -} - // regular polygon 2d impl GizmoPrimitive2d for GizmoBuffer diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 898850ddea901..ca1172316b2c6 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -5,8 +5,8 @@ use super::helpers::*; use bevy_color::Color; use bevy_math::{ primitives::{ - BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, - Polyline3d, Primitive3d, Segment3d, Sphere, Tetrahedron, Torus, Triangle3d, + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d, + Primitive3d, Segment3d, Sphere, Tetrahedron, Torus, Triangle3d, }, Dir3, Isometry3d, Quat, UVec2, Vec2, Vec3, }; @@ -235,7 +235,7 @@ where // polyline 3d -impl GizmoPrimitive3d> for GizmoBuffer +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -247,34 +247,7 @@ where fn primitive_3d( &mut self, - primitive: &Polyline3d, - isometry: impl Into, - color: impl Into, - ) -> Self::Output<'_> { - if !self.enabled { - return; - } - - let isometry = isometry.into(); - self.linestrip(primitive.vertices.map(|vec3| isometry * vec3), color); - } -} - -// boxed polyline 3d - -impl GizmoPrimitive3d for GizmoBuffer -where - Config: GizmoConfigGroup, - Clear: 'static + Send + Sync, -{ - type Output<'a> - = () - where - Self: 'a; - - fn primitive_3d( - &mut self, - primitive: &BoxedPolyline3d, + primitive: &Polyline3d, isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { @@ -284,11 +257,7 @@ where let isometry = isometry.into(); self.linestrip( - primitive - .vertices - .iter() - .copied() - .map(|vec3| isometry * vec3), + primitive.vertices.iter().map(|vec3| isometry * *vec3), color, ); } diff --git a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs index f55f40ddc6c87..958617b7aa566 100644 --- a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs @@ -5,15 +5,14 @@ use crate::{ ops, primitives::{ Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse, - Line2d, Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, - Triangle2d, + Line2d, Plane2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, }, Dir2, Isometry2d, Mat2, Rot2, Vec2, }; use core::f32::consts::{FRAC_PI_2, PI, TAU}; #[cfg(feature = "alloc")] -use crate::primitives::{BoxedPolygon, BoxedPolyline2d}; +use crate::primitives::{Polygon, Polyline2d}; use smallvec::SmallVec; @@ -279,18 +278,8 @@ impl Bounded2d for Segment2d { } } -impl Bounded2d for Polyline2d { - fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(isometry, &self.vertices) - } - - fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(isometry, &self.vertices) - } -} - #[cfg(feature = "alloc")] -impl Bounded2d for BoxedPolyline2d { +impl Bounded2d for Polyline2d { fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } @@ -366,7 +355,8 @@ impl Bounded2d for Rectangle { } } -impl Bounded2d for Polygon { +#[cfg(feature = "alloc")] +impl Bounded2d for Polygon { fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } @@ -376,24 +366,14 @@ impl Bounded2d for Polygon { } } -impl Bounded2d for ConvexPolygon { - fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(isometry, self.vertices().as_slice()) - } - - fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(isometry, self.vertices().as_slice()) - } -} - #[cfg(feature = "alloc")] -impl Bounded2d for BoxedPolygon { +impl Bounded2d for ConvexPolygon { fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(isometry, &self.vertices) + Aabb2d::from_point_cloud(isometry, self.vertices()) } fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(isometry, &self.vertices) + BoundingCircle::from_point_cloud(isometry, self.vertices()) } } @@ -908,7 +888,7 @@ mod tests { #[test] fn polyline() { - let polyline = Polyline2d::<4>::new([ + let polyline = Polyline2d::new([ Vec2::ONE, Vec2::new(-1.0, 1.0), Vec2::NEG_ONE, @@ -981,7 +961,7 @@ mod tests { #[test] fn polygon() { - let polygon = Polygon::<4>::new([ + let polygon = Polygon::new([ Vec2::ONE, Vec2::new(-1.0, 1.0), Vec2::NEG_ONE, diff --git a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs index 607d0f27464f3..8d849deaec433 100644 --- a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs +++ b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs @@ -6,14 +6,14 @@ use crate::{ bounding::{BoundingCircle, BoundingVolume}, ops, primitives::{ - Capsule2d, Cuboid, Cylinder, Ellipse, Extrusion, Line2d, Polygon, Polyline2d, Primitive2d, - Rectangle, RegularPolygon, Segment2d, Triangle2d, + Capsule2d, Cuboid, Cylinder, Ellipse, Extrusion, Line2d, Primitive2d, Rectangle, + RegularPolygon, Segment2d, Triangle2d, }, Isometry2d, Isometry3d, Quat, Rot2, }; #[cfg(feature = "alloc")] -use crate::primitives::{BoxedPolygon, BoxedPolyline2d}; +use crate::primitives::{Polygon, Polyline2d}; use crate::{bounding::Bounded2d, primitives::Circle}; @@ -96,19 +96,8 @@ impl BoundedExtrusion for Segment2d { } } -impl BoundedExtrusion for Polyline2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { - let isometry = isometry.into(); - let aabb = - Aabb3d::from_point_cloud(isometry, self.vertices.map(|v| v.extend(0.)).into_iter()); - let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); - - aabb.grow(depth.abs()) - } -} - #[cfg(feature = "alloc")] -impl BoundedExtrusion for BoxedPolyline2d { +impl BoundedExtrusion for Polyline2d { fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { let isometry = isometry.into(); let aabb = Aabb3d::from_point_cloud(isometry, self.vertices.iter().map(|v| v.extend(0.))); @@ -137,19 +126,7 @@ impl BoundedExtrusion for Rectangle { } } -impl BoundedExtrusion for Polygon { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { - let isometry = isometry.into(); - let aabb = - Aabb3d::from_point_cloud(isometry, self.vertices.map(|v| v.extend(0.)).into_iter()); - let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); - - aabb.grow(depth.abs()) - } -} - -#[cfg(feature = "alloc")] -impl BoundedExtrusion for BoxedPolygon { +impl BoundedExtrusion for Polygon { fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { let isometry = isometry.into(); let aabb = Aabb3d::from_point_cloud(isometry, self.vertices.iter().map(|v| v.extend(0.))); @@ -367,7 +344,7 @@ mod tests { #[test] fn polyline() { - let polyline = Polyline2d::<4>::new([ + let polyline = Polyline2d::new([ Vec2::ONE, Vec2::new(-1.0, 1.0), Vec2::NEG_ONE, @@ -413,7 +390,7 @@ mod tests { #[test] fn polygon() { - let polygon = Polygon::<4>::new([ + let polygon = Polygon::new([ Vec2::ONE, Vec2::new(-1.0, 1.0), Vec2::NEG_ONE, diff --git a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs index ebfd0266e81d0..7cf118e4d97e8 100644 --- a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs @@ -4,14 +4,14 @@ use crate::{ bounding::{Bounded2d, BoundingCircle, BoundingVolume}, ops, primitives::{ - Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Polyline3d, - Segment3d, Sphere, Torus, Triangle2d, Triangle3d, + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Segment3d, + Sphere, Torus, Triangle2d, Triangle3d, }, Isometry2d, Isometry3d, Mat3, Vec2, Vec3, Vec3A, }; #[cfg(feature = "alloc")] -use crate::primitives::BoxedPolyline3d; +use crate::primitives::Polyline3d; use super::{Aabb3d, Bounded3d, BoundingSphere}; @@ -86,18 +86,8 @@ impl Bounded3d for Segment3d { } } -impl Bounded3d for Polyline3d { - fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { - Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) - } - - fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { - BoundingSphere::from_point_cloud(isometry, &self.vertices) - } -} - #[cfg(feature = "alloc")] -impl Bounded3d for BoxedPolyline3d { +impl Bounded3d for Polyline3d { fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) } @@ -471,7 +461,7 @@ mod tests { #[test] fn polyline() { - let polyline = Polyline3d::<4>::new([ + let polyline = Polyline3d::new([ Vec3::ONE, Vec3::new(-1.0, 1.0, 1.0), Vec3::NEG_ONE, diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index 76da9555fa5f3..f037fb638eeb9 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -17,7 +17,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; #[cfg(feature = "alloc")] -use alloc::{boxed::Box, vec::Vec}; +use alloc::vec::Vec; /// A circle primitive, representing the set of points some distance from the origin #[derive(Clone, Copy, Debug, PartialEq)] @@ -1246,10 +1246,9 @@ pub struct Segment2d { impl Primitive2d for Segment2d {} impl Default for Segment2d { - /// Returns the default [`Segment2d`] with endpoints at `(0.0, 0.0)` and `(1.0, 0.0)`. fn default() -> Self { Self { - vertices: [Vec2::new(0.0, 0.0), Vec2::new(1.0, 0.0)], + vertices: [Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)], } } } @@ -1538,8 +1537,7 @@ impl From<(Vec2, Vec2)> for Segment2d { } /// A series of connected line segments in 2D space. -/// -/// For a version without generics: [`BoxedPolyline2d`] +#[cfg(feature = "alloc")] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( @@ -1551,63 +1549,53 @@ impl From<(Vec2, Vec2)> for Segment2d { all(feature = "serialize", feature = "bevy_reflect"), reflect(Serialize, Deserialize) )] -pub struct Polyline2d { +pub struct Polyline2d { /// The vertices of the polyline - #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))] - pub vertices: [Vec2; N], + pub vertices: Vec, } -impl Primitive2d for Polyline2d {} +#[cfg(feature = "alloc")] +impl Primitive2d for Polyline2d {} -impl FromIterator for Polyline2d { +#[cfg(feature = "alloc")] +impl FromIterator for Polyline2d { fn from_iter>(iter: I) -> Self { - let mut vertices: [Vec2; N] = [Vec2::ZERO; N]; - - for (index, i) in iter.into_iter().take(N).enumerate() { - vertices[index] = i; + Self { + vertices: iter.into_iter().collect(), } - Self { vertices } } } -impl Polyline2d { - /// Create a new `Polyline2d` from its vertices - pub fn new(vertices: impl IntoIterator) -> Self { - Self::from_iter(vertices) - } -} - -/// A series of connected line segments in 2D space, allocated on the heap -/// in a `Box<[Vec2]>`. -/// -/// For a version without alloc: [`Polyline2d`] -#[cfg(feature = "alloc")] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct BoxedPolyline2d { - /// The vertices of the polyline - pub vertices: Box<[Vec2]>, -} - -#[cfg(feature = "alloc")] -impl Primitive2d for BoxedPolyline2d {} - #[cfg(feature = "alloc")] -impl FromIterator for BoxedPolyline2d { - fn from_iter>(iter: I) -> Self { - let vertices: Vec = iter.into_iter().collect(); +impl Default for Polyline2d { + fn default() -> Self { Self { - vertices: vertices.into_boxed_slice(), + vertices: Vec::from([Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)]), } } } #[cfg(feature = "alloc")] -impl BoxedPolyline2d { - /// Create a new `BoxedPolyline2d` from its vertices +impl Polyline2d { + /// Create a new `Polyline2d` from its vertices pub fn new(vertices: impl IntoIterator) -> Self { Self::from_iter(vertices) } + + /// Create a new `Polyline2d` from two endpoints with subdivision points. + /// `subdivisions = 0` creates a simple line with just start and end points. + /// `subdivisions = 1` adds one point in the middle, creating 2 segments, etc. + pub fn with_subdivisions(start: Vec2, end: Vec2, subdivisions: usize) -> Self { + let total_vertices = subdivisions + 2; + let mut vertices = Vec::with_capacity(total_vertices); + + let step = (end - start) / (subdivisions + 1) as f32; + for i in 0..total_vertices { + vertices.push(start + step * i as f32); + } + + Self { vertices } + } } /// A triangle in 2D space @@ -1875,8 +1863,7 @@ impl Measured2d for Rectangle { } /// A polygon with N vertices. -/// -/// For a version without generics: [`BoxedPolygon`] +#[cfg(feature = "alloc")] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( @@ -1888,26 +1875,25 @@ impl Measured2d for Rectangle { all(feature = "serialize", feature = "bevy_reflect"), reflect(Serialize, Deserialize) )] -pub struct Polygon { +pub struct Polygon { /// The vertices of the `Polygon` - #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))] - pub vertices: [Vec2; N], + pub vertices: Vec, } -impl Primitive2d for Polygon {} +#[cfg(feature = "alloc")] +impl Primitive2d for Polygon {} -impl FromIterator for Polygon { +#[cfg(feature = "alloc")] +impl FromIterator for Polygon { fn from_iter>(iter: I) -> Self { - let mut vertices: [Vec2; N] = [Vec2::ZERO; N]; - - for (index, i) in iter.into_iter().take(N).enumerate() { - vertices[index] = i; + Self { + vertices: iter.into_iter().collect(), } - Self { vertices } } } -impl Polygon { +#[cfg(feature = "alloc")] +impl Polygon { /// Create a new `Polygon` from its vertices pub fn new(vertices: impl IntoIterator) -> Self { Self::from_iter(vertices) @@ -1923,8 +1909,9 @@ impl Polygon { } } -impl From> for Polygon { - fn from(val: ConvexPolygon) -> Self { +#[cfg(feature = "alloc")] +impl From for Polygon { + fn from(val: ConvexPolygon) -> Self { Polygon { vertices: val.vertices, } @@ -1932,6 +1919,7 @@ impl From> for Polygon { } /// A convex polygon with `N` vertices. +#[cfg(feature = "alloc")] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( @@ -1943,15 +1931,16 @@ impl From> for Polygon { all(feature = "serialize", feature = "bevy_reflect"), reflect(Serialize, Deserialize) )] -pub struct ConvexPolygon { +pub struct ConvexPolygon { /// The vertices of the [`ConvexPolygon`]. - #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))] - vertices: [Vec2; N], + vertices: Vec, } -impl Primitive2d for ConvexPolygon {} +#[cfg(feature = "alloc")] +impl Primitive2d for ConvexPolygon {} /// An error that happens when creating a [`ConvexPolygon`]. +#[cfg(feature = "alloc")] #[derive(Error, Debug, Clone)] pub enum ConvexPolygonError { /// The created polygon is not convex. @@ -1959,7 +1948,8 @@ pub enum ConvexPolygonError { Concave, } -impl ConvexPolygon { +#[cfg(feature = "alloc")] +impl ConvexPolygon { fn triangle_winding_order( &self, a_index: usize, @@ -1977,11 +1967,12 @@ impl ConvexPolygon { /// # Errors /// /// Returns [`ConvexPolygonError::Concave`] if the `vertices` do not form a convex polygon. - pub fn new(vertices: [Vec2; N]) -> Result { + pub fn new(vertices: impl IntoIterator) -> Result { let polygon = Self::new_unchecked(vertices); - let ref_winding_order = polygon.triangle_winding_order(N - 1, 0, 1); - for i in 1..N { - let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % N); + let len = polygon.vertices.len(); + let ref_winding_order = polygon.triangle_winding_order(len - 1, 0, 1); + for i in 1..len { + let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % len); if winding_order != ref_winding_order { return Err(ConvexPolygonError::Concave); } @@ -1992,66 +1983,27 @@ impl ConvexPolygon { /// Create a [`ConvexPolygon`] from its `vertices`, without checks. /// Use this version only if you know that the `vertices` make up a convex polygon. #[inline(always)] - pub fn new_unchecked(vertices: [Vec2; N]) -> Self { - Self { vertices } + pub fn new_unchecked(vertices: impl IntoIterator) -> Self { + Self { + vertices: vertices.into_iter().collect(), + } } /// Get the vertices of this polygon #[inline(always)] - pub fn vertices(&self) -> &[Vec2; N] { + pub fn vertices(&self) -> &[Vec2] { &self.vertices } } -impl TryFrom> for ConvexPolygon { +impl TryFrom for ConvexPolygon { type Error = ConvexPolygonError; - fn try_from(val: Polygon) -> Result { + fn try_from(val: Polygon) -> Result { ConvexPolygon::new(val.vertices) } } -/// A polygon with a variable number of vertices, allocated on the heap -/// in a `Box<[Vec2]>`. -/// -/// For a version without alloc: [`Polygon`] -#[cfg(feature = "alloc")] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct BoxedPolygon { - /// The vertices of the `BoxedPolygon` - pub vertices: Box<[Vec2]>, -} - -#[cfg(feature = "alloc")] -impl Primitive2d for BoxedPolygon {} - -#[cfg(feature = "alloc")] -impl FromIterator for BoxedPolygon { - fn from_iter>(iter: I) -> Self { - let vertices: Vec = iter.into_iter().collect(); - Self { - vertices: vertices.into_boxed_slice(), - } - } -} - -#[cfg(feature = "alloc")] -impl BoxedPolygon { - /// Create a new `BoxedPolygon` from its vertices - pub fn new(vertices: impl IntoIterator) -> Self { - Self::from_iter(vertices) - } - - /// Tests if the polygon is simple. - /// - /// A polygon is simple if it is not self intersecting and not self tangent. - /// As such, no two edges of the polygon may cross each other and each vertex must not lie on another edge. - pub fn is_simple(&self) -> bool { - is_polygon_simple(&self.vertices) - } -} - /// A polygon centered on the origin where all vertices lie on a circle, equally far apart. #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs index 76021f87f3e7b..729ebd48e9442 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -13,7 +13,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use glam::Quat; #[cfg(feature = "alloc")] -use alloc::{boxed::Box, vec::Vec}; +use alloc::vec::Vec; /// A sphere primitive, representing the set of all points some distance from the origin #[derive(Clone, Copy, Debug, PartialEq)] @@ -378,10 +378,9 @@ pub struct Segment3d { impl Primitive3d for Segment3d {} impl Default for Segment3d { - /// Returns the default [`Segment3d`] with endpoints at `(0.0, 0.0, 0.0)` and `(1.0, 0.0, 0.0)`. fn default() -> Self { Self { - vertices: [Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0)], + vertices: [Vec3::new(-0.5, 0.0, 0.0), Vec3::new(0.5, 0.0, 0.0)], } } } @@ -606,8 +605,7 @@ impl From<(Vec3, Vec3)> for Segment3d { } /// A series of connected line segments in 3D space. -/// -/// For a version without generics: [`BoxedPolyline3d`] +#[cfg(feature = "alloc")] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( @@ -619,62 +617,50 @@ impl From<(Vec3, Vec3)> for Segment3d { all(feature = "serialize", feature = "bevy_reflect"), reflect(Serialize, Deserialize) )] -pub struct Polyline3d { +pub struct Polyline3d { /// The vertices of the polyline - #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))] - pub vertices: [Vec3; N], + pub vertices: Vec, } -impl Primitive3d for Polyline3d {} +#[cfg(feature = "alloc")] +impl Primitive3d for Polyline3d {} -impl FromIterator for Polyline3d { +#[cfg(feature = "alloc")] +impl FromIterator for Polyline3d { fn from_iter>(iter: I) -> Self { - let mut vertices: [Vec3; N] = [Vec3::ZERO; N]; - - for (index, i) in iter.into_iter().take(N).enumerate() { - vertices[index] = i; + Self { + vertices: iter.into_iter().collect(), } - Self { vertices } } } -impl Polyline3d { - /// Create a new `Polyline3d` from its vertices - pub fn new(vertices: impl IntoIterator) -> Self { - Self::from_iter(vertices) +#[cfg(feature = "alloc")] +impl Default for Polyline3d { + fn default() -> Self { + Self::new([Vec3::new(-0.5, 0.0, 0.0), Vec3::new(0.5, 0.0, 0.0)]) } } -/// A series of connected line segments in 3D space, allocated on the heap -/// in a `Box<[Vec3]>`. -/// -/// For a version without alloc: [`Polyline3d`] #[cfg(feature = "alloc")] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -pub struct BoxedPolyline3d { - /// The vertices of the polyline - pub vertices: Box<[Vec3]>, -} +impl Polyline3d { + /// Create a new `Polyline3d` from its vertices + pub fn new(vertices: impl IntoIterator) -> Self { + Self::from_iter(vertices) + } -#[cfg(feature = "alloc")] -impl Primitive3d for BoxedPolyline3d {} + /// Create a new `Polyline3d` from two endpoints with subdivision points. + /// `subdivisions = 0` creates a simple line with just start and end points. + /// `subdivisions = 1` adds one point in the middle, creating 2 segments, etc. + pub fn with_subdivisions(start: Vec3, end: Vec3, subdivisions: usize) -> Self { + let total_vertices = subdivisions + 2; + let mut vertices = Vec::with_capacity(total_vertices); -#[cfg(feature = "alloc")] -impl FromIterator for BoxedPolyline3d { - fn from_iter>(iter: I) -> Self { - let vertices: Vec = iter.into_iter().collect(); - Self { - vertices: vertices.into_boxed_slice(), + let step = (end - start) / (subdivisions + 1) as f32; + for i in 0..total_vertices { + vertices.push(start + step * i as f32); } - } -} -#[cfg(feature = "alloc")] -impl BoxedPolyline3d { - /// Create a new `BoxedPolyline3d` from its vertices - pub fn new(vertices: impl IntoIterator) -> Self { - Self::from_iter(vertices) + Self { vertices } } } diff --git a/crates/bevy_math/src/primitives/mod.rs b/crates/bevy_math/src/primitives/mod.rs index b5c6644b13cc0..625a359dd711d 100644 --- a/crates/bevy_math/src/primitives/mod.rs +++ b/crates/bevy_math/src/primitives/mod.rs @@ -7,8 +7,6 @@ pub use dim2::*; mod dim3; pub use dim3::*; mod polygon; -#[cfg(feature = "serialize")] -mod serde; /// A marker trait for 2D primitives pub trait Primitive2d {} diff --git a/crates/bevy_math/src/primitives/serde.rs b/crates/bevy_math/src/primitives/serde.rs deleted file mode 100644 index a1b678132ee42..0000000000000 --- a/crates/bevy_math/src/primitives/serde.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! This module defines serialization/deserialization for const generic arrays. -//! Unlike serde's default behavior, it supports arbitrarily large arrays. -//! The code is based on this github comment: -//! - -pub(crate) mod array { - use core::marker::PhantomData; - use serde::{ - de::{SeqAccess, Visitor}, - ser::SerializeTuple, - Deserialize, Deserializer, Serialize, Serializer, - }; - - pub fn serialize( - data: &[T; N], - ser: S, - ) -> Result { - let mut s = ser.serialize_tuple(N)?; - for item in data { - s.serialize_element(item)?; - } - s.end() - } - - struct GenericArrayVisitor(PhantomData); - - impl<'de, T, const N: usize> Visitor<'de> for GenericArrayVisitor - where - T: Deserialize<'de>, - { - type Value = [T; N]; - - fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { - formatter.write_fmt(format_args!("an array of length {N}")) - } - - #[inline] - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut data = [const { Option::::None }; N]; - - for element in data.iter_mut() { - match (seq.next_element())? { - Some(val) => *element = Some(val), - None => return Err(serde::de::Error::invalid_length(N, &self)), - } - } - - let data = data.map(|value| match value { - Some(value) => value, - None => unreachable!(), - }); - - Ok(data) - } - } - - pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error> - where - D: Deserializer<'de>, - T: Deserialize<'de>, - { - deserializer.deserialize_tuple(N, GenericArrayVisitor::(PhantomData)) - } -} diff --git a/crates/bevy_mesh/src/primitives/dim2.rs b/crates/bevy_mesh/src/primitives/dim2.rs index 19af10afcf80b..dc2e99efad4f9 100644 --- a/crates/bevy_mesh/src/primitives/dim2.rs +++ b/crates/bevy_mesh/src/primitives/dim2.rs @@ -4,6 +4,7 @@ use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment}; use bevy_asset::RenderAssetUsages; use super::{Extrudable, MeshBuilder, Meshable}; +use bevy_math::prelude::Polyline2d; use bevy_math::{ ops, primitives::{ @@ -407,31 +408,32 @@ impl From for Mesh { /// /// You must verify that the `vertices` are not concave when constructing this type. You can /// guarantee this by creating a [`ConvexPolygon`] first, then calling [`ConvexPolygon::mesh()`]. -#[derive(Clone, Copy, Debug, Reflect)] +#[derive(Clone, Debug, Reflect)] #[reflect(Debug, Clone)] -pub struct ConvexPolygonMeshBuilder { - pub vertices: [Vec2; N], +pub struct ConvexPolygonMeshBuilder { + pub vertices: Vec, } -impl Meshable for ConvexPolygon { - type Output = ConvexPolygonMeshBuilder; +impl Meshable for ConvexPolygon { + type Output = ConvexPolygonMeshBuilder; fn mesh(&self) -> Self::Output { Self::Output { - vertices: *self.vertices(), + vertices: self.vertices().to_vec(), } } } -impl MeshBuilder for ConvexPolygonMeshBuilder { +impl MeshBuilder for ConvexPolygonMeshBuilder { fn build(&self) -> Mesh { - let mut indices = Vec::with_capacity((N - 2) * 3); - let mut positions = Vec::with_capacity(N); + let len = self.vertices.len(); + let mut indices = Vec::with_capacity((len - 2) * 3); + let mut positions = Vec::with_capacity(len); - for vertex in self.vertices { + for vertex in &self.vertices { positions.push([vertex.x, vertex.y, 0.0]); } - for i in 2..N as u32 { + for i in 2..len as u32 { indices.extend_from_slice(&[0, i - 1, i]); } Mesh::new( @@ -443,16 +445,16 @@ impl MeshBuilder for ConvexPolygonMeshBuilder { } } -impl Extrudable for ConvexPolygonMeshBuilder { +impl Extrudable for ConvexPolygonMeshBuilder { fn perimeter(&self) -> Vec { vec![PerimeterSegment::Flat { - indices: (0..N as u32).chain([0]).collect(), + indices: (0..self.vertices.len() as u32).chain([0]).collect(), }] } } -impl From> for Mesh { - fn from(polygon: ConvexPolygon) -> Self { +impl From for Mesh { + fn from(polygon: ConvexPolygon) -> Self { polygon.mesh().build() } } @@ -676,6 +678,50 @@ impl From for Mesh { } } +/// A builder used for creating a [`Mesh`] with a [`Polyline2d`] shape. +#[derive(Clone, Debug, Default, Reflect)] +#[reflect(Default, Debug, Clone)] +pub struct Polyline2dMeshBuilder { + polyline: Polyline2d, +} + +impl MeshBuilder for Polyline2dMeshBuilder { + fn build(&self) -> Mesh { + let positions: Vec<_> = self + .polyline + .vertices + .iter() + .map(|v| v.extend(0.0)) + .collect(); + + let indices = Indices::U32( + (0..self.polyline.vertices.len() as u32 - 1) + .flat_map(|i| [i, i + 1]) + .collect(), + ); + + Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + .with_inserted_indices(indices) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + } +} + +impl Meshable for Polyline2d { + type Output = Polyline2dMeshBuilder; + + fn mesh(&self) -> Self::Output { + Polyline2dMeshBuilder { + polyline: self.clone(), + } + } +} + +impl From for Mesh { + fn from(polyline: Polyline2d) -> Self { + polyline.mesh().build() + } +} + /// A builder for creating a [`Mesh`] with an [`Annulus`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] diff --git a/crates/bevy_mesh/src/primitives/dim3/mod.rs b/crates/bevy_mesh/src/primitives/dim3/mod.rs index a27d0a1bfb259..21cd80b3a420f 100644 --- a/crates/bevy_mesh/src/primitives/dim3/mod.rs +++ b/crates/bevy_mesh/src/primitives/dim3/mod.rs @@ -4,6 +4,7 @@ mod conical_frustum; mod cuboid; mod cylinder; mod plane; +mod polyline3d; mod segment3d; mod sphere; mod tetrahedron; diff --git a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs new file mode 100644 index 0000000000000..4d13112579f09 --- /dev/null +++ b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs @@ -0,0 +1,43 @@ +use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use bevy_asset::RenderAssetUsages; +use bevy_math::primitives::Polyline3d; +use bevy_reflect::prelude::*; + +/// A builder used for creating a [`Mesh`] with a [`Polyline3d`] shape. +#[derive(Clone, Debug, Default, Reflect)] +#[reflect(Default, Debug, Clone)] +pub struct Polyline3dMeshBuilder { + polyline: Polyline3d, +} + +impl MeshBuilder for Polyline3dMeshBuilder { + fn build(&self) -> Mesh { + let positions: Vec<_> = self.polyline.vertices.clone(); + + let indices = Indices::U32( + (0..self.polyline.vertices.len() as u32 - 1) + .flat_map(|i| [i, i + 1]) + .collect(), + ); + + Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + .with_inserted_indices(indices) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + } +} + +impl Meshable for Polyline3d { + type Output = Polyline3dMeshBuilder; + + fn mesh(&self) -> Self::Output { + Polyline3dMeshBuilder { + polyline: self.clone(), + } + } +} + +impl From for Mesh { + fn from(polyline: Polyline3d) -> Self { + polyline.mesh().build() + } +} diff --git a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs index 3c892b424277d..d032285283afb 100644 --- a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs @@ -29,6 +29,12 @@ impl Meshable for Segment3d { } } +impl From for Mesh { + fn from(segment: Segment3d) -> Self { + segment.mesh().build() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/examples/2d/2d_shapes.rs b/examples/2d/2d_shapes.rs index f69e138364190..03109baeee06e 100644 --- a/examples/2d/2d_shapes.rs +++ b/examples/2d/2d_shapes.rs @@ -54,6 +54,15 @@ fn setup( Vec2::new(-50.0, -50.0), Vec2::new(50.0, -50.0), )), + meshes.add(Segment2d::new( + Vec2::new(-50.0, 50.0), + Vec2::new(50.0, -50.0), + )), + meshes.add(Polyline2d::new(vec![ + Vec2::new(-50.0, 50.0), + Vec2::new(0.0, -50.0), + Vec2::new(50.0, 50.0), + ])), ]; let num_shapes = shapes.len(); diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index ef8fa16ddeb52..552f476d7f9c3 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -72,6 +72,12 @@ fn setup( meshes.add(ConicalFrustum::default()), meshes.add(Sphere::default().mesh().ico(5).unwrap()), meshes.add(Sphere::default().mesh().uv(32, 18)), + meshes.add(Segment3d::default()), + meshes.add(Polyline3d::new(vec![ + Vec3::new(-0.5, 0.0, 0.0), + Vec3::new(0.5, 0.0, 0.0), + Vec3::new(0.0, 0.5, 0.0), + ])), ]; let extrusions = [ diff --git a/examples/math/render_primitives.rs b/examples/math/render_primitives.rs index 26be0445baa90..1b7c7228c430b 100644 --- a/examples/math/render_primitives.rs +++ b/examples/math/render_primitives.rs @@ -197,33 +197,6 @@ const SEGMENT_3D: Segment3d = Segment3d { ], }; -const POLYLINE_2D: Polyline2d<4> = Polyline2d { - vertices: [ - Vec2::new(-BIG_2D, -SMALL_2D), - Vec2::new(-SMALL_2D, SMALL_2D), - Vec2::new(SMALL_2D, -SMALL_2D), - Vec2::new(BIG_2D, SMALL_2D), - ], -}; -const POLYLINE_3D: Polyline3d<4> = Polyline3d { - vertices: [ - Vec3::new(-BIG_3D, -SMALL_3D, -SMALL_3D), - Vec3::new(SMALL_3D, SMALL_3D, 0.0), - Vec3::new(-SMALL_3D, -SMALL_3D, 0.0), - Vec3::new(BIG_3D, SMALL_3D, SMALL_3D), - ], -}; - -const POLYGON_2D: Polygon<5> = Polygon { - vertices: [ - Vec2::new(-BIG_2D, -SMALL_2D), - Vec2::new(BIG_2D, -SMALL_2D), - Vec2::new(BIG_2D, SMALL_2D), - Vec2::new(0.0, 0.0), - Vec2::new(-BIG_2D, SMALL_2D), - ], -}; - const REGULAR_POLYGON: RegularPolygon = RegularPolygon { circumcircle: Circle { radius: BIG_2D }, sides: 5, @@ -452,8 +425,31 @@ fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res>, time PrimitiveSelected::Segment => { drop(gizmos.primitive_2d(&SEGMENT_2D, isometry, color)); } - PrimitiveSelected::Polyline => gizmos.primitive_2d(&POLYLINE_2D, isometry, color), - PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, isometry, color), + PrimitiveSelected::Polyline => gizmos.primitive_2d( + &Polyline2d { + vertices: vec![ + Vec2::new(-BIG_2D, -SMALL_2D), + Vec2::new(-SMALL_2D, SMALL_2D), + Vec2::new(SMALL_2D, -SMALL_2D), + Vec2::new(BIG_2D, SMALL_2D), + ], + }, + isometry, + color, + ), + PrimitiveSelected::Polygon => gizmos.primitive_2d( + &Polygon { + vertices: vec![ + Vec2::new(-BIG_2D, -SMALL_2D), + Vec2::new(BIG_2D, -SMALL_2D), + Vec2::new(BIG_2D, SMALL_2D), + Vec2::new(0.0, 0.0), + Vec2::new(-BIG_2D, SMALL_2D), + ], + }, + isometry, + color, + ), PrimitiveSelected::RegularPolygon => { gizmos.primitive_2d(®ULAR_POLYGON, isometry, color); } @@ -667,7 +663,18 @@ fn draw_gizmos_3d(mut gizmos: Gizmos, state: Res>, time PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, isometry, color)), PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, isometry, color), PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, isometry, color), - PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, isometry, color), + PrimitiveSelected::Polyline => gizmos.primitive_3d( + &Polyline3d { + vertices: vec![ + Vec3::new(-BIG_3D, -SMALL_3D, -SMALL_3D), + Vec3::new(SMALL_3D, SMALL_3D, 0.0), + Vec3::new(-SMALL_3D, -SMALL_3D, 0.0), + Vec3::new(BIG_3D, SMALL_3D, SMALL_3D), + ], + }, + isometry, + color, + ), PrimitiveSelected::Polygon => {} PrimitiveSelected::RegularPolygon => {} PrimitiveSelected::Capsule => drop( diff --git a/release-content/migration-guides/primitives_non_const_generic_meshable.md b/release-content/migration-guides/primitives_non_const_generic_meshable.md new file mode 100644 index 0000000000000..378bf86f95c93 --- /dev/null +++ b/release-content/migration-guides/primitives_non_const_generic_meshable.md @@ -0,0 +1,8 @@ +--- +title: Polylines and Polygons are no longer const-generic +pull_requests: [ 20250 ] +--- + +`Polyline2d`, `Polyline3d`, `Polygon`, and `ConvexPolygon` are no longer const-generic and now implement `Meshable` for +direct mesh generation. These types now use `Vec` instead of arrays internally and will therefore allocate and are no +longer `no_std` compatible.