parry3d/shape/
shared_shape.rs

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