Example #1
0
        public virtual void FastCollapsePass(double fMinEdgeLength)
        {
            if (mesh.TriangleCount == 0)    // badness if we don't catch this...
            {
                return;
            }

            MinEdgeLength = fMinEdgeLength;
            double min_sqr = MinEdgeLength * MinEdgeLength;

            // we don't collapse on the boundary
            HaveBoundary = false;

            begin_pass();

            begin_setup();
            Precompute();
            end_setup();

            begin_ops();

            begin_collapse();

            int      N = mesh.MaxEdgeID;
            Vector3d va = Vector3d.Zero, vb = Vector3d.Zero;

            for (int eid = 0; eid < N; ++eid)
            {
                if (!mesh.IsEdge(eid))
                {
                    continue;
                }
                if (mesh.IsBoundaryEdge(eid))
                {
                    continue;
                }

                mesh.GetEdgeV(eid, ref va, ref vb);
                if (va.DistanceSquared(ref vb) > min_sqr)
                {
                    continue;
                }

                COUNT_ITERATIONS++;

                Vector3d      midpoint = (va + vb) * 0.5;
                int           vKept;
                ProcessResult result = CollapseEdge(eid, midpoint, out vKept);
                if (result == ProcessResult.Ok_Collapsed)
                {
                    // do nothing?
                }
            }
            end_collapse();
            end_ops();

            Reproject();

            end_pass();
        }
 public bool IsInternalSpan()
 {
     int NV = Vertices.Length;
     for (int i = 0; i < NV-1; ++i ) {
         int eid = Mesh.FindEdge(Vertices[i], Vertices[i + 1]);
         Debug.Assert(eid != DMesh3.InvalidID);
         if (Mesh.IsBoundaryEdge(eid))
             return false;
     }
     return true;
 }
Example #3
0
        public bool IsInternalLoop()
        {
            int NV = Vertices.Length;

            for (int i = 0; i < NV; ++i)
            {
                int eid = Mesh.FindEdge(Vertices[i], Vertices[(i + 1) % NV]);
                Debug.Assert(eid != DMesh3.InvalidID);
                if (Mesh.IsBoundaryEdge(eid))
                {
                    return(false);
                }
            }
            return(true);
        }
Example #4
0
        /// <summary>
        /// construct EdgeLoop from a list of vertices of mesh
        /// if loop is a boundary edge, we can correct orientation if requested
        /// </summary>
        public static EdgeLoop FromVertices(DMesh3 mesh, IList <int> vertices, bool bAutoOrient = true)
        {
            int[] Vertices = new int[vertices.Count];
            for (int i = 0; i < Vertices.Length; i++)
            {
                Vertices[i] = vertices[i];
            }

            if (bAutoOrient)
            {
                int a = Vertices[0], b = Vertices[1];
                int eid = mesh.FindEdge(a, b);
                if (mesh.IsBoundaryEdge(eid))
                {
                    Index2i ev = mesh.GetOrientedBoundaryEdgeV(eid);
                    if (ev.a == b && ev.b == a)
                    {
                        Array.Reverse(Vertices);
                    }
                }
            }

            int[] Edges = new int[Vertices.Length];
            for (int i = 0; i < Edges.Length; ++i)
            {
                int a = Vertices[i], b = Vertices[(i + 1) % Vertices.Length];
                Edges[i] = mesh.FindEdge(a, b);
                if (Edges[i] == DMesh3.InvalidID)
                {
                    throw new Exception("EdgeLoop.FromVertices: invalid edge [" + a + "," + b + "]");
                }
            }

            return(new EdgeLoop(mesh, Vertices, Edges, false));
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <summary>
        /// if this is a border edge-loop, we can check that it is oriented correctly, and
        /// if not, reverse it.
        /// Returns true if we reversed orientation.
        /// </summary>
        public bool CorrectOrientation()
        {
            int a = Vertices[0], b = Vertices[1];
            int eid = Mesh.FindEdge(a, b);

            if (Mesh.IsBoundaryEdge(eid))
            {
                Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eid);
                if (ev.a == b && ev.b == a)
                {
                    Reverse();
                    return(true);
                }
            }
            return(false);
        }
