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 void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent) { int prevExtruder = gcodeLayer.getExtruder(); bool extruderChanged = gcodeLayer.setExtruder(volumeIndex); if (layerIndex == 0 && volumeIndex == 0 && !Raft.ShouldGenerateRaft(config)) { if (storage.skirt.Count > 0 && storage.skirt[0].Count > 0) { IntPoint lowestPoint = storage.skirt[0][0]; // lets make sure we start with the most outside loop foreach (Polygon polygon in storage.skirt) { foreach (IntPoint position in polygon) { if (position.Y < lowestPoint.Y) { lowestPoint = polygon[0]; } } } gcodeLayer.writeTravel(lowestPoint); } gcodeLayer.writePolygonsByOptimizer(storage.skirt, skirtConfig); } 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++) { partOrderOptimizer.AddPolygon(layer.parts[partIndex].insets[0][0]); } partOrderOptimizer.Optimize(); for (int partCounter = 0; partCounter < partOrderOptimizer.bestPolygonOrderIndex.Count; partCounter++) { SliceLayerPart part = layer.parts[partOrderOptimizer.bestPolygonOrderIndex[partCounter]]; if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.combBoundery); } else { gcodeLayer.setAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); CalculateInfillData(storage, volumeIndex, layerIndex, extrusionWidth_um, part, ref fillPolygons, ref bridgePolygons); // Write the bidge 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 (partCounter > 0) { gcodeLayer.forceRetract(); } if (config.continuousSpiralOuterPerimeter) { if (layerIndex >= config.numberOfBottomLayers) { inset0Config.spiralize = true; } if (layerIndex == config.numberOfBottomLayers && part.insets.Count > 0) { gcodeLayer.writePolygonsByOptimizer(part.insets[0], insetXConfig); } } // 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) { // First the outside (this helps with accuracy) if (part.insets.Count > 0) { gcodeLayer.writePolygonsByOptimizer(part.insets[0], inset0Config); } 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); }