private void AddSupportToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config) { if (!storage.support.generated) { return; } if (config.supportExtruder > -1) { int prevExtruder = gcodeLayer.getExtruder(); if (gcodeLayer.SetExtruder(config.supportExtruder)) { addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, config.extrusionWidth_um); } if (storage.wipeShield.Count > 0 && storage.volumes.Count == 1) { gcodeLayer.SetAlwaysRetract(true); gcodeLayer.WritePolygonsByOptimizer(storage.wipeShield[layerIndex], skirtConfig); gcodeLayer.SetAlwaysRetract(config.avoidCrossingPerimeters); } } int currentZHeight_um = config.firstLayerThickness_um; if (layerIndex == 0) { currentZHeight_um /= 2; } else { if (layerIndex > 1) { currentZHeight_um += (layerIndex - 1) * config.layerThickness_um; } currentZHeight_um += config.layerThickness_um / 2; } SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, currentZHeight_um); WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.supportPolygons, SupportType.General); if (config.supportInterfaceExtruder != -1 && config.supportInterfaceExtruder != config.supportExtruder) { gcodeLayer.SetExtruder(config.supportInterfaceExtruder); } WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.interfacePolygons, SupportType.Interface); }
//Add a single layer from a single mesh-volume to the GCode private void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent) { int prevExtruder = gcodeLayer.getExtruder(); bool extruderChanged = gcodeLayer.SetExtruder(volumeIndex); SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex]; if (extruderChanged) { addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um); } if (storage.wipeShield.Count > 0 && storage.volumes.Count > 1) { gcodeLayer.SetAlwaysRetract(true); gcodeLayer.WritePolygonsByOptimizer(storage.wipeShield[layerIndex], skirtConfig); gcodeLayer.SetAlwaysRetract(!config.avoidCrossingPerimeters); } PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } partOrderOptimizer.AddPolygon(layer.parts[partIndex].Insets[0][0]); } partOrderOptimizer.Optimize(); for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } SliceLayerPart part = layer.parts[partOrderOptimizer.bestPolygonOrderIndex[partIndex]]; if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery); } else { gcodeLayer.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); CalculateInfillData(storage, volumeIndex, layerIndex, part, ref fillPolygons, ref bridgePolygons); // Write the bridge polgons out first so the perimeter will have more to hold to while bridging the gaps. // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder. if (bridgePolygons.Count > 0) { gcode.WriteFanCommand(config.bridgeFanSpeedPercent); gcodeLayer.WritePolygonsByOptimizer(bridgePolygons, bridgConfig); gcode.WriteFanCommand(fanSpeedPercent); } if (config.numberOfPerimeters > 0) { if (partIndex != lastPartIndex) { // force a retract if changing islands gcodeLayer.ForceRetract(); lastPartIndex = partIndex; } if (config.continuousSpiralOuterPerimeter) { if (layerIndex >= config.numberOfBottomLayers) { inset0Config.spiralize = true; } } // If we are on the very first layer we start with the outside in so that we can stick to the bed better. if (config.outsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize) { // First the outside (this helps with accuracy) if (part.Insets.Count > 0) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config); } if (!inset0Config.spiralize) { for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig); } } } else // This is so we can do overhanges better (the outside can stick a bit to the inside). { // Print everything but the first perimeter from the outside in so the little parts have more to stick to. for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig); } // then 0 if (part.Insets.Count > 0) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config); } } } gcodeLayer.WritePolygonsByOptimizer(fillPolygons, fillConfig); //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.continuousSpiralOuterPerimeter || layerIndex < config.numberOfBottomLayers) { gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); }
//Add a single layer from a single extruder to the GCode private void QueueAirGappedExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent, long currentZ_um) { if (config.generateSupport && !config.continuousSpiralOuterPerimeter && layerIndex > 0) { int prevExtruder = gcodeLayer.getExtruder(); bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex); SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex]; PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } partOrderOptimizer.AddPolygon(layer.Islands[partIndex].InsetToolPaths[0][0]); } partOrderOptimizer.Optimize(); List<Polygons> bottomFillIslandPolygons = new List<Polygons>(); for (int inlandIndex = 0; inlandIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; inlandIndex++) { if (config.continuousSpiralOuterPerimeter && inlandIndex > 0) { continue; } LayerIsland part = layer.Islands[partOrderOptimizer.bestPolygonOrderIndex[inlandIndex]]; if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery); } else { gcodeLayer.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons topFillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); Polygons bottomFillPolygons = new Polygons(); CalculateInfillData(slicingData, extruderIndex, layerIndex, part, ref bottomFillPolygons, ref fillPolygons, ref topFillPolygons, ref bridgePolygons); bottomFillIslandPolygons.Add(bottomFillPolygons); // Write the bridge polygons out first so the perimeter will have more to hold to while bridging the gaps. // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder. if (bridgePolygons.Count > 0) { gcode.WriteFanCommand(config.bridgeFanSpeedPercent); gcodeLayer.QueuePolygonsByOptimizer(bridgePolygons, bridgConfig); } if (config.numberOfPerimeters > 0) { if (inlandIndex != lastPartIndex) { // force a retract if changing islands gcodeLayer.ForceRetract(); lastPartIndex = inlandIndex; } if (config.continuousSpiralOuterPerimeter) { if (layerIndex >= config.numberOfBottomLayers) { inset0Config.spiralize = true; } } } //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.continuousSpiralOuterPerimeter || layerIndex < config.numberOfBottomLayers) { gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } // Print everything but the first perimeter from the outside in so the little parts have more to stick to. for (int perimeterIndex = 1; perimeterIndex < part.InsetToolPaths.Count; perimeterIndex++) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.SupportedAreas); } // then 0 if (part.InsetToolPaths.Count > 0) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[0], inset0Config, SupportWriteType.SupportedAreas); } QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillIslandPolygons[inlandIndex], bottomFillConfig, SupportWriteType.SupportedAreas); } } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); }
//Add a single layer from a single extruder to the GCode private void QueueExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent, long currentZ_um) { int prevExtruder = gcodeLayer.getExtruder(); bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex); SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex]; if (extruderChanged) { addWipeTower(slicingData, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um); } if (slicingData.wipeShield.Count > 0 && slicingData.Extruders.Count > 1) { gcodeLayer.SetAlwaysRetract(true); gcodeLayer.QueuePolygonsByOptimizer(slicingData.wipeShield[layerIndex], skirtConfig); gcodeLayer.SetAlwaysRetract(!config.avoidCrossingPerimeters); } PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } partOrderOptimizer.AddPolygon(layer.Islands[partIndex].InsetToolPaths[0][0]); } partOrderOptimizer.Optimize(); List<Polygons> bottomFillIslandPolygons = new List<Polygons>(); for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } LayerIsland island = layer.Islands[partOrderOptimizer.bestPolygonOrderIndex[partIndex]]; if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(island.AvoidCrossingBoundery); } else { gcodeLayer.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons topFillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); Polygons bottomFillPolygons = new Polygons(); CalculateInfillData(slicingData, extruderIndex, layerIndex, island, ref bottomFillPolygons, ref fillPolygons, ref topFillPolygons, ref bridgePolygons); bottomFillIslandPolygons.Add(bottomFillPolygons); // Write the bridge polygons out first so the perimeter will have more to hold to while bridging the gaps. // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder. if (bridgePolygons.Count > 0) { gcode.WriteFanCommand(config.bridgeFanSpeedPercent); gcodeLayer.QueuePolygonsByOptimizer(bridgePolygons, bridgConfig); } if (config.numberOfPerimeters > 0) { if (partIndex != lastPartIndex) { // force a retract if changing islands if (config.retractWhenChangingIslands) { gcodeLayer.ForceRetract(); } lastPartIndex = partIndex; } if (config.continuousSpiralOuterPerimeter) { if (layerIndex >= config.numberOfBottomLayers) { inset0Config.spiralize = true; } } // If we are on the very first layer we start with the outside so that we can stick to the bed better. if (config.outsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize) { if (inset0Config.spiralize) { if (island.InsetToolPaths.Count > 0) { Polygon outsideSinglePolygon = island.InsetToolPaths[0][0]; gcodeLayer.QueuePolygonsByOptimizer(new Polygons() { outsideSinglePolygon }, inset0Config); } } else { // First the outside (this helps with accuracy) if (island.InsetToolPaths.Count > 0) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[0], inset0Config, SupportWriteType.UnsupportedAreas); } for (int perimeterIndex = 1; perimeterIndex < island.InsetToolPaths.Count; perimeterIndex++) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.UnsupportedAreas); } } } else // This is so we can do overhangs better (the outside can stick a bit to the inside). { // Figure out where the seam hiding start point is for inset 0 and move to that spot so // we have the minimum travel while starting inset 0 after printing the rest of the insets if (island?.InsetToolPaths?[0]?[0]?.Count > 0) { int bestPoint = PathOrderOptimizer.GetBestEdgeIndex(island.InsetToolPaths[0][0]); gcodeLayer.QueueTravel(island.InsetToolPaths[0][0][bestPoint]); } // Print everything but the first perimeter from the outside in so the little parts have more to stick to. for (int perimeterIndex = 1; perimeterIndex < island.InsetToolPaths.Count; perimeterIndex++) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.UnsupportedAreas); } // then 0 if (island.InsetToolPaths.Count > 0) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[0], inset0Config, SupportWriteType.UnsupportedAreas); } } } gcodeLayer.QueuePolygonsByOptimizer(fillPolygons, fillConfig); QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillPolygons, bottomFillConfig, SupportWriteType.UnsupportedAreas); gcodeLayer.QueuePolygonsByOptimizer(topFillPolygons, topFillConfig); //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.continuousSpiralOuterPerimeter || layerIndex < config.numberOfBottomLayers) { gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); }
//Add a single layer from a single extruder to the GCode private void QueueExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner layerGcodePlanner, int extruderIndex, int layerIndex, int extrusionWidth_um, long currentZ_um) { if(extruderIndex > slicingData.Extruders.Count-1) { return; } SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex]; if(layer.AllOutlines.Count == 0 && config.WipeShieldDistanceFromObject == 0) { // don't do anything on this layer return; } if (slicingData.wipeShield.Count > 0 && slicingData.Extruders.Count > 1) { layerGcodePlanner.SetAlwaysRetract(true); layerGcodePlanner.QueuePolygonsByOptimizer(slicingData.wipeShield[layerIndex], skirtConfig); layerGcodePlanner.SetAlwaysRetract(!config.AvoidCrossingPerimeters); } PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++) { if (config.ContinuousSpiralOuterPerimeter && partIndex > 0) { continue; } islandOrderOptimizer.AddPolygon(layer.Islands[partIndex].InsetToolPaths[0][0]); } islandOrderOptimizer.Optimize(); List<Polygons> bottomFillIslandPolygons = new List<Polygons>(); for (int islandOrderIndex = 0; islandOrderIndex < islandOrderOptimizer.bestIslandOrderIndex.Count; islandOrderIndex++) { if (config.ContinuousSpiralOuterPerimeter && islandOrderIndex > 0) { continue; } LayerIsland island = layer.Islands[islandOrderOptimizer.bestIslandOrderIndex[islandOrderIndex]]; if (config.AvoidCrossingPerimeters) { layerGcodePlanner.SetOuterPerimetersToAvoidCrossing(island.AvoidCrossingBoundary); } else { layerGcodePlanner.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons topFillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); Polygons bottomFillPolygons = new Polygons(); CalculateInfillData(slicingData, extruderIndex, layerIndex, island, bottomFillPolygons, fillPolygons, topFillPolygons, bridgePolygons); bottomFillIslandPolygons.Add(bottomFillPolygons); // Write the bridge polygons out first so the perimeter will have more to hold to while bridging the gaps. // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder. if (bridgePolygons.Count > 0) { QueuePolygonsConsideringSupport(layerIndex, layerGcodePlanner, bridgePolygons, bridgeConfig, SupportWriteType.UnsupportedAreas); } if (config.NumberOfPerimeters > 0) { if (islandOrderIndex != lastPartIndex) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { layerGcodePlanner.ForceRetract(); } lastPartIndex = islandOrderIndex; } if (config.ContinuousSpiralOuterPerimeter) { if (layerIndex >= config.NumberOfBottomLayers) { inset0Config.spiralize = true; } } // Figure out where the seam hiding start point is for inset 0 and move to that spot so // we have the minimum travel while starting inset 0 after printing the rest of the insets if (island?.InsetToolPaths?[0]?[0]?.Count > 0 && !config.ContinuousSpiralOuterPerimeter) { int bestPoint = PathOrderOptimizer.GetBestIndex(island.InsetToolPaths[0][0], config.ExtrusionWidth_um); layerGcodePlanner.QueueTravel(island.InsetToolPaths[0][0][bestPoint]); } // Put all the insets into a new list so we can keep track of what has been printed. List<Polygons> insetsToPrint = new List<Polygons>(island.InsetToolPaths.Count); for (int insetIndex = 0; insetIndex < island.InsetToolPaths.Count; insetIndex++) { insetsToPrint.Add(new Polygons()); for (int polygonIndex = 0; polygonIndex < island.InsetToolPaths[insetIndex].Count; polygonIndex++) { if (island.InsetToolPaths[insetIndex][polygonIndex].Count > 0) { insetsToPrint[insetIndex].Add(island.InsetToolPaths[insetIndex][polygonIndex]); } } } // If we are on the very first layer we start with the outside so that we can stick to the bed better. if (config.OutsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize) { if (inset0Config.spiralize) { if (island.InsetToolPaths.Count > 0) { Polygon outsideSinglePolygon = island.InsetToolPaths[0][0]; layerGcodePlanner.QueuePolygonsByOptimizer(new Polygons() { outsideSinglePolygon }, inset0Config); } } else { int insetCount = CountInsetsToPrint(insetsToPrint); while (insetCount > 0) { bool limitDistance = false; if (island.InsetToolPaths.Count > 0) { QueueClosetsInset(insetsToPrint[0], limitDistance, inset0Config, layerIndex, layerGcodePlanner); } if (island.InsetToolPaths.Count > 1) { // Move to the closest inset 1 and print it limitDistance = QueueClosetsInset(insetsToPrint[1], limitDistance, insetXConfig, layerIndex, layerGcodePlanner); for (int insetIndex = 2; insetIndex < island.InsetToolPaths.Count; insetIndex++) { limitDistance = QueueClosetsInset( insetsToPrint[insetIndex], limitDistance, insetIndex == 0 ? inset0Config : insetXConfig, layerIndex, layerGcodePlanner); } } insetCount = CountInsetsToPrint(insetsToPrint); } } } else // This is so we can do overhangs better (the outside can stick a bit to the inside). { int insetCount = CountInsetsToPrint(insetsToPrint); while (insetCount > 0) { bool limitDistance = false; if (island.InsetToolPaths.Count > 0) { // Move to the closest inset 1 and print it for (int insetIndex = island.InsetToolPaths.Count-1; insetIndex >= 0; insetIndex--) { limitDistance = QueueClosetsInset( insetsToPrint[insetIndex], limitDistance, insetIndex == 0 ? inset0Config : insetXConfig, layerIndex, layerGcodePlanner); } } insetCount = CountInsetsToPrint(insetsToPrint); } } } // TODO: Put all of these segments into a list that can be queued together and still preserver their individual config settings. // This will make the total amount of travel while printing infill much less. layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, fillConfig); QueuePolygonsConsideringSupport(layerIndex, layerGcodePlanner, bottomFillPolygons, bottomFillConfig, SupportWriteType.UnsupportedAreas); layerGcodePlanner.QueuePolygonsByOptimizer(topFillPolygons, topFillConfig); //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.ContinuousSpiralOuterPerimeter || layerIndex < config.NumberOfBottomLayers) { layerGcodePlanner.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } } // Find the thin lines for this layer and add them to the queue if (false) // this code is just for test. LBB { Polygons fillPolygons = new Polygons(); foreach (var island in layer.Islands) { List<Point3> path = new List<Point3>(); List<PathAndWidth> thinLines; foreach (var outline in island.IslandOutline.Offset(-extrusionWidth_um * 0)) { foreach (var point in outline) { path.Add(new Point3(point, currentZ_um)); } } if (layerGcodePlanner.FindThinLines(path, extrusionWidth_um - 2, out thinLines)) { foreach (var widthPath in thinLines) { Polygon thinPath = new Polygon(); foreach (var point in widthPath.Path) { thinPath.Add(new IntPoint(point.x, point.y)); } fillPolygons.Add(thinPath); } } } layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, fillConfig); } layerGcodePlanner.SetOuterPerimetersToAvoidCrossing(null); }
//Add a single layer from a single extruder to the GCode private void QueueAirGappedExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, long currentZ_um) { if (config.GenerateSupport && !config.ContinuousSpiralOuterPerimeter && layerIndex > 0) { int prevExtruder = gcodeLayer.GetExtruder(); bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex); SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex]; PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++) { if (config.ContinuousSpiralOuterPerimeter && partIndex > 0) { continue; } partOrderOptimizer.AddPolygon(layer.Islands[partIndex].InsetToolPaths[0][0]); } partOrderOptimizer.Optimize(); List<Polygons> bottomFillIslandPolygons = new List<Polygons>(); for (int inlandIndex = 0; inlandIndex < partOrderOptimizer.bestIslandOrderIndex.Count; inlandIndex++) { if (config.ContinuousSpiralOuterPerimeter && inlandIndex > 0) { continue; } LayerIsland part = layer.Islands[partOrderOptimizer.bestIslandOrderIndex[inlandIndex]]; if (config.AvoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundary); } else { gcodeLayer.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons topFillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); Polygons bottomFillPolygons = new Polygons(); CalculateInfillData(slicingData, extruderIndex, layerIndex, part, bottomFillPolygons, fillPolygons, topFillPolygons, bridgePolygons); bottomFillIslandPolygons.Add(bottomFillPolygons); #if DEBUG if (bridgePolygons.Count > 0) { new Exception("Unexpected bridge polygons in air gapped region"); } #endif if (config.NumberOfPerimeters > 0) { if (inlandIndex != lastPartIndex) { // force a retract if changing islands if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } lastPartIndex = inlandIndex; } if (config.ContinuousSpiralOuterPerimeter) { if (layerIndex >= config.NumberOfBottomLayers) { inset0Config.spiralize = true; } } } //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.ContinuousSpiralOuterPerimeter || layerIndex < config.NumberOfBottomLayers) { gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } // Print everything but the first perimeter from the outside in so the little parts have more to stick to. for (int insetIndex = 1; insetIndex < part.InsetToolPaths.Count; insetIndex++) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[insetIndex], airGappedBottomConfig, SupportWriteType.SupportedAreas); } // then 0 if (part.InsetToolPaths.Count > 0) { QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[0], airGappedBottomConfig, SupportWriteType.SupportedAreas); } QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillIslandPolygons[inlandIndex], airGappedBottomConfig, SupportWriteType.SupportedAreas); } } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); }