Example #1
0
        public void InitializeFromApply(DMesh3 mesh, IEnumerable <int> triangles)
        {
            initialize_buffers(mesh);
            bool has_groups = mesh.HasTriangleGroups;

            foreach (int tid in triangles)
            {
                if (!mesh.IsTriangle(tid))
                {
                    continue;
                }

                Index3i tv = mesh.GetTriangle(tid);
                bool    va = save_vertex(mesh, tv.a);
                bool    vb = save_vertex(mesh, tv.b);
                bool    vc = save_vertex(mesh, tv.c);

                var tri = new Index4i(tv.a, tv.b, tv.c,
                                      has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID);
                RemovedT.Add(tid);
                Triangles.Add(tri);

                MeshResult result = mesh.RemoveTriangle(tid, true, false);
                if (result != MeshResult.Ok)
                {
                    throw new Exception("RemoveTrianglesMeshChange.Initialize: exception in RemoveTriangle(" + tid.ToString() + "): " + result.ToString());
                }

                Util.gDevAssert(mesh.IsVertex(tv.a) == va && mesh.IsVertex(tv.b) == vb && mesh.IsVertex(tv.c) == vc);
            }
        }
 public void Select(int vID)
 {
     Debug.Assert(Mesh.IsVertex(vID));
     if (Mesh.IsVertex(vID))
     {
         add(vID);
     }
 }
        /// <summary>
        /// Exhaustively check that verts and edges of this EdgeSpan are consistent. Not for production use.
        /// </summary>
        public bool CheckValidity(FailMode eFailMode = FailMode.Throw)
        {
            bool          is_ok        = true;
            Action <bool> CheckOrFailF = (b) => { is_ok = is_ok && b; };

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

            CheckOrFailF(Vertices.Length == Edges.Length + 1);
            for (int ei = 0; ei < Edges.Length; ++ei)
            {
                Index2i ev = Mesh.GetEdgeV(Edges[ei]);
                CheckOrFailF(Mesh.IsVertex(ev.a));
                CheckOrFailF(Mesh.IsVertex(ev.b));
                CheckOrFailF(Mesh.FindEdge(ev.a, ev.b) != DMesh3.InvalidID);
                CheckOrFailF(Vertices[ei] == ev.a || Vertices[ei] == ev.b);
                CheckOrFailF(Vertices[ei + 1] == ev.a || Vertices[ei + 1] == ev.b);
            }
            for (int vi = 0; vi < Vertices.Length - 1; ++vi)
            {
                int a = Vertices[vi], b = Vertices[vi + 1];
                CheckOrFailF(Mesh.IsVertex(a));
                CheckOrFailF(Mesh.IsVertex(b));
                CheckOrFailF(Mesh.FindEdge(a, b) != DMesh3.InvalidID);
                if (vi < Vertices.Length - 2)
                {
                    int n = 0, edge_before_b = Edges[vi], edge_after_b = Edges[vi + 1];
                    foreach (int nbr_e in Mesh.VtxEdgesItr(b))
                    {
                        if (nbr_e == edge_before_b || nbr_e == edge_after_b)
                        {
                            n++;
                        }
                    }
                    CheckOrFailF(n == 2);
                }
            }
            return(true);
        }
