示例#1
0
        public void AddHole(Polygon2d hole, bool bCheck = true)
        {
            if (outer == null)
            {
                throw new Exception("GeneralPolygon2d.AddHole: outer polygon not set!");
            }
            if (bCheck)
            {
                if (outer.Contains(hole) == false)
                {
                    throw new Exception("GeneralPolygon2d.AddHole: outer does not contain hole!");
                }

                // [RMS] segment/segment intersection broken?
                foreach (var hole2 in holes)
                {
                    if (hole.Intersects(hole2))
                    {
                        throw new Exception("GeneralPolygon2D.AddHole: new hole intersects existing hole!");
                    }
                }
            }

            if ((bOuterIsCW && hole.IsClockwise) || (bOuterIsCW == false && hole.IsClockwise == false))
            {
                throw new Exception("GeneralPolygon2D.AddHole: new hole has same orientation as outer polygon!");
            }

            holes.Add(hole);
        }
        // Finds set of "solid" regions - eg boundary loops with interior holes.
        // Result has outer loops being clockwise, and holes counter-clockwise
        public SolidRegionInfo FindSolidRegions(double fSimplifyDeviationTol = 0.1, bool bWantCurveSolids = true)
        {
            List <SmoothLoopElement> valid = new List <SmoothLoopElement>(LoopsItr());
            int N = valid.Count;

            // precompute bounding boxes
            int maxid = 0;

            foreach (var v in valid)
            {
                maxid = Math.Max(maxid, v.ID + 1);
            }
            AxisAlignedBox2d[] bounds = new AxisAlignedBox2d[maxid];
            foreach (var v in valid)
            {
                bounds[v.ID] = v.Bounds();
            }

            // copy polygons, simplify if desired
            double fClusterTol   = 0.0;                         // don't do simple clustering, can lose corners
            double fDeviationTol = fSimplifyDeviationTol;

            Polygon2d[] polygons = new Polygon2d[maxid];
            foreach (var v in valid)
            {
                Polygon2d p = new Polygon2d(v.polygon);
                if (fClusterTol > 0 || fDeviationTol > 0)
                {
                    p.Simplify(fClusterTol, fDeviationTol);
                }
                polygons[v.ID] = p;
            }

            // sort by bbox containment to speed up testing (does it??)
            valid.Sort((x, y) => {
                return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1);
            });

            // containment sets
            bool[] bIsContained = new bool[N];
            Dictionary <int, List <int> > ContainSets = new Dictionary <int, List <int> >();

            // construct containment sets
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = valid[i];
                Polygon2d         polyi = polygons[loopi.ID];

                for (int j = 0; j < N; ++j)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    SmoothLoopElement loopj = valid[j];
                    Polygon2d         polyj = polygons[loopj.ID];

                    // cannot be contained if bounds are not contained
                    if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false)
                    {
                        continue;
                    }

                    // any other early-outs??

                    if (polyi.Contains(polyj))
                    {
                        if (ContainSets.ContainsKey(i) == false)
                        {
                            ContainSets.Add(i, new List <int>());
                        }
                        ContainSets[i].Add(j);
                        bIsContained[j] = true;
                    }
                }
            }

            List <GeneralPolygon2d>  polysolids = new List <GeneralPolygon2d>();
            List <PlanarSolid2d>     solids     = new List <PlanarSolid2d>();
            List <SmoothLoopElement> used       = new List <SmoothLoopElement>();

            // extract solids from containment relationships
            foreach (var i in ContainSets.Keys)
            {
                SmoothLoopElement outer_element = valid[i];
                used.Add(outer_element);
                if (bIsContained[i])
                {
                    throw new Exception("PlanarComplex.FindSolidRegions: multiply-nested regions not supported!");
                }

                Polygon2d          outer_poly = polygons[outer_element.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? outer_element.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                GeneralPolygon2d g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                PlanarSolid2d s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                foreach (int hi in ContainSets[i])
                {
                    SmoothLoopElement he = valid[hi];
                    used.Add(he);
                    Polygon2d          hole_poly = polygons[he.ID];
                    IParametricCurve2d hole_loop = (bWantCurveSolids) ? he.source.Clone() : null;
                    if (hole_poly.IsClockwise)
                    {
                        hole_poly.Reverse();
                        if (bWantCurveSolids)
                        {
                            hole_loop.Reverse();
                        }
                    }

                    try {
                        g.AddHole(hole_poly);
                        if (hole_loop != null)
                        {
                            s.AddHole(hole_loop);
                        }
                    } catch {
                        // don't add this hole - must intersect or something
                        // We should have caught this earlier!
                    }
                }

                polysolids.Add(g);
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }

            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = valid[i];
                if (used.Contains(loopi))
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                GeneralPolygon2d g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                PlanarSolid2d s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                polysolids.Add(g);
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }

            return(new SolidRegionInfo()
            {
                Polygons = polysolids,
                Solids = (bWantCurveSolids) ? solids : null
            });
        }
