/// <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);
        }
Beispiel #2
0
        /// <summary>
        /// Check if edge flip might reverse normal direction.
        ///
        /// Not entirely clear on how to implement this test.
        /// Currently checking if any normal-pairs are reversed.
        /// </summary>
        protected bool flip_inverts_normals(int a, int b, int c, int d, int t0)
        {
            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 (n0.Dot(f0) < 0 || n1.Dot(f0) < 0)
            {
                return(true);
            }
            Vector3d f1 = MathUtil.FastNormalDirection(ref vD, ref vC, ref vOA);

            if (n0.Dot(f1) < 0 || n1.Dot(f1) < 0)
            {
                return(true);
            }

            // this only checks if output faces are pointing towards eachother, which seems
            // to still result in normal-flips in some cases
            //if (f0.Dot(f1) < 0)
            //    return true;

            return(false);
        }
        /// <summary>
        /// construct EdgeSpan from a list of edges of mesh
        /// </summary>
        public static EdgeSpan FromEdges(DMesh3 mesh, IList <int> edges)
        {
            int[] Edges = new int[edges.Count];
            for (int i = 0; i < Edges.Length; ++i)
            {
                Edges[i] = edges[i];
            }
            int[]   Vertices = new int[Edges.Length + 1];
            Index2i start_ev = mesh.GetEdgeV(Edges[0]);
            Index2i prev_ev  = start_ev;

            if (Edges.Length > 1)
            {
                for (int i = 1; i < Edges.Length; ++i)
                {
                    Index2i next_ev = mesh.GetEdgeV(Edges[i]);
                    Vertices[i] = IndexUtil.find_shared_edge_v(ref prev_ev, ref next_ev);
                    prev_ev     = next_ev;
                }
                Vertices[0] = IndexUtil.find_edge_other_v(ref start_ev, Vertices[1]);
                Vertices[Vertices.Length - 1] = IndexUtil.find_edge_other_v(prev_ev, Vertices[Vertices.Length - 2]);
            }
            else
            {
                Vertices[0] = start_ev[0]; Vertices[1] = start_ev[1];
            }
            return(new EdgeSpan(mesh, Vertices, Edges, false));
        }
        // return same indices as GetEdgeV, but oriented based on attached triangle
        Index2i get_oriented_edgev(int eID, int tid_in, int tid_out)
        {
            Index2i edgev = Mesh.GetEdgeV(eID);
            int     a = edgev.a, b = edgev.b;
            Index3i tri = Mesh.GetTriangle(tid_in);
            int     ai  = IndexUtil.find_edge_index_in_tri(a, b, ref tri);

            return(new Index2i(tri[ai], tri[(ai + 1) % 3]));
        }
Beispiel #5
0
        /// <summary>
        /// Remove vertex vID, and all connected triangles if bRemoveAllTriangles = true
        /// (if false, them throws exception if there are still any triangles!)
        /// if bPreserveManifold, checks that we will not create a bowtie vertex first
        /// </summary>
        public MeshResult RemoveVertex(int vID, bool bRemoveAllTriangles = true, bool bPreserveManifold = true)
        {
            if (vertices_refcount.isValid(vID) == false)
            {
                return(MeshResult.Failed_NotAVertex);
            }

            if (bRemoveAllTriangles)
            {
                // if any one-ring vtx is a boundary vtx and one of its outer-ring edges is an
                // interior edge then we will create a bowtie if we remove that triangle
                if (bPreserveManifold)
                {
                    foreach (int tid in VtxTrianglesItr(vID))
                    {
                        Index3i tri = GetTriangle(tid);
                        int     j = IndexUtil.find_tri_index(vID, tri);
                        int     oa = tri[(j + 1) % 3], ob = tri[(j + 2) % 3];
                        int     eid = find_edge(oa, ob);
                        if (edge_is_boundary(eid))
                        {
                            continue;
                        }
                        if (vertex_is_boundary(oa) || vertex_is_boundary(ob))
                        {
                            return(MeshResult.Failed_WouldCreateBowtie);
                        }
                    }
                }

                List <int> tris = new List <int>();
                GetVtxTriangles(vID, tris, true);
                foreach (int tID in tris)
                {
                    MeshResult result = RemoveTriangle(tID, false, bPreserveManifold);
                    if (result != MeshResult.Ok)
                    {
                        return(result);
                    }
                }
            }

            if (vertices_refcount.refCount(vID) != 1)
            {
                throw new NotImplementedException("DMesh3.RemoveVertex: vertex is still referenced");
            }

            vertices_refcount.decrement(vID);
            Debug.Assert(vertices_refcount.isValid(vID) == false);
            vertex_edges[vID] = null;

            updateTimeStamp(true);
            return(MeshResult.Ok);
        }
        /// <summary>
        /// computes sum of opening-angles in triangles around vid, minus 2pi.
        /// This is zero on flat areas.
        /// </summary>
        public static double DiscreteGaussCurvature(DMesh3 mesh, int vid)
        {
            double angle_sum = 0;

            foreach (int tid in mesh.VtxTrianglesItr(vid))
            {
                Index3i et  = mesh.GetTriangle(tid);
                int     idx = IndexUtil.find_tri_index(vid, ref et);
                angle_sum += mesh.GetTriInternalAngleR(tid, idx);
            }
            return(angle_sum - MathUtil.TwoPI);
        }