Example #4
0
        public void RemoveContained()
        {
            DMeshAABBTree3 spatial = new DMeshAABBTree3(CutMesh, true);

            spatial.WindingNumber(Vector3d.Zero);
            SafeListBuilder <int> removeT = new SafeListBuilder <int>();

            gParallel.ForEach(Target.TriangleIndices(), (tid) => {
                Vector3d v = Target.GetTriCentroid(tid);
                if (spatial.WindingNumber(v) > 0.9)
                {
                    removeT.SafeAdd(tid);
                }
            });
            MeshEditor.RemoveTriangles(Target, removeT.Result);

            // [RMS] construct set of on-cut vertices? This is not
            // necessarily all boundary vertices...
            CutVertices = new List <int>();
            foreach (int vid in SegmentInsertVertices)
            {
                if (Target.IsVertex(vid))
                {
                    CutVertices.Add(vid);
                }
            }
        }
        public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> added_v, IEnumerable <int> added_t)
        {
            initialize_buffers(mesh);
            bool has_groups = mesh.HasTriangleGroups;

            if (added_v != null)
            {
                foreach (int vid in added_v)
                {
                    Util.gDevAssert(mesh.IsVertex(vid));
                    append_vertex(mesh, vid);
                }
            }

            foreach (int tid in added_t)
            {
                Util.gDevAssert(mesh.IsTriangle(tid));

                Index3i tv  = mesh.GetTriangle(tid);
                Index4i tri = new Index4i(tv.a, tv.b, tv.c,
                                          has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID);
                AddedT.Add(tid);
                Triangles.Add(tri);
            }
        }
        /// <summary>
        /// verifies that there is a 1-1 correspondence between the fill and target loops.
        /// If so, adds to mergeMapV and returns true;
        /// </summary>
        bool build_merge_map_simple(DMesh3 fillMesh, int[] fillLoopV,
                                    DMesh3 targetMesh, int[] targetLoopV,
                                    double tol, IndexMap mergeMapV)
        {
            if (fillLoopV.Length != targetLoopV.Length)
            {
                return(false);
            }

            int NV = fillLoopV.Length;

            for (int k = 0; k < NV; ++k)
            {
                if (!fillMesh.IsVertex(fillLoopV[k]))
                {
                    return(false);
                }

                Vector3d fillV   = fillMesh.GetVertex(fillLoopV[k]);
                Vector3d sourceV = Mesh.GetVertex(targetLoopV[k]);
                if (fillV.Distance(sourceV) > tol)
                {
                    return(false);
                }
            }
            for (int k = 0; k < NV; ++k)
            {
                mergeMapV[fillLoopV[k]] = targetLoopV[k];
            }

            return(true);
        }
 /// <summary>
 /// shortcut to construct graph for mesh vertices
 /// </summary>
 public static DijkstraGraphDistance MeshVertices(DMesh3 mesh, bool bSparse = false)
 {
     return((bSparse) ?
            new DijkstraGraphDistance(mesh.MaxVertexID, true,
                                      (id) => { return mesh.IsVertex(id); },
                                      (a, b) => { return (float)mesh.GetVertex(a).Distance(mesh.GetVertex(b)); },
                                      mesh.VtxVerticesItr, null)
                 : new DijkstraGraphDistance(mesh.MaxVertexID, false,
                                             (id) => { return true; },
                                             (a, b) => { return (float)mesh.GetVertex(a).Distance(mesh.GetVertex(b)); },
                                             mesh.VtxVerticesItr, null));
 }
Example #8
0
        public static IEnumerable <int> InteriorVertices(DMesh3 mesh)
        {
            int N = mesh.MaxVertexID;

            for (int i = 0; i < N; ++i)
            {
                if (mesh.IsVertex(i))
                {
                    if (mesh.IsBoundaryVertex(i) == false)
                    {
                        yield return(i);
                    }
                }
            }
        }
Example #9
0
        public static IEnumerable <int> FilteredVertices(DMesh3 mesh, Func <DMesh3, int, bool> FilterF)
        {
            int N = mesh.MaxVertexID;

            for (int i = 0; i < N; ++i)
            {
                if (mesh.IsVertex(i))
                {
                    if (FilterF(mesh, i))
                    {
                        yield return(i);
                    }
                }
            }
        }
Example #10
0
        public static IEnumerable <int> BowtieVertices(DMesh3 mesh)
        {
            int N = mesh.MaxVertexID;

            for (int i = 0; i < N; ++i)
            {
                if (mesh.IsVertex(i))
                {
                    if (mesh.IsBowtieVertex(i))
                    {
                        yield return(i);
                    }
                }
            }
        }
Example #11
0
        public void CopyTo(DMesh3 SetMesh)
        {
            if (SetMesh.MaxVertexID < Mesh.MaxVertexID)
            {
                throw new Exception("MeshNormals.Set: SetMesh does not have enough vertices!");
            }
            int NV = Mesh.MaxVertexID;

            for (int vi = 0; vi < NV; ++vi)
            {
                if (Mesh.IsVertex(vi) && SetMesh.IsVertex(vi))
                {
                    SetMesh.SetVertexNormal(vi, (Vector3f)Normals[vi]);
                }
            }
        }
