/// <summary> /// if before a flip we have normals (n1,n2) and after we have (m1,m2), check if /// the dot between any of the 4 pairs changes sign after the flip, or is /// less than the dot-product tolerance (ie angle tolerance) /// </summary> public static bool CheckIfEdgeFlipCreatesFlip(DMesh3 mesh, int eID, double flip_dot_tol = 0.0) { Util.gDevAssert(mesh.IsBoundaryEdge(eID) == false); Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Vector3d vC = mesh.GetVertex(c), vD = mesh.GetVertex(d); Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); Vector3d vOA = mesh.GetVertex(oa), vOB = mesh.GetVertex(ob); Vector3d n0 = MathUtil.FastNormalDirection(ref vOA, ref vOB, ref vC); Vector3d n1 = MathUtil.FastNormalDirection(ref vOB, ref vOA, ref vD); Vector3d f0 = MathUtil.FastNormalDirection(ref vC, ref vD, ref vOB); if (edge_flip_metric(ref n0, ref f0, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f0, flip_dot_tol) <= flip_dot_tol) { return(true); } Vector3d f1 = MathUtil.FastNormalDirection(ref vD, ref vC, ref vOA); if (edge_flip_metric(ref n0, ref f1, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f1, flip_dot_tol) <= flip_dot_tol) { return(true); } return(false); }
/// <summary> /// For given edge, return it's triangles and the triangles that would /// be created if it was flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipTris(DMesh3 mesh, int eID, out Index3i orig_t0, out Index3i orig_t1, out Index3i flip_t0, out Index3i flip_t1) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); orig_t0 = new Index3i(oa, ob, c); orig_t1 = new Index3i(ob, oa, d); flip_t0 = new Index3i(c, d, ob); flip_t1 = new Index3i(d, c, oa); }
/// <summary> /// For given edge, return normals of it's two triangles, and normals /// of the triangles created if edge is flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipNormals(DMesh3 mesh, int eID, out Vector3d n1, out Vector3d n2, out Vector3d on1, out Vector3d on2) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Vector3d vC = mesh.GetVertex(c), vD = mesh.GetVertex(d); Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); Vector3d vOA = mesh.GetVertex(oa), vOB = mesh.GetVertex(ob); n1 = MathUtil.Normal(ref vOA, ref vOB, ref vC); n2 = MathUtil.Normal(ref vOB, ref vOA, ref vD); on1 = MathUtil.Normal(ref vC, ref vD, ref vOB); on2 = MathUtil.Normal(ref vD, ref vC, ref vOA); }
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) Index2i ov = mesh.GetEdgeOpposingV(edgeID); int c = ov.a, d = ov.b; Vector3d vA = mesh.GetVertex(a); Vector3d vB = mesh.GetVertex(b); double edge_len_sqr = vA.DistanceSquared(vB); 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.IsBoundaryVertex(a)); bool b_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.IsBoundaryVertex(b)); bool c_is_boundary_vtx = (MeshIsClosed) ? false : mesh.IsBoundaryVertex(c); bool d_is_boundary_vtx = (MeshIsClosed) ? false : mesh.IsBoundaryVertex(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, ref 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); } }