Example #1
0
        public static List <List <Vector2> > Simplify(List <Vector2> polygon, FillMode fillMode, out PolyTree tree)
        {
            Clipper.Clear();
            Clipper.AddPath(polygon, PolyType.ptSubject, true);
            Clipper.AddPath(polygon, PolyType.ptClip, true);

            tree = new PolyTree();
            PolyFillType fillType = fillMode.ToPolyFillType();

            Clipper.Execute(ClipType.ctUnion, tree, fillType, fillType);
            return(Clipper.ClosedPathsFromPolyTree(tree));
        }
Example #2
0
        public static Polygons GetCorrectedWinding(this Polygons polygonsToFix)
        {
            polygonsToFix = Clipper.CleanPolygons(polygonsToFix);
            var     boundsPolygon = new Polygon();
            IntRect bounds        = Clipper.GetBounds(polygonsToFix);

            bounds.minX -= 10;
            bounds.minY -= 10;
            bounds.maxY += 10;
            bounds.maxX += 10;

            boundsPolygon.Add(new IntPoint(bounds.minX, bounds.minY));
            boundsPolygon.Add(new IntPoint(bounds.maxX, bounds.minY));
            boundsPolygon.Add(new IntPoint(bounds.maxX, bounds.maxY));
            boundsPolygon.Add(new IntPoint(bounds.minX, bounds.maxY));

            var clipper = new Clipper();

            clipper.AddPaths(polygonsToFix, PolyType.ptSubject, true);
            clipper.AddPath(boundsPolygon, PolyType.ptClip, true);

            var intersectionResult = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, intersectionResult);

            Polygons outputPolygons = Clipper.ClosedPathsFromPolyTree(intersectionResult);

            return(outputPolygons);
        }
Example #3
0
        private Polygons FixWinding(Polygons polygonsToPathAround)
        {
            polygonsToPathAround = Clipper.CleanPolygons(polygonsToPathAround);
            Polygon boundsPolygon = new Polygon();
            IntRect bounds        = Clipper.GetBounds(polygonsToPathAround);

            bounds.minX -= 10;
            bounds.maxY += 10;
            bounds.maxX += 10;
            bounds.minY -= 10;

            boundsPolygon.Add(new IntPoint(bounds.minX, bounds.minY));
            boundsPolygon.Add(new IntPoint(bounds.maxX, bounds.minY));
            boundsPolygon.Add(new IntPoint(bounds.maxX, bounds.maxY));
            boundsPolygon.Add(new IntPoint(bounds.minX, bounds.maxY));

            Clipper clipper = new Clipper();

            clipper.AddPaths(polygonsToPathAround, PolyType.ptSubject, true);
            clipper.AddPath(boundsPolygon, PolyType.ptClip, true);

            PolyTree intersectionResult = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, intersectionResult);

            Polygons outputPolygons = Clipper.ClosedPathsFromPolyTree(intersectionResult);

            return(outputPolygons);
        }
        private List <List <IntPoint> > ClipToRooms(List <IntPoint> roomFootprint, bool allowSplit)
        {
            _clipper.Clear();
            _clipper.AddPath(roomFootprint, PolyType.ptSubject, true);
            _clipper.AddPaths(_rooms.Select(r => r.OuterFootprint.Select(ToPoint).ToList()).ToList(), PolyType.ptClip, true);

            var solution = new PolyTree();

            _clipper.Execute(ClipType.ctDifference, solution);

            //Rooms with holes are not supported
            if (HasHole(solution))
            {
                //Rooms with holes are not supported (issue #166 - Will Not Fix)
                return(new List <List <IntPoint> >());
            }

            var shapes = Clipper.ClosedPathsFromPolyTree(solution);

            if (shapes.Count > 1 && !allowSplit)
            {
                return(new List <List <IntPoint> >());
            }

            return(shapes);
        }