Example #12
0
        public bool SolveAndUpdateMesh()
        {
            int N = Mesh.MaxVertexID;

            Vector3d[] Result = new Vector3d[N];
            if (!Solve(Result))
            {
                return(false);
            }
            for (int i = 0; i < N; ++i)
            {
                if (Mesh.IsVertex(i))
                {
                    Mesh.SetVertex(i, Result[i]);
                }
            }
            return(true);
        }
Example #13
0
        protected virtual void InitializeVertexQuadrics()
        {
            int NT = mesh.MaxTriangleID;

            QuadricError[] triQuadrics = new QuadricError[NT];
            double[]       triAreas    = new double[NT];
            gParallel.BlockStartEnd(0, mesh.MaxTriangleID - 1, (start_tid, end_tid) => {
                Vector3d c, n;
                for (int tid = start_tid; tid <= end_tid; tid++)
                {
                    if (mesh.IsTriangle(tid))
                    {
                        mesh.GetTriInfo(tid, out n, out triAreas[tid], out c);
                        triQuadrics[tid] = new QuadricError(ref n, ref c);
                    }
                }
            });


            int NV = mesh.MaxVertexID;

            vertQuadrics = new QuadricError[NV];
            gParallel.BlockStartEnd(0, mesh.MaxVertexID - 1, (start_vid, end_vid) => {
                for (int vid = start_vid; vid <= end_vid; vid++)
                {
                    vertQuadrics[vid] = QuadricError.Zero;
                    if (mesh.IsVertex(vid))
                    {
                        foreach (int tid in mesh.VtxTrianglesItr(vid))
                        {
                            vertQuadrics[vid].Add(triAreas[tid], ref triQuadrics[tid]);
                        }
                        //Util.gDevAssert(MathUtil.EpsilonEqual(0, vertQuadrics[i].Evaluate(mesh.GetVertex(i)), MathUtil.Epsilon * 10));
                    }
                }
            });
        }
        public static ValidationStatus IsEdgeLoop(DMesh3 mesh, EdgeLoop loop)
        {
            int N = loop.Vertices.Length;

            for (int i = 0; i < N; ++i)
            {
                if (!mesh.IsVertex(loop.Vertices[i]))
                {
                    return(ValidationStatus.NotAVertex);
                }
            }
            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);
                }
            }
            return(ValidationStatus.Ok);
        }
Example #15
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()
Example #16
0
        public virtual bool Extrude()
        {
            MeshNormals normals      = null;
            bool        bHaveNormals = Mesh.HasVertexNormals;

            if (!bHaveNormals)
            {
                normals = new MeshNormals(Mesh);
                normals.Compute();
            }

            InitialLoops     = new MeshBoundaryLoops(Mesh);
            InitialTriangles = Mesh.TriangleIndices().ToArray();
            InitialVertices  = Mesh.VertexIndices().ToArray();

            // duplicate triangles of mesh
            InitialToOffsetMapV = new IndexMap(Mesh.MaxVertexID, Mesh.MaxVertexID);
            OffsetGroupID       = OffsetGroup.GetGroupID(Mesh);
            var editor = new MeshEditor(Mesh);

            OffsetTriangles = editor.DuplicateTriangles(InitialTriangles, ref InitialToOffsetMapV, OffsetGroupID);

            // set vertices to new positions
            foreach (int vid in InitialVertices)
            {
                int newvid = InitialToOffsetMapV[vid];
                if (!Mesh.IsVertex(newvid))
                {
                    continue;
                }

                Vector3d v    = Mesh.GetVertex(vid);
                Vector3f n    = (bHaveNormals) ? Mesh.GetVertexNormal(vid) : (Vector3f)normals.Normals[vid];
                Vector3d newv = ExtrudedPositionF(v, n, vid);

                Mesh.SetVertex(newvid, newv);
            }

            // we need to reverse one side
            if (IsPositiveOffset)
            {
                editor.ReverseTriangles(InitialTriangles);
            }
            else
            {
                editor.ReverseTriangles(OffsetTriangles);
            }

            // stitch each loop
            NewLoops        = new EdgeLoop[InitialLoops.Count];
            StitchTriangles = new int[InitialLoops.Count][];
            StitchGroupIDs  = new int[InitialLoops.Count];
            int li = 0;

            foreach (var loop in InitialLoops)
            {
                int[] loop2 = new int[loop.VertexCount];
                for (int k = 0; k < loop2.Length; ++k)
                {
                    loop2[k] = InitialToOffsetMapV[loop.Vertices[k]];
                }

                StitchGroupIDs[li] = StitchGroups.GetGroupID(Mesh);
                if (IsPositiveOffset)
                {
                    StitchTriangles[li] = editor.StitchLoop(loop2, loop.Vertices, StitchGroupIDs[li]);
                }
                else
                {
                    StitchTriangles[li] = editor.StitchLoop(loop.Vertices, loop2, StitchGroupIDs[li]);
                }
                NewLoops[li] = EdgeLoop.FromVertices(Mesh, loop2);
                li++;
            }

            return(true);
        }
        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()
