public void Calculate(Vector3d vStartPos, double fStartA, bool alreadyInRetract = false) { double curA = fStartA; Vector3d curPos = vStartPos; double curRate = 0; bool inRetract = alreadyInRetract; // filter paths List <IToolpath> allPaths = new List <IToolpath>(); foreach (IToolpath p in Paths) { if (p is LinearToolpath3 <T> || p is ResetExtruderPathHack) { allPaths.Add(p); } } int N = allPaths.Count; LinearToolpath3 <T> prev_path = null; for (int pi = 0; pi < N; ++pi) { if (allPaths[pi] is ResetExtruderPathHack) { curA = 0; continue; } var path = allPaths[pi] as LinearToolpath3 <T>; if (path == null) { throw new Exception("Invalid path type!"); } if (!(path.Type == ToolpathTypes.Deposition || path.Type == ToolpathTypes.PlaneChange || path.Type == ToolpathTypes.Travel)) { throw new Exception("Unknown path type!"); } // if we are travelling between two extrusion paths, and neither is support, // and the travel distance is very short,then we will skip the retract. // [TODO] should only do this on interior travels. We should determine that upstream and set a flag on travel path. bool skip_retract = false; if (path.Type == ToolpathTypes.Travel && path.Length < MinRetractTravelLength) { bool prev_is_model_deposition = (prev_path != null) && (prev_path.Type == ToolpathTypes.Deposition) && prev_path.FillType.IsPart(); var next_path = (pi < N - 1) ? (allPaths[pi + 1] as LinearToolpath3 <T>) : null; bool next_is_model_deposition = (next_path != null) && (next_path.Type == ToolpathTypes.Deposition) && next_path.FillType.IsPart(); skip_retract = prev_is_model_deposition && next_is_model_deposition; } if (EnableRetraction == false) { skip_retract = true; } // figure out volume scaling based on path type double vol_scale = 1.0; vol_scale = path.FillType.AdjustVolume(vol_scale); for (int i = 0; i < path.VertexCount; ++i) { bool last_vtx = (i == path.VertexCount - 1); Vector3d newPos = path[i].Position; double newRate = path[i].FeedRate; // default line thickness and height double path_width = Settings.Machine.NozzleDiamMM; double path_height = Settings.Part.LayerHeightMM; // override with custom dimensions if provided Vector2d vtx_dims = path[i].Dimensions; if (vtx_dims.x > 0 && vtx_dims.x < 1000.0) { path_width = vtx_dims.x; } if (vtx_dims.y > 0 && vtx_dims.y < 1000.0) { path_height = vtx_dims.y; } if (path.Type != ToolpathTypes.Deposition) { // [RMS] if we switched to a travel move we retract, unless we don't if (skip_retract == false) { if (!inRetract) { curA -= FixedRetractDistance; inRetract = true; } } curPos = newPos; curRate = newRate; } else { // for i == 0 this dist is always 0 !! double dist = (newPos - curPos).Length; if (i == 0) { Util.gDevAssert(dist < 1e-12); // next path starts at end of previous!! if (inRetract) { curA += FixedRetractDistance; inRetract = false; } } else { curPos = newPos; curRate = newRate; double segment_width = (path[i].Dimensions.x != GCodeUtil.UnspecifiedValue) ? path[i].Dimensions.x : path_width; double segment_height = (path[i].Dimensions.y != GCodeUtil.UnspecifiedValue) ? path[i].Dimensions.y : path_height; double feed = ExtrusionMath.PathLengthToFilamentLength( segment_height, segment_width, Settings.Material.FilamentDiamMM, dist, vol_scale); // Change the extrusion amount if a modifier is present. // TODO: This is a bit of a hack since the modifier acts on a Vector3d // and we're ignoring data in the second & third positions. var modifier = path[i]?.ExtendedData?.ExtrusionModifierF; if (modifier != null) { feed = modifier(new Vector3d(feed, 0, 0))[0]; } curA += feed; } } T v = path[i]; v.Extrusion = GCodeUtil.Extrude(curA); path.UpdateVertex(i, v); } prev_path = path; } NumPaths = N; ExtrusionLength = curA; } // Calculate()
/// <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); }
public void Calculate(Vector3d vStartPos, double fStartA, bool alreadyInRetract = false) { double curA = fStartA; Vector3d curPos = vStartPos; double curRate = 0; bool inRetract = alreadyInRetract; // filter paths List <IToolpath> allPaths = new List <IToolpath>(); foreach (IToolpath ipath in Paths) { ToolpathUtil.ApplyToLeafPaths(ipath, (p) => { if (p is LinearToolpath3 <PrintVertex> || p is ResetExtruderPathHack) { allPaths.Add(p); } }); } int N = allPaths.Count; LinearToolpath3 <PrintVertex> prev_path = null; for (int pi = 0; pi < N; ++pi) { if (allPaths[pi] is ResetExtruderPathHack) { curA = 0; continue; } LinearToolpath3 <PrintVertex> path = allPaths[pi] as LinearToolpath3 <PrintVertex>; if (path == null) { throw new Exception("Invalid path type!"); } if (!(path.Type == ToolpathTypes.Deposition || path.Type == ToolpathTypes.PlaneChange || path.Type == ToolpathTypes.Travel)) { throw new Exception("Unknown path type!"); } // if we are travelling between two extrusion paths, and neither is support, // and the travel distance is very short,then we will skip the retract. // [TODO] should only do this on interior travels. We should determine that upstream and set a flag on travel path. bool skip_retract = false; if (path.Type == ToolpathTypes.Travel && path.Length < MinRetractTravelLength) { bool prev_is_model_deposition = (prev_path != null) && (prev_path.Type == ToolpathTypes.Deposition) && ((prev_path.TypeModifiers & FillTypeFlags.SupportMaterial) == 0); LinearToolpath3 <PrintVertex> next_path = (pi < N - 1) ? (allPaths[pi + 1] as LinearToolpath3 <PrintVertex>) : null; bool next_is_model_deposition = (next_path != null) && (next_path.Type == ToolpathTypes.Deposition) && ((next_path.TypeModifiers & FillTypeFlags.SupportMaterial) == 0); skip_retract = prev_is_model_deposition && next_is_model_deposition; } if (EnableRetraction == false) { skip_retract = true; } // figure out volume scaling based on path type double vol_scale = 1.0; if ((path.TypeModifiers & FillTypeFlags.SupportMaterial) != 0) { vol_scale *= SupportExtrudeScale; } else if ((path.TypeModifiers & FillTypeFlags.BridgeSupport) != 0) { vol_scale *= Settings.BridgeVolumeScale; } for (int i = 0; i < path.VertexCount; ++i) { bool last_vtx = (i == path.VertexCount - 1); Vector3d newPos = path[i].Position; double newRate = path[i].FeedRate; // default line thickness and height double path_width = Settings.Machine.NozzleDiamMM; double path_height = Settings.LayerHeightMM; // override with custom dimensions if provided Vector2d vtx_dims = path[i].Dimensions; if (vtx_dims.x > 0 && vtx_dims.x < 1000.0) { path_width = vtx_dims.x; } if (vtx_dims.y > 0 && vtx_dims.y < 1000.0) { path_height = vtx_dims.y; } if (path.Type != ToolpathTypes.Deposition) { // [RMS] if we switched to a travel move we retract, unless we don't if (skip_retract == false) { if (!inRetract) { curA -= FixedRetractDistance; inRetract = true; } } curPos = newPos; curRate = newRate; } else { // for i == 0 this dist is always 0 !! double dist = (newPos - curPos).Length; if (i == 0) { Util.gDevAssert(dist == 0); // next path starts at end of previous!! if (inRetract) { curA += FixedRetractDistance; inRetract = false; } } else { curPos = newPos; curRate = newRate; double feed = ExtrusionMath.PathLengthToFilamentLength( path_height, path_width, Settings.Machine.FilamentDiamMM, dist, vol_scale); curA += feed; } } PrintVertex v = path[i]; v.Extrusion = GCodeUtil.Extrude(curA); path.UpdateVertex(i, v); } prev_path = path; } NumPaths = N; ExtrusionLength = curA; } // Calculate()