Exemple #1
0
        /// <summary>
        /// Constructs the geometric differences between this Line and the supplied Polygons.
        /// </summary>
        /// <param name="diffs">The list of intersecting Polygons.</param>
        /// <returns>
        /// Returns a list of Lines representing the subtraction of the Lines intersecting the supplied list of Polygons.
        /// </returns>
        public static List <Line> Differences(this Line line, IList <Polygon> diffs)
        {
            var thisPath  = LineToClipper(line);
            var polyPaths = new List <List <IntPoint> >();

            foreach (Polygon poly in diffs)
            {
                polyPaths.Add(Shaper.PolygonToClipper(poly));
            }
            Clipper clipper = new Clipper();

            clipper.AddPath(thisPath, PolyType.ptSubject, false);
            clipper.AddPaths(polyPaths, PolyType.ptClip, true);
            var solution = new PolyTree();

            clipper.Execute(ClipType.ctDifference, solution, PolyFillType.pftEvenOdd);
            var soLines = Clipper.OpenPathsFromPolyTree(solution);
            var lines   = new List <Line>();

            foreach (List <IntPoint> path in soLines)
            {
                lines.Add(LineFromClipper(path.ToList()));
            }
            return(lines);
        }
Exemple #2
0
        public static void GenerateLinePaths(Polygons polygonToInfill,
                                             Polygons infillLinesToPrint,
                                             long lineSpacing_um,
                                             long infillExtendIntoPerimeter_um,
                                             double rotation,
                                             long rotationOffset = 0,
                                             int speed           = 0)
        {
            if (polygonToInfill.Count > 0)
            {
                Polygons outlines = polygonToInfill.Offset(infillExtendIntoPerimeter_um);
                if (outlines.Count > 0)
                {
                    PointMatrix matrix = new PointMatrix(-(rotation + 90));                     // we are rotating the part so we rotate by the negative so the lines go the way we expect

                    outlines.ApplyMatrix(matrix);

                    Aabb boundary = new Aabb(outlines);

                    boundary.min.X = ((boundary.min.X / lineSpacing_um) - 1) * lineSpacing_um - rotationOffset;
                    int      xLineCount       = (int)((boundary.max.X - boundary.min.X + (lineSpacing_um - 1)) / lineSpacing_um);
                    Polygons unclippedPattern = new Polygons();

                    long firstX = boundary.min.X / lineSpacing_um * lineSpacing_um;
                    for (int lineIndex = 0; lineIndex < xLineCount; lineIndex++)
                    {
                        Polygon line = new Polygon();
                        line.Add(new IntPoint(firstX + lineIndex * lineSpacing_um, boundary.min.Y)
                        {
                            Speed = speed
                        });
                        line.Add(new IntPoint(firstX + lineIndex * lineSpacing_um, boundary.max.Y)
                        {
                            Speed = speed
                        });
                        unclippedPattern.Add(line);
                    }

                    PolyTree ret     = new PolyTree();
                    Clipper  clipper = new Clipper();
                    clipper.AddPaths(unclippedPattern, PolyType.ptSubject, false);
                    clipper.AddPaths(outlines, PolyType.ptClip, true);
                    clipper.Execute(ClipType.ctIntersection, ret, PolyFillType.pftPositive, PolyFillType.pftEvenOdd);

                    Polygons    newSegments   = Clipper.OpenPathsFromPolyTree(ret);
                    PointMatrix inversematrix = new PointMatrix(rotation + 90);
                    newSegments.ApplyMatrix(inversematrix);

                    infillLinesToPrint.AddRange(newSegments);
                }
            }
        }
Exemple #3
0
        public static Polygons CreateLineIntersections(this Polygons areaToIntersect, Polygon line)
        {
            var clipper = new Clipper();

            clipper.AddPath(line, PolyType.ptSubject, false);
            clipper.AddPaths(areaToIntersect, PolyType.ptClip, true);

            var clippedLines = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, clippedLines);

            return(Clipper.OpenPathsFromPolyTree(clippedLines));
        }
