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