Example #18
0
 /// <summary>
 /// Check if this m2 is the same as this mesh. By default only checks
 /// vertices and triangles, turn on other parameters w/ flags
 /// </summary>
 public bool IsSameMesh(DMesh3 m2, bool bCheckEdges = false,
                        bool bCheckNormals          = false, bool bCheckColors = false, bool bCheckUVs = false,
                        bool bCheckGroups           = false,
                        float Epsilon = MathUtil.Epsilonf)
 {
     if (VertexCount != m2.VertexCount)
     {
         return(false);
     }
     if (TriangleCount != m2.TriangleCount)
     {
         return(false);
     }
     foreach (int vid in VertexIndices())
     {
         if (m2.IsVertex(vid) == false || GetVertex(vid).EpsilonEqual(m2.GetVertex(vid), Epsilon) == false)
         {
             return(false);
         }
     }
     foreach (int tid in TriangleIndices())
     {
         if (m2.IsTriangle(tid) == false || GetTriangle(tid).Equals(m2.GetTriangle(tid)) == false)
         {
             return(false);
         }
     }
     if (bCheckEdges)
     {
         if (EdgeCount != m2.EdgeCount)
         {
             return(false);
         }
         foreach (int eid in EdgeIndices())
         {
             if (m2.IsEdge(eid) == false || GetEdge(eid).Equals(m2.GetEdge(eid)) == false)
             {
                 return(false);
             }
         }
     }
     if (bCheckNormals)
     {
         if (HasVertexNormals != m2.HasVertexNormals)
         {
             return(false);
         }
         if (HasVertexNormals)
         {
             foreach (int vid in VertexIndices())
             {
                 if (GetVertexNormal(vid).EpsilonEqual(m2.GetVertexNormal(vid), Epsilon) == false)
                 {
                     return(false);
                 }
             }
         }
     }
     if (bCheckColors)
     {
         if (HasVertexColors != m2.HasVertexColors)
         {
             return(false);
         }
         if (HasVertexColors)
         {
             foreach (int vid in VertexIndices())
             {
                 if (GetVertexColor(vid).EpsilonEqual(m2.GetVertexColor(vid), Epsilon) == false)
                 {
                     return(false);
                 }
             }
         }
     }
     if (bCheckUVs)
     {
         if (HasVertexUVs != m2.HasVertexUVs)
         {
             return(false);
         }
         if (HasVertexUVs)
         {
             foreach (int vid in VertexIndices())
             {
                 if (GetVertexUV(vid).EpsilonEqual(m2.GetVertexUV(vid), Epsilon) == false)
                 {
                     return(false);
                 }
             }
         }
     }
     if (bCheckGroups)
     {
         if (HasTriangleGroups != m2.HasTriangleGroups)
         {
             return(false);
         }
         if (HasTriangleGroups)
         {
             foreach (int tid in TriangleIndices())
             {
                 if (GetTriangleGroup(tid) != m2.GetTriangleGroup(tid))
                 {
                     return(false);
                 }
             }
         }
     }
     return(true);
 }
