parry3d/shape/
trimesh.rs

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