///|
/// Output plus count snapshots (including level zero) taken before return.
pub(all) struct LoopResult {
mesh : Mesh
stages : Array[TopologyStats]
}
///|
/// Preflight every level before allocating new geometry, including open surfaces.
pub fn Mesh::plan_loop(
self : Mesh,
levels : Int,
) -> Array[TopologyStats] raise SurfaceError {
if levels < 0 {
raise InvalidArgument("Loop levels must be nonnegative")
}
if levels > self.limits.max_levels {
raise LimitExceeded("Loop levels")
}
if self.face_count() > self.limits.max_output_faces {
raise LimitExceeded("Loop faces")
}
let stages = [self.stats()]
for _ in 0.. self.limits.max_vertices {
raise LimitExceeded("Loop vertices")
}
if faces > self.limits.max_output_faces {
raise LimitExceeded("Loop faces")
}
stages.push({
..previous,
vertices,
faces,
edges: previous.edges * 2 + previous.faces * 3,
boundary_edges: previous.boundary_edges * 2,
})
}
stages
}
///|
/// A detached copy, including geometry, connectivity and unchanged resource policy.
pub fn Mesh::copy(self : Mesh) -> Mesh raise SurfaceError {
let indexed = self.to_indexed()
let limits = {
..self.limits,
max_input_faces: Int::max(self.face_count(), self.limits.max_input_faces),
}
let mesh = Mesh::from_indexed(indexed.vertices, indexed.triangles, limits~)
{ ..mesh, limits: self.limits, }
}
///| Uniform original Loop subdivision. The input is unchanged even on failure.
///|
/// Level zero also returns a detached mesh, rather than an alias of the input.
pub fn subdivide_loop(
mesh : Mesh,
levels : Int,
) -> LoopResult raise SurfaceError {
let stages = mesh.plan_loop(levels)
let mut result = mesh
if levels == 0 {
result = mesh.copy()
}
for _ in 0..