Example #7
0
        // return point that minimizes quadric error for edge [ea,eb]
        Vector3d OptimalPoint(int eid, ref QuadricError q, int ea, int eb)
        {
            // if we would like to preserve boundary, we need to know that here
            // so that we properly score these edges
            if (HaveBoundary && PreserveBoundary)
            {
                if (mesh.IsBoundaryEdge(eid))
                {
                    return((mesh.GetVertex(ea) + mesh.GetVertex(eb)) * 0.5);
                }
                else
                {
                    if (IsBoundaryV(ea))
                    {
                        return(mesh.GetVertex(ea));
                    }
                    else if (IsBoundaryV(eb))
                    {
                        return(mesh.GetVertex(eb));
                    }
                }
            }

            // [TODO] if we have constraints, we should apply them here, for same reason as bdry above...

            if (MinimizeQuadricPositionError == false)
            {
                return(project((mesh.GetVertex(ea) + mesh.GetVertex(eb)) * 0.5));
            }
            else
            {
                Vector3d result = Vector3d.Zero;
                if (q.OptimalPoint(ref result))
                {
                    return(project(result));
                }

                // degenerate matrix, evaluate quadric at edge end and midpoints
                // (could do line search here...)
                Vector3d va = mesh.GetVertex(ea);
                Vector3d vb = mesh.GetVertex(eb);
                Vector3d c  = project((va + vb) * 0.5);
                double   fa = q.Evaluate(va);
                double   fb = q.Evaluate(vb);
                double   fc = q.Evaluate(c);
                double   m  = MathUtil.Min(fa, fb, fc);
                if (m == fa)
                {
                    return(va);
                }
                else if (m == fb)
                {
                    return(vb);
                }
                return(c);
            }
        }
Example #8
0
 /// <summary>
 /// we can vastly speed things up if we precompute some invariants.
 /// You need to re-run this if you are changing the mesh externally
 /// between remesh passes, otherwise you will get weird results.
 /// But you will probably still come out ahead, computation-time-wise
 /// </summary>
 public virtual void Precompute()
 {
     // if we know mesh is closed, we can skip is-boundary checks, which makes
     // the flip-valence tests much faster!
     MeshIsClosed = true;
     foreach (int eid in mesh.EdgeIndices())
     {
         if (mesh.IsBoundaryEdge(eid))
         {
             MeshIsClosed = false;
             break;
         }
     }
 }
Example #9
0
        public static IEnumerable <int> BoundaryEdges(DMesh3 mesh)
        {
            int N = mesh.MaxEdgeID;

            for (int i = 0; i < N; ++i)
            {
                if (mesh.IsEdge(i))
                {
                    if (mesh.IsBoundaryEdge(i))
                    {
                        yield return(i);
                    }
                }
            }
        }
Example #10
0
        /// <summary>
        /// boundary vertices of mesh, but based on edges, so returns each vertex twice!
        /// </summary>
        public static IEnumerable <int> BoundaryEdgeVertices(DMesh3 mesh)
        {
            int N = mesh.MaxEdgeID;

            for (int i = 0; i < N; ++i)
            {
                if (mesh.IsEdge(i) && mesh.IsBoundaryEdge(i))
                {
                    Index2i ev = mesh.GetEdgeV(i);
                    yield return(ev.a);

                    yield return(ev.b);
                }
            }
        }
Example #11
0
        // for all mesh boundary edges, disable flip/split/collapse
        // for all mesh boundary vertices, pin in current position
        public static void FixAllBoundaryEdges(MeshConstraints cons, DMesh3 mesh)
        {
            int NE = mesh.MaxEdgeID;

            for (int ei = 0; ei < NE; ++ei)
            {
                if (mesh.IsEdge(ei) && mesh.IsBoundaryEdge(ei))
                {
                    cons.SetOrUpdateEdgeConstraint(ei, EdgeConstraint.FullyConstrained);

                    Index2i ev = mesh.GetEdgeV(ei);
                    cons.SetOrUpdateVertexConstraint(ev.a, VertexConstraint.Pinned);
                    cons.SetOrUpdateVertexConstraint(ev.b, VertexConstraint.Pinned);
                }
            }
        }
