public void AddFillLoop(EdgeLoop loop)
 {
     Loops.Add(new FillLoop()
     {
         edgeLoop = loop
     });
 }
Example #2
0
        // This function does local remeshing around a boundary loop within a fixed # of
        // rings, to try to 'massage' it into a cleaner shape/topology
        // [TODO] use geodesic distance instead of fixed # of rings?
        public static void cleanup_boundary(DMesh3 mesh, EdgeLoop loop, double target_edge_len, int nRings = 3)
        {
            Debug.Assert(loop.IsBoundaryLoop());

            MeshFaceSelection roi = new MeshFaceSelection(mesh);

            roi.SelectVertexOneRings(loop.Vertices);

            for (int i = 0; i < nRings; ++i)
            {
                roi.ExpandToOneRingNeighbours();
            }
            roi.LocalOptimize(true, true);

            RegionRemesher r = new RegionRemesher(mesh, roi.ToArray());

            r.Precompute();
            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = target_edge_len;
            r.MaxEdgeLength   = 2 * target_edge_len;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 0.1f;
            for (int k = 0; k < nRings * 3; ++k)
            {
                r.BasicRemeshPass();
            }
            Debug.Assert(mesh.CheckValidity());

            r.BackPropropagate();
        }
Example #3
0
        // smooths embedded loop in mesh, by first smoothing edge loop and then
        // smoothing vertex neighbourhood
        // [TODO] geodesic nbrhoold instead of # of rings
        // [TODO] reprojection?
        public static void smooth_loop(DMesh3 mesh, EdgeLoop loop, int nRings)
        {
            MeshFaceSelection roi_t = new MeshFaceSelection(mesh);

            roi_t.SelectVertexOneRings(loop.Vertices);
            for (int i = 0; i < nRings; ++i)
            {
                roi_t.ExpandToOneRingNeighbours();
            }
            roi_t.LocalOptimize(true, true);

            MeshVertexSelection roi_v = new MeshVertexSelection(mesh);

            roi_v.SelectTriangleVertices(roi_t.ToArray());
            roi_v.Deselect(loop.Vertices);

            MeshLoopSmooth loop_smooth = new MeshLoopSmooth(mesh, loop);

            loop_smooth.Rounds = 1;

            MeshIterativeSmooth mesh_smooth = new MeshIterativeSmooth(mesh, roi_v.ToArray(), true);

            mesh_smooth.Rounds = 1;

            for (int i = 0; i < 10; ++i)
            {
                loop_smooth.Smooth();
                mesh_smooth.Smooth();
            }
        }
Example #4
0
 public void UpdateLoop(EdgeLoop loop)
 {
     InputLoop    = loop;
     OutputLoop   = null;
     CurrentLoopE = new List <int>(loop.Edges);
     CurrentLoopV = new List <int>(loop.Vertices);
 }
Example #5
0
        public virtual bool Extrude(int group_id = -1)
        {
            // duplicate loop vertices
            int NV = Loop.Vertices.Length;

            NewLoop          = new EdgeLoop(Mesh);
            NewLoop.Vertices = new int[NV];

            for (int i = 0; i < NV; ++i)
            {
                int vid = Loop.Vertices[i];
                NewLoop.Vertices[i] = Mesh.AppendVertex(Mesh, vid);
            }

            // move to offset positions
            for (int i = 0; i < NV; ++i)
            {
                Vector3d v     = Mesh.GetVertex(Loop.Vertices[i]);
                Vector3f n     = Mesh.GetVertexNormal(Loop.Vertices[i]);
                Vector3d new_v = PositionF(v, n, i);
                Mesh.SetVertex(NewLoop.Vertices[i], new_v);
            }

            // stitch interior
            MeshEditor edit = new MeshEditor(Mesh);

            NewTriangles = edit.StitchLoop(Loop.Vertices, NewLoop.Vertices, group_id);

            return(true);
        }
        public SimpleHoleFiller(DMesh3 mesh, EdgeLoop loop)
        {
            Mesh = mesh;
            Loop = loop;

            NewVertex    = DMesh3.InvalidID;
            NewTriangles = null;
        }