Example #19
0
        private void Remove(TriangleRemoval rem = TriangleRemoval.contained)
        {
#if ACAD
            var lastColor = 0;
#endif

            DMeshAABBTree3 spatial = new DMeshAABBTree3(CutMesh, true);
            spatial.WindingNumber(Vector3d.Zero);
            SafeListBuilder <int> containedT    = new SafeListBuilder <int>();
            SafeListBuilder <int> removeAnywayT = new SafeListBuilder <int>();

            // if the windinging number for the centroid point candidate triangles
            // is one or more (or close for safety), then it's inside the volume of cutMesh
            //
            gParallel.ForEach(Target.TriangleIndices(), (tid) =>
            {
                if (Target.GetTriArea(tid) < VertexSnapTol)
                {
                    removeAnywayT.SafeAdd(tid);
                    return; // parallel: equivalent to continue.
                }
                Vector3d v = Target.GetTriCentroid(tid);
                if (AttemptPlanarRemoval)
                {
                    // slightly offset the point to be evaluated.
                    //
                    var nrm = Target.GetTriNormal(tid);
                    v      -= nrm * 5 * VertexSnapTol;
                }

                var winding     = spatial.WindingNumber(v);
                bool IsInternal = winding > 0.9;
#if ACAD
                // temporarily here for debug purposes
                var wantColor = IsInternal ? 1 : 2;
                if (lastColor != wantColor)
                {
                    Debug.WriteLine($"-LAYER set L{wantColor}");
                    Debug.WriteLine($"");
                    lastColor = wantColor;
                }
                Triangle3d tri = new Triangle3d();
                Target.GetTriVertices(tid, ref tri.V0, ref tri.V1, ref tri.V2);
                Debug.WriteLine($"3DPOLY {tri.V0.CommaDelimited} {tri.V1.CommaDelimited} {tri.V2.CommaDelimited} {tri.V0.CommaDelimited} {v.CommaDelimited} ");
#endif
                if (IsInternal)
                {
                    containedT.SafeAdd(tid);
                }
            });
            if (rem == TriangleRemoval.contained)
            {
                MeshEditor.RemoveTriangles(Target, containedT.Result);
            }
            else if (rem == TriangleRemoval.external)
            {
                var ext = Target.TriangleIndices().Except(containedT.Result);
                MeshEditor.RemoveTriangles(Target, ext);
            }

            MeshEditor.RemoveTriangles(Target, removeAnywayT.Result);

            // [RMS] construct set of on-cut vertices? This is not
            // necessarily all boundary vertices...
            CutVertices = new List <int>();
            foreach (int vid in SegmentInsertVertices)
            {
                if (Target.IsVertex(vid))
                {
                    CutVertices.Add(vid);
                }
            }
        }