Example #5
0
        private Polygons FixWinding(Polygons polygonsToPathAround)
        {
            polygonsToPathAround = Clipper.CleanPolygons(polygonsToPathAround);
            Polygon boundsPolygon = new Polygon();
            IntRect bounds        = Clipper.GetBounds(polygonsToPathAround);

            bounds.left   -= 10;
            bounds.bottom += 10;
            bounds.right  += 10;
            bounds.top    -= 10;

            boundsPolygon.Add(new IntPoint(bounds.left, bounds.top));
            boundsPolygon.Add(new IntPoint(bounds.right, bounds.top));
            boundsPolygon.Add(new IntPoint(bounds.right, bounds.bottom));
            boundsPolygon.Add(new IntPoint(bounds.left, bounds.bottom));

            Clipper clipper = new Clipper();

            clipper.AddPaths(polygonsToPathAround, PolyType.ptSubject, true);
            clipper.AddPath(boundsPolygon, PolyType.ptClip, true);

            PolyTree intersectionResult = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, intersectionResult);

            Polygons outputPolygons = Clipper.ClosedPathsFromPolyTree(intersectionResult);

            Clipper.ReversePaths(outputPolygons);

            return(outputPolygons);
        }
        public static Polygons GetCorrectedWinding(this Polygons polygonsToFix)
        {
            polygonsToFix = Clipper.CleanPolygons(polygonsToFix);
            Polygon boundsPolygon = new Polygon();
            IntRect bounds        = Clipper.GetBounds(polygonsToFix);

            bounds.left   -= 10;
            bounds.bottom += 10;
            bounds.right  += 10;
            bounds.top    -= 10;

            boundsPolygon.Add(new IntPoint(bounds.left, bounds.top));
            boundsPolygon.Add(new IntPoint(bounds.right, bounds.top));
            boundsPolygon.Add(new IntPoint(bounds.right, bounds.bottom));
            boundsPolygon.Add(new IntPoint(bounds.left, bounds.bottom));

            Clipper clipper = new Clipper();

            clipper.AddPaths(polygonsToFix, PolyType.ptSubject, true);
            clipper.AddPath(boundsPolygon, PolyType.ptClip, true);

            PolyTree intersectionResult = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, intersectionResult);

            Polygons outputPolygons = Clipper.ClosedPathsFromPolyTree(intersectionResult);

            return(outputPolygons);
        }
Example #7
0
        public void Offset(float offset)
        {
            Paths         polygons = Clipper.ClosedPathsFromPolyTree(polyTree);
            ClipperOffset co       = new ClipperOffset();

            co.ArcTolerance = scale * .0001f;
            co.AddPaths(polygons, JoinType.jtRound, EndType.etClosedPolygon);
            polyTree = new PolyTree();
            Paths offsetPaths = new Paths();

            co.Execute(ref offsetPaths, scale * offset);
            offsetPaths = Clipper.CleanPolygons(offsetPaths, scale * .0001f);
            polyTree    = PolygonsToPolyTree(offsetPaths);
        }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
            {
                // Transform point to Unity space
                Vector3D pointUnity3d = PointFToUnityVector_NoScale(new PointF(x, y));
                IntPoint point        = new IntPoint(pointUnity3d.X, pointUnity3d.Y);
                return(point);
            };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
            {
                Program.WriteLine(prog);
            };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            // Add our polygon and edge colliders
            List <XElement> polyColliderElements = new List <XElement>();

            AddPolygonCollider2DElements(Clipper.ClosedPathsFromPolyTree(solution), polyColliderElements);
            AddEdgeCollider2DElements(Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return(null);
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                             new XAttribute("name", "Collision"),
                             polyColliderElements);

            return(gameObjectCollision);
        }
Example #9
0
        public static Paths offset(Paths paths, float offset)
        {
            // Set cleaning precision
            IntRect brect = Clipper.GetBounds(paths);
            int     cleanPolygonPrecision = 2;

            if ((brect.right - brect.left) > 10000)
            {
                cleanPolygonPrecision = 30;
            }


            // Clean...
            AXClipperLib.JoinType jt = AXClipperLib.JoinType.jtSquare;
            paths = AXGeometryTools.Utilities.cleanPaths(paths, cleanPolygonPrecision);



            Paths resPaths = new Paths();

            AXClipperLib.PolyTree resPolytree = null;


            // OFFSETTER
            ClipperOffset co = new ClipperOffset();

            co.MiterLimit = 2.0f;



            foreach (Path path in paths)
            {
                co.Clear();
                resPolytree = null;

                co.AddPath(path, jt, AXClipperLib.EndType.etClosedPolygon);                  //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon);

                // this resPolytree has transformed curves in it
                resPolytree = new AXClipperLib.PolyTree();
                co.Execute(ref resPolytree, (double)(offset * AXGeometryTools.Utilities.IntPointPrecision));
                resPaths.AddRange(Clipper.ClosedPathsFromPolyTree(resPolytree));
            }

            return(resPaths);
        }