Example #7
0
        public EdgeLoopRemesher(DMesh3 m, EdgeLoop loop) : base(m)
        {
            UpdateLoop(loop);

            EnableFlips = false;

            CustomSmoothF = loop_smooth_vertex;
        }
 /// <summary>
 /// Generally after calling Apply(), we have over-triangulated the mesh, because we have split
 /// the original edges multiple times, etc. This function will walk the edges and collapse
 /// the unnecessary edges/vertices along the inserted loops.
 /// </summary>
 public void Simplify()
 {
     for (int k = 0; k < Loops.Count; ++k)
     {
         EdgeLoop newloop = simplify(Loops[k]);
         Loops[k] = newloop;
     }
 }
Example #9
0
        public MeshLoopSmooth(DMesh3 mesh, EdgeLoop loop)
        {
            Mesh = mesh;
            Loop = loop;

            SmoothedPostions = new Vector3d[Loop.Vertices.Length];

            ProjectF = null;
        }
Example #10
0
        public MeshExtrudeLoop(DMesh3 mesh, EdgeLoop loop)
        {
            Mesh = mesh;
            Loop = loop;

            PositionF = (pos, normal, idx) => {
                return(pos + Vector3d.AxisY);
            };
        }
Example #11
0
 public EdgeLoop(EdgeLoop copy)
 {
     Mesh     = copy.Mesh;
     Vertices = new int[copy.Vertices.Length];
     Array.Copy(copy.Vertices, Vertices, Vertices.Length);
     Edges = new int[copy.Edges.Length];
     Array.Copy(copy.Edges, Edges, Edges.Length);
     BowtieVertices = new int[copy.BowtieVertices.Length];
     Array.Copy(copy.BowtieVertices, BowtieVertices, BowtieVertices.Length);
 }
Example #12
0
        void find_cut_paths(HashSet <int> CutEdges)
        {
            Spans = new List <EdgeSpan>();
            Loops = new List <EdgeLoop>();

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

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

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

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

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

                if (isLoop)
                {
                    var loop = EdgeLoop.FromEdges(Mesh, forwardSpan);
                    Util.gDevAssert(loop.CheckValidity());
                    Loops.Add(loop);
                }
                else
                {
                    var span = EdgeSpan.FromEdges(Mesh, forwardSpan);
                    Util.gDevAssert(span.CheckValidity());
                    Spans.Add(span);
                }
            }
        }
Example #13
0
        // Check if Loop2 is the same set of positions on another mesh.
        // Does not require the indexing to be the same
        // Currently doesn't handle loop-reversal
        public bool IsSameLoop(EdgeLoop Loop2, bool bReverse2 = false, double tolerance = MathUtil.ZeroTolerance)
        {
            // find a duplicate starting vertex
            int N  = Vertices.Length;
            int N2 = Loop2.Vertices.Length;

            if (N != N2)
            {
                return(false);
            }

            DMesh3 Mesh2 = Loop2.Mesh;

            int start_i = 0, start_j = -1;

            // try to find a unique same-vertex on each loop. Do not
            // use vertices that have duplicate positions.
            bool bFoundGoodStart = false;

            while (!bFoundGoodStart && start_i < N)
            {
                Vector3d start_v = Mesh.GetVertex(start_i);
                int      count   = Loop2.CountWithinTolerance(start_v, tolerance, out start_j);
                if (count == 1)
                {
                    bFoundGoodStart = true;
                }
                else
                {
                    start_i++;
                }
            }
            if (!bFoundGoodStart)
            {
                return(false);                      // no within-tolerance duplicate vtx to start at
            }

            for (int ii = 0; ii < N; ++ii)
            {
                int i = (start_i + ii) % N;
                int j = (bReverse2) ?
                        MathUtil.WrapSignedIndex(start_j - ii, N2)
                                        : (start_j + ii) % N2;
                Vector3d v  = Mesh.GetVertex(Vertices[i]);
                Vector3d v2 = Mesh2.GetVertex(Loop2.Vertices[j]);
                if (v.Distance(v2) > tolerance)
                {
                    return(false);
                }
            }

            return(true);
        }
