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