private void RotaryPrintingFixes() { if (layers.Count == 0 || layers[0].Segments.Count == 0) { return; } GCodeSegment initialization = layers[0].Segments[0]; for (int i = 0; i < initialization.Lines.Count; i++) { GCodeLine line = initialization.Lines[i]; if (line.Content.StartsWith("G28", StringComparison.InvariantCulture)) { initialization.Lines.RemoveAt(i); // We need to manually decrement i to not miss the next line --i; } else if (line.Content.StartsWith("G32", StringComparison.InvariantCulture)) { initialization.Lines.RemoveAt(i); initialization.Lines.Insert(i, new GCodeLine("G29 S1 ; Load height map")); break; } } }
public GCodeSegment(string name, int tool, GCodeSegment lastSegment) { Name = name; Tool = tool; Lines = new List <GCodeLine>(); last = lastSegment; }
private OverrideRule GetRule(int tool, int layer, GCodeSegment segment) { foreach (OverrideRule rule in rules) { if (rule.Matches(tool, layer, segment)) { return(rule); } } return(null); }
private GCodeSegment GetClosestSegment(GCodeLayer layer, double x, double y) { GCodeSegment minSegment = null; double minDistance = 0.0; double lastX = double.NaN, lastY = double.NaN; foreach (GCodeSegment segment in layer.Segments) { foreach (GCodeLine line in segment.Lines) { int?gCode = line.GetIValue('G'); if (gCode == 0 || gCode == 1) { double?xCoord = line.GetFValue('X'); double?yCoord = line.GetFValue('Y'); if (xCoord.HasValue && yCoord.HasValue) { if (double.IsNaN(lastX) || double.IsNaN(lastY)) { lastX = xCoord.Value; lastY = yCoord.Value; } else if (line.GetFValue('E').HasValue) { // Get distance from the extruding G0/G1 line segment ([lastX, lastY] to [xCoord, yCoord]) to [x, y] double distance = Math.Abs((yCoord.Value - lastY) * x - (xCoord.Value - lastX) * y + xCoord.Value * lastY - yCoord.Value * lastX) / Math.Sqrt(Math.Pow(yCoord.Value - lastY, 2) + Math.Pow(xCoord.Value - lastX, 2)); if (minSegment == null || distance < minDistance) { minSegment = segment; minDistance = distance; } } } } } } return(minSegment); }
private Coordinate EnrichSegment(GCodeLayer layer, GCodeSegment segment, List <GCodeLine> replacementLines, int toolNumber, ref int currentTool, ref OverrideRule activeRule, ref double currentZ) { bool primeTool = false; bool ensureUnhopAfterToolChange = false; foreach (GCodeLine line in segment.Lines) { // Get GCode of current line int?gCode = line.GetIValue('G'); // Movement if (gCode == 0 || gCode == 1) { // Keep track of the current Z position double?zPosition = line.GetFValue('Z'); if (zPosition.HasValue) { currentZ = zPosition.Value; // Since we have a Z height in this line we don't have to insert an artificial one ensureUnhopAfterToolChange = false; } // Make sure to un-hop before the first extrusion if required if (!double.IsNaN(layer.ZHeight) && line.GetFValue('E').HasValue&& (currentZ != layer.ZHeight || ensureUnhopAfterToolChange)) { replacementLines.Add(new GCodeLine($"G1 Z{layer.ZHeight.ToString("F3", FrmMain.numberFormat)} F{(line.Feedrate * 60.0).ToString("F0", FrmMain.numberFormat)}")); currentZ = layer.ZHeight; ensureUnhopAfterToolChange = false; } // Prime tool before first extrusion if (primeTool && line.GetFValue('E').HasValue) { replacementLines.Add(new GCodeLine($"G1 E{toolChangeRetractionDistance.ToString("F2", FrmMain.numberFormat)} F{toolChangeRetractionSpeed.ToString(FrmMain.numberFormat)}", toolChangeRetractionSpeed / 60.0)); toolPrimed[currentTool - 1] = true; primeTool = false; } // Add next movement of the segment replacementLines.Add(line); // Insert potential tool changes after first G0/G1 code if (toolNumber != currentTool) { // Reset any speed overrides so tool change is not slowed down if (activeRule != null) { replacementLines.Add(new GCodeLine("M220 S100")); activeRule = null; } AddToolChange(replacementLines, currentTool, toolNumber); currentTool = toolNumber; primeTool = !toolPrimed[currentTool - 1]; // Make sure we go to the height of the current layer after tool change but only before the first extrusion (see above) ensureUnhopAfterToolChange = true; } } // Always add it if is no movement else { replacementLines.Add(line); } // Deal with custom rules OverrideRule rule = GetRule(currentTool, layer.Number, segment); if (rule != activeRule) { ApplyRule(activeRule, replacementLines, rule); activeRule = rule; } } return(segment.LastPosition); }
// Split up the G-code file into different segments holding corresponding G-code lines plus feedrate // Each segment is created when // - the tool number OR // - the print region as indicated by S3D ("; feature..." or "; ...") // changes. // These segments are combined later on in the post-processing step public async Task PreProcess() { StreamReader reader = new StreamReader(input); string lineBuffer = await reader.ReadLineAsync(); if (lineBuffer == null) { throw new ProcessorException("File is empty"); } if (lineBuffer.Contains("G-Code generated by Simplify3D(R)")) { maxProgress.Report((int)input.Length); double feedrate = DefaultFeedrate; double firstLayerHeight = 0; bool rotaryPrinting = settings.RotaryPrinting != null; int lineNumber = 1, numExtrusions = 0; bool isInterfacingSet = true; GCodeLayer layer = new GCodeLayer(0, 0.0), lastLayer = null; GCodeSegment segment = new GCodeSegment("Initialization", -1, null); layer.Segments.Add(segment); HashSet <int> usedTools = new HashSet <int>(); do { bool writeLine = true; GCodeLine line = new GCodeLine(lineBuffer); if (lineBuffer.StartsWith(";", StringComparison.InvariantCulture)) { if (lineBuffer.StartsWith("; layer ", StringComparison.InvariantCulture)) { segment.LastPosition = lastPoint.Clone(); // Add past layer layers.Add(layer); lastLayer = layer; // Get the Z height. S3D provides it via the comment except before the end string lastParameter = lineBuffer.Split(' ').Last(); double zHeight = (lastParameter == "end") ? double.NaN : double.Parse(lastParameter, FrmMain.numberFormat); if (lineBuffer.StartsWith("; layer 1, Z =", StringComparison.InvariantCulture)) { firstLayerHeight = zHeight; } // Create a new one layer = new GCodeLayer(layer.Number + 1, zHeight); segment = new GCodeSegment(lineBuffer, segment.Tool, segment); layer.Segments.Add(segment); isInterfacingSet = layer.Number < 2; } else if ((layer.Number == 0 && lineNumber > 2 && !lineBuffer.Contains("layerHeight")) || lineBuffer.StartsWith("; tool", StringComparison.InvariantCulture) || lineBuffer.StartsWith("; process", StringComparison.InvariantCulture)) { // Keep first two comment lines but get rid of S3D process description and // remove "; tool" as well as "; process" lines because they are completely useless writeLine = false; // Try to get the tool change parameters if (lineBuffer.Contains("toolChangeRetractionDistance")) { double?value = line.GetFValue(',', true); if (value.HasValue) { toolChangeRetractionDistance = value.Value; } } if (lineBuffer.Contains("toolChangeRetractionSpeed")) { double?value = line.GetFValue(',', true); if (value.HasValue) { toolChangeRetractionSpeed = value.Value; } } } else if (layer.Number > 0) { // T-codes are generated just before a new segment starts string region = lineBuffer.Substring(lineBuffer.StartsWith("; feature", StringComparison.InvariantCulture) ? 9 : 1).Trim(); if (segment.Lines.Count == 0) { segment.Name = region; } else { segment = new GCodeSegment(region, segment.Tool, segment); layer.Segments.Add(segment); } } } else { int?gCode = line.GetIValue('G'); if (gCode.HasValue) { // G0 / G1 if (gCode == 0 || gCode == 1) { double?xParam = line.GetFValue('X'); double?yParam = line.GetFValue('Y'); double?zParam = line.GetFValue('Z'); if (xParam.HasValue) { lastPoint.X = xParam.Value; } if (yParam.HasValue) { if (rotaryPrinting) { yParam = HandleRescale(firstLayerHeight, line, yParam); } lastPoint.Y = yParam.Value; } if (zParam.HasValue) { lastPoint.Z = zParam.Value; } if (numExtrusions < 2) { if (line.GetFValue('E').HasValue) { numExtrusions++; writeLine = false; } } double?fParam = line.GetFValue('F'); if (fParam.HasValue) { feedrate = fParam.Value / 60.0; } if (!isInterfacingSet && segment.Tool != -1 && xParam.HasValue && yParam.HasValue) { segment.IsInterfacing = GetClosestSegment(lastLayer, xParam.Value, yParam.Value)?.Tool != segment.Tool; isInterfacingSet = true; } } // G10 else if (gCode == 10) { int? pParam = line.GetIValue('P'); double?sParam = line.GetFValue('S'); if (pParam.HasValue && pParam.Value > 0 && pParam.Value <= settings.Tools.Length && sParam.HasValue) { // G10 P... S... settings.Tools[pParam.Value - 1].ActiveTemperature = (decimal)sParam.Value; } } else if (gCode == 28) { lastPoint = homingPosition.Clone(); } } else { int?mCode = line.GetIValue('M'); if (mCode.HasValue) { // M104 if (mCode == 104) { double?sParam = line.GetFValue('S'); int? tParam = line.GetIValue('T'); if (sParam.HasValue && tParam.HasValue && tParam.Value > 0 && tParam.Value <= settings.Tools.Length) { ToolSettings toolSettings = settings.Tools[tParam.Value - 1]; if (toolSettings.Type == ToolType.Nozzle) { if (toolSettings.ActiveTemperature <= 0m) { toolSettings.ActiveTemperature = (decimal)sParam.Value; segment.AddLine($"G10 P{tParam} R{toolSettings.StandbyTemperature.ToString(FrmMain.numberFormat)} S{toolSettings.ActiveTemperature.ToString(FrmMain.numberFormat)}"); } else { segment.AddLine($"G10 P{tParam} S{sParam.Value.ToString(FrmMain.numberFormat)}"); } } writeLine = false; } } } else { // T-Code int?tCode = line.GetIValue('T'); if (tCode.HasValue) { if (tCode > 0 && tCode <= settings.Tools.Length) { usedTools.Add(tCode.Value); if (settings.Tools[tCode.Value - 1].Type == ToolType.Nozzle) { // Keep track of tools in use. Tool change sequences are inserted by the post-processor if (segment.Lines.Count <= 1) { segment.Tool = tCode.Value; } else { segment = new GCodeSegment(segment.Name, tCode.Value, segment); layer.Segments.Add(segment); } writeLine = false; } else { // Make sure we don't print with inproperly configured tools... throw new ProcessorException($"Tool {tCode} is not configured as a nozzle (see line {lineNumber})"); } } else if (segment.Lines.Count == 0) { segment.Tool = -1; } else { segment = new GCodeSegment(segment.Name, -1, segment); layer.Segments.Add(segment); } } } } } // Add this line unless it was handled before if (writeLine) { line.Feedrate = feedrate; segment.AddLine(line); } lineBuffer = await reader.ReadLineAsync(); lineNumber++; // Report progress to the UI progress.Report((int)input.Position); } while (lineBuffer != null); layers.Add(layer); RemoveHeatingOfUnusedTools(usedTools); if (rotaryPrinting) { RotaryPrintingFixes(); } } else if (lineBuffer.Contains("Diabase")) { throw new ProcessorException("File has been already processed"); } else { throw new ProcessorException("File was not generated by Simplify3D"); } }
private void FixToolChangeRetractionAndPriming(ref int iteration) { // Fix toolchange retraction/priming for (int layerNumber = 1; layerNumber < layers.Count; layerNumber++) { GCodeLayer layer = layers[layerNumber]; for (int segmentNumber = 0; segmentNumber < layer.Segments.Count; segmentNumber++) { GCodeSegment segment = layer.Segments[segmentNumber]; for (int lineIndex = 0; lineIndex < segment.Lines.Count; lineIndex++) { GCodeLine line = segment.Lines[lineIndex]; // Look for toolchange if (!line.Content.EndsWith(ToolChangeMarker, StringComparison.InvariantCulture)) { continue; } // Search for retraction prior to toolchange // It will be located at the end of the previous segment var previousSegment = GetPreviousSegment(layerNumber, segmentNumber); if (previousSegment != null) { var lookbehindLimit = Math.Max(previousSegment.Lines.Count - 5, 0); for (var reverse = previousSegment.Lines.Count - 1; reverse >= lookbehindLimit; reverse--) { if (!previousSegment.Lines[reverse].Content.StartsWith("G1 E-", StringComparison.InvariantCulture)) { continue; } var eVal = previousSegment.Lines[reverse].GetFValue('E'); if (Math.Abs(eVal.Value) != toolChangeRetractionDistance) { previousSegment.Lines.RemoveAt(reverse); previousSegment.Lines.Insert(reverse, new GCodeLine($"G1 E-{toolChangeRetractionDistance.ToString("F2", FrmMain.numberFormat)} F{toolChangeRetractionSpeed.ToString(FrmMain.numberFormat)}", toolChangeRetractionSpeed / 60.0)); } break; } } // Search for priming post toolchange var lookaheadLimit = Math.Min(lineIndex + 5, segment.Lines.Count - 1); for (var forward = lineIndex + 1; forward <= lookaheadLimit; forward++) { if (!segment.Lines[forward].Content.StartsWith("G1 E", StringComparison.InvariantCulture)) { continue; } var eVal = segment.Lines[forward].GetFValue('E'); if (eVal.Value != toolChangeRetractionDistance) { segment.Lines.RemoveAt(forward); segment.Lines.Insert(forward, new GCodeLine($"G1 E{toolChangeRetractionDistance.ToString("F2", FrmMain.numberFormat)} F{toolChangeRetractionSpeed.ToString(FrmMain.numberFormat)}", toolChangeRetractionSpeed / 60.0)); } break; } } } progress.Report(iteration++); } }
public void PostProcess() { // We know how much we need to do here... maxProgress.Report(Math.Max(layers.Count * 4 - 4, 0)); // Combine tool islands per layer, adjust tool change sequences and take care of rules OverrideRule activeRule = null; int iteration = 1; bool startWithLowestTool = true; int currentTool = -1; for (int layerIndex = 1; layerIndex < layers.Count; layerIndex++) { GCodeLayer layer = layers[layerIndex]; GCodeLayer replacementLayer = new GCodeLayer(layerIndex, layer.ZHeight); if (settings.IslandCombining) { if (startWithLowestTool) { for (int toolNumber = 1; toolNumber <= settings.Tools.Length; toolNumber++) { // Go from T1-T5 GCodeSegment segment = CombineSegments(layer, toolNumber, ref currentTool, ref activeRule); if (segment != null) { replacementLayer.Segments.Add(segment); } } } else { for (int toolNumber = settings.Tools.Length; toolNumber >= 1; toolNumber--) { // Go from T5-T1 GCodeSegment segment = CombineSegments(layer, toolNumber, ref currentTool, ref activeRule); if (segment != null) { replacementLayer.Segments.Add(segment); } } } startWithLowestTool = !startWithLowestTool; } else { double currentZ = 0.0; foreach (GCodeSegment segment in layer.Segments) { int toolNumber = segment.Tool; List <GCodeLine> replacementLines = new List <GCodeLine>(); Coordinate lastPosition = EnrichSegment(layer, segment, replacementLines, toolNumber, ref currentTool, ref activeRule, ref currentZ); if (replacementLines.Count > 0) { replacementLayer.Segments.Add( new GCodeSegment($"T{toolNumber}", toolNumber, null) { Lines = replacementLines, LastPosition = lastPosition }); } } } layers[layerIndex] = replacementLayer; progress.Report(iteration++); } // Make sure the last applied rule is reset before the print finishes if (activeRule != null) { GCodeSegment lastSegment = layers.Last((layer) => layer.Segments.Count > 0).Segments.Last(); if (activeRule.SpeedFactor != 100) { lastSegment.Lines.Add(new GCodeLine("M220 S100")); } if (activeRule.ExtrusionFactor != 100) { lastSegment.Lines.Add(new GCodeLine("M221 S100")); } activeRule = null; } FixToolChangeRetractionAndPriming(ref iteration); InsertPreheatingSequences(ref iteration); }