Example #14
0
        // This function does local remeshing around a boundary loop within a fixed # of
        // rings, to try to 'massage' it into a cleaner shape/topology.
        // The result_edges list is the mapped edges of loop on the resulting mesh, but it is *not* in-order
        // [TODO] use geodesic distance instead of fixed # of rings?
        public static void cleanup_boundary(DMesh3 mesh, EdgeLoop loop, double target_edge_len, out List <int> result_edges, int nRings = 3)
        {
            Debug.Assert(loop.IsBoundaryLoop());

            var roi = new MeshFaceSelection(mesh);

            roi.SelectVertexOneRings(loop.Vertices);

            for (int i = 0; i < nRings; ++i)
            {
                roi.ExpandToOneRingNeighbours();
            }

            roi.LocalOptimize(true, true);

            var r = new RegionRemesher(mesh, roi.ToArray());

            // tag the input loop edges in the remesher, so that we can find this loop afterwards
            int[] init_loop_edges = new int[loop.EdgeCount];
            Array.Copy(loop.Edges, init_loop_edges, loop.EdgeCount);
            r.Region.MapEdgesToSubmesh(init_loop_edges);
            MeshConstraintUtil.AddTrackedEdges(r.Constraints, init_loop_edges, 100);
            //foreach (int eid in init_loop_edges)
            //    Debug.Assert(r.Region.SubMesh.IsBoundaryEdge(eid));

            r.Precompute();
            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = target_edge_len;
            r.MaxEdgeLength   = 2 * target_edge_len;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 0.1f;
            for (int k = 0; k < nRings * 3; ++k)
            {
                r.BasicRemeshPass();
            }

            Debug.Assert(mesh.CheckValidity());

            // extract the edges we tagged (they are unordered)
            List <int> new_loop_edges = r.Constraints.FindConstrainedEdgesBySetID(100);

            //foreach (int eid in new_loop_edges)
            //    Debug.Assert(r.Region.SubMesh.IsBoundaryEdge(eid));

            r.BackPropropagate();

            // map the extracted edges back to the backpropped input mesh
            result_edges = MeshIndexUtil.MapEdgesViaVertexMap(r.ReinsertSubToBaseMapV, r.Region.SubMesh, r.BaseMesh, new_loop_edges);
            //foreach (int eid in result_edges)
            //    Debug.Assert(mesh.IsBoundaryEdge(eid));
        }
        void compute_polygons()
        {
            Bounds = AxisAlignedBox2d.Empty;
            for (int i = 0; i < Loops.Count; ++i)
            {
                EdgeLoop  loop = Loops[i].edgeLoop;
                Polygon2d poly = new Polygon2d();

                foreach (int vid in loop.Vertices)
                {
                    Vector2d v = to2D(Mesh.GetVertex(vid));
                    poly.AppendVertex(v);
                }

                Loops[i].poly = poly;
                Bounds.Contain(poly.Bounds);
            }
        }
        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.edge_is_boundary(eid) == false)
                {
                    return(ValidationStatus.NotBoundaryEdge);
                }

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

            return(ValidationStatus.Ok);
        }
        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);
        }
        public bool Fill()
        {
            compute_polygons();

            // translate/scale fill loops to unit box. This will improve
            // accuracy in the calcs below...
            Vector2d shiftOrigin = Bounds.Center;
            double   scale       = 1.0 / Bounds.MaxDim;

            foreach (var floop in Loops)
            {
                floop.poly.Translate(-shiftOrigin);
                floop.poly.Scale(scale * Vector2d.One, Vector2d.Zero);
            }

            Dictionary <PlanarComplex.Element, int> ElemToLoopMap = new Dictionary <PlanarComplex.Element, int>();

            // [TODO] if we have multiple components in input mesh, we could do this per-component.
            // This also helps avoid nested shells creating holes.
            // *However*, we shouldn't *have* to because FindSolidRegions will do the right thing if
            // the polygons have the same orientation

            // add all loops to planar complex
            PlanarComplex complex = new PlanarComplex();

            for (int i = 0; i < Loops.Count; ++i)
            {
                var elem = complex.Add(Loops[i].poly);
                ElemToLoopMap[elem] = i;
            }

            // sort into separate 2d solids
            PlanarComplex.SolidRegionInfo solids =
                complex.FindSolidRegions(PlanarComplex.FindSolidsOptions.SortPolygons);

            // fill each 2d solid
            List <Index2i> failed_inserts = new List <Index2i>();
            List <Index2i> failed_merges  = new List <Index2i>();

            for (int fi = 0; fi < solids.Polygons.Count; ++fi)
            {
                var gpoly = solids.Polygons[fi];
                PlanarComplex.GeneralSolid gsolid = solids.PolygonsSources[fi];

                // [TODO] could do scale/translate here, per-polygon would be more precise

                // generate planar mesh that we will insert polygons into
                MeshGenerator meshgen;
                float         planeW     = 1.5f;
                int           nDivisions = 0;
                if (FillTargetEdgeLen < double.MaxValue && FillTargetEdgeLen > 0)
                {
                    int n = (int)((planeW / (float)scale) / FillTargetEdgeLen) + 1;
                    nDivisions = (n <= 1) ? 0 : n;
                }

                if (nDivisions == 0)
                {
                    meshgen = new TrivialRectGenerator()
                    {
                        IndicesMap = new Index2i(1, 2), Width = planeW, Height = planeW,
                    };
                }
                else
                {
                    meshgen = new GriddedRectGenerator()
                    {
                        IndicesMap   = new Index2i(1, 2), Width = planeW, Height = planeW,
                        EdgeVertices = nDivisions
                    };
                }
                DMesh3 FillMesh = meshgen.Generate().MakeDMesh();
                FillMesh.ReverseOrientation();   // why?!?

                // convenient list
                List <Polygon2d> polys = new List <Polygon2d>()
                {
                    gpoly.Outer
                };
                polys.AddRange(gpoly.Holes);

                // for each poly, we track the set of vertices inserted into mesh
                int[][] polyVertices = new int[polys.Count][];

                // insert each poly
                for (int pi = 0; pi < polys.Count; ++pi)
                {
                    MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(FillMesh, polys[pi]);
                    ValidationStatus      status = insert.Validate(MathUtil.ZeroTolerancef * scale);
                    bool failed = true;
                    if (status == ValidationStatus.Ok)
                    {
                        if (insert.Apply())
                        {
                            insert.Simplify();
                            polyVertices[pi] = insert.CurveVertices;
                            failed           = (insert.Loops.Count != 1) ||
                                               (insert.Loops[0].VertexCount != polys[pi].VertexCount);
                        }
                    }
                    if (failed)
                    {
                        failed_inserts.Add(new Index2i(fi, pi));
                    }
                }

                // remove any triangles not contained in gpoly
                // [TODO] degenerate triangle handling? may be 'on' edge of gpoly...
                List <int> removeT = new List <int>();
                foreach (int tid in FillMesh.TriangleIndices())
                {
                    Vector3d v = FillMesh.GetTriCentroid(tid);
                    if (gpoly.Contains(v.xy) == false)
                    {
                        removeT.Add(tid);
                    }
                }
                foreach (int tid in removeT)
                {
                    FillMesh.RemoveTriangle(tid, true, false);
                }

                //Util.WriteDebugMesh(FillMesh, "c:\\scratch\\CLIPPED_MESH.obj");

                // transform fill mesh back to 3d
                MeshTransforms.PerVertexTransform(FillMesh, (v) => {
                    Vector2d v2 = v.xy;
                    v2         /= scale;
                    v2         += shiftOrigin;
                    return(to3D(v2));
                });


                //Util.WriteDebugMesh(FillMesh, "c:\\scratch\\PLANAR_MESH_WITH_LOOPS.obj");
                //Util.WriteDebugMesh(MeshEditor.Combine(FillMesh, Mesh), "c:\\scratch\\FILLED_MESH.obj");

                // figure out map between new mesh and original edge loops
                // [TODO] if # of verts is different, we can still find correspondence, it is just harder
                // [TODO] should check that edges (ie sequential verts) are boundary edges on fill mesh
                //    if not, can try to delete nbr tris to repair
                IndexMap mergeMapV = new IndexMap(true);
                if (MergeFillBoundary)
                {
                    for (int pi = 0; pi < polys.Count; ++pi)
                    {
                        if (polyVertices[pi] == null)
                        {
                            continue;
                        }
                        int[] fillLoopVerts = polyVertices[pi];
                        int   NV            = fillLoopVerts.Length;

                        PlanarComplex.Element sourceElem = (pi == 0) ? gsolid.Outer : gsolid.Holes[pi - 1];
                        int      loopi      = ElemToLoopMap[sourceElem];
                        EdgeLoop sourceLoop = Loops[loopi].edgeLoop;

                        if (sourceLoop.VertexCount != NV)
                        {
                            failed_merges.Add(new Index2i(fi, pi));
                            continue;
                        }

                        for (int k = 0; k < NV; ++k)
                        {
                            Vector3d fillV   = FillMesh.GetVertex(fillLoopVerts[k]);
                            Vector3d sourceV = Mesh.GetVertex(sourceLoop.Vertices[k]);
                            if (fillV.Distance(sourceV) < MathUtil.ZeroTolerancef)
                            {
                                mergeMapV[fillLoopVerts[k]] = sourceLoop.Vertices[k];
                            }
                        }
                    }
                }

                // append this fill to input mesh
                MeshEditor editor = new MeshEditor(Mesh);
                int[]      mapV;
                editor.AppendMesh(FillMesh, mergeMapV, out mapV, Mesh.AllocateTriangleGroup());

                // [TODO] should verify that we actually merged the loops...
            }

            if (failed_inserts.Count > 0 || failed_merges.Count > 0)
            {
                return(false);
            }

            return(true);
        }