示例#3
0
        // Finds set of "solid" regions - eg boundary loops with interior holes.
        // Result has outer loops being clockwise, and holes counter-clockwise
        public SolidRegionInfo FindSolidRegions(FindSolidsOptions options)
        {
            var validLoops = new List <SmoothLoopElement>(LoopsItr());
            int N          = validLoops.Count;

            // precompute bounding boxes
            int maxid = 0;

            foreach (var v in validLoops)
            {
                maxid = Math.Max(maxid, v.ID + 1);
            }

            var bounds = new AxisAlignedBox2d[maxid];

            foreach (var v in validLoops)
            {
                bounds[v.ID] = v.Bounds();
            }

            // copy polygons, simplify if desired
            double fClusterTol   = 0.0;                 // don't do simple clustering, can lose corners
            double fDeviationTol = options.SimplifyDeviationTolerance;
            var    polygons      = new Polygon2d[maxid];

            foreach (var v in validLoops)
            {
                var p = new Polygon2d(v.polygon);
                if (fClusterTol > 0 || fDeviationTol > 0)
                {
                    p.Simplify(fClusterTol, fDeviationTol);
                }

                polygons[v.ID] = p;
            }

            // sort by bbox containment to speed up testing (does it??)
            validLoops.Sort((x, y) =>
            {
                return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1);
            });

            // containment sets
            bool[] bIsContained     = new bool[N];
            var    ContainSets      = new Dictionary <int, List <int> >();
            var    ContainedParents = new Dictionary <int, List <int> >();

            bool bUseOrient       = options.TrustOrientations;
            bool bWantCurveSolids = options.WantCurveSolids;
            bool bCheckHoles      = !options.AllowOverlappingHoles;

            // construct containment sets
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                Polygon2d         polyi = polygons[loopi.ID];

                for (int j = 0; j < N; ++j)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    SmoothLoopElement loopj = validLoops[j];
                    Polygon2d         polyj = polygons[loopj.ID];

                    // if we are preserving orientations, holes cannot contain holes and
                    // outers cannot contain outers!
                    if (bUseOrient && loopj.polygon.IsClockwise == loopi.polygon.IsClockwise)
                    {
                        continue;
                    }

                    // cannot be contained if bounds are not contained
                    if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false)
                    {
                        continue;
                    }

                    // any other early-outs??

                    if (polyi.Contains(polyj))
                    {
                        if (ContainSets.ContainsKey(i) == false)
                        {
                            ContainSets.Add(i, new List <int>());
                        }

                        ContainSets[i].Add(j);
                        bIsContained[j] = true;

                        if (ContainedParents.ContainsKey(j) == false)
                        {
                            ContainedParents.Add(j, new List <int>());
                        }

                        ContainedParents[j].Add(i);
                    }
                }
            }

            var polysolids     = new List <GeneralPolygon2d>();
            var polySolidsInfo = new List <GeneralSolid>();

            var solids = new List <PlanarSolid2d>();
            var used   = new HashSet <SmoothLoopElement>();

            var LoopToOuterIndex = new Dictionary <SmoothLoopElement, int>();

            var ParentsToProcess = new List <int>();


            // The following is a lot of code but it is very similar, just not clear how
            // to refactor out the common functionality
            //   1) we find all the top-level uncontained polys and add them to the final polys list
            //   2a) for any poly contained in those parent-polys, that is not also contained in anything else,
            //       add as hole to that poly
            //   2b) remove all those used parents & holes from consideration
            //   2c) now find all the "new" top-level polys
            //   3) repeat 2a-c until done all polys
            //   4) any remaining polys must be interior solids w/ no holes
            //          **or** weird leftovers like intersecting polys...

            // add all top-level uncontained polys
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                if (bIsContained[i])
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                var g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                var s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                int idx = polysolids.Count;
                LoopToOuterIndex[loopi] = idx;
                used.Add(loopi);

                if (ContainSets.ContainsKey(i))
                {
                    ParentsToProcess.Add(i);
                }

                polysolids.Add(g);
                polySolidsInfo.Add(new GeneralSolid()
                {
                    Outer = loopi
                });
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }


            // keep iterating until we processed all parent loops
            while (ParentsToProcess.Count > 0)
            {
                var ContainersToRemove = new List <int>();

                // now for all top-level polys that contain children, add those children
                // as long as they do not have multiple contain-parents
                foreach (int i in ParentsToProcess)
                {
                    SmoothLoopElement parentloop = validLoops[i];
                    int outer_idx = LoopToOuterIndex[parentloop];

                    List <int> children = ContainSets[i];
                    foreach (int childj in children)
                    {
                        SmoothLoopElement childLoop = validLoops[childj];
                        Debug.Assert(used.Contains(childLoop) == false);

                        // skip multiply-contained children
                        List <int> parents = ContainedParents[childj];
                        if (parents.Count > 1)
                        {
                            continue;
                        }

                        Polygon2d          hole_poly = polygons[childLoop.ID];
                        IParametricCurve2d hole_loop = (bWantCurveSolids) ? childLoop.source.Clone() : null;
                        if (hole_poly.IsClockwise)
                        {
                            hole_poly.Reverse();
                            if (bWantCurveSolids)
                            {
                                hole_loop.Reverse();
                            }
                        }

                        try
                        {
                            polysolids[outer_idx].AddHole(hole_poly, bCheckHoles);
                            polySolidsInfo[outer_idx].Holes.Add(childLoop);
                            if (hole_loop != null)
                            {
                                solids[outer_idx].AddHole(hole_loop);
                            }
                        }
                        catch
                        {
                            // don't add this hole - must intersect or something?
                            // We should have caught this earlier!
                        }

                        used.Add(childLoop);
                        if (ContainSets.ContainsKey(childj))
                        {
                            ContainersToRemove.Add(childj);
                        }
                    }
                    ContainersToRemove.Add(i);
                }

                // remove all containers that are no longer valid
                foreach (int ci in ContainersToRemove)
                {
                    ContainSets.Remove(ci);

                    // have to remove from each ContainedParents list
                    var keys = new List <int>(ContainedParents.Keys);
                    foreach (int j in keys)
                    {
                        if (ContainedParents[j].Contains(ci))
                        {
                            ContainedParents[j].Remove(ci);
                        }
                    }
                }

                ParentsToProcess.Clear();

                // ok now find next-level uncontained parents...
                for (int i = 0; i < N; ++i)
                {
                    SmoothLoopElement loopi = validLoops[i];
                    if (used.Contains(loopi))
                    {
                        continue;
                    }

                    if (ContainSets.ContainsKey(i) == false)
                    {
                        continue;
                    }

                    List <int> parents = ContainedParents[i];
                    if (parents.Count > 0)
                    {
                        continue;
                    }

                    Polygon2d          outer_poly = polygons[loopi.ID];
                    IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                    if (outer_poly.IsClockwise == false)
                    {
                        outer_poly.Reverse();
                        if (bWantCurveSolids)
                        {
                            outer_loop.Reverse();
                        }
                    }

                    var g = new GeneralPolygon2d();
                    g.Outer = outer_poly;
                    var s = new PlanarSolid2d();
                    if (bWantCurveSolids)
                    {
                        s.SetOuter(outer_loop, true);
                    }

                    int idx = polysolids.Count;
                    LoopToOuterIndex[loopi] = idx;
                    used.Add(loopi);

                    if (ContainSets.ContainsKey(i))
                    {
                        ParentsToProcess.Add(i);
                    }

                    polysolids.Add(g);
                    polySolidsInfo.Add(new GeneralSolid()
                    {
                        Outer = loopi
                    });
                    if (bWantCurveSolids)
                    {
                        solids.Add(s);
                    }
                }
            }


            // any remaining loops must be top-level
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                if (used.Contains(loopi))
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                var g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                var s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                polysolids.Add(g);
                polySolidsInfo.Add(new GeneralSolid()
                {
                    Outer = loopi
                });
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }



            return(new SolidRegionInfo()
            {
                Polygons = polysolids,
                PolygonsSources = polySolidsInfo,
                Solids = (bWantCurveSolids) ? solids : null
            });
        }
        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);
        }
        /// <summary>
        /// Constrained laplacian smoothing of input polygon, alpha X iterations.
        /// vertices are only allowed to move at most max_dist from constraint
        /// if bAllowShrink == false, vertices are kept outside input polygon
        /// if bAllowGrow == false, vertices are kept inside input polygon
        ///
        /// max_dist is measured from vertex[i] to original_vertex[i], unless
        /// you set bPerVertexDistances = false, then distance to original polygon
        /// is used (which is much more expensive)
        ///
        /// [TODO] this is pretty hacky...could be better in lots of ways...
        ///
        /// </summary>
        public static void LaplacianSmoothConstrained(Polygon2d poly, double alpha, int iterations,
                                                      double max_dist, bool bAllowShrink, bool bAllowGrow, bool bPerVertexDistances = true)
        {
            Polygon2d origPoly = new Polygon2d(poly);

            int N = poly.VertexCount;

            Vector2d[] newV = new Vector2d[poly.VertexCount];

            double max_dist_sqr = max_dist * max_dist;

            double beta = 1.0 - alpha;

            for (int ii = 0; ii < iterations; ++ii)
            {
                for (int i = 0; i < N; ++i)
                {
                    Vector2d curpos    = poly[i];
                    Vector2d smoothpos = (poly[(i + N - 1) % N] + poly[(i + 1) % N]) * 0.5;

                    bool do_smooth = true;
                    if (bAllowShrink == false || bAllowGrow == false)
                    {
                        bool is_inside = origPoly.Contains(smoothpos);
                        if (is_inside == true)
                        {
                            do_smooth = bAllowShrink;
                        }
                        else
                        {
                            do_smooth = bAllowGrow;
                        }
                    }

                    // [RMS] this is old code...I think not correct?
                    //bool contained = true;
                    //if (bAllowShrink == false || bAllowGrow == false)
                    //    contained = origPoly.Contains(smoothpos);
                    //bool do_smooth = true;
                    //if (bAllowShrink && contained == false)
                    //    do_smooth = false;
                    //if (bAllowGrow && contained == true)
                    //    do_smooth = false;

                    if (do_smooth)
                    {
                        Vector2d newpos = beta * curpos + alpha * smoothpos;
                        if (bPerVertexDistances)
                        {
                            while (origPoly[i].DistanceSquared(newpos) > max_dist_sqr)
                            {
                                newpos = (newpos + curpos) * 0.5;
                            }
                        }
                        else
                        {
                            while (origPoly.DistanceSquared(newpos) > max_dist_sqr)
                            {
                                newpos = (newpos + curpos) * 0.5;
                            }
                        }
                        newV[i] = newpos;
                    }
                    else
                    {
                        newV[i] = curpos;
                    }
                }

                for (int i = 0; i < N; ++i)
                {
                    poly[i] = newV[i];
                }
            }
        }