1use core::f64::consts::PI;
45
46use axiolid_core::{Point3, Scalar, Vec3};
47use axiolid_mesh::halfedge::{FaceId, HalfedgeBuildError, HalfedgeId, HalfedgeMesh, VertexId};
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum SubdivisionScheme {
53 Loop,
56 CatmullClark,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct SubdivisionOptions {
64 pub scheme: SubdivisionScheme,
66 pub levels: u32,
68 pub max_faces: usize,
70}
71
72impl SubdivisionOptions {
73 #[must_use]
75 pub const fn new(scheme: SubdivisionScheme, levels: u32) -> Self {
76 Self {
77 scheme,
78 levels,
79 max_faces: 50_000_000,
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, thiserror::Error)]
86#[non_exhaustive]
87pub enum SubdivisionError {
88 #[error("face {face} has {degree} sides; Loop subdivision needs triangles")]
90 NotATriangle {
91 face: FaceId,
93 degree: usize,
95 },
96 #[error("face {face} has {degree} sides; Catmull-Clark limit positions need quads")]
98 NotAQuad {
99 face: FaceId,
101 degree: usize,
103 },
104 #[error("subdivision would produce {faces} faces, over the {limit} budget")]
106 BudgetExceeded {
107 faces: usize,
109 limit: usize,
111 },
112 #[error("subdivided mesh could not be built: {0}")]
115 Build(#[from] HalfedgeBuildError),
116}
117
118pub fn subdivide(
126 mesh: &HalfedgeMesh,
127 options: &SubdivisionOptions,
128) -> Result<HalfedgeMesh, SubdivisionError> {
129 let mut current = mesh.clone();
130 for _ in 0..options.levels {
131 let faces = match options.scheme {
132 SubdivisionScheme::Loop => {
133 require_triangles(¤t)?;
134 current.face_count() * 4
135 }
136 SubdivisionScheme::CatmullClark => current
137 .faces()
138 .map(|f| current.face_degree(f))
139 .sum::<usize>(),
140 };
141 if faces > options.max_faces {
142 return Err(SubdivisionError::BudgetExceeded {
143 faces,
144 limit: options.max_faces,
145 });
146 }
147 current = match options.scheme {
148 SubdivisionScheme::Loop => loop_level(¤t)?,
149 SubdivisionScheme::CatmullClark => catmull_clark_level(¤t)?,
150 };
151 }
152 Ok(current)
153}
154
155pub fn limit_positions(
165 mesh: &HalfedgeMesh,
166 scheme: SubdivisionScheme,
167) -> Result<Vec<Point3>, SubdivisionError> {
168 match scheme {
169 SubdivisionScheme::Loop => require_triangles(mesh)?,
170 SubdivisionScheme::CatmullClark => {
171 for f in mesh.faces() {
172 let degree = mesh.face_degree(f);
173 if degree != 4 {
174 return Err(SubdivisionError::NotAQuad { face: f, degree });
175 }
176 }
177 }
178 }
179 let mut out = mesh.positions().to_vec();
180 for v in mesh.vertices() {
181 let p = mesh.position(v);
182 out[v.index()] = if mesh.is_isolated(v) {
183 p
184 } else if let Some((b1, b2)) = boundary_neighbours(mesh, v) {
185 (4.0 * p + b1 + b2) / 6.0
187 } else {
188 match scheme {
189 SubdivisionScheme::Loop => {
190 let n = mesh.degree(v) as Scalar;
191 let gamma = 1.0 / (3.0 / (8.0 * loop_beta(n)) + n);
192 let sum = mesh
193 .vertex_vertices(v)
194 .fold(Vec3::ZERO, |s, u| s + mesh.position(u));
195 (1.0 - n * gamma) * p + gamma * sum
196 }
197 SubdivisionScheme::CatmullClark => {
198 let n = mesh.degree(v) as Scalar;
199 let mut edges = Vec3::ZERO;
200 let mut diagonals = Vec3::ZERO;
201 for h in mesh.outgoing_halfedges(v) {
202 edges += mesh.position(mesh.target(h));
203 diagonals += mesh.position(mesh.target(mesh.next(h)));
204 }
205 (n * n * p + 4.0 * edges + diagonals) / (n * (n + 5.0))
206 }
207 }
208 };
209 }
210 Ok(out)
211}
212
213fn require_triangles(mesh: &HalfedgeMesh) -> Result<(), SubdivisionError> {
214 for f in mesh.faces() {
215 let degree = mesh.face_degree(f);
216 if degree != 3 {
217 return Err(SubdivisionError::NotATriangle { face: f, degree });
218 }
219 }
220 Ok(())
221}
222
223fn loop_beta(n: Scalar) -> Scalar {
225 let c = 3.0 / 8.0 + 0.25 * (2.0 * PI / n).cos();
226 (5.0 / 8.0 - c * c) / n
227}
228
229fn boundary_neighbours(mesh: &HalfedgeMesh, v: VertexId) -> Option<(Point3, Point3)> {
233 let h = mesh.vertex_halfedge(v)?;
234 if !mesh.is_boundary_halfedge(h) {
235 return None;
236 }
237 let next = mesh.target(h);
238 let previous = mesh.source(mesh.prev(h));
239 Some((mesh.position(next), mesh.position(previous)))
240}
241
242fn boundary_vertex(p: Point3, b1: Point3, b2: Point3) -> Point3 {
244 0.75 * p + 0.125 * (b1 + b2)
245}
246
247fn vertex_numbering(mesh: &HalfedgeMesh) -> (Vec<u32>, Vec<VertexId>) {
249 let mut number = vec![u32::MAX; mesh.positions().len()];
250 let live: Vec<VertexId> = mesh.vertices().collect();
251 for (k, v) in live.iter().enumerate() {
252 number[v.index()] = k as u32;
253 }
254 (number, live)
255}
256
257fn edge_numbering(mesh: &HalfedgeMesh, base: usize) -> Vec<u32> {
259 let slots = mesh.edges().last().map_or(0, |e| e.index() + 1);
260 let mut number = vec![u32::MAX; slots];
261 for (k, e) in mesh.edges().enumerate() {
262 number[e.index()] = (base + k) as u32;
263 }
264 number
265}
266
267fn loop_level(mesh: &HalfedgeMesh) -> Result<HalfedgeMesh, SubdivisionError> {
268 let (vertex_number, live) = vertex_numbering(mesh);
269 let edge_number = edge_numbering(mesh, live.len());
270 let mut positions = Vec::with_capacity(live.len() + mesh.edge_count());
271 for &v in &live {
272 let p = mesh.position(v);
273 positions.push(if mesh.is_isolated(v) {
274 p
275 } else if let Some((b1, b2)) = boundary_neighbours(mesh, v) {
276 boundary_vertex(p, b1, b2)
277 } else {
278 let n = mesh.degree(v) as Scalar;
279 let beta = loop_beta(n);
280 let sum = mesh
281 .vertex_vertices(v)
282 .fold(Vec3::ZERO, |s, u| s + mesh.position(u));
283 (1.0 - n * beta) * p + beta * sum
284 });
285 }
286 for e in mesh.edges() {
287 let h = mesh.edge_halfedge(e, 0);
288 let g = mesh.opposite(h);
289 let a = mesh.position(mesh.source(h));
290 let b = mesh.position(mesh.target(h));
291 positions.push(if mesh.is_boundary_edge(e) {
292 0.5 * (a + b)
293 } else {
294 let c = mesh.position(mesh.target(mesh.next(h)));
295 let d = mesh.position(mesh.target(mesh.next(g)));
296 0.375 * (a + b) + 0.125 * (c + d)
297 });
298 }
299 let mut faces: Vec<[u32; 3]> = Vec::with_capacity(mesh.face_count() * 4);
300 for f in mesh.faces() {
301 let hs: Vec<HalfedgeId> = mesh.face_halfedges(f).collect();
302 let corner = |i: usize| vertex_number[mesh.source(hs[i]).index()];
303 let mid = |i: usize| edge_number[mesh.edge(hs[i]).index()];
304 for i in 0..3 {
305 faces.push([corner(i), mid(i), mid((i + 2) % 3)]);
306 }
307 faces.push([mid(0), mid(1), mid(2)]);
308 }
309 Ok(HalfedgeMesh::from_faces(positions, &faces)?)
310}
311
312fn catmull_clark_level(mesh: &HalfedgeMesh) -> Result<HalfedgeMesh, SubdivisionError> {
313 let (vertex_number, live) = vertex_numbering(mesh);
314 let edge_number = edge_numbering(mesh, live.len());
315 let face_base = live.len() + mesh.edge_count();
316 let mut face_number = vec![u32::MAX; mesh.faces().last().map_or(0, |f| f.index() + 1)];
317 let mut face_point = vec![Vec3::ZERO; face_number.len()];
318 for (k, f) in mesh.faces().enumerate() {
319 face_number[f.index()] = (face_base + k) as u32;
320 let (sum, count) = mesh
321 .face_vertices(f)
322 .fold((Vec3::ZERO, 0usize), |(s, c), v| {
323 (s + mesh.position(v), c + 1)
324 });
325 face_point[f.index()] = sum / count as Scalar;
326 }
327 let mut positions = Vec::with_capacity(face_base + mesh.face_count());
328 for &v in &live {
329 let p = mesh.position(v);
330 positions.push(if mesh.is_isolated(v) {
331 p
332 } else if let Some((b1, b2)) = boundary_neighbours(mesh, v) {
333 boundary_vertex(p, b1, b2)
334 } else {
335 let mut q = Vec3::ZERO;
336 let mut r = Vec3::ZERO;
337 let mut n = 0usize;
338 for h in mesh.outgoing_halfedges(v) {
339 let f = mesh
340 .face(h)
341 .expect("an interior vertex has faces all round");
342 q += face_point[f.index()];
343 r += 0.5 * (p + mesh.position(mesh.target(h)));
344 n += 1;
345 }
346 let n = n as Scalar;
347 (q / n + 2.0 * r / n + (n - 3.0) * p) / n
348 });
349 }
350 for e in mesh.edges() {
351 let h = mesh.edge_halfedge(e, 0);
352 let a = mesh.position(mesh.source(h));
353 let b = mesh.position(mesh.target(h));
354 positions.push(match (mesh.face(h), mesh.face(mesh.opposite(h))) {
355 (Some(f1), Some(f2)) => {
356 0.25 * (a + b + face_point[f1.index()] + face_point[f2.index()])
357 }
358 _ => 0.5 * (a + b),
359 });
360 }
361 for f in mesh.faces() {
362 positions.push(face_point[f.index()]);
363 }
364 let mut faces: Vec<[u32; 4]> =
365 Vec::with_capacity(mesh.faces().map(|f| mesh.face_degree(f)).sum::<usize>());
366 for f in mesh.faces() {
367 let hs: Vec<HalfedgeId> = mesh.face_halfedges(f).collect();
368 let k = hs.len();
369 let centre = face_number[f.index()];
370 for i in 0..k {
371 let previous = hs[(i + k - 1) % k];
372 faces.push([
373 vertex_number[mesh.source(hs[i]).index()],
374 edge_number[mesh.edge(hs[i]).index()],
375 centre,
376 edge_number[mesh.edge(previous).index()],
377 ]);
378 }
379 }
380 Ok(HalfedgeMesh::from_faces(positions, &faces)?)
381}