// InsertVertex in the paper static void insert_cavity_vertex_helper(MutableTriangleTopology& mesh, RawField<const Perturbed2,VertexId> X, RawField<bool,VertexId> marked, const HalfedgeId vw) { // If wv is a boundary edge, or we're already Delaunay and properly oriented, we're done const auto wv = mesh.reverse(vw); if (mesh.is_boundary(wv)) return; const auto u = mesh.opposite(vw), v = mesh.src(vw), w = mesh.dst(vw), x = mesh.opposite(wv); const auto Xu = X[u], Xv = X[v], Xw = X[w], Xx = X[x]; const bool in = incircle(Xu,Xv,Xw,Xx); if (!in && triangle_oriented(Xu,Xv,Xw)) return; // Flip edge and recurse const auto xu = mesh.flip_edge(wv); assert(mesh.vertices(xu)==vec(x,u)); const auto vx = mesh.prev(xu), xw = mesh.next(mesh.reverse(xu)); // Grab this now before the recursive call changes uvx insert_cavity_vertex_helper(mesh,X,marked,vx), insert_cavity_vertex_helper(mesh,X,marked,xw); if (!in) marked[u] = marked[v] = marked[w] = marked[x] = true; }
static void insert_cavity_vertex(MutableTriangleTopology& mesh, RawField<const Perturbed2,VertexId> X, RawField<bool,VertexId> marked, const VertexId u, const VertexId v, const VertexId w) { #ifndef NDEBUG { const auto vw = mesh.halfedge(v); assert(mesh.is_boundary(vw) && mesh.dst(vw)==w); } #endif mesh.add_face(vec(u,v,w)); const auto vw = mesh.prev(mesh.reverse(mesh.halfedge(u))); assert(mesh.vertices(vw)==vec(v,w)); insert_cavity_vertex_helper(mesh,X,marked,vw); }
void decimate_inplace(MutableTriangleTopology& mesh, RawField<TV,VertexId> X, const T distance, const T max_angle, const int min_vertices, const T boundary_distance) { if (mesh.n_vertices() <= min_vertices) return; const T area = sqr(distance); const T sign_sqr_min_cos = sign_sqr(max_angle > .99*pi ? -1 : cos(max_angle)); // Finds the best edge to collapse v along. Returns (q(e),dst(e)). const auto best_collapse = [&mesh,X](const VertexId v) { Quadric q = compute_quadric(mesh,X,v); // Find the best edge, ignoring normal constraints T min_q = inf; HalfedgeId min_e; for (const auto e : mesh.outgoing(v)) { const T qx = q(X[mesh.dst(e)]); if (min_q > qx) { min_q = qx; min_e = e; } } return tuple(min_q,mesh.dst(min_e)); }; // Initialize quadrics and heap Heap heap(mesh.n_vertices_); for (const auto v : mesh.vertices()) { const auto qe = best_collapse(v); if (qe.x <= area) heap.inv_heap[v] = heap.heap.append(tuple(v,qe.x,qe.y)); } heap.make(); // Update the quadric information for a vertex const auto update = [&heap,best_collapse,area](const VertexId v) { const auto qe = best_collapse(v); if (qe.x <= area) heap.set(v,qe.x,qe.y); else heap.erase(v); }; // Repeatedly collapse the best vertex while (heap.size()) { const auto v = heap.pop(); // Do these vertices still exist? if (mesh.valid(v.x) && mesh.valid(v.y)) { const auto e = mesh.halfedge(v.x,v.y); // Is the collapse invalid? if (e.valid() && mesh.is_collapse_safe(e)) { const auto vs = mesh.src(e), vd = mesh.dst(e); const auto xs = X[vs], xd = X[vd]; // Are we moving a boundary vertex too far from its two boundary lines? { const auto b = mesh.halfedge(vs); if (mesh.is_boundary(b)) { const auto x0 = X[mesh.dst(b)], x1 = X[mesh.src(mesh.prev(b))]; if ( line_point_distance(simplex(xs,x0),xd) > boundary_distance || line_point_distance(simplex(xs,x1),xd) > boundary_distance) goto bad; } } // Do the normals change too much? if (sign_sqr_min_cos > -1) for (const auto ee : mesh.outgoing(vs)) if (e!=ee && !mesh.is_boundary(ee)) { const auto v2 = mesh.opposite(ee); if (v2 != vd) { const auto x1 = X[mesh.dst(ee)], x2 = X[v2]; const auto n0 = cross(x2-x1,xs-x1), n1 = cross(x2-x1,xd-x1); if (sign_sqr(dot(n0,n1)) < sign_sqr_min_cos*sqr_magnitude(n0)*sqr_magnitude(n1)) goto bad; } } // Collapse vs onto vd, then update the heap mesh.unsafe_collapse(e); if (mesh.n_vertices() <= min_vertices) break; update(vd); for (const auto e : mesh.outgoing(vd)) update(mesh.dst(e)); } } bad:; } }