parry3d/shape/
shared_shape.rs

1use super::TriMeshBuilderError;
2use crate::math::{Isometry, Point, Real, Vector, DIM};
3#[cfg(feature = "dim2")]
4use crate::shape::ConvexPolygon;
5#[cfg(feature = "serde-serialize")]
6use crate::shape::DeserializableTypedShape;
7#[cfg(feature = "dim3")]
8use crate::shape::HeightFieldFlags;
9use crate::shape::{
10    Ball, Capsule, Compound, Cuboid, HalfSpace, HeightField, Polyline, RoundShape, Segment, Shape,
11    TriMesh, TriMeshFlags, Triangle, TypedShape, Voxels,
12};
13#[cfg(feature = "dim3")]
14use crate::shape::{Cone, ConvexPolyhedron, Cylinder};
15use crate::transformation::vhacd::{VHACDParameters, VHACD};
16use crate::transformation::voxelization::{FillMode, VoxelSet};
17use alloc::sync::Arc;
18use alloc::{vec, vec::Vec};
19use core::fmt;
20use core::ops::Deref;
21use na::Unit;
22
23/// A reference-counted, shareable geometric shape.
24///
25/// `SharedShape` is a wrapper around [`Arc<dyn Shape>`] that allows multiple parts of your
26/// code to share ownership of the same shape without copying it. This is particularly useful
27/// when the same geometric shape is used by multiple colliders or when you want to avoid
28/// the memory overhead of duplicating complex shapes like triangle meshes.
29///
30/// # Why use SharedShape?
31///
32/// - **Memory efficiency**: Share expensive shapes (like [`TriMesh`] or [`HeightField`]) across multiple colliders
33/// - **Performance**: Cloning a `SharedShape` only increments a reference count, not the shape data
34/// - **Type erasure**: Store different shape types uniformly via the [`Shape`] trait
35/// - **Flexibility**: Can be converted to a unique instance via [`make_mut`](Self::make_mut) when needed
36///
37/// # When to use SharedShape?
38///
39/// Use `SharedShape` when:
40/// - You need multiple colliders with the same geometry
41/// - You're working with large, complex shapes (meshes, compounds, heightfields)
42/// - You want to store heterogeneous shape types in a collection
43///
44/// Use concrete shape types directly when:
45/// - You have a single, simple shape that won't be shared
46/// - You need direct access to shape-specific methods
47///
48/// # Examples
49///
50/// Creating and sharing a ball shape:
51///
52/// ```
53/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
54/// # use parry3d::shape::SharedShape;
55/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
56/// # use parry2d::shape::SharedShape;
57///
58/// // Create a ball with radius 1.0
59/// let shape = SharedShape::ball(1.0);
60///
61/// // Clone it cheaply - only the Arc is cloned, not the shape data
62/// let shape_clone = shape.clone();
63///
64/// // Both shapes reference the same underlying ball
65/// assert_eq!(shape.as_ball().unwrap().radius, 1.0);
66/// assert_eq!(shape_clone.as_ball().unwrap().radius, 1.0);
67/// # }
68/// # }
69/// ```
70#[derive(Clone)]
71pub struct SharedShape(pub Arc<dyn Shape>);
72
73impl Deref for SharedShape {
74    type Target = dyn Shape;
75    fn deref(&self) -> &dyn Shape {
76        &*self.0
77    }
78}
79
80impl AsRef<dyn Shape> for SharedShape {
81    fn as_ref(&self) -> &dyn Shape {
82        &*self.0
83    }
84}
85
86impl fmt::Debug for SharedShape {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        let typed_shape: TypedShape = (*self.0).as_typed_shape();
89        write!(f, "SharedShape ( Arc<{typed_shape:?}> )")
90    }
91}
92
93impl SharedShape {
94    /// Wraps any shape type into a `SharedShape`.
95    ///
96    /// This constructor accepts any type that implements the [`Shape`] trait and wraps it
97    /// in an `Arc` for shared ownership.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
103    /// # use parry3d::shape::{SharedShape, Ball};
104    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
105    /// # use parry2d::shape::{SharedShape, Ball};
106    ///
107    /// let ball = Ball::new(1.0);
108    /// let shared = SharedShape::new(ball);
109    /// # }
110    /// # }
111    /// ```
112    pub fn new(shape: impl Shape) -> Self {
113        Self(Arc::new(shape))
114    }
115
116    /// Returns a mutable reference to the underlying shape, cloning it if necessary.
117    ///
118    /// This method implements copy-on-write semantics. If the `Arc` has multiple references,
119    /// it will clone the shape data to create a unique instance. Otherwise, no cloning occurs.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
125    /// # use parry3d::shape::SharedShape;
126    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
127    /// # use parry2d::shape::SharedShape;
128    ///
129    /// let mut shape1 = SharedShape::ball(1.0);
130    /// let shape2 = shape1.clone(); // Shares the same Arc
131    ///
132    /// // This will clone the shape because it's shared
133    /// let ball = shape1.make_mut().as_ball_mut().unwrap();
134    /// ball.radius = 2.0;
135    ///
136    /// // shape1 has been modified, shape2 still has the original value
137    /// assert_eq!(shape1.as_ball().unwrap().radius, 2.0);
138    /// assert_eq!(shape2.as_ball().unwrap().radius, 1.0);
139    /// # }
140    /// # }
141    /// ```
142    pub fn make_mut(&mut self) -> &mut dyn Shape {
143        if Arc::get_mut(&mut self.0).is_none() {
144            let unique_self = self.0.clone_dyn();
145            self.0 = unique_self.into();
146        }
147        Arc::get_mut(&mut self.0).unwrap()
148    }
149
150    /// Creates a compound shape made of multiple subshapes.
151    ///
152    /// Each subshape has its own position and orientation relative to the compound's origin.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
158    /// # use parry3d::shape::SharedShape;
159    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
160    /// # use parry3d::math::Isometry;
161    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
162    /// # use parry2d::shape::SharedShape;
163    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
164    /// # use parry2d::math::Isometry;
165    ///
166    /// let ball1 = SharedShape::ball(0.5);
167    /// let ball2 = SharedShape::ball(0.5);
168    ///
169    /// #[cfg(feature = "dim3")]
170    /// let compound = SharedShape::compound(vec![
171    ///     (Isometry::translation(1.0, 0.0, 0.0), ball1),
172    ///     (Isometry::translation(-1.0, 0.0, 0.0), ball2),
173    /// ]);
174    ///
175    /// #[cfg(feature = "dim2")]
176    /// let compound = SharedShape::compound(vec![
177    ///     (Isometry::translation(1.0, 0.0), ball1),
178    ///     (Isometry::translation(-1.0, 0.0), ball2),
179    /// ]);
180    /// # }
181    /// # }
182    /// # }
183    /// # }
184    /// ```
185    pub fn compound(shapes: Vec<(Isometry<Real>, SharedShape)>) -> Self {
186        let raw_shapes = shapes.into_iter().map(|s| (s.0, s.1)).collect();
187        let compound = Compound::new(raw_shapes);
188        SharedShape(Arc::new(compound))
189    }
190
191    /// Creates a ball (sphere in 3D, circle in 2D) with the specified radius.
192    ///
193    /// A ball is the simplest and most efficient collision shape.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
199    /// # use parry3d::shape::SharedShape;
200    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
201    /// # use parry2d::shape::SharedShape;
202    ///
203    /// let ball = SharedShape::ball(1.0);
204    /// assert_eq!(ball.as_ball().unwrap().radius, 1.0);
205    /// # }
206    /// # }
207    /// ```
208    pub fn ball(radius: Real) -> Self {
209        SharedShape(Arc::new(Ball::new(radius)))
210    }
211
212    /// Initialize a plane shape defined by its outward normal.
213    pub fn halfspace(outward_normal: Unit<Vector<Real>>) -> Self {
214        SharedShape(Arc::new(HalfSpace::new(outward_normal)))
215    }
216
217    /// Initialize a cylindrical shape defined by its half-height
218    /// (along the y axis) and its radius.
219    #[cfg(feature = "dim3")]
220    pub fn cylinder(half_height: Real, radius: Real) -> Self {
221        SharedShape(Arc::new(Cylinder::new(half_height, radius)))
222    }
223
224    /// Initialize a rounded cylindrical shape defined by its half-height
225    /// (along along the y axis), its radius, and its roundedness (the
226    /// radius of the sphere used for dilating the cylinder).
227    #[cfg(feature = "dim3")]
228    pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
229        SharedShape(Arc::new(RoundShape {
230            inner_shape: Cylinder::new(half_height, radius),
231            border_radius,
232        }))
233    }
234
235    /// Initialize a rounded cone shape defined by its half-height
236    /// (along along the y axis), its radius, and its roundedness (the
237    /// radius of the sphere used for dilating the cylinder).
238    #[cfg(feature = "dim3")]
239    pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
240        SharedShape(Arc::new(RoundShape {
241            inner_shape: Cone::new(half_height, radius),
242            border_radius,
243        }))
244    }
245
246    /// Initialize a cone shape defined by its half-height
247    /// (along along the y axis) and its basis radius.
248    #[cfg(feature = "dim3")]
249    pub fn cone(half_height: Real, radius: Real) -> Self {
250        SharedShape(Arc::new(Cone::new(half_height, radius)))
251    }
252
253    /// Initialize a cuboid shape defined by its half-extents.
254    #[cfg(feature = "dim2")]
255    pub fn cuboid(hx: Real, hy: Real) -> Self {
256        SharedShape(Arc::new(Cuboid::new(Vector::new(hx, hy))))
257    }
258
259    /// Initialize a round cuboid shape defined by its half-extents and border radius.
260    #[cfg(feature = "dim2")]
261    pub fn round_cuboid(hx: Real, hy: Real, border_radius: Real) -> Self {
262        SharedShape(Arc::new(RoundShape {
263            inner_shape: Cuboid::new(Vector::new(hx, hy)),
264            border_radius,
265        }))
266    }
267
268    /// Creates a cuboid (box) with specified half-extents.
269    ///
270    /// In 3D: creates a box defined by half-extents along X, Y, and Z axes.
271    /// In 2D: creates a rectangle defined by half-extents along X and Y axes.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
277    /// # use parry3d::shape::SharedShape;
278    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
279    /// # use parry2d::shape::SharedShape;
280    ///
281    /// #[cfg(feature = "dim3")]
282    /// let cuboid = SharedShape::cuboid(1.0, 2.0, 3.0); // Box with dimensions 2x4x6
283    /// #[cfg(feature = "dim2")]
284    /// let cuboid = SharedShape::cuboid(1.0, 2.0); // Rectangle with dimensions 2x4
285    /// # }
286    /// # }
287    /// ```
288    #[cfg(feature = "dim3")]
289    pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
290        SharedShape(Arc::new(Cuboid::new(Vector::new(hx, hy, hz))))
291    }
292
293    /// Initialize a round cuboid shape defined by its half-extents and border radius.
294    #[cfg(feature = "dim3")]
295    pub fn round_cuboid(hx: Real, hy: Real, hz: Real, border_radius: Real) -> Self {
296        SharedShape(Arc::new(RoundShape {
297            inner_shape: Cuboid::new(Vector::new(hx, hy, hz)),
298            border_radius,
299        }))
300    }
301
302    /// Initialize a capsule shape from its endpoints and radius.
303    pub fn capsule(a: Point<Real>, b: Point<Real>, radius: Real) -> Self {
304        SharedShape(Arc::new(Capsule::new(a, b, radius)))
305    }
306
307    /// Initialize a capsule shape aligned with the `x` axis.
308    pub fn capsule_x(half_height: Real, radius: Real) -> Self {
309        let p = Point::from(Vector::x() * half_height);
310        Self::capsule(-p, p, radius)
311    }
312
313    /// Creates a capsule aligned with the Y axis.
314    ///
315    /// A capsule is a line segment with rounded ends, commonly used for character controllers.
316    ///
317    /// # Examples
318    ///
319    /// ```
320    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
321    /// # use parry3d::shape::SharedShape;
322    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
323    /// # use parry2d::shape::SharedShape;
324    ///
325    /// // Create a character capsule: 1.8 units tall with 0.3 radius
326    /// let character = SharedShape::capsule_y(0.9, 0.3);
327    /// # }
328    /// # }
329    /// ```
330    pub fn capsule_y(half_height: Real, radius: Real) -> Self {
331        let p = Point::from(Vector::y() * half_height);
332        Self::capsule(-p, p, radius)
333    }
334
335    /// Initialize a capsule shape aligned with the `z` axis.
336    #[cfg(feature = "dim3")]
337    pub fn capsule_z(half_height: Real, radius: Real) -> Self {
338        let p = Point::from(Vector::z() * half_height);
339        Self::capsule(-p, p, radius)
340    }
341
342    /// Initialize a segment shape from its endpoints.
343    pub fn segment(a: Point<Real>, b: Point<Real>) -> Self {
344        SharedShape(Arc::new(Segment::new(a, b)))
345    }
346
347    /// Initializes a triangle shape.
348    pub fn triangle(a: Point<Real>, b: Point<Real>, c: Point<Real>) -> Self {
349        SharedShape(Arc::new(Triangle::new(a, b, c)))
350    }
351    /// Initializes a triangle shape with round corners.
352    pub fn round_triangle(
353        a: Point<Real>,
354        b: Point<Real>,
355        c: Point<Real>,
356        border_radius: Real,
357    ) -> Self {
358        SharedShape(Arc::new(RoundShape {
359            inner_shape: Triangle::new(a, b, c),
360            border_radius,
361        }))
362    }
363
364    /// Initializes a polyline shape defined by its vertex and index buffers.
365    ///
366    /// If no index buffer is provided, the polyline is assumed to describe a line strip.
367    pub fn polyline(vertices: Vec<Point<Real>>, indices: Option<Vec<[u32; 2]>>) -> Self {
368        SharedShape(Arc::new(Polyline::new(vertices, indices)))
369    }
370
371    /// Creates a triangle mesh shape from vertices and triangle indices.
372    ///
373    /// A triangle mesh represents a complex surface as a collection of triangles, useful for
374    /// terrain, static geometry, or any shape that can't be represented by simpler primitives.
375    ///
376    /// # Returns
377    ///
378    /// Returns `Ok(SharedShape)` if the mesh is valid, or an error if indices are out of bounds.
379    ///
380    /// # Examples
381    ///
382    /// ```
383    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
384    /// # use parry3d::shape::SharedShape;
385    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
386    /// # use parry3d::math::Point;
387    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
388    /// # use parry2d::shape::SharedShape;
389    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
390    /// # use parry2d::math::Point;
391    ///
392    /// #[cfg(feature = "dim3")]
393    /// let vertices = vec![
394    ///     Point::new(0.0, 0.0, 0.0),
395    ///     Point::new(1.0, 0.0, 0.0),
396    ///     Point::new(0.0, 1.0, 0.0),
397    /// ];
398    /// #[cfg(feature = "dim3")]
399    /// let indices = vec![[0, 1, 2]];
400    /// #[cfg(feature = "dim3")]
401    /// let mesh = SharedShape::trimesh(vertices, indices).unwrap();
402    /// # }
403    /// # }
404    /// # }
405    /// # }
406    /// ```
407    pub fn trimesh(
408        vertices: Vec<Point<Real>>,
409        indices: Vec<[u32; 3]>,
410    ) -> Result<Self, TriMeshBuilderError> {
411        Ok(SharedShape(Arc::new(TriMesh::new(vertices, indices)?)))
412    }
413
414    /// Initializes a triangle mesh shape defined by its vertex and index buffers and
415    /// pre-processing flags.
416    pub fn trimesh_with_flags(
417        vertices: Vec<Point<Real>>,
418        indices: Vec<[u32; 3]>,
419        flags: TriMeshFlags,
420    ) -> Result<Self, TriMeshBuilderError> {
421        Ok(SharedShape(Arc::new(TriMesh::with_flags(
422            vertices, indices, flags,
423        )?)))
424    }
425
426    /// Initializes a shape made of voxels.
427    ///
428    /// Each voxel has the size `voxel_size` and grid coordinate given by `grid_coords`.
429    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
430    ///
431    /// For initializing a voxels shape from points in space, see [`Self::voxels_from_points`].
432    /// For initializing a voxels shape from a mesh to voxelize, see [`Self::voxelized_mesh`].
433    /// For initializing multiple voxels shape from the convex decomposition of a mesh, see
434    /// [`Self::voxelized_convex_decomposition`].
435    pub fn voxels(voxel_size: Vector<Real>, grid_coords: &[Point<i32>]) -> Self {
436        let shape = Voxels::new(voxel_size, grid_coords);
437        SharedShape::new(shape)
438    }
439
440    /// Initializes a shape made of voxels.
441    ///
442    /// Each voxel has the size `voxel_size` and contains at least one point from `centers`.
443    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
444    pub fn voxels_from_points(voxel_size: Vector<Real>, points: &[Point<Real>]) -> Self {
445        let shape = Voxels::from_points(voxel_size, points);
446        SharedShape::new(shape)
447    }
448
449    /// Initializes a voxels shape obtained from the decomposition of the given trimesh (in 3D)
450    /// or polyline (in 2D) into voxelized convex parts.
451    pub fn voxelized_mesh(
452        vertices: &[Point<Real>],
453        indices: &[[u32; DIM]],
454        voxel_size: Real,
455        fill_mode: FillMode,
456    ) -> Self {
457        let mut voxels = VoxelSet::with_voxel_size(vertices, indices, voxel_size, fill_mode, true);
458        voxels.compute_bb();
459        Self::from_voxel_set(&voxels)
460    }
461
462    fn from_voxel_set(vox_set: &VoxelSet) -> Self {
463        let centers: Vec<_> = vox_set
464            .voxels()
465            .iter()
466            .map(|v| vox_set.get_voxel_point(v))
467            .collect();
468        let shape = Voxels::from_points(Vector::repeat(vox_set.scale), &centers);
469        SharedShape::new(shape)
470    }
471
472    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D)
473    /// or polyline (in 2D) into voxelized convex parts.
474    pub fn voxelized_convex_decomposition(
475        vertices: &[Point<Real>],
476        indices: &[[u32; DIM]],
477    ) -> Vec<Self> {
478        Self::voxelized_convex_decomposition_with_params(
479            vertices,
480            indices,
481            &VHACDParameters::default(),
482        )
483    }
484
485    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D)
486    /// or polyline (in 2D) into voxelized convex parts.
487    pub fn voxelized_convex_decomposition_with_params(
488        vertices: &[Point<Real>],
489        indices: &[[u32; DIM]],
490        params: &VHACDParameters,
491    ) -> Vec<Self> {
492        let mut parts = vec![];
493        let decomp = VHACD::decompose(params, vertices, indices, true);
494
495        for vox_set in decomp.voxel_parts() {
496            parts.push(Self::from_voxel_set(vox_set));
497        }
498
499        parts
500    }
501
502    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D) or
503    /// polyline (in 2D) into convex parts.
504    pub fn convex_decomposition(vertices: &[Point<Real>], indices: &[[u32; DIM]]) -> Self {
505        Self::convex_decomposition_with_params(vertices, indices, &VHACDParameters::default())
506    }
507
508    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D) or
509    /// polyline (in 2D) into convex parts dilated with round corners.
510    pub fn round_convex_decomposition(
511        vertices: &[Point<Real>],
512        indices: &[[u32; DIM]],
513        border_radius: Real,
514    ) -> Self {
515        Self::round_convex_decomposition_with_params(
516            vertices,
517            indices,
518            &VHACDParameters::default(),
519            border_radius,
520        )
521    }
522
523    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D) or
524    /// polyline (in 2D) into convex parts.
525    pub fn convex_decomposition_with_params(
526        vertices: &[Point<Real>],
527        indices: &[[u32; DIM]],
528        params: &VHACDParameters,
529    ) -> Self {
530        let mut parts = vec![];
531        let decomp = VHACD::decompose(params, vertices, indices, true);
532
533        #[cfg(feature = "dim2")]
534        for vertices in decomp.compute_exact_convex_hulls(vertices, indices) {
535            if let Some(convex) = Self::convex_polyline(vertices) {
536                parts.push((Isometry::identity(), convex));
537            }
538        }
539
540        #[cfg(feature = "dim3")]
541        for (vertices, indices) in decomp.compute_exact_convex_hulls(vertices, indices) {
542            if let Some(convex) = Self::convex_mesh(vertices, &indices) {
543                parts.push((Isometry::identity(), convex));
544            }
545        }
546
547        Self::compound(parts)
548    }
549
550    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D) or
551    /// polyline (in 2D) into convex parts dilated with round corners.
552    pub fn round_convex_decomposition_with_params(
553        vertices: &[Point<Real>],
554        indices: &[[u32; DIM]],
555        params: &VHACDParameters,
556        border_radius: Real,
557    ) -> Self {
558        let mut parts = vec![];
559        let decomp = VHACD::decompose(params, vertices, indices, true);
560
561        #[cfg(feature = "dim2")]
562        for vertices in decomp.compute_exact_convex_hulls(vertices, indices) {
563            if let Some(convex) = Self::round_convex_polyline(vertices, border_radius) {
564                parts.push((Isometry::identity(), convex));
565            }
566        }
567
568        #[cfg(feature = "dim3")]
569        for (vertices, indices) in decomp.compute_exact_convex_hulls(vertices, indices) {
570            if let Some(convex) = Self::round_convex_mesh(vertices, &indices, border_radius) {
571                parts.push((Isometry::identity(), convex));
572            }
573        }
574
575        Self::compound(parts)
576    }
577
578    /// Creates a new shared shape that is the convex-hull of the given points.
579    pub fn convex_hull(points: &[Point<Real>]) -> Option<Self> {
580        #[cfg(feature = "dim2")]
581        return ConvexPolygon::from_convex_hull(points).map(|ch| SharedShape(Arc::new(ch)));
582        #[cfg(feature = "dim3")]
583        return ConvexPolyhedron::from_convex_hull(points).map(|ch| SharedShape(Arc::new(ch)));
584    }
585
586    /// Creates a new shared shape that is a 2D convex polygon from a set of points assumed to
587    /// describe a counter-clockwise convex polyline.
588    ///
589    /// This does **not** compute the convex-hull of the input points: convexity of the input is
590    /// assumed and not checked. For a version that calculates the convex hull of the input points,
591    /// use [`SharedShape::convex_hull`] instead.
592    ///
593    /// The generated [`ConvexPolygon`] will contain the given `points` with any point
594    /// collinear to the previous and next ones removed. For a version that leaves the input
595    /// `points` unmodified, use [`SharedShape::convex_polyline_unmodified`].
596    ///
597    /// Returns `None` if all points form an almost flat line.
598    #[cfg(feature = "dim2")]
599    pub fn convex_polyline(points: Vec<Point<Real>>) -> Option<Self> {
600        ConvexPolygon::from_convex_polyline(points).map(|ch| SharedShape(Arc::new(ch)))
601    }
602
603    /// Creates a new shared shape that is a 2D convex polygon from a set of points assumed to
604    /// describe a counter-clockwise convex polyline.
605    ///
606    /// This is the same as [`SharedShape::convex_polyline`] but without removing any point
607    /// from the input even if some are coplanar.
608    ///
609    /// Returns `None` if `points` doesn’t contain at least three points.
610    #[cfg(feature = "dim2")]
611    pub fn convex_polyline_unmodified(points: Vec<Point<Real>>) -> Option<Self> {
612        ConvexPolygon::from_convex_polyline_unmodified(points).map(|ch| SharedShape(Arc::new(ch)))
613    }
614
615    /// Creates a new shared shape that is a convex polyhedron formed by the
616    /// given set of points assumed to form a convex mesh (no convex-hull will be automatically
617    /// computed).
618    #[cfg(feature = "dim3")]
619    pub fn convex_mesh(points: Vec<Point<Real>>, indices: &[[u32; 3]]) -> Option<Self> {
620        ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| SharedShape(Arc::new(ch)))
621    }
622
623    /// Creates a new shared shape with rounded corners that is the
624    /// convex-hull of the given points, dilated by `border_radius`.
625    pub fn round_convex_hull(points: &[Point<Real>], border_radius: Real) -> Option<Self> {
626        #[cfg(feature = "dim2")]
627        return ConvexPolygon::from_convex_hull(points).map(|ch| {
628            SharedShape(Arc::new(RoundShape {
629                inner_shape: ch,
630                border_radius,
631            }))
632        });
633        #[cfg(feature = "dim3")]
634        return ConvexPolyhedron::from_convex_hull(points).map(|ch| {
635            SharedShape(Arc::new(RoundShape {
636                inner_shape: ch,
637                border_radius,
638            }))
639        });
640    }
641
642    /// Creates a new shared shape with round corners that is a convex polygon formed by the
643    /// given set of points assumed to form a convex polyline (no convex-hull will be automatically
644    /// computed).
645    #[cfg(feature = "dim2")]
646    pub fn round_convex_polyline(points: Vec<Point<Real>>, border_radius: Real) -> Option<Self> {
647        ConvexPolygon::from_convex_polyline(points).map(|ch| {
648            SharedShape(Arc::new(RoundShape {
649                inner_shape: ch,
650                border_radius,
651            }))
652        })
653    }
654
655    /// Creates a new shared shape with round corners that is a convex polyhedron formed by the
656    /// given set of points assumed to form a convex mesh (no convex-hull will be automatically
657    /// computed).
658    #[cfg(feature = "dim3")]
659    pub fn round_convex_mesh(
660        points: Vec<Point<Real>>,
661        indices: &[[u32; 3]],
662        border_radius: Real,
663    ) -> Option<Self> {
664        ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| {
665            SharedShape(Arc::new(RoundShape {
666                inner_shape: ch,
667                border_radius,
668            }))
669        })
670    }
671
672    /// Initializes a heightfield shape defined by its set of height and a scale
673    /// factor along each coordinate axis.
674    #[cfg(feature = "dim2")]
675    pub fn heightfield(heights: na::DVector<Real>, scale: Vector<Real>) -> Self {
676        SharedShape(Arc::new(HeightField::new(heights, scale)))
677    }
678
679    /// Initializes a heightfield shape on the x-z plane defined by its set of height and a scale
680    /// factor along each coordinate axis.
681    #[cfg(feature = "dim3")]
682    pub fn heightfield(heights: na::DMatrix<Real>, scale: Vector<Real>) -> Self {
683        SharedShape(Arc::new(HeightField::new(heights, scale)))
684    }
685
686    /// Initializes a heightfield shape on the x-z plane defined by its set of height, a scale
687    /// factor along each coordinate axis, and [`HeightFieldFlags`].
688    #[cfg(feature = "dim3")]
689    pub fn heightfield_with_flags(
690        heights: na::DMatrix<Real>,
691        scale: Vector<Real>,
692        flags: HeightFieldFlags,
693    ) -> Self {
694        SharedShape(Arc::new(HeightField::with_flags(heights, scale, flags)))
695    }
696}
697
698#[cfg(feature = "serde-serialize")]
699impl serde::Serialize for SharedShape {
700    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
701    where
702        S: serde::Serializer,
703    {
704        self.0.as_typed_shape().serialize(serializer)
705    }
706}
707
708#[cfg(feature = "serde-serialize")]
709impl<'de> serde::Deserialize<'de> for SharedShape {
710    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
711    where
712        D: serde::Deserializer<'de>,
713    {
714        use crate::serde::de::Error;
715        DeserializableTypedShape::deserialize(deserializer)?
716            .into_shared_shape()
717            .ok_or(D::Error::custom("Cannot deserialize custom shape."))
718    }
719}