public static void GenerateTriangleInfill(ConfigSettings config, Polygons partOutline, ref Polygons fillPolygons, double fillAngle) { if (config.infillPercent <= 0) { throw new Exception("infillPercent must be gerater than 0."); } int linespacing_um = (int)(config.extrusionWidth_um / (config.infillPercent / 100) * 3); long offset = linespacing_um / 2; Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset); fillAngle += 60; if (fillAngle > 360) { fillAngle -= 360; } Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset); fillAngle += 60; if (fillAngle > 360) { fillAngle -= 360; } Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset); }
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 if (config.GenerateSupportPerimeter) { Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4); gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig); } 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, supportNormalConfig); } }
public bool 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(); 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, supportNormalConfig)) { outputPaths = true; } } 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; } } if (gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig)) { outputPaths |= true; } } return(outputPaths); }
public static void GenerateHexagonInfill(ConfigSettings config, Polygons partOutline, ref Polygons fillPolygons, double fillAngle, int layerIndex) { if (config.infillPercent <= 0) { throw new Exception("infillPercent must be gerater than 0."); } int linespacing_um = (int)(config.extrusionWidth_um / (config.infillPercent / 100) * 3 * .66); Infill.GenerateHexLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, layerIndex); }
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 GenerateFillConsideringBridging(Polygons bottomFillIsland, Polygons bottomFillLines, ConfigSettings config, Polygons bridgePolygons, string debugName = "") { double bridgeAngle = 0; if (this.BridgeAngle(bottomFillIsland, out bridgeAngle)) { // TODO: Make this code handle very complex pathing between different sizes or layouts of support under the island to fill. Infill.GenerateLinePaths(bottomFillIsland, bridgePolygons, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, bridgeAngle); } else { Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, config.InfillStartingAngle); } }
public bool QueueInterfaceSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig) { // interface bool outputPaths = false; Polygons interfaceOutlines = InterfaceLayers[layerIndex]; if (interfaceOutlines.Count > 0) { List <Polygons> interfaceIslands = interfaceOutlines.ProcessIntoSeparateIslands(); foreach (Polygons interfaceIsland in interfaceIslands) { PathFinder pathFinder = null; if (config.AvoidCrossingPerimeters) { pathFinder = new PathFinder(interfaceIsland, -config.ExtrusionWidth_um / 2, useInsideCache: config.AvoidCrossingPerimeters, name: "interface"); } // 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 if (config.GenerateSupportPerimeter || layerIndex == 0) { if (gcodeLayer.QueuePolygonsByOptimizer(interfaceIsland.Offset(-config.ExtrusionWidth_um / 2), pathFinder, supportInterfaceConfig, 0)) { outputPaths = true; } infillOffset = config.ExtrusionWidth_um * -2 + config.InfillExtendIntoPerimeter_um; } Polygons supportLines = new Polygons(); Infill.GenerateLineInfill(config, interfaceIsland.Offset(infillOffset), supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um); if (gcodeLayer.QueuePolygonsByOptimizer(supportLines, pathFinder, supportInterfaceConfig, 0)) { outputPaths = true; } } } return(outputPaths); }
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 void QueueAirGappedBottomLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentAirGappedBottoms = AirGappedBottomOutlines[layerIndex]; List <Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparateIslands(); foreach (Polygons supportIsland in supportIslands) { PathFinder pathFinder = null; if (config.AvoidCrossingPerimeters) { pathFinder = new PathFinder(supportIsland, -config.ExtrusionWidth_um / 2, useInsideCache: config.AvoidCrossingPerimeters, name: "air gap"); } // 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 if (config.GenerateSupportPerimeter || layerIndex == 0) { var closedLoop = supportNormalConfig.ClosedLoop; supportNormalConfig.ClosedLoop = true; gcodeLayer.QueuePolygonsByOptimizer(supportIsland.Offset(-config.ExtrusionWidth_um / 2), pathFinder, supportNormalConfig, layerIndex); infillOffset = config.ExtrusionWidth_um * -2 + config.InfillExtendIntoPerimeter_um; supportNormalConfig.ClosedLoop = closedLoop; } Polygons infillOutline = supportIsland.Offset(infillOffset); Polygons islandInfillLines = new Polygons(); 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, pathFinder, supportNormalConfig, 0); } }
private void addWipeTower(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerNr, int prevExtruder, int extrusionWidth_um) { if (config.wipeTowerSize_um < 1) { return; } //If we changed extruder, print the wipe/prime tower for this nozzle; gcodeLayer.WritePolygonsByOptimizer(storage.wipeTower, supportInterfaceConfig); Polygons fillPolygons = new Polygons(); Infill.GenerateLinePaths(storage.wipeTower, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, 45 + 90 * (layerNr % 2)); gcodeLayer.WritePolygonsByOptimizer(fillPolygons, supportInterfaceConfig); //Make sure we wipe the old extruder on the wipe tower. gcodeLayer.WriteTravel(storage.wipePoint - config.extruderOffsets[prevExtruder] + config.extruderOffsets[gcodeLayer.getExtruder()]); }
public void CalculateInfillData(ConfigSettings config, int extruderIndex, int layerIndex, LayerIsland part, Polygons bottomFillLines, Polygons sparseFillPolygons = null, Polygons solidFillPolygons = null, Polygons firstTopFillPolygons = null, Polygons topFillPolygons = null, Polygons bridgePolygons = null, Polygons bridgeAreas = null) { double alternatingInfillAngle = config.InfillStartingAngle; if ((layerIndex % 2) == 0) { alternatingInfillAngle += 90; } // generate infill for the bottom layer including bridging foreach (Polygons bottomFillIsland in part.BottomPaths.ProcessIntoSeparateIslands()) { if (layerIndex > 0) { if (this.Support != null) { double infillAngle = config.SupportInterfaceLayers > 0 ? config.InfillStartingAngle : config.InfillStartingAngle + 90; Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, infillAngle); } else { SliceLayer previousLayer = this.Extruders[extruderIndex].Layers[layerIndex - 1]; if (bridgePolygons != null && previousLayer.BridgeAngle(bottomFillIsland, config.GetNumberOfPerimeters() * config.ExtrusionWidth_um, out double bridgeAngle, bridgeAreas)) { // TODO: Make this code handle very complex pathing between different sizes or layouts of support under the island to fill. Infill.GenerateLinePaths(bottomFillIsland, bridgePolygons, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, bridgeAngle); } else // we still need to extrude at bridging speed { Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, alternatingInfillAngle, 0, config.BridgeSpeed); } } }
public void QueueAirGappedBottomLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig, PathFinder pathFinder) { // normal support Polygons currentAirGappedBottoms = AirGappedBottomOutlines[layerIndex]; List <Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparateIslands(); foreach (Polygons supportIsland in supportIslands) { 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 if (config.GenerateSupportPerimeter || layerIndex == 0) { allSupport.AddRange(supportIsland.Offset(config.ExtrusionWidth_um / 2)); infillOffset = config.ExtrusionWidth_um * -2 + config.InfillExtendIntoPerimeter_um; } Polygons infillOutline = supportIsland.Offset(infillOffset); var islandInfillLines = new Polygons(); switch (config.SupportType) { case SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } allSupport.AddRange(islandInfillLines); gcodeLayer.QueuePolygonsByOptimizer(allSupport, pathFinder, supportNormalConfig, layerIndex); } }
public static void GenerateGridInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, double fillAngle, int linespacing_um = 0) { if (linespacing_um == 0) { if (config.InfillPercent <= 0) { throw new Exception("infillPercent must be greater than 0."); } linespacing_um = (int)(config.ExtrusionWidth_um / (config.InfillPercent / 100) * 2); } Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle); fillAngle += 90; if (fillAngle > 360) { fillAngle -= 360; } Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle); }
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 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 WriteRaftGCodeIfRequired(GCodeExport gcode, ConfigSettings config) { LayerDataStorage storage = this; if (config.ShouldGenerateRaft()) { GCodePathConfig raftBaseConfig = new GCodePathConfig("raftBaseConfig"); raftBaseConfig.SetData(config.FirstLayerSpeed, config.RaftBaseExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftMiddleConfig = new GCodePathConfig("raftMiddleConfig"); raftMiddleConfig.SetData(config.RaftPrintSpeed, config.RaftInterfaceExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftSurfaceConfig = new GCodePathConfig("raftMiddleConfig"); raftSurfaceConfig.SetData((config.RaftSurfacePrintSpeed > 0) ? config.RaftSurfacePrintSpeed : config.RaftPrintSpeed, config.RaftSurfaceExtrusionWidth_um, "SUPPORT"); // create the raft base { gcode.WriteComment("RAFT BASE"); LayerGCodePlanner layerPlanner = new LayerGCodePlanner(config, gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio); if (config.RaftExtruder >= 0) { // if we have a specified raft extruder use it layerPlanner.SetExtruder(config.RaftExtruder); } else if (config.SupportExtruder >= 0) { // else preserve the old behavior of using the support extruder if set. layerPlanner.SetExtruder(config.SupportExtruder); } gcode.CurrentZ = config.RaftBaseThickness_um; gcode.LayerChanged(-3, config.RaftBaseThickness_um); gcode.SetExtrusion(config.RaftBaseThickness_um, config.FilamentDiameter_um, config.ExtrusionMultiplier); // write the skirt around the raft layerPlanner.QueuePolygonsByOptimizer(storage.Skirt, null, raftBaseConfig, 0); List <Polygons> raftIslands = storage.raftOutline.ProcessIntoSeparateIslands(); foreach (var raftIsland in raftIslands) { // write the outline of the raft layerPlanner.QueuePolygonsByOptimizer(raftIsland, null, raftBaseConfig, 0); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(raftIsland.Offset(-config.RaftBaseExtrusionWidth_um), raftLines, config.RaftBaseLineSpacing_um, config.InfillExtendIntoPerimeter_um, 0); // write the inside of the raft base layerPlanner.QueuePolygonsByOptimizer(raftLines, null, raftBaseConfig, 0); if (config.RetractWhenChangingIslands) { layerPlanner.ForceRetract(); } } layerPlanner.WriteQueuedGCode(config.RaftBaseThickness_um); } // raft middle layers { gcode.WriteComment("RAFT MIDDLE"); LayerGCodePlanner layerPlanner = new LayerGCodePlanner(config, gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio); gcode.CurrentZ = config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um; gcode.LayerChanged(-2, config.RaftInterfaceThicknes_um); gcode.SetExtrusion(config.RaftInterfaceThicknes_um, config.FilamentDiameter_um, config.ExtrusionMultiplier); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftInterfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 45); layerPlanner.QueuePolygonsByOptimizer(raftLines, null, raftMiddleConfig, 0); layerPlanner.WriteQueuedGCode(config.RaftInterfaceThicknes_um); } for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.RaftSurfaceLayers; raftSurfaceIndex++) { gcode.WriteComment("RAFT SURFACE"); LayerGCodePlanner layerPlanner = new LayerGCodePlanner(config, gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio); gcode.CurrentZ = config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um + config.RaftSurfaceThickness_um * raftSurfaceIndex; gcode.LayerChanged(-1, config.RaftSurfaceThickness_um); 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, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, config.InfillStartingAngle + 90); } else { Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 90 * raftSurfaceIndex); } layerPlanner.QueuePolygonsByOptimizer(raftLines, null, raftSurfaceConfig, 0); layerPlanner.WriteQueuedGCode(config.RaftInterfaceThicknes_um); } } }
public bool QueueNormalSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig) { // normal support Polygons currentSupportOutlines = SparseSupportOutlines[layerIndex]; List <Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparateIslands(); List <PathFinder> pathFinders = new List <PathFinder>(); List <Polygons> infillOutlines = new List <Polygons>(); for (int i = 0; i < supportIslands.Count; i++) { pathFinders.Add(null); infillOutlines.Add(null); } Agg.Parallel.For(0, supportIslands.Count, (index) => { var infillOffset = -config.ExtrusionWidth_um + config.InfillExtendIntoPerimeter_um; var supportIsland = supportIslands[index]; infillOutlines[index] = supportIsland.Offset(infillOffset); if (config.AvoidCrossingPerimeters) { pathFinders[index] = new PathFinder(infillOutlines[index], -config.ExtrusionWidth_um / 2, useInsideCache: config.AvoidCrossingPerimeters, name: "normal support"); } }); bool outputPaths = false; for (int i = 0; i < supportIslands.Count; i++) { var supportIsland = supportIslands[i]; // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } // make a border if layer 0 if (config.GenerateSupportPerimeter || layerIndex == 0) { if (gcodeLayer.QueuePolygonsByOptimizer(supportIsland.Offset(-config.ExtrusionWidth_um / 2), pathFinders[i], supportNormalConfig, 0)) { outputPaths = true; } } Polygons islandInfillLines = new Polygons(); if (layerIndex == 0) { // on the first layer print this as solid Infill.GenerateLineInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um); } else { switch (config.SupportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } } if (gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, pathFinders[i], supportNormalConfig, 0)) { outputPaths |= true; } } return(outputPaths); }
private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, int extrusionWidth_um, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons) { // generate infill for outline including bridging foreach (Polygons outline in part.skinOutline.SplitIntoParts()) { double partFillAngle = config.infillStartingAngle; if ((layerIndex & 1) == 1) { partFillAngle += 90; } if (layerIndex > 0) { double bridgeAngle; if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle)) { Infill.GenerateLinePaths(outline, ref bridgePolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle); } else { Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle); } } else { Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle); } } double fillAngle = config.infillStartingAngle; // generate the infill for this part on this layer if (config.infillPercent > 0) { switch (config.infillType) { case ConfigConstants.INFILL_TYPE.LINES: if ((layerIndex & 1) == 1) { fillAngle += 90; } Infill.GenerateLineInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle); break; case ConfigConstants.INFILL_TYPE.GRID: Infill.GenerateGridInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle); break; case ConfigConstants.INFILL_TYPE.TRIANGLES: Infill.GenerateTriangleInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle); break; case ConfigConstants.INFILL_TYPE.HEXAGON: Infill.GenerateHexagonInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle, layerIndex); break; case ConfigConstants.INFILL_TYPE.CONCENTRIC: Infill.generateConcentricInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle); break; default: throw new NotImplementedException(); } } }
public static void GenerateRaftGCodeIfRequired(SliceDataStorage storage, ConfigSettings config, GCodeExport gcode) { if (ShouldGenerateRaft(config)) { 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); gcodeLayer.writePolygonsByOptimizer(storage.raftOutline, raftBaseConfig); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseLineSpacing_um, config.infillExtendIntoPerimeter_um, 0); gcodeLayer.writePolygonsByOptimizer(storage.skirt, raftBaseConfig); gcodeLayer.writePolygonsByOptimizer(raftLines, raftBaseConfig); gcodeLayer.writeGCode(false, 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.writePolygonsByOptimizer(raftLines, raftMiddleConfig); gcodeLayer.writeGCode(false, 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.writePolygonsByOptimizer(raftLines, raftSurfaceConfig); gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um); } } }
public static void GenerateRaftGCodeIfRequired(SliceDataStorage storage, ConfigSettings config, GCodeExport gcode) { if (ShouldGenerateRaft(config)) { GCodePathConfig raftBaseConfig = new GCodePathConfig(config.firstLayerSpeed, config.extrusionWidth_um * 3, "SUPPORT"); GCodePathConfig raftMiddleConfig = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceLinewidth_um, "SUPPORT"); GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceLinewidth_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.supportExtruder > 0) { gcodeLayer.setExtruder(config.supportExtruder); } gcode.setZ(config.raftBaseThickness_um); gcode.setExtrusion(config.raftBaseThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); gcodeLayer.writePolygonsByOptimizer(storage.raftOutline, raftBaseConfig); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseThickness_um, config.raftLineSpacing_um, config.infillExtendIntoPerimeter_um, 0); gcodeLayer.writePolygonsByOptimizer(storage.skirt, raftBaseConfig); gcodeLayer.writePolygonsByOptimizer(raftLines, raftBaseConfig); gcodeLayer.writeGCode(false, 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.raftInterfaceLinewidth_um, config.raftInterfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 45); gcodeLayer.writePolygonsByOptimizer(raftLines, raftMiddleConfig); gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um); } for (int raftSurfaceLayer = 1; raftSurfaceLayer <= config.raftSurfaceLayers; raftSurfaceLayer++) { 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 * raftSurfaceLayer); gcode.setExtrusion(config.raftSurfaceThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLinewidth_um, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 90 * raftSurfaceLayer); gcodeLayer.writePolygonsByOptimizer(raftLines, raftSurfaceConfig); gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um); } } }
private void WriteSupportPolygons(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config, Polygons supportPolygons, SupportType interfaceLayer) { for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++) { SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex]; for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++) { supportPolygons = supportPolygons.CreateDifference(layer.parts[partIndex].TotalOutline.Offset(config.supportXYDistance_um)); } } //Contract and expand the support polygons so small sections are removed and the final polygon is smoothed a bit. supportPolygons = supportPolygons.Offset(-config.extrusionWidth_um * 1); supportPolygons = supportPolygons.Offset(config.extrusionWidth_um * 1); List<Polygons> supportIslands = supportPolygons.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd); PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(gcode.GetPositionXY()); for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++) { islandOrderOptimizer.AddPolygon(supportIslands[islandIndex][0]); } islandOrderOptimizer.Optimize(); for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++) { Polygons island = supportIslands[islandOrderOptimizer.bestPolygonOrderIndex[islandIndex]]; Polygons supportLines = new Polygons(); if (config.supportLineSpacing_um > 0) { switch (interfaceLayer) { case SupportType.Interface: Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle + 90, config.extrusionWidth_um); break; case SupportType.General: switch (config.supportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; } break; default: throw new NotImplementedException(); } } if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(island); } switch (interfaceLayer) { case SupportType.Interface: gcodeLayer.WritePolygonsByOptimizer(supportLines, supportInterfaceConfig); break; case SupportType.General: if (config.supportType == ConfigConstants.SUPPORT_TYPE.GRID) { gcodeLayer.WritePolygonsByOptimizer(island, supportNormalConfig); } gcodeLayer.WritePolygonsByOptimizer(supportLines, supportNormalConfig); break; default: throw new NotImplementedException(); } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); } }
private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons) { // generate infill the bottom layers including bridging foreach (Polygons outline in part.SolidBottomOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { if (layerIndex > 0) { double bridgeAngle; if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle)) { Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle); } else { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } else { Infill.GenerateLinePaths(outline, ref fillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } // generate infill for the top layers foreach (Polygons outline in part.SolidTopOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } // generate infill intermediate layers foreach (Polygons outline in part.SolidInfillOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { if (true) // use the old infill method { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90 * (layerIndex % 2)); } else // use the new concentric infill (not tested enough yet) have to handle some bad casses better { double oldInfillPercent = config.infillPercent; config.infillPercent = 100; Infill.GenerateConcentricInfill(config, outline, ref fillPolygons); config.infillPercent = oldInfillPercent; } } double fillAngle = config.infillStartingAngle; // generate the infill for this part on this layer if (config.infillPercent > 0) { switch (config.infillType) { case ConfigConstants.INFILL_TYPE.LINES: if ((layerIndex & 1) == 1) { fillAngle += 90; } Infill.GenerateLineInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.GRID: Infill.GenerateGridInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.TRIANGLES: Infill.GenerateTriangleInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.HEXAGON: Infill.GenerateHexagonInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle, layerIndex); break; case ConfigConstants.INFILL_TYPE.CONCENTRIC: Infill.GenerateConcentricInfill(config, part.InfillOutlines, ref fillPolygons); break; default: throw new NotImplementedException(); } } }
public bool EnsureWipeTowerIsSolid(int layerIndex, PathFinder pathFinder, LayerGCodePlanner layerGcodePlanner, GCodePathConfig fillConfig, ConfigSettings config) { if (layerIndex >= LastLayerWithChange(config)) { return(false); } // TODO: if layer index == 0 do all the loops from the outside-in, in order (no lines should be in the wipe tower) if (layerIndex == 0 && !config.EnableRaft) { CheckNoExtruderPrimed(config); long insetPerLoop = fillConfig.LineWidth_um; int maxPrimingLoops = MaxPrimingLoops(config); Polygons outlineForExtruder = this.WipeLayer(layerIndex); var fillPolygons = new Polygons(); while (outlineForExtruder.Count > 0) { for (int polygonIndex = 0; polygonIndex < outlineForExtruder.Count; polygonIndex++) { Polygon newInset = outlineForExtruder[polygonIndex]; newInset.Add(newInset[0]); // add in the last move so it is a solid polygon fillPolygons.Add(newInset); } outlineForExtruder = outlineForExtruder.Offset(-insetPerLoop); } // set the path planner to avoid islands if (this.HaveWipeTower(config, layerIndex)) { layerGcodePlanner.QueueTravel(WipeCenter_um, pathFinder); } // turn off the planner for the wipe tower layerGcodePlanner.QueuePolygons(fillPolygons, null, fillConfig); } else { bool allowPartialInfill = false; // check if there was a change, if not partial fill the wipe tower if (primesThisLayer == 0 && allowPartialInfill) { var outlinePolygons = new Polygons(); for (int i = 0; i < config.NumberOfPerimeters; i++) { var insets = this.WipeLayer(layerIndex).Offset(i * -fillConfig.LineWidth_um); foreach (var inset in insets) { outlinePolygons.Add(inset); // Add the first point back onto the polygon outlinePolygons.Last().Add(outlinePolygons.Last()[0]); } } layerGcodePlanner.QueuePolygons(outlinePolygons, null, fillConfig); // print sparse infill on the wipe tower var fillPolygons = new Polygons(); Infill.GenerateTriangleInfill( config, this.WipeLayer(layerIndex).Offset(config.NumberOfPerimeters * -fillConfig.LineWidth_um), fillPolygons, config.InfillStartingAngle); layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, null, fillConfig, layerIndex); } else { // print all of the extruder loops that have not already been printed int maxPrimingLoops = MaxPrimingLoops(config); for (int primeLoop = primesThisLayer; primeLoop < maxPrimingLoops; primeLoop++) { // write the loops for this extruder, but don't change to it. We are just filling the prime tower. PrimeOnWipeTower(layerIndex, layerGcodePlanner, pathFinder, fillConfig, config, false); } } // clear the history of printer extruders for the next layer primesThisLayer = 0; } return(true); }
public bool QueueNormalSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig, PathFinder pathFinder) { var foundSupport = false; // normal support Polygons currentSupportOutlines = SparseSupportOutlines[layerIndex]; List <Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparateIslands(); var infillOutlines = new List <Polygons>(); for (int i = 0; i < supportIslands.Count; i++) { infillOutlines.Add(null); } Agg.Parallel.For(0, supportIslands.Count, (index) => { var infillOffset = -config.ExtrusionWidth_um + config.InfillExtendIntoPerimeter_um; var supportIsland = supportIslands[index]; infillOutlines[index] = supportIsland.Offset(infillOffset); }); for (int i = 0; i < supportIslands.Count; i++) { var allSupport = new Polygons(); var supportIsland = supportIslands[i]; // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } // make a border if layer 0 if (config.GenerateSupportPerimeter || layerIndex == 0) { allSupport.AddRange(supportIsland.Offset(config.ExtrusionWidth_um / 2)); } var islandInfillLines = new Polygons(); if (layerIndex == 0) { // on the first layer print this as solid Infill.GenerateLineInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um); } else { switch (config.SupportType) { case SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; case SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um); break; } } allSupport.AddRange(islandInfillLines); if (gcodeLayer.QueuePolygonsByOptimizer(allSupport, pathFinder, supportNormalConfig, 0)) { foundSupport = true; } } return(foundSupport); }