/// <summary> /// Conduct a clip operation on this profile. /// </summary> internal void Clip(IEnumerable <Profile> additionalHoles = null, double tolerance = Vector3.EPSILON) { var clipper = new ClipperLib.Clipper(); clipper.AddPath(this.Perimeter.ToClipperPath(tolerance), ClipperLib.PolyType.ptSubject, true); if (this.Voids != null) { clipper.AddPaths(this.Voids.Select(p => p.ToClipperPath(tolerance)).ToList(), ClipperLib.PolyType.ptClip, true); } if (additionalHoles != null) { clipper.AddPaths(additionalHoles.Select(h => h.Perimeter.ToClipperPath(tolerance)).ToList(), ClipperLib.PolyType.ptClip, true); } var solution = new List <List <ClipperLib.IntPoint> >(); var result = clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); // Completely disjoint polygons like a circular pipe // profile will result in an empty solution. if (solution.Count > 0) { var polys = solution.Select(s => s.ToPolygon(tolerance)).ToArray(); this.Perimeter = polys[0]; this.Voids = polys.Skip(1).ToArray(); } }
public static XYPolygon GetIntersection(XYPolygon pol1, XYPolygon pol2) { List<List<IntPoint>> subj = new List<List<IntPoint>>(); subj.Add(new List<IntPoint>(pol1.Points.Count)); foreach(var p in pol1.Points) subj[0].Add(new IntPoint(p.X, p.Y)); List<List<IntPoint>> clip = new List<List<IntPoint>>(); clip.Add(new List<IntPoint>(pol2.Points.Count)); foreach (var p in pol2.Points) clip[0].Add(new IntPoint(p.X, p.Y)); List<List<IntPoint>> solution = new List<List<IntPoint>>(); Clipper c = new Clipper(); c.AddPaths(subj, PolyType.ptSubject, true); c.AddPaths(clip, PolyType.ptClip, true); c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); XYPolygon ToReturn = new XYPolygon(); if (solution.Count > 0) foreach (var p in solution[0]) ToReturn.Points.Add(new XYPoint(p.X,p.Y)); return ToReturn; }
private double ClippedArea() { if (this.Voids == null || this.Voids.Count == 0) { return(this.Perimeter.Area()); } var clipper = new ClipperLib.Clipper(); var normal = Perimeter.Normal(); if (normal.IsAlmostEqualTo(Vector3.ZAxis)) { clipper.AddPath(Perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(this.Voids.Select(p => p.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); } else { var transform = new Transform(Perimeter.Start, normal); transform.Invert(); var perimeter = Perimeter.TransformedPolygon(transform); clipper.AddPath(perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(this.Voids.Select(p => p.TransformedPolygon(transform).ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); } var solution = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); return(solution.Sum(s => ClipperLib.Clipper.Area(s)) / Math.Pow(1.0 / Vector3.EPSILON, 2)); }
//Apply a polygon clipper operation on subject vertices using cut vertices public static List<Vector2[]> ClipPoly(Vector2[] subject, Vector2[] cut, ClipType operation) { List<Vector2[]> cutPolygons = new List<Vector2[]>(); Paths subj = new Paths(1); subj.Add(Vector2ToIntList(subject)); Paths clip = new Paths(1); clip.Add(Vector2ToIntList(cut)); Paths solution = new Paths(); Clipper c = new Clipper(); c.AddPaths(subj, PolyType.ptSubject, true); c.AddPaths(clip, PolyType.ptClip, true); c.Execute(operation,solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); /* for(int i = 0; i<solution.Count; i++){ if( Mathf.Abs((float)Clipper.Area(solution[i])) > ignoreArea){ cutPolygons.Add( IntListToVector2( solution[i] )); } } */ return IntListsToVector2(solution); }
public static List<List<Vector2>> clip(List<Vector2> boundary, List<Vector2> region) { Polygons boundaryPoly = createPolygons(boundary); Polygons regionPoly = createPolygons(region); //clip triangular polygon against the boundary polygon Polygons result = new Polygons(); Clipper c = new Clipper(); c.AddPaths(regionPoly, PolyType.ptClip, true); c.AddPaths(boundaryPoly, PolyType.ptSubject, true); c.Execute(ClipType.ctIntersection, result, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); List<List<Vector2>> clippedPolygons = new List<List<Vector2>>(); foreach (Polygon poly in result) { List<Vector2> clippedPoly = new List<Vector2>(); foreach (IntPoint p in poly) { clippedPoly.Add(new Vector2(p.X, p.Y) / multiplier); } clippedPolygons.Add(clippedPoly); } return clippedPolygons; }
public List<List<IntPoint>> Run (List<List<IntPoint>> subject, double scale) { var c = ToClipper (scale, clip); var clipper = new Clipper (); clipper.AddPaths (subject, PolyType.ptSubject, subjectClosed); clipper.AddPaths (c, PolyType.ptClip, true); var solution = new List<List<IntPoint>> (); clipper.Execute (type, solution, subjectFill, clipFill); return solution; }
List<IntPoint> GetArea(Mesh mesh) { int[] triangles = mesh.triangles; Vector3[] vertices = mesh.vertices; List<Vector2> list = new List<Vector2>(); //Debug.Log("ver count: " + vertices.Length + " triangle: " + triangles.Length); float y = float.MinValue; for(int i=0; i < triangles.Length; i = i+3){ Vector3 p0 = vertices[triangles[i]]; Vector3 p1 = vertices[triangles[i+1]]; Vector3 p2 = vertices[triangles[i+2]]; if(Approximately(p0.y, p1.y) && Approximately(p0.y, p2.y)){ //Debug.Log(string.Format("({0}, {1}, {2})", p0, p1, p2)); if(y == float.MinValue){ y = p0.y; } if(Approximately(p0.y, y)){ list.Add(new Vector2(p0.x, p0.z)); list.Add(new Vector2(p1.x, p1.z)); list.Add(new Vector2(p2.x, p2.z)); } } } //Debug.Log("list: " + list.Count); List<List<IntPoint>> paths = new List<List<IntPoint>>(); for(int i=0; i < list.Count; i = i+3){ List<IntPoint> path = new List<IntPoint>(); for(int j=0; j < 3; j++){ path.Add(new IntPoint(list[i+j].x * clipScalling, list[i+j].y * clipScalling)); } paths.Add(path); } Clipper clipper = new Clipper(); List<List<IntPoint>> solution = new List<List<IntPoint>>(); clipper.AddPaths(paths, PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, solution); if(solution.Count > 0){ return solution[0]; } return null; }
public double FitnessFunctionG() { // return 3; double fitnessFunctionVar = 0; int precision = 100; Clipper cc = new ClipperLib.Clipper(); Paths solution = new Paths(); Paths subjects = new Paths(); Paths clips = new Paths(); foreach (Room room in collection) { subjects.Add(new Path(room.GetClipperLibPath(precision))); } IntPoint boundaryA = new IntPoint( (int)((boundary.GetBoundingBox(false).Center.X - boundary.GetBoundingBox(false).Diagonal.X / 2) * precision) , (int)((boundary.GetBoundingBox(false).Center.Y - boundary.GetBoundingBox(false).Diagonal.Y / 2) * precision)); IntPoint boundaryB = new IntPoint( (int)((boundary.GetBoundingBox(false).Center.X - boundary.GetBoundingBox(false).Diagonal.X / 2) * precision) , (int)((boundary.GetBoundingBox(false).Center.Y + boundary.GetBoundingBox(false).Diagonal.Y / 2) * precision)); IntPoint boundaryC = new IntPoint( (int)((boundary.GetBoundingBox(false).Center.X + boundary.GetBoundingBox(false).Diagonal.X / 2) * precision) , (int)((boundary.GetBoundingBox(false).Center.Y + boundary.GetBoundingBox(false).Diagonal.Y / 2) * precision)); IntPoint boundaryD = new IntPoint( (int)((boundary.GetBoundingBox(false).Center.X + boundary.GetBoundingBox(false).Diagonal.X / 2) * precision) , (int)((boundary.GetBoundingBox(false).Center.Y - boundary.GetBoundingBox(false).Diagonal.Y / 2) * precision)); clips.Add(new Path(new List <IntPoint>() { boundaryA, boundaryB, boundaryC, boundaryD })); cc.AddPaths(subjects, PolyType.ptSubject, true); cc.AddPaths(clips, PolyType.ptClip, true); cc.Execute(ClipType.ctIntersection, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero); foreach (Path path in solution) { fitnessFunctionVar += Clipper.Area(path); } return(fitnessFunctionVar); }
public void DoCircleClipping(Vector2 pos, float radius) { List<VertexPosition[]> shape_old = shape; bool isCleared = false; int sc = 0; for (sc = 0; sc < shape_old.Count; sc++) { ClippingPolygons subj = new ClippingPolygons(1); subj.Add(new ClippingPolygon(shape_old[sc].Length)); foreach (VertexPosition point in shape_old[sc]) { subj[0].Add(new IntPoint((int)((point.Position.X) * accuracy), (int)((point.Position.Y) * accuracy))); } ClippingPolygons clip = new ClippingPolygons(1); clip.Add(new ClippingPolygon()); for (int alpha = 0; alpha < 360; alpha += 10) { clip[0].Add(new IntPoint((int)(((Math.Sin((alpha) * Math.PI / 180.0) * radius) + pos.X) * accuracy), (int)(((Math.Cos((alpha) * Math.PI / 180.0) * radius) + pos.Y) * accuracy))); //log.Log(pos.ToString()); } ClippingPolygons solution = new ClippingPolygons(); Clipper c = new Clipper(); c.AddPolygons(subj, PolyType.ptSubject); c.AddPolygons(clip, PolyType.ptClip); if (c.Execute(ClipType.ctDifference, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd)) { if (!isCleared) { shape = new List<VertexPosition[]>(); drawer.Clear(); isCleared = true; } for (int f = 0; f < solution.Count; f++) { shape.Add(new VertexPosition[solution[f].Count]); drawer.Add(new ChunkDrawer(ref log)); for (int i = 0; i < solution[f].Count; i++) { shape[shape.Count-1][i] = new VertexPosition(solution[f][i].X / accuracy, solution[f][i].Y / accuracy, 0); } drawer[shape.Count-1].BufferVertices(shape[shape.Count - 1]); } } } }
public double RelativeAreaDiff (List<List<IntPoint>> actual, List<List<IntPoint>>expected) { var expectedArea = expected.Sum (path => Clipper.Area (path)); var difference = new List<List<IntPoint>> (); var clipper = new Clipper (); clipper.AddPaths (actual, PolyType.ptSubject, true); clipper.AddPaths (expected, PolyType.ptClip, true); clipper.Execute (ClipType.ctXor, difference, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); var differenceArea = difference.Sum (path => Clipper.Area (path)); return Math.Abs (differenceArea) / Math.Abs (expectedArea); }
// perform xor public static Paths ClipperXor(this Polygon clip, Polygon subject) { var subj = new Paths(); subj.Add(subject.ToClipperPath()); var clp = new Paths(); clp.Add(clip.ToClipperPath()); var result = new Paths(); var c = new Clipper(); c.Execute(ClipType.ctXor, result, PolyFillType.pftPositive, PolyFillType.pftPositive); return result; }
/// <summary> /// Checks if polygons are intersecting /// </summary> /// <param name="p1">Subject polygon</param> /// <param name="p2">Clip polygon(s)</param> /// <returns>true if intersects</returns> public static bool IsIntersects(Paths p1, params Paths[] p2) { Clipper c = new Clipper(); Paths solution = new Paths(); c.AddPaths(p1, PolyType.ptSubject, true); for(int i = 0; i < p2.Length; i++) c.AddPaths(p2[i], PolyType.ptClip, true); c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); return solution.Count != 0; }
public static Vector3[] MeshDifference(Vector3[] mesh1, Vector3[] mesh2) { List<IntPoint> subj = MeshToPath3D(mesh1); List<IntPoint> clip = MeshToPath3D(mesh2); List<List<IntPoint>> solution = new List<List<IntPoint>>(); Clipper c = new Clipper(); c.AddPath(subj, PolyType.ptSubject, true); c.AddPath(clip, PolyType.ptClip, true); c.Execute(ClipType.ctDifference, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); return PathToMesh3D(solution[0]); }
public Paths BuildOutlines(float growamt = 5 *3) { ClipperLib.Clipper cp = new ClipperLib.Clipper(); Path pp = new Path(); pp.AddRange(Vertices); Paths pps = new Paths(); pps.Add(pp); var Res = Clipper.OffsetPolygons(pps, growamt, JoinType.jtRound); return(Res); }
// perform union on a list of polygons public static Paths ClipperUnion(this List<Polygon> polygons) { var subj = new Paths(polygons.Count); var clip = new Paths(polygons.Count); foreach (var polygon in polygons) { subj.Add(polygon.ToClipperPath()); clip.Add(polygon.ToClipperPath()); } var solution = new Paths(); var c = new Clipper(); c.AddPaths(subj, PolyType.ptSubject, true); c.AddPaths(clip, PolyType.ptClip, true); c.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); return solution; }
public static List<List<IntPoint>> ClipPolygons(List<Polygon> polygons) { var subj = new List<List<IntPoint>>(polygons.Count); var clip = new List<List<IntPoint>>(polygons.Count); foreach (var polygon in polygons) { subj.Add(polygon.ToClipperPath()); clip.Add(polygon.ToClipperPath()); } var solution = new List<List<IntPoint>>(); var c = new Clipper(); c.AddPaths(subj, PolyType.ptSubject, true); c.AddPaths(clip, PolyType.ptClip, true); c.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftEvenOdd); return solution; }
private double ClippedArea() { if (this.Voids == null || this.Voids.Count == 0) { return(this.Perimeter.Area()); } var clipper = new ClipperLib.Clipper(); clipper.AddPath(this.Perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(this.Voids.Select(p => p.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); var solution = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); return(solution.Sum(s => ClipperLib.Clipper.Area(s)) / Math.Pow(1.0 / Vector3.EPSILON, 2)); }
public static List<Vector2> Add(this Shape shape, Shape secondShape, Action<Shape> completed) { List<Vector2> points = new List<Vector2>(); Clipper c = new Clipper(); List<List<IntPoint>> subj = new List<List<IntPoint>>(); List<List<IntPoint>> clip = new List<List<IntPoint>>(); List<List<IntPoint>> solution = new List<List<IntPoint>>(); List<IntPoint> p1 = new List<IntPoint>(); List<IntPoint> p2 = new List<IntPoint>(); int i = 0, l = shape.Points.Length; Vector2 pos = shape.BuiltGameObject.transform.position; for(;i<l;++i) { IntPoint ip = new IntPoint(shape.Points[i].x + pos.x,shape.Points[i].y + pos.y); p1.Add(ip); } p1.Add(p1[0]); pos = secondShape.BuiltGameObject.transform.position; i = 0; l = secondShape.Points.Length; for(;i<l;++i) { IntPoint ip = new IntPoint(secondShape.Points[i].x + pos.x,secondShape.Points[i].y + pos.y); p2.Add(ip); } p2.Add(p2[0]); subj.Add(p1); clip.Add(p2); c.AddPaths(subj,PolyType.ptSubject,true); c.AddPaths(clip,PolyType.ptClip,true); c.Execute(ClipType.ctUnion,solution); i = 0; l = solution[0].Count; for(;i<l;++i) { float x = System.Convert.ToSingle(solution[0][i].X); float y = System.Convert.ToSingle(solution[0][i].Y); points.Add(new Vector2(x,y)); } Mesh2D.Instance.ReBuild(shape.BuiltGameObject,points,completed,shape.Col); return points; }
public List<Polygon> Clip(Polygon p1, Polygon p2, OpType operation) { List<IntPoint> pol1 = new List<IntPoint>(); List<IntPoint> pol2 = new List<IntPoint>(); List<List<IntPoint>> res = new List<List<IntPoint>>(); foreach (Point point in p1.Points) { pol1.Add(new IntPoint(point.X, point.Y)); } foreach (Point point in p2.Points) { pol2.Add(new IntPoint(point.X, point.Y)); } Clipper clipper = new Clipper(); clipper.AddPolygon(pol1, PolyType.ptSubject); clipper.AddPolygon(pol2, PolyType.ptClip); switch (operation) { case OpType.Difference: clipper.Execute(ClipType.ctDifference, res); break; case OpType.Intersection: clipper.Execute(ClipType.ctIntersection, res); break; case OpType.Union: clipper.Execute(ClipType.ctUnion, res); break; case OpType.Xor: clipper.Execute(ClipType.ctXor, res); break; } List<Polygon> ret = new List<Polygon>(); foreach (var poly in res) { Polygon pol = new Polygon() { Points = new List<Point>() }; foreach (var poi in poly) { pol.Points.Add(new Point() { X = poi.X, Y = poi.Y }); } ret.Add(pol); } return ret; }
private PathStorage CombinePaths(IVertexSource a, IVertexSource b, ClipType clipType) { List<List<IntPoint>> aPolys = VertexSourceToClipperPolygons.CreatePolygons(a); List<List<IntPoint>> bPolys = VertexSourceToClipperPolygons.CreatePolygons(b); Clipper clipper = new Clipper(); clipper.AddPaths(aPolys, PolyType.ptSubject, true); clipper.AddPaths(bPolys, PolyType.ptClip, true); List<List<IntPoint>> intersectedPolys = new List<List<IntPoint>>(); clipper.Execute(clipType, intersectedPolys); PathStorage output = VertexSourceToClipperPolygons.CreatePathStorage(intersectedPolys); output.Add(0, 0, ShapePath.FlagsAndCommand.CommandStop); return output; }
/// <summary> /// Perform a union operation, returning a new profile that is the union of the current profile with the other profile /// <param name="profile">The profile with which to create a union.</param> /// <param name="tolerance">An optional tolerance.</param> /// </summary> public Profile Union(Profile profile, double tolerance = Vector3.EPSILON) { var clipper = new ClipperLib.Clipper(); clipper.AddPath(this.Perimeter.ToClipperPath(tolerance), PolyType.ptSubject, true); clipper.AddPath(profile.Perimeter.ToClipperPath(tolerance), PolyType.ptClip, true); if (this.Voids != null && this.Voids.Count > 0) { clipper.AddPaths(this.Voids.Select(v => v.ToClipperPath(tolerance)).ToList(), PolyType.ptSubject, true); } if (profile.Voids != null && profile.Voids.Count > 0) { clipper.AddPaths(profile.Voids.Select(v => v.ToClipperPath(tolerance)).ToList(), PolyType.ptClip, true); } var solution = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipType.ctUnion, solution); return(new Profile(solution.Select(s => s.ToPolygon(tolerance)).ToList())); }
public void DoCircleClipping(Vector2 pos, float radius) { ClippingPolygons subj = new ClippingPolygons(1); subj.Add(new ClippingPolygon(shape.Length)); foreach(VertexPosition point in shape){ subj[0].Add(new IntPoint((int)((point.Position.X) * accuracy), (int)((point.Position.Y) * accuracy))); } ClippingPolygons clip = new ClippingPolygons(1); clip.Add(new ClippingPolygon()); for (int alpha = 0; alpha < 360; alpha += 10) { clip[0].Add(new IntPoint((int)(((Math.Sin((alpha) * Math.PI / 180.0) * radius)+pos.X) * accuracy), (int)(((Math.Cos((alpha) * Math.PI / 180.0) * radius)+pos.Y) * accuracy))); //log.Log(pos.ToString()); } ClippingPolygons solution = new ClippingPolygons(); Clipper c = new Clipper(); c.AddPolygons(subj, PolyType.ptSubject); c.AddPolygons(clip, PolyType.ptClip); if (c.Execute(ClipType.ctDifference, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd)) { for (int f = 0; f < solution.Count; f++) { if (f == 0) { shape = new VertexPosition[solution[f].Count]; for (int i = 0; i < solution[f].Count; i++) { shape[i] = new VertexPosition(solution[f][i].X / accuracy, solution[f][i].Y / accuracy, 0); } } } } BufferVertices(ref shape); }
public void Clip(double x0, double x1, double y0, double y1) { var p00 = new Point3d(x0, y0, 0.0); var p01 = new Point3d(x0, y1, 0.0); var p11 = new Point3d(x1, y1, 0.0); var p10 = new Point3d(x1, y0, 0.0); var clip = new[] { p00, p10, p11, p01, p00 }.ToPolygon(Plane.WorldXY, Unit); var clipper = new Clipper(); clipper.AddPaths(Polygons, PolyType.ptSubject, true); clipper.AddPath(clip, PolyType.ptClip, true); var solution = new List<List<IntPoint>>(); clipper.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); Polygons = solution; Curves = Polygons.ToCurves(Plane, Unit); }
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 JsonResult GetBySponsor(ObjectId id) { var spots = Context.SponsorSpots.GetSpotsBySponsors(id); Clipper clipper = new Clipper(); var polygons = new List<List<IntPoint>>(); var scale = 100000000.0; foreach (var spot in spots) { var polygon = new List<IntPoint>(); foreach (var coord in spot.SpotShape) { polygon.Add(new IntPoint(coord.Longitude * scale, coord.Latitude * scale)); } polygons.Add(polygon); } var solution = new List<List<IntPoint>>(); clipper.AddPaths(polygons, PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero); var results = new List<Spot>(); foreach (var shape in solution) { var resultShape = new Spot(); foreach (var item in shape) { resultShape.SpotShape.Add(new Coordinate { Latitude = item.Y / scale, Longitude = item.X / scale }); } results.Add(resultShape); } return Json(new { success = true, results = results }, JsonRequestBehavior.AllowGet); }
private PathStorage CombinePaths(IVertexSource a, IVertexSource b, ClipType clipType) { List<List<IntPoint>> aPolys = CreatePolygons(a); List<List<IntPoint>> bPolys = CreatePolygons(b); Clipper clipper = new Clipper(); clipper.AddPaths(aPolys, PolyType.ptSubject, true); clipper.AddPaths(bPolys, PolyType.ptClip, true); List<List<IntPoint>> intersectedPolys = new List<List<IntPoint>>(); clipper.Execute(clipType, intersectedPolys); PathStorage output = new PathStorage(); foreach (List<IntPoint> polygon in intersectedPolys) { bool first = true; foreach (IntPoint point in polygon) { if (first) { output.Add(point.X / 1000.0, point.Y / 1000.0, ShapePath.FlagsAndCommand.CommandMoveTo); first = false; } else { output.Add(point.X / 1000.0, point.Y / 1000.0, ShapePath.FlagsAndCommand.CommandLineTo); } } output.ClosePolygon(); } output.Add(0, 0, ShapePath.FlagsAndCommand.CommandStop); return output; }
public void OriginalClipperFileBasedTest() { var testData = LoadTestHelper.LoadFromFile("TestData/tests.txt"); foreach (var test in testData.Values) { var subjects = test.Subjects.ToOriginal(); var clips = test.Clips.ToOriginal(); var clipper = new ClipperLib.Clipper(); clipper.AddPaths(subjects, PolyType.ptSubject, true); clipper.AddPaths(clips, PolyType.ptClip, true); var originalSolution = new List <List <ClipperLib.IntPoint> >(); var clipType = (ClipType)Enum.Parse(typeof(ClipType), $"ct{test.ClipOperation}", true); var fillType = (PolyFillType)Enum.Parse(typeof(PolyFillType), $"pft{test.FillType}", true); Assert.IsTrue(clipper.Execute(clipType, originalSolution, fillType)); Assert.AreEqual(test.Solution.Count, originalSolution.Count, test.Caption); var solution = originalSolution.ToNew(); // TODO: reinclude these tests once test data is verified. var ignoreTestNumbers = new[] { 36, 38, 39, 44, 46, 48, 51, 52, 59, 64, 67, 69 }; if (ignoreTestNumbers.Contains(test.TestNumber)) { continue; } Assert.AreEqual(test.Solution.Count, solution.Count, $"{test.TestNumber}: {test.Caption}"); // Match points, THIS IS DESTRUCTIVE TO BOTH THE TEST DATA AND RESULT DATA. Assert.IsTrue(AreSame(test, solution)); // If we had an exact match then both solutions should now be empty. Assert.AreEqual(0, test.Solution.Count, $"{test.TestNumber}: {test.Caption}"); Assert.AreEqual(0, solution.Count, $"{test.TestNumber}: {test.Caption}"); } }
private static void TestCliper() { Paths subj = new Paths(2); subj.Add(new Path(4)); subj[0].Add(new IntPoint(180, 200)); subj[0].Add(new IntPoint(260, 200)); subj[0].Add(new IntPoint(260, 150)); subj[0].Add(new IntPoint(180, 150)); subj.Add(new Path(3)); subj[1].Add(new IntPoint(215, 160)); subj[1].Add(new IntPoint(230, 190)); subj[1].Add(new IntPoint(200, 190)); Paths clip = new Paths(1); clip.Add(new Path(4)); clip[0].Add(new IntPoint(190, 210)); clip[0].Add(new IntPoint(240, 210)); clip[0].Add(new IntPoint(240, 130)); clip[0].Add(new IntPoint(190, 130)); //DrawPolygons(subj, Color.FromArgb(0x16, 0, 0, 0xFF), // Color.FromArgb(0x60, 0, 0, 0xFF)); //DrawPolygons(clip, Color.FromArgb(0x20, 0xFF, 0xFF, 0), // Color.FromArgb(0x30, 0xFF, 0, 0)); Paths solution = new Paths(); Clipper c = new Clipper(); c.AddPaths(subj, PolyType.ptSubject, true); c.AddPaths(clip, PolyType.ptClip, true); c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); //DrawPolygons(solution, Color.FromArgb(0x30, 0, 0xFF, 0), // Color.FromArgb(0xFF, 0, 0x66, 0)); }
/// <summary> /// Joins all the polygones. /// ClipType: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/ClipType.htm /// PolyFillType: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/PolyFillType.htm /// </summary> /// <param name="sList">The s list.</param> /// <param name="cType">Type of the c.</param> /// <param name="pType">Type of the p.</param> /// <param name="pFType1">The p f type1.</param> /// <param name="pFType2">The p f type2.</param> /// <returns></returns> public static List<Polygon> ELJoinPolygons(this List<Polygon> sList, ClipType cType, PolyType pType = PolyType.ptClip, PolyFillType pFType1 = PolyFillType.pftNonZero, PolyFillType pFType2 = PolyFillType.pftNonZero) { var p = ELClipPolygons(sList); var tList = new List<List<IntPoint>>(); var c = new Clipper(); c.AddPaths(p, pType, true); c.Execute(cType, tList, pFType1, pFType2); return ToPolygons(tList); }
/// <summary> /// Joins all the polygones in the list in one polygone if they interect. /// </summary> /// <param name="sList">The polygon list.</param> /// <returns></returns> public static List<Polygon> ELJoinPolygons(this List<Polygon> sList) { var p = ELClipPolygons(sList); var tList = new List<List<IntPoint>>(); var c = new Clipper(); c.AddPaths(p, PolyType.ptClip, true); c.Execute(ClipType.ctUnion, tList, PolyFillType.pftNonZero, PolyFillType.pftNonZero); return ToPolygons(tList); }
private static void InterceptionQ(Obj_AI_Hero Enemy) { Geometry.Polygon.Circle Qspellpoly = new Geometry.Polygon.Circle(PreCastPos(Enemy, 0.6f), 130f); Paths subjs = new Paths(); foreach (var bla in WPPolygon(Enemy).ToPolygons()) { subjs.Add(bla.ToClipperPath()); } Paths clips = new Paths(1); clips.Add(Qspellpoly.ToClipperPath()); Paths solution = new Paths(); Clipper c = new Clipper(); c.AddPaths(subjs, PolyType.ptSubject, true); c.AddPaths(clips, PolyType.ptClip, true); c.Execute(ClipType.ctIntersection, solution); foreach (var bli in solution.ToPolygons()) { bli.Draw(System.Drawing.Color.Blue); } }
/// <summary> /// Applies a buoyant force, drag and lift to an object submerged in the water. /// </summary> /// <param name="subjectPoly"> The polygon of the object in the water.</param> /// <param name="minIndex"> The min index for a value in the "waterLinePoints" list. </param> /// <param name="maxIndex"> The max index for a value in the "waterLinePoints" list. </param> /// <param name="isIntersecting"> Are the subject and clipping polygon intersecting?. </param> private List<List<Vector2>> GetIntersectionPolygon(Vector2[] subjectPoly, int minIndex, int maxIndex, out bool isIntersecting) { Vector2 bottomHandleGlobalPos = transform.TransformPoint(water2D.handlesPosition[1]); List<List<Vector2>> intersectionPoly = new List<List<Vector2>>(); List<Vector2> clipPolygon = new List<Vector2>(); Clipper clipper = new Clipper(); Paths solutionPath = new Paths(); Path subjPath = new Path(); Path clipPath = new Path(); int len, len2, min, max; isIntersecting = true; if (surfaceVertsCount > meshSegmentsPerWaterLineSegment) { min = (int)Mathf.Floor(minIndex / meshSegmentsPerWaterLineSegment); max = (int)Mathf.Floor(maxIndex / meshSegmentsPerWaterLineSegment) + 1; if (max > waterLinePoints.Count - 2) max = waterLinePoints.Count - 2; for (int i = min; i <= max; i++) { clipPolygon.Add(waterLinePoints[i]); } int last = clipPolygon.Count - 1; clipPolygon.Add(new Vector2(clipPolygon[last].x, bottomHandleGlobalPos.y)); clipPolygon.Add(new Vector2(clipPolygon[0].x, bottomHandleGlobalPos.y)); } else { Vector2 vertGlobalPos = transform.TransformPoint(vertices[surfaceVertsCount]); clipPolygon.Add(vertGlobalPos); vertGlobalPos = transform.TransformPoint(vertices[surfaceVertsCount + surfaceVertsCount - 1]); clipPolygon.Add(new Vector2(vertGlobalPos.x, vertGlobalPos.y)); int last = clipPolygon.Count - 1; clipPolygon.Add(new Vector2(clipPolygon[last].x, bottomHandleGlobalPos.y)); clipPolygon.Add(new Vector2(clipPolygon[0].x, bottomHandleGlobalPos.y)); } if (showClippingPlolygon) { for (int i = 0; i < clipPolygon.Count; i++) { if (i < clipPolygon.Count - 1) Debug.DrawLine(clipPolygon[i], clipPolygon[i + 1], Color.green); else Debug.DrawLine(clipPolygon[i], clipPolygon[0], Color.green); } } len = subjectPoly.Length; for (int i = 0; i < len; i++) { subjPath.Add(new IntPoint(subjectPoly[i].x * scaleFactor, subjectPoly[i].y * scaleFactor)); } len = clipPolygon.Count; for (int i = 0; i < len; i++) { clipPath.Add(new IntPoint(clipPolygon[i].x * scaleFactor, clipPolygon[i].y * scaleFactor)); } clipper.AddPath(subjPath, PolyType.ptSubject, true); clipper.AddPath(clipPath, PolyType.ptClip, true); clipper.Execute(ClipType.ctIntersection, solutionPath, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); if (solutionPath.Count != 0) { len = solutionPath.Count; for (int i = 0; i < len; i++) { len2 = solutionPath[i].Count; List<Vector2> list = new List<Vector2>(); for (int j = 0; j < len2; j++) { list.Add(new Vector2(solutionPath[i][j].X / scaleFactor, solutionPath[i][j].Y / scaleFactor)); } intersectionPoly.Add(list); } return intersectionPoly; } else { isIntersecting = false; return null; } }
public static NFP simplifyFunction(NFP polygon, bool inside) { var tolerance = 4 * Config.curveTolerance; // give special treatment to line segments above this length (squared) var fixedTolerance = 40 * Config.curveTolerance * 40 * Config.curveTolerance; int i, j, k; if (Config.simplify) { /* * // use convex hull * var hull = new ConvexHullGrahamScan(); * for(var i=0; i<polygon.length; i++){ * hull.addPoint(polygon[i].x, polygon[i].y); * } * * return hull.getHull();*/ var hull = Background.getHull(polygon); if (hull != null) { return(hull); } else { return(polygon); } } var cleaned = cleanPolygon2(polygon); if (cleaned != null && cleaned.length > 1) { polygon = cleaned; } else { return(polygon); } // polygon to polyline var copy = polygon.slice(0); copy.push(copy[0]); // mark all segments greater than ~0.25 in to be kept // the PD simplification algo doesn't care about the accuracy of long lines, only the absolute distance of each point // we care a great deal for (i = 0; i < copy.length - 1; i++) { var p1 = copy[i]; var p2 = copy[i + 1]; var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); if (sqd > fixedTolerance) { p1.marked = true; p2.marked = true; } } var simple = Simplify.simplify(copy, tolerance, true); // now a polygon again //simple.pop(); simple.Points = simple.Points.Take(simple.Points.Count() - 1).ToArray(); // could be dirty again (self intersections and/or coincident points) simple = cleanPolygon2(simple); // simplification process reduced poly to a line or point if (simple == null) { simple = polygon; } var offsets = polygonOffsetDeepNest(simple, inside ? -tolerance : tolerance); NFP offset = null; double offsetArea = 0; List <NFP> holes = new List <NFP>(); for (i = 0; i < offsets.Length; i++) { var area = GeometryUtil.polygonArea(offsets[i]); if (offset == null || area < offsetArea) { offset = offsets[i]; offsetArea = area; } if (area > 0) { holes.Add(offsets[i]); } } // mark any points that are exact for (i = 0; i < simple.length; i++) { var seg = new NFP(); seg.AddPoint(simple[i]); seg.AddPoint(simple[i + 1 == simple.length ? 0 : i + 1]); var index1 = find(seg[0], polygon); var index2 = find(seg[1], polygon); if (index1 + 1 == index2 || index2 + 1 == index1 || (index1 == 0 && index2 == polygon.length - 1) || (index2 == 0 && index1 == polygon.length - 1)) { seg[0].exact = true; seg[1].exact = true; } } var numshells = 4; NFP[] shells = new NFP[numshells]; for (j = 1; j < numshells; j++) { var delta = j * (tolerance / numshells); delta = inside ? -delta : delta; var shell = polygonOffsetDeepNest(simple, delta); if (shell.Count() > 0) { shells[j] = shell.First(); } else { //shells[j] = shell; } } if (offset == null) { return(polygon); } // selective reversal of offset for (i = 0; i < offset.length; i++) { var o = offset[i]; var target = getTarget(o, simple, 2 * tolerance); // reverse point offset and try to find exterior points var test = clone(offset); test.Points[i] = new SvgPoint(target.x, target.y); if (!exterior(test, polygon, inside)) { o.x = target.x; o.y = target.y; } else { // a shell is an intermediate offset between simple and offset for (j = 1; j < numshells; j++) { if (shells[j] != null) { var shell = shells[j]; var delta = j * (tolerance / numshells); target = getTarget(o, shell, 2 * delta); test = clone(offset); test.Points[i] = new SvgPoint(target.x, target.y); if (!exterior(test, polygon, inside)) { o.x = target.x; o.y = target.y; break; } } } } } // straighten long lines // a rounded rectangle would still have issues at this point, as the long sides won't line up straight var straightened = false; for (i = 0; i < offset.length; i++) { var p1 = offset[i]; var p2 = offset[i + 1 == offset.length ? 0 : i + 1]; var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); if (sqd < fixedTolerance) { continue; } for (j = 0; j < simple.length; j++) { var s1 = simple[j]; var s2 = simple[j + 1 == simple.length ? 0 : j + 1]; var sqds = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); if (sqds < fixedTolerance) { continue; } if ((GeometryUtil._almostEqual(s1.x, s2.x) || GeometryUtil._almostEqual(s1.y, s2.y)) && // we only really care about vertical and horizontal lines GeometryUtil._withinDistance(p1, s1, 2 * tolerance) && GeometryUtil._withinDistance(p2, s2, 2 * tolerance) && (!GeometryUtil._withinDistance(p1, s1, Config.curveTolerance / 1000) || !GeometryUtil._withinDistance(p2, s2, Config.curveTolerance / 1000))) { p1.x = s1.x; p1.y = s1.y; p2.x = s2.x; p2.y = s2.y; straightened = true; } } } //if(straightened){ var Ac = _Clipper.ScaleUpPaths(offset, 10000000); var Bc = _Clipper.ScaleUpPaths(polygon, 10000000); var combined = new List <List <IntPoint> >(); var clipper = new ClipperLib.Clipper(); clipper.AddPath(Ac.ToList(), ClipperLib.PolyType.ptSubject, true); clipper.AddPath(Bc.ToList(), ClipperLib.PolyType.ptSubject, true); // the line straightening may have made the offset smaller than the simplified if (clipper.Execute(ClipperLib.ClipType.ctUnion, combined, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)) { double?largestArea = null; for (i = 0; i < combined.Count; i++) { var n = Background.toNestCoordinates(combined[i].ToArray(), 10000000); var sarea = -GeometryUtil.polygonArea(n); if (largestArea == null || largestArea < sarea) { offset = n; largestArea = sarea; } } } //} cleaned = cleanPolygon2(offset); if (cleaned != null && cleaned.length > 1) { offset = cleaned; } // mark any points that are exact (for line merge detection) for (i = 0; i < offset.length; i++) { var seg = new SvgPoint[] { offset[i], offset[i + 1 == offset.length ? 0 : i + 1] }; var index1 = find(seg[0], polygon); var index2 = find(seg[1], polygon); if (index1 == null) { index1 = 0; } if (index2 == null) { index2 = 0; } if (index1 + 1 == index2 || index2 + 1 == index1 || (index1 == 0 && index2 == polygon.length - 1) || (index2 == 0 && index1 == polygon.length - 1)) { seg[0].exact = true; seg[1].exact = true; } } if (!inside && holes != null && holes.Count > 0) { offset.children = holes; } return(offset); }
//------------------------------------------------------------------------------ public static Polygons SimplifyPolygons(Polygons polys) { Polygons result = new Polygons(); Clipper c = new Clipper(); c.AddPolygons(polys, PolyType.ptSubject); c.Execute(ClipType.ctUnion, result); return result; }
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); }
private void SetTest() { if (!_testData.ContainsKey(_testNumber)) { return; } // Get test of interest. var test = _testData[_testNumber]; _testSubject = test.Subjects.FirstOrDefault(); _testClip = test.Clips.FirstOrDefault(); _testSolution = test.Solution; _newClipperSolution = new PolygonPath(); var clipper = new Clipper.Clipper(); clipper.AddPath(test.Subjects, PolygonKind.Subject); clipper.AddPath(test.Clips, PolygonKind.Clip); clipper.Execute(ClipOperation.Union, _newClipperSolution); _testBoundary = _testSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_testSolution.First(), PolygonKind.Subject) .ToList() : null; _newClipperBoundary = _newClipperSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_newClipperSolution.First(), PolygonKind.Subject) .ToList() : null; var originalClipperSolution = new List <List <IntPoint> >(); var originalClipper = new ClipperLib.Clipper(); originalClipper.AddPaths(test.Subjects.ToOriginal(), PolyType.ptSubject, true); originalClipper.AddPaths(test.Clips.ToOriginal(), PolyType.ptClip, true); originalClipper.Execute(ClipType.ctUnion, originalClipperSolution); _originalClipperSolution = originalClipperSolution.ToNew(); _originalClipperBoundary = _originalClipperSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_originalClipperSolution.First(), PolygonKind.Subject) .ToList() : null; Program.VisualizerForm.ClipperViewControl.Subjects = new[] { new PolygonViewModel { IsOpen = false, EdgeColor = Color.LawnGreen, VertexColor = Color.DarkGreen, Items = _testSubject?.ToVertices() } }; Program.VisualizerForm.ClipperViewControl.Clips = new[] { new PolygonViewModel { IsOpen = false, EdgeColor = Color.Blue, VertexColor = Color.DarkBlue, Items = _testClip?.ToVertices() } }; _solutionType = SolutionType.Test; SetSolution(); solutionComboBox.SelectedIndex = 0; }
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 PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, JoinType jointype, double MiterLimit = 2) { //precondtion: solution != pts if (delta == 0) { solution = pts; return; } this.pts = pts; this.delta = delta; if (MiterLimit <= 1) MiterLimit = 1; double RMin = 2/(MiterLimit*MiterLimit); normals = new List<DoublePoint>(); double deltaSq = delta*delta; solution.Clear(); solution.Capacity = pts.Count; for (m_i = 0; m_i < pts.Count; m_i++) { int len = pts[m_i].Count; if (len > 1 && pts[m_i][0].X == pts[m_i][len - 1].X && pts[m_i][0].Y == pts[m_i][len - 1].Y) len--; if (len == 0 || (len < 3 && delta <= 0)) continue; else if (len == 1) { Polygon arc; arc = BuildArc(pts[m_i][len - 1], 0, 2 * Math.PI, delta); solution.Add(arc); continue; } //build normals ... normals.Clear(); normals.Capacity = len; for (int j = 0; j < len -1; ++j) normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1])); normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0])); currentPoly = new Polygon(); m_k = len - 1; for (m_j = 0; m_j < len; ++m_j) { switch (jointype) { case JoinType.jtMiter: { m_R = 1 + (normals[m_j].X*normals[m_k].X + normals[m_j].Y*normals[m_k].Y); if (m_R >= RMin) DoMiter(); else DoSquare(MiterLimit); break; } case JoinType.jtRound: DoRound(); break; case JoinType.jtSquare: DoSquare(1); break; } m_k = m_j; } solution.Add(currentPoly); } //finally, clean up untidy corners ... Clipper clpr = new Clipper(); clpr.AddPolygons(solution, PolyType.ptSubject); if (delta > 0) { clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect r = clpr.GetBounds(); Polygon outer = new Polygon(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.AddPolygon(outer, PolyType.ptSubject); clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); if (solution.Count > 0) { solution.RemoveAt(0); for (int i = 0; i < solution.Count; i++) solution[i].Reverse(); } } }
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); }
//--------------------------------------------------------------------------- private void DrawBitmap(bool justClip = false) { if (!justClip) { if (rbTest2.Checked) GenerateAustPlusRandomEllipses((int)nudCount.Value); else GenerateRandomPolygon((int)nudCount.Value); } Cursor.Current = Cursors.WaitCursor; Graphics newgraphic; newgraphic = Graphics.FromImage(mybitmap); newgraphic.SmoothingMode = SmoothingMode.AntiAlias; newgraphic.Clear(Color.White); GraphicsPath path = new GraphicsPath(); if (rbNonZero.Checked) path.FillMode = FillMode.Winding; //draw subjects ... foreach (Polygon pg in subjects) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6); SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0)); newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); path.Reset(); //draw clips ... if (rbNonZero.Checked) path.FillMode = FillMode.Winding; foreach (Polygon pg in clips) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } myPen.Color = Color.FromArgb(196, 0xF9, 0xBE, 0xA6); myBrush.Color = Color.FromArgb(127, 0xFF, 0xE0, 0xE0); newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //do the clipping ... if ((clips.Count > 0 || subjects.Count > 0) && !rbNone.Checked) { Polygons solution2 = new Polygons(); Clipper c = new Clipper(); c.AddPolygons(subjects, PolyType.ptSubject); c.AddPolygons(clips, PolyType.ptClip); exSolution.Clear(); solution.Clear(); bool succeeded = c.Execute(GetClipType(), solution, GetPolyFillType(), GetPolyFillType()); if (succeeded) { myBrush.Color = Color.Black; path.Reset(); //It really shouldn't matter what FillMode is used for solution //polygons because none of the solution polygons overlap. //However, FillMode.Winding will show any orientation errors where //holes will be stroked (outlined) correctly but filled incorrectly ... path.FillMode = FillMode.Winding; //or for something fancy ... if (nudOffset.Value != 0) solution2 = Clipper.OffsetPolygons(solution, (double)nudOffset.Value * scale, JoinType.jtMiter); else solution2 = new Polygons(solution); foreach (Polygon pg in solution2) { PointF[] pts = PolygonToPointFArray(pg, scale); if (pts.Count() > 2) path.AddPolygon(pts); pts = null; } myBrush.Color = Color.FromArgb(127, 0x66, 0xEF, 0x7F); myPen.Color = Color.FromArgb(255, 0, 0x33, 0); myPen.Width = 1.0f; newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //now do some fancy testing ... Font f = new Font("Arial", 8); SolidBrush b = new SolidBrush(Color.Navy); double subj_area = 0, clip_area = 0, int_area = 0, union_area = 0; c.Clear(); c.AddPolygons(subjects, PolyType.ptSubject); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) subj_area += Clipper.Area(pg); c.Clear(); c.AddPolygons(clips, PolyType.ptClip); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) clip_area += Clipper.Area(pg); c.AddPolygons(subjects, PolyType.ptSubject); c.Execute(ClipType.ctIntersection, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) int_area += Clipper.Area(pg); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) union_area += Clipper.Area(pg); StringFormat lftStringFormat = new StringFormat(); lftStringFormat.Alignment = StringAlignment.Near; lftStringFormat.LineAlignment = StringAlignment.Near; StringFormat rtStringFormat = new StringFormat(); rtStringFormat.Alignment = StringAlignment.Far; rtStringFormat.LineAlignment = StringAlignment.Near; Rectangle rec = new Rectangle(pictureBox1.ClientSize.Width - 108, pictureBox1.ClientSize.Height - 116, 104, 106); newgraphic.FillRectangle(new SolidBrush(Color.FromArgb(196, Color.WhiteSmoke)), rec); newgraphic.DrawRectangle(myPen, rec); rec.Inflate(new Size(-2, 0)); newgraphic.DrawString("Areas", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 14)); newgraphic.DrawString("subj: ", f, b, rec, lftStringFormat); newgraphic.DrawString((subj_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("clip: ", f, b, rec, lftStringFormat); newgraphic.DrawString((clip_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("intersect: ", f, b, rec, lftStringFormat); newgraphic.DrawString((int_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("s + c - i: ", f, b, rec, lftStringFormat); newgraphic.DrawString(((subj_area + clip_area - int_area) / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("union: ", f, b, rec, lftStringFormat); newgraphic.DrawString((union_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); } //end if succeeded } //end if something to clip pictureBox1.Image = mybitmap; newgraphic.Dispose(); Cursor.Current = Cursors.Default; }
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; }
/// <summary> /// Initializes a new instance of the <see cref="Clipper"/> class. /// </summary> public Clipper() { this.innerClipper = new ClipperLib.Clipper(); }
/// <summary> /// Performs the Boolean Operations from the Clipper Library /// </summary> /// <param name="clipType"></param> /// <param name="subject"></param> /// <param name="clip"></param> /// <param name="simplifyPriorToBooleanOperation"></param> /// <param name="scale"></param> /// <param name="fillMethod"></param> /// <returns></returns> /// <exception cref="Exception"></exception> private static List <Polygon> BooleanViaClipper(PolyFillType fillMethod, ClipType clipType, IEnumerable <Polygon> subject, IEnumerable <Polygon> clip = null, bool subjectIsClosed = true, bool clipIsClosed = true) { //Remove any polygons that are only a line. //subject = subject.Where(p => p.Count > 2); //clip = clip?.Where(p => p.Count > 2); var simplifyPriorToBooleanOperation = true; if (simplifyPriorToBooleanOperation) { //subject = subject.Select(p=>SimplifyFuzzy(p)); subject = subject.Select(p => Simplify(p, 0.0000003)); } if (simplifyPriorToBooleanOperation) { //If not null //clip = clip?.Select(p => SimplifyFuzzy(p)); clip = clip?.Select(p => Simplify(p, 0.0000003)); } if (!subject.Any()) { if (clip == null || !clip.Any()) { return(new List <Polygon>()); } //Use the clip as the subject if this is a union operation and the clip is not null. if (clipType == ClipType.ctUnion) { subject = clip; clip = null; } } var subjectAll = subject.SelectMany(p => p.AllPolygons).ToList(); var clipperSolution = new List <List <IntPoint> >(); //Convert Points (TVGL) to IntPoints (Clipper) var clipperSubject = subjectAll.Select(loop => loop.Vertices.Select(point => new IntPoint(point.X * scale, point.Y * scale)).ToList()).ToList(); //Setup Clipper var clipper = new ClipperLib.Clipper() { StrictlySimple = true }; clipper.AddPaths(clipperSubject, PolyType.ptSubject, subjectIsClosed); if (clip != null) { var clipAll = clip.SelectMany(p => p.AllPolygons).ToList(); var clipperClip = clipAll.Select(loop => loop.Vertices.Select(point => new IntPoint(point.X * scale, point.Y * scale)).ToList()).ToList(); clipper.AddPaths(clipperClip, PolyType.ptClip, clipIsClosed); } //Begin an evaluation var result = clipper.Execute(clipType, clipperSolution, fillMethod, fillMethod); if (!result) { throw new Exception("Clipper Union Failed"); } //Convert back to points and return solution var solution = clipperSolution.Select(clipperPath => new Polygon(clipperPath.Select(point => new Vector2(point.X / scale, point.Y / scale)))); return(solution.CreateShallowPolygonTrees(true)); }