private void RemoveHeatingOfUnusedTools(HashSet <int> usedTools)
 {
     // Filter out heating for unsused tools
     foreach (GCodeLayer l in layers)
     {
         foreach (GCodeSegment s in l.Segments)
         {
             for (int i = 0; i < s.Lines.Count; i++)
             {
                 GCodeLine line = s.Lines[i];
                 if (line.Content.StartsWith("G10 P", StringComparison.InvariantCulture))
                 {
                     int?toolNo = line.GetIValue('P');
                     if (!usedTools.Contains(toolNo.Value))
                     {
                         // If this tool was never called remove it's heating command
                         s.Lines.RemoveAt(i);
                         // We need to manually decrement i to not miss the next line
                         --i;
                     }
                 }
             }
         }
     }
 }
        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 void AddLine(string line)
        {
            if (Lines.Count == 0 && (last == null || last.Lines.Count == 0))
            {
                throw new ArgumentException($"No sticky parameters available for AddLine call (segment {Name})");
            }

            GCodeLine lastLine = (Lines.Count > 0) ? Lines[Lines.Count - 1] : last.Lines[last.Lines.Count - 1];

            AddLine(new GCodeLine(line, lastLine.Feedrate));
        }
        private double?HandleRescale(double firstLayerHeight, GCodeLine line, double?yParam)
        {
            double scaledY = yParam.Value * (OneeightyOverPI / (settings.RotaryPrinting.InnerRadius + lastPoint.Z - firstLayerHeight));

            if (!line.UpdateFValue('Y', scaledY))
            {
                throw new ProcessorException($"Y could not be updated on {line.Content}");
            }
            yParam = line.GetFValue('Y');

            return(yParam);
        }
        // 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 AddLine(GCodeLine line) => Lines.Add(line);