Exemple #4
0
        public static Polygons CreateLineDifference(this Polygons areaToRemove, Polygons linePolygons)
        {
            var clipper = new Clipper();

            clipper.AddPaths(linePolygons, PolyType.ptSubject, false);
            clipper.AddPaths(areaToRemove, PolyType.ptClip, true);

            var clippedLines = new PolyTree();

            clipper.Execute(ClipType.ctDifference, clippedLines);

            return(Clipper.OpenPathsFromPolyTree(clippedLines));
        }
        public static Polygons CreateLineIntersections(this Polygons polygons, Polygons other)
        {
            Clipper clipper = new Clipper();

            clipper.AddPaths(other, PolyType.ptSubject, false);
            clipper.AddPaths(polygons, PolyType.ptClip, true);

            PolyTree clippedLines = new PolyTree();

            clipper.Execute(ClipType.ctIntersection, clippedLines);

            return(Clipper.OpenPathsFromPolyTree(clippedLines));
        }
        internal static List <List <Curve> > BuildFilling(List <List <Curve> > plist, double spacing, double w, double h, LaserGRBL.RasterConverter.ImageProcessor.Direction dir)
        {
            if (dir == LaserGRBL.RasterConverter.ImageProcessor.Direction.NewInsetFilling)
            {
                return(BuildInsetFilling(plist, spacing));
            }

            List <List <Curve> > flist = new List <List <Curve> >();

            Clipper c = new Clipper();

            AddGridSubject(c, spacing, w, h, dir);
            AddGridClip(plist, c);

            long     t1        = Tools.HiResTimer.TotalMilliseconds;
            PolyTree solution  = new PolyTree();
            bool     succeeded = c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);

            long t2 = Tools.HiResTimer.TotalMilliseconds;

            System.Diagnostics.Debug.WriteLine($"ClipperExecute: {t2 - t1}ms");

            //GraphicsPath result = new GraphicsPath();

            List <List <IntPoint> > ll = Clipper.OpenPathsFromPolyTree(solution);

            foreach (List <IntPoint> pg in ll)
            {
                PointF[] pts = PolygonToPointFArray(pg, resolution);
                if (pts.Count() > 2)
                {
                    ;                    // result.AddPolygon(pts);
                }
                else
                {
                    dPoint a = new dPoint(pts[0].X, pts[0].Y);
                    dPoint b = new dPoint(pts[1].X, pts[1].Y);
                    flist.Add(new List <Curve>()
                    {
                        new Curve(CurveKind.Line, a, a, b, b)
                    });
                }
                //result.AddLine(pts[0], pts[1]);
            }

            flist.Reverse();
            return(flist);
        }
Exemple #7
0
        private Polygons CreateTravelPath(Polygons polygonsToPathAround, Polygons travelPolysLine)
        {
            Clipper clipper = new Clipper();

            clipper.AddPaths(travelPolysLine, PolyType.ptSubject, false);
            clipper.AddPaths(polygonsToPathAround, PolyType.ptClip, true);

            PolyTree clippedLine = new PolyTree();

            //List<List<IntPoint>> intersectedPolys = new List<List<IntPoint>>();
            //clipper.Execute(ClipType.ctDifference, intersectedPolys);

            clipper.Execute(ClipType.ctDifference, clippedLine);

            return(Clipper.OpenPathsFromPolyTree(clippedLine));
        }
        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);
        }
Exemple #9
0
        private static List <List <Curve> > BuildGridFilling(List <List <Curve> > plist, double w, double h, LaserGRBL.GrblFile.L2LConf cnf)
        {
            Clipper c = new Clipper();

            AddGridSubject(c, w, h, cnf);
            AddGridClip(plist, c);

            PolyTree solution  = new PolyTree();
            bool     succeeded = c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);

            if (!succeeded)
            {
                return(null);
            }

            List <List <IntPoint> > ll = Clipper.OpenPathsFromPolyTree(solution);

            return(ToPotraceList(ll, false));
        }