Beispiel #7
0
        void find_cut_paths(HashSet <int> CutEdges)
        {
            Spans = new List <EdgeSpan>();
            Loops = new List <EdgeLoop>();

            // [TODO] what about if vert appears more than twice in list? we should check for that!

            var Remaining = new HashSet <int>(CutEdges);

            while (Remaining.Count > 0)
            {
                int start_edge = Remaining.First();
                Remaining.Remove(start_edge);
                Index2i start_edge_v = Mesh.GetEdgeV(start_edge);

                bool       isLoop;
                List <int> forwardSpan = walk_edge_span_forward(Mesh, start_edge, start_edge_v.a, Remaining, out isLoop);
                if (isLoop == false)
                {
                    List <int> backwardSpan = walk_edge_span_forward(Mesh, start_edge, start_edge_v.b, Remaining, out isLoop);
                    if (isLoop)
                    {
                        throw new Exception("find_cut_paths: how did this possibly happen?!?");
                    }

                    if (backwardSpan.Count > 1)
                    {
                        backwardSpan.Reverse();
                        backwardSpan.RemoveAt(backwardSpan.Count - 1);
                        backwardSpan.AddRange(forwardSpan);
                        Index2i start_ev = Mesh.GetEdgeV(backwardSpan[0]);
                        Index2i end_ev   = Mesh.GetEdgeV(backwardSpan[backwardSpan.Count - 1]);
                        // [RMS] >2 check here catches two-edge span case, where we do have shared vert but
                        //   can never be loop unless we have duplicate edge (!)
                        isLoop      = backwardSpan.Count > 2 && IndexUtil.find_shared_edge_v(ref start_ev, ref end_ev) != DMesh3.InvalidID;
                        forwardSpan = backwardSpan;
                    }
                }

                if (isLoop)
                {
                    var loop = EdgeLoop.FromEdges(Mesh, forwardSpan);
                    Util.gDevAssert(loop.CheckValidity());
                    Loops.Add(loop);
                }
                else
                {
                    var span = EdgeSpan.FromEdges(Mesh, forwardSpan);
                    Util.gDevAssert(span.CheckValidity());
                    Spans.Add(span);
                }
            }
        }
Beispiel #8
0
        // 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;
            }
        }
Beispiel #9
0
        // [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));
            }
        }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        int find_edge_from_tri(int vA, int vB, int tID)
        {
            int i = 3 * tID;

            if (IndexUtil.same_pair_unordered(vA, vB, triangles[i], triangles[i + 1]))
            {
                return(triangle_edges[i]);
            }
            if (IndexUtil.same_pair_unordered(vA, vB, triangles[i + 1], triangles[i + 2]))
            {
                return(triangle_edges[i + 1]);
            }
            if (IndexUtil.same_pair_unordered(vA, vB, triangles[i + 2], triangles[i]))
            {
                return(triangle_edges[i + 2]);
            }
            return(InvalidID);
        }
        public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> remove_t)
        {
            initialize_buffers(mesh);
            bool has_groups = mesh.HasTriangleGroups;

            HashSet <int> triangles = new HashSet <int>(remove_t);
            HashSet <int> vertices  = new HashSet <int>();

            IndexUtil.TrianglesToVertices(mesh, remove_t, vertices);
            List <int> save_v = new List <int>();

            foreach (int vid in vertices)
            {
                bool all_contained = true;
                foreach (int tid in mesh.VtxTrianglesItr(vid))
                {
                    if (triangles.Contains(tid) == false)
                    {
                        all_contained = false;
                        break;
                    }
                }
                if (all_contained)
                {
                    save_v.Add(vid);
                }
            }

            foreach (int vid in save_v)
            {
                save_vertex(mesh, vid, true);
            }

            foreach (int tid in remove_t)
            {
                Util.gDevAssert(mesh.IsTriangle(tid));
                Index3i tv  = mesh.GetTriangle(tid);
                Index4i tri = new Index4i(tv.a, tv.b, tv.c,
                                          has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID);
                RemovedT.Add(tid);
                Triangles.Add(tri);
            }
        }
Beispiel #13
0
        /// <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 #14
0
        // returns 0/1/2
        public int find_tri_neighbour_index(int tID, int vA, int vB)
        {
            int i = 3 * tID;
            int tv0 = triangles[i], tv1 = triangles[i + 1];

            if (IndexUtil.same_pair_unordered(tv0, tv1, vA, vB))
            {
                return(0);
            }
            int tv2 = triangles[i + 2];

            if (IndexUtil.same_pair_unordered(tv1, tv2, vA, vB))
            {
                return(1);
            }
            if (IndexUtil.same_pair_unordered(tv2, tv0, vA, vB))
            {
                return(2);
            }
            return(InvalidID);
        }
Beispiel #15
0
        /// <summary>
        /// construct EdgeLoop from a list of edges of mesh
        /// </summary>
        public static EdgeLoop FromEdges(DMesh3 mesh, IList <int> edges)
        {
            int[] Edges = new int[edges.Count];
            for (int i = 0; i < Edges.Length; ++i)
            {
                Edges[i] = edges[i];
            }

            int[]   Vertices = new int[Edges.Length];
            Index2i start_ev = mesh.GetEdgeV(Edges[0]);
            Index2i prev_ev  = start_ev;

            for (int i = 1; i < Edges.Length; ++i)
            {
                Index2i next_ev = mesh.GetEdgeV(Edges[i % Edges.Length]);
                Vertices[i] = IndexUtil.find_shared_edge_v(ref prev_ev, ref next_ev);
                prev_ev     = next_ev;
            }
            Vertices[0] = IndexUtil.find_edge_other_v(ref start_ev, Vertices[1]);
            return(new EdgeLoop(mesh, Vertices, Edges, false));
        }
