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}