Exemple #10
0
        public static void GenerateLinePaths(Polygons in_outline, ref Polygons result, int extrusionWidth_um, int lineSpacing, int infillExtendIntoPerimeter_um, double rotation, long rotationOffset = 0)
        {
            if (in_outline.Count > 0)
            {
                Polygons outlines = in_outline.Offset(infillExtendIntoPerimeter_um);
                if (outlines.Count > 0)
                {
                    PointMatrix matrix = new PointMatrix(-(rotation + 90)); // we are rotating the part so we rotate by the negative so the lines go the way we expect

                    outlines.applyMatrix(matrix);

                    AABB boundary = new AABB(outlines);

                    boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing - rotationOffset;
                    int      lineCount      = (int)((boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing);
                    Polygons unclipedPatern = new Polygons();
                    long     firstX         = boundary.min.X / lineSpacing * lineSpacing;
                    for (int lineIndex = 0; lineIndex < lineCount; lineIndex++)
                    {
                        Polygon line = new Polygon();
                        line.Add(new IntPoint(firstX + lineIndex * lineSpacing, boundary.min.Y));
                        line.Add(new IntPoint(firstX + lineIndex * lineSpacing, boundary.max.Y));
                        unclipedPatern.Add(line);
                    }

                    PolyTree ret     = new PolyTree();
                    Clipper  clipper = new Clipper();
                    clipper.AddPaths(unclipedPatern, PolyType.ptSubject, false);
                    clipper.AddPaths(outlines, PolyType.ptClip, true);
                    clipper.Execute(ClipType.ctIntersection, ret, PolyFillType.pftPositive, PolyFillType.pftEvenOdd);

                    Polygons    newSegments   = Clipper.OpenPathsFromPolyTree(ret);
                    PointMatrix inversematrix = new PointMatrix((rotation + 90));
                    newSegments.applyMatrix(inversematrix);

                    result.AddRange(newSegments);
                }
            }
        }
Exemple #11
0
        public static void GenerateHexLinePaths(Polygons in_outline, ref Polygons result, int lineSpacing, int infillExtendIntoPerimeter_um, double rotationDegrees, int layerIndex)
        {
            int extraRotationAngle = 0;

            if (in_outline.Count > 0)
            {
                Polygons outlines = in_outline.Offset(infillExtendIntoPerimeter_um);
                if (outlines.Count > 0)
                {
                    int         perIncrementOffset = (int)(lineSpacing * Math.Sqrt(3) / 2 + .5);
                    PointMatrix matrix             = new PointMatrix(-(rotationDegrees + extraRotationAngle));         // we are rotating the part so we rotate by the negative so the lines go the way we expect

                    outlines.ApplyMatrix(matrix);

                    Aabb boundary = new Aabb(outlines);

                    boundary.min.X  = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
                    boundary.min.Y  = ((boundary.min.Y / perIncrementOffset) - 2) * perIncrementOffset;
                    boundary.max.X += lineSpacing;
                    boundary.max.Y += perIncrementOffset;
                    Polygons unclipedPatern = new Polygons();

                    foreach (IntPoint startPoint in StartPositionIterator(boundary, lineSpacing, layerIndex))
                    {
                        Polygon attachedLine = new Polygon();
                        foreach (IntPoint center in IncrementPositionIterator(startPoint, boundary, lineSpacing, layerIndex))
                        {
                            // what we are adding are the little plusses that define the points
                            //        | top
                            //        |
                            //        /\ center
                            //   left/  \ right
                            //
                            IntPoint left  = center + new IntPoint(-lineSpacing / 2, -perIncrementOffset / 3);
                            IntPoint right = center + new IntPoint(lineSpacing / 2, -perIncrementOffset / 3);
                            IntPoint top   = center + new IntPoint(0, perIncrementOffset * 2 / 3);

                            switch (layerIndex % 3)
                            {
                            case 0:                                     // left to right
                                attachedLine.Add(left); attachedLine.Add(center);
                                attachedLine.Add(center); attachedLine.Add(right);
                                unclipedPatern.Add(new Polygon()
                                {
                                    top, center
                                });
                                break;

                            case 1:                                     // left to top
                                attachedLine.Add(left); attachedLine.Add(center);
                                attachedLine.Add(center); attachedLine.Add(top);
                                unclipedPatern.Add(new Polygon()
                                {
                                    center, right
                                });
                                break;

                            case 2:                                     // top to right
                                attachedLine.Add(top); attachedLine.Add(center);
                                attachedLine.Add(center); attachedLine.Add(right);
                                unclipedPatern.Add(new Polygon()
                                {
                                    left, center
                                });
                                break;
                            }
                        }
                        if (attachedLine.Count > 0)
                        {
                            unclipedPatern.Add(attachedLine);
                        }
                    }

                    PolyTree ret     = new PolyTree();
                    Clipper  clipper = new Clipper();
                    clipper.AddPaths(unclipedPatern, PolyType.ptSubject, false);
                    clipper.AddPaths(outlines, PolyType.ptClip, true);
                    clipper.Execute(ClipType.ctIntersection, ret, PolyFillType.pftPositive, PolyFillType.pftEvenOdd);

                    Polygons    newSegments   = Clipper.OpenPathsFromPolyTree(ret);
                    PointMatrix inversematrix = new PointMatrix((rotationDegrees + extraRotationAngle));
                    newSegments.ApplyMatrix(inversematrix);

                    result.AddRange(newSegments);
                }
            }
        }
    protected List <EdgeType[]> CollectChildLoops(SubdividableEdgeLoop <EdgeType> parent, List <DividingEdge> dividingEdges)
    {
        List <EdgeType> knownEdges    = new List <EdgeType>(parent.GetEdgesEnumerable());
        Polygon         parentPoly    = parent.GetPolygon();
        Path            polygonAsClip = parentPoly.ClipperPath(HelperFunctions.clipperScale);

        Vector2[] parentPoints = parent.GetPoints();

        //kinda ugly, these two variables are implicitly paired together
        Paths edgePaths = new Paths();
        List <ILinkedGraphEdgeFactory <EdgeType> > edgePathFactories = new List <ILinkedGraphEdgeFactory <EdgeType> >();
        List <System.Object[]> edgePathFactoriesParams = new List <System.Object[]>();

        foreach (DividingEdge edge in dividingEdges)
        {
            Path edgePath = new Path();
            edgePath.Add(HelperFunctions.GetIntPoint(edge.p1));
            edgePath.Add(HelperFunctions.GetIntPoint(edge.p2));

            PolyTree clippedResults = new PolyTree();
            Clipper  clipper        = new Clipper();

            clipper.AddPath(edgePath, PolyType.ptSubject, false);
            clipper.AddPath(polygonAsClip, PolyType.ptClip, true);
            clipper.Execute(ClipType.ctIntersection, clippedResults);

            Paths subPaths = Clipper.OpenPathsFromPolyTree(clippedResults);
            edgePaths.AddRange(subPaths);
            //if this edge was split into multiple paths when intersecting with parent poly, note that each subpath has the same factory
            foreach (Path path in subPaths)
            {
                edgePathFactories.Add(edge.factory);
                edgePathFactoriesParams.Add(edge.factoryParams);
            }
        }

        DebugLines debug = GameObject.FindObjectOfType <DebugLines>();


        int totalAddedEdges = 0;

        for (int j = 0; j < edgePaths.Count; j++)
        {
            Path edgePath = edgePaths[j];
            ILinkedGraphEdgeFactory <EdgeType> edgeFactory = edgePathFactories[j];
            System.Object[] edgeParams = edgePathFactoriesParams[j];
            //this is almost always just 2 elements in which case it runs once
            for (int i = 0; i < edgePath.Count - 1; i++)
            {
                //convert path back into regular coordinates. Watch out that there is high enough resolution
                //that when this conversion happens, the linkedGraph still thinks points/edges are adjacent and connects them
                Vector2 p1 = HelperFunctions.GetPoint(edgePath[i]);
                Vector2 p2 = HelperFunctions.GetPoint(edgePath[i + 1]);

                LinkedGraph <EdgeType> .ConnectNewEdge(p1, p2, edgeFactory, edgeParams, knownEdges);

                totalAddedEdges++;
            }
        }

        List <EdgeType[]> formedChildLoops = parent.GetInteriorEdgeLoops();

        if (formedChildLoops.Count == 0)
        {
            Debug.Log("No Children " + parent.GetHashCode());
        }
        return(formedChildLoops);
    }
Exemple #13
0
        // This is used with rails since the Clipper Lib does not support
        // difference with the lines of the clip object removed.
        // In other words, to open a door in a closed polygon,
        // we use this to remove the edges that were originally on the clip object
        // leaving only the line segments of the subject that were not inside the clip shape.
        public Paths assembleRailPathsFromClippedSegments(AXClipperLib.PolyTree solutionRail)
        {
            // Take all the clipped segments and connect
            Paths clippedSegments = Clipper.OpenPathsFromPolyTree(solutionRail);

            //Debug.Log ("clipped segments " + clippedSegments.Count);
            //Archimatix.printPaths(clippedSegments);

            Dictionary <Path, AXSegment> axsegs = new Dictionary <Path, AXSegment>();

            foreach (Path p in clippedSegments)
            {
                axsegs.Add(p, new AXSegment(p));
            }

            int cc = 0;

            foreach (Path clipseg in clippedSegments)
            {
                //Debug.Log ("PASS["+cc+"] " + AXGeometryTools.Utilities.pathToString(clipseg));

                if (axsegs[clipseg].prev != null && axsegs[clipseg].next != null)
                {
                    //Debug.Log("Already done");
                    cc++;
                    continue;
                }
                int ccc = 0;
                foreach (Path compseg in clippedSegments)
                {
                    if (compseg == clipseg)
                    {
                        //Debug.Log("COMP["+cc+"]["+ccc+"] skip same");
                        ccc++;
                        continue;
                    }
                    //Debug.Log ("COMP["+cc+"]["+ccc+"]"+AXGeometryTools.Utilities.pathToString(clipseg)+ " with "+AXGeometryTools.Utilities.pathToString(compseg));

                    if (axsegs[clipseg].prev == null && axsegs[compseg].next == null)
                    {
                        if (AXSegment.pointsAreEqual(clipseg[0], compseg[1]))
                        {
                            axsegs[clipseg].prev = axsegs[compseg];
                            axsegs[compseg].next = axsegs[clipseg];
                            //Debug.Log ("prev");
                        }
                    }
                    if (axsegs[clipseg].prev == null && axsegs[compseg].prev == null)
                    {
                        if (AXSegment.pointsAreEqual(clipseg[0], compseg[0]))
                        {
                            compseg.Reverse();
                            axsegs[clipseg].prev = axsegs[compseg];
                            axsegs[compseg].next = axsegs[clipseg];
                        }
                    }
                    if (axsegs[clipseg].next == null && axsegs[compseg].prev == null)
                    {
                        if (AXSegment.pointsAreEqual(clipseg[1], compseg[0]))
                        {
                            axsegs[clipseg].next = axsegs[compseg];
                            axsegs[compseg].prev = axsegs[clipseg];
                        }
                    }
                    if (axsegs[clipseg].next == null && axsegs[compseg].next == null)
                    {
                        if (AXSegment.pointsAreEqual(clipseg[1], compseg[1]))
                        {
                            compseg.Reverse();
                            axsegs[clipseg].next = axsegs[compseg];
                            axsegs[compseg].prev = axsegs[clipseg];
                        }
                    }
                    ccc++;
                }
                cc++;
            }

            // now all segs should be linked.
            // go through each that has no prev and create new paths...
            Paths retPaths = new Paths();

            foreach (KeyValuePair <Path, AXSegment> item in axsegs)
            {
                Path path = item.Key;
                if (item.Value.prev == null)
                {                   // now crawl up the nexts adding pt[1]s
                    AXSegment next = item.Value;
                    while ((next = next.next) != null)
                    {
                        path.Add(next.path[1]);
                    }

                    retPaths.Add(path);
                }
            }

            return(retPaths);
        }
Exemple #14
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();
        }
        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);
        }