Example #19
0
        public bool Insert()
        {
            OuterInsert = new MeshInsertUVPolyCurve(Mesh, Polygon.Outer);
            Util.gDevAssert(OuterInsert.Validate() == ValidationStatus.Ok);
            bool outerApplyOK = OuterInsert.Apply();

            if (outerApplyOK == false || OuterInsert.Loops.Count == 0)
            {
                return(false);
            }

            if (SimplifyInsertion)
            {
                OuterInsert.Simplify();
            }

            HoleInserts = new List <MeshInsertUVPolyCurve>(Polygon.Holes.Count);
            for (int hi = 0; hi < Polygon.Holes.Count; ++hi)
            {
                var insert = new MeshInsertUVPolyCurve(Mesh, Polygon.Holes[hi]);
                Util.gDevAssert(insert.Validate() == ValidationStatus.Ok);
                insert.Apply();
                if (SimplifyInsertion)
                {
                    insert.Simplify();
                }

                HoleInserts.Add(insert);
            }


            // find a triangle connected to loop that is inside the polygon
            //   [TODO] maybe we could be a bit more robust about this? at least
            //   check if triangle is too degenerate...
            int      seed_tri   = -1;
            EdgeLoop outer_loop = OuterInsert.Loops[0];

            for (int i = 0; i < outer_loop.EdgeCount; ++i)
            {
                if (!Mesh.IsEdge(outer_loop.Edges[i]))
                {
                    continue;
                }

                Index2i  et   = Mesh.GetEdgeT(outer_loop.Edges[i]);
                Vector3d ca   = Mesh.GetTriCentroid(et.a);
                bool     in_a = Polygon.Outer.Contains(ca.xy);
                Vector3d cb   = Mesh.GetTriCentroid(et.b);
                bool     in_b = Polygon.Outer.Contains(cb.xy);
                if (in_a && in_b == false)
                {
                    seed_tri = et.a;
                    break;
                }
                else if (in_b && in_a == false)
                {
                    seed_tri = et.b;
                    break;
                }
            }
            if (seed_tri == -1)
            {
                throw new Exception("MeshPolygonsInserter: could not find seed triangle!");
            }

            // make list of all outer & hole edges
            InsertedPolygonEdges = new HashSet <int>(outer_loop.Edges);
            foreach (var insertion in HoleInserts)
            {
                foreach (int eid in insertion.Loops[0].Edges)
                {
                    InsertedPolygonEdges.Add(eid);
                }
            }

            // flood-fill inside loop from seed triangle
            InteriorTriangles = new MeshFaceSelection(Mesh);
            InteriorTriangles.FloodFill(seed_tri, null, (eid) => { return(InsertedPolygonEdges.Contains(eid) == false); });

            return(true);
        }