Example #20
0
        // Assumption here is that Submesh has been modified, but boundary loop has
        // been preserved, and that old submesh has already been removed from this mesh.
        // So, we just have to append new vertices and then rewrite triangles
        // If new_tris or new_verts is non-null, we will return this info.
        // new_tris should be set to TriangleCount (ie it is not necessarily a map)
        // For new_verts, if we used an existing bdry vtx instead, we set the value to -(existing_index+1),
        // otherwise the value is new_index (+1 is to handle 0)
        //
        // Returns true if submesh successfully inserted, false if any triangles failed
        // (which happens if triangle would result in non-manifold mesh)
        public bool ReinsertSubmesh(DSubmesh3 sub, ref int[] new_tris, out IndexMap SubToNewV)
        {
            if (sub.BaseBorderV == null)
            {
                throw new Exception("MeshEditor.ReinsertSubmesh: Submesh does not have required boundary info. Call ComputeBoundaryInfo()!");
            }

            DMesh3 submesh = sub.SubMesh;
            bool   bAllOK  = true;

            IndexFlagSet done_v = new IndexFlagSet(submesh.MaxVertexID, submesh.TriangleCount / 2);

            SubToNewV = new IndexMap(submesh.MaxVertexID, submesh.VertexCount);

            int nti = 0;
            int NT  = submesh.MaxTriangleID;

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

                Index3i sub_t = submesh.GetTriangle(ti);
                int     gid   = submesh.GetTriangleGroup(ti);

                Index3i new_t = Index3i.Zero;
                for (int j = 0; j < 3; ++j)
                {
                    int sub_v = sub_t[j];
                    int new_v = -1;
                    if (done_v[sub_v] == false)
                    {
                        // first check if this is a boundary vtx on submesh and maps to a bdry vtx on base mesh
                        if (submesh.vertex_is_boundary(sub_v))
                        {
                            int base_v = (sub_v < sub.SubToBaseV.size) ? sub.SubToBaseV[sub_v] : -1;
                            if (base_v >= 0 && Mesh.IsVertex(base_v) && sub.BaseBorderV[base_v] == true)
                            {
                                // [RMS] this should always be true, but assert in tests to find out
                                Debug.Assert(Mesh.vertex_is_boundary(base_v));
                                if (Mesh.vertex_is_boundary(base_v))
                                {
                                    new_v = base_v;
                                }
                            }
                        }

                        // if that didn't happen, append new vtx
                        if (new_v == -1)
                        {
                            new_v = Mesh.AppendVertex(submesh, sub_v);
                        }

                        SubToNewV[sub_v] = new_v;
                        done_v[sub_v]    = true;
                    }
                    else
                    {
                        new_v = SubToNewV[sub_v];
                    }

                    new_t[j] = new_v;
                }

                Debug.Assert(Mesh.FindTriangle(new_t.a, new_t.b, new_t.c) == DMesh3.InvalidID);

                int new_tid = Mesh.AppendTriangle(new_t, gid);
                Debug.Assert(new_tid != DMesh3.InvalidID && new_tid != DMesh3.NonManifoldID);
                if (!Mesh.IsTriangle(new_tid))
                {
                    bAllOK = false;
                }

                if (new_tris != null)
                {
                    new_tris[nti++] = new_tid;
                }
            }

            return(bAllOK);
        }
