// from edge and vert, returns other vert, two opposing verts, and two triangles public void GetVtxNbrhood(int eID, int vID, ref int vOther, ref int oppV1, ref int oppV2, ref int t1, ref int t2) { int i = 4 * eID; vOther = (edges[i] == vID) ? edges[i + 1] : edges[i]; t1 = edges[i + 2]; oppV1 = IndexUtil.find_tri_other_vtx(vID, vOther, triangles, t1); t2 = edges[i + 3]; if (t2 != InvalidID) { oppV2 = IndexUtil.find_tri_other_vtx(vID, vOther, triangles, t2); } else { t2 = InvalidID; } }
// [RMS] this does more work than necessary, see public Index2i GetEdgeOpposingV(int eID) { int i = 4 * eID; int a = edges[i], b = edges[i + 1]; int t0 = edges[i + 2], t1 = edges[i + 3]; int c = IndexUtil.find_tri_other_vtx(a, b, triangles, t0); if (t1 != InvalidID) { int d = IndexUtil.find_tri_other_vtx(a, b, triangles, t1); return(new Index2i(c, d)); } else { return(new Index2i(c, InvalidID)); } }
protected virtual ProcessResult CollapseEdge(int edgeID, Vector3d vNewPos, out int collapseToV) { collapseToV = DMesh3.InvalidID; RuntimeDebugCheck(edgeID); EdgeConstraint constraint = (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID); if (constraint.NoModifications) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } if (constraint.CanCollapse == false) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } // look up verts and tris for this edge int a = 0, b = 0, t0 = 0, t1 = 0; if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false) { return(ProcessResult.Failed_NotAnEdge); } bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID); // look up 'other' verts c (from t0) and d (from t1, if it exists) Index3i T0tv = mesh.GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); Index3i T1tv = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1); int d = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv); Vector3d vA = mesh.GetVertex(a); Vector3d vB = mesh.GetVertex(b); double edge_len_sqr = (vA - vB).LengthSquared; if (edge_len_sqr > MinEdgeLength * MinEdgeLength) { return(ProcessResult.Ignored_EdgeTooLong); } begin_collapse(); // check if we should collapse, and also find which vertex we should collapse to, // in cases where we have constraints/etc int collapse_to = -1; bool bCanCollapse = can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to); if (bCanCollapse == false) { return(ProcessResult.Ignored_Constrained); } // if we have a boundary, we want to collapse to boundary if (PreserveBoundaryShape && HaveBoundary) { if (collapse_to != -1) { if ((IsBoundaryV(b) && collapse_to != b) || (IsBoundaryV(a) && collapse_to != a)) { return(ProcessResult.Ignored_Constrained); } } if (IsBoundaryV(b)) { collapse_to = b; } else if (IsBoundaryV(a)) { collapse_to = a; } } // optimization: if edge cd exists, we cannot collapse or flip. look that up here? // funcs will do it internally... // (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...) ProcessResult retVal = ProcessResult.Failed_OpNotSuccessful; int iKeep = b, iCollapse = a; // if either vtx is fixed, collapse to that position if (collapse_to == b) { vNewPos = vB; } else if (collapse_to == a) { iKeep = a; iCollapse = b; vNewPos = vA; } else { vNewPos = get_projected_collapse_position(iKeep, vNewPos); } // check if this collapse will create a normal flip. Also checks // for invalid collapse nbrhood, since we are doing one-ring iter anyway. // [TODO] could we skip this one-ring check in CollapseEdge? pass in hints? if (collapse_creates_flip_or_invalid(a, b, ref vNewPos, t0, t1) || collapse_creates_flip_or_invalid(b, a, ref vNewPos, t0, t1)) { retVal = ProcessResult.Ignored_CreatesFlip; goto skip_to_end; } // lots of cases where we cannot collapse, but we should just let // mesh sort that out, right? COUNT_COLLAPSES++; DMesh3.EdgeCollapseInfo collapseInfo; MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo); if (result == MeshResult.Ok) { collapseToV = iKeep; mesh.SetVertex(iKeep, vNewPos); if (constraints != null) { constraints.ClearEdgeConstraint(edgeID); constraints.ClearEdgeConstraint(collapseInfo.eRemoved0); if (collapseInfo.eRemoved1 != DMesh3.InvalidID) { constraints.ClearEdgeConstraint(collapseInfo.eRemoved1); } constraints.ClearVertexConstraint(iCollapse); } OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo); DoDebugChecks(); retVal = ProcessResult.Ok_Collapsed; } skip_to_end: end_collapse(); return(retVal); }
// Walk along edge loop and collapse to inserted curve vertices. EdgeLoop simplify(EdgeLoop loop) { HashSet <int> curve_verts = new HashSet <int>(CurveVertices); List <int> remaining_edges = new List <int>(); for (int li = 0; li < loop.EdgeCount; ++li) { int eid = loop.Edges[li]; Index2i ev = Mesh.GetEdgeV(eid); // cannot collapse edge between two "original" polygon verts (ie created by face pokes) if (curve_verts.Contains(ev.a) && curve_verts.Contains(ev.b)) { remaining_edges.Add(eid); continue; } // if we have an original vert, we need to keep it (and its position!) int keep = ev.a, discard = ev.b; Vector3d set_to = Vector3d.Zero; if (curve_verts.Contains(ev.b)) { keep = ev.b; discard = ev.a; set_to = Mesh.GetVertex(ev.b); } else if (curve_verts.Contains(ev.a)) { set_to = Mesh.GetVertex(ev.a); } else { set_to = 0.5 * (Mesh.GetVertex(ev.a) + Mesh.GetVertex(ev.b)); } // make sure we are not going to flip any normals // [OPTIMIZATION] May be possible to do this more efficiently because we know we are in // 2D and each tri should have same cw/ccw orientation. But we don't quite "know" we // are in 2D here, as CollapseEdge function is operating on the mesh coordinates... if (MeshUtil.CheckIfCollapseCreatesFlip(Mesh, eid, set_to)) { remaining_edges.Add(eid); continue; } // cannot collapse if the 'other' edges we would discard are OnCutEdges. This would // result in loop potentially being broken. bad! Index4i einfo = Mesh.GetEdge(eid); int c = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.c)); int d = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.d)); int ec = Mesh.FindEdge(discard, c); int ed = Mesh.FindEdge(discard, d); if (OnCutEdges.Contains(ec) || OnCutEdges.Contains(ed)) { remaining_edges.Add(eid); continue; } // do collapse and update internal data structures DMesh3.EdgeCollapseInfo collapse; MeshResult result = Mesh.CollapseEdge(keep, discard, out collapse); if (result == MeshResult.Ok) { Mesh.SetVertex(collapse.vKept, set_to); OnCutEdges.Remove(collapse.eCollapsed); } else { remaining_edges.Add(eid); } } return(EdgeLoop.FromEdges(Mesh, remaining_edges)); }
public MeshResult CollapseEdge(int vKeep, int vRemove, out EdgeCollapseInfo collapse) { collapse = new EdgeCollapseInfo(); if (IsVertex(vKeep) == false || IsVertex(vRemove) == false) { return(MeshResult.Failed_NotAnEdge); } int b = vKeep; // renaming for sanity. We remove a and keep b int a = vRemove; List <int> edges_b = vertex_edges[b]; int eab = find_edge(a, b); if (eab == InvalidID) { return(MeshResult.Failed_NotAnEdge); } int t0 = edges[4 * eab + 2]; if (t0 == InvalidID) { return(MeshResult.Failed_BrokenTopology); } Index3i T0tv = GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); // look up opposing triangle/vtx if we are not in boundary case bool bIsBoundaryEdge = false; int d = InvalidID; int t1 = edges[4 * eab + 3]; if (t1 != InvalidID) { Index3i T1tv = GetTriangle(t1); d = IndexUtil.find_tri_other_vtx(a, b, T1tv); if (c == d) { return(MeshResult.Failed_FoundDuplicateTriangle); } } else { bIsBoundaryEdge = true; } // We cannot collapse if edge lists of a and b share vertices other // than c and d (because then we will make a triangle [x b b]. // Unfortunately I cannot see a way to do this more efficiently than brute-force search // [TODO] if we had tri iterator for a, couldn't we check each tri for b (skipping t0 and t1) ? List <int> edges_a = vertex_edges[a]; int edges_a_count = 0; foreach (int eid_a in edges_a) { int vax = edge_other_v(eid_a, a); edges_a_count++; if (vax == b || vax == c || vax == d) { continue; } foreach (int eid_b in edges_b) { if (edge_other_v(eid_b, b) == vax) { return(MeshResult.Failed_InvalidNeighbourhood); } } } // We cannot collapse if we have a tetrahedron. In this case a has 3 nbr edges, // and edge cd exists. But that is not conclusive, we also have to check that // cd is an internal edge, and that each of its tris contain a or b if (edges_a_count == 3 && bIsBoundaryEdge == false) { int edc = find_edge(d, c); int edc_i = 4 * edc; if (edc != InvalidID && edges[edc_i + 3] != InvalidID) { int edc_t0 = edges[edc_i + 2]; int edc_t1 = edges[edc_i + 3]; if ((tri_has_v(edc_t0, a) && tri_has_v(edc_t1, b)) || (tri_has_v(edc_t0, b) && tri_has_v(edc_t1, a))) { return(MeshResult.Failed_CollapseTetrahedron); } } } else if (edges_a_count == 2 && bIsBoundaryEdge == true) { // cannot collapse edge if we are down to a single triangle if (edges_b.Count == 2 && vertex_edges[c].Count == 2) { return(MeshResult.Failed_CollapseTriangle); } } // [RMS] this was added from C++ version...seems like maybe I never hit // this case? Conceivably could be porting bug but looking at the // C++ code I cannot see how we could possibly have caught this case... // // cannot collapse an edge where both vertices are boundary vertices // because that would create a bowtie // // NOTE: potentially scanning all edges here...couldn't we // pick up eac/bc/ad/bd as we go? somehow? if (bIsBoundaryEdge == false && vertex_is_boundary(a) && vertex_is_boundary(b)) { return(MeshResult.Failed_InvalidNeighbourhood); } // 1) remove edge ab from vtx b // 2) find edges ad and ac, and tris tad, tac across those edges (will use later) // 3) for other edges, replace a with b, and add that edge to b // 4) replace a with b in all triangles connected to a int ead = InvalidID, eac = InvalidID; int tad = InvalidID, tac = InvalidID; foreach (int eid in edges_a) { int o = edge_other_v(eid, a); if (o == b) { if (edges_b.Remove(eid) != true) { debug_fail("remove case o == b"); } } else if (o == c) { eac = eid; if (vertex_edges[c].Remove(eid) != true) { debug_fail("remove case o == c"); } tac = edge_other_t(eid, t0); } else if (o == d) { ead = eid; if (vertex_edges[d].Remove(eid) != true) { debug_fail("remove case o == c, step 1"); } tad = edge_other_t(eid, t1); } else { if (replace_edge_vertex(eid, a, b) == -1) { debug_fail("remove case else"); } edges_b.Add(eid); } // [TODO] perhaps we can already have unique tri list because of the manifold-nbrhood check we need to do... for (int j = 0; j < 2; ++j) { int t_j = edges[4 * eid + 2 + j]; if (t_j != InvalidID && t_j != t0 && t_j != t1) { if (tri_has_v(t_j, a)) { if (replace_tri_vertex(t_j, a, b) == -1) { debug_fail("remove last check"); } vertices_refcount.increment(b); vertices_refcount.decrement(a); } } } } int ebc = InvalidID, ebd = InvalidID; if (bIsBoundaryEdge == false) { // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.refCount(a) == 3); // in t0,t1, and initial ref vertices_refcount.decrement(a, 3); Debug.Assert(vertices_refcount.isValid(a) == false); // remove triangles T0 and T1, and update b/c/d refcounts triangles_refcount.decrement(t0); triangles_refcount.decrement(t1); vertices_refcount.decrement(c); vertices_refcount.decrement(d); vertices_refcount.decrement(b, 2); Debug.Assert(triangles_refcount.isValid(t0) == false); Debug.Assert(triangles_refcount.isValid(t1) == false); // remove edges ead, eab, eac edges_refcount.decrement(ead); edges_refcount.decrement(eab); edges_refcount.decrement(eac); Debug.Assert(edges_refcount.isValid(ead) == false); Debug.Assert(edges_refcount.isValid(eab) == false); Debug.Assert(edges_refcount.isValid(eac) == false); // replace t0 and t1 in edges ebd and ebc that we kept ebd = find_edge(b, d); ebc = find_edge(b, c); if (replace_edge_triangle(ebd, t1, tad) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } if (replace_edge_triangle(ebc, t0, tac) == -1) { debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tad and tac if (tad != InvalidID) { if (replace_triangle_edge(tad, ead, ebd) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } } if (tac != InvalidID) { if (replace_triangle_edge(tac, eac, ebc) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } } } else { // this is basically same code as above, just not referencing t1/d // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.refCount(a) == 2); // in t0 and initial ref vertices_refcount.decrement(a, 2); Debug.Assert(vertices_refcount.isValid(a) == false); // remove triangle T0 and update b/c refcounts triangles_refcount.decrement(t0); vertices_refcount.decrement(c); vertices_refcount.decrement(b); Debug.Assert(triangles_refcount.isValid(t0) == false); // remove edges eab and eac edges_refcount.decrement(eab); edges_refcount.decrement(eac); Debug.Assert(edges_refcount.isValid(eab) == false); Debug.Assert(edges_refcount.isValid(eac) == false); // replace t0 in edge ebc that we kept ebc = find_edge(b, c); if (replace_edge_triangle(ebc, t0, tac) == -1) { debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tac if (tac != InvalidID) { if (replace_triangle_edge(tac, eac, ebc) == -1) { debug_fail("isboundary=true branch, ebd replace triangle"); } } } collapse.vKept = vKeep; collapse.vRemoved = vRemove; collapse.bIsBoundary = bIsBoundaryEdge; collapse.eCollapsed = eab; collapse.tRemoved0 = t0; collapse.tRemoved1 = t1; collapse.eRemoved0 = eac; collapse.eRemoved1 = ead; collapse.eKept0 = ebc; collapse.eKept1 = ebd; updateTimeStamp(true); return(MeshResult.Ok); }
public MeshResult FlipEdge(int eab, out EdgeFlipInfo flip) { flip = new EdgeFlipInfo(); if (!IsEdge(eab)) { return(MeshResult.Failed_NotAnEdge); } if (edge_is_boundary(eab)) { return(MeshResult.Failed_IsBoundaryEdge); } // find oriented edge [a,b], tris t0,t1, and other verts c in t0, d in t1 int eab_i = 4 * eab; int a = edges[eab_i], b = edges[eab_i + 1]; int t0 = edges[eab_i + 2], t1 = edges[eab_i + 3]; int[] T0tv = GetTriangle(t0).array; int[] T1tv = GetTriangle(t1).array; int c = IndexUtil.orient_tri_edge_and_find_other_vtx(ref a, ref b, T0tv); int d = IndexUtil.find_tri_other_vtx(a, b, T1tv); if (c == InvalidID || d == InvalidID) { return(MeshResult.Failed_BrokenTopology); } int flipped = find_edge(c, d); if (flipped != InvalidID) { return(MeshResult.Failed_FlippedEdgeExists); } // find edges bc, ca, ad, db int ebc = find_tri_neighbour_edge(t0, b, c); int eca = find_tri_neighbour_edge(t0, c, a); int ead = find_tri_neighbour_edge(t1, a, d); int edb = find_tri_neighbour_edge(t1, d, b); // update triangles set_triangle(t0, c, d, b); set_triangle(t1, d, c, a); // update edge AB, which becomes flipped edge CD set_edge_vertices(eab, c, d); set_edge_triangles(eab, t0, t1); int ecd = eab; // update the two other edges whose triangle nbrs have changed if (replace_edge_triangle(eca, t0, t1) == -1) { throw new ArgumentException("DMesh3.FlipEdge: first replace_edge_triangle failed"); } if (replace_edge_triangle(edb, t1, t0) == -1) { throw new ArgumentException("DMesh3.FlipEdge: second replace_edge_triangle failed"); } // update triangle nbr lists (these are edges) set_triangle_edges(t0, ecd, edb, ebc); set_triangle_edges(t1, ecd, eca, ead); // remove old eab from verts a and b, and decrement ref counts if (vertex_edges[a].Remove(eab) == false) { throw new ArgumentException("DMesh3.FlipEdge: first vertex_edges remove failed"); } if (vertex_edges[b].Remove(eab) == false) { throw new ArgumentException("DMesh3.FlipEdge: second vertex_edges remove failed"); } vertices_refcount.decrement(a); vertices_refcount.decrement(b); if (IsVertex(a) == false || IsVertex(b) == false) { throw new ArgumentException("DMesh3.FlipEdge: either a or b is not a vertex?"); } // add new edge ecd to verts c and d, and increment ref counts vertex_edges[c].Add(ecd); vertex_edges[d].Add(ecd); vertices_refcount.increment(c); vertices_refcount.increment(d); // success! collect up results flip.eID = eab; flip.v0 = a; flip.v1 = b; flip.ov0 = c; flip.ov1 = d; flip.t0 = t0; flip.t1 = t1; updateTimeStamp(true); return(MeshResult.Ok); }
public MeshResult SplitEdge(int eab, out EdgeSplitInfo split) { split = new EdgeSplitInfo(); if (!IsEdge(eab)) { return(MeshResult.Failed_NotAnEdge); } // look up primary edge & triangle int eab_i = 4 * eab; int a = edges[eab_i], b = edges[eab_i + 1]; int t0 = edges[eab_i + 2]; if (t0 == InvalidID) { return(MeshResult.Failed_BrokenTopology); } Index3i T0tv = GetTriangle(t0); int[] T0tv_array = T0tv.array; int c = IndexUtil.orient_tri_edge_and_find_other_vtx(ref a, ref b, T0tv_array); // create new vertex Vector3d vNew = 0.5 * (GetVertex(a) + GetVertex(b)); int f = AppendVertex(vNew); // quite a bit of code is duplicated between boundary and non-boundary case, but it // is too hard to follow later if we factor it out... if (edge_is_boundary(eab)) { // look up edge bc, which needs to be modified Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; // rewrite existing triangle replace_tri_vertex(t0, b, f); // add new second triangle int t2 = add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.insert(triangle_groups[t0], t2); } // rewrite edge bc, create edge af replace_edge_triangle(ebc, t0, t2); int eaf = eab; replace_edge_vertex(eaf, b, f); vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges fb and fc int efb = add_edge(f, b, t2); int efc = add_edge(f, c, t0, t2); // update triangle edge-nbrs replace_triangle_edge(t0, ebc, efc); set_triangle_edges(t2, efb, ebc, efc); // update vertex refcounts vertices_refcount.increment(c); vertices_refcount.increment(f, 2); split.bIsBoundary = true; split.vNew = f; split.eNew = efb; updateTimeStamp(true); return(MeshResult.Ok); } else // interior triangle branch // look up other triangle { int t1 = edges[eab_i + 3]; Index3i T1tv = GetTriangle(t1); int[] T1tv_array = T1tv.array; int d = IndexUtil.find_tri_other_vtx(a, b, T1tv_array); // look up edges that we are going to need to update // [TODO OPT] could use ordering to reduce # of compares here Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; Index3i T1te = GetTriEdges(t1); int edb = T1te[IndexUtil.find_edge_index_in_tri(d, b, T1tv_array)]; // rewrite existing triangles replace_tri_vertex(t0, b, f); replace_tri_vertex(t1, b, f); // add two new triangles to close holes we just created int t2 = add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); int t3 = add_triangle_only(f, d, b, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.insert(triangle_groups[t0], t2); triangle_groups.insert(triangle_groups[t1], t3); } // update the edges we found above, to point to new triangles replace_edge_triangle(ebc, t0, t2); replace_edge_triangle(edb, t1, t3); // edge eab became eaf int eaf = eab; //Edge * eAF = eAB; replace_edge_vertex(eaf, b, f); // update a/b/f vertex-edges vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges connected to f (also updates vertex-edges) int efb = add_edge(f, b, t2, t3); int efc = add_edge(f, c, t0, t2); int edf = add_edge(d, f, t1, t3); // update triangle edge-nbrs replace_triangle_edge(t0, ebc, efc); replace_triangle_edge(t1, edb, edf); set_triangle_edges(t2, efb, ebc, efc); set_triangle_edges(t3, edf, edb, efb); // update vertex refcounts vertices_refcount.increment(c); vertices_refcount.increment(d); vertices_refcount.increment(f, 4); split.bIsBoundary = false; split.vNew = f; split.eNew = efb; updateTimeStamp(true); return(MeshResult.Ok); } }
protected virtual ProcessResult ProcessEdge(int edgeID) { RuntimeDebugCheck(edgeID); EdgeConstraint constraint = (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID); if (constraint.NoModifications) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } // look up verts and tris for this edge int a = 0, b = 0, t0 = 0, t1 = 0; if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false) { return(ProcessResult.Failed_NotAnEdge); } bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID); // look up 'other' verts c (from t0) and d (from t1, if it exists) Index3i T0tv = mesh.GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); Index3i T1tv = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1); int d = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv); Vector3d vA = mesh.GetVertex(a); Vector3d vB = mesh.GetVertex(b); double edge_len_sqr = (vA - vB).LengthSquared; begin_collapse(); // check if we should collapse, and also find which vertex we should collapse to, // in cases where we have constraints/etc int collapse_to = -1; bool bCanCollapse = EnableCollapses && constraint.CanCollapse && edge_len_sqr < MinEdgeLength * MinEdgeLength && can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to); // optimization: if edge cd exists, we cannot collapse or flip. look that up here? // funcs will do it internally... // (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...) // if edge length is too short, we want to collapse it bool bTriedCollapse = false; if (bCanCollapse) { int iKeep = b, iCollapse = a; Vector3d vNewPos = (vA + vB) * 0.5; // if either vtx is fixed, collapse to that position if (collapse_to == b) { vNewPos = vB; } else if (collapse_to == a) { iKeep = a; iCollapse = b; vNewPos = vA; } else { vNewPos = get_projected_collapse_position(iKeep, vNewPos); } // TODO be smart about picking b (keep vtx). // - swap if one is bdry vtx, for example? // lots of cases where we cannot collapse, but we should just let // mesh sort that out, right? COUNT_COLLAPSES++; DMesh3.EdgeCollapseInfo collapseInfo; MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo); if (result == MeshResult.Ok) { mesh.SetVertex(iKeep, vNewPos); if (constraints != null) { constraints.ClearEdgeConstraint(edgeID); constraints.ClearEdgeConstraint(collapseInfo.eRemoved0); if (collapseInfo.eRemoved1 != DMesh3.InvalidID) { constraints.ClearEdgeConstraint(collapseInfo.eRemoved1); } constraints.ClearVertexConstraint(iCollapse); } OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo); DoDebugChecks(); return(ProcessResult.Ok_Collapsed); } else { bTriedCollapse = true; } } end_collapse(); begin_flip(); // if this is not a boundary edge, maybe we want to flip bool bTriedFlip = false; if (EnableFlips && constraint.CanFlip && bIsBoundaryEdge == false) { // don't want to flip if it will invert triangle...tetrahedron sign?? // can we do this more efficiently somehow? bool a_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(a)); bool b_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(b)); bool c_is_boundary_vtx = (MeshIsClosed) ? false : mesh.vertex_is_boundary(c); bool d_is_boundary_vtx = (MeshIsClosed) ? false : mesh.vertex_is_boundary(d); int valence_a = mesh.GetVtxEdgeCount(a), valence_b = mesh.GetVtxEdgeCount(b); int valence_c = mesh.GetVtxEdgeCount(c), valence_d = mesh.GetVtxEdgeCount(d); int valence_a_target = (a_is_boundary_vtx) ? valence_a : 6; int valence_b_target = (b_is_boundary_vtx) ? valence_b : 6; int valence_c_target = (c_is_boundary_vtx) ? valence_c : 6; int valence_d_target = (d_is_boundary_vtx) ? valence_d : 6; // if total valence error improves by flip, we want to do it int curr_err = Math.Abs(valence_a - valence_a_target) + Math.Abs(valence_b - valence_b_target) + Math.Abs(valence_c - valence_c_target) + Math.Abs(valence_d - valence_d_target); int flip_err = Math.Abs((valence_a - 1) - valence_a_target) + Math.Abs((valence_b - 1) - valence_b_target) + Math.Abs((valence_c + 1) - valence_c_target) + Math.Abs((valence_d + 1) - valence_d_target); if (flip_err < curr_err) { // try flip DMesh3.EdgeFlipInfo flipInfo; COUNT_FLIPS++; MeshResult result = mesh.FlipEdge(edgeID, out flipInfo); if (result == MeshResult.Ok) { DoDebugChecks(); return(ProcessResult.Ok_Flipped); } else { bTriedFlip = true; } } } end_flip(); begin_split(); // if edge length is too long, we want to split it bool bTriedSplit = false; if (EnableSplits && constraint.CanSplit && edge_len_sqr > MaxEdgeLength * MaxEdgeLength) { DMesh3.EdgeSplitInfo splitInfo; COUNT_SPLITS++; MeshResult result = mesh.SplitEdge(edgeID, out splitInfo); if (result == MeshResult.Ok) { update_after_split(edgeID, a, b, splitInfo); OnEdgeSplit(edgeID, a, b, splitInfo); DoDebugChecks(); return(ProcessResult.Ok_Split); } else { bTriedSplit = true; } } end_split(); if (bTriedFlip || bTriedSplit || bTriedCollapse) { return(ProcessResult.Failed_OpNotSuccessful); } else { return(ProcessResult.Ignored_EdgeIsFine); } }