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); } }