public static List <Polygon> OffsetLinesViaClipper(this IEnumerable <IEnumerable <Vector2> > polylines, double offset, double tolerance) { if (double.IsNaN(tolerance) || tolerance.IsNegligible()) { tolerance = Constants.BaseTolerance; } //Convert Points (TVGL) to IntPoints (Clipper) var clipperSubject = polylines.Select(line => line.Select(point => new IntPoint(point.X * scale, point.Y * scale)).ToList()).ToList(); //Setup Clipper var clip = new ClipperOffset(2, tolerance * scale); clip.AddPaths(clipperSubject, JoinType.jtSquare, EndType.etOpenSquare); //Begin an evaluation var clipperSolution = new List <List <IntPoint> >(); clip.Execute(clipperSolution, offset * scale); //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)); }
/// <summary> /// Adds all iText /// <see cref="iText.Kernel.Geom.Subpath"/> /// s of the iText /// <see cref="iText.Kernel.Geom.Path"/> /// 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); }
private IEnumerable <HPGLLine> OffsetLine(double offset, HPGLLine line) { var newlines = new List <HPGLLine> { line }; var co = new ClipperOffset(); var solution = new List <List <IntPoint> >(); var solution2 = new List <List <IntPoint> >(); solution.Add(line.Commands.Select(x => new IntPoint(_scale * x.PointFrom.X0, _scale * x.PointFrom.Y0)).ToList()); co.AddPaths(solution, JoinType.jtRound, EndType.etClosedPolygon); co.Execute(ref solution2, offset); var existingline = line; foreach (var polygon in solution2) { var newcmds = new List <HPGLCommand>(); HPGLCommand last = null; foreach (var pt in polygon) { var from = new Point3D { X = pt.X / _scale, Y = pt.Y / _scale }; var hpgl = new HPGLCommand { PointFrom = from, CommandType = HPGLCommand.HPGLCommandType.PenDown }; newcmds.Add(hpgl); if (last != null) { last.PointTo = from; } last = hpgl; } last.PointTo = newcmds.First().PointFrom; if (existingline == null) { // add new line existingline = new HPGLLine { PreCommands = new List <HPGLCommand> { new HPGLCommand { CommandType = HPGLCommand.HPGLCommandType.PenUp } }, PostCommands = new List <HPGLCommand>(), ParentLine = line.ParentLine }; newlines.Add(existingline); } existingline.Commands = newcmds; existingline.PreCommands.Last(l => l.IsPenCommand).PointTo = newcmds.First().PointFrom; existingline = null; } return(newlines); }
private static List <Polygon> OffsetViaClipper(IEnumerable <Polygon> polygons, double offset, bool notMiter, double tolerance, double deltaAngle) { var allPolygons = polygons.SelectMany(polygon => polygon.AllPolygons).ToList(); if (double.IsNaN(tolerance) || tolerance.IsNegligible()) { var totalLength = allPolygons.Sum(loop => loop.Perimeter); tolerance = totalLength * 0.001; } var joinType = notMiter ? (double.IsNaN(deltaAngle) ? JoinType.jtSquare : JoinType.jtRound) : JoinType.jtMiter; //Convert Points (TVGL) to IntPoints (Clipper) var clipperSubject = allPolygons.Select(loop => loop.Vertices.Select(point => new IntPoint(point.X * scale, point.Y * scale)).ToList()).ToList(); //Setup Clipper var clip = new ClipperOffset(2, tolerance * scale); clip.AddPaths(clipperSubject, joinType, EndType.etClosedPolygon); //Begin an evaluation var clipperSolution = new List <List <IntPoint> >(); clip.Execute(clipperSolution, offset * scale); //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)); }
private void InsetPath() { var path = this.Children.OfType <IPathObject>().FirstOrDefault(); if (path == null) { // clear our existing data VertexSource = new VertexStorage(); return; } var aPolys = path.VertexSource.CreatePolygons(); var offseter = new ClipperOffset(); offseter.AddPaths(aPolys, InflatePathObject3D.GetJoinType(OuterStyle), EndType.etClosedPolygon); var outerLoops = new List <List <IntPoint> >(); offseter.Execute(ref outerLoops, OutlineWidth * Ratio * 1000); Clipper.CleanPolygons(outerLoops); offseter.AddPaths(aPolys, InflatePathObject3D.GetJoinType(InnerStyle), EndType.etClosedPolygon); var innerLoops = new List <List <IntPoint> >(); offseter.Execute(ref innerLoops, -OutlineWidth * (1 - Ratio) * 1000); Clipper.CleanPolygons(innerLoops); var allLoops = outerLoops; allLoops.AddRange(innerLoops); VertexSource = allLoops.CreateVertexStorage(); (VertexSource as VertexStorage).Add(0, 0, ShapePath.FlagsAndCommand.Stop); }
public static IEnumerable <IPolyLine2D> BuildOffset(IEnumerable <IPolygon2D> pgons, double delta, JoinType joinType, EndType endType, double mitterLimit, double maxDiscretizationAngle = 5) { bool containsCircArc = false; var polygons = new List <List <IntPoint> >(pgons.Count()); foreach (var polygon in pgons) { var p = CreatePolygon(polygon); polygons.Add(p); } if (joinType == JoinType.jtRound || endType == EndType.etOpenRound) { containsCircArc = true; } var co = new ClipperOffset(mitterLimit); co.AddPaths(polygons, 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)); } }
/// <summary> Creates skeleton lines connected to outer walls and transit polygons. </summary> private static List <Wall> CreateConnectedAndTransit(InDoorGeneratorSettings settings, List <SkeletonEdge> walls, out List <List <IntPoint> > transitPolygons) { var offset = new ClipperOffset(); var connected = new List <Wall>(); var paths = new List <List <IntPoint> >(); var skeleton = settings.Skeleton; foreach (var wall in walls) { var start = new IntPoint(wall.Start.X * Scale, wall.Start.Y * Scale); var end = new IntPoint(wall.End.X * Scale, wall.End.Y * Scale); if (!wall.IsOuter) { paths.Add(new List <IntPoint> { start, end }); } else if (wall.IsSkeleton && (skeleton.Distances[wall.Start] < settings.HalfTransitAreaWidth || skeleton.Distances[wall.End] < settings.HalfTransitAreaWidth)) { var undesired = new Wall(wall.Start, wall.End, false); // TODO do I need this check here? //if (!connected.Contains(undesired)) connected.Add(undesired); } } transitPolygons = new List <List <IntPoint> >(1); offset.AddPaths(paths, JoinType.jtMiter, EndType.etClosedLine); offset.Execute(ref transitPolygons, settings.HalfTransitAreaWidth * Scale); return(connected); }
public List <Point> GetPath() { List <Point> result = new List <Point>(); List <IntPoint> intPoints = new List <IntPoint>(); List <List <IntPoint> > solution = new List <List <IntPoint> >(); for (int i = 0; i < linkedBars.Count; i++) { intPoints.Add(new IntPoint { X = Convert.ToInt64(linkedBars[i].X * 100), Y = Convert.ToInt64(linkedBars[i].Y * 100) }); } ClipperOffset c = new ClipperOffset(); c.AddPath(intPoints, JoinType.jtRound, EndType.etClosedPolygon); c.Execute(ref solution, (linkedBars[0].diametar + diametar) * 10.0); for (int i = 0; i < solution[0].Count; i++) { result.Add(new Point { X = solution[0][i].X / 100.0, Y = solution[0][i].Y / 100.0, }); } return(result); }
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> /// Offsets a polygon by the given distance. Counter-clockwise polygons will expand while clockwise polygons /// will contract. /// </summary> public static List <NativeArray <RigidTransform> > OffsetPolygon( NativeArray <RigidTransform> samples, float distance, Allocator allocator) { var solution = new List <List <IntPoint> >(); var polygon = FromSamplesToClipper(samples); // Clipper uses Even-Odd polygon boolean operations to calculate offsets, so offsetting a clockwise path // will result in an expanded offset path rather than a contracted path we're looking for. The offset // direction is reversed here to ensure that the orientation of the polygon determines the offset direction. var orientation = Clipper.Orientation(polygon); if (!orientation) { distance *= -1; } var clipperOffset = new ClipperOffset(); clipperOffset.AddPath(polygon, JoinType.jtRound, EndType.etClosedPolygon); clipperOffset.Execute(ref solution, distance * UpScaleFactor); if (!orientation) { for (var i = 0; i < solution.Count; i++) { solution[i].Reverse(); } } return(ConvertToSamples(solution, allocator)); }
private 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> /// 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> /// <param name="tolerance">An optional tolerance.</param> /// <returns>A new closed Polygon offset in all directions by offset from the polyline.</returns> public virtual Polygon[] Offset(double offset, EndType endType, double tolerance = Vector3.EPSILON) { var clipperScale = 1.0 / tolerance; var path = this.ToClipperPath(tolerance); 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 * clipperScale); // important, scale also used here var result = new Polygon[solution.Count]; for (var i = 0; i < result.Length; i++) { result[i] = solution[i].ToPolygon(tolerance); } return(result); }
public Nav2D(Vector2 leftBottomMapCorner, Vector2 rightUpperMapCorner, float agentRadius, Accuracy accuracy) { if (new Quad(leftBottomMapCorner, rightUpperMapCorner).Area() < _MinWorldAreaSize) { throw new Exception("World is too small! The world minimum area is 1 m^2"); } _LeftBottomMapCorner = leftBottomMapCorner; _RightUpperMapCorner = rightUpperMapCorner; AgentRadius = agentRadius; _Obstacles = new List <NavElement>(); _Surfaces = new List <NavElement>(); _ClipperOffset = new ClipperOffset(); _ExitExtendPoints = new List <List <IntPoint> >(); elementsGroups = new List <ElementsGroup>(); _Connections = new Dictionary <Tuple <NavPoint, NavPoint>, ConnectionData>(); _NavPoints = new List <NavPoint>(); _TmpGroupedElements = new HashSet <NavElement>(); _TmpLocalGroupedElements = new List <NavElement>(); _TmpCollidedElements = new Queue <NavElement>(); _TmpSearchList = new List <NavElement>(); _TmpElementsGroups = new Dictionary <uint, ElementsGroup>(); elementsGroupPull = new Stack <ElementsGroup>(); clipper = new Clipper(); polyTree = new PolyTree(); QuadTree = new QuadTree <NavElement>(10, 6, GetQuadTreeBounds(_LeftBottomMapCorner, _RightUpperMapCorner)); _Accuracy = accuracy; _NextElementsGroupId = 1; }
private static IPolygon Clopening(IPolygon p, Box2 boundingBox, double offset) { if (p.IsEmpty) { return(Polygon.Empty); } double scale = Math.Max(boundingBox.Width, boundingBox.Height); var fixedPointRange = new Box2(boundingBox.MinCorner, new Vector2(scale, scale)); var fixedP = ConvertToFixedPoint(p, fixedPointRange); var fixedScaleOffset = offset * _fixedPointRange / scale; try { var offsetter = new ClipperOffset(); offsetter.AddPaths(fixedP, JoinType.jtMiter, EndType.etClosedPolygon); var fixedIntermediate = new ClipperPolygon(); offsetter.Execute(ref fixedIntermediate, fixedScaleOffset); offsetter.Clear(); offsetter.AddPaths(fixedIntermediate, JoinType.jtMiter, EndType.etClosedPolygon); var fixedAnswer = new ClipperPolygon(); offsetter.Execute(ref fixedAnswer, -fixedScaleOffset); return(ConvertToFloatingPoint(fixedAnswer, fixedPointRange)); } catch (Exception e) { Console.WriteLine("EXCEPTION: {0}", e); return(p); } }
public static IPolygon Offset(this IPolygon p, double offset) { if (p.IsEmpty) { return(Polygon.Empty); } var boundingBox = p.BoundingBox().Offset(Math.Max(offset, 0)); double scale = Math.Max(boundingBox.Width, boundingBox.Height); var fixedPointRange = new Box2(boundingBox.MinCorner, new Vector2(scale, scale)); var fixedP = ConvertToFixedPoint(p, fixedPointRange); try { var offsetter = new ClipperOffset(); offsetter.AddPaths(fixedP, JoinType.jtMiter, EndType.etClosedPolygon); var fixedAnswer = new ClipperPolygon(); offsetter.Execute(ref fixedAnswer, offset * _fixedPointRange / scale); return(ConvertToFloatingPoint(fixedAnswer, fixedPointRange)); } catch (Exception e) { Console.WriteLine("EXCEPTION: {0}", e); return(p); } }
private static List <List <Curve> > BuildInsetFilling(List <List <Curve> > plist, LaserGRBL.GrblFile.L2LConf cnf) { double spacing = cnf.res / cnf.fres; List <List <Curve> > flist = new List <List <Curve> >(); ClipperOffset c = new ClipperOffset(); AddOffsetSubject(plist, c); for (int i = 1; ; i++) { List <List <IntPoint> > solution = new List <List <IntPoint> >(); c.Execute(ref solution, -spacing * i * resolution); if (solution.Count > 0) { flist.AddRange(ToPotraceList(solution, true)); } else { break; } } return(flist); }
public static IEnumerable <IPolyLine2D> BuildOffset(IEnumerable <IPolyLine2D> polylines, 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 }; bool containsCircArc = joinType == JoinType.jtRound || endType == EndType.etOpenRound; var polygons = new List <List <IntPoint> >(polylines.Count()); foreach (var polyline in polylines) { if (!containsCircArc) { containsCircArc |= polyline.Segments.FirstOrDefault(s => s is CircularArcSegment2D) != null; } var polygon = CreatePolygon(polyline, discretizator, true); polygons.Add(polygon); } var co = new ClipperOffset(mitterLimit); co.AddPaths(polygons, joinType, endType); var solution = new List <List <IntPoint> >(); co.Execute(ref solution, delta * ClipperScale); foreach (var polygon in solution) { yield return(CreatePolyline(polygon, containsCircArc, maxDiscretizationAngle + 1)); } }
protected internal virtual Path FilterStrokePath(Path path, Matrix ctm, float lineWidth, int lineCapStyle, int lineJoinStyle, float miterLimit, LineDashPattern lineDashPattern) { JoinType joinType = GetJoinType(lineJoinStyle); EndType endType = GetEndType(lineCapStyle); if (lineDashPattern != null) { if (IsZeroDash(lineDashPattern)) { return(new Path()); } if (!IsSolid(lineDashPattern)) { path = ApplyDashPattern(path, lineDashPattern); } } ClipperOffset offset = new ClipperOffset(miterLimit, PdfCleanUpProcessor.ArcTolerance * PdfCleanUpProcessor.FloatMultiplier); AddPath(offset, path, joinType, endType); PolyTree resultTree = new PolyTree(); offset.Execute(ref resultTree, lineWidth * PdfCleanUpProcessor.FloatMultiplier / 2); return(FilterFillPath(ConvertToPath(resultTree), ctm, PathPaintingRenderInfo.NONZERO_WINDING_RULE)); }
/// <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) { 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 : openEndCapStyle; offset.AddPath(points, style, type); } return(ExecuteOutliner(width, offset)); }
// 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); }
public static bool addSteinerPointsAtOffset(ref Polygon _polygon, ref ClipperOffset co, float offset, int seglenBigInt) { PolyTree resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(-offset * AXGeometryTools.Utilities.IntPointPrecision)); Paths paths = Clipper.PolyTreeToPaths(resPolytree); if (paths != null && paths.Count > 0 && paths[0] != null && paths[0].Count > 0) { foreach (Path path in paths) { if (path != null && path.Count > 0) { Path ppp = Pather.segmentPath(path, seglenBigInt); if (ppp != null && ppp.Count > 0) { foreach (IntPoint ip in ppp) { _polygon.AddSteinerPoint(new TriangulationPoint((double)ip.X / (double)AXGeometryTools.Utilities.IntPointPrecision, (double)ip.Y / (double)AXGeometryTools.Utilities.IntPointPrecision)); } } } } return(true); } return(false); }
public static List <GeneralPolygon2d> ComputeOffsetPolygon(GeneralPolygon2d poly, double fOffset, bool bMiter = false) { double nIntScale = GetIntScale(poly); CPolygonList clipper_polys = new CPolygonList(); clipper_polys.Add(ClipperUtil.ConvertToClipper(poly.Outer, nIntScale)); foreach (Polygon2d hole in poly.Holes) { clipper_polys.Add(ClipperUtil.ConvertToClipper(hole, nIntScale)); } CPolygonList dilate_solution = new CPolygonList(); try { ClipperOffset co = new ClipperOffset(); if (bMiter) { co.AddPaths(clipper_polys, JoinType.jtMiter, EndType.etClosedPolygon); } else { co.AddPaths(clipper_polys, JoinType.jtRound, EndType.etClosedPolygon); } co.Execute(ref dilate_solution, fOffset * nIntScale); } catch /*( Exception e )*/ { //System.Diagnostics.Debug.WriteLine("ClipperUtil.ComputeOffsetPolygon: Clipper threw exception: " + e.Message); return(new List <GeneralPolygon2d>()); } List <GeneralPolygon2d> polys = ClipperUtil.ConvertFromClipper(dilate_solution, nIntScale); return(polys); }
/// <summary> /// Creates a set of polygonal regions offset from a given set of Clipper polygons /// </summary> /// <param name="paths">Hierarchy of Clipper polygons to be offset</param> /// <param name="innerOffset">Padding distance from the edge of the given paths region</param> /// <param name="outerOffset">The outer boundary distance of the offset region</param> /// <returns></returns> public static List <List <IntPoint> > GenerateOffsetRegionFromRoadPaths( List <List <IntPoint> > paths, float innerOffset, float outerOffset) { var solution = new List <List <IntPoint> >(); if (outerOffset < 0f || outerOffset < innerOffset) { return(solution); } var clipperOffset = new ClipperOffset(); clipperOffset.AddPaths(paths, JoinType.jtMiter, EndType.etClosedPolygon); var innerOffsetRegions = new List <List <IntPoint> >(); clipperOffset.Execute(ref innerOffsetRegions, innerOffset * UpScaleFactor); var outerOffsetRegions = new List <List <IntPoint> >(); clipperOffset.Execute(ref outerOffsetRegions, outerOffset * UpScaleFactor); var clipper = new Clipper(); clipper.AddPaths(outerOffsetRegions, PolyType.ptSubject, true); clipper.AddPaths(innerOffsetRegions, PolyType.ptClip, true); clipper.Execute(ClipType.ctXor, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); return(solution); }
public void GenerateCollision() { //List<List<Vector2>> polygons = new List<List<Vector2>>(); List <List <Vector2> > unitedPolygons = new List <List <Vector2> >(); Clipper clipper = new Clipper(); int scalingFactor = 10000; for (int x = 0; x < layer.chunkSize_x; x++) { for (int y = 0; y < layer.chunkSize_y; y++) { if (grid[x, y] != null && grid[x, y].colider) { Path newPoligons = new Path(4); newPoligons.Add(new IntPoint(Mathf.Floor((0 + x) * scalingFactor), Mathf.Floor((0 + y) * scalingFactor))); newPoligons.Add(new IntPoint(Mathf.Floor((0 + x) * scalingFactor), Mathf.Floor((1 + y) * scalingFactor))); newPoligons.Add(new IntPoint(Mathf.Floor((1 + x) * scalingFactor), Mathf.Floor((1 + y) * scalingFactor))); newPoligons.Add(new IntPoint(Mathf.Floor((1 + x) * scalingFactor), Mathf.Floor((0 + y) * scalingFactor))); clipper.AddPath(newPoligons, PolyType.ptSubject, true); } } } Paths solution = new Paths(); clipper.Execute(ClipType.ctUnion, solution); ClipperOffset offset = new ClipperOffset(); offset.AddPaths(solution, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref solution, 5f); foreach (Path path in solution) { List <Vector2> unitedPolygon = new List <Vector2>(); foreach (IntPoint point in path) { unitedPolygon.Add(new Vector2(point.X / (float)scalingFactor, point.Y / (float)scalingFactor)); } unitedPolygons.Add(unitedPolygon); } unitedPolygons = TileMap.RemoveClosePointsInPolygons(unitedPolygons); PolygonCollider2D collider = layer.map.gameObject.GetComponent <PolygonCollider2D>(); collider.pathCount = unitedPolygons.Count; for (int i = 0; i < unitedPolygons.Count; i++) { Vector2[] points = unitedPolygons[i].ToArray(); collider.SetPath(i, points); } }
//this function takes a list of polygons as a parameter, this list of polygons represent all the polygons that constitute collision in your level. public List <List <Vector2> > UniteCollisionPolygons(List <List <Vector2> > polygons) { //this is going to be the result of the method List <List <Vector2> > unitedPolygons = new List <List <Vector2> >(); Clipper clipper = new Clipper(); //clipper only works with ints, so if we're working with floats, we need to multiply all our floats by //a scaling factor, and when we're done, divide by the same scaling factor again int scalingFactor = 10000; //this loop will convert our List<List<Vector2>> to what Clipper works with, which is "Path" and "IntPoint" //and then add all the Paths to the clipper object so we can process them for (int i = 0; i < polygons.Count; i++) { Path allPolygonsPath = new Path(polygons[i].Count); for (int j = 0; j < polygons[i].Count; j++) { allPolygonsPath.Add(new IntPoint(Mathf.Floor(polygons[i][j].x * scalingFactor), Mathf.Floor(polygons[i][j].y * scalingFactor))); //print(new IntPoint(Mathf.Floor(polygons[i][j].x * scalingFactor), Mathf.Floor(polygons[i][j].y * scalingFactor)).X + ", " + new IntPoint(Mathf.Floor(polygons[i][j].x * scalingFactor), Mathf.Floor(polygons[i][j].y * scalingFactor)).Y); } clipper.AddPath(allPolygonsPath, PolyType.ptSubject, true); } //this will be the result Paths solution = new Paths(); //having added all the Paths added to the clipper object, we tell clipper to execute an union clipper.Execute(ClipType.ctUnion, solution); //the union may not end perfectly, so we're gonna do an offset in our polygons, that is, expand them outside a little bit ClipperOffset offset = new ClipperOffset(); offset.AddPaths(solution, JoinType.jtMiter, EndType.etClosedPolygon); //5 is the amount of offset offset.Execute(ref solution, 0.1f); //now we just need to convert it into a List<List<Vector2>> while removing the scaling foreach (Path path in solution) { List <Vector2> unitedPolygon = new List <Vector2>(); foreach (IntPoint point in path) { unitedPolygon.Add(new Vector2(point.X / (float)scalingFactor, point.Y / (float)scalingFactor)); } unitedPolygons.Add(unitedPolygon); } //this removes some redundant vertices in the polygons when they are too close from each other //may be useful to clean things up a little if your initial collisions don't match perfectly from tile to tile unitedPolygons = RemoveClosePointsInPolygons(unitedPolygons); //everything done return(unitedPolygons); }
public static void generateOutlineSegments() { Logger.logProgress("Generating outline segments"); //Check if there should be at least one shell if (Global.Values.shellThickness < 1) { return; } //We need to got through every layercomponent //for (ushort i = 0; i < Global.Values.layerCount; i++) foreach (LayerComponent layer in Global.Values.layerComponentList) { Logger.logProgress("Outline: " + layer.layerNumber); //And every island inside the list foreach (Island island in layer.islandList) { if (island.outlinePolygons.Count < 1) { continue; } //The first outline will be one that is half an extrusion thinner than the sliced outline, ths is sothat the dimensions //do not change once extruded Polygons outline = new Polygons(); ClipperOffset offset = new ClipperOffset(); offset.AddPaths(island.outlinePolygons, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref outline, -Global.Values.nozzleWidth / 2); for (ushort j = 0; j < Global.Values.shellThickness; j++) { //Place the newly created outline in its own segment LayerSegment outlineSegment = new LayerSegment(SegmentType.OutlineSegment); outlineSegment.segmentType = SegmentType.OutlineSegment; outlineSegment.segmentSpeed = layer.layerSpeed; outlineSegment.outlinePolygons = new Polygons(outline); island.segmentList.Add(outlineSegment); var distance = (-Global.Values.nozzleWidth / 2) - Global.Values.nozzleWidth * (j + 1); //We now shrink the outline with one extrusion width for the next shell if any offset = new ClipperOffset(); offset.AddPaths(island.outlinePolygons, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref outline, distance); } //We now need to store the smallest outline as the new layer outline for infill trimming purposes //the current outline though is just half an extrusion width to small offset = new ClipperOffset(); offset.AddPaths(outline, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref island.outlinePolygons, Global.Values.nozzleWidth); } } }
/// <summary> /// Gets a parallel <see cref="Loxy"/> with offset /// </summary> /// <param name="JT">Jointype</param> /// <param name="ET">Endtype</param> /// <param name="Offset">Offset</param> /// <returns></returns> public Loxy GetOffset(JoinType JT, EndType ET, double Offset) { ClipperOffset co = new ClipperOffset(); List <List <IntPoint> > Solution = new List <List <IntPoint> >(); co.AddPaths(ToClipperPoly(this), (ClipperLib.JoinType)JT, (ClipperLib.EndType)ET); co.Execute(ref Solution, (long)(ToInt * Offset)); return(FromClipperLoxy(Solution)); }
public static Polygons Offset(this Polygons polygons, int distance) { ClipperOffset offseter = new ClipperOffset(); offseter.AddPaths(polygons, JoinType.jtMiter, EndType.etClosedPolygon); Paths solution = new Polygons(); offseter.Execute(ref solution, distance); return(solution); }
static public ClipperPolygons Buffer(this ClipperPolygons polygons, double distance) { ClipperPolygons result = new ClipperPolygons(); ClipperOffset co = new ClipperOffset(); co.AddPaths(polygons, JoinType.jtRound, EndType.etClosedPolygon); co.Execute(ref result, distance * Acc); return result; }
public static void calculateSupportSegments() { Logger.logProgress("Calculating support segments"); if (!Global.Values.shouldSupportMaterial || Global.Values.supportMaterialDesnity <= 0) { return; } //To calculate the support segments we will keep a union of the above layers and perform a difference with them and the current layer Polygons topUnion = new Polygons(); for (int i = Global.Values.layerCount - 1; i > -1; i--) { Polygons layerPolygons = new Polygons(); foreach (Island island in Global.Values.layerComponentList[i].islandList) { Polygons offsetResult = new Polygons(); ClipperOffset offset = new ClipperOffset(); offset.AddPaths(island.outlinePolygons, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref offsetResult, Global.Values.shellThickness * Global.Values.nozzleWidth); Clipper clipper = new Clipper(); clipper.AddPaths(offsetResult, PolyType.ptClip, true); clipper.AddPaths(layerPolygons, PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, layerPolygons); } Polygons supportOutline = new Polygons(); Clipper supportClipper = new Clipper(); supportClipper.AddPaths(topUnion, PolyType.ptSubject, true); supportClipper.AddPaths(layerPolygons, PolyType.ptClip, true); supportClipper.Execute(ClipType.ctDifference, supportOutline); //We should just offset the support slightly so that it does not touch the rest of the model ClipperOffset clipperOffset = new ClipperOffset(); clipperOffset.AddPaths(supportOutline, JoinType.jtMiter, EndType.etClosedPolygon); clipperOffset.Execute(ref supportOutline, -Global.Values.nozzleWidth); Island supportIsland = new Island(); LayerSegment segment = new LayerSegment(SegmentType.SupportSegment); segment.segmentSpeed = Global.Values.layerComponentList[i].supportSpeed; segment.outlinePolygons = supportOutline; supportIsland.outlinePolygons = supportOutline; supportIsland.segmentList.Add(segment); Global.Values.layerComponentList[i].islandList.Add(supportIsland); Clipper unionClipper = new Clipper(); unionClipper.AddPaths(topUnion, PolyType.ptClip, true); unionClipper.AddPaths(layerPolygons, PolyType.ptSubject, true); unionClipper.Execute(ClipType.ctUnion, topUnion); } }
protected internal virtual Path FilterStrokePath(Path path, Matrix ctm, float lineWidth, int lineCapStyle, int lineJoinStyle, float miterLimit, LineDashPattern lineDashPattern) { JoinType joinType = GetJoinType(lineJoinStyle); EndType endType = GetEndType(lineCapStyle); if (lineDashPattern != null) { if (IsZeroDash(lineDashPattern)) { return new Path(); } if (!IsSolid(lineDashPattern)) { path = ApplyDashPattern(path, lineDashPattern); } } ClipperOffset offset = new ClipperOffset(miterLimit, PdfCleanUpProcessor.ArcTolerance * PdfCleanUpProcessor.FloatMultiplier); AddPath(offset, path, joinType, endType); PolyTree resultTree = new PolyTree(); offset.Execute(ref resultTree, lineWidth * PdfCleanUpProcessor.FloatMultiplier / 2); return FilterFillPath(ConvertToPath(resultTree), ctm, PathPaintingRenderInfo.NONZERO_WINDING_RULE); }
public static Polygons Offset(this Polygons polygons, int distance) { ClipperOffset offseter = new ClipperOffset(); offseter.AddPaths(polygons, JoinType.jtMiter, EndType.etClosedPolygon); Paths solution = new Polygons(); offseter.Execute(ref solution, distance); return solution; }
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); } } }
protected internal Path FilterStrokePath(Path sourcePath, Matrix ctm, float lineWidth, int lineCapStyle, int lineJoinStyle, float miterLimit, LineDashPattern lineDashPattern) { Path path = sourcePath; JoinType joinType = GetJoinType(lineJoinStyle); EndType endType = GetEndType(lineCapStyle); if (lineDashPattern != null && !lineDashPattern.IsSolid()) { path = ApplyDashPattern(path, lineDashPattern); } ClipperOffset offset = new ClipperOffset(miterLimit, PdfCleanUpProcessor.ArcTolerance * PdfCleanUpProcessor.FloatMultiplier); IList<Subpath> degenerateSubpaths = AddPath(offset, path, joinType, endType); PolyTree resultTree = new PolyTree(); offset.Execute(ref resultTree, lineWidth * PdfCleanUpProcessor.FloatMultiplier / 2); Path offsetedPath = ConvertToPath(resultTree); if (degenerateSubpaths.Count > 0) { if (endType == EndType.etOpenRound) { IList<Subpath> circles = ConvertToCircles(degenerateSubpaths, lineWidth / 2); offsetedPath.AddSubpaths(circles); } else if (endType == EndType.etOpenSquare && lineDashPattern != null) { IList<Subpath> squares = ConvertToSquares(degenerateSubpaths, lineWidth, sourcePath); offsetedPath.AddSubpaths(squares); } } return FilterFillPath(offsetedPath, ctm, PathPaintingRenderInfo.NONZERO_WINDING_RULE); }
/** * 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; }