GameObject SpawnSegment(bool leftSide) { clipType = leftSide ? ClipperLib.ClipType.ctIntersection : ClipperLib.ClipType.ctDifference; ClipperLib.PolyTree tree = new ClipperLib.PolyTree(); clip.Execute(clipType, tree, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); List <Vector3> verts = new List <Vector3>(); List <Vector3> norms = new List <Vector3>(); List <Vector2> uvs = new List <Vector2>(); List <int> faces = new List <int>(); int idx = 0; foreach (ClipperLib.PolyNode child in tree.Childs) { if (!child.IsHole) { renderTree(child, verts, norms, uvs, faces, ref idx); } } if (verts.Count == 0) { return(null); } Mesh mesh = new Mesh(); mesh.Clear(); mesh.vertices = verts.ToArray(); mesh.uv = uvs.ToArray(); mesh.normals = norms.ToArray(); mesh.triangles = faces.ToArray(); GameObject obj = GameObject.Instantiate <GameObject>(sliceInfo.slicePrefab); obj.GetComponent <MeshFilter>().mesh = mesh; obj.GetComponent <MeshRenderer>().material = sliceInfo.renderObject.material; Transform t = obj.transform; t.localPosition = transform.localPosition; t.localRotation = transform.localRotation; t.localScale = transform.localScale; Vector3 min = new Vector3(1000, 1000, 0); Vector3 max = new Vector3(-1000, -1000, 0); foreach (Vector3 v in verts) { min = Vector3.Min(min, v); max = Vector3.Max(max, v); } BoxCollider2D c = obj.GetComponent <BoxCollider2D>(); c.offset = (max + min) / 2; c.size = max - min; return(obj); }
public SimplifiedGeometryShape(List<List<Vector2>> polygons, PolyTree tree) { this.polygons = polygons; this.tree = tree; Triangulated = false; }
public static PolyTree Combine(List<List<ControlPoint>> polygons) { Clipper.Clear(); Clipper.AddPaths(polygons, PolyType.ptSubject, true); Clipper.AddPaths(polygons, PolyType.ptClip, true); var tree = new PolyTree(); Clipper.Execute(ClipType.ctUnion, tree, PolyFillType.pftNonZero, PolyFillType.pftNonZero); return tree; }
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); }
public static List<List<Vector2>> Combine(List<List<Vector2>> subjectPolygons, List<List<Vector2>> clippingPolygons, FillMode subjectFillMode, FillMode clipFillMode, CombineMode combineMode, out PolyTree tree) { Clipper.Clear(); Clipper.AddPaths(subjectPolygons, PolyType.ptSubject, true); Clipper.AddPaths(clippingPolygons, PolyType.ptClip, true); tree = new PolyTree(); Clipper.Execute(combineMode.ToClipType(), tree, subjectFillMode.ToPolyFillType(), clipFillMode.ToPolyFillType()); return Clipper.ClosedPathsFromPolyTree(tree); }
public static List<List<Vector2>> Outline(List<Vector2> polygon, FillMode fillMode, bool closed, StrokeStyle strokeStyle, float strokeWidth, out PolyTree tree) { List<List<Vector2>> simplified = Clipper.SimplifyPolygon(polygon, fillMode.ToPolyFillType()); Offsetter.Clear(); Offsetter.MiterLimit = strokeStyle.MiterLimit; Offsetter.AddPaths(simplified, (JoinType)strokeStyle.LineJoin, closed ? EndType.etClosedLine : strokeStyle.CapStyle.ToEndType()); tree = new PolyTree(); Offsetter.Execute(ref tree, strokeWidth / 2); return Clipper.ClosedPathsFromPolyTree(tree); }
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); }
//------------------------------------------------------------------------------ public void Execute(ref PolyTree solution, double delta) { solution.Clear(); FixOrientations(); DoOffset(delta); //now clean up 'corners' ... Clipper clpr = new Clipper(); clpr.AddPaths(m_destPolys, PolyType.ptSubject, true); if (delta > 0) { clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect r = Clipper.GetBounds(m_destPolys); List <IntPoint> outer = new List <IntPoint>(4); outer.Add(new IntPoint(r.left - 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.top - 10)); outer.Add(new IntPoint(r.left - 10, r.top - 10)); clpr.AddPath(outer, PolyType.ptSubject, true); clpr.ReverseSolution = true; clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); //remove the outer PolyNode rectangle ... if (solution.ChildCount == 1 && solution.Childs[0].ChildCount > 0) { PolyNode outerNode = solution.Childs[0]; solution.Childs.Capacity = outerNode.ChildCount; solution.Childs[0] = outerNode.Childs[0]; solution.Childs[0].m_Parent = solution; for (int i = 1; i < outerNode.ChildCount; i++) { solution.AddChild(outerNode.Childs[i]); } } else { solution.Clear(); } } }
public static void GenerateLinePaths(Polygons polygonToInfill, ref Polygons infillLinesToPrint, int lineSpacing, int infillExtendIntoPerimeter_um, double rotation, long rotationOffset = 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) - 1) * lineSpacing - rotationOffset; int xLineCount = (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 < xLineCount; 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); infillLinesToPrint.AddRange(newSegments); } } }
public void Execute(ref PolyTree solution, double delta) { solution.Clear(); FixOrientations(); DoOffset(delta); Clipper clipper = new Clipper(0); clipper.AddPaths(m_destPolys, PolyType.ptSubject, true); if (delta > 0.0) { clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect bounds = ClipperBase.GetBounds(m_destPolys); List <IntPoint> list = new List <IntPoint>(4); list.Add(new IntPoint(bounds.left - 10, bounds.bottom + 10)); list.Add(new IntPoint(bounds.right + 10, bounds.bottom + 10)); list.Add(new IntPoint(bounds.right + 10, bounds.top - 10)); list.Add(new IntPoint(bounds.left - 10, bounds.top - 10)); clipper.AddPath(list, PolyType.ptSubject, true); clipper.ReverseSolution = true; clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); if (solution.ChildCount == 1 && solution.Childs[0].ChildCount > 0) { PolyNode polyNode = solution.Childs[0]; solution.Childs.Capacity = polyNode.ChildCount; solution.Childs[0] = polyNode.Childs[0]; solution.Childs[0].m_Parent = solution; for (int i = 1; i < polyNode.ChildCount; i++) { solution.AddChild(polyNode.Childs[i]); } } else { solution.Clear(); } } }
public static List <Polyline> Boolean(ClipType clipType, IEnumerable <Polyline> polyA, IEnumerable <Polyline> polyB, Plane pln, double tolerance, bool evenOddFilling) { var clipper = new Clipper(); var polyfilltype = PolyFillType.pftEvenOdd; if (!evenOddFilling) { polyfilltype = PolyFillType.pftNonZero; } foreach (var plA in polyA) { clipper.AddPath(plA.ToPath2D(pln, tolerance), PolyType.ptSubject, plA.IsClosed); } foreach (var plB in polyB) { clipper.AddPath(plB.ToPath2D(pln, tolerance), PolyType.ptClip, true); } var polytree = new PolyTree(); clipper.Execute(clipType, polytree, polyfilltype, polyfilltype); var output = new List <Polyline>(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var pn in polytree.Iterate()) { if (pn.Contour.Count > 1) { output.Add(pn.Contour.ToPolyline(pln, tolerance, !pn.IsOpen)); } } return(output); }
public static PolyTree Clip_PolyTree(Paths subjpaths, bool blnSubjClosed, Paths clippaths, bool blnClipClosed, ClipType pClipType) { Clipper pClipper = new Clipper(); //var blnadd = pClipper.AddPaths(subjpaths, PolyType.ptSubject, blnSubjClosed); //var blnad2 = pClipper.AddPaths(clippaths, PolyType.ptClip, blnClipClosed); if (pClipper.AddPaths(subjpaths, PolyType.ptSubject, blnSubjClosed) == false || pClipper.AddPaths(clippaths, PolyType.ptClip, blnClipClosed) == false) { throw new ArgumentException("failed to add paths!"); } PolyTree clippedPolyTree = new ClipperLib.PolyTree(); if (pClipper.Execute(pClipType, clippedPolyTree) == false) { throw new ArgumentException("failed to cut!"); } return(clippedPolyTree); }
public List<DelaunayTriangle> TriangulateComplex( PolyTree polyTree, bool ignoreFills = false, bool ignoreHoles = true) { PolyNode rootNode; if (polyTree.Total == 0) { Console.WriteLine(0); rootNode = new PolyNode(); } else { rootNode = polyTree.GetFirst().Parent; } // Equivalent to rootNode.Contour = bounds; var contourField = rootNode.GetType().GetField("m_polygon", BindingFlags.Instance | BindingFlags.NonPublic); if (contourField == null) { throw new Exception("Could not find field contour backing field."); } contourField.SetValue(rootNode, kTriangulatorBounds); var result = new List<DelaunayTriangle>(); int i = 0; for (var currentNode = rootNode; currentNode != null; currentNode = currentNode.GetNext()) { if ((ignoreHoles && currentNode.IsHole) || (ignoreFills && !currentNode.IsHole)) continue; var polyline = DownscalePolygon(currentNode.Contour); var finalPolygon = new Polygon(polyline); foreach (var child in currentNode.Childs) { var shrunkContour = EdgeShrink(child.Contour); var holePoints = DownscalePolygon(shrunkContour); var holePoly = new Polygon(holePoints); finalPolygon.AddHole(holePoly); } P2T.Triangulate(finalPolygon); result.AddRange(finalPolygon.Triangles); } return result; }
private Paths PolyTreeToPolygons(PolyTree tree) { Paths p = new Paths(); PolyNode n = tree.GetFirst(); while (null != n) { p.Add(n.Contour); n = n.GetNext(); } return p; }
private PolyTree PolygonsToPolyTree(Paths polygons) { var tree = new PolyTree(); Clipper c = new Clipper(); c.Clear(); c.AddPaths(polygons, PolyType.ptSubject, true); c.Execute(ClipType.ctUnion, tree); return tree; }
private void Init(IEnumerable<LineStrip> lines, Plane plane, PolyFillType pft = PolyFillType.pftEvenOdd) { transform = plane.CreateMatrix(); transform = Matrix4.Mult(transform, Matrix4.CreateScale(scale)); inverseTransform = Matrix4.Invert(transform); this.plane = plane; polyTree = GetPolyTree(lines, pft); }
private PolyTree GetPolyTree(IEnumerable<LineStrip> lines, PolyFillType pft) { Paths polygons = new Paths(); Clipper c = new Clipper(); c.Clear(); foreach (var line in lines) { polygons.Add(LineStripToPolygon(line)); } polygons = Clipper.SimplifyPolygons(polygons, pft); c.AddPaths(polygons, PolyType.ptSubject, true); PolyTree tree = new PolyTree(); c.Execute(ClipType.ctUnion, tree); return tree; }
public void Union(Slice other) { Clipper c = new Clipper(); c.Clear(); c.AddPaths(Clipper.PolyTreeToPaths(polyTree), PolyType.ptSubject, true); c.AddPaths(Clipper.PolyTreeToPaths(other.polyTree), PolyType.ptClip, true); polyTree = new PolyTree(); c.Execute(ClipType.ctUnion, polyTree); }
public void SubtractFrom(Slice other) { Clipper c = new Clipper(); c.Clear(); c.AddPaths(PolyTreeToPolygons(other.polyTree), PolyType.ptSubject, true); c.AddPaths(PolyTreeToPolygons(polyTree), PolyType.ptClip, true); polyTree = new PolyTree(); c.Execute(ClipType.ctDifference, polyTree); }
public static PolyTree ExecuteClipper(TmxMap map, TmxChunk chunk, TransformPointFunc xfFunc) { ////for(int i=0;i<chunk.Height;i++) //// { //// for(int j=0; j<chunk.Width;j++) //// { //// var raw = chunk.GetRawTileIdAt(j, i); //// if(raw!=0) //// { //// var tid = TmxMath.GetTileIdWithoutFlags(raw); //// var tile = map.Tiles[tid]; //// foreach(var p in tile.ObjectGroup.Objects) //// { //// if(p is TmxHasPoints) //// { //// p.ToEnumerable().Where((x) => //// { //// if (!usingUnityLayerOverride) //// { //// return string.Compare(tuple.Item1.Type, chunk.ParentData.ParentLayer.Name, true) == 0; //// } //// return true; //// }); //// } //// } //// } //// } //// } // Clipper clipper = new Clipper(0); // Tuple<TmxObject, TmxTile, uint> tuple = new Tuple<TmxObject, TmxTile, uint>(null, null, 0); // bool usingUnityLayerOverride = !string.IsNullOrEmpty(chunk.ParentData.ParentLayer.UnityLayerOverrideName); // foreach (var item2 in from h__TransparentIdentifier4 in (from y in Enumerable.Range(0, chunk.Height) // from x in Enumerable.Range(0, chunk.Width) // let rawTileId = chunk.GetRawTileIdAt(x, y) // where rawTileId != 0 // let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) // let tile = map.Tiles[tileId] // from polygon in tile.ObjectGroup.Objects // where polygon is TmxHasPoints // select polygon.ToEnumerable().ToList().TrueForAll // (h__TransparentIdentifier4 => // { // UnityEngine.Debug.Log("liudaodelh"); // tuple = new Tuple<TmxObject, TmxTile, uint>(polygon, tile, rawTileId); // if (!usingUnityLayerOverride) // { // return string.Compare(tuple.Item1.Type, chunk.ParentData.ParentLayer.Name, true) == 0; // } // return true; // })) // select new // { // PositionOnMap = map.GetMapPositionAt((int)tuple.Item1.Position.X + chunk.X, (int)tuple.Item1.Position.Y + chunk.Y, tuple.Item2), // HasPointsInterface = (tuple.Item1 as TmxHasPoints), // TmxObjectInterface = tuple.Item1, // IsFlippedDiagnoally = TmxMath.IsTileFlippedDiagonally(tuple.Item3), // IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(tuple.Item3), // IsFlippedVertically = TmxMath.IsTileFlippedVertically(tuple.Item3), // TileCenter = new PointF((float)tuple.Item2.TileSize.Width * 0.5f, (float)tuple.Item2.TileSize.Height * 0.5f) // }) // { // List<IntPoint> list = new List<IntPoint>(); // SizeF offset = new SizeF(item2.TmxObjectInterface.Position); // PointF[] array = item2.HasPointsInterface.Points.Select((PointF pt) => PointF.Add(pt, offset)).ToArray(); // TmxMath.TransformPoints(array, item2.TileCenter, item2.IsFlippedDiagnoally, item2.IsFlippedHorizontally, item2.IsFlippedVertically); // PointF[] array2 = array; // for (int i = 0; i < array2.Length; i++) // { // PointF pointF = array2[i]; // float x2 = (float)item2.PositionOnMap.X + pointF.X; // float y2 = (float)item2.PositionOnMap.Y + pointF.Y; // IntPoint item = xfFunc(x2, y2); // list.Add(item); // } // list.Reverse(); // clipper.AddPath(list, PolyType.ptSubject, item2.HasPointsInterface.ArePointsClosed()); // } // PolyTree polyTree = new PolyTree(); // clipper.Execute(ClipType.ctUnion, polyTree, SubjectFillRule, ClipFillRule); // return polyTree; ClipperLib.Clipper clipper = new ClipperLib.Clipper(); // Limit to polygon "type" that matches the collision layer name (unless we are overriding the whole layer to a specific Unity Layer Name) bool usingUnityLayerOverride = !String.IsNullOrEmpty(chunk.ParentData.ParentLayer.UnityLayerOverrideName); var polygons = from y in Enumerable.Range(0, chunk.Height) from x in Enumerable.Range(0, chunk.Width) let rawTileId = chunk.GetRawTileIdAt(x, y) where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = map.Tiles[tileId] from polygon in tile.ObjectGroup.Objects where (polygon as TmxHasPoints) != null where usingUnityLayerOverride || String.Compare(polygon.Type, chunk.ParentData.ParentLayer.Name, true) == 0 select new { PositionOnMap = map.GetMapPositionAt(x + chunk.X, y + chunk.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), }; // 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 polygons) { // 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 them 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" clipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed()); } ClipperLib.PolyTree solution = new ClipperLib.PolyTree(); clipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule); return(solution); }
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); }
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; }
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); }
//------------------------------------------------------------------------------ public static void PolyTreeToPolygons(PolyTree polytree, Polygons polygons) { polygons.Clear(); polygons.Capacity = polytree.Total; AddPolyNodeToPolygons(polytree, polygons); }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolyTree polytree) { return Execute(clipType, polytree, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); }
/// <summary> /// Offsets the specified polylines. /// </summary> /// <param name="polylines">A list of polylines</param> /// <param name="openFilletType">Optional: line endtype (Butt, Square, Round)</param> /// <param name="closedFilltetType">Optional: join type: Round, Miter (uses miter parameter) or Square</param> /// <param name="plane">Plane to project the polylines to</param> /// <param name="tolerance">Tolerance: Cutoff point. Eg. point {1.245; 9.244351; 19.3214} with precision {0.1} will be cut /// off to {1.2; 9.2; 19.3}.</param> /// <param name="distance">Distances to offset set of shapes.</param> /// <param name="miter">Miter deterimines how long narrow spikes can become before they are cut off: A miter setting of 2 /// means not longer than 2 times the offset distance. A miter of 25 will give big spikes.</param> /// <param name="arcTolerance">The arc tolerance.</param> /// <param name="outContour">The out contour.</param> /// <param name="outHoles">The out holes.</param> public static void Offset(IEnumerable <Polyline> polylines, List <OpenFilletType> openFilletType, List <ClosedFilletType> closedFilltetType, Plane plane, double tolerance, IEnumerable <double> distance, double miter, double arcTolerance, out List <List <Polyline> > outContour, out List <List <Polyline> > outHoles, EndType endType = default) { outContour = new List <List <Polyline> >(); outHoles = new List <List <Polyline> >(); /* * iEndType: How to handle open ended polygons. * Open Closed * etOpenSquare etClosedLine (fill inside & outside) * etOpenRound etClosedPolygon (fill outside only) * etOpenButt * * See: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/EndType.htm */ /* * jtJoinType * How to fill angles of closed polygons * jtRound: Round * jtMiter: Square with variable distance * jtSquare: Square with fixed distance (jtMiter = 1) */ var cOffset = new ClipperOffset(miter, arcTolerance); var i = 0; foreach (var pl in polylines) { var et = EndType.etOpenButt; var jt = JoinType.jtSquare; if (pl.IsClosed) { et = EndType.etClosedLine; } else if (openFilletType.Count != 0) { var oft = IndexOrLast(openFilletType, i); switch (oft) { case OpenFilletType.Butt: et = EndType.etOpenButt; break; case OpenFilletType.Round: et = EndType.etOpenRound; break; case OpenFilletType.Square: et = EndType.etOpenSquare; break; } } else { et = EndType.etOpenButt; } if (closedFilltetType.Count != 0) { var cft = IndexOrLast(closedFilltetType, i); switch (cft) { case ClosedFilletType.Miter: jt = JoinType.jtMiter; break; case ClosedFilletType.Round: jt = JoinType.jtRound; break; case ClosedFilletType.Square: jt = JoinType.jtSquare; break; } } else { jt = JoinType.jtSquare; } if (endType != default) { et = endType; } cOffset.AddPath(pl.ToPath2D(plane, tolerance), jt, et); i++; } foreach (var offsetDistance in distance) { var tree = new PolyTree(); cOffset.Execute(ref tree, offsetDistance / tolerance); var holes = new List <Polyline>(); var contours = new List <Polyline>(); foreach (var path in tree.Iterate()) { if (path.Contour.Count == 0) { continue; } Polyline polyline = path.Contour.ToPolyline(plane, tolerance, !path.IsOpen); if (path.IsHole) { holes.Add(polyline); } else { contours.Add(polyline); } } outContour.Add(contours); outHoles.Add(holes); } }
//------------------------------------------------------------------------------ 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 void RemoveHoles(float maxPerimiter) { Paths keep = new Paths(); PolyNode node = polyTree.GetFirst(); while (node != null) { if (node.IsHole && node.ChildCount == 0) { var line = LineStripFromPolygon(node.Contour); float length = line.Length(LineStrip.Type.Closed); if (length < maxPerimiter) { // Remove it } else { keep.Add(node.Contour); } } else { keep.Add(node.Contour); } node = node.GetNext(); } Clipper c = new Clipper(); c.Clear(); c.AddPaths(keep, PolyType.ptSubject, true); polyTree = new PolyTree(); c.Execute(ClipType.ctUnion, polyTree); }
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); } } }
//------------------------------------------------------------------------------ public override bool Execute(ClipType clipType, PolyTree polytree, Paths Open, FillRule ft = FillRule.EvenOdd) { return(false); //unsupported }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolyTree polytree, PolyFillType subjFillType, PolyFillType clipFillType) { if (m_ExecuteLocked) return false; m_ExecuteLocked = true; m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; m_UsingPolyTree = true; bool succeeded = ExecuteInternal(); //build the return polygons ... if (succeeded) BuildResult2(polytree); m_ExecuteLocked = false; return succeeded; }
//------------------------------------------------------------------------------ public static Paths OpenPathsFromPolyTree(PolyTree polytree) { Paths result = new Paths(); result.Capacity = polytree.ChildCount; for (int i = 0; i < polytree.ChildCount; i++) if (polytree.Childs[i].IsOpen) result.Add(polytree.Childs[i].Contour); return result; }
//------------------------------------------------------------------------------ private void BuildResult2(PolyTree polytree) { polytree.Clear(); //add each output polygon/contour to polytree ... polytree.m_AllPolys.Capacity = m_PolyOuts.Count; for (int i = 0; i < m_PolyOuts.Count; i++) { OutRec outRec = m_PolyOuts[i]; int cnt = PointCount(outRec.pts); if (cnt < 3) continue; FixHoleLinkage(outRec); PolyNode pn = new PolyNode(); polytree.m_AllPolys.Add(pn); outRec.polyNode = pn; pn.m_polygon.Capacity = cnt; OutPt op = outRec.pts; for (int j = 0; j < cnt; j++) { pn.m_polygon.Add(op.pt); op = op.prev; } } //fixup PolyNode links etc ... polytree.m_Childs.Capacity = m_PolyOuts.Count; for (int i = 0; i < m_PolyOuts.Count; i++) { OutRec outRec = m_PolyOuts[i]; if (outRec.polyNode == null) continue; if (outRec.FirstLeft == null) polytree.AddChild(outRec.polyNode); else outRec.FirstLeft.polyNode.AddChild(outRec.polyNode); } }
//------------------------------------------------------------------------------ public static Paths ClosedPathsFromPolyTree(PolyTree polytree) { Paths result = new Paths(); result.Capacity = polytree.Total; AddPolyNodeToPaths(polytree, NodeType.ntClosed, result); return result; }
public static Polygons CreateLineDifference(this Polygons linePolygons, Polygons removePolygons) { Clipper clipper = new Clipper(); clipper.AddPaths(linePolygons, PolyType.ptSubject, false); clipper.AddPaths(removePolygons, PolyType.ptClip, true); PolyTree clippedLines = new PolyTree(); clipper.Execute(ClipType.ctDifference, clippedLines); return Clipper.OpenPathsFromPolyTree(clippedLines); }
public Slice(Slice fromSlice) { this.plane = fromSlice.plane; this.transform = fromSlice.transform; this.inverseTransform = fromSlice.inverseTransform; this.polyTree = fromSlice.polyTree; }
public static List<Polygons> ProcessIntoSeparatIslands(this Polygons polygons) { List<Polygons> ret = new List<Polygons>(); Clipper clipper = new Clipper(); PolyTree resultPolyTree = new PolyTree(); clipper.AddPaths(polygons, PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, resultPolyTree); polygons.ProcessPolyTreeNodeIntoSeparatIslands(resultPolyTree, ret); return ret; }
/// <summary> /// Create a slice from an open path with the given width /// </summary> /// <param name="path"></param> /// <param name="width"></param> /// <param name="plane"></param> public Slice(LineStrip path, float width, Plane plane, bool closed = false) { this.plane = plane; transform = plane.CreateMatrix(); transform = Matrix4.Mult(transform, Matrix4.CreateScale(scale)); inverseTransform = Matrix4.Invert(transform); polyTree = new PolyTree(); ClipperOffset co = new ClipperOffset(); co.ArcTolerance = scale * 0.0001f; if (closed) { co.AddPath(LineStripToPolygon(path), JoinType.jtRound, EndType.etClosedLine); } else { co.AddPath(LineStripToPolygon(path), JoinType.jtRound, EndType.etOpenRound); } co.Execute(ref this.polyTree, scale * width / 2.0f); }
static public PolyTree FindDistictObjectBounds(ImageBuffer image) { MarchingSquaresByte marchingSquaresData = new MarchingSquaresByte(image, 5, 0); marchingSquaresData.CreateLineSegments(); Polygons lineLoops = marchingSquaresData.CreateLineLoops(1); if (lineLoops.Count == 1) { return null; } // create a bounding polygon to clip against IntPoint min = new IntPoint(long.MaxValue, long.MaxValue); IntPoint max = new IntPoint(long.MinValue, long.MinValue); foreach (Polygon polygon in lineLoops) { foreach (IntPoint point in polygon) { min.X = Math.Min(point.X - 10, min.X); min.Y = Math.Min(point.Y - 10, min.Y); max.X = Math.Max(point.X + 10, max.X); max.Y = Math.Max(point.Y + 10, max.Y); } } Polygon boundingPoly = new Polygon(); boundingPoly.Add(min); boundingPoly.Add(new IntPoint(min.X, max.Y)); boundingPoly.Add(max); boundingPoly.Add(new IntPoint(max.X, min.Y)); // now clip the polygons to get the inside and outside polys Clipper clipper = new Clipper(); clipper.AddPaths(lineLoops, PolyType.ptSubject, true); clipper.AddPath(boundingPoly, PolyType.ptClip, true); PolyTree polyTreeForPlate = new PolyTree(); clipper.Execute(ClipType.ctIntersection, polyTreeForPlate); return polyTreeForPlate; }
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); }