oxiphysics_geometry/mesh_repair/
types.rs1use std::collections::{HashMap, HashSet};
6
7use super::functions::{
8 fill_hole_ear_clipping, find_boundary_loops, fix_non_manifold_edges, merge_duplicate_vertices,
9};
10
11pub struct HalfEdge {
13 pub vertex: usize,
15 pub face: usize,
17 pub next: usize,
19 pub prev: usize,
21 pub twin: Option<usize>,
23}
24pub struct HalfEdgeMesh {
26 pub vertices: Vec<[f64; 3]>,
28 pub half_edges: Vec<HalfEdge>,
30 pub face_starts: Vec<usize>,
32}
33impl HalfEdgeMesh {
34 pub fn from_triangle_soup(vertices: Vec<[f64; 3]>, triangles: Vec<[usize; 3]>) -> Self {
36 let mut half_edges: Vec<HalfEdge> = Vec::new();
37 let mut face_starts: Vec<usize> = Vec::new();
38 let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
39 for (face_idx, tri) in triangles.iter().enumerate() {
40 let base = half_edges.len();
41 face_starts.push(base);
42 for k in 0..3 {
43 let to_v = tri[(k + 1) % 3];
44 half_edges.push(HalfEdge {
45 vertex: to_v,
46 face: face_idx,
47 next: base + (k + 1) % 3,
48 prev: base + (k + 2) % 3,
49 twin: None,
50 });
51 edge_map.insert((tri[k], to_v), base + k);
52 }
53 }
54 let n = half_edges.len();
55 for i in 0..n {
56 if half_edges[i].twin.is_none() {
57 let prev_idx = half_edges[i].prev;
58 let from_v = half_edges[prev_idx].vertex;
59 let to_v = half_edges[i].vertex;
60 if let Some(&twin_idx) = edge_map.get(&(to_v, from_v)) {
61 half_edges[i].twin = Some(twin_idx);
62 half_edges[twin_idx].twin = Some(i);
63 }
64 }
65 }
66 HalfEdgeMesh {
67 vertices,
68 half_edges,
69 face_starts,
70 }
71 }
72 pub fn is_manifold(&self) -> bool {
74 let mut seen: HashMap<(usize, usize), usize> = HashMap::new();
75 for (i, he) in self.half_edges.iter().enumerate() {
76 let prev = &self.half_edges[he.prev];
77 let from_v = prev.vertex;
78 let to_v = he.vertex;
79 let count = seen.entry((from_v, to_v)).or_insert(0);
80 *count += 1;
81 if *count > 1 {
82 let _ = i;
83 return false;
84 }
85 }
86 true
87 }
88 pub fn boundary_edges(&self) -> Vec<usize> {
90 self.half_edges
91 .iter()
92 .enumerate()
93 .filter_map(|(i, he)| if he.twin.is_none() { Some(i) } else { None })
94 .collect()
95 }
96 pub fn vertex_valence(&self, v: usize) -> usize {
98 self.half_edges.iter().filter(|he| he.vertex == v).count()
99 }
100 pub fn face_count(&self) -> usize {
102 self.face_starts.len()
103 }
104 pub fn vertex_count(&self) -> usize {
106 self.vertices.len()
107 }
108 pub fn boundary_loops(&self) -> Vec<Vec<usize>> {
111 let boundary_hes = self.boundary_edges();
112 if boundary_hes.is_empty() {
113 return Vec::new();
114 }
115 let mut from_to_he: HashMap<usize, usize> = HashMap::new();
116 for &he_idx in &boundary_hes {
117 let prev_idx = self.half_edges[he_idx].prev;
118 let from_v = self.half_edges[prev_idx].vertex;
119 from_to_he.insert(from_v, he_idx);
120 }
121 let mut visited: HashSet<usize> = HashSet::new();
122 let mut loops = Vec::new();
123 for &start_he in &boundary_hes {
124 if visited.contains(&start_he) {
125 continue;
126 }
127 let mut loop_verts = Vec::new();
128 let mut current = start_he;
129 loop {
130 visited.insert(current);
131 let to_v = self.half_edges[current].vertex;
132 loop_verts.push(to_v);
133 match from_to_he.get(&to_v) {
134 Some(&next_he) if !visited.contains(&next_he) => {
135 current = next_he;
136 }
137 _ => break,
138 }
139 }
140 if loop_verts.len() >= 3 {
141 loops.push(loop_verts);
142 }
143 }
144 loops
145 }
146}
147pub struct MeshRepairReport {
149 pub n_degenerate_removed: usize,
151 pub n_holes_filled: usize,
153 pub n_duplicates_merged: usize,
155 pub n_normals_fixed: usize,
157}
158pub struct FlipMesh {
163 pub vertices: Vec<[f64; 3]>,
165 pub triangles: Vec<[usize; 3]>,
167 pub n_flips: usize,
169}
170impl FlipMesh {
171 pub fn new(vertices: Vec<[f64; 3]>, triangles: Vec<[usize; 3]>) -> Self {
173 Self {
174 vertices,
175 triangles,
176 n_flips: 0,
177 }
178 }
179 pub fn is_locally_delaunay(va: [f64; 3], vb: [f64; 3], vc: [f64; 3], vd: [f64; 3]) -> bool {
186 let ax = va[0] - vd[0];
187 let ay = va[1] - vd[1];
188 let bx = vb[0] - vd[0];
189 let by = vb[1] - vd[1];
190 let cx = vc[0] - vd[0];
191 let cy = vc[1] - vd[1];
192 let det = ax * (by * (cx * cx + cy * cy) - cy * (bx * bx + by * by))
193 - ay * (bx * (cx * cx + cy * cy) - cx * (bx * bx + by * by))
194 + (ax * ax + ay * ay) * (bx * cy - by * cx);
195 det <= 0.0
196 }
197 pub fn flip_pass(&mut self) -> usize {
200 let n = self.triangles.len();
201 let mut edge_map: std::collections::HashMap<(usize, usize), Vec<usize>> =
202 std::collections::HashMap::new();
203 for (ti, tri) in self.triangles.iter().enumerate() {
204 for k in 0..3 {
205 let a = tri[k];
206 let b = tri[(k + 1) % 3];
207 let key = if a < b { (a, b) } else { (b, a) };
208 edge_map.entry(key).or_default().push(ti);
209 }
210 }
211 let mut flips = 0usize;
212 let candidates: Vec<((usize, usize), usize, usize)> = edge_map
213 .iter()
214 .filter_map(|(&key, tris)| {
215 if tris.len() == 2 {
216 Some((key, tris[0], tris[1]))
217 } else {
218 None
219 }
220 })
221 .collect();
222 for (edge_key, t0, t1) in candidates {
223 if t0 >= n || t1 >= n {
224 continue;
225 }
226 let tri0 = self.triangles[t0];
227 let tri1 = self.triangles[t1];
228 let (ea, eb) = edge_key;
229 let opp0 = tri0.iter().copied().find(|&v| v != ea && v != eb);
230 let opp1 = tri1.iter().copied().find(|&v| v != ea && v != eb);
231 let (c, d) = match (opp0, opp1) {
232 (Some(c), Some(d)) => (c, d),
233 _ => continue,
234 };
235 let va = self.vertices[ea];
236 let vb = self.vertices[eb];
237 let vc = self.vertices[c];
238 let vd = self.vertices[d];
239 if !Self::is_locally_delaunay(va, vb, vc, vd) {
240 self.triangles[t0] = [ea, c, d];
241 self.triangles[t1] = [eb, d, c];
242 flips += 1;
243 }
244 }
245 self.n_flips += flips;
246 flips
247 }
248 pub fn flip_to_delaunay(&mut self, max_passes: usize) {
250 for _ in 0..max_passes {
251 if self.flip_pass() == 0 {
252 break;
253 }
254 }
255 }
256}
257pub struct MeshStats {
260 pub n_vertices: usize,
262 pub n_triangles: usize,
264 pub n_edges: usize,
266 pub n_boundary_edges: usize,
268 pub n_non_manifold_edges: usize,
270 pub euler_characteristic: i64,
272}
273pub struct MeshRepair {
281 pub vertices: Vec<[f64; 3]>,
283 pub triangles: Vec<[usize; 3]>,
285}
286impl MeshRepair {
287 pub fn new(vertices: Vec<[f64; 3]>, triangles: Vec<[usize; 3]>) -> Self {
289 Self {
290 vertices,
291 triangles,
292 }
293 }
294 pub fn fill_holes(&mut self) -> usize {
302 let loops = find_boundary_loops(&self.triangles);
303 let n_holes = loops.len();
304 for lp in &loops {
305 if lp.len() < 3 {
306 continue;
307 }
308 let hub = lp[0];
309 for i in 1..(lp.len() - 1) {
310 self.triangles.push([hub, lp[i], lp[i + 1]]);
311 }
312 }
313 n_holes
314 }
315 pub fn fill_holes_ear(&mut self) -> usize {
319 let loops = find_boundary_loops(&self.triangles);
320 let n_holes = loops.len();
321 for lp in &loops {
322 let new_tris = fill_hole_ear_clipping(&self.vertices, lp);
323 self.triangles.extend_from_slice(&new_tris);
324 }
325 n_holes
326 }
327 pub fn remove_duplicate_vertices(&mut self, epsilon: f64) -> usize {
332 let (new_v, new_t, n_merged) =
333 merge_duplicate_vertices(&self.vertices, &self.triangles, epsilon);
334 self.vertices = new_v;
335 self.triangles = new_t;
336 n_merged
337 }
338 pub fn fix_non_manifold_edges(&mut self) -> usize {
343 let before = self.triangles.len();
344 self.triangles = fix_non_manifold_edges(&self.triangles);
345 before - self.triangles.len()
346 }
347 pub fn compute_euler_characteristic(&self) -> i32 {
352 let v = self.vertices.len();
353 let mut edge_set: HashSet<(usize, usize)> = HashSet::new();
354 for tri in &self.triangles {
355 for k in 0..3 {
356 let a = tri[k];
357 let b = tri[(k + 1) % 3];
358 let key = if a < b { (a, b) } else { (b, a) };
359 edge_set.insert(key);
360 }
361 }
362 let e = edge_set.len();
363 let f = self.triangles.len();
364 v as i32 - e as i32 + f as i32
365 }
366}