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