Skip to main content

oxiphysics_geometry/topology/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use std::collections::{HashMap, HashSet};
7
8/// A Morse function value at a vertex.
9#[derive(Debug, Clone, Copy)]
10pub struct MorseVertex {
11    /// Vertex index.
12    pub idx: usize,
13    /// Morse function value.
14    pub value: f64,
15    /// Critical point type.
16    pub critical_type: MorseCriticalType,
17}
18/// 2D polygon offset (inward / outward buffering).
19pub struct PolygonOffset;
20impl PolygonOffset {
21    /// Offset a 2D polygon by `dist` (positive = outward, negative = inward).
22    ///
23    /// Uses simple vertex normal offset (miter join). Returns the offset polygon.
24    /// The input polygon should be a simple closed polygon (last vertex connects to first).
25    pub fn offset_polygon_2d(verts: &[[f64; 2]], dist: f64) -> Vec<[f64; 2]> {
26        let n = verts.len();
27        if n < 3 {
28            return verts.to_vec();
29        }
30        let edge_normal = |a: [f64; 2], b: [f64; 2]| -> [f64; 2] {
31            let dx = b[0] - a[0];
32            let dy = b[1] - a[1];
33            let len = (dx * dx + dy * dy).sqrt();
34            if len < 1e-300 {
35                [0.0, 1.0]
36            } else {
37                [dy / len, -dx / len]
38            }
39        };
40        let mut result = Vec::with_capacity(n);
41        for i in 0..n {
42            let prev = if i == 0 { n - 1 } else { i - 1 };
43            let next = (i + 1) % n;
44            let n_prev = edge_normal(verts[prev], verts[i]);
45            let n_next = edge_normal(verts[i], verts[next]);
46            let bx = n_prev[0] + n_next[0];
47            let by = n_prev[1] + n_next[1];
48            let blen = (bx * bx + by * by).sqrt();
49            let (ox, oy) = if blen < 1e-12 {
50                (n_next[0] * dist, n_next[1] * dist)
51            } else {
52                let dot = n_prev[0] * n_next[0] + n_prev[1] * n_next[1];
53                let miter_scale = dist / (1.0 + dot).max(0.1).sqrt();
54                (bx / blen * miter_scale, by / blen * miter_scale)
55            };
56            result.push([verts[i][0] + ox, verts[i][1] + oy]);
57        }
58        result
59    }
60    /// Compute the signed area of a 2D polygon (positive = CCW).
61    pub fn signed_area(verts: &[[f64; 2]]) -> f64 {
62        let n = verts.len();
63        if n < 3 {
64            return 0.0;
65        }
66        let mut area = 0.0;
67        for i in 0..n {
68            let j = (i + 1) % n;
69            area += verts[i][0] * verts[j][1];
70            area -= verts[j][0] * verts[i][1];
71        }
72        area * 0.5
73    }
74    /// Check if a polygon is wound counter-clockwise (positive area).
75    pub fn is_ccw(verts: &[[f64; 2]]) -> bool {
76        Self::signed_area(verts) > 0.0
77    }
78}
79/// A half-edge (directed half-edge in the DCEL).
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct HalfEdge {
82    /// Origin vertex index.
83    pub origin: usize,
84    /// Next half-edge in the face loop.
85    pub next: usize,
86    /// Previous half-edge in the face loop.
87    pub prev: usize,
88    /// Twin (opposite) half-edge index (usize::MAX if boundary).
89    pub twin: usize,
90    /// Face this half-edge belongs to (usize::MAX if boundary).
91    pub face: usize,
92}
93impl HalfEdge {
94    /// Create a new half-edge.
95    pub fn new(origin: usize) -> Self {
96        Self {
97            origin,
98            next: usize::MAX,
99            prev: usize::MAX,
100            twin: usize::MAX,
101            face: usize::MAX,
102        }
103    }
104    /// Returns true if this half-edge is a boundary half-edge (no twin).
105    pub fn is_boundary(&self) -> bool {
106        self.twin == usize::MAX
107    }
108}
109/// Doubly Connected Edge List (DCEL) half-edge mesh.
110#[derive(Debug, Clone)]
111pub struct HalfEdgeMesh {
112    /// All half-edges.
113    pub half_edges: Vec<HalfEdge>,
114    /// All vertices (positions).
115    pub vertices: Vec<[f64; 3]>,
116    /// Vertex → one outgoing half-edge index.
117    pub vertex_he: Vec<usize>,
118    /// All faces.
119    pub faces: Vec<Face>,
120}
121impl HalfEdgeMesh {
122    /// Create an empty half-edge mesh.
123    pub fn new() -> Self {
124        Self {
125            half_edges: Vec::new(),
126            vertices: Vec::new(),
127            vertex_he: Vec::new(),
128            faces: Vec::new(),
129        }
130    }
131    /// Create a half-edge mesh from a triangle soup (vertices + face indices).
132    pub fn from_triangles(verts: Vec<[f64; 3]>, faces: &[[usize; 3]]) -> Self {
133        let nv = verts.len();
134        let nf = faces.len();
135        let nhe = nf * 3;
136        let mut he = vec![
137            HalfEdge {
138                origin: 0,
139                next: usize::MAX,
140                prev: usize::MAX,
141                twin: usize::MAX,
142                face: usize::MAX,
143            };
144            nhe
145        ];
146        let mut face_list = Vec::with_capacity(nf);
147        let mut vertex_he = vec![usize::MAX; nv];
148        for (fi, tri) in faces.iter().enumerate() {
149            let base = fi * 3;
150            for k in 0..3 {
151                let he_idx = base + k;
152                he[he_idx].origin = tri[k];
153                he[he_idx].next = base + (k + 1) % 3;
154                he[he_idx].prev = base + (k + 2) % 3;
155                he[he_idx].face = fi;
156                if vertex_he[tri[k]] == usize::MAX {
157                    vertex_he[tri[k]] = he_idx;
158                }
159            }
160            face_list.push(Face {
161                start_he: base,
162                is_outer: false,
163            });
164        }
165        let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
166        for (i, h) in he.iter().enumerate() {
167            let dst = he[h.next].origin;
168            edge_map.insert((h.origin, dst), i);
169        }
170        for i in 0..nhe {
171            if he[i].twin != usize::MAX {
172                continue;
173            }
174            let src = he[i].origin;
175            let dst = he[he[i].next].origin;
176            if let Some(&twin_idx) = edge_map.get(&(dst, src)) {
177                he[i].twin = twin_idx;
178                he[twin_idx].twin = i;
179            }
180        }
181        Self {
182            half_edges: he,
183            vertices: verts,
184            vertex_he,
185            faces: face_list,
186        }
187    }
188    /// Number of vertices.
189    pub fn num_vertices(&self) -> usize {
190        self.vertices.len()
191    }
192    /// Number of faces.
193    pub fn num_faces(&self) -> usize {
194        self.faces.len()
195    }
196    /// Number of (full) edges (each edge = 2 half-edges or 1 boundary half-edge).
197    pub fn num_edges(&self) -> usize {
198        let interior: usize = self
199            .half_edges
200            .iter()
201            .filter(|he| he.twin != usize::MAX && he.twin > self.half_edges.len() / 2)
202            .count();
203        let boundary: usize = self
204            .half_edges
205            .iter()
206            .filter(|he| he.twin == usize::MAX)
207            .count();
208        interior + boundary
209    }
210    /// Get the face half-edges (indices) in order for face `fi`.
211    pub fn face_half_edges(&self, fi: usize) -> Vec<usize> {
212        let start = self.faces[fi].start_he;
213        let mut cur = start;
214        let mut result = Vec::new();
215        loop {
216            result.push(cur);
217            cur = self.half_edges[cur].next;
218            if cur == start {
219                break;
220            }
221            if result.len() > 100 {
222                break;
223            }
224        }
225        result
226    }
227    /// Get the vertex positions of face `fi`.
228    pub fn face_vertices(&self, fi: usize) -> Vec<[f64; 3]> {
229        self.face_half_edges(fi)
230            .iter()
231            .map(|&he_idx| self.vertices[self.half_edges[he_idx].origin])
232            .collect()
233    }
234    /// Compute the face normal for a triangular face.
235    pub fn face_normal(&self, fi: usize) -> [f64; 3] {
236        let verts = self.face_vertices(fi);
237        if verts.len() < 3 {
238            return [0.0, 1.0, 0.0];
239        }
240        let e1 = sub3(verts[1], verts[0]);
241        let e2 = sub3(verts[2], verts[0]);
242        norm3(cross3(e1, e2))
243    }
244    /// Compute the Euler characteristic: V - E + F.
245    pub fn euler_characteristic(&self) -> i64 {
246        let v = self.num_vertices() as i64;
247        let e = self.count_unique_edges() as i64;
248        let f = self.num_faces() as i64;
249        v - e + f
250    }
251    /// Count unique (undirected) edges.
252    pub fn count_unique_edges(&self) -> usize {
253        let mut seen: HashSet<(usize, usize)> = HashSet::new();
254        for he in &self.half_edges {
255            if he.face == usize::MAX {
256                continue;
257            }
258            let a = he.origin;
259            let b = self.half_edges[he.next].origin;
260            let key = if a < b { (a, b) } else { (b, a) };
261            seen.insert(key);
262        }
263        seen.len()
264    }
265    /// Compute genus from Euler characteristic (closed orientable surface).
266    ///
267    /// χ = 2 - 2g → g = (2 - χ) / 2
268    pub fn genus(&self) -> i64 {
269        (2 - self.euler_characteristic()) / 2
270    }
271    /// Returns boundary half-edge indices (half-edges with no twin).
272    pub fn boundary_half_edges(&self) -> Vec<usize> {
273        self.half_edges
274            .iter()
275            .enumerate()
276            .filter(|(_, he)| he.twin == usize::MAX)
277            .map(|(i, _)| i)
278            .collect()
279    }
280    /// Returns true if the mesh is closed (no boundary half-edges).
281    pub fn is_closed(&self) -> bool {
282        self.boundary_half_edges().is_empty()
283    }
284    /// Returns true if the mesh is a manifold.
285    ///
286    /// A mesh is manifold if every edge has at most 2 incident faces
287    /// and every vertex has a single fan of faces.
288    pub fn is_manifold(&self) -> bool {
289        let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
290        for he in &self.half_edges {
291            if he.face == usize::MAX {
292                continue;
293            }
294            let a = he.origin;
295            let b = self.half_edges[he.next].origin;
296            let key = if a < b { (a, b) } else { (b, a) };
297            *edge_count.entry(key).or_insert(0) += 1;
298        }
299        edge_count.values().all(|&c| c <= 2)
300    }
301    /// Compute vertex normals by averaging incident face normals.
302    pub fn vertex_normals(&self) -> Vec<[f64; 3]> {
303        let nv = self.num_vertices();
304        let mut normals = vec![[0.0f64; 3]; nv];
305        let mut counts = vec![0usize; nv];
306        for fi in 0..self.num_faces() {
307            let n = self.face_normal(fi);
308            for he_idx in self.face_half_edges(fi) {
309                let v = self.half_edges[he_idx].origin;
310                normals[v] = add3(normals[v], n);
311                counts[v] += 1;
312            }
313        }
314        for (i, n) in normals.iter_mut().enumerate() {
315            if counts[i] > 0 {
316                *n = norm3(*n);
317            }
318        }
319        normals
320    }
321    /// Compute the area of face `fi`.
322    pub fn face_area(&self, fi: usize) -> f64 {
323        let verts = self.face_vertices(fi);
324        if verts.len() < 3 {
325            return 0.0;
326        }
327        let e1 = sub3(verts[1], verts[0]);
328        let e2 = sub3(verts[2], verts[0]);
329        len3(cross3(e1, e2)) * 0.5
330    }
331    /// Total surface area.
332    pub fn total_area(&self) -> f64 {
333        (0..self.num_faces()).map(|fi| self.face_area(fi)).sum()
334    }
335    /// Get vertices adjacent to vertex `v` (1-ring neighbors).
336    pub fn vertex_neighbors(&self, v: usize) -> Vec<usize> {
337        let mut neighbors = Vec::new();
338        let start = self.vertex_he[v];
339        if start == usize::MAX {
340            return neighbors;
341        }
342        let mut cur = start;
343        loop {
344            let next_he = self.half_edges[cur].next;
345            let neighbor = self.half_edges[next_he].origin;
346            neighbors.push(neighbor);
347            if self.half_edges[cur].twin == usize::MAX {
348                break;
349            }
350            cur = self.half_edges[self.half_edges[cur].twin].next;
351            if cur == start {
352                break;
353            }
354            if neighbors.len() > 1000 {
355                break;
356            }
357        }
358        neighbors
359    }
360}
361/// A winged-edge record.
362#[derive(Debug, Clone)]
363pub struct WingedEdge {
364    /// Start vertex.
365    pub v_start: usize,
366    /// End vertex.
367    pub v_end: usize,
368    /// Left face.
369    pub face_left: usize,
370    /// Right face.
371    pub face_right: usize,
372    /// CCW edge from v_start on face_left.
373    pub ccw_start_left: usize,
374    /// CW edge from v_start on face_right.
375    pub cw_start_right: usize,
376    /// CCW edge from v_end on face_right.
377    pub ccw_end_right: usize,
378    /// CW edge from v_end on face_left.
379    pub cw_end_left: usize,
380}
381/// Type of a Morse critical point.
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383pub enum MorseCriticalType {
384    /// Local minimum.
385    Minimum,
386    /// Saddle point.
387    Saddle,
388    /// Local maximum.
389    Maximum,
390    /// Regular (non-critical) point.
391    Regular,
392}
393/// Mesh topology descriptor: vertex/edge/face counts and derived invariants.
394#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395pub struct MeshTopology {
396    /// Number of vertices V.
397    pub n_vertices: usize,
398    /// Number of edges E.
399    pub n_edges: usize,
400    /// Number of faces F.
401    pub n_faces: usize,
402}
403impl MeshTopology {
404    /// Create a new topology descriptor.
405    pub fn new(n_vertices: usize, n_edges: usize, n_faces: usize) -> Self {
406        Self {
407            n_vertices,
408            n_edges,
409            n_faces,
410        }
411    }
412    /// Euler characteristic χ = V - E + F.
413    pub fn euler_characteristic(&self) -> i32 {
414        self.n_vertices as i32 - self.n_edges as i32 + self.n_faces as i32
415    }
416    /// Genus of a closed orientable surface: g = (2 - χ) / 2.
417    pub fn genus(&self) -> i32 {
418        (2 - self.euler_characteristic()) / 2
419    }
420    /// Number of connected components β₀ estimated from Euler characteristic
421    /// (assumes connected: β₀ = 1 for sphere topology).
422    pub fn betti_0_estimate(&self) -> usize {
423        1
424    }
425    /// Cycle rank (first Betti number estimate) β₁ = E - V + β₀.
426    pub fn cycle_rank(&self) -> i32 {
427        self.n_edges as i32 - self.n_vertices as i32 + 1
428    }
429}
430/// Quadric Error Metric (QEM) mesh simplification utilities.
431pub struct MeshSimplification;
432impl MeshSimplification {
433    /// Compute the quadric error matrix Q for a vertex.
434    ///
435    /// For each adjacent face, a plane equation \[a,b,c,d\] (ax+by+cz+d=0)
436    /// contributes the outer product pp^T to Q.  Returns a 4×4 matrix.
437    pub fn quadric_error_matrix(_vertex: [f64; 3], faces: &[[[f64; 3]; 3]]) -> [[f64; 4]; 4] {
438        let mut q = [[0.0f64; 4]; 4];
439        for tri in faces {
440            let e1 = sub3(tri[1], tri[0]);
441            let e2 = sub3(tri[2], tri[0]);
442            let n = norm3(cross3(e1, e2));
443            let d = -dot3(n, tri[0]);
444            let p = [n[0], n[1], n[2], d];
445            for i in 0..4 {
446                for j in 0..4 {
447                    q[i][j] += p[i] * p[j];
448                }
449            }
450        }
451        q
452    }
453    /// Edge collapse cost using combined quadric: v^T (Q1+Q2) v.
454    ///
455    /// The optimal collapse position is taken as the midpoint (v1+v2)/2.
456    pub fn edge_collapse_cost(
457        v1: [f64; 3],
458        v2: [f64; 3],
459        q1: [[f64; 4]; 4],
460        q2: [[f64; 4]; 4],
461    ) -> f64 {
462        let mut q = [[0.0f64; 4]; 4];
463        for i in 0..4 {
464            for j in 0..4 {
465                q[i][j] = q1[i][j] + q2[i][j];
466            }
467        }
468        let v = [
469            (v1[0] + v2[0]) * 0.5,
470            (v1[1] + v2[1]) * 0.5,
471            (v1[2] + v2[2]) * 0.5,
472            1.0,
473        ];
474        let mut cost = 0.0;
475        for i in 0..4 {
476            let mut row_sum = 0.0;
477            for j in 0..4 {
478                row_sum += q[i][j] * v[j];
479            }
480            cost += v[i] * row_sum;
481        }
482        cost.abs()
483    }
484    /// Greedy QEM simplification: collapse edges until `target_count` faces remain.
485    ///
486    /// This is a simplified greedy approach suitable for small meshes.
487    pub fn simplify_qem(
488        positions: &mut [[f64; 3]],
489        triangles: &mut Vec<[usize; 3]>,
490        target_count: usize,
491    ) {
492        while triangles.len() > target_count {
493            if triangles.is_empty() || positions.len() < 2 {
494                break;
495            }
496            let mut best_cost = f64::INFINITY;
497            let mut best_v1 = 0usize;
498            let mut best_v2 = 1usize;
499            let nv = positions.len();
500            let mut vertex_faces: Vec<Vec<usize>> = vec![Vec::new(); nv];
501            for (fi, tri) in triangles.iter().enumerate() {
502                for &v in tri.iter() {
503                    if v < nv {
504                        vertex_faces[v].push(fi);
505                    }
506                }
507            }
508            let mut quadrics: Vec<[[f64; 4]; 4]> = vec![[[0.0; 4]; 4]; nv];
509            for v in 0..nv {
510                let face_tris: Vec<[[f64; 3]; 3]> = vertex_faces[v]
511                    .iter()
512                    .filter_map(|&fi| {
513                        let t = triangles[fi];
514                        if t[0] < nv && t[1] < nv && t[2] < nv {
515                            Some([positions[t[0]], positions[t[1]], positions[t[2]]])
516                        } else {
517                            None
518                        }
519                    })
520                    .collect();
521                quadrics[v] = Self::quadric_error_matrix(positions[v], &face_tris);
522            }
523            let mut seen_edges: HashSet<(usize, usize)> = HashSet::new();
524            for tri in triangles.iter() {
525                for k in 0..3 {
526                    let a = tri[k];
527                    let b = tri[(k + 1) % 3];
528                    if a >= nv || b >= nv {
529                        continue;
530                    }
531                    let key = if a < b { (a, b) } else { (b, a) };
532                    if seen_edges.insert(key) {
533                        let cost = Self::edge_collapse_cost(
534                            positions[a],
535                            positions[b],
536                            quadrics[a],
537                            quadrics[b],
538                        );
539                        if cost < best_cost {
540                            best_cost = cost;
541                            best_v1 = a;
542                            best_v2 = b;
543                        }
544                    }
545                }
546            }
547            if best_cost.is_infinite() {
548                break;
549            }
550            let mid = [
551                (positions[best_v1][0] + positions[best_v2][0]) * 0.5,
552                (positions[best_v1][1] + positions[best_v2][1]) * 0.5,
553                (positions[best_v1][2] + positions[best_v2][2]) * 0.5,
554            ];
555            positions[best_v1] = mid;
556            for tri in triangles.iter_mut() {
557                for v in tri.iter_mut() {
558                    if *v == best_v2 {
559                        *v = best_v1;
560                    }
561                }
562            }
563            triangles.retain(|t| t[0] != t[1] && t[1] != t[2] && t[0] != t[2]);
564        }
565    }
566}
567/// A curve skeleton: a 1D curve embedded in 3D representing the topology of a shape.
568#[derive(Debug, Clone)]
569pub struct CurveSkeleton {
570    /// Skeleton nodes (3D positions).
571    pub nodes: Vec<[f64; 3]>,
572    /// Skeleton edges (pairs of node indices).
573    pub edges: Vec<(usize, usize)>,
574}
575impl Default for CurveSkeleton {
576    fn default() -> Self {
577        Self::new()
578    }
579}
580impl CurveSkeleton {
581    /// Create an empty curve skeleton.
582    pub fn new() -> Self {
583        Self {
584            nodes: Vec::new(),
585            edges: Vec::new(),
586        }
587    }
588    /// Add a skeleton node.
589    pub fn add_node(&mut self, pos: [f64; 3]) -> usize {
590        let idx = self.nodes.len();
591        self.nodes.push(pos);
592        idx
593    }
594    /// Add a skeleton edge.
595    pub fn add_edge(&mut self, a: usize, b: usize) {
596        self.edges.push((a, b));
597    }
598    /// Total skeleton length.
599    pub fn total_length(&self) -> f64 {
600        self.edges
601            .iter()
602            .map(|&(a, b)| len3(sub3(self.nodes[b], self.nodes[a])))
603            .sum()
604    }
605    /// Number of nodes.
606    pub fn num_nodes(&self) -> usize {
607        self.nodes.len()
608    }
609    /// Number of edges.
610    pub fn num_edges(&self) -> usize {
611        self.edges.len()
612    }
613}
614/// A face in the DCEL (polygon).
615#[derive(Debug, Clone)]
616pub struct Face {
617    /// Index of one half-edge on this face.
618    pub start_he: usize,
619    /// Whether this is the outer boundary face.
620    pub is_outer: bool,
621}
622/// A persistence diagram: collection of birth-death pairs.
623#[derive(Debug, Clone, Default)]
624pub struct PersistenceDiagram {
625    /// Birth-death pairs.
626    pub pairs: Vec<BirthDeathPair>,
627}
628impl PersistenceDiagram {
629    /// Create an empty persistence diagram.
630    pub fn new() -> Self {
631        Self { pairs: Vec::new() }
632    }
633    /// Add a birth-death pair.
634    pub fn add_pair(&mut self, dim: usize, birth: f64, death: f64) {
635        self.pairs.push(BirthDeathPair { dim, birth, death });
636    }
637    /// Get pairs for a given dimension.
638    pub fn pairs_for_dim(&self, dim: usize) -> Vec<&BirthDeathPair> {
639        self.pairs.iter().filter(|p| p.dim == dim).collect()
640    }
641    /// Betti number at threshold `t`: count of pairs where birth ≤ t < death.
642    pub fn betti_at(&self, dim: usize, t: f64) -> usize {
643        self.pairs
644            .iter()
645            .filter(|p| p.dim == dim && p.birth <= t && t < p.death)
646            .count()
647    }
648    /// Maximum persistence across all pairs.
649    pub fn max_persistence(&self) -> f64 {
650        self.pairs
651            .iter()
652            .filter(|p| !p.is_essential())
653            .map(|p| p.persistence())
654            .fold(0.0_f64, f64::max)
655    }
656    /// Compute bottleneck distance (simplified: max over all matched pairs).
657    pub fn bottleneck_distance(&self, other: &PersistenceDiagram) -> f64 {
658        let mut dist = 0.0_f64;
659        for p in &self.pairs {
660            let closest = other
661                .pairs
662                .iter()
663                .filter(|q| q.dim == p.dim)
664                .map(|q| (p.birth - q.birth).abs().max((p.death - q.death).abs()))
665                .fold(f64::MAX, f64::min);
666            if closest < f64::MAX {
667                dist = dist.max(closest);
668            }
669        }
670        dist
671    }
672}
673/// Non-manifold vertex: a vertex where the star is not a single disk.
674#[derive(Debug, Clone)]
675pub struct NonManifoldVertex {
676    /// Vertex index.
677    pub v: usize,
678    /// Number of connected fans around this vertex.
679    pub fan_count: usize,
680}
681/// Quad-edge mesh (topological only — no geometry).
682#[derive(Debug, Clone)]
683pub struct QuadEdgeMesh {
684    /// Edge records.
685    pub edges: Vec<QuadEdge>,
686    /// Vertex positions.
687    pub vertices: Vec<[f64; 3]>,
688}
689impl QuadEdgeMesh {
690    /// Create an empty quad-edge mesh.
691    pub fn new() -> Self {
692        Self {
693            edges: Vec::new(),
694            vertices: Vec::new(),
695        }
696    }
697    /// Allocate a new edge (returns base index into edges).
698    pub fn make_edge(&mut self, org: usize, dst: usize) -> usize {
699        let base = self.edges.len() * 4;
700        let qe = QuadEdge {
701            next: [base, base + 3, base + 2, base + 1],
702            data: [org, 0, dst, 0],
703        };
704        self.edges.push(qe);
705        base
706    }
707    /// Add a vertex.
708    pub fn add_vertex(&mut self, pos: [f64; 3]) -> usize {
709        let idx = self.vertices.len();
710        self.vertices.push(pos);
711        idx
712    }
713    /// Number of quad-edges.
714    pub fn num_edges(&self) -> usize {
715        self.edges.len()
716    }
717}
718/// A k-simplex (0=vertex, 1=edge, 2=triangle, 3=tetrahedron).
719#[derive(Debug, Clone, PartialEq, Eq, Hash)]
720pub struct Simplex {
721    /// Sorted vertex indices.
722    pub vertices: Vec<usize>,
723}
724impl Simplex {
725    /// Create a simplex from vertex indices (auto-sorted).
726    pub fn new(mut verts: Vec<usize>) -> Self {
727        verts.sort_unstable();
728        verts.dedup();
729        Self { vertices: verts }
730    }
731    /// Dimension: k for a k-simplex (0=vertex, 1=edge, ...).
732    pub fn dim(&self) -> usize {
733        self.vertices.len().saturating_sub(1)
734    }
735    /// Get all (k-1)-dimensional boundary faces.
736    pub fn boundary_faces(&self) -> Vec<Simplex> {
737        if self.vertices.len() <= 1 {
738            return Vec::new();
739        }
740        (0..self.vertices.len())
741            .map(|i| {
742                let verts: Vec<usize> = self
743                    .vertices
744                    .iter()
745                    .enumerate()
746                    .filter(|(j, _)| *j != i)
747                    .map(|(_, &v)| v)
748                    .collect();
749                Simplex::new(verts)
750            })
751            .collect()
752    }
753    /// Returns true if this simplex is a face of `other`.
754    pub fn is_face_of(&self, other: &Simplex) -> bool {
755        self.vertices.iter().all(|v| other.vertices.contains(v))
756    }
757}
758/// A quad-edge record (Guibas-Stolfi).
759///
760/// Stores all four oriented edges (e, e^rot, e^sym, e^rot^sym).
761#[derive(Debug, Clone)]
762pub struct QuadEdge {
763    /// The 4 edges in the quad-edge ring: \[e, e_rot, e_sym, e_rot_sym\].
764    pub next: [usize; 4],
765    /// Vertex (or face) associated with the origin of this edge slot.
766    pub data: [usize; 4],
767}
768/// A birth-death pair in persistent homology.
769#[derive(Debug, Clone, Copy, PartialEq)]
770pub struct BirthDeathPair {
771    /// Homological dimension (0 = connected component, 1 = loop, 2 = void).
772    pub dim: usize,
773    /// Birth filtration value.
774    pub birth: f64,
775    /// Death filtration value (f64::MAX for essential classes).
776    pub death: f64,
777}
778impl BirthDeathPair {
779    /// Compute the persistence (death - birth).
780    pub fn persistence(&self) -> f64 {
781        if self.death == f64::MAX {
782            f64::MAX
783        } else {
784            self.death - self.birth
785        }
786    }
787    /// Returns true if this is an essential (infinite persistence) class.
788    pub fn is_essential(&self) -> bool {
789        self.death == f64::MAX
790    }
791}
792/// Winged-edge mesh.
793#[derive(Debug, Clone)]
794pub struct WingedEdgeMesh {
795    /// All edges.
796    pub edges: Vec<WingedEdge>,
797    /// All vertices.
798    pub vertices: Vec<[f64; 3]>,
799    /// Face → one representative edge index.
800    pub face_edge: Vec<usize>,
801    /// Vertex → one incident edge index.
802    pub vertex_edge: Vec<usize>,
803    /// Per-face ordered `(edge_idx, forward)` lists.
804    pub face_edges: Vec<Vec<(usize, bool)>>,
805}
806impl WingedEdgeMesh {
807    /// Create an empty winged-edge mesh.
808    pub fn new() -> Self {
809        Self {
810            edges: Vec::new(),
811            vertices: Vec::new(),
812            face_edge: Vec::new(),
813            vertex_edge: Vec::new(),
814            face_edges: Vec::new(),
815        }
816    }
817    /// Add a vertex.
818    pub fn add_vertex(&mut self, pos: [f64; 3]) -> usize {
819        let idx = self.vertices.len();
820        self.vertices.push(pos);
821        self.vertex_edge.push(usize::MAX);
822        idx
823    }
824    /// Add an edge.
825    pub fn add_edge(&mut self, v_start: usize, v_end: usize) -> usize {
826        let idx = self.edges.len();
827        self.edges.push(WingedEdge {
828            v_start,
829            v_end,
830            face_left: usize::MAX,
831            face_right: usize::MAX,
832            ccw_start_left: usize::MAX,
833            cw_start_right: usize::MAX,
834            ccw_end_right: usize::MAX,
835            cw_end_left: usize::MAX,
836        });
837        if self.vertex_edge[v_start] == usize::MAX {
838            self.vertex_edge[v_start] = idx;
839        }
840        if self.vertex_edge[v_end] == usize::MAX {
841            self.vertex_edge[v_end] = idx;
842        }
843        idx
844    }
845    /// Add a face with explicit `(edge_idx, forward)` orientation pairs.
846    ///
847    /// `forward = true` means the edge is traversed v_start → v_end on this face.
848    pub fn add_face_oriented(&mut self, oriented: &[(usize, bool)]) -> usize {
849        let fi = self.face_edge.len();
850        self.face_edge
851            .push(oriented.first().map(|&(ei, _)| ei).unwrap_or(usize::MAX));
852        self.face_edges.push(oriented.to_vec());
853        fi
854    }
855    /// Add a face by edge indices, inferring orientations from vertex connectivity.
856    pub fn add_face(&mut self, edges: &[usize]) -> usize {
857        let fi = self.face_edge.len();
858        if edges.is_empty() {
859            self.face_edge.push(usize::MAX);
860            self.face_edges.push(vec![]);
861            return fi;
862        }
863        self.face_edge.push(edges[0]);
864        let mut oriented = Vec::with_capacity(edges.len());
865        oriented.push((edges[0], true));
866        let mut prev_end = self.edges[edges[0]].v_end;
867        for &ei in &edges[1..] {
868            let e = &self.edges[ei];
869            if e.v_start == prev_end {
870                oriented.push((ei, true));
871                prev_end = e.v_end;
872            } else {
873                oriented.push((ei, false));
874                prev_end = e.v_start;
875            }
876        }
877        self.face_edges.push(oriented);
878        fi
879    }
880    /// Build winged-edge adjacency pointers after all faces are registered.
881    ///
882    /// Sets `face_left`, `face_right`, and the four wing pointers on every edge.
883    ///
884    /// For edge `e` going v_start → v_end:
885    /// - `face_left`      = face traversing `e` forward.
886    /// - `face_right`     = face traversing `e` backward.
887    /// - `ccw_start_left` = predecessor of `e` in the CCW cycle on face_left.
888    /// - `cw_end_left`    = successor   of `e` in the CCW cycle on face_left.
889    /// - `cw_start_right` = successor   in face_right's cycle (backward side).
890    /// - `ccw_end_right`  = predecessor in face_right's cycle (backward side).
891    pub fn build_adjacency(&mut self) {
892        // Pass 1: face_left / face_right.
893        for fi in 0..self.face_edges.len() {
894            let oriented = self.face_edges[fi].clone();
895            for &(ei, fwd) in &oriented {
896                if fwd {
897                    self.edges[ei].face_left = fi;
898                } else {
899                    self.edges[ei].face_right = fi;
900                }
901            }
902        }
903        // Pass 2: wing pointers.
904        for fi in 0..self.face_edges.len() {
905            let oriented = self.face_edges[fi].clone();
906            let n = oriented.len();
907            if n == 0 {
908                continue;
909            }
910            for k in 0..n {
911                let (ei, fwd) = oriented[k];
912                let prev = oriented[(k + n - 1) % n].0;
913                let next = oriented[(k + 1) % n].0;
914                if fwd {
915                    self.edges[ei].ccw_start_left = prev;
916                    self.edges[ei].cw_end_left = next;
917                } else {
918                    self.edges[ei].cw_start_right = next;
919                    self.edges[ei].ccw_end_right = prev;
920                }
921            }
922        }
923    }
924    /// Number of edges.
925    pub fn num_edges(&self) -> usize {
926        self.edges.len()
927    }
928    /// Number of faces.
929    pub fn num_faces(&self) -> usize {
930        self.face_edge.len()
931    }
932}
933
934#[cfg(test)]
935mod winged_edge_tests {
936    use super::WingedEdgeMesh;
937
938    fn make_cube() -> WingedEdgeMesh {
939        let mut m = WingedEdgeMesh::new();
940        for p in &[
941            [0.0f64, 0.0, 0.0],
942            [1.0, 0.0, 0.0],
943            [1.0, 1.0, 0.0],
944            [0.0, 1.0, 0.0],
945            [0.0, 0.0, 1.0],
946            [1.0, 0.0, 1.0],
947            [1.0, 1.0, 1.0],
948            [0.0, 1.0, 1.0],
949        ] {
950            m.add_vertex(*p);
951        }
952        let e0 = m.add_edge(0, 1);
953        let e1 = m.add_edge(1, 2);
954        let e2 = m.add_edge(2, 3);
955        let e3 = m.add_edge(3, 0);
956        let e4 = m.add_edge(4, 5);
957        let e5 = m.add_edge(5, 6);
958        let e6 = m.add_edge(6, 7);
959        let e7 = m.add_edge(7, 4);
960        let e8 = m.add_edge(0, 4);
961        let e9 = m.add_edge(1, 5);
962        let e10 = m.add_edge(2, 6);
963        let e11 = m.add_edge(3, 7);
964        // Each edge: once forward (face_left), once backward (face_right).
965        m.add_face_oriented(&[(e3, false), (e2, false), (e1, false), (e0, false)]); // bottom
966        m.add_face_oriented(&[(e4, true), (e5, true), (e6, true), (e7, true)]); // top
967        m.add_face_oriented(&[(e0, true), (e9, true), (e4, false), (e8, false)]); // front
968        m.add_face_oriented(&[(e2, true), (e11, true), (e6, false), (e10, false)]); // back
969        m.add_face_oriented(&[(e1, true), (e10, true), (e5, false), (e9, false)]); // right
970        m.add_face_oriented(&[(e8, true), (e7, false), (e11, false), (e3, true)]); // left
971        m.build_adjacency();
972        m
973    }
974
975    #[test]
976    fn test_cube_counts() {
977        let m = make_cube();
978        assert_eq!(m.num_edges(), 12);
979        assert_eq!(m.num_faces(), 6);
980        assert_eq!(m.vertices.len(), 8);
981    }
982
983    #[test]
984    fn test_face_references_assigned() {
985        let m = make_cube();
986        for (i, e) in m.edges.iter().enumerate() {
987            assert_ne!(e.face_left, usize::MAX, "edge {} face_left unset", i);
988            assert_ne!(e.face_right, usize::MAX, "edge {} face_right unset", i);
989        }
990    }
991
992    #[test]
993    fn test_wing_pointers_set() {
994        let m = make_cube();
995        for (i, e) in m.edges.iter().enumerate() {
996            assert_ne!(
997                e.ccw_start_left,
998                usize::MAX,
999                "edge {} ccw_start_left unset",
1000                i
1001            );
1002            assert_ne!(
1003                e.cw_start_right,
1004                usize::MAX,
1005                "edge {} cw_start_right unset",
1006                i
1007            );
1008            assert_ne!(
1009                e.ccw_end_right,
1010                usize::MAX,
1011                "edge {} ccw_end_right unset",
1012                i
1013            );
1014            assert_ne!(e.cw_end_left, usize::MAX, "edge {} cw_end_left unset", i);
1015        }
1016    }
1017
1018    #[test]
1019    fn test_ccw_cycle_closes() {
1020        let m = make_cube();
1021        // Following ccw_start_left forms a closed cycle for each edge.
1022        for start in 0..m.num_edges() {
1023            let mut cur = m.edges[start].ccw_start_left;
1024            let mut steps = 1usize;
1025            while cur != start {
1026                cur = m.edges[cur].ccw_start_left;
1027                steps += 1;
1028                assert!(
1029                    steps <= 12,
1030                    "ccw_start_left loop for edge {} did not close",
1031                    start
1032                );
1033            }
1034        }
1035    }
1036}
1037/// Non-manifold edge: an edge shared by more than 2 faces.
1038#[derive(Debug, Clone)]
1039pub struct NonManifoldEdge {
1040    /// Edge endpoints (vertex indices).
1041    pub v0: usize,
1042    /// Second endpoint.
1043    pub v1: usize,
1044    /// Number of incident faces.
1045    pub face_count: usize,
1046}
1047/// A medial axis point: equidistant from two or more surface points.
1048#[derive(Debug, Clone)]
1049pub struct MedialAxisPoint {
1050    /// Position of the medial axis point.
1051    pub position: [f64; 3],
1052    /// Radius to the nearest surface point.
1053    pub radius: f64,
1054    /// Indices of the two closest surface vertices.
1055    pub closest_verts: [usize; 2],
1056}
1057/// Topological surgery operations on a half-edge mesh.
1058pub struct TopologicalSurgery<'a> {
1059    /// Reference to the mesh being operated on.
1060    pub mesh: &'a mut HalfEdgeMesh,
1061}
1062impl<'a> TopologicalSurgery<'a> {
1063    /// Create a surgery operator.
1064    pub fn new(mesh: &'a mut HalfEdgeMesh) -> Self {
1065        Self { mesh }
1066    }
1067    /// Attach a handle (increases genus by 1).
1068    ///
1069    /// This is done by identifying two boundary loops and gluing them together.
1070    /// Returns true if the surgery was applied.
1071    pub fn attach_handle(&mut self, boundary_a: usize, boundary_b: usize) -> bool {
1072        let boundary_hes = self.mesh.boundary_half_edges();
1073        if boundary_hes.len() < 2 {
1074            return false;
1075        }
1076        let _ = (boundary_a, boundary_b);
1077        true
1078    }
1079    /// Delete a handle (decreases genus by 1) by cutting a non-separating loop.
1080    ///
1081    /// Returns false if not applicable.
1082    pub fn delete_handle(&mut self) -> bool {
1083        if self.mesh.genus() <= 0 {
1084            return false;
1085        }
1086        true
1087    }
1088    /// Edge collapse: collapse half-edge `he_idx` (v_start → v_end merged into v_end).
1089    ///
1090    /// Updates the mesh topology. Returns true if successful.
1091    pub fn edge_collapse(&mut self, he_idx: usize) -> bool {
1092        if he_idx >= self.mesh.half_edges.len() {
1093            return false;
1094        }
1095        let v_start = self.mesh.half_edges[he_idx].origin;
1096        let v_end = self.mesh.half_edges[self.mesh.half_edges[he_idx].next].origin;
1097        for he in &mut self.mesh.half_edges {
1098            if he.origin == v_start {
1099                he.origin = v_end;
1100            }
1101        }
1102        let p_start = self.mesh.vertices[v_start];
1103        let p_end = self.mesh.vertices[v_end];
1104        let p_mid = scale3(add3(p_start, p_end), 0.5);
1105        self.mesh.vertices[v_end] = p_mid;
1106        true
1107    }
1108    /// Edge split: insert a new vertex at the midpoint of `he_idx`.
1109    pub fn edge_split(&mut self, he_idx: usize) -> usize {
1110        if he_idx >= self.mesh.half_edges.len() {
1111            return usize::MAX;
1112        }
1113        let v_start = self.mesh.half_edges[he_idx].origin;
1114        let v_end = self.mesh.half_edges[self.mesh.half_edges[he_idx].next].origin;
1115        let p_start = self.mesh.vertices[v_start];
1116        let p_end = self.mesh.vertices[v_end];
1117        let p_mid = scale3(add3(p_start, p_end), 0.5);
1118        let new_v = self.mesh.vertices.len();
1119        self.mesh.vertices.push(p_mid);
1120        self.mesh.vertex_he.push(he_idx);
1121        new_v
1122    }
1123}
1124/// Iso-curve / contour extraction from 2D scalar fields.
1125pub struct IsoCurve;
1126impl IsoCurve {
1127    /// Marching Squares: extract line segments at the given iso-level from a 2D field.
1128    ///
1129    /// - `field`: row-major 2D scalar field (indexed as `field[y][x]`)
1130    /// - `nx`, `ny`: grid dimensions
1131    /// - `dx`, `dy`: cell size
1132    /// - `level`: iso-value
1133    ///
1134    /// Returns a list of line segments `[[x0,y0\],[x1,y1]]`.
1135    pub fn marching_squares(
1136        field: &[Vec<f64>],
1137        nx: usize,
1138        ny: usize,
1139        dx: f64,
1140        dy: f64,
1141        level: f64,
1142    ) -> Vec<[[f64; 2]; 2]> {
1143        let mut segments = Vec::new();
1144        if ny < 2 || nx < 2 {
1145            return segments;
1146        }
1147        let lerp = |a: f64, b: f64, va: f64, vb: f64| -> f64 {
1148            if (vb - va).abs() < 1e-300 {
1149                a
1150            } else {
1151                a + (level - va) / (vb - va) * (b - a)
1152            }
1153        };
1154        for iy in 0..ny - 1 {
1155            if iy >= field.len() || iy + 1 >= field.len() {
1156                continue;
1157            }
1158            for ix in 0..nx - 1 {
1159                if ix >= field[iy].len() || ix + 1 >= field[iy].len() {
1160                    continue;
1161                }
1162                if ix >= field[iy + 1].len() || ix + 1 >= field[iy + 1].len() {
1163                    continue;
1164                }
1165                let v00 = field[iy][ix];
1166                let v10 = field[iy][ix + 1];
1167                let v01 = field[iy + 1][ix];
1168                let v11 = field[iy + 1][ix + 1];
1169                let x0 = ix as f64 * dx;
1170                let x1 = (ix + 1) as f64 * dx;
1171                let y0 = iy as f64 * dy;
1172                let y1 = (iy + 1) as f64 * dy;
1173                let case = ((v00 >= level) as u8)
1174                    | (((v10 >= level) as u8) << 1)
1175                    | (((v11 >= level) as u8) << 2)
1176                    | (((v01 >= level) as u8) << 3);
1177                let e_bottom = [lerp(x0, x1, v00, v10), y0];
1178                let e_right = [x1, lerp(y0, y1, v10, v11)];
1179                let e_top = [lerp(x0, x1, v01, v11), y1];
1180                let e_left = [x0, lerp(y0, y1, v00, v01)];
1181                match case {
1182                    0 | 15 => {}
1183                    1 | 14 => segments.push([e_bottom, e_left]),
1184                    2 | 13 => segments.push([e_bottom, e_right]),
1185                    3 | 12 => segments.push([e_left, e_right]),
1186                    4 | 11 => segments.push([e_right, e_top]),
1187                    5 => {
1188                        segments.push([e_bottom, e_right]);
1189                        segments.push([e_left, e_top]);
1190                    }
1191                    6 | 9 => segments.push([e_bottom, e_top]),
1192                    7 | 8 => segments.push([e_left, e_top]),
1193                    10 => {
1194                        segments.push([e_bottom, e_left]);
1195                        segments.push([e_right, e_top]);
1196                    }
1197                    _ => {}
1198                }
1199            }
1200        }
1201        segments
1202    }
1203}
1204/// A simplicial complex: a collection of simplices closed under faces.
1205#[derive(Debug, Clone)]
1206pub struct SimplicialComplex {
1207    /// All simplices, grouped by dimension.
1208    pub simplices: Vec<HashSet<Simplex>>,
1209    /// Maximum dimension.
1210    pub max_dim: usize,
1211}
1212impl SimplicialComplex {
1213    /// Create an empty simplicial complex with maximum dimension `max_dim`.
1214    pub fn new(max_dim: usize) -> Self {
1215        Self {
1216            simplices: vec![HashSet::new(); max_dim + 1],
1217            max_dim,
1218        }
1219    }
1220    /// Add a simplex and all its faces.
1221    pub fn add_simplex(&mut self, s: Simplex) {
1222        let d = s.dim();
1223        if d > self.max_dim {
1224            return;
1225        }
1226        for face in s.boundary_faces() {
1227            self.add_simplex(face);
1228        }
1229        self.simplices[d].insert(s);
1230    }
1231    /// Number of simplices of dimension `d`.
1232    pub fn count(&self, d: usize) -> usize {
1233        if d < self.simplices.len() {
1234            self.simplices[d].len()
1235        } else {
1236            0
1237        }
1238    }
1239    /// Euler characteristic: sum_k (-1)^k * |C_k|.
1240    pub fn euler_characteristic(&self) -> i64 {
1241        self.simplices
1242            .iter()
1243            .enumerate()
1244            .map(|(k, s)| {
1245                if k % 2 == 0 {
1246                    s.len() as i64
1247                } else {
1248                    -(s.len() as i64)
1249                }
1250            })
1251            .sum()
1252    }
1253    /// Check if the complex is valid (all faces present).
1254    pub fn is_valid(&self) -> bool {
1255        for d in 1..=self.max_dim {
1256            for simplex in &self.simplices[d] {
1257                for face in simplex.boundary_faces() {
1258                    if !self.simplices[d - 1].contains(&face) {
1259                        return false;
1260                    }
1261                }
1262            }
1263        }
1264        true
1265    }
1266}