Example #20
0
        /// <summary>
        /// given EdgeLoop on MeshA, and vertex map from A to B, map to EdgeLoop on B
        /// </summary>
        public static EdgeLoop MapLoopViaVertexMap(IIndexMap AtoBV, DMesh3 MeshA, DMesh3 MeshB, EdgeLoop loopIn)
        {
            int NV = loopIn.VertexCount, NE = loopIn.EdgeCount;

            int[] newVerts = new int[NV];
            for (int i = 0; i < NV; ++i)
            {
                newVerts[i] = AtoBV[loopIn.Vertices[i]];
            }

            int[] newEdges = new int[NE];
            for (int i = 0; i < NE; ++i)
            {
                int     eid_a = loopIn.Edges[i];
                Index2i aev   = MeshA.GetEdgeV(eid_a);
                int     bev0  = AtoBV[aev.a];
                int     bev1  = AtoBV[aev.b];
                newEdges[i] = MeshB.FindEdge(bev0, bev1);
                Debug.Assert(newEdges[i] != DMesh3.InvalidID);
            }

            return(new EdgeLoop(MeshB, newVerts, newEdges, false));
        }
Example #21
0
        // 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 Exception("extract_subloops: argh");
                }
                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.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.BowtieVertices = bowties.ToArray();
                subs.Add(loop);
            }

            return(subs);
        }