Example #10
0
        public static IEnumerable <PointF[]> SolutionPolygons_Complex(PolyTree solution)
        {
            List <List <IntPoint> > .Enumerator enumerator = Clipper.ClosedPathsFromPolyTree(solution).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    IEnumerable <PointF> source = from pt in enumerator.Current
                                                  select new PointF((float)pt.X, (float)pt.Y);
                    yield return(source.ToArray());
                }
            }
            finally
            {
                ((IDisposable)enumerator).Dispose();
            }

            enumerator = default(List <List <IntPoint> > .Enumerator);
        }
Example #11
0
        //------------------------------------------------------------------------------

        private void BmpUpdateNeeded()
        {
            const int textOffset = 20;

            if (bmpGraphics == null)
            {
                return;
            }
            FillMode fm = (mEvenOdd.Checked ? FillMode.Alternate : FillMode.Winding);

            bmpGraphics.Clear(Color.White);

            //draw the subject and clip paths ...
            Paths openPaths   = new Paths();
            Paths closedPaths = new Paths();
            Paths clipPaths   = new Paths();

            //sort the paths into open and closed subjects and (closed) clips ...
            foreach (MultiPath mp2 in allPaths)
            {
                if (mp2.RefID == CLIP)
                {
                    clipPaths.Add(mp2.Flatten());
                }
                else if (mp2.IsClosed)
                {
                    closedPaths.Add(mp2.Flatten());
                }
                else
                {
                    openPaths.Add(mp2.Flatten());
                }
            }

            DrawPath(bmpGraphics, openPaths, false, 0x0, 0xFFAAAAAA, fm, 1.0);
            DrawPath(bmpGraphics, closedPaths, true, 0x0, 0xFFAAAAAA, fm, 1.0);
            DrawPath(bmpGraphics, clipPaths, true, 0x10FF6600, 0x99FF6600, fm, 1.0);

            if (cbShowCoords.Checked)
            {
                Font       fnt   = new Font("Arial", 8);
                SolidBrush brush = new SolidBrush(Color.Navy);
                foreach (MultiPath mp2 in allPaths)
                {
                    foreach (MultiPathSegment mps in mp2)
                    {
                        foreach (IntPoint ip in mps)
                        {
                            IntPoint ip2    = new IntPoint(ip.X / scale, ip.Y / scale);
                            string   coords = ip2.X.ToString() + "," + ip2.Y.ToString();
                            bmpGraphics.DrawString(coords, fnt, brush, ip2.X - textOffset, ip2.Y - textOffset, null);
                        }
                    }
                }
                fnt.Dispose();
                brush.Dispose();
            }

            //for the active path, draw control buttons and control lines too ...
            MultiPath activePath = GetActivePath();

            if (activePath != null && activePath.Count > 0)
            {
                foreach (MultiPathSegment mps in activePath)
                {
                    CurveType pt = mps.curvetype;
                    if (pt == CurveType.CubicBezier)
                    {
                        DrawBezierCtrlLines(bmpGraphics, mps, 0xFFEEEEEE);
                    }
                    else if (pt == CurveType.QuadBezier)
                    {
                        DrawBezierCtrlLines(bmpGraphics, mps, 0xFFEEEEEE);
                    }
                }

                DrawButtons(bmpGraphics, activePath);

                //display the coords of a moving button ...
                if (MovingButtonIdx >= 0)
                {
                    Font       f  = new Font("Arial", 8);
                    SolidBrush b  = new SolidBrush(Color.Navy);
                    IntPoint   ip = MovingButtonSeg[MovingButtonIdx];
                    ip.X = (int)(ip.X / scale); ip.Y = (int)(ip.Y / scale);
                    string coords = ip.X.ToString() + "," + ip.Y.ToString();
                    bmpGraphics.DrawString(coords, f, b, ip.X - textOffset, ip.Y - textOffset, null);
                    f.Dispose();
                    b.Dispose();
                }
            }

            //if there's any clipping to be done, do it here ...
            if (!mNone.Checked && GetCurrentSubjMultiPath() != null && GetCurrentClipMultiPath() != null)
            {
                PolyFillType pft = (mEvenOdd.Checked ? PolyFillType.pftEvenOdd : PolyFillType.pftNonZero);
                ClipType     ct;
                if (mUnion.Checked)
                {
                    ct = ClipType.ctUnion;
                }
                else if (mDifference.Checked)
                {
                    ct = ClipType.ctDifference;
                }
                else if (mXor.Checked)
                {
                    ct = ClipType.ctXor;
                }
                else
                {
                    ct = ClipType.ctIntersection;
                }

                //CLIPPING DONE HERE ...
                Clipper c = new Clipper();
                c.ZFillFunction = MultiPaths.ClipCallback; //set the callback function (called at intersections)
                if (openPaths.Count > 0)
                {
                    c.AddPaths(openPaths, PolyType.ptSubject, false);
                }
                if (closedPaths.Count > 0)
                {
                    c.AddPaths(closedPaths, PolyType.ptSubject, true);
                }
                c.AddPaths(clipPaths, PolyType.ptClip, true);
                PolyTree polytree = new PolyTree();

                Paths solution;
                c.Execute(ct, polytree, pft, pft); //EXECUTE CLIP !!!!!!!!!!!!!!!!!!!!!!
                solution = Clipper.ClosedPathsFromPolyTree(polytree);
                if (!cbReconstCurve.Checked)
                {
                    DrawPath(bmpGraphics, solution, true, 0x2033AA00, 0xFF33AA00, fm, 2.0);
                }
                solution = Clipper.OpenPathsFromPolyTree(polytree);
                if (!cbReconstCurve.Checked)
                {
                    DrawPath(bmpGraphics, solution, false, 0x0, 0xFF33AA00, fm, 2.0);
                }

                //now to demonstrate reconstructing beziers & arcs ...
                if (cbReconstCurve.Checked)
                {
                    PolyNode pn = polytree.GetFirst();
                    while (pn != null)
                    {
                        if (pn.IsHole || pn.Contour.Count < 2)
                        {
                            pn = pn.GetNext();
                            continue;
                        }

                        if (pn.ChildCount > 0)
                        {
                            throw new Exception("Sorry, this demo doesn't currently handle holes");
                        }

                        //and reconstruct each curve ...
                        MultiPath reconstructedMultiPath = allPaths.Reconstruct(pn.Contour);

                        if (cbShowCtrls.Enabled && cbShowCtrls.Checked)
                        {
                            //show (small) buttons on the red reconstructed path too ...
                            DrawButtons(bmpGraphics, reconstructedMultiPath, true);
                        }

                        //now to show how accurate these reconstructed (control) paths are,
                        //we flatten them (drawing them red) so we can compare them with
                        //the original flattened paths (light gray) ...
                        Paths paths = new Paths();
                        paths.Add(reconstructedMultiPath.Flatten());
                        DrawPath(bmpGraphics, paths, !pn.IsOpen, 0x18FF0000, 0xFFFF0000, fm, 2.0);

                        pn = pn.GetNext();
                    }
                }
                //else //shows just how many vertices there are in flattened paths ...
                //{
                //  solution = Clipper.PolyTreeToPaths(polytree);
                //  MultiPath flatMultiPath = new MultiPath(null, 0, false);
                //  foreach (Path p in solution)
                //    flatMultiPath.NewMultiPathSegment(PathType.Line, p);
                //  DrawButtons(bmpGraphics, flatMultiPath, true);
                //}
            }

            string s = "  ";

            if (mIntersection.Checked)
            {
                s += "INTERSECTION";
            }
            else if (mUnion.Checked)
            {
                s += "UNION";
            }
            else if (mDifference.Checked)
            {
                s += "DIFFERENCE";
            }
            else if (mXor.Checked)
            {
                s += "XOR";
            }
            else
            {
                s += "NO CLIPPING";
            }
            s += " with ";
            if (mEvenOdd.Checked)
            {
                s += "EVENODD fill.";
            }
            else
            {
                s += "NONZERO fill.";
            }
            toolStripStatusLabel2.Text = s;
            displayPanel.Invalidate();
        }
Example #12
0
        public static PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
                                from x in Enumerable.Range(0, tmxLayer.Width)
                                let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
                                                let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                                             where tileId != 0
                                                             let tile = tmxMap.Tiles[tileId]
                                                                        from polygon in tile.ObjectGroup.Objects
                                                                        where (polygon as TmxHasPoints) != null
                                                                        let groupX = x / LayerClipper.GroupBySize
                                                                                     let groupY = y / LayerClipper.GroupBySize
                                                                                                  group new
            {
                PositionOnMap         = tmxMap.GetMapPositionAt(x, y, tile),
                HasPointsInterface    = polygon as TmxHasPoints,
                TmxObjectInterface    = polygon,
                IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                TileCenter            = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
            }
            by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (var polyGroup in polygonGroups)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.UniqueName, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (var poly in polyGroup)
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform then we put all the points into local space relative to the tile
                    SizeF    offset            = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(Clipper.ClosedPathsFromPolyTree(solution), PolyType.ptSubject, true);
                fullClipper.AddPaths(Clipper.OpenPathsFromPolyTree(solution), PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.UniqueName));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution);

            return(fullSolution);
        }