Typical loop (libigl/OpenMesh style):
while (!Q.empty()) {
auto [op, eid] = Q.pop();
if (is_removed(eid)) continue;
if (!link_condition(eid)) continue;
if (!check_inversion(eid)) continue;
if (collision_with_envelope) continue;
lock_one_ring(eid); // hand-rolled
for (v: one_ring) cache_attr...
collapse(eid); // manually splice
for (v: new_ring) recompute, push Q
unlock();
}
Every project rewrites link_condition, check_inversion, attr lerp, locking. Miss one envelope test → self-intersect. Lock order → deadlock.
Our vision – 15 LoC:
// declarative – no half-edge juggling
auto m = TriMesh::from_file("bunny.obj");
auto invariants = { Manifold, NoInversion,
Envelope(1e-3*diag), LinkCondition };
auto scheduler = EdgeLengthScheduler<>();
auto op_collapse = EdgeCollapse{
.energy = [](Edge e){ return -e.length(); },
.precondition = [](Edge e){
return e.length() < 4.0/3*target; },
.transfer = { .pos=Linear, .uv=Wachspress }
};
wmtk::run(m,invariants,scheduler,op_collapse);
Change 4/3 to sqrt(2) and you have a new paper.