protected FillCurveSet2d ComputeFillPaths(GeneralPolygon2d poly, SegmentSet2d polyCache) { List <List <Segment2d> > StepSpans = ComputeSegments(poly, polyCache); int N = StepSpans.Count; //double hard_max_dist = 5 * PathSpacing; // [TODO] need a pathfinder here, that can chain segments efficiently // (for now just do dumb things?) FillCurveSet2d paths = new FillCurveSet2d(); //FillPolyline2d cur = new FillPolyline2d(); foreach (var seglist in StepSpans) { foreach (Segment2d seg in seglist) { FillPolyline2d fill_seg = new FillPolyline2d() { TypeFlags = FillTypeFlags.SolidInfill }; fill_seg.AppendVertex(seg.P0); fill_seg.AppendVertex(seg.P1); paths.Append(fill_seg); } } return(paths); }
// extract set of spans from poly where clipped=false List <FillPolyline2d> find_polygon_spans(Vector2d[] poly, bool[] clipped) { // assumption: at least one vtx is clipped int iStart = 0; // handle no-wrap case if (clipped[iStart] == false && clipped[poly.Length - 1] == true) { iStart = 0; } else { while (clipped[iStart] == true) // find first non-clipped pt { iStart++; } } List <FillPolyline2d> result = new List <FillPolyline2d>(); int iCur = iStart; bool done = false; while (done == false) { FillPolyline2d cur = new FillPolyline2d(); do { cur.AppendVertex(poly[iCur]); iCur = (iCur + 1) % poly.Length; } while (clipped[iCur] == false && iCur != iStart); if (cur.VertexCount > 1) { result.Add(cur); } while (clipped[iCur] && iCur != iStart) { iCur++; } if (iCur == iStart) { done = true; } } return(result); }
// (approximately) clip insetPoly to band around clipPoly. // vertices are discarded if outside clipPoly, or within clip_dist // remaining polylines are returned // In all-pass case currently returns polyline w/ explicit first==last vertices public List <FillPolyline2d> clip_to_band(Polygon2d insetpoly, GeneralPolygon2d clipPoly, double clip_dist) { double clipSqr = clip_dist * clip_dist; int N = insetpoly.VertexCount; Vector2d[] midline = new Vector2d[N]; bool[] clipped = new bool[N]; int nClipped = 0; for (int i = 0; i < N; ++i) { Vector2d po = insetpoly[i]; if (clipPoly.Contains(po) == false) { clipped[i] = true; nClipped++; continue; } int iHole, iSeg; double segT; double distSqr = clipPoly.DistanceSquared(po, out iHole, out iSeg, out segT); if (distSqr < clipSqr) { clipped[i] = true; nClipped++; continue; } // not ideal... midline[i] = po; } if (nClipped == N) { return(new List <FillPolyline2d>()); } if (nClipped == 0) { FillPolyline2d all = new FillPolyline2d(midline); all.AppendVertex(all.Start); return(new List <FillPolyline2d>() { all }); } return(find_polygon_spans(midline, clipped)); }
static Segment2d get_case(FillPolyline2d l0, FillPolyline2d l1, int which) { if (which == 0) { return(new Segment2d(l0.Start, l1.Start)); } else if (which == 1) { return(new Segment2d(l0.Start, l1.End)); } else if (which == 2) { return(new Segment2d(l0.End, l1.Start)); } else { return(new Segment2d(l0.End, l1.End)); } }
// [TODO] would it ever make sense to break polyline to avoid huge travel?? public virtual void AppendPolyline2d(FillPolyline2d curve) { Vector3d currentPos = Builder.Position; Vector2d currentPos2 = currentPos.xy; int N = curve.VertexCount; if (N < 2) { throw new Exception("PathScheduler.AppendPolyline2d: degenerate curve!"); } int iNearest = 0; bool bReverse = false; if (curve.Start.DistanceSquared(currentPos2) > curve.End.DistanceSquared(currentPos2)) { iNearest = N - 1; bReverse = true; } Vector2d startPt = curve[iNearest]; Builder.AppendTravel(startPt, Settings.RapidTravelSpeed); List <Vector2d> loopV; List <TPVertexFlags> flags = null; if (bReverse) { loopV = new List <Vector2d>(N); for (int i = N - 1; i >= 0; --i) { loopV.Add(curve[i]); } if (curve.HasFlags) { flags = new List <TPVertexFlags>(N); for (int i = N - 1; i >= 0; --i) { flags.Add(curve.GetFlag(i)); } } } else { loopV = new List <Vector2d>(curve); if (curve.HasFlags) { flags = new List <TPVertexFlags>(curve.Flags()); } } double useSpeed = select_speed(curve); Vector2d dimensions = GCodeUtil.UnspecifiedDimensions; if (curve.CustomThickness > 0) { dimensions.x = curve.CustomThickness; } Builder.AppendExtrude(loopV, useSpeed, dimensions, curve.TypeFlags, flags); }
public void Append(FillPolyline2d curve) { Curves.Add(curve); }
// DEPRECATED - remove? // this connects up the paths with small connectors? used in DenseLinesFillPolygon public void OptimizeCurves(double max_dist, Func <Segment2d, bool> ValidateF) { int[] which = new int[4]; double[] dists = new double[4]; for (int ci = 0; ci < Curves.Count; ++ci) { FillPolyline2d l0 = Curves[ci]; // find closest viable connection int iClosest = -1; int iClosestCase = -1; for (int cj = ci + 1; cj < Curves.Count; ++cj) { FillPolyline2d l1 = Curves[cj]; dists[0] = l0.Start.Distance(l1.Start); which[0] = 0; dists[1] = l0.Start.Distance(l1.End); which[1] = 1; dists[2] = l0.End.Distance(l1.Start); which[2] = 2; dists[3] = l0.End.Distance(l1.End); which[3] = 3; Array.Sort(dists, which); for (int k = 0; k < 4 && iClosest != cj; ++k) { if (dists[k] > max_dist) { continue; } Segment2d connector = get_case(l0, l1, which[k]); if (ValidateF(connector) == false) { continue; } iClosest = cj; iClosestCase = which[k]; } } if (iClosest == -1) { continue; } // [TODO] it would be better to preserve start/direction of // longest path, if possible. Maybe make that an option? // ok we will join ci w/ iClosest. May need reverse one FillPolyline2d ljoin = Curves[iClosest]; if (iClosestCase == 0) { l0.Reverse(); } else if (iClosestCase == 1) { l0.Reverse(); ljoin.Reverse(); } else if (iClosestCase == 3) { ljoin.Reverse(); } // now we are in straight-append order l0.AppendVertices(ljoin); Curves.RemoveAt(iClosest); // force check again w/ this curve ci--; } }
/// <summary> /// fill poly w/ adjacent straight line segments, connected by connectors /// </summary> protected FillCurveSet2d ComputeFillPaths(GeneralPolygon2d poly) { FillCurveSet2d paths = new FillCurveSet2d(); // smooth the input poly a little bit, this simplifies the filling // (simplify after?) //GeneralPolygon2d smoothed = poly.Duplicate(); //CurveUtils2.LaplacianSmoothConstrained(smoothed, 0.5, 5, ToolWidth / 2, true, false); //poly = smoothed; // compute 2D non-manifold graph consisting of original polygon and // inserted line segments DGraph2 spanGraph = ComputeSpanGraph(poly); if (spanGraph == null || spanGraph.VertexCount == poly.VertexCount) { return(paths); } DGraph2 pathGraph = BuildPathGraph(spanGraph); // filter out self-overlaps from graph if (FilterSelfOverlaps) { PathOverlapRepair repair = new PathOverlapRepair(pathGraph); repair.OverlapRadius = ToolWidth * SelfOverlapToolWidthX; repair.PreserveEdgeFilterF = (eid) => { return(repair.Graph.GetEdgeGroup(eid) > 0); }; repair.Compute(); pathGraph = repair.GetResultGraph(); } HashSet <int> boundaries = new HashSet <int>(); foreach (int vid in pathGraph.VertexIndices()) { if (pathGraph.IsBoundaryVertex(vid)) { boundaries.Add(vid); } if (pathGraph.IsJunctionVertex(vid)) { throw new Exception("DenseLinesFillPolygon: PathGraph has a junction???"); } } // walk paths from boundary vertices while (boundaries.Count > 0) { int start_vid = boundaries.First(); boundaries.Remove(start_vid); int vid = start_vid; int eid = pathGraph.GetVtxEdges(vid)[0]; FillPolyline2d path = new FillPolyline2d() { TypeFlags = this.TypeFlags }; path.AppendVertex(pathGraph.GetVertex(vid)); while (true) { Index2i next = DGraph2Util.NextEdgeAndVtx(eid, vid, pathGraph); eid = next.a; vid = next.b; int gid = pathGraph.GetEdgeGroup(eid); if (gid < 0) { path.AppendVertex(pathGraph.GetVertex(vid), TPVertexFlags.IsConnector); } else { path.AppendVertex(pathGraph.GetVertex(vid)); } if (boundaries.Contains(vid)) { boundaries.Remove(vid); break; } } // discard paths that are too short if (path.ArcLength < MinPathLengthMM) { continue; } // run polyline simplification to get rid of unneccesary detail in connectors // [TODO] we could do this at graph level...) // [TODO] maybe should be checkign for collisions? we could end up creating // non-trivial overlaps here... if (SimplifyAmount != SimplificationLevel.None && path.VertexCount > 2) { PolySimplification2 simp = new PolySimplification2(path); switch (SimplifyAmount) { default: case SimplificationLevel.Minor: simp.SimplifyDeviationThreshold = ToolWidth / 4; break; case SimplificationLevel.Aggressive: simp.SimplifyDeviationThreshold = ToolWidth; break; case SimplificationLevel.Moderate: simp.SimplifyDeviationThreshold = ToolWidth / 2; break; } simp.Simplify(); path = new FillPolyline2d(simp.Result.ToArray()) { TypeFlags = this.TypeFlags }; } paths.Append(path); } // Check to make sure that we are not putting way too much material in the // available volume. Computes extrusion volume from path length and if the // ratio is too high, scales down the path thickness // TODO: do we need to compute volume? If we just divide everything by // height we get the same scaling, no? Then we don't need layer height. if (MaxOverfillRatio > 0) { throw new NotImplementedException("this is not finished yet"); #if false double LayerHeight = 0.2; // AAAHHH hardcoded nonono double len = paths.TotalLength(); double extrude_vol = ExtrusionMath.PathLengthToVolume(LayerHeight, ToolWidth, len); double polygon_vol = LayerHeight * Math.Abs(poly.Area); double ratio = extrude_vol / polygon_vol; if (ratio > MaxOverfillRatio && PathSpacing == ToolWidth) { double use_width = ExtrusionMath.WidthFromTargetVolume(LayerHeight, len, polygon_vol); //System.Console.WriteLine("Extrusion volume: {0} PolyVolume: {1} % {2} ScaledWidth: {3}", //extrude_vol, polygon_vol, extrude_vol / polygon_vol, use_width); foreach (var path in paths.Curves) { path.CustomThickness = use_width; } } #endif } return(paths); }