mesh_graph/elements/
face.rs1use glam::Vec3;
2use itertools::Itertools;
3use parry3d::{bounding_volume::Aabb, na::Point3};
4use tracing::{error, instrument};
5
6use crate::{CircularHalfedgesIterator, MeshGraph, error_none};
7
8use super::{FaceId, HalfedgeId, VertexId};
9
10#[derive(Default, Debug, Clone, Copy)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Face {
13 pub halfedge: HalfedgeId,
16
17 pub index: u32,
19
20 pub id: FaceId,
22}
23
24impl Face {
25 #[instrument(skip(mesh_graph))]
27 pub fn halfedges<'a>(&self, mesh_graph: &'a MeshGraph) -> CircularHalfedgesIterator<'a> {
28 CircularHalfedgesIterator::new(Some(self.halfedge), mesh_graph, |he, mesh_graph| {
29 mesh_graph
30 .halfedges
31 .get(he)
32 .or_else(error_none!("Halfedge not found"))?
33 .next
34 })
35 }
36
37 #[instrument(skip(mesh_graph))]
39 pub fn vertices(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = VertexId> {
40 self.halfedges(mesh_graph).filter_map(|he| {
41 mesh_graph
42 .halfedges
43 .get(he)
44 .or_else(error_none!("Halfedge not found"))
45 .map(|he| he.end_vertex)
46 })
47 }
48
49 #[instrument(skip(mesh_graph))]
51 pub fn center(&self, mesh_graph: &MeshGraph) -> Vec3 {
52 self.vertex_positions(mesh_graph).sum::<Vec3>() / 3.0
53 }
54
55 #[instrument(skip(mesh_graph))]
57 pub fn aabb(&self, mesh_graph: &MeshGraph) -> Aabb {
58 Aabb::from_points(
59 self.vertex_positions(mesh_graph)
60 .map(|p| Point3::new(p.x, p.y, p.z)),
61 )
62 }
63
64 #[instrument(skip(mesh_graph))]
66 pub fn vertex_positions(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = Vec3> {
67 self.vertices(mesh_graph).filter_map(|v| {
68 mesh_graph
69 .positions
70 .get(v)
71 .or_else(error_none!("Position not found"))
72 .copied()
73 })
74 }
75
76 #[instrument(skip(mesh_graph))]
78 pub fn normal(&self, mesh_graph: &MeshGraph) -> Option<Vec3> {
79 let positions = self.vertex_positions(mesh_graph).collect_vec();
80
81 if positions.len() < 3 {
82 error!("Face has less than 3 vertex positions");
83 return None;
84 }
85
86 let a = positions[1] - positions[0];
87 let b = positions[2] - positions[0];
88 Some(a.cross(b).normalize())
89 }
90
91 #[instrument(skip(mesh_graph))]
93 pub fn is_degenerate(&self, mesh_graph: &MeshGraph, epsilon_sqr: f32) -> bool {
94 let positions = self.vertex_positions(mesh_graph).collect_vec();
95
96 if positions.len() < 3 {
97 error!("Face has less than 3 vertex positions");
98 return true;
99 }
100
101 let p0 = positions[0];
102 let p1 = positions[1];
103 let p2 = positions[2];
104
105 if p0.distance_squared(p1) < epsilon_sqr
107 || p0.distance_squared(p2) < epsilon_sqr
108 || p1.distance_squared(p2) < epsilon_sqr
109 {
110 return true;
111 }
112
113 let a = p1 - p0;
115 let b = p2 - p0;
116 let cross = a.cross(b);
117
118 cross.length_squared() < epsilon_sqr
120 }
121}