Beispiel #16
0
        // Remove the original submesh region and merge in the remeshed version.
        // You can call this multiple times as the base-triangle-set is updated.
        //
        // By default, we allow the submesh to be modified to prevent creation of
        // non-manifold edges. You can disable this, however then some of the submesh
        // triangles may be discarded.
        //
        // Returns false if there were errors in insertion, ie if some triangles
        // failed to insert. Does not revert changes that were successful.
        public bool BackPropropagate(bool bAllowSubmeshRepairs = true)
        {
            if (bAllowSubmeshRepairs)
            {
                RepairPossibleNonManifoldEdges();
            }

            // remove existing submesh triangles
            var editor = new MeshEditor(BaseMesh);

            editor.RemoveTriangles(cur_base_tris, true);

            // insert new submesh
            int[] new_tris = new int[Region.SubMesh.TriangleCount];
            ReinsertSubToBaseMapV = null;
            bool bOK = editor.ReinsertSubmesh(Region, ref new_tris, out ReinsertSubToBaseMapV, ReinsertDuplicateTriBehavior);

            // reconstruct this...hacky?
            int NT = Region.SubMesh.MaxTriangleID;

            ReinsertSubToBaseMapT = new IndexMap(false, NT);
            int nti = 0;

            for (int ti = 0; ti < NT; ++ti)
            {
                if (Region.SubMesh.IsTriangle(ti) == false)
                {
                    continue;
                }

                ReinsertSubToBaseMapT[ti] = new_tris[nti++];
            }

            // assert that new triangles are all valid (goes wrong sometimes??)
            Debug.Assert(IndexUtil.IndicesCheck(new_tris, BaseMesh.IsTriangle));

            cur_base_tris = new_tris;
            return(bOK);
        }
        // Remove the original submesh region and merge in the remeshed version.
        // You can call this multiple times as the base-triangle-set is updated.
        //
        // By default, we allow the submesh to be modified to prevent creation of
        // non-manifold edges. You can disable this, however then some of the submesh
        // triangles may be discarded.
        //
        // Returns false if there were errors in insertion, ie if some triangles
        // failed to insert. Does not revert changes that were successful.
        public bool BackPropropagate(bool bAllowSubmeshRepairs = true)
        {
            if (bAllowSubmeshRepairs)
            {
                RepairPossibleNonManifoldEdges();
            }

            // remove existing submesh triangles
            MeshEditor editor = new MeshEditor(BaseMesh);

            editor.RemoveTriangles(cur_base_tris, true);

            // insert new submesh
            int[] new_tris = new int[Region.SubMesh.TriangleCount];
            ReinsertSubToBaseMapV = null;
            bool bOK = editor.ReinsertSubmesh(Region, ref new_tris, out ReinsertSubToBaseMapV, ReinsertDuplicateTriBehavior);

            // assert that new triangles are all valid (goes wrong sometimes??)
            Debug.Assert(IndexUtil.IndicesCheck(new_tris, BaseMesh.IsTriangle));

            cur_base_tris = new_tris;
            return(bOK);
        }
