/// <summary> /// Offset this polyline by the specified amount. /// </summary> /// <param name="offset">The amount to offset.</param> /// <param name="endType">The closure type to use on the offset polygon.</param> /// <returns>A new closed Polygon offset in all directions by offset from the polyline.</returns> public virtual Polygon[] Offset(double offset, EndType endType) { var path = this.ToClipperPath(); var solution = new List <List <IntPoint> >(); var co = new ClipperOffset(); ClipperLib.EndType clEndType; switch (endType) { case EndType.Butt: clEndType = ClipperLib.EndType.etOpenButt; break; case EndType.ClosedPolygon: clEndType = ClipperLib.EndType.etClosedPolygon; break; case EndType.Square: default: clEndType = ClipperLib.EndType.etOpenSquare; break; } co.AddPath(path, JoinType.jtMiter, clEndType); co.Execute(ref solution, offset * CLIPPER_SCALE); // important, scale also used here var result = new Polygon[solution.Count]; for (var i = 0; i < result.Length; i++) { result[i] = solution[i].ToPolygon(); } return(result); }
// Rough shape only used in Inspector for quick preview. internal static List <Vector2> GetOutlinePath(Vector3[] shapePath, float offsetDistance) { const float kClipperScale = 10000.0f; List <IntPoint> path = new List <IntPoint>(); List <Vector2> output = new List <Vector2>(); for (var i = 0; i < shapePath.Length; ++i) { var newPoint = new Vector2(shapePath[i].x, shapePath[i].y) * kClipperScale; path.Add(new IntPoint((System.Int64)(newPoint.x), (System.Int64)(newPoint.y))); } List <List <IntPoint> > solution = new List <List <IntPoint> >(); ClipperOffset clipOffset = new ClipperOffset(2048.0f); clipOffset.AddPath(path, JoinType.jtRound, EndType.etClosedPolygon); clipOffset.Execute(ref solution, kClipperScale * offsetDistance, path.Count); if (solution.Count > 0) { for (int i = 0; i < solution[0].Count; ++i) { output.Add(new Vector2(solution[0][i].X / kClipperScale, solution[0][i].Y / kClipperScale)); } } return(output); }
/// <summary> /// Adds all iText /// <see cref="iText.Kernel.Geom.Subpath"/> /// s of the iText /// <see cref="List<IntPoint>"/> /// to the /// <see cref="ClipperOffset"/> /// object with one /// note: it doesn't add degenerate subpaths. /// </summary> /// <returns> /// /// <see cref="System.Collections.IList{E}"/> /// consisting of all degenerate iText /// <see cref="iText.Kernel.Geom.Subpath"/> /// s of the path. /// </returns> public static IList <Subpath> AddPath(ClipperOffset offset, Path path, JoinType joinType, EndType endType) { IList <Subpath> degenerateSubpaths = new List <Subpath>(); foreach (Subpath subpath in path.GetSubpaths()) { if (subpath.IsDegenerate()) { degenerateSubpaths.Add(subpath); continue; } if (!subpath.IsSinglePointClosed() && !subpath.IsSinglePointOpen()) { EndType et; if (subpath.IsClosed()) { // Offsetting is never used for path being filled et = EndType.CLOSED_LINE; } else { et = endType; } IList <Point> linearApproxPoints = subpath.GetPiecewiseLinearApproximation(); offset.AddPath(new List <IntPoint>(ConvertToLongPoints(linearApproxPoints)), joinType, et); } } return(degenerateSubpaths); }
public static Vector2[] Extend(this Vector2[] points, float radius) { if (Mathf.Approximately(radius, 0)) { return(points); } Path polygon = points.ToList().ConvertAll(p => new IntPoint(p.x, p.y)); Paths solution = new Paths(); ClipperOffset c = new ClipperOffset(); c.AddPath(polygon, JoinType.jtRound, EndType.etClosedPolygon); c.Execute(ref solution, radius); var r = solution.Count > 0 ? solution[0].ConvertAll(p => new Vector2(p.X, p.Y)) : new List <Vector2>(); if (r.Count > 0) { r.Add(r[0]); } return(r.ToArray()); }
/// <summary> /// Generates a solid outline of the path. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="jointStyle">The style to render the joints.</param> /// <param name="endCapStyle">The style to render the end caps of open paths (ignored on closed paths).</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle = JointStyle.Square, EndCapStyle endCapStyle = EndCapStyle.Square) { var offset = new ClipperOffset() { MiterLimit = MiterOffsetDelta }; JoinType style = Convert(jointStyle); EndType openEndCapStyle = Convert(endCapStyle); // Pattern can be applied to the path by cutting it into segments IEnumerable <ISimplePath> paths = path.Flatten(); foreach (ISimplePath p in paths) { ReadOnlySpan <Vector2> vectors = MemoryMarshal.Cast <PointF, Vector2>(p.Points.Span); var points = new List <IntPoint>(vectors.Length); foreach (Vector2 v in vectors) { points.Add(new IntPoint(v.X * ScalingFactor, v.Y * ScalingFactor)); } EndType type = p.IsClosed ? EndType.etClosedLine : openEndCapStyle; offset.AddPath(points, style, type); } return(ExecuteOutliner(width, offset)); }
private static List <Point> Unclip(List <PointF> box, float unclip_ratio) { List <IntPoint> theCliperPts = new List <IntPoint>(); foreach (PointF pt in box) { IntPoint a1 = new IntPoint((int)pt.X, (int)pt.Y); theCliperPts.Add(a1); } float area = Math.Abs(SignedPolygonArea(box.ToArray <PointF>())); double length = LengthOfPoints(box); double distance = area * unclip_ratio / length; ClipperOffset co = new ClipperOffset(); co.AddPath(theCliperPts, JoinType.jtRound, EndType.etClosedPolygon); List <List <IntPoint> > solution = new List <List <IntPoint> >(); co.Execute(ref solution, distance); if (solution.Count == 0) { return(null); } List <Point> retPts = new List <Point>(); foreach (IntPoint ip in solution[0]) { retPts.Add(new Point((int)ip.X, (int)ip.Y)); } return(retPts); }
/// <summary> /// Generates a solid outline of the path. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="jointStyle">The style to render the joints.</param> /// <param name="endCapStyle">The style to render the end caps of open paths (ignored on closed paths).</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle = JointStyle.Square, EndCapStyle endCapStyle = EndCapStyle.Butt) { var offset = new ClipperOffset() { MiterLimit = MiterOffsetDelta }; var style = Convert(jointStyle); var openEndCapStyle = Convert(endCapStyle); // Pattern can be applied to the path by cutting it into segments IEnumerable <ISimplePath> paths = path.Flatten(); foreach (ISimplePath p in paths) { IReadOnlyList <PointF> vectors = p.Points; var points = new List <IntPoint>(vectors.Count); for (int i = 0; i < vectors.Count; i++) { var v = vectors[i]; points.Add(new IntPoint(v.X * ScalingFactor, v.Y * ScalingFactor)); } EndType type = p.IsClosed ? EndType.etClosedLine : openEndCapStyle; offset.AddPath(points, style, type); } return(ExecuteOutliner(width, offset)); }
/// <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); }
} /// <summary> /// 线两侧偏移后为矩形,偏移距离须与点的放大倍数一致 /// </summary> public static Paths Offset(this Path _Path, double _offset, double magnification, EndType endType) { Paths solution = new Paths(); ClipperOffset _Co = new ClipperOffset(); _Co.MiterLimit = 3; _Co.AddPath(_Path, JoinType.jtMiter, endType); _Co.Execute(ref solution, _offset * magnification); //solution = Clipper.CleanPolygons(solution, Precision_.clipperMultiple * (0.003)); //solution = Clipper.SimplifyPolygons(solution); return(solution); }
static void AddClip(ClipperOffset c, GraphicsPath psubject) { psubject.Flatten(); List <IntPoint> subject = new List <IntPoint>(psubject.PathPoints.Count()); foreach (PointF p in psubject.PathPoints) { subject.Add(new IntPoint(p.X * resolution, p.Y * resolution)); } c.AddPath(subject, JoinType.jtRound, EndType.etClosedPolygon); }
public static List <NestPath> polygonOffset(NestPath polygon, double offset) { List <NestPath> result = new List <NestPath>(); if (offset == 0 || GeometryUtil.almostEqual(offset, 0)) { /** * return EmptyResult */ return(result); } Path p = new Path(); foreach (Segment s in polygon.getSegments()) { ClipperCoor cc = toClipperCoor(s.getX(), s.getY()); p.Add(new IntPoint(cc.getX(), cc.getY())); } int miterLimit = 2; ClipperOffset co = new ClipperOffset(miterLimit, Config.CURVE_TOLERANCE * Config.CLIIPER_SCALE); co.AddPath(p, JoinType.jtRound, EndType.etClosedPolygon); Paths newpaths = new Paths(); co.Execute(ref newpaths, offset * Config.CLIIPER_SCALE); /** * 这里的length是1的话就是我们想要的 */ for (int i = 0; i < newpaths.Count; i++) { result.Add(CommonUtil.clipperToNestPath(newpaths[i])); } if (offset > 0) { NestPath from = result[0]; if (GeometryUtil.polygonArea(from) > 0) { from.reverse(); } from.add(from.get(0)); from.getSegments().RemoveAt(0); } return(result); }
public List <GameObject> CreateStreets(List <List <Vector3> > streetsPoly) { List <GameObject> streets = new List <GameObject> (); for (int i = 0; i < streetsPoly.Count; i++) { GameObject street = new GameObject("street" + i, typeof(MeshRenderer), typeof(MeshFilter)); street.transform.parent = this.transform; street.GetComponent <MeshRenderer> ().material = (Material)Resources.Load("StreetMat"); List <List <IntPoint> > solution = new List <List <IntPoint> > (); //transform vertices of each mesh in points for clipper List <IntPoint> intPoint = FromVecToIntPoint(streetsPoly [i].ToArray()); //offset each mesh ClipperOffset co = new ClipperOffset(); co.AddPath(intPoint, JoinType.jtRound, EndType.etOpenRound); co.Execute(ref solution, 700.0); List <Vector2> vertices2D = new List <Vector2> (); for (int j = 0; j < solution.Count; j++) { vertices2D = vertices2D.Concat(FromIntPointToVec(solution [j])).ToList(); } // Use the triangulator to get indices for creating triangles Triangulator tr = new Triangulator(vertices2D.ToArray()); int[] indices = tr.Triangulate(); // Create the Vector3 vertices Vector3[] vertices = new Vector3[vertices2D.Count]; for (int k = 0; k < vertices.Length; k++) { vertices [k] = new Vector3(vertices2D [k].x, 0f, vertices2D [k].y); } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertices; msh.triangles = indices; msh.RecalculateNormals(); msh.RecalculateBounds(); // Set up game object with mesh; street.GetComponent <MeshFilter> ().mesh = msh; street.AddComponent <MeshCollider> (); street.transform.position = new Vector3(street.transform.position.x, 0.02f, street.transform.position.z); streets.Add(street); } DrawStreetLineMesh(streetsPoly, streets); return(streets); }
public static Paths offset(Paths paths, float offset) { // Set cleaning precision IntRect brect = Clipper.GetBounds(paths); int cleanPolygonPrecision = 2; if ((brect.right - brect.left) > 10000) { cleanPolygonPrecision = 30; } // Clean... AXClipperLib.JoinType jt = AXClipperLib.JoinType.jtSquare; paths = AXGeometryTools.Utilities.cleanPaths(paths, cleanPolygonPrecision); Paths resPaths = new Paths(); AXClipperLib.PolyTree resPolytree = null; // OFFSETTER ClipperOffset co = new ClipperOffset(); co.MiterLimit = 2.0f; foreach (Path path in paths) { co.Clear(); resPolytree = null; co.AddPath(path, jt, AXClipperLib.EndType.etClosedPolygon); //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); // this resPolytree has transformed curves in it resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(offset * AXGeometryTools.Utilities.IntPointPrecision)); resPaths.AddRange(Clipper.ClosedPathsFromPolyTree(resPolytree)); } return(resPaths); }
/// <summary> /// Offset this polyline by the specified amount. /// </summary> /// <param name="offset">The amount to offset.</param> /// <returns>A new polyline offset by offset.</returns> public IList <Polygon> Offset(double offset) { var path = this.ToClipperPath(); var solution = new List <List <IntPoint> >(); var co = new ClipperOffset(); co.AddPath(path, JoinType.jtMiter, EndType.etClosedPolygon); co.Execute(ref solution, offset * scale); // important, scale also used here var result = new List <Polygon>(); foreach (var loop in solution) { result.Add(loop.ToPolygon()); } return(result); }
/// <summary> /// Offset this polygon by the specified amount. /// </summary> /// <param name="offset">The amount to offset.</param> /// <returns>A new Polygon offset by offset.</returns> public Polygon[] Offset(double offset) { var path = this.ToClipperPath(); var solution = new List <List <IntPoint> >(); var co = new ClipperOffset(); co.AddPath(path, JoinType.jtMiter, EndType.etClosedPolygon); co.Execute(ref solution, offset * scale); // important, scale also used here var result = new Polygon[solution.Count]; for (var i = 0; i < result.Length; i++) { result[i] = solution[i].ToPolygon(); } return(result); }
/// <summary> /// Split a set of profiles with a collection of open polylines, with an optional gap between results. /// </summary> /// <param name="profiles">The profiles to split</param> /// <param name="splitLines">The polylines defining the splits.</param> /// <param name="gapSize">An optional gap size between split pieces. If splits are failing, it can be helpful to increase this.</param> /// <param name="tolerance">An optional tolerance.</param> public static List <Profile> Split(IEnumerable <Profile> profiles, IEnumerable <Polyline> splitLines, double gapSize = 0, double tolerance = Vector3.EPSILON) { // We're doing something a little bit questionable here — we're offsetting the split curves by a hair's width // so that clipper can handle them as a subtraction, since it doesn't have a built-in split mechanism. // We increase the tolerance so that the result is well within the specified tolerance of the expected result. // This is imperfect, but no more imperfect than all of the other clipper-based operations we currently employ. var internalTolerance = tolerance / 10; // to keep splits within tolerance, we execute clipper at a 10x smaller tolerance. Clipper clipper = new Clipper(); foreach (var profile in profiles) { var clipperPaths = profile.ToClipperPaths(internalTolerance); clipper.AddPaths(clipperPaths, PolyType.ptSubject, true); } foreach (var line in splitLines) { var unionClipper = new Clipper(); // This is basically the same as // line.Offset(offsetDist, EndType.Butt, internalTolerance), // but without the unneccessary conversion back to Elements geometry. var co = new ClipperOffset(); var offsetSolution = new List <List <IntPoint> >(); var offsetPath = line.ToClipperPath(internalTolerance); var offsetDist = (internalTolerance) + gapSize; var clipperScale = 1.0 / internalTolerance; co.AddPath(offsetPath, JoinType.jtMiter, ClipperLib.EndType.etOpenButt); co.Execute(ref offsetSolution, offsetDist * clipperScale); List <List <IntPoint> > unionSolution = new List <List <IntPoint> >(); unionClipper.AddPaths(offsetSolution, PolyType.ptSubject, true); unionClipper.Execute(ClipType.ctUnion, unionSolution, PolyFillType.pftNonZero); clipper.AddPaths(unionSolution, PolyType.ptClip, true); } PolyTree solution = new PolyTree(); clipper.Execute(ClipType.ctDifference, solution, PolyFillType.pftNonZero); var joinedProfiles = solution.ToProfiles(internalTolerance); return(joinedProfiles); }
/// <summary> /// increase of decrease polygon points offseting /// </summary> public static IEnumerable <Vector3D> Offset(this IReadOnlyList <Vector3D> pts, double tol, double offset) { var intmap = new Int64Map(tol, pts.SelectMany(x => x.Coordinates)); var clipper = new ClipperOffset(); { var path = pts.Select(p => new IntPoint(intmap.ToInt64(p.X), intmap.ToInt64(p.Y))).ToList(); // http://www.angusj.com/delphi/clipper.php clipper.AddPath(path, JoinType.jtMiter, EndType.etClosedPolygon); } var intoffset = intmap.ToInt64(intmap.Origin + offset) - intmap.ToInt64(intmap.Origin); var sol = new List <List <IntPoint> >(); clipper.Execute(ref sol, intoffset); return(sol.SelectMany(s => s.Select(si => new Vector3D(intmap.FromInt64(si.X), intmap.FromInt64(si.Y), 0)))); }
public void DrawSegment(List <Vector3> segment, List <Mesh> intersectionPolys, List <GameObject> lines) { List <List <IntPoint> > solution = new List <List <IntPoint> > (); //transform vertices of each mesh in points for clipper List <IntPoint> intPoint = FromVecToIntPoint(segment.ToArray()); //offset each mesh ClipperOffset co = new ClipperOffset(); co.AddPath(intPoint, JoinType.jtRound, EndType.etOpenRound); co.Execute(ref solution, 100.0); List <Vector2> vertices2D = new List <Vector2> (); for (int j = 0; j < solution.Count; j++) { vertices2D = vertices2D.Concat(FromIntPointToVec(solution [j])).ToList(); } // Use the triangulator to get indices for creating triangles Triangulator tr = new Triangulator(vertices2D.ToArray()); int[] indices = tr.Triangulate(); // Create the Vector3 vertices Vector3[] vertices = new Vector3[vertices2D.Count]; for (int k = 0; k < vertices.Length; k++) { vertices [k] = new Vector3(vertices2D [k].x, 0f, vertices2D [k].y); } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertices; msh.triangles = indices; msh.RecalculateNormals(); msh.RecalculateBounds(); Mesh newMesh = ExecuteMultiDifferencePoly(msh, intersectionPolys); GameObject lineSegment = new GameObject("segment", typeof(MeshFilter), typeof(MeshRenderer)); lineSegment.GetComponent <MeshFilter> ().sharedMesh = newMesh; lines.Add(lineSegment); }
public DominoPath getOffsetRectangle(int offset) { List <IntPoint> intpoints = points.Select(p => new IntPoint(p.X, p.Y)).ToList(); intpoints.Add(new IntPoint(points[0].X, points[0].Y)); List <List <IntPoint> > solution = new List <List <IntPoint> >(); ClipperOffset co = new ClipperOffset(); co.AddPath(intpoints, JoinType.jtMiter, EndType.etClosedLine); co.Execute(ref solution, offset); if (solution.Count == 0) { return(this); } return(new DominoPath() { points = solution[0].Select(p => new Point(p.X, p.Y)).ToArray() }); }
public List <Vector2> ExtendPolygon(List <Vector2> points, float radius, GMLGeometry.GeometryType type) { Path polygon = points.ConvertAll(p => new IntPoint(p.x, p.y)); Paths solution = new Paths(); ClipperOffset c = new ClipperOffset(); c.AddPath(polygon, JoinType.jtRound, type == GMLGeometry.GeometryType.Polygon ? EndType.etClosedPolygon : EndType.etOpenRound); c.Execute(ref solution, radius); var r = solution.Count > 0 ? solution[0].ConvertAll(p => new Vector2(p.X, p.Y)) : new List <Vector2>(); if (r.Count > 0) { r.Add(r[0]); } return(r); }
internal void ExtendThePolygon(ref List <IntPoint> enterPoints, ref Vector2[] extendedPoints, float extraOffset) { double distance = (AgentRadius + extraOffset) * (float)_Accuracy; _ClipperOffset.Clear(); _ExitExtendPoints.Clear(); _ClipperOffset.AddPath(enterPoints, JoinType.jtMiter, EndType.etClosedPolygon); _ClipperOffset.Execute(ref _ExitExtendPoints, distance); if (_ExitExtendPoints[0].Count != extendedPoints.Length) { extendedPoints = new Vector2[_ExitExtendPoints[0].Count]; } for (int i = 0; i < _ExitExtendPoints[0].Count; ++i) { extendedPoints[i] = ParseToVector2(_ExitExtendPoints[0][i]); } }
public static Polyline Offset(Polyline polyline, double distance) { if (polyline.Count < 2) { return(new Polyline()); } var region = polyline.ToRegion(); var offset = new ClipperOffset(); offset.AddPath(region, JoinType.jtRound, EndType.etClosedPolygon); PolyTree tree = new PolyTree(); offset.Execute(ref tree, distance / Tol); var height = polyline[0].Z; var first = tree.ToPolylines(height).MaxBy(p => p.Length).FirstOrDefault(); return(first ?? new Polyline(0)); }
public void DrawSitePicture(Site site, Graphics g, bool fillShapes, bool showOutlines, int fullWidth, int fullHeight) { double xscale = ((double)fullWidth) / ((double)_boundingBox.Width); double yscale = ((double)fullHeight) / ((double)_boundingBox.Height); g.InterpolationMode = InterpolationMode.High; g.SmoothingMode = SmoothingMode.AntiAlias; int minX = site.RegionPoints.Min(pt => pt.X); int minY = site.RegionPoints.Min(pt => pt.Y); //var offsetPoints = site.RegionPoints.Select(pt => new Point(pt.X - minX + 5, pt.Y - minY + 5)).ToArray(); var offsetPoints = site.RegionPoints .Select(pt => new Point((int)(xscale * ((double)pt.X - minX)) + 2 * ImageOffset, (int)(yscale * ((double)pt.Y - minY) + 2 * ImageOffset))).ToArray(); if (fillShapes) { g.FillPolygon(new SolidBrush(site.Color), offsetPoints); } if (showOutlines) { g.DrawPolygon(Pens.Black, offsetPoints); } if (showOutlines) { ClipperOffset offset = new ClipperOffset(); offset.AddPath(offsetPoints.Select(op => new IntPoint(op.X, op.Y)).ToList(), JoinType.jtMiter, EndType.etClosedLine); List <List <IntPoint> > solution = new List <List <IntPoint> >(); offset.Execute(ref solution, ImageOffset); if (solution.Count > 0) { var offsetPoly = solution[0]; g.DrawPolygon(Pens.Red, offsetPoly.Select(ip => new Point((int)ip.X, (int)ip.Y)).ToArray()); } } }
public static IEnumerable <IPolyLine2D> BuildOffset(IPolygon2D polygon, double delta, JoinType joinType, EndType endType, double mitterLimit, double maxDiscretizationAngle = 5) { bool containsCircArc = false; var p = CreatePolygon(polygon); if (joinType == JoinType.jtRound || endType == EndType.etOpenRound) { containsCircArc = true; } var co = new ClipperOffset(mitterLimit); co.AddPath(p, joinType, endType); var solution = new List <List <IntPoint> >(); co.Execute(ref solution, delta * ClipperScale); foreach (var poly in solution) { yield return(CreatePolyline(poly, containsCircArc, maxDiscretizationAngle + 1)); } }
public static NativeArray <RigidTransform> OffsetPath( NativeArray <RigidTransform> samples, float distance, bool looped, Allocator allocator) { var solution = new List <List <IntPoint> >(); var polygon = FromSamplesToClipper(samples); if (!Clipper.Orientation(polygon)) { distance *= -1; } var clipperOffset = new ClipperOffset(); clipperOffset.AddPath(polygon, JoinType.jtRound, EndType.etOpenButt); clipperOffset.Execute(ref solution, distance * UpScaleFactor); return(IntPointsToRigidTransforms(solution[0], looped, allocator)); }
public static IEnumerable <IPolyLine2D> BuildOffset(IPolyLine2D polyline, double delta, JoinType joinType, EndType endType, double mitterLimit, bool sort = false, double maxDiscretizationAngle = 5) { PolyLine2DDiscretizator discretizator = new PolyLine2DDiscretizator { NumberOfTiles = 1, LengthOfTile = double.MaxValue, Angle = maxDiscretizationAngle }; var p = CreatePolygon(polyline, discretizator, true); bool containsCircArc = joinType == JoinType.jtRound || endType == EndType.etOpenRound; if (!containsCircArc) { containsCircArc = polyline.Segments.FirstOrDefault(s => s is CircularArcSegment2D) != null; } var co = new ClipperOffset(mitterLimit); co.AddPath(p, joinType, endType); var solution = new List <List <IntPoint> >(); co.Execute(ref solution, delta * ClipperScale); if (sort && polyline.IsClosed && !containsCircArc && joinType == JoinType.jtMiter && (endType == EndType.etClosedLine || endType == EndType.etClosedPolygon) && solution.Count == 1) { // try to sort offset path according to source path var newPolyline = CreatePolyline(solution[0], containsCircArc, maxDiscretizationAngle + 1); TrySortSegments(polyline, newPolyline, delta); yield return(newPolyline); } else { foreach (var polygon in solution) { yield return(CreatePolyline(polygon, containsCircArc, maxDiscretizationAngle + 1)); } } }
void ShrinkPolygon(double scale, ref PolygonCollider2D poly) { List <IntPoint> subj; List <List <IntPoint> > solution = new List <List <IntPoint> >(); ClipperOffset co = new ClipperOffset(); Vector2[] path; Vector2 tempPoint; int pathNdx = 0; int pointNdx = 0; for (int ndx = 0; ndx < poly.pathCount; ndx++) { subj = new List <IntPoint>(); foreach (Vector2 point in poly.GetPath(0)) { subj.Add(new IntPoint(point.x * 1000, point.y * 1000)); } co.AddPath(subj, JoinType.jtMiter, EndType.etClosedPolygon); } co.Execute(ref solution, scale); foreach (List <IntPoint> list in solution) { path = new Vector2[solution[pathNdx].Count]; pointNdx = 0; foreach (IntPoint point in list) { tempPoint = new Vector2(); tempPoint.x = (float)point.X / 1000; tempPoint.y = (float)point.Y / 1000; path.SetValue(tempPoint, pointNdx); pointNdx++; } poly.SetPath(pathNdx, path); pathNdx++; } }
private static void AddPath(ClipperOffset offset, Path path, JoinType joinType, EndType endType) { foreach (Subpath subpath in path.Subpaths) { if (!subpath.IsSinglePointClosed() && !subpath.IsSinglePointOpen()) { EndType et; if (subpath.Closed) { // Offsetting is never used for path being filled et = EndType.etClosedLine; } else { et = endType; } IList <Point2D> linearApproxPoints = subpath.GetPiecewiseLinearApproximation(); offset.AddPath(ConvertToIntPoints(linearApproxPoints), joinType, et); } } }
/// <summary> /// Generates a solid outline of the path. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width) { var offset = new ClipperOffset(); // Pattern can be applied to the path by cutting it into segments IEnumerable <ISimplePath> paths = path.Flatten(); foreach (ISimplePath p in paths) { IReadOnlyList <PointF> vectors = p.Points; var points = new List <IntPoint>(vectors.Count); foreach (Vector2 v in vectors) { points.Add(new IntPoint(v.X * ScalingFactor, v.Y * ScalingFactor)); } EndType type = p.IsClosed ? EndType.etClosedLine : EndType.etOpenButt; offset.AddPath(points, JoinType.jtSquare, type); } return(ExecuteOutliner(width, offset)); }
public static IEnumerable <Rhino.Geometry.Curve> Offset(IEnumerable <Rhino.Geometry.Curve> curves, double distance, JoinType joinType, EndType endType, double miter, double arcTolerance, Plane?plane) { var curveList = curves as IList <Rhino.Geometry.Curve> ?? curves.ToList(); plane = GetPlane(curveList, plane); var polylines2D = curveList.Select(o => Polyline2D.FromCurve(o, plane.Value)) .Where(o => o != null) .ToList(); double unit; Point2d center; CalculateUnit(polylines2D, miter * distance, out unit, out center); var polylinesInt = polylines2D.Select(o => PolylineInt.FromPolyline2D(o, center, unit)) .ToList(); var clipper = new ClipperOffset(miter, arcTolerance / unit); foreach (var polygon in polylinesInt) { clipper.AddPath(polygon, joinType, polygon.Closed ? EndType.etClosedPolygon : endType); } var solution = new List <List <IntPoint> >(); clipper.Execute(ref solution, distance / unit); return(solution.Select(o => o.ToCurve(plane.Value, center, unit))); }
private static void AddPath(ClipperOffset offset, Path path, JoinType joinType, EndType endType) { foreach (Subpath subpath in path.Subpaths) { if (!subpath.IsSinglePointClosed() && !subpath.IsSinglePointOpen()) { EndType et; if (subpath.Closed) { // Offsetting is never used for path being filled et = EndType.etClosedLine; } else { et = endType; } IList<Point2D> linearApproxPoints = subpath.GetPiecewiseLinearApproximation(); offset.AddPath(ConvertToIntPoints(linearApproxPoints), joinType, et); } } }
/** * Adds all subpaths of the path to the {@link ClipperOffset} object with one * note: it doesn't add degenerate subpaths. * * @return {@link java.util.List} consisting of all degenerate subpaths of the path. */ private static IList<Subpath> AddPath(ClipperOffset offset, Path path, JoinType joinType, EndType endType) { IList<Subpath> degenerateSubpaths = new List<Subpath>(); foreach (Subpath subpath in path.Subpaths) { if (subpath.IsDegenerate()) { degenerateSubpaths.Add(subpath); continue; } if (!subpath.IsSinglePointClosed() && !subpath.IsSinglePointOpen()) { EndType et; if (subpath.Closed) { // Offsetting is never used for path being filled et = EndType.etClosedLine; } else { et = endType; } IList<Point2D> linearApproxPoints = subpath.GetPiecewiseLinearApproximation(); offset.AddPath(ConvertToIntPoints(linearApproxPoints), joinType, et); } } return degenerateSubpaths; }