Example #22
0
        // This algorithm assumes that triangles are oriented consistently,
        // so boundary-loop can be followed
        public bool Compute()
        {
            Loops = new List <EdgeLoop>();

            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];

            // 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.edge_is_boundary(eid) == false)
                {
                    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;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    loop_verts.Add(cure_a);

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

                    if (bdry_nbrs < 2)
                    {
                        throw new Exception("MeshBoundaryLoops.Compute: found broken neighbourhood at vertex " + cure_b);
                    }

                    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[bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            // 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)
                            {
                                throw new Exception("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b);
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        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
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        eCur            = eNext;
                        used_edge[eCur] = true;
                    }
                }

                // if we saw a bowtie vertex, we might need to break up this loop,
                // so call extract_subloops
                if (bowties.Count > 0)
                {
                    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);
        }
Example #23
0
 protected override void end_pass()
 {
     OutputLoop = new EdgeLoop(mesh, CurrentLoopV.ToArray(), CurrentLoopE.ToArray(), false);
 }
Example #24
0
 public MeshLoopClosure(DMesh3 mesh, EdgeLoop border_loop)
 {
     Mesh = mesh;
     InitialBorderLoop = border_loop;
 }
Example #25
0
        public void Close_Flat()
        {
            double minlen, maxlen, avglen;

            MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000);
            double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen;

            // massage around boundary loop
            List <int> refinedBorderEdges;

            cleanup_boundary(Mesh, InitialBorderLoop, avglen, out refinedBorderEdges, 3);

            // find new border loop. try to find new loop containing edges from loop we refined in cleanup_boundary,
            // if that fails just use largest loop.
            MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh);
            int iloop = loops.FindLoopContainingEdge(refinedBorderEdges[0]);

            if (iloop == -1)
            {
                iloop = loops.MaxVerticesLoopIndex;
            }
            EdgeLoop fill_loop = loops.Loops[iloop];

            int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup;
            int fill_group    = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup;

            // decide on projection plane
            //AxisAlignedBox3d loopbox = fill_loop.GetBounds();
            //Vector3d topPt = loopbox.Center;
            //if ( bIsUpper ) {
            //    topPt.y = loopbox.Max.y + 0.25 * dims.y;
            //} else {
            //    topPt.y = loopbox.Min.y - 0.25 * dims.y;
            //}
            //Frame3f plane = new Frame3f((Vector3f)topPt);

            // extrude loop to this plane
            MeshExtrudeLoop extrude = new MeshExtrudeLoop(Mesh, fill_loop);

            extrude.PositionF = (v, n, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3f)v, 1));
            };
            extrude.Extrude(extrude_group);
            MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop);

            Debug.Assert(Mesh.CheckValidity());

            // smooth the extrude loop
            MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop);

            loop_smooth.ProjectF = (v, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3f)v, 1));
            };
            loop_smooth.Alpha  = 0.5f;
            loop_smooth.Rounds = 100;
            loop_smooth.Smooth();

            Debug.Assert(Mesh.CheckValidity());

            // fill result
            SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop);

            filler.Fill(fill_group);

            Debug.Assert(Mesh.CheckValidity());

            // make selection for remesh region
            MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh);

            remesh_roi.Select(extrude.NewTriangles);
            remesh_roi.Select(filler.NewTriangles);
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.LocalOptimize(true, true);
            int[] new_roi = remesh_roi.ToArray();

            // get rid of extrude group
            FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0);

            /*  clean up via remesh
             *     - constrain loop we filled to itself
             */

            RegionRemesher r = new RegionRemesher(Mesh, new_roi);

            DCurve3 top_curve = MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices);
            DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve);

            int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone();
            r.Region.MapVerticesToSubmesh(top_loop);
            MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target);

            DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh);

            spatial.Build();
            MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial);

            r.SetProjectionTarget(target);

            bool bRemesh = true;

            if (bRemesh)
            {
                r.Precompute();
                r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
                r.MinEdgeLength   = target_edge_len;
                r.MaxEdgeLength   = 2 * target_edge_len;
                r.EnableSmoothing = true;
                r.SmoothSpeedT    = 1.0f;
                for (int k = 0; k < 40; ++k)
                {
                    r.BasicRemeshPass();
                }
                r.SetProjectionTarget(null);
                r.SmoothSpeedT = 0.25f;
                for (int k = 0; k < 10; ++k)
                {
                    r.BasicRemeshPass();
                }
                Debug.Assert(Mesh.CheckValidity());

                r.BackPropropagate();
            }

            // smooth around the join region to clean up ugliness
            smooth_region(Mesh, r.Region.BaseBorderV, 3);
        }
        // Walk along edge loop and collapse to inserted curve vertices.
        EdgeLoop simplify(EdgeLoop loop)
        {
            HashSet <int> curve_verts = new HashSet <int>(CurveVertices);

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

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

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

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

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

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

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

            return(EdgeLoop.FromEdges(Mesh, remaining_edges));
        }
Example #27
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);
        }