Beispiel #18
0
        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);
        }
        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);
        }
        static List <int> walk_edge_span_forward(DMesh3 mesh, int start_edge, int start_pivot_v, HashSet <int> EdgeSet, out bool bClosedLoop)
        {
            bClosedLoop = false;

            List <int> edgeSpan = new List <int>();

            edgeSpan.Add(start_edge);

            // we update this as we step
            //int cur_edge = start_edge;
            int cur_pivot_v  = start_pivot_v;
            int stop_pivot_v = IndexUtil.find_edge_other_v(mesh.GetEdgeV(start_edge), start_pivot_v);

            Util.gDevAssert(stop_pivot_v != DMesh3.InvalidID);

            bool done = false;

            while (!done)
            {
                // find outgoing edge in set and connected to current pivot vtx
                int next_edge = -1;
                foreach (int nbr_edge in mesh.VtxEdgesItr(cur_pivot_v))
                {
                    if (EdgeSet.Contains(nbr_edge))
                    {
                        next_edge = nbr_edge;
                        break;
                    }
                }

                // could not find - must be done span
                if (next_edge == -1)
                {
                    done = true;
                    break;
                }

                // figure out next pivot vtx (is 'other' from current pivot on next edge)
                Index2i next_edge_v = mesh.GetEdgeV(next_edge);
                if (next_edge_v.a == cur_pivot_v)
                {
                    cur_pivot_v = next_edge_v.b;
                }
                else if (next_edge_v.b == cur_pivot_v)
                {
                    cur_pivot_v = next_edge_v.a;
                }
                else
                {
                    throw new Exception("walk_edge_span_forward: found valid next edge but not connected to previous vertex??");
                }

                edgeSpan.Add(next_edge);
                EdgeSet.Remove(next_edge);

                // if this happens, we closed a loop
                if (cur_pivot_v == stop_pivot_v)
                {
                    done        = true;
                    bClosedLoop = true;
                }
            }

            return(edgeSpan);
        }
        // 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 virtual bool Apply()
        {
            HashSet <int> OnCurveVerts = new HashSet <int>();     // original vertices that were epsilon-coincident w/ curve vertices

            insert_corners(OnCurveVerts);

            // [RMS] not using this?
            //HashSet<int> corner_v = new HashSet<int>(CurveVertices);

            // not sure we need to track all of these
            HashSet <int> ZeroEdges    = new HashSet <int>();
            HashSet <int> ZeroVertices = new HashSet <int>();

            OnCutEdges = new HashSet <int>();

            HashSet <int> NewEdges       = new HashSet <int>();
            HashSet <int> NewCutVertices = new HashSet <int>();

            sbyte[] signs = new sbyte[2 * Mesh.MaxVertexID + 2 * Curve.VertexCount];

            HashSet <int> segTriangles = new HashSet <int>();
            HashSet <int> segVertices  = new HashSet <int>();
            HashSet <int> segEdges     = new HashSet <int>();

            // loop over segments, insert each one in sequence
            int N = (IsLoop) ? Curve.VertexCount : Curve.VertexCount - 1;

            for (int si = 0; si < N; ++si)
            {
                int       i0  = si;
                int       i1  = (si + 1) % Curve.VertexCount;
                Segment2d seg = new Segment2d(Curve[i0], Curve[i1]);

                int i0_vid = CurveVertices[i0];
                int i1_vid = CurveVertices[i1];

                // If these vertices are already connected by an edge, we can just continue.
                int existing_edge = Mesh.FindEdge(i0_vid, i1_vid);
                if (existing_edge != DMesh3.InvalidID)
                {
                    add_cut_edge(existing_edge);
                    continue;
                }

                if (triSpatial != null)
                {
                    segTriangles.Clear(); segVertices.Clear(); segEdges.Clear();
                    AxisAlignedBox2d segBounds = new AxisAlignedBox2d(seg.P0); segBounds.Contain(seg.P1);
                    segBounds.Expand(MathUtil.ZeroTolerancef * 10);
                    triSpatial.FindTrianglesInRange(segBounds, segTriangles);
                    IndexUtil.TrianglesToVertices(Mesh, segTriangles, segVertices);
                    IndexUtil.TrianglesToEdges(Mesh, segTriangles, segEdges);
                }

                int MaxVID = Mesh.MaxVertexID;
                IEnumerable <int> vertices = Interval1i.Range(MaxVID);
                if (triSpatial != null)
                {
                    vertices = segVertices;
                }

                // compute edge-crossing signs
                // [TODO] could walk along mesh from a to b, rather than computing for entire mesh?
                if (signs.Length < MaxVID)
                {
                    signs = new sbyte[2 * MaxVID];
                }
                gParallel.ForEach(vertices, (vid) => {
                    if (Mesh.IsVertex(vid))
                    {
                        if (vid == i0_vid || vid == i1_vid)
                        {
                            signs[vid] = 0;
                        }
                        else
                        {
                            Vector2d v2 = PointF(vid);
                            // tolerance defines band in which we will consider values to be zero
                            signs[vid] = (sbyte)seg.WhichSide(v2, SpatialEpsilon);
                        }
                    }
                    else
                    {
                        signs[vid] = sbyte.MaxValue;
                    }
                });

                // have to skip processing of new edges. If edge id
                // is > max at start, is new. Otherwise if in NewEdges list, also new.
                // (need both in case we re-use an old edge index)
                int MaxEID = Mesh.MaxEdgeID;
                NewEdges.Clear();
                NewCutVertices.Clear();
                NewCutVertices.Add(i0_vid);
                NewCutVertices.Add(i1_vid);

                // cut existing edges with segment
                IEnumerable <int> edges = Interval1i.Range(MaxEID);
                if (triSpatial != null)
                {
                    edges = segEdges;
                }
                foreach (int eid in edges)
                {
                    if (Mesh.IsEdge(eid) == false)
                    {
                        continue;
                    }
                    if (eid >= MaxEID || NewEdges.Contains(eid))
                    {
                        continue;
                    }

                    // cannot cut boundary edges?
                    if (Mesh.IsBoundaryEdge(eid))
                    {
                        continue;
                    }

                    Index2i ev       = Mesh.GetEdgeV(eid);
                    int     eva_sign = signs[ev.a];
                    int     evb_sign = signs[ev.b];

                    // [RMS] should we be using larger epsilon here? If we don't track OnCurveVerts explicitly, we
                    // need to at least use same epsilon we passed to insert_corner_from_bary...do we still also
                    // need that to catch the edges we split in the poke?
                    bool eva_in_segment = false;
                    if (eva_sign == 0)
                    {
                        eva_in_segment = OnCurveVerts.Contains(ev.a) || Math.Abs(seg.Project(PointF(ev.a))) < (seg.Extent + SpatialEpsilon);
                    }
                    bool evb_in_segment = false;
                    if (evb_sign == 0)
                    {
                        evb_in_segment = OnCurveVerts.Contains(ev.b) || Math.Abs(seg.Project(PointF(ev.b))) < (seg.Extent + SpatialEpsilon);
                    }

                    // If one or both vertices are on-segment, we have special case.
                    // If just one vertex is on the segment, we can skip this edge.
                    // If both vertices are on segment, then we can just re-use this edge.
                    if (eva_in_segment || evb_in_segment)
                    {
                        if (eva_in_segment && evb_in_segment)
                        {
                            ZeroEdges.Add(eid);
                            add_cut_edge(eid);
                            NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b);
                        }
                        else
                        {
                            int zvid = eva_in_segment ? ev.a : ev.b;
                            ZeroVertices.Add(zvid);
                            NewCutVertices.Add(zvid);
                        }
                        continue;
                    }

                    // no crossing
                    if (eva_sign * evb_sign > 0)
                    {
                        continue;
                    }

                    // compute segment/segment intersection
                    Vector2d             va       = PointF(ev.a);
                    Vector2d             vb       = PointF(ev.b);
                    Segment2d            edge_seg = new Segment2d(va, vb);
                    IntrSegment2Segment2 intr     = new IntrSegment2Segment2(seg, edge_seg);
                    intr.Compute();
                    if (intr.Type == IntersectionType.Segment)
                    {
                        // [RMS] we should have already caught this above, so if it happens here it is probably spurious?
                        // we should have caught this case above, but numerics are different so it might occur again
                        ZeroEdges.Add(eid);
                        NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b);
                        add_cut_edge(eid);
                        continue;
                    }
                    else if (intr.Type != IntersectionType.Point)
                    {
                        continue; // no intersection
                    }
                    Vector2d x = intr.Point0;
                    double   t = Math.Sqrt(x.DistanceSquared(va) / va.DistanceSquared(vb));

                    // this case happens if we aren't "on-segment" but after we do the test the intersection pt
                    // is within epsilon of one end of the edge. This is a spurious t-intersection and we
                    // can ignore it. Some other edge should exist that picks up this vertex as part of it.
                    // [TODO] what about if this edge is degenerate?
                    bool x_in_segment = Math.Abs(edge_seg.Project(x)) < (edge_seg.Extent - SpatialEpsilon);
                    if (!x_in_segment)
                    {
                        continue;
                    }

                    Index2i et = Mesh.GetEdgeT(eid);
                    spatial_remove_triangles(et.a, et.b);

                    // split edge at this segment
                    DMesh3.EdgeSplitInfo splitInfo;
                    MeshResult           result = Mesh.SplitEdge(eid, out splitInfo, t);
                    if (result != MeshResult.Ok)
                    {
                        throw new Exception("MeshInsertUVSegment.Apply: SplitEdge failed - " + result.ToString());
                        //return false;
                    }

                    // move split point to intersection position
                    SetPointF(splitInfo.vNew, x);
                    NewCutVertices.Add(splitInfo.vNew);

                    NewEdges.Add(splitInfo.eNewBN);
                    NewEdges.Add(splitInfo.eNewCN);

                    spatial_add_triangles(et.a, et.b);
                    spatial_add_triangles(splitInfo.eNewT2, splitInfo.eNewT3);

                    // some splits - but not all - result in new 'other' edges that are on
                    // the polypath. We want to keep track of these edges so we can extract loop later.
                    Index2i ecn = Mesh.GetEdgeV(splitInfo.eNewCN);
                    if (NewCutVertices.Contains(ecn.a) && NewCutVertices.Contains(ecn.b))
                    {
                        add_cut_edge(splitInfo.eNewCN);
                    }

                    // since we don't handle bdry edges this should never be false, but maybe we will handle bdry later...
                    if (splitInfo.eNewDN != DMesh3.InvalidID)
                    {
                        NewEdges.Add(splitInfo.eNewDN);
                        Index2i edn = Mesh.GetEdgeV(splitInfo.eNewDN);
                        if (NewCutVertices.Contains(edn.a) && NewCutVertices.Contains(edn.b))
                        {
                            add_cut_edge(splitInfo.eNewDN);
                        }
                    }
                }
            }

            // extract the cut paths
            if (EnableCutSpansAndLoops)
            {
                find_cut_paths(OnCutEdges);
            }

            return(true);
        }         // Apply()