Example #12
0
 public static void DebugEdgeInfo(DMesh3 mesh, params int[] edgeIds)
 {
     foreach (var edgeId in edgeIds)
     {
         Debug.Write($"Info on edge {edgeId}... ");
         if (!mesh.IsEdge(edgeId))
         {
             Debug.WriteLine("Not an edge.");
             continue;
         }
         var e = mesh.GetEdge(edgeId);
         Debug.WriteLine($"IsBorder: {mesh.IsBoundaryEdge(edgeId)}");
         var from = mesh.GetVertex(e.a);
         var to   = mesh.GetVertex(e.b);
         Debug.WriteLine($"  from: {from.x},{from.y},{from.z} to {to.x},{to.y},{to.z}");
     }
 }
Example #13
0
        /// <summary>
        /// Check if all edges of this loop are boundary edges.
        /// If testMesh != null, will check that mesh instead of internal Mesh
        /// </summary>
        public bool IsBoundaryLoop(DMesh3 testMesh = null)
        {
            DMesh3 useMesh = (testMesh != null) ? testMesh : Mesh;

            int NV = Vertices.Length;

            for (int i = 0; i < NV; ++i)
            {
                int eid = useMesh.FindEdge(Vertices[i], Vertices[(i + 1) % NV]);
                Debug.Assert(eid != DMesh3.InvalidID);
                if (useMesh.IsBoundaryEdge(eid) == false)
                {
                    return(false);
                }
            }
            return(true);
        }
Example #14
0
        // for all mesh boundary vertices, pin in current position, but allow splits
        public static void FixAllBoundaryEdges_AllowSplit(MeshConstraints cons, DMesh3 mesh, int setID)
        {
            EdgeConstraint   edgeCons = new EdgeConstraint(EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse);
            VertexConstraint vertCons = new VertexConstraint(true, setID);

            int NE = mesh.MaxEdgeID;

            for (int ei = 0; ei < NE; ++ei)
            {
                if (mesh.IsEdge(ei) && mesh.IsBoundaryEdge(ei))
                {
                    cons.SetOrUpdateEdgeConstraint(ei, edgeCons);

                    Index2i ev = mesh.GetEdgeV(ei);
                    cons.SetOrUpdateVertexConstraint(ev.a, vertCons);
                    cons.SetOrUpdateVertexConstraint(ev.b, vertCons);
                }
            }
        }
Example #15
0
        public virtual int[] AddTriangleFan_OrderedEdgeLoop(int center, int[] edge_loop, int group_id = -1)
        {
            int N = edge_loop.Length;

            int[] new_tris = new int[N];

            int i = 0;

            for (i = 0; i < N; ++i)
            {
                if (Mesh.IsBoundaryEdge(edge_loop[i]) == false)
                {
                    goto operation_failed;
                }

                Index2i ev = Mesh.GetOrientedBoundaryEdgeV(edge_loop[i]);
                int     a = ev.a, b = ev.b;

                Index3i newT    = new Index3i(center, b, a);
                int     new_tid = Mesh.AppendTriangle(newT, group_id);
                if (new_tid == DMesh3.InvalidID)
                {
                    goto operation_failed;
                }

                new_tris[i] = new_tid;
            }

            return(new_tris);


operation_failed:
            // remove what we added so far
            if (i > 0)
            {
                if (remove_triangles(new_tris, i - 1) == false)
                {
                    throw new Exception("MeshConstructor.AddTriangleFan_OrderedEdgeLoop: failed to add fan, and also failed to back out changes.");
                }
            }
            return(null);
        }
