/// <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);
        }
Beispiel #4
0
        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);
            }
        }