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);
        }
        public bool Fill()
        {
            compute_polygon();

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

            SpansPoly.Translate(-shiftOrigin);
            SpansPoly.Scale(scale * Vector2d.One, Vector2d.Zero);

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

            // 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?!?

            int[] polyVertices = null;

            // insert each poly
            var insert = new MeshInsertUVPolyCurve(FillMesh, SpansPoly);
            ValidationStatus status = insert.Validate(MathUtil.ZeroTolerancef * scale);
            bool             failed = true;

            if (status == ValidationStatus.Ok)
            {
                if (insert.Apply())
                {
                    insert.Simplify();
                    polyVertices = insert.CurveVertices;
                    failed       = false;
                }
            }
            if (failed)
            {
                return(false);
            }

            // remove any triangles not contained in gpoly
            // [TODO] degenerate triangle handling? may be 'on' edge of gpoly...
            var removeT = new List <int>();

            foreach (int tid in FillMesh.TriangleIndices())
            {
                Vector3d v = FillMesh.GetTriCentroid(tid);
                if (SpansPoly.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
            var mergeMapV = new IndexMap(true);

            if (MergeFillBoundary && polyVertices != null)
            {
                throw new NotImplementedException("PlanarSpansFiller: merge fill boundary not implemented!");

                //int[] fillLoopVerts = polyVertices;
                //int NV = fillLoopVerts.Length;

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

                //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
            var editor = new MeshEditor(Mesh);

            int[] mapV;
            editor.AppendMesh(FillMesh, mergeMapV, out mapV, Mesh.AllocateTriangleGroup());

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

            return(true);
        }
Exemple #3
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);
        }