Example #16
0
        public static ValidationStatus IsBoundaryLoop(DMesh3 mesh, EdgeLoop loop)
        {
            int N = loop.Vertices.Length;

            for (int i = 0; i < N; ++i)
            {
                if (!mesh.vertex_is_boundary(loop.Vertices[i]))
                {
                    return(ValidationStatus.NotBoundaryVertex);
                }
            }

            for (int i = 0; i < N; ++i)
            {
                int a = loop.Vertices[i];
                int b = loop.Vertices[(i + 1) % N];

                int eid = mesh.FindEdge(a, b);
                if (eid == DMesh3.InvalidID)
                {
                    return(ValidationStatus.VerticesNotConnectedByEdge);
                }

                if (mesh.IsBoundaryEdge(eid) == false)
                {
                    return(ValidationStatus.NotBoundaryEdge);
                }

                Index2i ev = mesh.GetOrientedBoundaryEdgeV(eid);
                if (!(ev.a == a && ev.b == b))
                {
                    return(ValidationStatus.IncorrectLoopOrientation);
                }
            }

            return(ValidationStatus.Ok);
        }
        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()
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            // early-out if we don't actually have boundaries
            if (Mesh.CachedIsClosed)
            {
                return(true);
            }

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            var used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            var loop_edges = new List <int>();                // [RMS] not sure we need this...
            var loop_verts = new List <int>();
            var bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }

                if (used_edge[eid] == true)
                {
                    continue;
                }

                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }

                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)
                    {                       // hit an 'endpoint' vertex (should only happen when Filter is on...)
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  };
                        }

                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;                                // begin open span
                            eCur        = loop_edges[0];                       // restart at other end of loop
                            loop_edges.Reverse();                              // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }

                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          };
                                }

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  };
                        }

                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();                          // orient properly
                        var span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    foreach (var loop in subloops.Loops)
                    {
                        Loops.Add(loop);
                    }

                    if (subloops.Spans.Count > 0)
                    {
                        FellBackToSpansOnFailure = true;
                        foreach (var span in subloops.Spans)
                        {
                            Spans.Add(span);
                        }
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    var loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }
Example #19
0
 public bool IsVertex(int vID)
 {
     return(Mesh.IsEdge(vID) && Mesh.IsBoundaryEdge(vID));
 }
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            // early-out if we don't actually have boundaries
            if (Mesh.CachedIsClosed)
            {
                return(true);
            }

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            BitArray used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            List <int> loop_edges = new List <int>();     // [RMS] not sure we need this...
            List <int> loop_verts = new List <int>();
            List <int> bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }
                if (used_edge[eid] == true)
                {
                    continue;
                }
                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }
                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)     // hit an 'endpoint' vertex (should only happen when Filter is on...)
                    {
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  }
                        }
                        ;
                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;          // begin open span
                            eCur        = loop_edges[0]; // restart at other end of loop
                            loop_edges.Reverse();        // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          }
                                }
                                ;

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  }
                        }
                        ;
                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();  // orient properly
                        EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    foreach (var loop in subloops.Loops)
                    {
                        Loops.Add(loop);
                    }
                    if (subloops.Spans.Count > 0)
                    {
                        FellBackToSpansOnFailure = true;
                        foreach (var span in subloops.Spans)
                        {
                            Spans.Add(span);
                        }
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    EdgeLoop loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }

        // [TODO] cache this in a dictionary? we will not need very many, but we will
        //   need each multiple times!
        Vector3d get_vtx_normal(int vid)
        {
            Vector3d n = Vector3d.Zero;

            foreach (int ti in Mesh.VtxTrianglesItr(vid))
            {
                n += Mesh.GetTriNormal(ti);
            }
            n.Normalize();
            return(n);
        }

        // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v.
        // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e.
        // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v.
        // So, we compute the tangent plane at bowtie_v, and then the signed angle for each
        // viable edge in this plane.
        //
        // [TODO] handle degenerate edges. what do we do then? Currently will only chose
        //  degenerate edge if there are no other options (I think...)
        int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges)
        {
            // compute normal and edge [a,bowtie]
            Vector3d n       = get_vtx_normal(bowtie_v);
            int      other_v = Mesh.edge_other_v(incoming_e, bowtie_v);
            Vector3d ab      = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v);

            // our winner
            int    best_e     = -1;
            double best_angle = double.MaxValue;

            for (int i = 0; i < bdry_edges_count; ++i)
            {
                int bdry_eid = bdry_edges[i];
                if (used_edges[bdry_eid] == true)
                {
                    continue;       // this edge is already used
                }
                Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid);
                if (bdry_ev.a != bowtie_v)
                {
                    continue;       // have to be able to chain to end of current edge, orientation-wise
                }
                // compute projected angle
                Vector3d bc      = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v);
                float    fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n);

                // turn left!
                if (best_angle == double.MaxValue || fAngleS < best_angle)
                {
                    best_angle = fAngleS;
                    best_e     = bdry_eid;
                }
            }

            // [RMS] w/ bowtie vertices and open spans, this does happen
            //Debug.Assert(best_e != -1);

            return(best_e);
        }
