public void finalize(int maxObjectHeight, int moveSpeed, string endCode) { writeFanCommand(0); writeRetraction(); setZ(maxObjectHeight + 5000); writeMove(getPositionXY(), moveSpeed, 0); writeCode(endCode); writeComment("filament used = {0:0.0}".FormatWith(getTotalFilamentUsed(0) + getTotalFilamentUsed(1))); writeComment("filament used extruder 1 (mm) = {0:0.0}".FormatWith(getTotalFilamentUsed(0))); writeComment("filament used extruder 2 (mm) = {0:0.0}".FormatWith(getTotalFilamentUsed(1))); writeComment("total print time (s) = {0:0}".FormatWith(getTotalPrintTime())); LogOutput.log("Print time: {0}\n".FormatWith((int)(getTotalPrintTime()))); LogOutput.log("Filament: {0}\n".FormatWith((int)(getTotalFilamentUsed(0)))); LogOutput.log("Filament2: {0}\n".FormatWith((int)(getTotalFilamentUsed(1)))); if (GetOutputType() == ConfigConstants.OUTPUT_TYPE.ULTIGCODE) { string numberString; numberString = "{0}".FormatWith((int)(getTotalPrintTime())); //replaceTagInStart("<__TIME__>", numberString); numberString = "{0}".FormatWith((int)(getTotalFilamentUsed(0))); //replaceTagInStart("<FILAMENT>", numberString); numberString = "{0}".FormatWith((int)(getTotalFilamentUsed(1))); //replaceTagInStart("<FILAMEN2>", numberString); } }
public bool LoadStlFile(string input_filename) { preSetup(config.extrusionWidth_um); timeKeeper.Restart(); LogOutput.log("Loading {0} from disk...\n".FormatWith(input_filename)); if (!SimpleModel.loadModelFromFile(simpleModel, input_filename, config.modelRotationMatrix)) { LogOutput.logError("Failed to load model: {0}\n".FormatWith(input_filename)); return(false); } LogOutput.log("Loaded from disk in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds)); return(true); }
void sliceModels(SliceDataStorage storage) { timeKeeper.Restart(); #if False optomizedModel.saveDebugSTL("debug_output.stl"); #endif LogOutput.log("Slicing model...\n"); List <Slicer> slicerList = new List <Slicer>(); for (int volumeIndex = 0; volumeIndex < optomizedModel.volumes.Count; volumeIndex++) { Slicer slicer = new Slicer(optomizedModel.volumes[volumeIndex], config); slicerList.Add(slicer); } #if false slicerList[0].DumpSegmentsToGcode("Volume 0 Segments.gcode"); slicerList[0].DumpPolygonsToGcode("Volume 0 Polygons.gcode"); slicerList[0].DumpPolygonsToHTML("Volume 0 Polygons.html"); #endif LogOutput.log("Sliced model in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Restart(); LogOutput.log("Generating support map...\n"); storage.support.GenerateSupportGrid(optomizedModel, config); storage.modelSize = optomizedModel.size_um; storage.modelMin = optomizedModel.minXYZ_um; storage.modelMax = optomizedModel.maxXYZ_um; LogOutput.log("Generating layer parts...\n"); for (int volumeIndex = 0; volumeIndex < slicerList.Count; volumeIndex++) { storage.volumes.Add(new SliceVolumeStorage()); LayerPart.createLayerParts(storage.volumes[volumeIndex], slicerList[volumeIndex], config.repairOverlaps); if (config.enableRaft) { //Add the raft offset to each layer. for (int layerNr = 0; layerNr < storage.volumes[volumeIndex].layers.Count; layerNr++) { storage.volumes[volumeIndex].layers[layerNr].printZ += config.raftBaseThickness_um + config.raftInterfaceThicknes_um; } } } LogOutput.log("Generated layer parts in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Restart(); }
public void tellFileSize() { double fsize = gcodeFileStream.BaseStream.Length; if (fsize > 1024 * 1024) { fsize /= 1024.0 * 1024.0; LogOutput.log("Wrote {0:0.0} MB.\n".FormatWith(fsize)); } if (fsize > 1024) { fsize /= 1024.0; LogOutput.log("Wrote {0:0.0} kilobytes.\n".FormatWith(fsize)); } }
public void DoProcessing() { if (!gcode.isOpened()) { return; } timeKeeper.Restart(); LogOutput.log("Analyzing and optimizing model...\n"); optomizedModel = new OptimizedModel(simpleModel); if (MatterSlice.Canceled) { return; } optomizedModel.SetPositionAndSize(simpleModel, config.positionToPlaceObjectCenter_um.X, config.positionToPlaceObjectCenter_um.Y, -config.bottomClipAmount_um, config.centerObjectInXy); for (int volumeIndex = 0; volumeIndex < simpleModel.volumes.Count; volumeIndex++) { LogOutput.log(" Face counts: {0} . {1} {2:0.0}%\n".FormatWith((int)simpleModel.volumes[volumeIndex].faceTriangles.Count, (int)optomizedModel.volumes[volumeIndex].facesTriangle.Count, (double)(optomizedModel.volumes[volumeIndex].facesTriangle.Count) / (double)(simpleModel.volumes[volumeIndex].faceTriangles.Count) * 100)); LogOutput.log(" Vertex counts: {0} . {1} {2:0.0}%\n".FormatWith((int)simpleModel.volumes[volumeIndex].faceTriangles.Count * 3, (int)optomizedModel.volumes[volumeIndex].vertices.Count, (double)(optomizedModel.volumes[volumeIndex].vertices.Count) / (double)(simpleModel.volumes[volumeIndex].faceTriangles.Count * 3) * 100)); } LogOutput.log("Optimize model {0:0.0}s \n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Reset(); Stopwatch timeKeeperTotal = new Stopwatch(); timeKeeperTotal.Start(); preSetup(config.extrusionWidth_um); sliceModels(storage); processSliceData(storage); if (MatterSlice.Canceled) { return; } writeGCode(storage); if (MatterSlice.Canceled) { return; } LogOutput.logProgress("process", 1, 1); //Report to the GUI that a file has been fully processed. LogOutput.log("Total time elapsed {0:0.00}s.\n".FormatWith(timeKeeperTotal.Elapsed.Seconds)); }
public static int ProcessArgs(string[] args) { #if DEBUG Tests.BridgeTests.Run(); #endif ConfigSettings config = new ConfigSettings(); fffProcessor processor = new fffProcessor(config); LogOutput.log("\nMatterSlice version {0}\n\n".FormatWith(ConfigConstants.VERSION)); for (int argn = 0; argn < args.Length; argn++) { string str = args[argn]; if (str[0] == '-') { for (int stringIndex = 1; stringIndex < str.Length; stringIndex++) { switch (str[stringIndex]) { case 'h': print_usage(); return(0); case 'v': LogOutput.verbose_level++; break; case 'o': argn++; if (!processor.setTargetFile(args[argn])) { LogOutput.logError("Failed to open {0} for output.\n".FormatWith(args[argn])); return(1); } break; case 'c': { // Read a config file from the given path argn++; if (!config.ReadSettings(args[argn])) { LogOutput.logError("Failed to read config '{0}'\n".FormatWith(args[argn])); } } break; case 'd': config.DumpSettings("settings.ini"); break; case 's': { argn++; int equalsPos = args[argn].IndexOf('='); if (equalsPos != -1) { string key = args[argn].Substring(0, equalsPos); string value = args[argn].Substring(equalsPos + 1); if (key.Length > 1) { if (!config.SetSetting(key, value)) { LogOutput.logError("Setting not found: {0} {1}\n".FormatWith(key, value)); } } } } break; case 'm': argn++; throw new NotImplementedException("m"); #if false sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", &config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2], &config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2], &config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]); #endif break; default: throw new NotImplementedException("Unknown option: {0}\n".FormatWith(str)); LogOutput.logError("Unknown option: {0}\n".FormatWith(str)); break; } } } else { processor.processFile(args[argn]); } } processor.finalize(); return(0); }
public Slicer(OptimizedVolume ov, int initialLayerThickness, int layerThickness, ConfigConstants.REPAIR_OUTLINES outlineRepairTypes) { modelSize = ov.model.size; modelMin = ov.model.minXYZ; int heightWithoutFirstLayer = modelSize.z - initialLayerThickness; int countOfNormalThicknessLayers = heightWithoutFirstLayer / layerThickness; int layerCount = countOfNormalThicknessLayers + 1; // we have to add in the first layer (that is a differnt size) LogOutput.log(string.Format("Layer count: {0}\n", layerCount)); layers.Capacity = layerCount; for (int i = 0; i < layerCount; i++) { layers.Add(new SlicerLayer()); } for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { if (layerIndex == 0) { layers[layerIndex].z = initialLayerThickness / 2; } else { layers[layerIndex].z = initialLayerThickness + layerThickness / 2 + layerThickness * (layerIndex - 1); } } for (int faceIndex = 0; faceIndex < ov.facesTriangle.Count; faceIndex++) { Point3 p0 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[0]].position; Point3 p1 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[1]].position; Point3 p2 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[2]].position; int minZ = p0.z; int maxZ = p0.z; if (p1.z < minZ) { minZ = p1.z; } if (p2.z < minZ) { minZ = p2.z; } if (p1.z > maxZ) { maxZ = p1.z; } if (p2.z > maxZ) { maxZ = p2.z; } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { int z = layers[layerIndex].z; if (z < minZ || layerIndex < 0) { continue; } SlicerSegment polyCrossingAtThisZ; if (p0.z < z && p1.z >= z && p2.z >= z) { // p1 p2 // -------- // p0 polyCrossingAtThisZ = GetCrossingAtZ(p0, p2, p1, z); } else if (p0.z >= z && p1.z < z && p2.z < z) { // p0 // -------- // p1 p2 polyCrossingAtThisZ = GetCrossingAtZ(p0, p1, p2, z); } else if (p1.z < z && p0.z >= z && p2.z >= z) { // p0 p2 // -------- // p1 polyCrossingAtThisZ = GetCrossingAtZ(p1, p0, p2, z); } else if (p1.z >= z && p0.z < z && p2.z < z) { // p1 // -------- // p0 p2 polyCrossingAtThisZ = GetCrossingAtZ(p1, p2, p0, z); } else if (p2.z < z && p1.z >= z && p0.z >= z) { // p1 p0 // -------- // p2 polyCrossingAtThisZ = GetCrossingAtZ(p2, p1, p0, z); } else if (p2.z >= z && p1.z < z && p0.z < z) { // p2 // -------- // p1 p0 polyCrossingAtThisZ = GetCrossingAtZ(p2, p0, p1, z); } else { //Not all cases create a segment, because a point of a face could create just a dot, and two touching faces // on the slice would create two segments continue; } layers[layerIndex].faceTo2DSegmentIndex[faceIndex] = layers[layerIndex].segmentList.Count; polyCrossingAtThisZ.faceIndex = faceIndex; polyCrossingAtThisZ.hasBeenAddedToPolygon = false; layers[layerIndex].segmentList.Add(polyCrossingAtThisZ); } } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { layers[layerIndex].MakePolygons(ov, outlineRepairTypes); } }
void writeGCode(SliceDataStorage storage) { gcode.writeComment("filamentDiameter = {0}".FormatWith(config.filamentDiameter)); gcode.writeComment("extrusionWidth = {0}".FormatWith(config.extrusionWidth)); gcode.writeComment("firstLayerExtrusionWidth = {0}".FormatWith(config.firstLayerExtrusionWidth)); gcode.writeComment("layerThickness = {0}".FormatWith(config.layerThickness)); gcode.writeComment("firstLayerThickness = {0}".FormatWith(config.firstLayerThickness)); if (fileNr == 1) { if (gcode.GetOutputType() == ConfigConstants.OUTPUT_TYPE.ULTIGCODE) { gcode.writeComment("TYPE:UltiGCode"); gcode.writeComment("TIME:<__TIME__>"); gcode.writeComment("MATERIAL:<FILAMENT>"); gcode.writeComment("MATERIAL2:<FILAMEN2>"); } gcode.writeCode(config.startCode); if (gcode.GetOutputType() == ConfigConstants.OUTPUT_TYPE.BFB) { gcode.writeComment("enable auto-retraction"); gcode.writeLine("M227 S{0} P{1}".FormatWith(config.retractionOnTravel * 2560, config.retractionOnTravel * 2560)); } } else { gcode.writeFanCommand(0); gcode.resetExtrusionValue(); gcode.writeRetraction(); gcode.setZ(maxObjectHeight + 5000); gcode.writeMove(gcode.getPositionXY(), config.travelSpeed, 0); gcode.writeMove(new IntPoint(storage.modelMin.x, storage.modelMin.y), config.travelSpeed, 0); } fileNr++; int totalLayers = storage.volumes[0].layers.Count; // let's remove any of the layers on top that are empty { for (int layerIndex = totalLayers - 1; layerIndex >= 0; layerIndex--) { bool layerHasData = false; foreach (SliceVolumeStorage currentVolume in storage.volumes) { SliceLayer currentLayer = currentVolume.layers[layerIndex]; for (int partIndex = 0; partIndex < currentVolume.layers[layerIndex].parts.Count; partIndex++) { SliceLayerPart currentPart = currentLayer.parts[partIndex]; if (currentPart.outline.Count > 0) { layerHasData = true; break; } } } if (layerHasData) { break; } totalLayers--; } } gcode.writeComment("Layer count: {0}".FormatWith(totalLayers)); // keep the raft generation code inside of raft Raft.GenerateRaftGCodeIfRequired(storage, config, gcode); int volumeIdx = 0; for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++) { if (MatterSlice.Canceled) { return; } LogOutput.log("Writing Layers {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers)); LogOutput.logProgress("export", layerIndex + 1, totalLayers); int extrusionWidth_um = config.extrusionWidth_um; if (layerIndex == 0) { extrusionWidth_um = config.firstLayerExtrusionWidth_um; } if (layerIndex == 0) { skirtConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "SKIRT"); inset0Config.setData(config.firstLayerSpeed, extrusionWidth_um, "WALL-OUTER"); insetXConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "WALL-INNER"); fillConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "FILL", false); bridgConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "BRIDGE"); supportNormalConfig.setData(config.firstLayerSpeed, config.supportExtrusionWidth_um, "SUPPORT"); supportInterfaceConfig.setData(config.firstLayerSpeed, config.extrusionWidth_um, "SUPPORT-INTERFACE"); } else { skirtConfig.setData(config.insidePerimetersSpeed, extrusionWidth_um, "SKIRT"); inset0Config.setData(config.outsidePerimeterSpeed, extrusionWidth_um, "WALL-OUTER"); insetXConfig.setData(config.insidePerimetersSpeed, extrusionWidth_um, "WALL-INNER"); fillConfig.setData(config.infillSpeed, extrusionWidth_um, "FILL", false); bridgConfig.setData(config.bridgeSpeed, extrusionWidth_um, "BRIDGE"); supportNormalConfig.setData(config.supportMaterialSpeed, config.supportExtrusionWidth_um, "SUPPORT"); supportInterfaceConfig.setData(config.supportMaterialSpeed, config.extrusionWidth_um, "SUPPORT-INTERFACE"); } gcode.writeComment("LAYER:{0}".FormatWith(layerIndex)); if (layerIndex == 0) { gcode.setExtrusion(config.firstLayerThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); } else { gcode.setExtrusion(config.layerThickness_um, config.filamentDiameter_um, config.extrusionMultiplier); } GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um); // get the correct height for this layer int z = config.firstLayerThickness_um + layerIndex * config.layerThickness_um; if (config.enableRaft) { z += config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceLayers * config.raftSurfaceThickness_um; if (layerIndex == 0) { // We only raise the first layer of the print up by the air gap. // To give it: // Less press into the raft // More time to cool // more surface area to air while extruding z += config.raftAirGap_um; } } gcode.setZ(z); bool printSupportFirst = (storage.support.generated && config.supportExtruder > 0 && config.supportExtruder == gcodeLayer.getExtruder()); if (printSupportFirst) { AddSupportToGCode(storage, gcodeLayer, layerIndex, config); } int fanSpeedPercent = GetFanSpeed(layerIndex, gcodeLayer); for (int volumeCnt = 0; volumeCnt < storage.volumes.Count; volumeCnt++) { if (volumeCnt > 0) { volumeIdx = (volumeIdx + 1) % storage.volumes.Count; } AddVolumeLayerToGCode(storage, gcodeLayer, volumeIdx, layerIndex, extrusionWidth_um, fanSpeedPercent); } if (!printSupportFirst) { AddSupportToGCode(storage, gcodeLayer, layerIndex, config); } //Finish the layer by applying speed corrections for minimum layer times. gcodeLayer.forceMinimumLayerTime(config.minimumLayerTimeSeconds, config.minimumPrintingSpeed); gcode.writeFanCommand(fanSpeedPercent); int currentLayerThickness_um = config.layerThickness_um; if (layerIndex <= 0) { currentLayerThickness_um = config.firstLayerThickness_um; } gcodeLayer.writeGCode(config.doCoolHeadLift, currentLayerThickness_um); } LogOutput.log("Wrote layers in {0:0.00}s.\n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Restart(); gcode.tellFileSize(); gcode.writeFanCommand(0); //Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position. maxObjectHeight = Math.Max(maxObjectHeight, storage.modelSize.z); }
void processSliceData(SliceDataStorage storage) { MultiVolumes.RemoveVolumesIntersections(storage.volumes); MultiVolumes.OverlapMultipleVolumesSlightly(storage.volumes, config.multiVolumeOverlapPercent); #if False LayerPart.dumpLayerparts(storage, "output.html"); #endif int totalLayers = storage.volumes[0].layers.Count; #if DEBUG for (int volumeIndex = 1; volumeIndex < storage.volumes.Count; volumeIndex++) { if (totalLayers != storage.volumes[volumeIndex].layers.Count) { throw new Exception("All the valumes must have the same number of layers (they just can have empty layers)."); } } #endif for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++) { for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++) { if (MatterSlice.Canceled) { return; } int insetCount = config.numberOfPerimeters; if (config.continuousSpiralOuterPerimeter && (int)(layerIndex) < config.numberOfBottomLayers && layerIndex % 2 == 1) { //Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight. insetCount += 5; } SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex]; int extrusionWidth = config.extrusionWidth_um; if (layerIndex == 0) { extrusionWidth = config.firstLayerExtrusionWidth_um; } Inset.generateInsets(layer, extrusionWidth, insetCount); } LogOutput.log("Creating Insets {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers)); } if (config.wipeShieldDistanceFromShapes_um > 0) { CreateWipeShields(storage, totalLayers); } LogOutput.log("Generated inset in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Restart(); for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++) { if (MatterSlice.Canceled) { return; } //Only generate bottom and top layers and infill for the first X layers when spiralize is choosen. if (!config.continuousSpiralOuterPerimeter || (int)(layerIndex) < config.numberOfBottomLayers) { for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++) { int extrusionWidth = config.extrusionWidth_um; if (layerIndex == 0) { extrusionWidth = config.firstLayerExtrusionWidth_um; } Skin.generateTopAndBottomLayers(layerIndex, storage.volumes[volumeIndex], extrusionWidth, config.numberOfBottomLayers, config.numberOfTopLayers); Skin.generateSparse(layerIndex, storage.volumes[volumeIndex], extrusionWidth, config.numberOfBottomLayers, config.numberOfTopLayers); } } LogOutput.log("Creating Top & Bottom Layers {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers)); } LogOutput.log("Generated top bottom layers in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds)); timeKeeper.Restart(); if (config.wipeTowerSize_um > 0) { Polygon p = new Polygon(); storage.wipeTower.Add(p); p.Add(new IntPoint(storage.modelMin.x - 3000, storage.modelMax.y + 3000)); p.Add(new IntPoint(storage.modelMin.x - 3000, storage.modelMax.y + 3000 + config.wipeTowerSize_um)); p.Add(new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um, storage.modelMax.y + 3000 + config.wipeTowerSize_um)); p.Add(new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um, storage.modelMax.y + 3000)); storage.wipePoint = new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um / 2, storage.modelMax.y + 3000 + config.wipeTowerSize_um / 2); } if (config.enableRaft) { Raft.GenerateRaftOutlines(storage, config.raftExtraDistanceAroundPart_um); Skirt.generateSkirt(storage, config.skirtDistance_um + config.raftBaseLineSpacing_um, config.raftBaseLineSpacing_um, config.numberOfSkirtLoops, config.skirtMinLength_um, config.raftBaseThickness_um); } else { Skirt.generateSkirt(storage, config.skirtDistance_um, config.firstLayerExtrusionWidth_um, config.numberOfSkirtLoops, config.skirtMinLength_um, config.firstLayerThickness_um); } }