parry2d/shape/trimesh.rs
1use crate::bounding_volume::Aabb;
2use crate::math::{Isometry, Point, Real, Vector};
3use crate::partitioning::{Bvh, BvhBuildStrategy};
4use crate::shape::{FeatureId, Shape, Triangle, TrianglePseudoNormals, TypedCompositeShape};
5use crate::utils::HashablePartialEq;
6use alloc::{vec, vec::Vec};
7use core::fmt;
8#[cfg(feature = "dim3")]
9use {crate::shape::Cuboid, crate::utils::SortedPair, na::Unit};
10
11use {
12 crate::shape::composite_shape::CompositeShape,
13 crate::utils::hashmap::{Entry, HashMap},
14 crate::utils::hashset::HashSet,
15};
16
17#[cfg(feature = "dim2")]
18use crate::transformation::ear_clipping::triangulate_ear_clipping;
19
20use crate::query::details::NormalConstraints;
21#[cfg(feature = "rkyv")]
22use rkyv::{bytecheck, CheckBytes};
23
24/// Errors that occur when computing or validating triangle mesh topology.
25///
26/// Triangle mesh topology describes the connectivity and adjacency relationships between
27/// vertices, edges, and triangles. When constructing a mesh with [`TriMeshFlags::HALF_EDGE_TOPOLOGY`],
28/// Parry validates these relationships and returns this error if inconsistencies are found.
29///
30/// # When This Occurs
31///
32/// Topology errors typically occur when:
33/// - The mesh is non-manifold (edges with more than 2 adjacent faces)
34/// - Adjacent triangles have inconsistent winding order
35/// - Triangles have degenerate geometry (duplicate vertices)
36///
37/// [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]: crate::shape::TriMeshFlags::HALF_EDGE_TOPOLOGY
38#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
39pub enum TopologyError {
40 /// A triangle has two or three identical vertices (degenerate triangle).
41 ///
42 /// This error indicates that a triangle in the mesh references the same vertex
43 /// multiple times, creating a degenerate triangle (zero area). For example,
44 /// a triangle with indices `[5, 5, 7]` or `[1, 2, 1]`.
45 ///
46 /// Degenerate triangles cannot be part of a valid mesh topology because they
47 /// don't have proper edges or face normals.
48 ///
49 // /// TODO: figure out why this doc-test fails.
50 // /// # How to Fix
51 // ///
52 // /// ```
53 // /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
54 // /// # use parry3d::shape::{TriMesh, TriMeshFlags};
55 // /// # use nalgebra::Point3;
56 // /// let vertices = vec![
57 // /// Point3::origin(),
58 // /// Point3::new(1.0, 0.0, 0.0),
59 // /// Point3::new(0.0, 1.0, 0.0),
60 // /// ];
61 // ///
62 // /// // BAD: Triangle with duplicate vertices
63 // /// let bad_indices = vec![[0, 0, 1]]; // vertex 0 appears twice!
64 // ///
65 // /// let result = TriMesh::with_flags(
66 // /// vertices.clone(),
67 // /// bad_indices,
68 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
69 // /// );
70 // /// assert!(result.is_err());
71 // ///
72 // /// // GOOD: All three vertices are distinct
73 // /// let good_indices = vec![[0, 1, 2]];
74 // /// let mesh = TriMesh::with_flags(
75 // /// vertices,
76 // /// good_indices,
77 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
78 // /// ).expect("Valid mesh");
79 // /// # }
80 // /// ```
81 ///
82 /// Alternatively, use [`TriMeshFlags::DELETE_DEGENERATE_TRIANGLES`] to automatically
83 /// remove degenerate triangles:
84 ///
85 /// ```
86 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
87 /// # use parry3d::shape::{TriMesh, TriMeshFlags};
88 /// # use nalgebra::Point3;
89 /// # let vertices = vec![Point3::origin(), Point3::new(1.0, 0.0, 0.0), Point3::new(0.0, 1.0, 0.0)];
90 /// # let indices = vec![[0, 0, 1], [0, 1, 2]];
91 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY
92 /// | TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES;
93 ///
94 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
95 /// .expect("Bad triangles removed");
96 /// # }
97 /// ```
98 #[error("the triangle {0} has at least two identical vertices.")]
99 BadTriangle(u32),
100
101 /// Two adjacent triangles have opposite orientations (inconsistent winding).
102 ///
103 /// For a manifold mesh with consistent normals, adjacent triangles must have
104 /// compatible orientations. If two triangles share an edge, they must traverse
105 /// that edge in opposite directions.
106 ///
107 /// This error reports:
108 /// - `triangle1`, `triangle2`: The indices of the two conflicting triangles
109 /// - `edge`: The shared edge as a pair of vertex indices `(v1, v2)`
110 ///
111 /// # Example of the Problem
112 ///
113 /// ```text
114 /// CORRECT (opposite winding on shared edge):
115 /// Triangle 1: [a, b, c] -> edge (a,b)
116 /// Triangle 2: [b, a, d] -> edge (b,a) ✓ opposite direction
117 ///
118 /// INCORRECT (same winding on shared edge):
119 /// Triangle 1: [a, b, c] -> edge (a,b)
120 /// Triangle 2: [a, b, d] -> edge (a,b) ✗ same direction!
121 /// ```
122 ///
123 // /// TODO: figure out why this doc test fails?
124 // /// # How to Fix
125 // ///
126 // /// You need to reverse the winding order of one of the triangles:
127 // ///
128 // /// ```
129 // /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
130 // /// # use parry3d::shape::{TriMesh, TriMeshFlags};
131 // /// # use nalgebra::Point3;
132 // /// let vertices = vec![
133 // /// Point3::origin(), // vertex 0
134 // /// Point3::new(1.0, 0.0, 0.0), // vertex 1
135 // /// Point3::new(0.5, 1.0, 0.0), // vertex 2
136 // /// Point3::new(0.5, -1.0, 0.0), // vertex 3
137 // /// ];
138 // ///
139 // /// // BAD: Both triangles traverse edge (0,1) in the same direction
140 // /// let bad_indices = vec![
141 // /// [0, 1, 2], // edge (0,1)
142 // /// [0, 1, 3], // edge (0,1) - same direction!
143 // /// ];
144 // ///
145 // /// let result = TriMesh::with_flags(
146 // /// vertices.clone(),
147 // /// bad_indices,
148 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
149 // /// );
150 // /// assert!(result.is_err());
151 // ///
152 // /// // GOOD: Second triangle reversed to [1, 0, 3]
153 // /// let good_indices = vec![
154 // /// [0, 1, 2], // edge (0,1)
155 // /// [1, 0, 3], // edge (1,0) - opposite direction!
156 // /// ];
157 // ///
158 // /// let mesh = TriMesh::with_flags(
159 // /// vertices,
160 // /// good_indices,
161 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
162 // /// ).expect("Valid mesh");
163 // /// # }
164 // /// ```
165 ///
166 /// # Common Causes
167 ///
168 /// - Mixing clockwise and counter-clockwise triangle definitions
169 /// - Incorrectly constructed mesh from modeling software
170 /// - Manual mesh construction with inconsistent winding
171 /// - Merging separate meshes with different conventions
172 #[error("the triangles {triangle1} and {triangle2} sharing the edge {edge:?} have opposite orientations.")]
173 BadAdjacentTrianglesOrientation {
174 /// The first triangle, with an orientation opposite to the second triangle.
175 triangle1: u32,
176 /// The second triangle, with an orientation opposite to the first triangle.
177 triangle2: u32,
178 /// The edge shared between the two triangles.
179 edge: (u32, u32),
180 },
181}
182
183/// Errors that occur when creating a triangle mesh.
184///
185/// When constructing a [`TriMesh`] using [`TriMesh::new`] or [`TriMesh::with_flags`],
186/// various validation checks are performed. This error type describes what went wrong.
187///
188/// # Common Usage
189///
190/// ```
191/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
192/// # use parry3d::shape::{TriMesh, TriMeshBuilderError, TopologyError};
193/// # use nalgebra::Point3;
194/// let vertices = vec![Point3::origin()];
195/// let indices = vec![]; // Empty!
196///
197/// match TriMesh::new(vertices, indices) {
198/// Err(TriMeshBuilderError::EmptyIndices) => {
199/// println!("Cannot create a mesh with no triangles");
200/// }
201/// Err(TriMeshBuilderError::TopologyError(topo_err)) => {
202/// println!("Mesh topology is invalid: {}", topo_err);
203/// }
204/// Ok(mesh) => {
205/// println!("Mesh created successfully");
206/// }
207/// }
208/// # }
209/// ```
210///
211/// [`TriMesh`]: crate::shape::TriMesh
212/// [`TriMesh::new`]: crate::shape::TriMesh::new
213/// [`TriMesh::with_flags`]: crate::shape::TriMesh::with_flags
214#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
215pub enum TriMeshBuilderError {
216 /// The index buffer is empty (no triangles provided).
217 ///
218 /// A triangle mesh must contain at least one triangle. An empty index buffer
219 /// is not valid because there's nothing to render or use for collision detection.
220 ///
221 /// # How to Fix
222 ///
223 /// Ensure your index buffer has at least one triangle:
224 ///
225 /// ```
226 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
227 /// # use parry3d::shape::TriMesh;
228 /// # use nalgebra::Point3;
229 /// let vertices = vec![
230 /// Point3::origin(),
231 /// Point3::new(1.0, 0.0, 0.0),
232 /// Point3::new(0.0, 1.0, 0.0),
233 /// ];
234 ///
235 /// // BAD: No triangles
236 /// let empty_indices = vec![];
237 /// assert!(TriMesh::new(vertices.clone(), empty_indices).is_err());
238 ///
239 /// // GOOD: At least one triangle
240 /// let indices = vec![[0, 1, 2]];
241 /// assert!(TriMesh::new(vertices, indices).is_ok());
242 /// # }
243 /// ```
244 #[error("A triangle mesh must contain at least one triangle.")]
245 EmptyIndices,
246
247 /// The mesh topology is invalid.
248 ///
249 /// This wraps a [`TopologyError`] that provides details about the specific
250 /// topology problem. This only occurs when creating a mesh with topology
251 /// validation enabled (e.g., [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]).
252 ///
253 /// See [`TopologyError`] for details on specific topology problems and how
254 /// to fix them.
255 ///
256 /// # Example
257 ///
258 /// ```
259 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
260 /// # use parry3d::shape::{TriMesh, TriMeshFlags, TriMeshBuilderError};
261 /// # use nalgebra::Point3;
262 /// let vertices = vec![
263 /// Point3::origin(),
264 /// Point3::new(1.0, 0.0, 0.0),
265 /// Point3::new(0.0, 1.0, 0.0),
266 /// ];
267 ///
268 /// // Triangle with duplicate vertices
269 /// let bad_indices = vec![[0, 0, 1]];
270 ///
271 /// match TriMesh::with_flags(vertices, bad_indices, TriMeshFlags::HALF_EDGE_TOPOLOGY) {
272 /// Err(TriMeshBuilderError::TopologyError(topo_err)) => {
273 /// println!("Topology error: {}", topo_err);
274 /// // Handle the specific topology issue
275 /// }
276 /// _ => {}
277 /// }
278 /// # }
279 /// ```
280 ///
281 /// [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]: crate::shape::TriMeshFlags::HALF_EDGE_TOPOLOGY
282 #[error("Topology Error: {0}")]
283 TopologyError(TopologyError),
284}
285
286/// The set of pseudo-normals of a triangle mesh.
287///
288/// These pseudo-normals are used for the inside-outside test of a
289/// point on the triangle, as described in the paper:
290/// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
291/// DOI: 10.1109/TVCG.2005.49
292#[derive(Default, Clone)]
293#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
294#[cfg_attr(
295 feature = "rkyv",
296 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
297 archive(check_bytes)
298)]
299#[repr(C)]
300#[cfg(feature = "dim3")]
301pub struct TriMeshPseudoNormals {
302 /// The pseudo-normals of the vertices.
303 pub vertices_pseudo_normal: Vec<Vector<Real>>,
304 /// The pseudo-normals of the edges.
305 pub edges_pseudo_normal: Vec<[Vector<Real>; 3]>,
306}
307
308/// The connected-components of a triangle mesh.
309#[derive(Debug, Clone)]
310#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
311#[cfg_attr(
312 feature = "rkyv",
313 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
314 archive(check_bytes)
315)]
316#[repr(C)]
317pub struct TriMeshConnectedComponents {
318 /// The `face_colors[i]` gives the connected-component index
319 /// of the i-th face.
320 pub face_colors: Vec<u32>,
321 /// The set of faces grouped by connected components.
322 pub grouped_faces: Vec<u32>,
323 /// The range of connected components. `self.grouped_faces[self.ranges[i]..self.ranges[i + 1]]`
324 /// contains the indices of all the faces part of the i-th connected component.
325 pub ranges: Vec<usize>,
326}
327
328impl TriMeshConnectedComponents {
329 /// The total number of connected components.
330 pub fn num_connected_components(&self) -> usize {
331 self.ranges.len() - 1
332 }
333
334 /// Convert the connected-component description into actual meshes (returned as raw index and
335 /// vertex buffers).
336 ///
337 /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
338 /// unexpected result.
339 pub fn to_mesh_buffers(&self, mesh: &TriMesh) -> Vec<(Vec<Point<Real>>, Vec<[u32; 3]>)> {
340 let mut result = vec![];
341 let mut new_vtx_index: Vec<_> = vec![u32::MAX; mesh.vertices.len()];
342
343 for ranges in self.ranges.windows(2) {
344 let num_faces = ranges[1] - ranges[0];
345
346 if num_faces == 0 {
347 continue;
348 }
349
350 let mut vertices = Vec::with_capacity(num_faces);
351 let mut indices = Vec::with_capacity(num_faces);
352
353 for fid in ranges[0]..ranges[1] {
354 let vids = mesh.indices[self.grouped_faces[fid] as usize];
355 let new_vids = vids.map(|id| {
356 if new_vtx_index[id as usize] == u32::MAX {
357 vertices.push(mesh.vertices[id as usize]);
358 new_vtx_index[id as usize] = vertices.len() as u32 - 1;
359 }
360
361 new_vtx_index[id as usize]
362 });
363 indices.push(new_vids);
364 }
365
366 result.push((vertices, indices));
367 }
368
369 result
370 }
371
372 /// Convert the connected-component description into actual meshes.
373 ///
374 /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
375 /// unexpected result.
376 ///
377 /// All the meshes are constructed with the given `flags`.
378 pub fn to_meshes(
379 &self,
380 mesh: &TriMesh,
381 flags: TriMeshFlags,
382 ) -> Vec<Result<TriMesh, TriMeshBuilderError>> {
383 self.to_mesh_buffers(mesh)
384 .into_iter()
385 .map(|(vtx, idx)| TriMesh::with_flags(vtx, idx, flags))
386 .collect()
387 }
388}
389
390/// A vertex of a triangle-mesh’s half-edge topology.
391#[derive(Clone, Copy, Debug)]
392#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
393#[cfg_attr(
394 feature = "rkyv",
395 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
396 archive(as = "Self")
397)]
398#[repr(C)]
399pub struct TopoVertex {
400 /// One of the half-edge with this vertex as endpoint.
401 pub half_edge: u32,
402}
403
404/// A face of a triangle-mesh’s half-edge topology.
405#[derive(Clone, Copy, Debug)]
406#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
407#[cfg_attr(
408 feature = "rkyv",
409 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
410 archive(as = "Self")
411)]
412#[repr(C)]
413pub struct TopoFace {
414 /// The half-edge adjacent to this face, with a starting point equal
415 /// to the first point of this face.
416 pub half_edge: u32,
417}
418
419/// A half-edge of a triangle-mesh’s half-edge topology.
420#[derive(Clone, Copy, Debug)]
421#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
422#[cfg_attr(
423 feature = "rkyv",
424 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
425 archive(as = "Self")
426)]
427#[repr(C)]
428pub struct TopoHalfEdge {
429 /// The next half-edge.
430 pub next: u32,
431 /// This half-edge twin on the adjacent triangle.
432 ///
433 /// This is `u32::MAX` if there is no twin.
434 pub twin: u32,
435 /// The first vertex of this edge.
436 pub vertex: u32,
437 /// The face associated to this half-edge.
438 pub face: u32,
439}
440
441/// The half-edge topology information of a triangle mesh.
442#[derive(Default, Clone)]
443#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
444#[cfg_attr(
445 feature = "rkyv",
446 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
447 archive(check_bytes)
448)]
449#[repr(C)]
450pub struct TriMeshTopology {
451 /// The vertices of this half-edge representation.
452 pub vertices: Vec<TopoVertex>,
453 /// The faces of this half-edge representation.
454 pub faces: Vec<TopoFace>,
455 /// The half-edges of this half-edge representation.
456 pub half_edges: Vec<TopoHalfEdge>,
457}
458
459#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
460#[cfg_attr(
461 feature = "rkyv",
462 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
463 archive(as = "Self")
464)]
465#[repr(C)]
466#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
467/// Controls how a [`TriMesh`] should be loaded.
468pub struct TriMeshFlags(u16);
469
470bitflags::bitflags! {
471 impl TriMeshFlags: u16 {
472 /// If set, the half-edge topology of the trimesh will be computed if possible.
473 const HALF_EDGE_TOPOLOGY = 1;
474 /// If set, the connected components of the trimesh will be computed.
475 const CONNECTED_COMPONENTS = 1 << 1;
476 /// If set, any triangle that results in a failing half-hedge topology computation will be deleted.
477 const DELETE_BAD_TOPOLOGY_TRIANGLES = 1 << 2;
478 /// If set, the trimesh will be assumed to be oriented (with outward normals).
479 ///
480 /// The pseudo-normals of its vertices and edges will be computed.
481 const ORIENTED = 1 << 3;
482 /// If set, the duplicate vertices of the trimesh will be merged.
483 ///
484 /// Two vertices with the exact same coordinates will share the same entry on the
485 /// vertex buffer and the index buffer is adjusted accordingly.
486 const MERGE_DUPLICATE_VERTICES = 1 << 4;
487 /// If set, the triangles sharing two vertices with identical index values will be removed.
488 ///
489 /// Because of the way it is currently implemented, this methods implies that duplicate
490 /// vertices will be merged. It will no longer be the case in the future once we decouple
491 /// the computations.
492 const DELETE_DEGENERATE_TRIANGLES = 1 << 5;
493 /// If set, two triangles sharing three vertices with identical index values (in any order)
494 /// will be removed.
495 ///
496 /// Because of the way it is currently implemented, this methods implies that duplicate
497 /// vertices will be merged. It will no longer be the case in the future once we decouple
498 /// the computations.
499 const DELETE_DUPLICATE_TRIANGLES = 1 << 6;
500 /// If set, a special treatment will be applied to contact manifold calculation to eliminate
501 /// or fix contacts normals that could lead to incorrect bumps in physics simulation
502 /// (especially on flat surfaces).
503 ///
504 /// This is achieved by taking into account adjacent triangle normals when computing contact
505 /// points for a given triangle.
506 const FIX_INTERNAL_EDGES = (1 << 7) | Self::MERGE_DUPLICATE_VERTICES.bits();
507 }
508}
509
510#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
511#[cfg_attr(
512 feature = "rkyv",
513 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
514 archive(check_bytes)
515)]
516#[repr(C)]
517#[derive(Clone)]
518/// A triangle mesh.
519pub struct TriMesh {
520 bvh: Bvh,
521 vertices: Vec<Point<Real>>,
522 indices: Vec<[u32; 3]>,
523 #[cfg(feature = "dim3")]
524 pub(crate) pseudo_normals: Option<TriMeshPseudoNormals>,
525 topology: Option<TriMeshTopology>,
526 connected_components: Option<TriMeshConnectedComponents>,
527 flags: TriMeshFlags,
528}
529
530impl fmt::Debug for TriMesh {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 write!(f, "GenericTriMesh")
533 }
534}
535
536impl TriMesh {
537 /// Creates a new triangle mesh from a vertex buffer and an index buffer.
538 ///
539 /// This is the most common way to construct a `TriMesh`. The mesh is created with
540 /// default settings (no topology computation, no pseudo-normals, etc.). For more
541 /// control over these optional features, use [`TriMesh::with_flags`] instead.
542 ///
543 /// # Arguments
544 ///
545 /// * `vertices` - A vector of 3D points representing the mesh vertices
546 /// * `indices` - A vector of triangles, where each triangle is represented by
547 /// three indices into the vertex buffer. Indices should be in counter-clockwise
548 /// order for outward-facing normals.
549 ///
550 /// # Returns
551 ///
552 /// * `Ok(TriMesh)` - If the mesh was successfully created
553 /// * `Err(TriMeshBuilderError)` - If the mesh is invalid (e.g., empty index buffer)
554 ///
555 /// # Errors
556 ///
557 /// This function returns an error if:
558 /// - The index buffer is empty (at least one triangle is required)
559 ///
560 /// # Performance
561 ///
562 /// This function builds a BVH (Bounding Volume Hierarchy) acceleration structure
563 /// for the mesh, which takes O(n log n) time where n is the number of triangles.
564 ///
565 /// # Example
566 ///
567 /// ```
568 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
569 /// use parry3d::shape::TriMesh;
570 /// use nalgebra::Point3;
571 ///
572 /// // Create a simple triangle mesh (a single triangle)
573 /// let vertices = vec![
574 /// Point3::origin(),
575 /// Point3::new(1.0, 0.0, 0.0),
576 /// Point3::new(0.0, 1.0, 0.0),
577 /// ];
578 /// let indices = vec![[0, 1, 2]];
579 ///
580 /// let trimesh = TriMesh::new(vertices, indices).expect("Invalid mesh");
581 /// assert_eq!(trimesh.num_triangles(), 1);
582 /// # }
583 /// ```
584 ///
585 /// ```
586 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
587 /// use parry2d::shape::TriMesh;
588 /// use nalgebra::Point2;
589 ///
590 /// // Create a quad (two triangles) in 2D
591 /// let vertices = vec![
592 /// Point2::origin(), // bottom-left
593 /// Point2::new(1.0, 0.0), // bottom-right
594 /// Point2::new(1.0, 1.0), // top-right
595 /// Point2::new(0.0, 1.0), // top-left
596 /// ];
597 /// let indices = vec![
598 /// [0, 1, 2], // first triangle
599 /// [0, 2, 3], // second triangle
600 /// ];
601 ///
602 /// let quad = TriMesh::new(vertices, indices).unwrap();
603 /// assert_eq!(quad.num_triangles(), 2);
604 /// assert_eq!(quad.vertices().len(), 4);
605 /// # }
606 /// ```
607 pub fn new(
608 vertices: Vec<Point<Real>>,
609 indices: Vec<[u32; 3]>,
610 ) -> Result<Self, TriMeshBuilderError> {
611 Self::with_flags(vertices, indices, TriMeshFlags::empty())
612 }
613
614 /// Creates a new triangle mesh from a vertex buffer and an index buffer, and flags controlling optional properties.
615 ///
616 /// This is the most flexible way to create a `TriMesh`, allowing you to specify exactly
617 /// which optional features should be computed. Use this when you need:
618 /// - Half-edge topology for adjacency information
619 /// - Connected components analysis
620 /// - Pseudo-normals for robust inside/outside tests
621 /// - Automatic merging of duplicate vertices
622 /// - Removal of degenerate or duplicate triangles
623 ///
624 /// # Arguments
625 ///
626 /// * `vertices` - A vector of 3D points representing the mesh vertices
627 /// * `indices` - A vector of triangles, where each triangle is represented by
628 /// three indices into the vertex buffer
629 /// * `flags` - A combination of [`TriMeshFlags`] controlling which optional features to compute
630 ///
631 /// # Returns
632 ///
633 /// * `Ok(TriMesh)` - If the mesh was successfully created
634 /// * `Err(TriMeshBuilderError)` - If the mesh is invalid or topology computation failed
635 ///
636 /// # Errors
637 ///
638 /// This function returns an error if:
639 /// - The index buffer is empty
640 /// - [`TriMeshFlags::HALF_EDGE_TOPOLOGY`] is set but the topology is invalid
641 /// (e.g., non-manifold edges or inconsistent orientations)
642 ///
643 /// # Performance
644 ///
645 /// - Base construction: O(n log n) for BVH building
646 /// - `HALF_EDGE_TOPOLOGY`: O(n) where n is the number of triangles
647 /// - `CONNECTED_COMPONENTS`: O(n) with union-find
648 /// - `ORIENTED` or `FIX_INTERNAL_EDGES`: O(n) for pseudo-normal computation
649 /// - `MERGE_DUPLICATE_VERTICES`: O(n) with hash map lookups
650 ///
651 /// # Example
652 ///
653 /// ```
654 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
655 /// use parry3d::shape::{TriMesh, TriMeshFlags};
656 /// use nalgebra::Point3;
657 ///
658 /// // Create vertices for a simple mesh
659 /// let vertices = vec![
660 /// Point3::origin(),
661 /// Point3::new(1.0, 0.0, 0.0),
662 /// Point3::new(0.0, 1.0, 0.0),
663 /// Point3::new(1.0, 1.0, 0.0),
664 /// ];
665 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
666 ///
667 /// // Create a mesh with half-edge topology
668 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY;
669 /// let mesh = TriMesh::with_flags(vertices.clone(), indices.clone(), flags)
670 /// .expect("Failed to create mesh");
671 ///
672 /// // The topology information is now available
673 /// assert!(mesh.topology().is_some());
674 /// # }
675 /// ```
676 ///
677 /// ```
678 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
679 /// use parry3d::shape::{TriMesh, TriMeshFlags};
680 /// use nalgebra::Point3;
681 ///
682 /// # let vertices = vec![
683 /// # Point3::origin(),
684 /// # Point3::new(1.0, 0.0, 0.0),
685 /// # Point3::new(0.0, 1.0, 0.0),
686 /// # ];
687 /// # let indices = vec![[0, 1, 2]];
688 /// // Combine multiple flags for advanced features
689 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY
690 /// | TriMeshFlags::MERGE_DUPLICATE_VERTICES
691 /// | TriMeshFlags::DELETE_DEGENERATE_TRIANGLES;
692 ///
693 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
694 /// .expect("Failed to create mesh");
695 /// # }
696 /// ```
697 ///
698 /// ```
699 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
700 /// use parry3d::shape::{TriMesh, TriMeshFlags};
701 /// use nalgebra::Point3;
702 ///
703 /// # let vertices = vec![
704 /// # Point3::origin(),
705 /// # Point3::new(1.0, 0.0, 0.0),
706 /// # Point3::new(0.0, 1.0, 0.0),
707 /// # ];
708 /// # let indices = vec![[0, 1, 2]];
709 /// // For robust point containment tests in 3D
710 /// let flags = TriMeshFlags::ORIENTED;
711 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
712 /// .expect("Failed to create mesh");
713 ///
714 /// // Pseudo-normals are now computed for accurate inside/outside tests
715 /// assert!(mesh.pseudo_normals().is_some());
716 /// # }
717 /// ```
718 pub fn with_flags(
719 vertices: Vec<Point<Real>>,
720 indices: Vec<[u32; 3]>,
721 flags: TriMeshFlags,
722 ) -> Result<Self, TriMeshBuilderError> {
723 if indices.is_empty() {
724 return Err(TriMeshBuilderError::EmptyIndices);
725 }
726
727 let mut result = Self {
728 bvh: Bvh::new(),
729 vertices,
730 indices,
731 #[cfg(feature = "dim3")]
732 pseudo_normals: None,
733 topology: None,
734 connected_components: None,
735 flags: TriMeshFlags::empty(),
736 };
737
738 let _ = result.set_flags(flags);
739
740 if result.bvh.is_empty() {
741 // The BVH hasn’t been computed by `.set_flags`.
742 result.rebuild_bvh();
743 }
744
745 Ok(result)
746 }
747
748 /// Sets the flags of this triangle mesh, controlling its optional associated data.
749 pub fn set_flags(&mut self, flags: TriMeshFlags) -> Result<(), TopologyError> {
750 let mut result = Ok(());
751 let prev_indices_len = self.indices.len();
752
753 if !flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
754 self.topology = None;
755 }
756
757 #[cfg(feature = "dim3")]
758 if !flags.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
759 self.pseudo_normals = None;
760 }
761
762 if !flags.contains(TriMeshFlags::CONNECTED_COMPONENTS) {
763 self.connected_components = None;
764 }
765
766 let difference = flags & !self.flags;
767
768 if difference.intersects(
769 TriMeshFlags::MERGE_DUPLICATE_VERTICES
770 | TriMeshFlags::DELETE_DEGENERATE_TRIANGLES
771 | TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
772 ) {
773 self.merge_duplicate_vertices(
774 flags.contains(TriMeshFlags::DELETE_DEGENERATE_TRIANGLES),
775 flags.contains(TriMeshFlags::DELETE_DUPLICATE_TRIANGLES),
776 )
777 }
778
779 if difference.intersects(
780 TriMeshFlags::HALF_EDGE_TOPOLOGY | TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES,
781 ) {
782 result =
783 self.compute_topology(flags.contains(TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES));
784 }
785
786 #[cfg(feature = "std")]
787 if difference.intersects(TriMeshFlags::CONNECTED_COMPONENTS) {
788 self.compute_connected_components();
789 }
790
791 #[cfg(feature = "dim3")]
792 if difference.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
793 self.compute_pseudo_normals();
794 }
795
796 if prev_indices_len != self.indices.len() {
797 self.rebuild_bvh();
798 }
799
800 self.flags = flags;
801 result
802 }
803
804 // TODO: support a crate like get_size2 (will require support on nalgebra too)?
805 /// An approximation of the memory usage (in bytes) for this struct plus
806 /// the memory it allocates dynamically.
807 pub fn total_memory_size(&self) -> usize {
808 size_of::<Self>() + self.heap_memory_size()
809 }
810
811 /// An approximation of the memory dynamically-allocated by this struct.
812 pub fn heap_memory_size(&self) -> usize {
813 // NOTE: if a new field is added to `Self`, adjust this function result.
814 let Self {
815 bvh,
816 vertices,
817 indices,
818 topology,
819 connected_components,
820 flags: _,
821 #[cfg(feature = "dim3")]
822 pseudo_normals,
823 } = self;
824 let sz_bvh = bvh.heap_memory_size();
825 let sz_vertices = vertices.capacity() * size_of::<Point<Real>>();
826 let sz_indices = indices.capacity() * size_of::<[u32; 3]>();
827 #[cfg(feature = "dim3")]
828 let sz_pseudo_normals = pseudo_normals
829 .as_ref()
830 .map(|pn| {
831 pn.vertices_pseudo_normal.capacity() * size_of::<Vector<Real>>()
832 + pn.edges_pseudo_normal.capacity() * size_of::<[Vector<Real>; 3]>()
833 })
834 .unwrap_or(0);
835 #[cfg(feature = "dim2")]
836 let sz_pseudo_normals = 0;
837 let sz_topology = topology
838 .as_ref()
839 .map(|t| {
840 t.vertices.capacity() * size_of::<TopoVertex>()
841 + t.faces.capacity() * size_of::<TopoFace>()
842 + t.half_edges.capacity() * size_of::<TopoHalfEdge>()
843 })
844 .unwrap_or(0);
845 let sz_connected_components = connected_components
846 .as_ref()
847 .map(|c| {
848 c.face_colors.capacity() * size_of::<u32>()
849 + c.grouped_faces.capacity() * size_of::<f32>()
850 + c.ranges.capacity() * size_of::<usize>()
851 })
852 .unwrap_or(0);
853
854 sz_bvh
855 + sz_vertices
856 + sz_indices
857 + sz_pseudo_normals
858 + sz_topology
859 + sz_connected_components
860 }
861
862 /// Transforms in-place the vertices of this triangle mesh.
863 ///
864 /// Applies a rigid transformation (rotation and translation) to all vertices
865 /// of the mesh. This is useful for positioning or orienting a mesh in world space.
866 /// The transformation also updates the BVH and pseudo-normals (if present).
867 ///
868 /// # Arguments
869 ///
870 /// * `transform` - The isometry (rigid transformation) to apply to all vertices
871 ///
872 /// # Performance
873 ///
874 /// This operation is O(n) for transforming vertices, plus O(n log n) for
875 /// rebuilding the BVH, where n is the number of triangles.
876 ///
877 /// # Example
878 ///
879 /// ```
880 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
881 /// use parry3d::shape::TriMesh;
882 /// use nalgebra::{Point3, Vector3, Isometry3};
883 ///
884 /// let vertices = vec![
885 /// Point3::origin(),
886 /// Point3::new(1.0, 0.0, 0.0),
887 /// Point3::new(0.0, 1.0, 0.0),
888 /// ];
889 /// let indices = vec![[0, 1, 2]];
890 /// let mut mesh = TriMesh::new(vertices, indices).unwrap();
891 ///
892 /// // Translate the mesh by (10, 0, 0)
893 /// let transform = Isometry3::translation(10.0, 0.0, 0.0);
894 /// mesh.transform_vertices(&transform);
895 ///
896 /// // All vertices are now shifted
897 /// assert_eq!(mesh.vertices()[0], Point3::new(10.0, 0.0, 0.0));
898 /// assert_eq!(mesh.vertices()[1], Point3::new(11.0, 0.0, 0.0));
899 /// assert_eq!(mesh.vertices()[2], Point3::new(10.0, 1.0, 0.0));
900 /// # }
901 /// ```
902 pub fn transform_vertices(&mut self, transform: &Isometry<Real>) {
903 self.vertices
904 .iter_mut()
905 .for_each(|pt| *pt = transform * *pt);
906 self.rebuild_bvh();
907
908 // The pseudo-normals must be rotated too.
909 #[cfg(feature = "dim3")]
910 if let Some(pseudo_normals) = &mut self.pseudo_normals {
911 pseudo_normals
912 .vertices_pseudo_normal
913 .iter_mut()
914 .for_each(|n| *n = transform * *n);
915 pseudo_normals.edges_pseudo_normal.iter_mut().for_each(|n| {
916 n[0] = transform * n[0];
917 n[1] = transform * n[1];
918 n[2] = transform * n[2];
919 });
920 }
921 }
922
923 /// Returns a scaled version of this triangle mesh.
924 ///
925 /// Creates a new mesh with all vertices scaled by the given per-axis scale factors.
926 /// Unlike rigid transformations, scaling can change the shape of the mesh (when
927 /// scale factors differ between axes).
928 ///
929 /// The scaling also updates pseudo-normals (if present) to remain valid after
930 /// the transformation, and efficiently scales the BVH structure.
931 ///
932 /// # Arguments
933 ///
934 /// * `scale` - The scale factors for each axis (x, y, z in 3D)
935 ///
936 /// # Returns
937 ///
938 /// A new `TriMesh` with scaled vertices
939 ///
940 /// # Example
941 ///
942 /// ```
943 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
944 /// use parry3d::shape::TriMesh;
945 /// use nalgebra::{Point3, Vector3};
946 ///
947 /// let vertices = vec![
948 /// Point3::origin(),
949 /// Point3::new(1.0, 0.0, 0.0),
950 /// Point3::new(0.0, 1.0, 0.0),
951 /// ];
952 /// let indices = vec![[0, 1, 2]];
953 /// let mesh = TriMesh::new(vertices, indices).unwrap();
954 ///
955 /// // Uniform scaling: double all dimensions
956 /// let scaled = mesh.clone().scaled(&Vector3::new(2.0, 2.0, 2.0));
957 /// assert_eq!(scaled.vertices()[1], Point3::new(2.0, 0.0, 0.0));
958 ///
959 /// // Non-uniform scaling: stretch along X axis
960 /// let stretched = mesh.scaled(&Vector3::new(3.0, 1.0, 1.0));
961 /// assert_eq!(stretched.vertices()[1], Point3::new(3.0, 0.0, 0.0));
962 /// assert_eq!(stretched.vertices()[2], Point3::new(0.0, 1.0, 0.0));
963 /// # }
964 /// ```
965 pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
966 self.vertices
967 .iter_mut()
968 .for_each(|pt| pt.coords.component_mul_assign(scale));
969
970 #[cfg(feature = "dim3")]
971 if let Some(pn) = &mut self.pseudo_normals {
972 pn.vertices_pseudo_normal.iter_mut().for_each(|n| {
973 n.component_mul_assign(scale);
974 let _ = n.try_normalize_mut(0.0);
975 });
976 pn.edges_pseudo_normal.iter_mut().for_each(|n| {
977 n[0].component_mul_assign(scale);
978 n[1].component_mul_assign(scale);
979 n[2].component_mul_assign(scale);
980
981 let _ = n[0].try_normalize_mut(0.0);
982 let _ = n[1].try_normalize_mut(0.0);
983 let _ = n[2].try_normalize_mut(0.0);
984 });
985 }
986
987 let mut bvh = self.bvh.clone();
988 bvh.scale(scale);
989
990 Self {
991 bvh,
992 vertices: self.vertices,
993 indices: self.indices,
994 #[cfg(feature = "dim3")]
995 pseudo_normals: self.pseudo_normals,
996 topology: self.topology,
997 connected_components: self.connected_components,
998 flags: self.flags,
999 }
1000 }
1001
1002 /// Appends a second triangle mesh to this triangle mesh.
1003 ///
1004 /// This combines two meshes into one by adding all vertices and triangles from
1005 /// the `rhs` mesh to this mesh. The vertex indices in the appended triangles
1006 /// are automatically adjusted to reference the correct vertices in the combined
1007 /// vertex buffer.
1008 ///
1009 /// After appending, all optional features (topology, pseudo-normals, etc.) are
1010 /// recomputed according to this mesh's current flags.
1011 ///
1012 /// # Arguments
1013 ///
1014 /// * `rhs` - The mesh to append to this mesh
1015 ///
1016 /// # Performance
1017 ///
1018 /// This operation rebuilds the entire mesh with all its features, which can be
1019 /// expensive for large meshes. Time complexity is O(n log n) where n is the
1020 /// total number of triangles after appending.
1021 ///
1022 /// # Example
1023 ///
1024 /// ```
1025 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1026 /// use parry3d::shape::TriMesh;
1027 /// use nalgebra::Point3;
1028 ///
1029 /// // Create first mesh (a triangle)
1030 /// let vertices1 = vec![
1031 /// Point3::origin(),
1032 /// Point3::new(1.0, 0.0, 0.0),
1033 /// Point3::new(0.0, 1.0, 0.0),
1034 /// ];
1035 /// let indices1 = vec![[0, 1, 2]];
1036 /// let mut mesh1 = TriMesh::new(vertices1, indices1).unwrap();
1037 ///
1038 /// // Create second mesh (another triangle, offset in space)
1039 /// let vertices2 = vec![
1040 /// Point3::new(2.0, 0.0, 0.0),
1041 /// Point3::new(3.0, 0.0, 0.0),
1042 /// Point3::new(2.0, 1.0, 0.0),
1043 /// ];
1044 /// let indices2 = vec![[0, 1, 2]];
1045 /// let mesh2 = TriMesh::new(vertices2, indices2).unwrap();
1046 ///
1047 /// // Append second mesh to first
1048 /// mesh1.append(&mesh2);
1049 ///
1050 /// assert_eq!(mesh1.num_triangles(), 2);
1051 /// assert_eq!(mesh1.vertices().len(), 6);
1052 /// # }
1053 /// ```
1054 pub fn append(&mut self, rhs: &TriMesh) {
1055 let base_id = self.vertices.len() as u32;
1056 self.vertices.extend_from_slice(rhs.vertices());
1057 self.indices.extend(
1058 rhs.indices()
1059 .iter()
1060 .map(|idx| [idx[0] + base_id, idx[1] + base_id, idx[2] + base_id]),
1061 );
1062
1063 let vertices = core::mem::take(&mut self.vertices);
1064 let indices = core::mem::take(&mut self.indices);
1065 *self = TriMesh::with_flags(vertices, indices, self.flags).unwrap();
1066 }
1067
1068 /// Create a `TriMesh` from a set of points assumed to describe a counter-clockwise non-convex polygon.
1069 ///
1070 /// This function triangulates a 2D polygon using ear clipping algorithm. The polygon
1071 /// can be convex or concave, but must be simple (no self-intersections) and have
1072 /// counter-clockwise winding order.
1073 ///
1074 /// # Arguments
1075 ///
1076 /// * `vertices` - The vertices of the polygon in counter-clockwise order
1077 ///
1078 /// # Returns
1079 ///
1080 /// * `Some(TriMesh)` - If triangulation succeeded
1081 /// * `None` - If the polygon is invalid (self-intersecting, degenerate, etc.)
1082 ///
1083 /// # Requirements
1084 ///
1085 /// - Polygon must be simple (no self-intersections)
1086 /// - Vertices must be in counter-clockwise order
1087 /// - Polygon must have non-zero area
1088 /// - Only available in 2D (`dim2` feature)
1089 ///
1090 /// # Performance
1091 ///
1092 /// Ear clipping has O(n²) time complexity in the worst case, where n is the
1093 /// number of vertices.
1094 ///
1095 /// # Example
1096 ///
1097 /// ```
1098 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
1099 /// use parry2d::shape::TriMesh;
1100 /// use nalgebra::Point2;
1101 ///
1102 /// // Create a simple concave polygon (L-shape)
1103 /// let vertices = vec![
1104 /// Point2::origin(),
1105 /// Point2::new(2.0, 0.0),
1106 /// Point2::new(2.0, 1.0),
1107 /// Point2::new(1.0, 1.0),
1108 /// Point2::new(1.0, 2.0),
1109 /// Point2::new(0.0, 2.0),
1110 /// ];
1111 ///
1112 /// let mesh = TriMesh::from_polygon(vertices)
1113 /// .expect("Failed to triangulate polygon");
1114 ///
1115 /// // The polygon has been triangulated
1116 /// assert!(mesh.num_triangles() > 0);
1117 /// # }
1118 /// ```
1119 #[cfg(feature = "dim2")]
1120 pub fn from_polygon(vertices: Vec<Point<Real>>) -> Option<Self> {
1121 triangulate_ear_clipping(&vertices).map(|indices| Self::new(vertices, indices).unwrap())
1122 }
1123
1124 /// A flat view of the index buffer of this mesh.
1125 ///
1126 /// Returns the triangle indices as a flat array of `u32` values, where every
1127 /// three consecutive values form a triangle. This is useful for interfacing
1128 /// with graphics APIs that expect flat index buffers.
1129 ///
1130 /// # Example
1131 ///
1132 /// ```
1133 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1134 /// use parry3d::shape::TriMesh;
1135 /// use nalgebra::Point3;
1136 ///
1137 /// let vertices = vec![
1138 /// Point3::origin(),
1139 /// Point3::new(1.0, 0.0, 0.0),
1140 /// Point3::new(0.0, 1.0, 0.0),
1141 /// Point3::new(1.0, 1.0, 0.0),
1142 /// ];
1143 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1144 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1145 ///
1146 /// let flat = mesh.flat_indices();
1147 /// assert_eq!(flat, &[0, 1, 2, 1, 3, 2]);
1148 /// assert_eq!(flat.len(), mesh.num_triangles() * 3);
1149 /// # }
1150 /// ```
1151 pub fn flat_indices(&self) -> &[u32] {
1152 unsafe {
1153 let len = self.indices.len() * 3;
1154 let data = self.indices.as_ptr() as *const u32;
1155 core::slice::from_raw_parts(data, len)
1156 }
1157 }
1158
1159 fn rebuild_bvh(&mut self) {
1160 let leaves = self.indices.iter().enumerate().map(|(i, idx)| {
1161 let aabb = Triangle::new(
1162 self.vertices[idx[0] as usize],
1163 self.vertices[idx[1] as usize],
1164 self.vertices[idx[2] as usize],
1165 )
1166 .local_aabb();
1167 (i, aabb)
1168 });
1169
1170 self.bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves)
1171 }
1172
1173 /// Reverse the orientation of the triangle mesh.
1174 ///
1175 /// This flips the winding order of all triangles, effectively turning the mesh
1176 /// "inside-out". If triangles had counter-clockwise winding (outward normals),
1177 /// they will have clockwise winding (inward normals) after this operation.
1178 ///
1179 /// This is useful when:
1180 /// - A mesh was imported with incorrect orientation
1181 /// - You need to flip normals for rendering or physics
1182 /// - Creating the "back side" of a mesh
1183 ///
1184 /// # Performance
1185 ///
1186 /// This operation modifies triangles in-place (O(n)), but recomputes topology
1187 /// and pseudo-normals if they were present, which can be expensive for large meshes.
1188 ///
1189 /// # Example
1190 ///
1191 /// ```
1192 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1193 /// use parry3d::shape::TriMesh;
1194 /// use nalgebra::Point3;
1195 ///
1196 /// let vertices = vec![
1197 /// Point3::origin(),
1198 /// Point3::new(1.0, 0.0, 0.0),
1199 /// Point3::new(0.0, 1.0, 0.0),
1200 /// ];
1201 /// let indices = vec![[0, 1, 2]];
1202 ///
1203 /// let mut mesh = TriMesh::new(vertices, indices).unwrap();
1204 /// let original_triangle = mesh.triangle(0);
1205 ///
1206 /// // Reverse the mesh orientation
1207 /// mesh.reverse();
1208 ///
1209 /// let reversed_triangle = mesh.triangle(0);
1210 ///
1211 /// // The first two vertices are swapped
1212 /// assert_eq!(original_triangle.a, reversed_triangle.b);
1213 /// assert_eq!(original_triangle.b, reversed_triangle.a);
1214 /// assert_eq!(original_triangle.c, reversed_triangle.c);
1215 /// # }
1216 /// ```
1217 pub fn reverse(&mut self) {
1218 self.indices.iter_mut().for_each(|idx| idx.swap(0, 1));
1219
1220 // NOTE: the BVH, and connected components are not changed by this operation.
1221 // The pseudo-normals just have to be flipped.
1222 // The topology must be recomputed.
1223
1224 #[cfg(feature = "dim3")]
1225 if let Some(pseudo_normals) = &mut self.pseudo_normals {
1226 for n in &mut pseudo_normals.vertices_pseudo_normal {
1227 *n = -*n;
1228 }
1229
1230 for n in pseudo_normals.edges_pseudo_normal.iter_mut() {
1231 n[0] = -n[0];
1232 n[1] = -n[1];
1233 n[2] = -n[2];
1234 }
1235 }
1236
1237 if self.flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
1238 // TODO: this could be done more efficiently.
1239 let _ = self.compute_topology(false);
1240 }
1241 }
1242
1243 /// Merge all duplicate vertices and adjust the index buffer accordingly.
1244 ///
1245 /// If `delete_degenerate_triangles` is set to true, any triangle with two
1246 /// identical vertices will be removed.
1247 ///
1248 /// This is typically used to recover a vertex buffer from which we can deduce
1249 /// adjacency information. between triangles by observing how the vertices are
1250 /// shared by triangles based on the index buffer.
1251 fn merge_duplicate_vertices(
1252 &mut self,
1253 delete_degenerate_triangles: bool,
1254 delete_duplicate_triangles: bool,
1255 ) {
1256 let mut vtx_to_id = HashMap::default();
1257 let mut new_vertices = Vec::with_capacity(self.vertices.len());
1258 let mut new_indices = Vec::with_capacity(self.indices.len());
1259 let mut triangle_set = HashSet::default();
1260
1261 fn resolve_coord_id(
1262 coord: &Point<Real>,
1263 vtx_to_id: &mut HashMap<HashablePartialEq<Point<Real>>, u32>,
1264 new_vertices: &mut Vec<Point<Real>>,
1265 ) -> u32 {
1266 let key = HashablePartialEq::new(*coord);
1267 let id = match vtx_to_id.entry(key) {
1268 Entry::Occupied(entry) => entry.into_mut(),
1269 Entry::Vacant(entry) => entry.insert(new_vertices.len() as u32),
1270 };
1271
1272 if *id == new_vertices.len() as u32 {
1273 new_vertices.push(*coord);
1274 }
1275
1276 *id
1277 }
1278
1279 for t in self.indices.iter() {
1280 let va = resolve_coord_id(
1281 &self.vertices[t[0] as usize],
1282 &mut vtx_to_id,
1283 &mut new_vertices,
1284 );
1285
1286 let vb = resolve_coord_id(
1287 &self.vertices[t[1] as usize],
1288 &mut vtx_to_id,
1289 &mut new_vertices,
1290 );
1291
1292 let vc = resolve_coord_id(
1293 &self.vertices[t[2] as usize],
1294 &mut vtx_to_id,
1295 &mut new_vertices,
1296 );
1297
1298 let is_degenerate = va == vb || va == vc || vb == vc;
1299
1300 if !is_degenerate || !delete_degenerate_triangles {
1301 if delete_duplicate_triangles {
1302 let (c, b, a) = crate::utils::sort3(&va, &vb, &vc);
1303 if triangle_set.insert((*a, *b, *c)) {
1304 new_indices.push([va, vb, vc])
1305 }
1306 } else {
1307 new_indices.push([va, vb, vc]);
1308 }
1309 }
1310 }
1311
1312 new_vertices.shrink_to_fit();
1313
1314 self.vertices = new_vertices;
1315 self.indices = new_indices;
1316
1317 // Vertices and indices changed: the pseudo-normals are no longer valid.
1318 #[cfg(feature = "dim3")]
1319 if self.pseudo_normals.is_some() {
1320 self.compute_pseudo_normals();
1321 }
1322
1323 // Vertices and indices changed: the topology no longer valid.
1324 #[cfg(feature = "dim3")]
1325 if self.topology.is_some() {
1326 let _ = self.compute_topology(false);
1327 }
1328 }
1329
1330 #[cfg(feature = "dim3")]
1331 /// Computes the pseudo-normals used for solid point-projection.
1332 ///
1333 /// This computes the pseudo-normals needed by the point containment test described in
1334 /// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
1335 /// DOI: 10.1109/TVCG.2005.49
1336 ///
1337 /// For the point-containment test to properly detect the inside of the trimesh (i.e. to return
1338 /// `proj.is_inside = true`), the trimesh must:
1339 /// - Be manifold (closed, no t-junctions, etc.)
1340 /// - Be oriented with outward normals.
1341 ///
1342 /// If the trimesh is correctly oriented, but is manifold everywhere except at its boundaries,
1343 /// then the computed pseudo-normals will provide correct point-containment test results except
1344 /// for points closest to the boundary of the mesh.
1345 ///
1346 /// It may be useful to call `self.remove_duplicate_vertices()` before this method, in order to fix the
1347 /// index buffer if some of the vertices of this trimesh are duplicated.
1348 fn compute_pseudo_normals(&mut self) {
1349 let mut vertices_pseudo_normal = vec![Vector::zeros(); self.vertices().len()];
1350 let mut edges_pseudo_normal = HashMap::default();
1351 let mut edges_multiplicity = HashMap::default();
1352
1353 for idx in self.indices() {
1354 let vtx = self.vertices();
1355 let tri = Triangle::new(
1356 vtx[idx[0] as usize],
1357 vtx[idx[1] as usize],
1358 vtx[idx[2] as usize],
1359 );
1360
1361 if let Some(n) = tri.normal() {
1362 let ang1 = (tri.b - tri.a).angle(&(tri.c - tri.a));
1363 let ang2 = (tri.a - tri.b).angle(&(tri.c - tri.b));
1364 let ang3 = (tri.b - tri.c).angle(&(tri.a - tri.c));
1365
1366 vertices_pseudo_normal[idx[0] as usize] += *n * ang1;
1367 vertices_pseudo_normal[idx[1] as usize] += *n * ang2;
1368 vertices_pseudo_normal[idx[2] as usize] += *n * ang3;
1369
1370 let edges = [
1371 SortedPair::new(idx[0], idx[1]),
1372 SortedPair::new(idx[0], idx[2]),
1373 SortedPair::new(idx[1], idx[2]),
1374 ];
1375
1376 for edge in &edges {
1377 let edge_n = edges_pseudo_normal
1378 .entry(*edge)
1379 .or_insert_with(Vector::zeros);
1380 *edge_n += *n; // NOTE: there is no need to multiply by the incident angle since it is always equal to PI for all the edges.
1381 let edge_mult = edges_multiplicity.entry(*edge).or_insert(0);
1382 *edge_mult += 1;
1383 }
1384 }
1385 }
1386
1387 let edges_pseudo_normal = self
1388 .indices()
1389 .iter()
1390 .map(|idx| {
1391 let e0 = SortedPair::new(idx[0], idx[1]);
1392 let e1 = SortedPair::new(idx[1], idx[2]);
1393 let e2 = SortedPair::new(idx[2], idx[0]);
1394 let default = Vector::zeros();
1395 [
1396 edges_pseudo_normal.get(&e0).copied().unwrap_or(default),
1397 edges_pseudo_normal.get(&e1).copied().unwrap_or(default),
1398 edges_pseudo_normal.get(&e2).copied().unwrap_or(default),
1399 ]
1400 })
1401 .collect();
1402
1403 self.pseudo_normals = Some(TriMeshPseudoNormals {
1404 vertices_pseudo_normal,
1405 edges_pseudo_normal,
1406 })
1407 }
1408
1409 fn delete_bad_topology_triangles(&mut self) {
1410 let mut half_edge_set = HashSet::default();
1411 let mut deleted_any = false;
1412
1413 // First, create three half-edges for each face.
1414 self.indices.retain(|idx| {
1415 if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
1416 deleted_any = true;
1417 return false;
1418 }
1419
1420 for k in 0..3 {
1421 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1422 if half_edge_set.contains(&edge_key) {
1423 deleted_any = true;
1424 return false;
1425 }
1426 }
1427
1428 for k in 0..3 {
1429 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1430 let _ = half_edge_set.insert(edge_key);
1431 }
1432
1433 true
1434 });
1435 }
1436
1437 /// Computes half-edge topological information for this triangle mesh, based on its index buffer only.
1438 ///
1439 /// This computes the half-edge representation of this triangle mesh’s topology. This is useful for advanced
1440 /// geometric operations like trimesh-trimesh intersection geometry computation.
1441 ///
1442 /// It may be useful to call `self.merge_duplicate_vertices(true, true)` before this method, in order to fix the
1443 /// index buffer if some of the vertices of this trimesh are duplicated.
1444 ///
1445 /// # Return
1446 /// Returns `true` if the computation succeeded. Returns `false` if this mesh can’t have an half-edge representation
1447 /// because at least three faces share the same edge.
1448 fn compute_topology(&mut self, delete_bad_triangles: bool) -> Result<(), TopologyError> {
1449 if delete_bad_triangles {
1450 self.delete_bad_topology_triangles();
1451 }
1452
1453 let mut topology = TriMeshTopology::default();
1454 let mut half_edge_map = HashMap::default();
1455
1456 topology.vertices.resize(
1457 self.vertices.len(),
1458 TopoVertex {
1459 half_edge: u32::MAX,
1460 },
1461 );
1462
1463 // First, create three half-edges for each face.
1464 for (fid, idx) in self.indices.iter().enumerate() {
1465 let half_edge_base_id = topology.half_edges.len() as u32;
1466
1467 if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
1468 return Err(TopologyError::BadTriangle(fid as u32));
1469 }
1470
1471 for k in 0u32..3 {
1472 let half_edge = TopoHalfEdge {
1473 next: half_edge_base_id + (k + 1) % 3,
1474 // We don’t know which one it is yet.
1475 // If the twin doesn’t exist, we use `u32::MAX` as
1476 // it’s (invalid) index. This value can be relied on
1477 // by other algorithms.
1478 twin: u32::MAX,
1479 vertex: idx[k as usize],
1480 face: fid as u32,
1481 };
1482 topology.half_edges.push(half_edge);
1483
1484 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1485
1486 if let Some(existing) = half_edge_map.insert(edge_key, half_edge_base_id + k) {
1487 // If the same edge already exists (with the same vertex order) then
1488 // we have two triangles sharing the same but with opposite incompatible orientations.
1489 return Err(TopologyError::BadAdjacentTrianglesOrientation {
1490 edge: edge_key,
1491 triangle1: topology.half_edges[existing as usize].face,
1492 triangle2: fid as u32,
1493 });
1494 }
1495
1496 topology.vertices[idx[k as usize] as usize].half_edge = half_edge_base_id + k;
1497 }
1498
1499 topology.faces.push(TopoFace {
1500 half_edge: half_edge_base_id,
1501 })
1502 }
1503
1504 // Second, identify twins.
1505 for (key, he1) in &half_edge_map {
1506 if key.0 < key.1 {
1507 // Test, to avoid checking the same pair twice.
1508 if let Some(he2) = half_edge_map.get(&(key.1, key.0)) {
1509 topology.half_edges[*he1 as usize].twin = *he2;
1510 topology.half_edges[*he2 as usize].twin = *he1;
1511 }
1512 }
1513 }
1514
1515 self.topology = Some(topology);
1516
1517 Ok(())
1518 }
1519
1520 // NOTE: this is private because that calculation is controlled by TriMeshFlags::CONNECTED_COMPONENTS
1521 // TODO: we should remove the CONNECTED_COMPONENTS flags and just have this be a free function.
1522 // TODO: this should be no_std compatible once ena is or once we have an alternative for it.
1523 #[cfg(feature = "std")]
1524 fn compute_connected_components(&mut self) {
1525 use ena::unify::{InPlaceUnificationTable, UnifyKey};
1526
1527 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
1528 struct IntKey(u32);
1529
1530 impl UnifyKey for IntKey {
1531 type Value = ();
1532 fn index(&self) -> u32 {
1533 self.0
1534 }
1535 fn from_index(u: u32) -> IntKey {
1536 IntKey(u)
1537 }
1538 fn tag() -> &'static str {
1539 "IntKey"
1540 }
1541 }
1542
1543 let mut ufind: InPlaceUnificationTable<IntKey> = InPlaceUnificationTable::new();
1544 let mut face_colors = vec![u32::MAX; self.indices.len()];
1545 let mut ranges = vec![0];
1546 let mut vertex_to_range = vec![u32::MAX; self.vertices.len()];
1547 let mut grouped_faces = vec![u32::MAX; self.indices.len()];
1548 let mut vertex_to_key = vec![IntKey(u32::MAX); self.vertices.len()];
1549
1550 let mut vertex_key = |id: u32, ufind: &mut InPlaceUnificationTable<IntKey>| {
1551 if vertex_to_key[id as usize].0 == u32::MAX {
1552 let new_key = ufind.new_key(());
1553 vertex_to_key[id as usize] = new_key;
1554 new_key
1555 } else {
1556 vertex_to_key[id as usize]
1557 }
1558 };
1559
1560 for idx in self.indices() {
1561 let keys = idx.map(|i| vertex_key(i, &mut ufind));
1562 ufind.union(keys[0], keys[1]);
1563 ufind.union(keys[1], keys[2]);
1564 ufind.union(keys[2], keys[0]);
1565 }
1566
1567 for (idx, face_color) in self.indices().iter().zip(face_colors.iter_mut()) {
1568 debug_assert_eq!(
1569 ufind.find(vertex_to_key[idx[0] as usize]),
1570 ufind.find(vertex_to_key[idx[1] as usize])
1571 );
1572 debug_assert_eq!(
1573 ufind.find(vertex_to_key[idx[0] as usize]),
1574 ufind.find(vertex_to_key[idx[2] as usize])
1575 );
1576
1577 let group_index = ufind.find(vertex_to_key[idx[0] as usize]).0 as usize;
1578
1579 if vertex_to_range[group_index] == u32::MAX {
1580 // Additional range
1581 ranges.push(0);
1582 vertex_to_range[group_index] = ranges.len() as u32 - 1;
1583 }
1584
1585 let range_id = vertex_to_range[group_index];
1586 ranges[range_id as usize] += 1;
1587 // NOTE: the range_id points to the range upper bound. The face color is the range lower bound.
1588 *face_color = range_id - 1;
1589 }
1590
1591 // Cumulated sum on range indices, to find the first index faces need to be inserted into
1592 // for each range.
1593 for i in 1..ranges.len() {
1594 ranges[i] += ranges[i - 1];
1595 }
1596
1597 debug_assert_eq!(*ranges.last().unwrap(), self.indices().len());
1598
1599 // Group faces.
1600 let mut insertion_in_range_index = ranges.clone();
1601 for (face_id, face_color) in face_colors.iter().enumerate() {
1602 let insertion_index = &mut insertion_in_range_index[*face_color as usize];
1603 grouped_faces[*insertion_index] = face_id as u32;
1604 *insertion_index += 1;
1605 }
1606
1607 self.connected_components = Some(TriMeshConnectedComponents {
1608 face_colors,
1609 grouped_faces,
1610 ranges,
1611 })
1612 }
1613
1614 #[allow(dead_code)] // Useful for testing.
1615 pub(crate) fn assert_half_edge_topology_is_valid(&self) {
1616 let topo = self
1617 .topology
1618 .as_ref()
1619 .expect("No topology information found.");
1620 assert_eq!(self.vertices.len(), topo.vertices.len());
1621 assert_eq!(self.indices.len(), topo.faces.len());
1622
1623 for (face_id, (face, idx)) in topo.faces.iter().zip(self.indices.iter()).enumerate() {
1624 let he0 = topo.half_edges[face.half_edge as usize];
1625 assert_eq!(he0.face, face_id as u32);
1626 assert_eq!(he0.vertex, idx[0]);
1627 let he1 = topo.half_edges[he0.next as usize];
1628 assert_eq!(he1.face, face_id as u32);
1629 assert_eq!(he1.vertex, idx[1]);
1630 let he2 = topo.half_edges[he1.next as usize];
1631 assert_eq!(he2.face, face_id as u32);
1632 assert_eq!(he2.vertex, idx[2]);
1633 assert_eq!(he2.next, face.half_edge);
1634 }
1635
1636 for he in &topo.half_edges {
1637 let idx = &self.indices[he.face as usize];
1638 assert!(he.vertex == idx[0] || he.vertex == idx[1] || he.vertex == idx[2]);
1639 }
1640 }
1641
1642 /// An iterator through all the triangles of this mesh.
1643 ///
1644 /// Returns an iterator that yields [`Triangle`] shapes representing each
1645 /// triangle in the mesh. Each triangle contains the actual 3D coordinates
1646 /// of its three vertices (not indices).
1647 ///
1648 /// # Performance
1649 ///
1650 /// The iterator performs vertex lookups on-the-fly, so iterating through
1651 /// all triangles is O(n) where n is the number of triangles.
1652 ///
1653 /// # Example
1654 ///
1655 /// ```
1656 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1657 /// use parry3d::shape::TriMesh;
1658 /// use nalgebra::Point3;
1659 ///
1660 /// let vertices = vec![
1661 /// Point3::origin(),
1662 /// Point3::new(1.0, 0.0, 0.0),
1663 /// Point3::new(0.0, 1.0, 0.0),
1664 /// Point3::new(1.0, 1.0, 0.0),
1665 /// ];
1666 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1667 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1668 ///
1669 /// // Iterate through all triangles
1670 /// for triangle in mesh.triangles() {
1671 /// println!("Triangle: {:?}, {:?}, {:?}", triangle.a, triangle.b, triangle.c);
1672 /// }
1673 ///
1674 /// // Count triangles with specific properties
1675 /// let count = mesh.triangles()
1676 /// .filter(|tri| tri.area() > 0.1)
1677 /// .count();
1678 /// # }
1679 /// ```
1680 pub fn triangles(&self) -> impl ExactSizeIterator<Item = Triangle> + '_ {
1681 self.indices.iter().map(move |ids| {
1682 Triangle::new(
1683 self.vertices[ids[0] as usize],
1684 self.vertices[ids[1] as usize],
1685 self.vertices[ids[2] as usize],
1686 )
1687 })
1688 }
1689
1690 #[cfg(feature = "dim3")]
1691 /// Gets the normal of the triangle represented by `feature`.
1692 pub fn feature_normal(&self, feature: FeatureId) -> Option<Unit<Vector<Real>>> {
1693 match feature {
1694 FeatureId::Face(i) => self
1695 .triangle(i % self.num_triangles() as u32)
1696 .feature_normal(FeatureId::Face(0)),
1697 _ => None,
1698 }
1699 }
1700}
1701
1702impl TriMesh {
1703 /// The flags of this triangle mesh.
1704 ///
1705 /// Returns the [`TriMeshFlags`] that were used to construct this mesh,
1706 /// indicating which optional features are enabled.
1707 ///
1708 /// # Example
1709 ///
1710 /// ```
1711 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1712 /// use parry3d::shape::{TriMesh, TriMeshFlags};
1713 /// use nalgebra::Point3;
1714 ///
1715 /// let vertices = vec![
1716 /// Point3::origin(),
1717 /// Point3::new(1.0, 0.0, 0.0),
1718 /// Point3::new(0.0, 1.0, 0.0),
1719 /// ];
1720 /// let indices = vec![[0, 1, 2]];
1721 ///
1722 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY;
1723 /// let mesh = TriMesh::with_flags(vertices, indices, flags).unwrap();
1724 ///
1725 /// assert!(mesh.flags().contains(TriMeshFlags::HALF_EDGE_TOPOLOGY));
1726 /// # }
1727 /// ```
1728 pub fn flags(&self) -> TriMeshFlags {
1729 self.flags
1730 }
1731
1732 /// Compute the axis-aligned bounding box of this triangle mesh.
1733 ///
1734 /// Returns the AABB that tightly bounds all triangles of the mesh after
1735 /// applying the given isometry transformation.
1736 ///
1737 /// # Arguments
1738 ///
1739 /// * `pos` - The position/orientation of the mesh in world space
1740 ///
1741 /// # Example
1742 ///
1743 /// ```
1744 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1745 /// use parry3d::shape::TriMesh;
1746 /// use nalgebra::{Point3, Isometry3};
1747 ///
1748 /// let vertices = vec![
1749 /// Point3::origin(),
1750 /// Point3::new(1.0, 0.0, 0.0),
1751 /// Point3::new(0.0, 1.0, 0.0),
1752 /// ];
1753 /// let indices = vec![[0, 1, 2]];
1754 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1755 ///
1756 /// let identity = Isometry3::identity();
1757 /// let aabb = mesh.aabb(&identity);
1758 ///
1759 /// // The AABB contains all vertices
1760 /// assert!(aabb.contains_local_point(&Point3::new(0.5, 0.5, 0.0)));
1761 /// # }
1762 /// ```
1763 pub fn aabb(&self, pos: &Isometry<Real>) -> Aabb {
1764 self.bvh.root_aabb().transform_by(pos)
1765 }
1766
1767 /// Gets the local axis-aligned bounding box of this triangle mesh.
1768 ///
1769 /// Returns the AABB in the mesh's local coordinate system (without any
1770 /// transformation applied). This is faster than [`TriMesh::aabb`] when
1771 /// no transformation is needed.
1772 ///
1773 /// # Example
1774 ///
1775 /// ```
1776 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1777 /// use parry3d::shape::TriMesh;
1778 /// use nalgebra::Point3;
1779 ///
1780 /// let vertices = vec![
1781 /// Point3::new(-1.0, -1.0, 0.0),
1782 /// Point3::new(1.0, -1.0, 0.0),
1783 /// Point3::new(0.0, 1.0, 0.0),
1784 /// ];
1785 /// let indices = vec![[0, 1, 2]];
1786 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1787 ///
1788 /// let aabb = mesh.local_aabb();
1789 /// assert_eq!(aabb.mins.x, -1.0);
1790 /// assert_eq!(aabb.maxs.x, 1.0);
1791 /// # }
1792 /// ```
1793 pub fn local_aabb(&self) -> Aabb {
1794 self.bvh.root_aabb()
1795 }
1796
1797 /// The acceleration structure used by this triangle-mesh.
1798 ///
1799 /// Returns a reference to the BVH (Bounding Volume Hierarchy) that
1800 /// accelerates spatial queries on this mesh. The BVH is used internally
1801 /// for ray casting, collision detection, and other geometric queries.
1802 ///
1803 /// # Example
1804 ///
1805 /// ```
1806 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1807 /// use parry3d::shape::TriMesh;
1808 /// use nalgebra::Point3;
1809 ///
1810 /// let vertices = vec![
1811 /// Point3::origin(),
1812 /// Point3::new(1.0, 0.0, 0.0),
1813 /// Point3::new(0.0, 1.0, 0.0),
1814 /// ];
1815 /// let indices = vec![[0, 1, 2]];
1816 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1817 ///
1818 /// let bvh = mesh.bvh();
1819 /// // The BVH can be used for advanced spatial queries
1820 /// assert!(!bvh.is_empty());
1821 /// # }
1822 /// ```
1823 pub fn bvh(&self) -> &Bvh {
1824 &self.bvh
1825 }
1826
1827 /// The number of triangles forming this mesh.
1828 ///
1829 /// # Example
1830 ///
1831 /// ```
1832 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1833 /// use parry3d::shape::TriMesh;
1834 /// use nalgebra::Point3;
1835 ///
1836 /// let vertices = vec![
1837 /// Point3::origin(),
1838 /// Point3::new(1.0, 0.0, 0.0),
1839 /// Point3::new(0.0, 1.0, 0.0),
1840 /// Point3::new(1.0, 1.0, 0.0),
1841 /// ];
1842 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1843 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1844 ///
1845 /// assert_eq!(mesh.num_triangles(), 2);
1846 /// # }
1847 /// ```
1848 pub fn num_triangles(&self) -> usize {
1849 self.indices.len()
1850 }
1851
1852 /// Does the given feature ID identify a backface of this trimesh?
1853 pub fn is_backface(&self, feature: FeatureId) -> bool {
1854 if let FeatureId::Face(i) = feature {
1855 i >= self.indices.len() as u32
1856 } else {
1857 false
1858 }
1859 }
1860
1861 /// Get the `i`-th triangle of this mesh.
1862 ///
1863 /// Returns a [`Triangle`] shape with the actual vertex coordinates (not indices)
1864 /// of the requested triangle. The triangle index must be less than the number
1865 /// of triangles in the mesh.
1866 ///
1867 /// # Arguments
1868 ///
1869 /// * `i` - The index of the triangle to retrieve (0-based)
1870 ///
1871 /// # Panics
1872 ///
1873 /// Panics if `i >= self.num_triangles()`.
1874 ///
1875 /// # Example
1876 ///
1877 /// ```
1878 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1879 /// use parry3d::shape::TriMesh;
1880 /// use nalgebra::Point3;
1881 ///
1882 /// let vertices = vec![
1883 /// Point3::origin(),
1884 /// Point3::new(1.0, 0.0, 0.0),
1885 /// Point3::new(0.0, 1.0, 0.0),
1886 /// ];
1887 /// let indices = vec![[0, 1, 2]];
1888 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1889 ///
1890 /// let triangle = mesh.triangle(0);
1891 /// assert_eq!(triangle.a, Point3::origin());
1892 /// assert_eq!(triangle.b, Point3::new(1.0, 0.0, 0.0));
1893 /// assert_eq!(triangle.c, Point3::new(0.0, 1.0, 0.0));
1894 /// # }
1895 /// ```
1896 pub fn triangle(&self, i: u32) -> Triangle {
1897 let idx = self.indices[i as usize];
1898 Triangle::new(
1899 self.vertices[idx[0] as usize],
1900 self.vertices[idx[1] as usize],
1901 self.vertices[idx[2] as usize],
1902 )
1903 }
1904
1905 /// Returns the pseudo-normals of one of this mesh’s triangles, if it was computed.
1906 ///
1907 /// This returns `None` if the pseudo-normals of this triangle were not computed.
1908 /// To have its pseudo-normals computed, be sure to set the [`TriMeshFlags`] so that
1909 /// they contain the [`TriMeshFlags::FIX_INTERNAL_EDGES`] flag.
1910 #[cfg(feature = "dim3")]
1911 pub fn triangle_normal_constraints(&self, i: u32) -> Option<TrianglePseudoNormals> {
1912 if self.flags.contains(TriMeshFlags::FIX_INTERNAL_EDGES) {
1913 let triangle = self.triangle(i);
1914 let pseudo_normals = self.pseudo_normals.as_ref()?;
1915 let edges_pseudo_normals = pseudo_normals.edges_pseudo_normal[i as usize];
1916
1917 // TODO: could the pseudo-normal be pre-normalized instead of having to renormalize
1918 // every time we need them?
1919 Some(TrianglePseudoNormals {
1920 face: triangle.normal()?,
1921 edges: [
1922 Unit::try_new(edges_pseudo_normals[0], 1.0e-6)?,
1923 Unit::try_new(edges_pseudo_normals[1], 1.0e-6)?,
1924 Unit::try_new(edges_pseudo_normals[2], 1.0e-6)?,
1925 ],
1926 })
1927 } else {
1928 None
1929 }
1930 }
1931
1932 #[cfg(feature = "dim2")]
1933 #[doc(hidden)]
1934 pub fn triangle_normal_constraints(&self, _i: u32) -> Option<TrianglePseudoNormals> {
1935 None
1936 }
1937
1938 /// The vertex buffer of this mesh.
1939 ///
1940 /// Returns a slice containing all vertex positions as 3D points.
1941 /// The vertices are stored in the order they were provided during construction.
1942 ///
1943 /// # Example
1944 ///
1945 /// ```
1946 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1947 /// use parry3d::shape::TriMesh;
1948 /// use nalgebra::Point3;
1949 ///
1950 /// let vertices = vec![
1951 /// Point3::origin(),
1952 /// Point3::new(1.0, 0.0, 0.0),
1953 /// Point3::new(0.0, 1.0, 0.0),
1954 /// ];
1955 /// let indices = vec![[0, 1, 2]];
1956 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1957 ///
1958 /// assert_eq!(mesh.vertices().len(), 3);
1959 /// assert_eq!(mesh.vertices()[0], Point3::origin());
1960 /// # }
1961 /// ```
1962 pub fn vertices(&self) -> &[Point<Real>] {
1963 &self.vertices
1964 }
1965
1966 /// The index buffer of this mesh.
1967 ///
1968 /// Returns a slice of triangles, where each triangle is represented as an
1969 /// array of three vertex indices. The indices reference positions in the
1970 /// vertex buffer returned by [`TriMesh::vertices`].
1971 ///
1972 /// # Example
1973 ///
1974 /// ```
1975 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1976 /// use parry3d::shape::TriMesh;
1977 /// use nalgebra::Point3;
1978 ///
1979 /// let vertices = vec![
1980 /// Point3::origin(),
1981 /// Point3::new(1.0, 0.0, 0.0),
1982 /// Point3::new(0.0, 1.0, 0.0),
1983 /// Point3::new(1.0, 1.0, 0.0),
1984 /// ];
1985 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1986 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1987 ///
1988 /// assert_eq!(mesh.indices().len(), 2);
1989 /// assert_eq!(mesh.indices()[0], [0, 1, 2]);
1990 /// assert_eq!(mesh.indices()[1], [1, 3, 2]);
1991 /// # }
1992 /// ```
1993 pub fn indices(&self) -> &[[u32; 3]] {
1994 &self.indices
1995 }
1996
1997 /// Returns the topology information of this trimesh, if it has been computed.
1998 ///
1999 /// Topology information includes half-edge data structures that describe
2000 /// adjacency relationships between vertices, edges, and faces. This is
2001 /// computed when the [`TriMeshFlags::HALF_EDGE_TOPOLOGY`] flag is set.
2002 ///
2003 /// # Example
2004 ///
2005 /// ```
2006 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2007 /// use parry3d::shape::{TriMesh, TriMeshFlags};
2008 /// use nalgebra::Point3;
2009 ///
2010 /// let vertices = vec![
2011 /// Point3::origin(),
2012 /// Point3::new(1.0, 0.0, 0.0),
2013 /// Point3::new(0.0, 1.0, 0.0),
2014 /// ];
2015 /// let indices = vec![[0, 1, 2]];
2016 ///
2017 /// // Without topology flag
2018 /// let mesh = TriMesh::new(vertices.clone(), indices.clone()).unwrap();
2019 /// assert!(mesh.topology().is_none());
2020 ///
2021 /// // With topology flag
2022 /// let mesh = TriMesh::with_flags(
2023 /// vertices,
2024 /// indices,
2025 /// TriMeshFlags::HALF_EDGE_TOPOLOGY
2026 /// ).unwrap();
2027 /// assert!(mesh.topology().is_some());
2028 /// # }
2029 /// ```
2030 pub fn topology(&self) -> Option<&TriMeshTopology> {
2031 self.topology.as_ref()
2032 }
2033
2034 /// Returns the connected-component information of this trimesh, if it has been computed.
2035 ///
2036 /// Connected components represent separate, non-connected parts of the mesh.
2037 /// This is computed when the [`TriMeshFlags::CONNECTED_COMPONENTS`] flag is set
2038 /// (requires the `std` feature).
2039 ///
2040 /// # Example
2041 ///
2042 /// ```
2043 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2044 /// # #[cfg(feature = "std")] {
2045 /// use parry3d::shape::{TriMesh, TriMeshFlags};
2046 /// use nalgebra::Point3;
2047 ///
2048 /// // Create two separate triangles (not connected)
2049 /// let vertices = vec![
2050 /// // First triangle
2051 /// Point3::origin(),
2052 /// Point3::new(1.0, 0.0, 0.0),
2053 /// Point3::new(0.0, 1.0, 0.0),
2054 /// // Second triangle (separate)
2055 /// Point3::new(5.0, 0.0, 0.0),
2056 /// Point3::new(6.0, 0.0, 0.0),
2057 /// Point3::new(5.0, 1.0, 0.0),
2058 /// ];
2059 /// let indices = vec![[0, 1, 2], [3, 4, 5]];
2060 ///
2061 /// let mesh = TriMesh::with_flags(
2062 /// vertices,
2063 /// indices,
2064 /// TriMeshFlags::CONNECTED_COMPONENTS
2065 /// ).unwrap();
2066 ///
2067 /// if let Some(cc) = mesh.connected_components() {
2068 /// assert_eq!(cc.num_connected_components(), 2);
2069 /// }
2070 /// # }
2071 /// # }
2072 /// ```
2073 pub fn connected_components(&self) -> Option<&TriMeshConnectedComponents> {
2074 self.connected_components.as_ref()
2075 }
2076
2077 /// Returns the connected-component of this mesh.
2078 ///
2079 /// The connected-components are returned as a set of `TriMesh` build with the given `flags`.
2080 pub fn connected_component_meshes(
2081 &self,
2082 flags: TriMeshFlags,
2083 ) -> Option<Vec<Result<TriMesh, TriMeshBuilderError>>> {
2084 self.connected_components()
2085 .map(|cc| cc.to_meshes(self, flags))
2086 }
2087
2088 /// The pseudo-normals of this triangle mesh, if they have been computed.
2089 #[cfg(feature = "dim3")]
2090 pub fn pseudo_normals(&self) -> Option<&TriMeshPseudoNormals> {
2091 self.pseudo_normals.as_ref()
2092 }
2093
2094 /// The pseudo-normals of this triangle mesh, if they have been computed **and** this mesh was
2095 /// marked as [`TriMeshFlags::ORIENTED`].
2096 #[cfg(feature = "dim3")]
2097 pub fn pseudo_normals_if_oriented(&self) -> Option<&TriMeshPseudoNormals> {
2098 if self.flags.intersects(TriMeshFlags::ORIENTED) {
2099 self.pseudo_normals.as_ref()
2100 } else {
2101 None
2102 }
2103 }
2104}
2105
2106#[cfg(feature = "dim3")]
2107impl From<crate::shape::HeightField> for TriMesh {
2108 fn from(heightfield: crate::shape::HeightField) -> Self {
2109 let (vtx, idx) = heightfield.to_trimesh();
2110 TriMesh::new(vtx, idx).unwrap()
2111 }
2112}
2113
2114#[cfg(feature = "dim3")]
2115impl From<Cuboid> for TriMesh {
2116 fn from(cuboid: Cuboid) -> Self {
2117 let (vtx, idx) = cuboid.to_trimesh();
2118 TriMesh::new(vtx, idx).unwrap()
2119 }
2120}
2121
2122impl CompositeShape for TriMesh {
2123 fn map_part_at(
2124 &self,
2125 i: u32,
2126 f: &mut dyn FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>),
2127 ) {
2128 let tri = self.triangle(i);
2129 let normals = self.triangle_normal_constraints(i);
2130 f(
2131 None,
2132 &tri,
2133 normals.as_ref().map(|n| n as &dyn NormalConstraints),
2134 )
2135 }
2136
2137 fn bvh(&self) -> &Bvh {
2138 &self.bvh
2139 }
2140}
2141
2142impl TypedCompositeShape for TriMesh {
2143 type PartShape = Triangle;
2144 type PartNormalConstraints = TrianglePseudoNormals;
2145
2146 #[inline(always)]
2147 fn map_typed_part_at<T>(
2148 &self,
2149 i: u32,
2150 mut f: impl FnMut(
2151 Option<&Isometry<Real>>,
2152 &Self::PartShape,
2153 Option<&Self::PartNormalConstraints>,
2154 ) -> T,
2155 ) -> Option<T> {
2156 let tri = self.triangle(i);
2157 let pseudo_normals = self.triangle_normal_constraints(i);
2158 Some(f(None, &tri, pseudo_normals.as_ref()))
2159 }
2160
2161 #[inline(always)]
2162 fn map_untyped_part_at<T>(
2163 &self,
2164 i: u32,
2165 mut f: impl FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
2166 ) -> Option<T> {
2167 let tri = self.triangle(i);
2168 let pseudo_normals = self.triangle_normal_constraints(i);
2169 Some(f(
2170 None,
2171 &tri,
2172 pseudo_normals.as_ref().map(|n| n as &dyn NormalConstraints),
2173 ))
2174 }
2175}
2176
2177#[cfg(test)]
2178mod test {
2179 use crate::math::{Real, Vector};
2180 use crate::shape::{Cuboid, TriMesh, TriMeshFlags};
2181
2182 #[test]
2183 fn trimesh_error_empty_indices() {
2184 assert!(
2185 TriMesh::with_flags(vec![], vec![], TriMeshFlags::empty()).is_err(),
2186 "A triangle mesh with no triangles is invalid."
2187 );
2188 }
2189
2190 #[test]
2191 fn connected_components() {
2192 let (vtx, idx) = Cuboid::new(Vector::repeat(0.5)).to_trimesh();
2193
2194 // Push 10 copy of the mesh, each time pushed with an offset.
2195 let mut mesh = TriMesh::new(vtx.clone(), idx.clone()).unwrap();
2196
2197 for i in 1..10 {
2198 let cc_vtx = vtx
2199 .iter()
2200 .map(|pt| pt + Vector::repeat(2.0 * i as Real))
2201 .collect();
2202
2203 let to_append = TriMesh::new(cc_vtx, idx.clone()).unwrap();
2204 mesh.append(&to_append);
2205 }
2206
2207 mesh.set_flags(TriMeshFlags::CONNECTED_COMPONENTS).unwrap();
2208 let connected_components = mesh.connected_components().unwrap();
2209 assert_eq!(connected_components.num_connected_components(), 10);
2210
2211 let cc_meshes = connected_components.to_meshes(&mesh, TriMeshFlags::empty());
2212
2213 for cc in cc_meshes {
2214 let cc = cc.unwrap();
2215 assert_eq!(cc.vertices.len(), vtx.len());
2216 assert_eq!(cc.indices.len(), idx.len());
2217 }
2218 }
2219}