Beispiel #23
0
        /// <summary>
        // This function checks that the mesh is well-formed, ie all internal data
        // structures are consistent
        /// </summary>
        public bool CheckValidity(bool bAllowNonManifoldVertices = false, FailMode eFailMode = FailMode.Throw)
        {
            int[] triToVtxRefs = new int[this.MaxVertexID];

            bool          is_ok        = true;
            Action <bool> CheckOrFailF = (b) => { is_ok = is_ok && b; };

            if (eFailMode == FailMode.DebugAssert)
            {
                CheckOrFailF = (b) => { Debug.Assert(b); is_ok = is_ok && b; };
            }
            else if (eFailMode == FailMode.gDevAssert)
            {
                CheckOrFailF = (b) => { Util.gDevAssert(b); is_ok = is_ok && b; };
            }
            else if (eFailMode == FailMode.Throw)
            {
                CheckOrFailF = (b) => { if (b == false)
                                        {
                                            throw new Exception("DMesh3.CheckValidity: check failed");
                                        }
                };
            }

            if (normals != null)
            {
                CheckOrFailF(normals.size == vertices.size);
            }
            if (colors != null)
            {
                CheckOrFailF(colors.size == vertices.size);
            }
            if (uv != null)
            {
                CheckOrFailF(uv.size / 2 == vertices.size / 3);
            }
            if (triangle_groups != null)
            {
                CheckOrFailF(triangle_groups.size == triangles.size / 3);
            }

            foreach (int tID in TriangleIndices())
            {
                CheckOrFailF(IsTriangle(tID));
                CheckOrFailF(triangles_refcount.refCount(tID) == 1);

                // vertices must exist
                Index3i tv = GetTriangle(tID);
                for (int j = 0; j < 3; ++j)
                {
                    CheckOrFailF(IsVertex(tv[j]));
                    triToVtxRefs[tv[j]] += 1;
                }

                // edges must exist and reference this tri
                Index3i e = new Index3i();
                for (int j = 0; j < 3; ++j)
                {
                    int a = tv[j], b = tv[(j + 1) % 3];
                    e[j] = FindEdge(a, b);
                    CheckOrFailF(e[j] != InvalidID);
                    CheckOrFailF(edge_has_t(e[j], tID));
                    CheckOrFailF(e[j] == FindEdgeFromTri(a, b, tID));
                }
                CheckOrFailF(e[0] != e[1] && e[0] != e[2] && e[1] != e[2]);

                // tri nbrs must exist and reference this tri, or same edge must be boundary edge
                Index3i te = GetTriEdges(tID);
                for (int j = 0; j < 3; ++j)
                {
                    int eid = te[j];
                    CheckOrFailF(IsEdge(eid));
                    int tOther = edge_other_t(eid, tID);
                    if (tOther == InvalidID)
                    {
                        CheckOrFailF(tri_is_boundary(tID));
                        continue;
                    }

                    CheckOrFailF(tri_has_neighbour_t(tOther, tID) == true);

                    // edge must have same two verts as tri for same index
                    int     a = tv[j], b = tv[(j + 1) % 3];
                    Index2i ev = GetEdgeV(te[j]);
                    CheckOrFailF(IndexUtil.same_pair_unordered(a, b, ev[0], ev[1]));

                    // also check that nbr edge has opposite orientation
                    Index3i othertv = GetTriangle(tOther);
                    int     found   = IndexUtil.find_tri_ordered_edge(b, a, othertv.array);
                    CheckOrFailF(found != InvalidID);
                }
            }


            // edge verts/tris must exist
            foreach (int eID in EdgeIndices())
            {
                CheckOrFailF(IsEdge(eID));
                CheckOrFailF(edges_refcount.refCount(eID) == 1);
                Index2i ev = GetEdgeV(eID);
                Index2i et = GetEdgeT(eID);
                CheckOrFailF(IsVertex(ev[0]));
                CheckOrFailF(IsVertex(ev[1]));
                CheckOrFailF(et[0] != InvalidID);
                CheckOrFailF(ev[0] < ev[1]);
                CheckOrFailF(IsTriangle(et[0]));
                if (et[1] != InvalidID)
                {
                    CheckOrFailF(IsTriangle(et[1]));
                }
            }

            // verify compact check
            bool is_compact = vertices_refcount.is_dense;

            if (is_compact)
            {
                for (int vid = 0; vid < vertices.Length / 3; ++vid)
                {
                    CheckOrFailF(vertices_refcount.isValid(vid));
                }
            }

            // vertex edges must exist and reference this vert
            foreach (int vID in VertexIndices())
            {
                CheckOrFailF(IsVertex(vID));

                Vector3d v = GetVertex(vID);
                CheckOrFailF(double.IsNaN(v.LengthSquared) == false);
                CheckOrFailF(double.IsInfinity(v.LengthSquared) == false);

                List <int> l = vertex_edges[vID];
                foreach (int edgeid in l)
                {
                    CheckOrFailF(IsEdge(edgeid));
                    CheckOrFailF(edge_has_v(edgeid, vID));

                    int otherV = edge_other_v(edgeid, vID);
                    int e2     = find_edge(vID, otherV);
                    CheckOrFailF(e2 != InvalidID);
                    CheckOrFailF(e2 == edgeid);
                    e2 = find_edge(otherV, vID);
                    CheckOrFailF(e2 != InvalidID);
                    CheckOrFailF(e2 == edgeid);
                }

                List <int> vTris = new List <int>(), vTris2 = new List <int>();
                GetVtxTriangles(vID, vTris, false);
                GetVtxTriangles(vID, vTris2, true);
                CheckOrFailF(vTris.Count == vTris2.Count);
                //System.Console.WriteLine(string.Format("{0} {1} {2}", vID, vTris.Count, GetVtxEdges(vID).Count));
                if (bAllowNonManifoldVertices)
                {
                    CheckOrFailF(vTris.Count <= GetVtxEdges(vID).Count);
                }
                else
                {
                    CheckOrFailF(vTris.Count == GetVtxEdges(vID).Count || vTris.Count == GetVtxEdges(vID).Count - 1);
                }
                CheckOrFailF(vertices_refcount.refCount(vID) == vTris.Count + 1);
                CheckOrFailF(triToVtxRefs[vID] == vTris.Count);
                foreach (int tID in vTris)
                {
                    CheckOrFailF(tri_has_v(tID, vID));
                }

                // check that edges around vert only references tris above, and reference all of them!
                List <int> vRemoveTris = new List <int>(vTris);
                foreach (int edgeid in l)
                {
                    Index2i edget = GetEdgeT(edgeid);
                    CheckOrFailF(vTris.Contains(edget[0]));
                    if (edget[1] != InvalidID)
                    {
                        CheckOrFailF(vTris.Contains(edget[1]));
                    }
                    vRemoveTris.Remove(edget[0]);
                    if (edget[1] != InvalidID)
                    {
                        vRemoveTris.Remove(edget[1]);
                    }
                }
                CheckOrFailF(vRemoveTris.Count == 0);
            }

            return(is_ok);
        }
