Beispiel #1
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 #2
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));
            }
        }
        protected virtual ProcessResult CollapseEdge(int edgeID, Vector3d vNewPos, out int collapseToV)
        {
            collapseToV = DMesh3.InvalidID;
            RuntimeDebugCheck(edgeID);

            EdgeConstraint constraint =
                (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID);

            if (constraint.NoModifications)
            {
                return(ProcessResult.Ignored_EdgeIsFullyConstrained);
            }
            if (constraint.CanCollapse == false)
            {
                return(ProcessResult.Ignored_EdgeIsFullyConstrained);
            }


            // look up verts and tris for this edge
            int a = 0, b = 0, t0 = 0, t1 = 0;

            if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false)
            {
                return(ProcessResult.Failed_NotAnEdge);
            }
            bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID);

            // look up 'other' verts c (from t0) and d (from t1, if it exists)
            Index3i T0tv         = mesh.GetTriangle(t0);
            int     c            = IndexUtil.find_tri_other_vtx(a, b, T0tv);
            Index3i T1tv         = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1);
            int     d            = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv);

            Vector3d vA           = mesh.GetVertex(a);
            Vector3d vB           = mesh.GetVertex(b);
            double   edge_len_sqr = (vA - vB).LengthSquared;

            if (edge_len_sqr > MinEdgeLength * MinEdgeLength)
            {
                return(ProcessResult.Ignored_EdgeTooLong);
            }

            begin_collapse();

            // check if we should collapse, and also find which vertex we should collapse to,
            // in cases where we have constraints/etc
            int  collapse_to  = -1;
            bool bCanCollapse = can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to);

            if (bCanCollapse == false)
            {
                return(ProcessResult.Ignored_Constrained);
            }

            // if we have a boundary, we want to collapse to boundary
            if (PreserveBoundaryShape && HaveBoundary)
            {
                if (collapse_to != -1)
                {
                    if ((IsBoundaryV(b) && collapse_to != b) ||
                        (IsBoundaryV(a) && collapse_to != a))
                    {
                        return(ProcessResult.Ignored_Constrained);
                    }
                }
                if (IsBoundaryV(b))
                {
                    collapse_to = b;
                }
                else if (IsBoundaryV(a))
                {
                    collapse_to = a;
                }
            }

            // optimization: if edge cd exists, we cannot collapse or flip. look that up here?
            //  funcs will do it internally...
            //  (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...)
            ProcessResult retVal = ProcessResult.Failed_OpNotSuccessful;

            int iKeep = b, iCollapse = a;

            // if either vtx is fixed, collapse to that position
            if (collapse_to == b)
            {
                vNewPos = vB;
            }
            else if (collapse_to == a)
            {
                iKeep   = a; iCollapse = b;
                vNewPos = vA;
            }
            else
            {
                vNewPos = get_projected_collapse_position(iKeep, vNewPos);
            }

            // check if this collapse will create a normal flip. Also checks
            // for invalid collapse nbrhood, since we are doing one-ring iter anyway.
            // [TODO] could we skip this one-ring check in CollapseEdge? pass in hints?
            if (collapse_creates_flip_or_invalid(a, b, ref vNewPos, t0, t1) || collapse_creates_flip_or_invalid(b, a, ref vNewPos, t0, t1))
            {
                retVal = ProcessResult.Ignored_CreatesFlip;
                goto skip_to_end;
            }

            // lots of cases where we cannot collapse, but we should just let
            // mesh sort that out, right?
            COUNT_COLLAPSES++;
            DMesh3.EdgeCollapseInfo collapseInfo;
            MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo);

            if (result == MeshResult.Ok)
            {
                collapseToV = iKeep;
                mesh.SetVertex(iKeep, vNewPos);
                if (constraints != null)
                {
                    constraints.ClearEdgeConstraint(edgeID);
                    constraints.ClearEdgeConstraint(collapseInfo.eRemoved0);
                    if (collapseInfo.eRemoved1 != DMesh3.InvalidID)
                    {
                        constraints.ClearEdgeConstraint(collapseInfo.eRemoved1);
                    }
                    constraints.ClearVertexConstraint(iCollapse);
                }
                OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo);
                DoDebugChecks();

                retVal = ProcessResult.Ok_Collapsed;
            }

skip_to_end:
            end_collapse();
            return(retVal);
        }
        // Walk along edge loop and collapse to inserted curve vertices.
        EdgeLoop simplify(EdgeLoop loop)
        {
            HashSet <int> curve_verts = new HashSet <int>(CurveVertices);

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

            for (int li = 0; li < loop.EdgeCount; ++li)
            {
                int     eid = loop.Edges[li];
                Index2i ev  = Mesh.GetEdgeV(eid);

                // cannot collapse edge between two "original" polygon verts (ie created by face pokes)
                if (curve_verts.Contains(ev.a) && curve_verts.Contains(ev.b))
                {
                    remaining_edges.Add(eid);
                    continue;
                }

                // if we have an original vert, we need to keep it (and its position!)
                int      keep = ev.a, discard = ev.b;
                Vector3d set_to = Vector3d.Zero;
                if (curve_verts.Contains(ev.b))
                {
                    keep    = ev.b;
                    discard = ev.a;
                    set_to  = Mesh.GetVertex(ev.b);
                }
                else if (curve_verts.Contains(ev.a))
                {
                    set_to = Mesh.GetVertex(ev.a);
                }
                else
                {
                    set_to = 0.5 * (Mesh.GetVertex(ev.a) + Mesh.GetVertex(ev.b));
                }

                // make sure we are not going to flip any normals
                // [OPTIMIZATION] May be possible to do this more efficiently because we know we are in
                //   2D and each tri should have same cw/ccw orientation. But we don't quite "know" we
                //   are in 2D here, as CollapseEdge function is operating on the mesh coordinates...
                if (MeshUtil.CheckIfCollapseCreatesFlip(Mesh, eid, set_to))
                {
                    remaining_edges.Add(eid);
                    continue;
                }

                // cannot collapse if the 'other' edges we would discard are OnCutEdges. This would
                // result in loop potentially being broken. bad!
                Index4i einfo = Mesh.GetEdge(eid);
                int     c     = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.c));
                int     d     = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.d));
                int     ec    = Mesh.FindEdge(discard, c);
                int     ed    = Mesh.FindEdge(discard, d);
                if (OnCutEdges.Contains(ec) || OnCutEdges.Contains(ed))
                {
                    remaining_edges.Add(eid);
                    continue;
                }

                // do collapse and update internal data structures
                DMesh3.EdgeCollapseInfo collapse;
                MeshResult result = Mesh.CollapseEdge(keep, discard, out collapse);
                if (result == MeshResult.Ok)
                {
                    Mesh.SetVertex(collapse.vKept, set_to);
                    OnCutEdges.Remove(collapse.eCollapsed);
                }
                else
                {
                    remaining_edges.Add(eid);
                }
            }

            return(EdgeLoop.FromEdges(Mesh, remaining_edges));
        }
Beispiel #5
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 #6
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);
        }
Beispiel #7
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 #8
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);
            }
        }