Example #21
0
        /// <summary>
        /// Check if this m2 is the same as this mesh. By default only checks
        /// vertices and triangles, turn on other parameters w/ flags
        /// </summary>
        public bool IsSameMesh(DMesh3 m2, bool bCheckConnectivity, bool bCheckEdgeIDs = false,
                               bool bCheckNormals = false, bool bCheckColors = false, bool bCheckUVs = false,
                               bool bCheckGroups  = false,
                               float Epsilon      = MathUtil.Epsilonf)
        {
            if (VertexCount != m2.VertexCount)
            {
                return(false);
            }

            if (TriangleCount != m2.TriangleCount)
            {
                return(false);
            }

            foreach (int vid in VertexIndices())
            {
                if (m2.IsVertex(vid) == false || GetVertex(vid).EpsilonEqual(m2.GetVertex(vid), Epsilon) == false)
                {
                    return(false);
                }
            }
            foreach (int tid in TriangleIndices())
            {
                if (m2.IsTriangle(tid) == false || GetTriangle(tid).Equals(m2.GetTriangle(tid)) == false)
                {
                    return(false);
                }
            }
            if (bCheckConnectivity)
            {
                foreach (int eid in EdgeIndices())
                {
                    Index4i e         = GetEdge(eid);
                    int     other_eid = m2.FindEdge(e.a, e.b);
                    if (other_eid == InvalidID)
                    {
                        return(false);
                    }

                    Index4i oe = m2.GetEdge(other_eid);
                    if (Math.Min(e.c, e.d) != Math.Min(oe.c, oe.d) || Math.Max(e.c, e.d) != Math.Max(oe.c, oe.d))
                    {
                        return(false);
                    }
                }
            }
            if (bCheckEdgeIDs)
            {
                if (EdgeCount != m2.EdgeCount)
                {
                    return(false);
                }

                foreach (int eid in EdgeIndices())
                {
                    if (m2.IsEdge(eid) == false || GetEdge(eid).Equals(m2.GetEdge(eid)) == false)
                    {
                        return(false);
                    }
                }
            }
            if (bCheckNormals)
            {
                if (HasVertexNormals != m2.HasVertexNormals)
                {
                    return(false);
                }

                if (HasVertexNormals)
                {
                    foreach (int vid in VertexIndices())
                    {
                        if (GetVertexNormal(vid).EpsilonEqual(m2.GetVertexNormal(vid), Epsilon) == false)
                        {
                            return(false);
                        }
                    }
                }
            }
            if (bCheckColors)
            {
                if (HasVertexColors != m2.HasVertexColors)
                {
                    return(false);
                }

                if (HasVertexColors)
                {
                    foreach (int vid in VertexIndices())
                    {
                        if (GetVertexColor(vid).EpsilonEqual(m2.GetVertexColor(vid), Epsilon) == false)
                        {
                            return(false);
                        }
                    }
                }
            }
            if (bCheckUVs)
            {
                if (HasVertexUVs != m2.HasVertexUVs)
                {
                    return(false);
                }

                if (HasVertexUVs)
                {
                    foreach (int vid in VertexIndices())
                    {
                        if (GetVertexUV(vid).EpsilonEqual(m2.GetVertexUV(vid), Epsilon) == false)
                        {
                            return(false);
                        }
                    }
                }
            }
            if (bCheckGroups)
            {
                if (HasTriangleGroups != m2.HasTriangleGroups)
                {
                    return(false);
                }

                if (HasTriangleGroups)
                {
                    foreach (int tid in TriangleIndices())
                    {
                        if (GetTriangleGroup(tid) != m2.GetTriangleGroup(tid))
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }
Example #22
0
        public virtual bool Cut()
        {
            double invalidDist = double.MinValue;

            MeshEdgeSelection   CutEdgeSet   = null;
            MeshVertexSelection CutVertexSet = null;

            if (CutFaceSet != null)
            {
                CutEdgeSet   = new MeshEdgeSelection(Mesh, CutFaceSet);
                CutVertexSet = new MeshVertexSelection(Mesh, CutEdgeSet);
            }

            // compute signs
            int MaxVID = Mesh.MaxVertexID;

            double[] signs = new double[MaxVID];
            gParallel.ForEach(Interval1i.Range(MaxVID), (vid) => {
                if (Mesh.IsVertex(vid))
                {
                    Vector3d v = Mesh.GetVertex(vid);
                    signs[vid] = (v - PlaneOrigin).Dot(PlaneNormal);
                }
                else
                {
                    signs[vid] = invalidDist;
                }
            });

            HashSet <int> ZeroEdges    = new HashSet <int>();
            HashSet <int> ZeroVertices = new HashSet <int>();
            HashSet <int> OnCutEdges   = new HashSet <int>();

            // have to skip processing of new edges. If edge id
            // is > max at start, is new. Otherwise if in NewEdges list, also new.
            int           MaxEID   = Mesh.MaxEdgeID;
            HashSet <int> NewEdges = new HashSet <int>();

            IEnumerable <int> edgeItr = Interval1i.Range(MaxEID);

            if (CutEdgeSet != null)
            {
                edgeItr = CutEdgeSet;
            }

            // cut existing edges with plane, using edge split
            foreach (int eid in edgeItr)
            {
                if (Mesh.IsEdge(eid) == false)
                {
                    continue;
                }
                if (eid >= MaxEID || NewEdges.Contains(eid))
                {
                    continue;
                }

                Index2i ev = Mesh.GetEdgeV(eid);
                double  f0 = signs[ev.a];
                double  f1 = signs[ev.b];

                // If both signs are 0, this edge is on-contour
                // If one sign is 0, that vertex is on-contour
                int n0 = (Math.Abs(f0) < MathUtil.Epsilon) ? 1 : 0;
                int n1 = (Math.Abs(f1) < MathUtil.Epsilon) ? 1 : 0;
                if (n0 + n1 > 0)
                {
                    if (n0 + n1 == 2)
                    {
                        ZeroEdges.Add(eid);
                    }
                    else
                    {
                        ZeroVertices.Add((n0 == 1) ? ev[0] : ev[1]);
                    }
                    continue;
                }

                // no crossing
                if (f0 * f1 > 0)
                {
                    continue;
                }

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

                // SplitEdge just bisects edge - use plane intersection instead
                double   t      = f0 / (f0 - f1);
                Vector3d newPos = (1 - t) * Mesh.GetVertex(ev.a) + (t) * Mesh.GetVertex(ev.b);
                Mesh.SetVertex(splitInfo.vNew, newPos);

                NewEdges.Add(splitInfo.eNewBN);
                NewEdges.Add(splitInfo.eNewCN);  OnCutEdges.Add(splitInfo.eNewCN);
                if (splitInfo.eNewDN != DMesh3.InvalidID)
                {
                    NewEdges.Add(splitInfo.eNewDN);
                    OnCutEdges.Add(splitInfo.eNewDN);
                }
            }

            // remove one-rings of all positive-side vertices.
            IEnumerable <int> vertexSet = Interval1i.Range(MaxVID);

            if (CutVertexSet != null)
            {
                vertexSet = CutVertexSet;
            }
            foreach (int vid in vertexSet)
            {
                if (signs[vid] > 0 && Mesh.IsVertex(vid))
                {
                    Mesh.RemoveVertex(vid, true, false);
                }
            }

            // ok now we extract boundary loops, but restricted
            // to either the zero-edges we found, or the edges we created! bang!!
            Func <int, bool> CutEdgeFilterF = (eid) => {
                if (OnCutEdges.Contains(eid) || ZeroEdges.Contains(eid))
                {
                    return(true);
                }
                return(false);
            };

            try {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, false);
                loops.EdgeFilterF = CutEdgeFilterF;
                loops.Compute();

                CutLoops       = loops.Loops;
                CutSpans       = loops.Spans;
                CutLoopsFailed = false;
                FoundOpenSpans = CutSpans.Count > 0;
            } catch {
                CutLoops       = new List <EdgeLoop>();
                CutLoopsFailed = true;
            }

            return(true);
        }         // Cut()
        /// <summary>
        /// Construct vertex correspondences between fill mesh boundary loop
        /// and input mesh boundary loop. In ideal case there is an easy 1-1
        /// correspondence. If that is not true, then do a brute-force search
        /// to find the best correspondences we can.
        ///
        /// Currently only returns unique correspondences. If any vertex
        /// matches with multiple input vertices it is not merged.
        /// [TODO] we could do better in many cases...
        ///
        /// Return value is list of indices into fillLoopV that were not merged
        /// </summary>
        List <int> build_merge_map(DMesh3 fillMesh, int[] fillLoopV,
                                   DMesh3 targetMesh, int[] targetLoopV,
                                   double tol, IndexMap mergeMapV)
        {
            if (fillLoopV.Length == targetLoopV.Length)
            {
                if (build_merge_map_simple(fillMesh, fillLoopV, targetMesh, targetLoopV, tol, mergeMapV))
                {
                    return(null);
                }
            }

            int NF = fillLoopV.Length, NT = targetLoopV.Length;

            bool[] doneF  = new bool[NF], doneT = new bool[NT];
            int[]  countF = new int[NF], countT = new int[NT];
            var    errorV = new List <int>();

            var matchF = new SmallListSet(); matchF.Resize(NF);

            // find correspondences
            double tol_sqr = tol * tol;

            for (int i = 0; i < NF; ++i)
            {
                if (fillMesh.IsVertex(fillLoopV[i]) == false)
                {
                    doneF[i] = true;
                    errorV.Add(i);
                    continue;
                }
                matchF.AllocateAt(i);
                Vector3d v = fillMesh.GetVertex(fillLoopV[i]);
                for (int j = 0; j < NT; ++j)
                {
                    Vector3d v2 = targetMesh.GetVertex(targetLoopV[j]);
                    if (v.DistanceSquared(ref v2) < tol_sqr)
                    {
                        matchF.Insert(i, j);
                    }
                }
            }

            for (int i = 0; i < NF; ++i)
            {
                if (doneF[i])
                {
                    continue;
                }

                if (matchF.Count(i) == 1)
                {
                    int j = matchF.First(i);
                    mergeMapV[fillLoopV[i]] = targetLoopV[j];
                    doneF[i] = true;
                }
            }

            for (int i = 0; i < NF; ++i)
            {
                if (doneF[i] == false)
                {
                    errorV.Add(i);
                }
            }

            return(errorV);
        }