Beispiel #24
0
        /// <summary>
        /// Stitch two sets of boundary edges that are provided as unordered pairs of edges, by
        /// adding triangulated quads between each edge pair.
        /// If bAbortOnFailure==true and a failure is encountered during stitching, the triangles added up to that point are removed.
        /// If bAbortOnFailure==false, failures are ignored and the returned triangle list may contain invalid values!
        /// </summary>
        public virtual int[] StitchUnorderedEdges(List <Index2i> EdgePairs, int group_id, bool bAbortOnFailure, out bool stitch_incomplete)
        {
            int N = EdgePairs.Count;

            int[] new_tris = new int[N * 2];
            if (bAbortOnFailure == false)
            {
                for (int k = 0; k < new_tris.Length; ++k)
                {
                    new_tris[k] = DMesh3.InvalidID;
                }
            }
            stitch_incomplete = false;

            int i = 0;

            for (; i < N; ++i)
            {
                Index2i edges = EdgePairs[i];

                // look up and orient the first edge
                Index4i edge_a = Mesh.GetEdge(edges.a);
                if (edge_a.d != DMesh3.InvalidID)
                {
                    if (bAbortOnFailure)
                    {
                        goto operation_failed;
                    }
                    else
                    {
                        stitch_incomplete = true; continue;
                    }
                }
                Index3i edge_a_tri = Mesh.GetTriangle(edge_a.c);
                int     a = edge_a.a, b = edge_a.b;
                IndexUtil.orient_tri_edge(ref a, ref b, edge_a_tri);

                // look up and orient the second edge
                Index4i edge_b = Mesh.GetEdge(edges.b);
                if (edge_b.d != DMesh3.InvalidID)
                {
                    if (bAbortOnFailure)
                    {
                        goto operation_failed;
                    }
                    else
                    {
                        stitch_incomplete = true; continue;
                    }
                }
                Index3i edge_b_tri = Mesh.GetTriangle(edge_b.c);
                int     c = edge_b.a, d = edge_b.b;
                IndexUtil.orient_tri_edge(ref c, ref d, edge_b_tri);

                // swap second edge (right? should this be a parameter?)
                int tmp = c; c = d; d = tmp;

                var t1 = new Index3i(b, a, d);
                var t2 = new Index3i(a, c, d);

                int tid1 = Mesh.AppendTriangle(t1, group_id);
                int tid2 = Mesh.AppendTriangle(t2, group_id);

                if (tid1 < 0 || tid2 < 0)
                {
                    if (bAbortOnFailure)
                    {
                        goto operation_failed;
                    }
                    else
                    {
                        stitch_incomplete = true; continue;
                    }
                }

                new_tris[2 * i]     = tid1;
                new_tris[2 * i + 1] = tid2;
            }

            return(new_tris);

operation_failed:
            // remove what we added so far
            if (i > 0)
            {
                if (remove_triangles(new_tris, 2 * (i - 1)) == false)
                {
                    throw new Exception("MeshEditor.StitchLoop: failed to add all triangles, and also failed to back out changes.");
                }
            }
            return(null);
        }
