Skip to main content

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