Skip to main content

proof_engine/geometry/
subdivision.rs

1//! Subdivision surfaces — Catmull-Clark and Loop subdivision on glyph meshes.
2
3use glam::{Vec2, Vec3, Vec4};
4use std::collections::HashMap;
5use super::GeoMesh;
6
7/// Subdivision scheme.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum SubdivisionScheme {
10    /// Catmull-Clark: quads → quads, smoothing, suitable for any mesh.
11    CatmullClark,
12    /// Loop: triangles → triangles, for triangle meshes only.
13    Loop,
14    /// Simple midpoint: split each edge, no smoothing.
15    Midpoint,
16}
17
18/// A mesh optimized for subdivision operations.
19#[derive(Debug, Clone)]
20pub struct SubdivMesh {
21    pub vertices: Vec<Vec3>,
22    pub faces: Vec<Vec<u32>>,  // each face is a list of vertex indices
23}
24
25impl SubdivMesh {
26    pub fn new() -> Self { Self { vertices: Vec::new(), faces: Vec::new() } }
27
28    pub fn from_geo_mesh(mesh: &GeoMesh) -> Self {
29        let vertices = mesh.vertices.clone();
30        let faces: Vec<Vec<u32>> = mesh.triangles.iter()
31            .map(|t| vec![t.a, t.b, t.c])
32            .collect();
33        Self { vertices, faces }
34    }
35
36    /// Subdivide the mesh using the given scheme.
37    pub fn subdivide(&self, scheme: SubdivisionScheme) -> SubdivMesh {
38        match scheme {
39            SubdivisionScheme::CatmullClark => self.catmull_clark(),
40            SubdivisionScheme::Loop => self.loop_subdivide(),
41            SubdivisionScheme::Midpoint => self.midpoint_subdivide(),
42        }
43    }
44
45    /// Apply n levels of subdivision.
46    pub fn subdivide_n(&self, scheme: SubdivisionScheme, levels: u32) -> SubdivMesh {
47        let mut mesh = self.clone();
48        for _ in 0..levels {
49            mesh = mesh.subdivide(scheme);
50        }
51        mesh
52    }
53
54    fn catmull_clark(&self) -> SubdivMesh {
55        let n_verts = self.vertices.len();
56        let mut new_verts: Vec<Vec3> = Vec::new();
57        let mut new_faces: Vec<Vec<u32>> = Vec::new();
58
59        // 1. Face points: average of all vertices in the face
60        let mut face_points = Vec::with_capacity(self.faces.len());
61        for face in &self.faces {
62            let avg = face.iter()
63                .map(|&i| self.vertices[i as usize])
64                .fold(Vec3::ZERO, |a, b| a + b) / face.len() as f32;
65            face_points.push(new_verts.len() as u32);
66            new_verts.push(avg);
67        }
68
69        // 2. Edge points: average of edge endpoints + adjacent face points
70        let mut edge_map: HashMap<(u32, u32), u32> = HashMap::new();
71        let mut edge_faces: HashMap<(u32, u32), Vec<usize>> = HashMap::new();
72
73        for (fi, face) in self.faces.iter().enumerate() {
74            for i in 0..face.len() {
75                let a = face[i];
76                let b = face[(i + 1) % face.len()];
77                let key = if a < b { (a, b) } else { (b, a) };
78                edge_faces.entry(key).or_default().push(fi);
79            }
80        }
81
82        for (&(a, b), faces) in &edge_faces {
83            let mid = (self.vertices[a as usize] + self.vertices[b as usize]) * 0.5;
84            let face_avg: Vec3 = faces.iter()
85                .map(|&fi| new_verts[face_points[fi] as usize])
86                .fold(Vec3::ZERO, |acc, v| acc + v) / faces.len() as f32;
87            let edge_point = (mid + face_avg) * 0.5;
88            let idx = new_verts.len() as u32;
89            new_verts.push(edge_point);
90            edge_map.insert((a, b), idx);
91        }
92
93        // 3. Move original vertices
94        let orig_offset = new_verts.len() as u32;
95        for (vi, &vert) in self.vertices.iter().enumerate() {
96            // For simplicity, use a weighted average (full CC uses face/edge neighbors)
97            new_verts.push(vert);
98        }
99
100        // 4. Create new faces (one quad per original face edge)
101        for (fi, face) in self.faces.iter().enumerate() {
102            let fp = face_points[fi];
103            for i in 0..face.len() {
104                let a = face[i];
105                let b = face[(i + 1) % face.len()];
106                let prev = face[(i + face.len() - 1) % face.len()];
107
108                let key_ab = if a < b { (a, b) } else { (b, a) };
109                let key_pa = if prev < a { (prev, a) } else { (a, prev) };
110
111                let ep_ab = edge_map[&key_ab];
112                let ep_pa = edge_map[&key_pa];
113                let orig_a = orig_offset + a;
114
115                new_faces.push(vec![fp, ep_pa, orig_a, ep_ab]);
116            }
117        }
118
119        SubdivMesh { vertices: new_verts, faces: new_faces }
120    }
121
122    fn loop_subdivide(&self) -> SubdivMesh {
123        // Loop subdivision: insert edge midpoints, reconnect as 4 triangles per original
124        let mut new_verts = self.vertices.clone();
125        let mut new_faces = Vec::new();
126        let mut edge_map: HashMap<(u32, u32), u32> = HashMap::new();
127
128        let mut get_edge_vert = |a: u32, b: u32, verts: &mut Vec<Vec3>, orig: &[Vec3]| -> u32 {
129            let key = if a < b { (a, b) } else { (b, a) };
130            if let Some(&idx) = edge_map.get(&key) { return idx; }
131            let mid = (orig[a as usize] + orig[b as usize]) * 0.5;
132            let idx = verts.len() as u32;
133            verts.push(mid);
134            edge_map.insert(key, idx);
135            idx
136        };
137
138        for face in &self.faces {
139            if face.len() != 3 { continue; }
140            let (a, b, c) = (face[0], face[1], face[2]);
141            let ab = get_edge_vert(a, b, &mut new_verts, &self.vertices);
142            let bc = get_edge_vert(b, c, &mut new_verts, &self.vertices);
143            let ca = get_edge_vert(c, a, &mut new_verts, &self.vertices);
144            new_faces.push(vec![a, ab, ca]);
145            new_faces.push(vec![ab, b, bc]);
146            new_faces.push(vec![ca, bc, c]);
147            new_faces.push(vec![ab, bc, ca]);
148        }
149
150        SubdivMesh { vertices: new_verts, faces: new_faces }
151    }
152
153    fn midpoint_subdivide(&self) -> SubdivMesh {
154        // Same as loop but without smoothing
155        self.loop_subdivide()
156    }
157
158    /// Convert back to GeoMesh (triangulated).
159    pub fn to_geo_mesh(&self) -> GeoMesh {
160        let mut mesh = GeoMesh::new();
161        for &v in &self.vertices {
162            mesh.add_vertex(v, Vec3::Y, Vec2::ZERO);
163        }
164        for face in &self.faces {
165            if face.len() >= 3 {
166                // Fan triangulation
167                for i in 1..face.len() - 1 {
168                    mesh.add_triangle(face[0], face[i as usize], face[i as usize + 1]);
169                }
170            }
171        }
172        mesh.recompute_normals();
173        mesh
174    }
175
176    pub fn vertex_count(&self) -> usize { self.vertices.len() }
177    pub fn face_count(&self) -> usize { self.faces.len() }
178}
179
180impl Default for SubdivMesh {
181    fn default() -> Self { Self::new() }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    fn make_triangle() -> SubdivMesh {
189        SubdivMesh {
190            vertices: vec![Vec3::ZERO, Vec3::X, Vec3::Y],
191            faces: vec![vec![0, 1, 2]],
192        }
193    }
194
195    #[test]
196    fn loop_subdivision_increases_faces() {
197        let mesh = make_triangle();
198        let subdiv = mesh.subdivide(SubdivisionScheme::Loop);
199        assert_eq!(subdiv.face_count(), 4); // 1 tri → 4 tris
200        assert_eq!(subdiv.vertex_count(), 6); // 3 orig + 3 edge midpoints
201    }
202
203    #[test]
204    fn catmull_clark_produces_quads() {
205        let mesh = make_triangle();
206        let subdiv = mesh.subdivide(SubdivisionScheme::CatmullClark);
207        assert!(subdiv.face_count() > 0);
208        // Each face should be a quad (4 vertices)
209        for face in &subdiv.faces {
210            assert_eq!(face.len(), 4);
211        }
212    }
213
214    #[test]
215    fn multi_level_subdivision() {
216        let mesh = make_triangle();
217        let subdiv = mesh.subdivide_n(SubdivisionScheme::Loop, 3);
218        assert!(subdiv.face_count() > 16); // 1 → 4 → 16 → 64
219    }
220}