Beispiel #25
0
        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);
        }
Beispiel #26
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)
            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);
            }
        }
Beispiel #27
0
        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);
            }
        }
Beispiel #28
0
        /// <summary>
        /// Stitch two sets of boundary edges that are provided as unordered pairs of edges, by
        /// adding triangulated quads between each edge pair.
        /// If a failure is encountered during stitching, the triangles added up to that point are removed.
        /// </summary>
        public virtual int[] StitchUnorderedEdges(List <Index2i> EdgePairs, int group_id = -1)
        {
            int N = EdgePairs.Count;

            int[] new_tris = new int[N * 2];

            int i = 0;

            for (; i < N; ++i)
            {
                Index2i edges = EdgePairs[i];

                // look up and orient the first edge
                Index4i edge_a = Mesh.GetEdge(edges.a);
                if (edge_a.d != DMesh3.InvalidID)
                {
                    goto operation_failed;
                }
                Index3i edge_a_tri = Mesh.GetTriangle(edge_a.c);
                int     a = edge_a.a, b = edge_a.b;
                IndexUtil.orient_tri_edge(ref a, ref b, edge_a_tri);

                // look up and orient the second edge
                Index4i edge_b = Mesh.GetEdge(edges.b);
                if (edge_b.d != DMesh3.InvalidID)
                {
                    goto operation_failed;
                }
                Index3i edge_b_tri = Mesh.GetTriangle(edge_b.c);
                int     c = edge_b.a, d = edge_b.b;
                IndexUtil.orient_tri_edge(ref c, ref d, edge_b_tri);

                // swap second edge (right? should this be a parameter?)
                int tmp = c; c = d; d = tmp;

                Index3i t1 = new Index3i(b, a, d);
                Index3i t2 = new Index3i(a, c, d);

                int tid1 = Mesh.AppendTriangle(t1, group_id);
                int tid2 = Mesh.AppendTriangle(t2, group_id);

                if (tid1 == DMesh3.InvalidID || tid2 == DMesh3.InvalidID)
                {
                    goto operation_failed;
                }

                new_tris[2 * i]     = tid1;
                new_tris[2 * i + 1] = tid2;
            }

            return(new_tris);

operation_failed:
            // remove what we added so far
            if (i > 0)
            {
                if (remove_triangles(new_tris, 2 * (i - 1)) == false)
                {
                    throw new Exception("MeshConstructor.StitchLoop: failed to add all triangles, and also failed to back out changes.");
                }
            }
            return(null);
        }
