public void InitializeLayerData(ExtruderData slicer, ConfigSettings config, int extruderIndex, int extruderCount, Polygons extraPathingConsideration) { for (int layerIndex = 0; layerIndex < slicer.layers.Count; layerIndex++) { int start = slicer.layers.Count * extruderIndex; LogOutput.Log("Generating Layer Outlines {0}/{1}\n".FormatWith(start + layerIndex + 1, extruderCount * slicer.layers.Count)); if (config.outputOnlyFirstLayer && layerIndex > 0) { break; } Layers.Add(new SliceLayer()); Layers[layerIndex].LayerZ = slicer.layers[layerIndex].Z; Layers[layerIndex].AllOutlines = slicer.layers[layerIndex].PolygonList; Layers[layerIndex].AllOutlines = Layers[layerIndex].AllOutlines.GetCorrectedWinding(); long avoidInset = config.ExtrusionWidth_um * 3 / 2; var boundary = Layers[layerIndex].AllOutlines.GetBounds(); var extraBoundary = extraPathingConsideration.GetBounds(); boundary.ExpandToInclude(extraBoundary); boundary.Inflate(config.ExtrusionWidth_um * 10); Layers[layerIndex].PathFinder = new Pathfinding.PathFinder(Layers[layerIndex].AllOutlines, avoidInset, boundary); } }
public static int ProcessArgs(string[] args) { if (args.Length == 0) { print_usage(); return(0); } ConfigSettings config = new ConfigSettings(); fffProcessor processor = new fffProcessor(config); LogOutput.Log("\nMatterSlice version {0}\n\n".FormatWith(ConfigConstants.VERSION)); if (ProcessArgs(args, config, processor) == 0) { return(0); } if (!Canceled) { processor.DoProcessing(); } if (!Canceled) { processor.finalize(); } if (Canceled) { processor.Cancel(); } Canceled = true; return(0); }
public static void InitializeLayerPathing(ConfigSettings config, Polygons extraPathingConsideration, List <ExtruderLayers> extruders) { for (int layerIndex = 0; layerIndex < extruders[0].Layers.Count; layerIndex++) { if (MatterSlice.Canceled) { return; } LogOutput.Log("Generating Outlines {0}/{1}\n".FormatWith(layerIndex + 1, extruders[0].Layers.Count)); long avoidInset = config.ExtrusionWidth_um * 3 / 2; var allOutlines = new Polygons(); for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { allOutlines.AddRange(extruders[extruderIndex].Layers[layerIndex].AllOutlines); } var boundary = allOutlines.GetBounds(); var extraBoundary = extraPathingConsideration.GetBounds(); boundary.ExpandToInclude(extraBoundary); boundary.Inflate(config.ExtrusionWidth_um * 10); var pathFinder = new Pathfinding.PathFinder(allOutlines, avoidInset, boundary, config.AvoidCrossingPerimeters); // assign the same pathing to all extruders for this layer for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { extruders[extruderIndex].Layers[layerIndex].PathFinder = pathFinder; } } }
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); } }
static void print_usage() { LogOutput.logError("usage: MatterSlice [-h] [-d] [-v] [-m 3x3matrix] [-c <config file>]\n [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n\n"); LogOutput.logError(" [] enclose optional settings, <> are required.\n\n"); LogOutput.logError(" -h Show this message.\n"); LogOutput.logError(" -d Save the currently loaded settings to settings.ini (usefull to see all settings).\n"); LogOutput.logError(" -v Increment verbose level.\n"); LogOutput.logError(" -m A 3x3 matrix for translating and rotating the layers.\n"); LogOutput.logError(" -c A config file to apply to the current settings.\n Can be applyed multiple times.\n Formated like the default.ini (partial settings are fine).\n"); LogOutput.logError(" -s Specify a setting on the command line.\n Uses the same names and values as default.ini.\n"); LogOutput.logError(" -o Specify the path and filename to save 'output.gcode'.\n"); LogOutput.logError(" model.stl, the file that will be loaded and sliced.\n"); }
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; }
public void Finalize(long maxObjectHeight, int moveSpeed, string endCode) { WriteFanCommand(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)))); }
private 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)); } }
private static void print_usage() { LogOutput.LogError("usage: MatterSlice [-h] [-d] [-v] [-t] [-m 3x3matrix]\n [-b boolean math] [-c <config file>]\n [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n\n"); LogOutput.LogError(" [] enclose optional settings, <> are required.\n\n"); LogOutput.LogError(" -h Show this message.\n"); LogOutput.LogError(" -d Save the currently loaded settings to settings.ini (useful to see\n all settings).\n"); LogOutput.LogError(" -v Increment verbose level.\n"); LogOutput.LogError(" -t Run unit tests.\n"); LogOutput.LogError(" -m A 3x3 matrix for translating and rotating the layers.\n"); LogOutput.LogError(" -b A string describing the boolean math to do on the loaded models.\n (indexA,indexB) - parentheses = union\n {indexA,indexBToRemove} - curly brackets = difference\n [indexA,indexB] - square brackets = intersection\n Example: b (0,[1,{2,3}]) intersect 2+3, remove from 1, union with 0\n"); LogOutput.LogError(" -c A config file to apply to the current settings.\n Can be applied multiple times.\n Formated like the default.ini (partial settings are fine).\n"); LogOutput.LogError(" -s Specify a setting on the command line.\n Uses the same names and values as default.ini.\n"); LogOutput.LogError(" -o Specify the path and filename to save 'output.gcode'.\n"); LogOutput.LogError(" model.stl, the file that will be loaded and sliced.\n"); }
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 OptimizedVolume(SimpleVolume volume, OptimizedModel model) { this.model = model; vertices.Capacity = volume.faceTriangles.Count * 3; facesTriangle.Capacity = volume.faceTriangles.Count; Dictionary <int, List <int> > indexMap = new Dictionary <int, List <int> >(); Stopwatch t = new Stopwatch(); t.Start(); for (int i = 0; i < volume.faceTriangles.Count; i++) { OptimizedFace f = new OptimizedFace(); if ((i % 1000 == 0) && t.Elapsed.Seconds > 2) { LogOutput.logProgress("optimized", i + 1, volume.faceTriangles.Count); } for (int j = 0; j < 3; j++) { Point3 p = volume.faceTriangles[i].v[j]; int hash = (int)(((p.x + MELD_DIST / 2) / MELD_DIST) ^ (((p.y + MELD_DIST / 2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST / 2) / MELD_DIST) << 20)); int idx = 0; bool add = true; if (indexMap.ContainsKey(hash)) { for (int n = 0; n < indexMap[hash].Count; n++) { if ((vertices[indexMap[hash][n]].position - p).testLength(MELD_DIST)) { idx = indexMap[hash][n]; add = false; break; } } } if (add) { if (!indexMap.ContainsKey(hash)) { indexMap.Add(hash, new List <int>()); } indexMap[hash].Add(vertices.Count); idx = vertices.Count; vertices.Add(new OptimizedPoint3(p)); } f.vertexIndex[j] = idx; } if (f.vertexIndex[0] != f.vertexIndex[1] && f.vertexIndex[0] != f.vertexIndex[2] && f.vertexIndex[1] != f.vertexIndex[2]) { //Check if there is a face with the same points bool duplicate = false; for (int _idx0 = 0; _idx0 < vertices[f.vertexIndex[0]].usedByFacesList.Count; _idx0++) { for (int _idx1 = 0; _idx1 < vertices[f.vertexIndex[1]].usedByFacesList.Count; _idx1++) { for (int _idx2 = 0; _idx2 < vertices[f.vertexIndex[2]].usedByFacesList.Count; _idx2++) { if (vertices[f.vertexIndex[0]].usedByFacesList[_idx0] == vertices[f.vertexIndex[1]].usedByFacesList[_idx1] && vertices[f.vertexIndex[0]].usedByFacesList[_idx0] == vertices[f.vertexIndex[2]].usedByFacesList[_idx2]) { duplicate = true; } } } } if (!duplicate) { vertices[f.vertexIndex[0]].usedByFacesList.Add(facesTriangle.Count); vertices[f.vertexIndex[1]].usedByFacesList.Add(facesTriangle.Count); vertices[f.vertexIndex[2]].usedByFacesList.Add(facesTriangle.Count); facesTriangle.Add(f); } } } //fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t)); int openFacesCount = 0; for (int i = 0; i < facesTriangle.Count; i++) { OptimizedFace f = facesTriangle[i]; f.touchingFaces[0] = getFaceIdxWithPoints(f.vertexIndex[0], f.vertexIndex[1], i); f.touchingFaces[1] = getFaceIdxWithPoints(f.vertexIndex[1], f.vertexIndex[2], i); f.touchingFaces[2] = getFaceIdxWithPoints(f.vertexIndex[2], f.vertexIndex[0], i); if (f.touchingFaces[0] == -1) { openFacesCount++; } if (f.touchingFaces[1] == -1) { openFacesCount++; } if (f.touchingFaces[2] == -1) { openFacesCount++; } } //fprintf(stdout, " Number of open faces: %i\n", openFacesCount); }
private 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 (fileNumber == 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); } fileNumber++; 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.TotalOutline.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 volumeIndex = 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); // We only create the skirt if we are on layer 0 and the first volume and there is no raft. if (layerIndex == 0 && volumeIndex == 0 && !Raft.ShouldGenerateRaft(config)) { AddSkirtToGCode(storage, gcodeLayer, volumeIndex, layerIndex); } 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) { volumeIndex = (volumeIndex + 1) % storage.volumes.Count; } AddVolumeLayerToGCode(storage, gcodeLayer, volumeIndex, 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); }
private void processSliceData(SliceDataStorage storage) { if (config.continuousSpiralOuterPerimeter) { config.numberOfTopLayers = 0; config.infillPercent = 0; } 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; } TopsAndBottoms.GenerateTopAndBottom(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, config); Skirt.generateSkirt(storage, config.skirtDistance_um + config.raftBaseLineSpacing_um, config.raftBaseLineSpacing_um, config.numberOfSkirtLoops, config.skirtMinLength_um, config.raftBaseThickness_um, config); } else { Skirt.generateSkirt(storage, config.skirtDistance_um, config.firstLayerExtrusionWidth_um, config.numberOfSkirtLoops, config.skirtMinLength_um, config.firstLayerThickness_um, config); } }
public Slicer(OptimizedVolume ov, ConfigSettings config) { int initialLayerThickness_um = config.firstLayerThickness_um; int layerThickness_um = config.layerThickness_um; ConfigConstants.REPAIR_OUTLINES outlineRepairTypes = config.repairOutlines; modelSize = ov.parentModel.size_um; modelMin = ov.parentModel.minXYZ_um; int heightWithoutFirstLayer = modelSize.z - initialLayerThickness_um - config.bottomClipAmount_um; int countOfNormalThicknessLayers = Math.Max(0, (int)((heightWithoutFirstLayer / (double)layerThickness_um) + .5)); int layerCount = countOfNormalThicknessLayers; if (initialLayerThickness_um > 0) { // we have to add in the first layer (that is a differnt size) layerCount++; } LogOutput.Log(string.Format("Layer count: {0}\n", layerCount)); layers.Capacity = layerCount; for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { int z; if (layerIndex == 0) { z = initialLayerThickness_um / 2; } else { z = initialLayerThickness_um + layerThickness_um / 2 + layerThickness_um * (layerIndex - 1); } layers.Add(new SlicerLayer(z)); } 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 || z > maxZ) { 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; } polyCrossingAtThisZ.hasBeenAddedToPolygon = false; layers[layerIndex].SegmentList.Add(polyCrossingAtThisZ); } } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { layers[layerIndex].MakePolygons(outlineRepairTypes); } }
public ExtruderData(OptimizedMesh ov, ConfigSettings config) { int initialLayerThickness_um = config.FirstLayerThickness_um; int layerThickness_um = config.LayerThickness_um; modelSize = ov.containingCollection.size_um; modelMin = ov.containingCollection.minXYZ_um; long heightWithoutFirstLayer = modelSize.Z - initialLayerThickness_um + Math.Max(0, modelMin.Z); int countOfNormalThicknessLayers = Math.Max(0, (int)((heightWithoutFirstLayer / (double)layerThickness_um) + .5)); int layerCount = countOfNormalThicknessLayers; if (initialLayerThickness_um > 0) { // we have to add in the first layer (that is a different size) layerCount++; } if (config.outputOnlyFirstLayer) { layerCount = 1; } LogOutput.Log(string.Format("Layer count: {0}\n", layerCount)); layers.Capacity = layerCount; for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { long z; if (layerIndex == 0) { z = initialLayerThickness_um / 2; } else { z = initialLayerThickness_um + layerThickness_um / 2 + layerThickness_um * (layerIndex - 1); } layers.Add(new MeshProcessingLayer(z)); } for (int faceIndex = 0; faceIndex < ov.facesTriangle.Count; faceIndex++) { if (MatterSlice.Canceled) { return; } IntPoint p0 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[0]].position; IntPoint p1 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[1]].position; IntPoint p2 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[2]].position; long minZ = p0.Z; long 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++) { long z = layers[layerIndex].Z; if (z < minZ || z > maxZ) { continue; } SlicePerimeterSegment 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; } polyCrossingAtThisZ.hasBeenAddedToPolygon = false; layers[layerIndex].SegmentList.Add(polyCrossingAtThisZ); } } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { LogOutput.Log($"Slicing model {layerIndex + 1}/{layers.Count}\n"); layers[layerIndex].MakePolygons(); } }
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); } }
public OptimizedMesh(SimpleMesh simpleMesh, OptimizedMeshCollection containingCollection) { this.containingCollection = containingCollection; vertices.Capacity = simpleMesh.faceTriangles.Count * 3; facesTriangle.Capacity = simpleMesh.faceTriangles.Count; Dictionary <int, List <int> > indexMap = new Dictionary <int, List <int> >(); Stopwatch t = new Stopwatch(); t.Start(); for (int faceIndex = 0; faceIndex < simpleMesh.faceTriangles.Count; faceIndex++) { if (MatterSlice.Canceled) { return; } OptimizedFace optimizedFace = new OptimizedFace(); if ((faceIndex % 1000 == 0) && t.Elapsed.TotalSeconds > 2) { LogOutput.logProgress("optimized", faceIndex + 1, simpleMesh.faceTriangles.Count); } for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { IntPoint p = simpleMesh.faceTriangles[faceIndex].vertices[vertexIndex]; int hash = (int)(((p.X + MELD_DIST / 2) / MELD_DIST) ^ (((p.Y + MELD_DIST / 2) / MELD_DIST) << 10) ^ (((p.Z + MELD_DIST / 2) / MELD_DIST) << 20)); int idx = 0; bool add = true; if (indexMap.ContainsKey(hash)) { for (int n = 0; n < indexMap[hash].Count; n++) { if ((vertices[indexMap[hash][n]].position - p).Length() < MELD_DIST) { idx = indexMap[hash][n]; add = false; break; } } } if (add) { if (!indexMap.ContainsKey(hash)) { indexMap.Add(hash, new List <int>()); } indexMap[hash].Add(vertices.Count); idx = vertices.Count; vertices.Add(new OptimizedPoint3(p)); } optimizedFace.vertexIndex[vertexIndex] = idx; } if (optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[1] && optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[2] && optimizedFace.vertexIndex[1] != optimizedFace.vertexIndex[2]) { //Check if there is a face with the same points bool duplicate = false; for (int _idx0 = 0; _idx0 < vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Count; _idx0++) { for (int _idx1 = 0; _idx1 < vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Count; _idx1++) { for (int _idx2 = 0; _idx2 < vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Count; _idx2++) { if (vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[1]].usedByFacesList[_idx1] && vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[2]].usedByFacesList[_idx2]) { duplicate = true; } } } } if (!duplicate) { vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Add(facesTriangle.Count); facesTriangle.Add(optimizedFace); } } } //fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t)); int openFacesCount = 0; for (int faceIndex = 0; faceIndex < facesTriangle.Count; faceIndex++) { OptimizedFace optimizedFace = facesTriangle[faceIndex]; optimizedFace.touchingFaces[0] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[0], optimizedFace.vertexIndex[1], faceIndex); optimizedFace.touchingFaces[1] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[1], optimizedFace.vertexIndex[2], faceIndex); optimizedFace.touchingFaces[2] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[2], optimizedFace.vertexIndex[0], faceIndex); if (optimizedFace.touchingFaces[0] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[1] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[2] == -1) { openFacesCount++; } } //fprintf(stdout, " Number of open faces: %i\n", openFacesCount); }
public static int ProcessArgs(string[] args, ConfigSettings config, FffProcessor processor) { 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])); } // process any matrix and mesh requested by config file List <string> commands = new List <string>(); foreach (string command in SplitCommandLine.DoSplit(config.AdditionalArgsToProcess)) { commands.Add(command); } string[] subArgs = commands.ToArray(); ProcessArgs(subArgs, config, processor); } 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++; string[] matrixValues = args[argn].Split(','); var loadedMatrix = Matrix4X4.Identity; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { string valueString = matrixValues[i * 4 + j]; double value; if (double.TryParse(valueString, out value)) { loadedMatrix[i, j] = value; } } } config.ModelMatrix = loadedMatrix; break; default: throw new NotImplementedException("Unknown option: {0}\n".FormatWith(str)); // LogOutput.logError("Unknown option: {0}\n".FormatWith(str)); // break; } } } else { using (new QuickTimer2("LoadStlFile")) { processor.LoadStlFile(args[argn]); } } } return(1); }