public void QueuePolygons(Polygons polygons, GCodePathConfig config) { foreach (var polygon in polygons) { QueuePolygon(polygon, 0, config); } }
public GCodePath QueueFanCommand(int fanSpeedPercent, GCodePathConfig config) { var path = GetNewPath(config); path.FanPercent = fanSpeedPercent; return(path); }
public void QueueExtrusionMove(IntPoint destination, GCodePathConfig config) { GetLatestPathWithConfig(config).Polygon.Add(new IntPoint(destination, CurrentZ)); LastPosition = destination; //ValidatePaths(); }
public GCodePath(GCodePath copyPath) { this.config = copyPath.config; this.Done = copyPath.Done; this.ExtruderIndex = copyPath.ExtruderIndex; this.Retract = copyPath.Retract; this.Polygon = new Polygon(copyPath.Polygon); }
public void QueueFanCommand(int fanSpeedPercent, GCodePathConfig config) { var path = GetNewPath(config); path.FanPercent = fanSpeedPercent; queuedFanSpeeds.Add(path); }
public GCodePath(GCodePath copyPath) { this.config = copyPath.config; this.done = copyPath.done; this.extruderIndex = copyPath.extruderIndex; this.Retract = copyPath.Retract; this.points = new Polygon(copyPath.points); }
private GCodePath GetNewPath(GCodePathConfig config) { GCodePath path = new GCodePath(); paths.Add(path); path.Retract = RetractType.None; path.ExtruderIndex = currentExtruderIndex; path.Done = false; path.Config = config; return(path); }
private GCodePath GetLatestPathWithConfig(GCodePathConfig config) { if (paths.Count > 0 && paths[paths.Count - 1].config == config && !paths[paths.Count - 1].Done) { return(paths[paths.Count - 1]); } var path = GetNewPath(config); return(path); }
private GCodePath GetLatestPathWithConfig(GCodePathConfig config, bool forceUniquePath = false) { if (!forceUniquePath && paths.Count > 0 && paths[paths.Count - 1].Config == config && !paths[paths.Count - 1].Done) { return(paths[paths.Count - 1]); } var path = GetNewPath(config); return(path); }
public GCodePlanner(GCodeExport gcode, int travelSpeed, int retractionMinimumDistance_um, double perimeterStartEndOverlap = 0) { this.gcodeExport = gcode; travelConfig = new GCodePathConfig("travelConfig"); travelConfig.SetData(travelSpeed, 0, "travel"); LastPosition = gcode.GetPositionXY(); totalPrintTime = 0.0; forceRetraction = false; currentExtruderIndex = gcode.GetExtruderIndex(); this.retractionMinimumDistance_um = retractionMinimumDistance_um; this.perimeterStartEndOverlapRatio = Math.Max(0, Math.Min(1, perimeterStartEndOverlap)); }
public void writePolygonsByOptimizer(Polygons polygons, GCodePathConfig config) { PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(lastPosition); orderOptimizer.AddPolygons(polygons); orderOptimizer.Optimize(config); for (int i = 0; i < orderOptimizer.bestPolygonOrderIndex.Count; i++) { int polygonIndex = orderOptimizer.bestPolygonOrderIndex[i]; writePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config); } }
public LayerGCodePlanner(ConfigSettings config, GCodeExport gcode, int travelSpeed, long retractionMinimumDistance_um, double perimeterStartEndOverlap = 0) { this.config = config; this.gcodeExport = gcode; travelConfig = new GCodePathConfig("travelConfig", "travel"); travelConfig.SetData(travelSpeed, 0); LastPosition = gcode.GetPositionXY(); forceRetraction = false; currentExtruderIndex = gcode.GetExtruderIndex(); this.retractionMinimumDistance_um = retractionMinimumDistance_um; this.perimeterStartEndOverlapRatio = Math.Max(0, Math.Min(1, perimeterStartEndOverlap)); }
public void QueuePolygon(Polygon polygon, int startIndex, GCodePathConfig config) { IntPoint currentPosition = polygon[startIndex]; if (!config.spiralize && (LastPosition.X != currentPosition.X || LastPosition.Y != currentPosition.Y)) { QueueTravel(currentPosition); } if (config.closedLoop) { for (int positionIndex = 1; positionIndex < polygon.Count; positionIndex++) { IntPoint destination = polygon[(startIndex + positionIndex) % polygon.Count]; QueueExtrusionMove(destination, config); currentPosition = destination; } // We need to actually close the polygon so go back to the first point if (polygon.Count > 2) { QueueExtrusionMove(polygon[startIndex], config); } } else // we are not closed { if (startIndex == 0) { for (int positionIndex = 1; positionIndex < polygon.Count; positionIndex++) { IntPoint destination = polygon[positionIndex]; QueueExtrusionMove(destination, config); currentPosition = destination; } } else { for (int positionIndex = polygon.Count - 1; positionIndex >= 1; positionIndex--) { IntPoint destination = polygon[(startIndex + positionIndex) % polygon.Count]; QueueExtrusionMove(destination, config); currentPosition = destination; } } } }
public bool QueueInterfaceSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig) { var foundSupport = false; // interface Polygons interfaceOutlines = InterfaceLayers[layerIndex]; if (interfaceOutlines.Count > 0) { List <Polygons> interfaceIslands = interfaceOutlines.ProcessIntoSeparateIslands(); foreach (Polygons interfaceIsland in interfaceIslands) { var allSupport = new Polygons(); // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } var infillOffset = -config.ExtrusionWidth_um + config.InfillExtendIntoPerimeter_um; // make a border if layer 0 // make a border if layer 0 if (config.GenerateSupportPerimeter || layerIndex == 0) { allSupport.AddRange(interfaceIsland.Offset(config.ExtrusionWidth_um / 2)); } var supportLines = new Polygons(); Infill.GenerateLineInfill(config, interfaceIsland.Offset(infillOffset), supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um); allSupport.AddRange(supportLines); var pathFinder = new PathFinder(interfaceIsland, config.ExtrusionWidth_um * 3 / 2); if (gcodeLayer.QueuePolygonsByOptimizer(allSupport, pathFinder, supportInterfaceConfig, 0)) { foundSupport = true; } } } return(foundSupport); }
public GCodePlanner(GCodeExport gcode, int travelSpeed, int retractionMinimumDistance) { this.gcode = gcode; travelConfig = new GCodePathConfig(travelSpeed, 0, "travel"); lastPosition = gcode.getPositionXY(); outerPerimetersToAvoidCrossing = null; extrudeSpeedFactor = 100; travelSpeedFactor = 100; extraTime = 0.0; totalPrintTime = 0.0; forceRetraction = false; alwaysRetract = false; currentExtruderIndex = gcode.getExtruderIndex(); this.retractionMinimumDistance = retractionMinimumDistance; }
public GCodePlanner(GCodeExport gcode, int travelSpeed, int retractionMinimumDistance_um) { this.gcode = gcode; travelConfig = new GCodePathConfig(travelSpeed, 0, "travel"); lastPosition = gcode.GetPositionXY(); outerPerimetersToAvoidCrossing = null; extrudeSpeedFactor = 100; travelSpeedFactor = 100; extraTime = 0.0; totalPrintTime = 0.0; forceRetraction = false; alwaysRetract = false; currentExtruderIndex = gcode.GetExtruderIndex(); this.retractionMinimumDistance_um = retractionMinimumDistance_um; }
public bool QueuePolygonByOptimizer(Polygon polygon, PathFinder pathFinder, GCodePathConfig config, int layerIndex) { PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition); orderOptimizer.AddPolygon(polygon); orderOptimizer.Optimize(pathFinder, layerIndex, config); for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++) { int polygonIndex = orderOptimizer.bestIslandOrderIndex[i]; QueuePolygon(polygon, orderOptimizer.startIndexInPolygon[polygonIndex], config); } return(true); }
public void writePolygon(Polygon polygon, int startIndex, GCodePathConfig config) { IntPoint currentPosition = polygon[startIndex]; writeTravel(currentPosition); for (int i = 1; i < polygon.Count; i++) { IntPoint destination = polygon[(startIndex + i) % polygon.Count]; writeExtrusionMove(destination, config); currentPosition = destination; } if (polygon.Count > 2) { writeExtrusionMove(polygon[startIndex], config); } }
public void writePolygonsByOptimizer(Polygons polygons, GCodePathConfig config) { PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(lastPosition); for (int i = 0; i < polygons.Count; i++) { orderOptimizer.addPolygon(polygons[i]); } orderOptimizer.optimize(); for (int i = 0; i < orderOptimizer.polyOrder.Count; i++) { int polygonIndex = orderOptimizer.polyOrder[i]; writePolygon(polygons[polygonIndex], orderOptimizer.polyStart[polygonIndex], config); } }
GCodePath getLatestPathWithConfig(GCodePathConfig config) { if (paths.Count > 0 && paths[paths.Count - 1].config == config && !paths[paths.Count - 1].done) { return(paths[paths.Count - 1]); } paths.Add(new GCodePath()); GCodePath ret = paths[paths.Count - 1]; ret.Retract = false; ret.config = config; ret.extruderIndex = currentExtruderIndex; ret.done = false; return(ret); }
public void QueuePolygonsByOptimizer(Polygons polygons, GCodePathConfig config) { if (polygons.Count == 0) { return; } PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition); orderOptimizer.AddPolygons(polygons); orderOptimizer.Optimize(config); for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++) { int polygonIndex = orderOptimizer.bestIslandOrderIndex[i]; QueuePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config); } }
public GCodePlanner(GCodeExport gcode, int travelSpeed, int retractionMinimumDistance_um, double perimeterStartEndOverlap = 0) { this.gcodeExport = gcode; travelConfig = new GCodePathConfig("travelConfig"); travelConfig.SetData(travelSpeed, 0, "travel"); LastPosition = gcode.GetPositionXY(); outerPerimetersToAvoidCrossing = null; extrudeSpeedFactor = 100; travelSpeedFactor = 100; extraTime = 0.0; totalPrintTime = 0.0; forceRetraction = false; alwaysRetract = false; currentExtruderIndex = gcode.GetExtruderIndex(); this.retractionMinimumDistance_um = retractionMinimumDistance_um; this.perimeterStartEndOverlapRatio = Math.Max(0, Math.Min(1, perimeterStartEndOverlap)); }
public GCodePlanner(GCodeExport gcode, int travelSpeed, int retractionMinimumDistance_um, double perimeterStartEndOverlap = 0, bool mergeOverlappingLines = false) { this.mergeOverlappingLines = mergeOverlappingLines; this.gcodeExport = gcode; travelConfig = new GCodePathConfig("travelConfig"); travelConfig.SetData(travelSpeed, 0, "travel"); LastPosition = gcode.GetPositionXY(); outerPerimetersToAvoidCrossing = null; extrudeSpeedFactor = 100; travelSpeedFactor = 100; extraTime = 0.0; totalPrintTime = 0.0; forceRetraction = false; alwaysRetract = false; currentExtruderIndex = gcode.GetExtruderIndex(); this.retractionMinimumDistance_um = retractionMinimumDistance_um; this.perimeterStartEndOverlapRatio = Math.Max(0, Math.Min(1, perimeterStartEndOverlap)); }
public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config) { if (layerIndex >= LastLayerWithChange(config) || extrudersThatHaveBeenPrimed == null) { return; } // print all of the extruder loops that have not already been printed for (int extruderIndex = 0; extruderIndex < config.MaxExtruderCount(); extruderIndex++) { if (!extrudersThatHaveBeenPrimed[extruderIndex]) { // write the loops for this extruder, but don't change to it. We are just filling the prime tower. PrimeOnWipeTower(extruderIndex, 0, gcodeLayer, fillConfig, config); } // clear the history of printer extruders for the next layer extrudersThatHaveBeenPrimed[extruderIndex] = false; } }
public void WriteQueuedGCode(int layerThickness, int fanSpeedPercent = -1, int bridgeFanSpeedPercent = -1) { GCodePathConfig lastConfig = null; int extruderIndex = gcodeExport.GetExtruderIndex(); for (int pathIndex = 0; pathIndex < paths.Count; pathIndex++) { GCodePath path = paths[pathIndex]; if (extruderIndex != path.extruderIndex) { extruderIndex = path.extruderIndex; gcodeExport.SwitchExtruder(extruderIndex); } else if (path.Retract) { gcodeExport.WriteRetraction(); } if (path.config != travelConfig && lastConfig != path.config) { if (path.config.gcodeComment == "BRIDGE" && bridgeFanSpeedPercent != -1) { gcodeExport.WriteFanCommand(bridgeFanSpeedPercent); } else if (lastConfig?.gcodeComment == "BRIDGE" && bridgeFanSpeedPercent != -1) { gcodeExport.WriteFanCommand(fanSpeedPercent); } gcodeExport.WriteComment("TYPE:{0}".FormatWith(path.config.gcodeComment)); lastConfig = path.config; } double speed = path.config.speed; if (path.config.lineWidth_um != 0) { // Prevent cooling overrides from affecting bridge moves if (path.config.gcodeComment != "BRIDGE") { speed = speed * extrudeSpeedFactor / 100; } } else { speed = speed * travelSpeedFactor / 100; } if (path.points.Count == 1 && path.config != travelConfig && (gcodeExport.GetPositionXY() - path.points[0].XYPoint).ShorterThen(path.config.lineWidth_um * 2)) { //Check for lots of small moves and combine them into one large line Point3 nextPosition = path.points[0]; int i = pathIndex + 1; while (i < paths.Count && paths[i].points.Count == 1 && (nextPosition - paths[i].points[0]).ShorterThen(path.config.lineWidth_um * 2)) { nextPosition = paths[i].points[0]; i++; } if (paths[i - 1].config == travelConfig) { i--; } if (i > pathIndex + 2) { nextPosition = gcodeExport.GetPosition(); for (int x = pathIndex; x < i - 1; x += 2) { long oldLen = (nextPosition - paths[x].points[0]).Length(); Point3 newPoint = (paths[x].points[0] + paths[x + 1].points[0]) / 2; long newLen = (gcodeExport.GetPosition() - newPoint).Length(); if (newLen > 0) { gcodeExport.WriteMove(newPoint, speed, (int)(path.config.lineWidth_um * oldLen / newLen)); } nextPosition = paths[x + 1].points[0]; } gcodeExport.WriteMove(paths[i - 1].points[0], speed, path.config.lineWidth_um); pathIndex = i - 1; continue; } } bool spiralize = path.config.spiralize; if (spiralize) { //Check if we are the last spiralize path in the list, if not, do not spiralize. for (int m = pathIndex + 1; m < paths.Count; m++) { if (paths[m].config.spiralize) { spiralize = false; } } } if (spiralize) // if we are still in spiralize mode { //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. double totalLength = 0; long z = gcodeExport.GetPositionZ(); IntPoint currentPosition = gcodeExport.GetPositionXY(); for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { IntPoint nextPosition = path.points[pointIndex].XYPoint; totalLength += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; } double length = 0.0; currentPosition = gcodeExport.GetPositionXY(); for (int i = 0; i < path.points.Count; i++) { IntPoint nextPosition = path.points[i].XYPoint; length += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; Point3 nextExtrusion = path.points[i]; nextExtrusion.z = (int)(z + layerThickness * length / totalLength + .5); gcodeExport.WriteMove(nextExtrusion, speed, path.config.lineWidth_um); } } else { bool pathIsClosed = true; if (perimeterStartEndOverlapRatio < 1) { pathIsClosed = !TrimPerimeterIfNeeded(path, perimeterStartEndOverlapRatio); } // This is test code to remove double drawn small perimeter lines. List <PathAndWidth> pathsWithOverlapsRemoved; if (RemovePerimetersThatOverlap(path, speed, out pathsWithOverlapsRemoved, pathIsClosed)) { for (int polygonIndex = 0; polygonIndex < pathsWithOverlapsRemoved.Count; polygonIndex++) { PathAndWidth polygon = pathsWithOverlapsRemoved[polygonIndex]; if (polygon.Path.Count == 2) { // make sure the path is ordered with the first point the closest to where we are now Point3 currentPosition = gcodeExport.GetPosition(); // if the second point is closer swap them if ((polygon.Path[1] - currentPosition).LengthSquared() < (polygon.Path[0] - currentPosition).LengthSquared()) { // swap them Point3 temp = polygon.Path[0]; polygon.Path[0] = polygon.Path[1]; polygon.Path[1] = temp; } } // move to the start of this polygon gcodeExport.WriteMove(polygon.Path[0], travelConfig.speed, 0); // write all the data for the polygon for (int pointIndex = 1; pointIndex < polygon.Path.Count; pointIndex++) { gcodeExport.WriteMove(polygon.Path[pointIndex], speed, polygon.ExtrusionWidthUm); } } } else { for (int i = 0; i < path.points.Count; i++) { gcodeExport.WriteMove(path.points[i], speed, path.config.lineWidth_um); } } } } gcodeExport.UpdateTotalPrintTime(); }
public void WriteRaftGCodeIfRequired(ConfigSettings config, GCodeExport gcode) { LayerDataStorage storage = this; if (config.ShouldGenerateRaft()) { GCodePathConfig raftBaseConfig = new GCodePathConfig(config.firstLayerSpeed, config.raftBaseExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftMiddleConfig = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceExtrusionWidth_um, "SUPPORT"); // create the raft base { gcode.WriteComment("LAYER:-3"); gcode.WriteComment("RAFT BASE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um); if (config.raftExtruder >= 0) { // if we have a specified raft extruder use it gcodeLayer.SetExtruder(config.raftExtruder); } else if (config.supportExtruder >= 0) { // else preserve the old behavior of using the support extruder if set. gcodeLayer.SetExtruder(config.supportExtruder); } gcode.setZ(config.raftBaseThickness_um); gcode.SetExtrusion(config.raftBaseThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseLineSpacing_um, config.infillExtendIntoPerimeter_um, 0); // write the skirt around the raft gcodeLayer.QueuePolygonsByOptimizer(storage.skirt, raftBaseConfig); // write the outline of the raft gcodeLayer.QueuePolygonsByOptimizer(storage.raftOutline, raftBaseConfig); // write the inside of the raft base gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftBaseConfig); gcodeLayer.WriteQueuedGCode(config.raftBaseThickness_um); } if (config.raftFanSpeedPercent > 0) { gcode.WriteFanCommand(config.raftFanSpeedPercent); } // raft middle layers { gcode.WriteComment("LAYER:-2"); gcode.WriteComment("RAFT MIDDLE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um); gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um); gcode.SetExtrusion(config.raftInterfaceThicknes_um, config.filamentDiameter_um, config.extrusionMultiplier); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftInterfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 45); gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftMiddleConfig); gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um); } for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.raftSurfaceLayers; raftSurfaceIndex++) { gcode.WriteComment("LAYER:-1"); gcode.WriteComment("RAFT SURFACE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um); gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceThickness_um * raftSurfaceIndex); gcode.SetExtrusion(config.raftSurfaceThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); Polygons raftLines = new Polygons(); if (raftSurfaceIndex == config.raftSurfaceLayers) { // make sure the top layer of the raft is 90 degrees offset to the first layer of the part so that it has minimum contact points. Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90); } else { Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 90 * raftSurfaceIndex); } gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftSurfaceConfig); gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um); } } }
public bool QueueInterfaceSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig) { // interface bool outputPaths = false; Polygons currentInterfaceOutlines2 = InterfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2); if (currentInterfaceOutlines2.Count > 0) { List <Polygons> interfaceIslands = currentInterfaceOutlines2.ProcessIntoSeparateIslands(); foreach (Polygons interfaceOutline in interfaceIslands) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } // make a border if layer 0 if (layerIndex == 0) { Polygons infillOutline = interfaceOutline.Offset(-supportInterfaceConfig.lineWidth_um / 2); Polygons outlines = Clipper.CleanPolygons(infillOutline, config.ExtrusionWidth_um / 4); if (gcodeLayer.QueuePolygonsByOptimizer(outlines, null, supportInterfaceConfig, 0)) { outputPaths = true; } } Polygons supportLines = new Polygons(); Infill.GenerateLineInfill(config, interfaceOutline, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um); if (gcodeLayer.QueuePolygonsByOptimizer(supportLines, null, supportInterfaceConfig, 0)) { outputPaths = true; } } } return(outputPaths); }
public void QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig) { // interface Polygons currentInterfaceOutlines = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2); if (currentInterfaceOutlines.Count > 0) { Polygons supportLines = new Polygons(); Infill.GenerateLineInfill(config, currentInterfaceOutlines, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um); gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig); } }
public void writeGCode(bool liftHeadIfNeeded, int layerThickness) { GCodePathConfig lastConfig = null; int extruderIndex = gcode.getExtruderIndex(); for (int pathIndex = 0; pathIndex < paths.Count; pathIndex++) { GCodePath path = paths[pathIndex]; if (extruderIndex != path.extruderIndex) { extruderIndex = path.extruderIndex; gcode.switchExtruder(extruderIndex); } else if (path.Retract) { gcode.writeRetraction(); } if (path.config != travelConfig && lastConfig != path.config) { gcode.writeComment("TYPE:{0}".FormatWith(path.config.name)); lastConfig = path.config; } int speed = path.config.speed; if (path.config.lineWidth != 0) { // Only apply the extrudeSpeedFactor to extrusion moves speed = speed * extrudeSpeedFactor / 100; } else { speed = speed * travelSpeedFactor / 100; } if (path.points.Count == 1 && path.config != travelConfig && (gcode.getPositionXY() - path.points[0]).ShorterThen(path.config.lineWidth * 2)) { //Check for lots of small moves and combine them into one large line IntPoint nextPosition = path.points[0]; int i = pathIndex + 1; while (i < paths.Count && paths[i].points.Count == 1 && (nextPosition - paths[i].points[0]).ShorterThen(path.config.lineWidth * 2)) { nextPosition = paths[i].points[0]; i++; } if (paths[i - 1].config == travelConfig) { i--; } if (i > pathIndex + 2) { nextPosition = gcode.getPositionXY(); for (int x = pathIndex; x < i - 1; x += 2) { long oldLen = (nextPosition - paths[x].points[0]).vSize(); IntPoint newPoint = (paths[x].points[0] + paths[x + 1].points[0]) / 2; long newLen = (gcode.getPositionXY() - newPoint).vSize(); if (newLen > 0) { gcode.writeMove(newPoint, speed, (int)(path.config.lineWidth * oldLen / newLen)); } nextPosition = paths[x + 1].points[0]; } gcode.writeMove(paths[i - 1].points[0], speed, path.config.lineWidth); pathIndex = i - 1; continue; } } bool spiralize = path.config.spiralize; if (spiralize) { //Check if we are the last spiralize path in the list, if not, do not spiralize. for (int m = pathIndex + 1; m < paths.Count; m++) { if (paths[m].config.spiralize) { spiralize = false; } } } if (spiralize) { //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. double totalLength = 0; int z = gcode.getPositionZ(); IntPoint currentPosition = gcode.getPositionXY(); for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { IntPoint nextPosition = path.points[pointIndex]; totalLength += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; } double length = 0.0; currentPosition = gcode.getPositionXY(); for (int i = 0; i < path.points.Count; i++) { IntPoint nextPosition = path.points[i]; length += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; gcode.setZ((int)(z + layerThickness * length / totalLength + .5)); gcode.writeMove(path.points[i], speed, path.config.lineWidth); } } else { for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { gcode.writeMove(path.points[pointIndex], speed, path.config.lineWidth); } } } gcode.updateTotalPrintTime(); if (liftHeadIfNeeded && extraTime > 0.0) { gcode.writeComment("Small layer, adding delay of {0}".FormatWith(extraTime)); gcode.writeRetraction(); gcode.setZ(gcode.getPositionZ() + 3000); gcode.writeMove(gcode.getPositionXY(), travelConfig.speed, 0); gcode.writeMove(gcode.getPositionXY() - new IntPoint(-20000, 0), travelConfig.speed, 0); gcode.writeDelay(extraTime); } }
public void writeExtrusionMove(IntPoint destination, GCodePathConfig config) { getLatestPathWithConfig(config).points.Add(destination); lastPosition = destination; }
public void QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig, GCodePathConfig supportInterfaceConfig) { // normal support Polygons currentSupportOutlines = supportOutlines[layerIndex]; currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2); List<Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands(); foreach (Polygons islandOutline in supportIslands) { Polygons islandInfillLines = new Polygons(); // render a grid of support if (config.generateSupportPerimeter) { gcodeLayer.QueuePolygonsByOptimizer(islandOutline, supportNormalConfig); } Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2); switch (config.supportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; } gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig); } // interface Polygons currentInterfaceOutlines = interfaceLayers[layerIndex].Offset(-config.extrusionWidth_um / 2); if (currentInterfaceOutlines.Count > 0) { Polygons supportLines = new Polygons(); Infill.GenerateLineInfill(config, currentInterfaceOutlines, ref supportLines, config.infillStartingAngle + 90, config.extrusionWidth_um); gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig); } }
public void QueueAirGappedBottomLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentAirGappedBottoms = airGappedBottomOutlines[layerIndex]; currentAirGappedBottoms = currentAirGappedBottoms.Offset(-config.extrusionWidth_um / 2); List<Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparatIslands(); foreach (Polygons islandOutline in supportIslands) { Polygons islandInfillLines = new Polygons(); // render a grid of support gcodeLayer.QueuePolygonsByOptimizer(islandOutline, supportNormalConfig); Polygons infillOutline = islandOutline.Offset(-config.extrusionWidth_um / 2); switch (config.supportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; } gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig); } }
public void QueueExtrusionMove(IntPoint destination, GCodePathConfig config) { GetLatestPathWithConfig(config).points.Add(new Point3(destination, CurrentZ)); LastPosition = destination; }
private void QueuePolygonsConsideringSupport(int layerIndex, GCodePlanner gcodeLayer, Polygons polygonsToWrite, GCodePathConfig fillConfig, SupportWriteType supportWriteType) { if (config.generateSupport && layerIndex > 0 && !config.continuousSpiralOuterPerimeter) { Polygons supportOutlines = slicingData.support.GetRequiredSupportAreas(layerIndex); if (supportWriteType == SupportWriteType.UnsupportedAreas) { if (supportOutlines.Count > 0) { Polygons polygonsNotOnSupport; // don't write the bottoms that are sitting on supported areas (they will be written at air gap distance later). polygonsToWrite = PolygonsHelper.ConvertToLines(polygonsToWrite); polygonsNotOnSupport = polygonsToWrite.CreateLineDifference(supportOutlines); gcodeLayer.QueuePolygonsByOptimizer(polygonsNotOnSupport, fillConfig); } else { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } } else { if (supportOutlines.Count > 0) { if (supportOutlines.Count > 0) { // write the bottoms that are sitting on supported areas. Polygons polygonsOnSupport; polygonsToWrite = PolygonsHelper.ConvertToLines(polygonsToWrite); polygonsOnSupport = supportOutlines.CreateLineIntersections(polygonsToWrite); // ensure that all the segments have only 2 points foreach(Polygon poly in polygonsOnSupport) { while(poly.Count > 2) { // This is an error and I'm not sure why it happened. It needs to be investigated. // LBB 2016 01 12 poly.RemoveAt(poly.Count - 1); } } gcodeLayer.QueuePolygonsByOptimizer(polygonsOnSupport, fillConfig); } else { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } } } } else if (supportWriteType == SupportWriteType.UnsupportedAreas) { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } }
public void QueuePolygons(Polygons polygons, GCodePathConfig config) { foreach(var polygon in polygons) { QueuePolygon(polygon, 0, config); } }
private bool QueueClosetsInset(Polygons insetsConsider, bool limitDistance, GCodePathConfig pathConfig, int layerIndex, GCodePlanner gcodeLayer) { long maxDist_um = long.MaxValue; if (limitDistance) { maxDist_um = config.ExtrusionWidth_um * 4; } int polygonPrintedIndex = -1; for (int polygonIndex = 0; polygonIndex < insetsConsider.Count; polygonIndex++) { Polygon currentPolygon = insetsConsider[polygonIndex]; int bestPoint = PathOrderOptimizer.GetClosestIndex(currentPolygon, gcodeLayer.LastPosition); if (bestPoint > -1) { long distance = (currentPolygon[bestPoint] - gcodeLayer.LastPosition).Length(); if (distance < maxDist_um) { maxDist_um = distance; polygonPrintedIndex = polygonIndex; } } } if (polygonPrintedIndex > -1) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, new Polygons() { insetsConsider[polygonPrintedIndex] }, pathConfig, SupportWriteType.UnsupportedAreas); insetsConsider.RemoveAt(polygonPrintedIndex); return true; } // Return the original limitDistance value if we didn't match a polygon return limitDistance; }
public void WriteExtrusionMove(IntPoint destination, GCodePathConfig config) { GetLatestPathWithConfig(config).points.Add(destination); lastPosition = destination; }
public void Optimize(GCodePathConfig config = null) { bool canTravelForwardOrBackward = config != null && !config.closedLoop; // Find the point that is closest to our current position (start position) bool[] polygonHasBeenAdded = new bool[polygons.Count]; for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++) { Polygon currentPolygon = polygons[polygonIndex]; if (canTravelForwardOrBackward || currentPolygon.Count < 3) { startIndexInPolygon.Add(0); } else // This is a closed loop. { //int bestPointIndex = GetBestEdgeIndex(currentPolygon); int bestPointIndex = GetClosestIndex(currentPolygon, startPosition); startIndexInPolygon.Add(bestPointIndex); } } IntPoint currentPosition = startPosition; // We loop over the polygon list twice, at each inner loop we only pick one polygon. for (int polygonIndexOuterLoop = 0; polygonIndexOuterLoop < polygons.Count; polygonIndexOuterLoop++) { int bestPolygonIndex = -1; double bestDist = double.MaxValue; for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++) { if (polygonHasBeenAdded[polygonIndex] || polygons[polygonIndex].Count < 1) { continue; } // If there are only 2 points (a single line) or the path is marked as travel both ways, we are willing to start from the start or the end. if (polygons[polygonIndex].Count == 2 || canTravelForwardOrBackward) { double distToSart = (polygons[polygonIndex][0] - currentPosition).LengthSquared(); if (distToSart < bestDist) { bestPolygonIndex = polygonIndex; bestDist = distToSart; startIndexInPolygon[polygonIndex] = 0; } double distToEnd = (polygons[polygonIndex][polygons[polygonIndex].Count - 1] - currentPosition).LengthSquared(); if (distToEnd < bestDist) { bestPolygonIndex = polygonIndex; bestDist = distToEnd; startIndexInPolygon[polygonIndex] = 1; } } else { double dist = (polygons[polygonIndex][startIndexInPolygon[polygonIndex]] - currentPosition).LengthSquared(); if (dist < bestDist) { bestPolygonIndex = polygonIndex; bestDist = dist; } } } if (bestPolygonIndex > -1) { if (polygons[bestPolygonIndex].Count == 2 || canTravelForwardOrBackward) { // get the point that is opposite from the one we started on int startIndex = startIndexInPolygon[bestPolygonIndex]; if (startIndex == 0) { currentPosition = polygons[bestPolygonIndex][polygons[bestPolygonIndex].Count - 1]; } else { currentPosition = polygons[bestPolygonIndex][0]; } } else { currentPosition = polygons[bestPolygonIndex][startIndexInPolygon[bestPolygonIndex]]; } polygonHasBeenAdded[bestPolygonIndex] = true; bestPolygonOrderIndex.Add(bestPolygonIndex); } } currentPosition = startPosition; foreach (int bestPolygonIndex in bestPolygonOrderIndex) { int bestStartPoint = -1; double bestDist = double.MaxValue; if (canTravelForwardOrBackward) { bestDist = (polygons[bestPolygonIndex][0] - currentPosition).LengthSquared(); bestStartPoint = 0; // check if the end is better int endIndex = polygons[bestPolygonIndex].Count - 1; double dist = (polygons[bestPolygonIndex][endIndex] - currentPosition).LengthSquared(); if (dist < bestDist) { bestStartPoint = endIndex; bestDist = dist; } } else { for (int pointIndex = 0; pointIndex < polygons[bestPolygonIndex].Count; pointIndex++) { double dist = (polygons[bestPolygonIndex][pointIndex] - currentPosition).LengthSquared(); if (dist < bestDist) { bestStartPoint = pointIndex; bestDist = dist; } } } startIndexInPolygon[bestPolygonIndex] = bestStartPoint; if (polygons[bestPolygonIndex].Count == 2 || canTravelForwardOrBackward) { if (bestStartPoint == 0) { currentPosition = polygons[bestPolygonIndex][polygons[bestPolygonIndex].Count - 1]; } else { currentPosition = polygons[bestPolygonIndex][0]; } } else { currentPosition = polygons[bestPolygonIndex][bestStartPoint]; } } }
public void QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentSupportOutlines = supportOutlines[layerIndex]; currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2); List<Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands(); foreach (Polygons islandOutline in supportIslands) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } Polygons islandInfillLines = new Polygons(); // render a grid of support if (config.GenerateSupportPerimeter) { Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4); gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig); } Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2); if (layerIndex == 0) { // on the first layer print this as solid Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um); } else { switch (config.SupportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } } gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig); } }
private void QueuePolygonsConsideringSupport(int layerIndex, GCodePlanner gcodeLayer, Polygons polygonsToWrite, GCodePathConfig fillConfig, SupportWriteType supportWriteType) { bool oldLoopValue = fillConfig.closedLoop; if (config.GenerateSupport && layerIndex > 0 && !config.ContinuousSpiralOuterPerimeter) { Polygons supportOutlines = slicingData.support.GetRequiredSupportAreas(layerIndex).Offset(fillConfig.lineWidth_um/2); if (supportWriteType == SupportWriteType.UnsupportedAreas) { if (supportOutlines.Count > 0) { // don't write the bottoms that are sitting on supported areas (they will be written at air gap distance later). Polygons polygonsToWriteAsLines = PolygonsHelper.ConvertToLines(polygonsToWrite); Polygons polygonsNotOnSupport = polygonsToWriteAsLines.CreateLineDifference(supportOutlines); fillConfig.closedLoop = false; gcodeLayer.QueuePolygonsByOptimizer(polygonsNotOnSupport, fillConfig); } else { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } } else { if (supportOutlines.Count > 0) { if (supportOutlines.Count > 0) { // write the bottoms that are sitting on supported areas. Polygons polygonsToWriteAsLines = PolygonsHelper.ConvertToLines(polygonsToWrite); Polygons polygonsOnSupport = supportOutlines.CreateLineIntersections(polygonsToWriteAsLines); fillConfig.closedLoop = false; gcodeLayer.QueuePolygonsByOptimizer(polygonsOnSupport, fillConfig); } else { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } } } } else if (supportWriteType == SupportWriteType.UnsupportedAreas) { gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig); } fillConfig.closedLoop = oldLoopValue; }
public void QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig) { // interface Polygons currentInterfaceOutlines2 = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2); if (currentInterfaceOutlines2.Count > 0) { List<Polygons> interfaceIslands = currentInterfaceOutlines2.ProcessIntoSeparatIslands(); foreach (Polygons interfaceOutline in interfaceIslands) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } Polygons supportLines = new Polygons(); Infill.GenerateLineInfill(config, interfaceOutline, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um); gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig); } } }
public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config) { if(layerIndex >= LastLayerWithChange(config) || extrudersThatHaveBeenPrimed == null) { return; } // print all of the extruder loops that have not already been printed for (int extruderIndex = 0; extruderIndex < config.MaxExtruderCount(); extruderIndex++) { if (!extrudersThatHaveBeenPrimed[extruderIndex]) { // write the loops for this extruder, but don't change to it. We are just filling the prime tower. PrimeOnWipeTower(extruderIndex, 0, gcodeLayer, fillConfig, config); } // clear the history of printer extruders for the next layer extrudersThatHaveBeenPrimed[extruderIndex] = false; } }
public void QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentSupportOutlines = supportOutlines[layerIndex]; currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2); List <Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands(); foreach (Polygons islandOutline in supportIslands) { Polygons islandInfillLines = new Polygons(); // render a grid of support if (config.GenerateSupportPerimeter) { Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4); gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig); } Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2); if (layerIndex == 0) { // on the first layer print this as solid Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um); } else { switch (config.SupportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } } gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig); } }
public void PrimeOnWipeTower(int extruderIndex, int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config) { if (config.WipeTowerSize_um < 1 || extrudersThatHaveBeenPrimed == null || layerIndex > LastLayerWithChange(config) + 1) { return; } //If we changed extruder, print the wipe/prime tower for this nozzle; Polygons fillPolygons = new Polygons(); GenerateWipeTowerInfill(extruderIndex, this.wipeTower, fillPolygons, fillConfig.lineWidth_um, config); gcodeLayer.QueuePolygons(fillPolygons, fillConfig); extrudersThatHaveBeenPrimed[extruderIndex] = true; }
public void QueueAirGappedBottomLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentAirGappedBottoms = AirGappedBottomOutlines[layerIndex]; currentAirGappedBottoms = currentAirGappedBottoms.Offset(-config.ExtrusionWidth_um / 2); List <Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparateIslands(); foreach (Polygons islandOutline in supportIslands) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } Polygons islandInfillLines = new Polygons(); // render a grid of support if (config.GenerateSupportPerimeter) { Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4); gcodeLayer.QueuePolygonsByOptimizer(outlines, null, supportNormalConfig, 0); } Polygons infillOutline = islandOutline.Offset(-config.ExtrusionWidth_um / 2); switch (config.SupportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, null, supportNormalConfig, 0); } }
public void QueuePolygonsByOptimizer(Polygons polygons, GCodePathConfig config) { PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition); orderOptimizer.AddPolygons(polygons); orderOptimizer.Optimize(config); for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++) { int polygonIndex = orderOptimizer.bestIslandOrderIndex[i]; QueuePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config); } }
public bool QueueNormalSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentSupportOutlines = SparseSupportOutlines[layerIndex]; currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2); List <Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparateIslands(); bool outputPaths = false; foreach (Polygons islandOutline in supportIslands) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } Polygons islandInfillLines = new Polygons(); // render a grid of support if (config.GenerateSupportPerimeter || layerIndex == 0) { Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4); if (gcodeLayer.QueuePolygonsByOptimizer(outlines, null, supportNormalConfig, 0)) { outputPaths = true; } } Polygons infillOutline = islandOutline.Offset(-(int)supportNormalConfig.lineWidth_um); if (layerIndex == 0) { // on the first layer print this as solid Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um); } else { switch (config.SupportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } } PathFinder pathFinder = null; if (config.AvoidCrossingPerimeters) { pathFinder = new PathFinder(infillOutline, -config.ExtrusionWidth_um / 2, useInsideCache: config.AvoidCrossingPerimeters); } var oldPathFinder = gcodeLayer.PathFinder; gcodeLayer.PathFinder = pathFinder; if (gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, null, supportNormalConfig, 0)) { outputPaths |= true; } gcodeLayer.PathFinder = oldPathFinder; } return(outputPaths); }
private GCodePath GetLatestPathWithConfig(GCodePathConfig config) { if (paths.Count > 0 && paths[paths.Count - 1].config == config && !paths[paths.Count - 1].done) { return paths[paths.Count - 1]; } paths.Add(new GCodePath()); GCodePath ret = paths[paths.Count - 1]; ret.Retract = false; ret.config = config; ret.extruderIndex = currentExtruderIndex; ret.done = false; return ret; }