Example #21
0
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            BitArray used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            List <int> loop_edges = new List <int>();     // [RMS] not sure we need this...
            List <int> loop_verts = new List <int>();
            List <int> bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }
                if (used_edge[eid] == true)
                {
                    continue;
                }
                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }
                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)     // hit an 'endpoint' vertex (should only happen when Filter is on...)
                    {
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  }
                        }
                        ;
                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;          // begin open span
                            eCur        = loop_edges[0]; // restart at other end of loop
                            loop_edges.Reverse();        // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          }
                                }
                                ;

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  }
                        }
                        ;
                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();  // orient properly
                        EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    List <EdgeLoop> subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    for (int i = 0; i < subloops.Count; ++i)
                    {
                        Loops.Add(subloops[i]);
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    EdgeLoop loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }

        // [TODO] cache this in a dictionary? we will not need very many, but we will
        //   need each multiple times!
        Vector3d get_vtx_normal(int vid)
        {
            Vector3d n = Vector3d.Zero;

            foreach (int ti in Mesh.VtxTrianglesItr(vid))
            {
                n += Mesh.GetTriNormal(ti);
            }
            n.Normalize();
            return(n);
        }

        // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v.
        // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e.
        // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v.
        // So, we compute the tangent plane at bowtie_v, and then the signed angle for each
        // viable edge in this plane.
        //
        // [TODO] handle degenerate edges. what do we do then? Currently will only chose
        //  degenerate edge if there are no other options (I think...)
        int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges)
        {
            // compute normal and edge [a,bowtie]
            Vector3d n       = get_vtx_normal(bowtie_v);
            int      other_v = Mesh.edge_other_v(incoming_e, bowtie_v);
            Vector3d ab      = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v);

            // our winner
            int    best_e     = -1;
            double best_angle = double.MaxValue;

            for (int i = 0; i < bdry_edges_count; ++i)
            {
                int bdry_eid = bdry_edges[i];
                if (used_edges[bdry_eid] == true)
                {
                    continue;       // this edge is already used
                }
                Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid);
                if (bdry_ev.a != bowtie_v)
                {
                    continue;       // have to be able to chain to end of current edge, orientation-wise
                }
                // compute projected angle
                Vector3d bc      = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v);
                float    fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n);

                // turn left!
                if (best_angle == double.MaxValue || fAngleS < best_angle)
                {
                    best_angle = fAngleS;
                    best_e     = bdry_eid;
                }
            }

            // [RMS] w/ bowtie vertices and open spans, this does happen
            //Debug.Assert(best_e != -1);

            return(best_e);
        }

        // This is called when loopV contains one or more "bowtie" vertices.
        // These vertices *might* be duplicated in loopV (but not necessarily)
        // If they are, we have to break loopV into subloops that don't contain duplicates.
        //
        // The list bowties contains all the possible duplicates
        // (all v in bowties occur in loopV at least once)
        //
        // Currently loopE is not used, and the returned EdgeLoop objects do not have their Edges
        // arrays initialized. Perhaps to improve in future.
        List <EdgeLoop> extract_subloops(List <int> loopV, List <int> loopE, List <int> bowties)
        {
            List <EdgeLoop> subs = new List <EdgeLoop>();

            // figure out which bowties we saw are actually duplicated in loopV
            List <int> dupes = new List <int>();

            foreach (int bv in bowties)
            {
                if (count_in_list(loopV, bv) > 1)
                {
                    dupes.Add(bv);
                }
            }

            // we might not actually have any duplicates, if we got luck. Early out in that case
            if (dupes.Count == 0)
            {
                subs.Add(new EdgeLoop(Mesh)
                {
                    Vertices = loopV.ToArray(), Edges = loopE.ToArray(), BowtieVertices = bowties.ToArray()
                });
                return(subs);
            }

            // This loop extracts subloops until we have dealt with all the
            // duplicate vertices in loopV
            while (dupes.Count > 0)
            {
                // Find shortest "simple" loop, ie a loop from a bowtie to itself that
                // does not contain any other bowties. This is an independent loop.
                // We're doing a lot of extra work here if we only have one element in dupes...
                int bi = 0, bv = 0;
                int start_i = -1, end_i = -1;
                int bv_shortest = -1; int shortest = int.MaxValue;
                for ( ; bi < dupes.Count; ++bi)
                {
                    bv = dupes[bi];
                    if (is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i))
                    {
                        int len = count_span(loopV, start_i, end_i);
                        if (len < shortest)
                        {
                            bv_shortest = bv;
                            shortest    = len;
                        }
                    }
                }
                if (bv_shortest == -1)
                {
                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: Cannot find a valid simple loop");
                }
                if (bv != bv_shortest)
                {
                    bv = bv_shortest;
                    // running again just to get start_i and end_i...
                    is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i);
                }

                Debug.Assert(loopV[start_i] == bv && loopV[end_i] == bv);

                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices       = extract_span(loopV, start_i, end_i, true);
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);

                // If there are no more duplicates of this bowtie, we can treat
                // it like a regular vertex now
                if (count_in_list(loopV, bv) < 2)
                {
                    dupes.Remove(bv);
                }
            }

            // Should have one loop left that contains duplicates.
            // Extract this as a separate loop
            int nLeft = 0;

            for (int i = 0; i < loopV.Count; ++i)
            {
                if (loopV[i] != -1)
                {
                    nLeft++;
                }
            }
            if (nLeft > 0)
            {
                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices = new int[nLeft];
                int vi = 0;
                for (int i = 0; i < loopV.Count; ++i)
                {
                    if (loopV[i] != -1)
                    {
                        loop.Vertices[vi++] = loopV[i];
                    }
                }
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);
            }

            return(subs);
        }

        /*
         * In all the functions below, the list loopV is assumed to possibly
         * contain "removed" vertices indicated by -1. These are ignored.
         */


        // Check if the loop from bowtieV to bowtieV inside loopV contains any other bowtie verts.
        // Also returns start and end indices in loopV of "clean" loop
        // Note that start may be < end, if the "clean" loop wraps around the end
        bool is_simple_bowtie_loop(List <int> loopV, List <int> bowties, int bowtieV, out int start_i, out int end_i)
        {
            // find two indices of bowtie vert
            start_i = find_index(loopV, 0, bowtieV);
            end_i   = find_index(loopV, start_i + 1, bowtieV);

            if (is_simple_path(loopV, bowties, bowtieV, start_i, end_i))
            {
                return(true);
            }
            else if (is_simple_path(loopV, bowties, bowtieV, end_i, start_i))
            {
                int tmp = start_i; start_i = end_i; end_i = tmp;
                return(true);
            }
            else
            {
                return(false);       // not a simple bowtie loop!
            }
        }

        // check if forward path from loopV[i1] to loopV[i2] contains any bowtie verts other than bowtieV
        bool is_simple_path(List <int> loopV, List <int> bowties, int bowtieV, int i1, int i2)
        {
            int N = loopV.Count;

            for (int i = i1; i != i2; i = (i + 1) % N)
            {
                int vi = loopV[i];
                if (vi == -1)
                {
                    continue;       // skip removed vertices
                }
                if (vi != bowtieV && bowties.Contains(vi))
                {
                    return(false);
                }
            }
            return(true);
        }

        // Read out the span from loop[i0] to loop [i1-1] into an array.
        // If bMarkInvalid, then these values are set to -1 in loop
        int[] extract_span(List <int> loop, int i0, int i1, bool bMarkInvalid)
        {
            int num = count_span(loop, i0, i1);

            int[] a  = new int[num];
            int   ai = 0;
            int   N  = loop.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (loop[i] != -1)
                {
                    a[ai++] = loop[i];
                    if (bMarkInvalid)
                    {
                        loop[i] = -1;
                    }
                }
            }
            return(a);
        }

        // count number of valid vertices in l between loop[i0] and loop[i1-1]
        int count_span(List <int> l, int i0, int i1)
        {
            int c = 0;
            int N = l.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (l[i] != -1)
                {
                    c++;
                }
            }
            return(c);
        }

        // find the index of item in loop, starting at start index
        int find_index(List <int> loop, int start, int item)
        {
            for (int i = start; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    return(i);
                }
            }
            return(-1);
        }

        // count number of times item appears in loop
        int count_in_list(List <int> loop, int item)
        {
            int c = 0;

            for (int i = 0; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    c++;
                }
            }
            return(c);
        }
    }
}
Example #22
0
        public virtual bool Apply()
        {
            insert_corners();

            // [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>();

            // 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)
                {
                    OnCutEdges.Add(existing_edge);
                    continue;
                }

                // compute edge-crossing signs
                // [TODO] could walk along mesh from a to b, rather than computing for entire mesh?
                int   MaxVID = Mesh.MaxVertexID;
                int[] signs  = new int[MaxVID];
                gParallel.ForEach(Interval1i.Range(MaxVID), (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] = seg.WhichSide(v2, MathUtil.ZeroTolerance);
                        }
                    }
                    else
                    {
                        signs[vid] = int.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;
                HashSet <int> NewEdges       = new HashSet <int>();
                HashSet <int> NewCutVertices = new HashSet <int>();
                NewCutVertices.Add(i0_vid);
                NewCutVertices.Add(i1_vid);

                // cut existing edges with segment
                for (int eid = 0; eid < MaxEID; ++eid)
                {
                    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];

                    bool eva_in_segment = false;
                    if (eva_sign == 0)
                    {
                        eva_in_segment = Math.Abs(seg.Project(PointF(ev.a))) < (seg.Extent + MathUtil.ZeroTolerance);
                    }
                    bool evb_in_segment = false;
                    if (evb_sign == 0)
                    {
                        evb_in_segment = Math.Abs(seg.Project(PointF(ev.b))) < (seg.Extent + MathUtil.ZeroTolerance);
                    }

                    // 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);
                            OnCutEdges.Add(eid);
                        }
                        else
                        {
                            ZeroVertices.Add(eva_in_segment ? ev.a : ev.b);
                        }
                        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);
                        OnCutEdges.Add(eid);
                        continue;
                    }
                    else if (intr.Type != IntersectionType.Point)
                    {
                        continue; // no intersection
                    }
                    Vector2d x = intr.Point0;

                    // 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 - MathUtil.ZeroTolerance);
                    if (!x_in_segment)
                    {
                        continue;
                    }

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

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

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

                    // 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))
                    {
                        OnCutEdges.Add(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))
                        {
                            OnCutEdges.Add(splitInfo.eNewDN);
                        }
                    }
                }
            }


            //MeshEditor editor = new MeshEditor(Mesh);
            //foreach (int eid in OnCutEdges)
            //    editor.AppendBox(new Frame3f(Mesh.GetEdgePoint(eid, 0.5)), 0.1f);
            //Util.WriteDebugMesh(Mesh, string.Format("C:\\git\\geometry3SharpDemos\\geometry3Test\\test_output\\after_inserted.obj"));


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

            return(true);
        }         // Apply()