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), ¢ers);
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}