Beispiel #29
0
        public MeshResult MergeEdges(int eKeep, int eDiscard, out MergeEdgesInfo merge_info)
        {
            merge_info = new MergeEdgesInfo();
            if (IsEdge(eKeep) == false || IsEdge(eDiscard) == false)
            {
                return(MeshResult.Failed_NotAnEdge);
            }

            Index4i edgeinfo_keep    = GetEdge(eKeep);
            Index4i edgeinfo_discard = GetEdge(eDiscard);

            if (edgeinfo_keep.d != InvalidID || edgeinfo_discard.d != InvalidID)
            {
                return(MeshResult.Failed_NotABoundaryEdge);
            }

            int a = edgeinfo_keep.a, b = edgeinfo_keep.b;
            int tab = edgeinfo_keep.c;
            int eab = eKeep;
            int c = edgeinfo_discard.a, d = edgeinfo_discard.b;
            int tcd = edgeinfo_discard.c;
            int ecd = eDiscard;

            // Need to correctly orient a,b and c,d and then check that
            // we will not join triangles with incompatible winding order
            // I can't see how to do this purely topologically.
            // So relying on closest-pairs testing.
            IndexUtil.orient_tri_edge(ref a, ref b, GetTriangle(tab));
            //int tcd_otherv = IndexUtil.orient_tri_edge_and_find_other_vtx(ref c, ref d, GetTriangle(tcd));
            IndexUtil.orient_tri_edge(ref c, ref d, GetTriangle(tcd));
            int      x = c; c = d; d = x;          // joinable bdry edges have opposing orientations, so flip to get ac and b/d correspondences
            Vector3d Va = GetVertex(a), Vb = GetVertex(b), Vc = GetVertex(c), Vd = GetVertex(d);

            if ((Va.DistanceSquared(Vc) + Vb.DistanceSquared(Vd)) >
                (Va.DistanceSquared(Vd) + Vb.DistanceSquared(Vc)))
            {
                return(MeshResult.Failed_SameOrientation);
            }

            // alternative that detects normal flip of triangle tcd. This is a more
            // robust geometric test, but fails if tri is degenerate...also more expensive
            //Vector3d otherv = GetVertex(tcd_otherv);
            //Vector3d Ncd = MathUtil.FastNormalDirection(GetVertex(c), GetVertex(d), otherv);
            //Vector3d Nab = MathUtil.FastNormalDirection(GetVertex(a), GetVertex(b), otherv);
            //if (Ncd.Dot(Nab) < 0)
            //return MeshResult.Failed_SameOrientation;

            merge_info.eKept    = eab;
            merge_info.eRemoved = ecd;

            // [TODO] this acts on each interior tri twice. could avoid using vtx-tri iterator?

            List <int> edges_a = vertex_edges[a];

            if (a != c)
            {
                // replace c w/ a in edges and tris connected to c, and move edges to a
                List <int> edges_c = vertex_edges[c];
                for (int i = 0; i < edges_c.Count; ++i)
                {
                    int eid = edges_c[i];
                    if (eid == eDiscard)
                    {
                        continue;
                    }
                    replace_edge_vertex(eid, c, a);
                    short rc = 0;
                    if (replace_tri_vertex(edges[4 * eid + 2], c, a) >= 0)
                    {
                        rc++;
                    }
                    if (edges[4 * eid + 3] != InvalidID)
                    {
                        if (replace_tri_vertex(edges[4 * eid + 3], c, a) >= 0)
                        {
                            rc++;
                        }
                    }
                    edges_a.Add(eid);
                    if (rc > 0)
                    {
                        vertices_refcount.increment(a, rc);
                        vertices_refcount.decrement(c, rc);
                    }
                }
                vertex_edges[c] = null;
                vertices_refcount.decrement(c);
                merge_info.vRemoved[0] = c;
            }
            else
            {
                edges_a.Remove(ecd);
                merge_info.vRemoved[0] = InvalidID;
            }
            merge_info.vKept[0] = a;

            List <int> edges_b = vertex_edges[b];

            if (d != b)
            {
                // replace d w/ b in edges and tris connected to d, and move edges to b
                List <int> edges_d = vertex_edges[d];
                for (int i = 0; i < edges_d.Count; ++i)
                {
                    int eid = edges_d[i];
                    if (eid == eDiscard)
                    {
                        continue;
                    }
                    replace_edge_vertex(eid, d, b);
                    short rc = 0;
                    if (replace_tri_vertex(edges[4 * eid + 2], d, b) >= 0)
                    {
                        rc++;
                    }
                    if (edges[4 * eid + 3] != InvalidID)
                    {
                        if (replace_tri_vertex(edges[4 * eid + 3], d, b) >= 0)
                        {
                            rc++;
                        }
                    }
                    edges_b.Add(eid);
                    if (rc > 0)
                    {
                        vertices_refcount.increment(b, rc);
                        vertices_refcount.decrement(d, rc);
                    }
                }
                vertex_edges[d] = null;
                vertices_refcount.decrement(d);
                merge_info.vRemoved[1] = d;
            }
            else
            {
                edges_b.Remove(ecd);
                merge_info.vRemoved[1] = InvalidID;
            }
            merge_info.vKept[1] = b;

            // replace edge cd with edge ab in triangle tcd
            replace_triangle_edge(tcd, ecd, eab);
            edges_refcount.decrement(ecd);

            // update edge-tri adjacency
            set_edge_triangles(eab, tab, tcd);

            // Once we merge ab to cd, there may be additional edges (now) connected
            // to either a or b that are connected to the same vertex on their 'other' side.
            // So we now have two boundary edges connecting the same two vertices - disaster!
            // We need to find and merge these edges.
            // Q: I don't think it is possible to have multiple such edge-pairs at a or b
            //    But I am not certain...is a bit tricky to handle because we modify edges_v...
            merge_info.eRemovedExtra = new Vector2i(InvalidID, InvalidID);
            merge_info.eKeptExtra    = merge_info.eRemovedExtra;
            for (int vi = 0; vi < 2; ++vi)
            {
                int v1 = a, v2 = c;                   // vertices of merged edge
                if (vi == 1)
                {
                    v1 = b; v2 = d;
                }
                if (v1 == v2)
                {
                    continue;
                }
                List <int> edges_v = vertex_edges[v1];
                int        Nedges  = edges_v.Count;
                bool       found   = false;
                // in this loop, we compare 'other' vert_1 and vert_2 of edges around v1.
                // problem case is when vert_1 == vert_2  (ie two edges w/ same other vtx).
                for (int i = 0; i < Nedges && found == false; ++i)
                {
                    int edge_1 = edges_v[i];
                    if (edge_is_boundary(edge_1) == false)
                    {
                        continue;
                    }
                    int vert_1 = edge_other_v(edge_1, v1);
                    for (int j = i + 1; j < Nedges; ++j)
                    {
                        int edge_2 = edges_v[j];
                        int vert_2 = edge_other_v(edge_2, v1);
                        if (vert_1 == vert_2 && edge_is_boundary(edge_2))                           // if ! boundary here, we are in deep trouble...
                        // replace edge_2 w/ edge_1 in tri, update edge and vtx-edge-nbr lists
                        {
                            int tri_1 = edges[4 * edge_1 + 2];
                            int tri_2 = edges[4 * edge_2 + 2];
                            replace_triangle_edge(tri_2, edge_2, edge_1);
                            set_edge_triangles(edge_1, tri_1, tri_2);
                            vertex_edges[v1].Remove(edge_2);
                            vertex_edges[vert_1].Remove(edge_2);
                            edges_refcount.decrement(edge_2);
                            merge_info.eRemovedExtra[vi] = edge_2;
                            merge_info.eKeptExtra[vi]    = edge_1;

                            //Nedges = edges_v.Count; // this code allows us to continue checking, ie in case we had
                            //i--;					  // multiple such edges. but I don't think it's possible.
                            found = true;                                                 // exit outer i loop
                            break;                                                        // exit inner j loop
                        }
                    }
                }
            }

            return(MeshResult.Ok);
        }