public (double fixedTime, double variableTime, double totalTime) GetLayerTimes()
        {
            IntPoint lastPosition = gcodeExport.GetPosition();
            double   fixedTime    = 0.0;
            double   variableTime = 0.0;

            foreach (var path in paths)
            {
                for (int pointIndex = 0; pointIndex < path.Polygon.Count; pointIndex++)
                {
                    IntPoint currentPosition = path.Polygon[pointIndex];

                    double thisTime = (lastPosition - currentPosition).LengthMm() / (double)(path.Speed);

                    thisTime = Estimator.GetSecondsForMovement((lastPosition - currentPosition).LengthMm(),
                                                               path.Speed,
                                                               config.MaxAcceleration,
                                                               config.MaxVelocity,
                                                               config.JerkVelocity) * config.PrintTimeEstimateMultiplier;

                    if (PathCanAdjustSpeed(path))
                    {
                        variableTime += thisTime;
                    }
                    else
                    {
                        fixedTime += thisTime;
                    }

                    lastPosition = currentPosition;
                }
            }

            return(fixedTime, variableTime, fixedTime + variableTime);
        }
        private void AnalyzeGCodeLines(CancellationToken cancellationToken, Action <double, string> progressReporter,
                                       Vector4 maxAccelerationMmPerS2,
                                       Vector4 maxVelocityMmPerS,
                                       Vector4 velocitySameAsStopMmPerS,
                                       Vector4 speedMultiplier)
        {
            double  feedRateMmPerMin    = 0;
            Vector3 lastPrinterPosition = new Vector3();
            double  lastEPosition       = 0;

            Stopwatch maxProgressReport = new Stopwatch();

            maxProgressReport.Start();

            for (int lineIndex = 0; lineIndex < GCodeCommandQueue.Count; lineIndex++)
            {
                PrinterMachineInstruction instruction = GCodeCommandQueue[lineIndex];
                string  line = instruction.Line;
                Vector3 deltaPositionThisLine  = new Vector3();
                double  deltaEPositionThisLine = 0;
                string  lineToParse            = line.ToUpper().Trim();
                if (lineToParse.StartsWith("G0") || lineToParse.StartsWith("G1"))
                {
                    double newFeedRateMmPerMin = 0;
                    if (GetFirstNumberAfter("F", lineToParse, ref newFeedRateMmPerMin))
                    {
                        feedRateMmPerMin = newFeedRateMmPerMin;
                    }

                    Vector3 attemptedDestination = lastPrinterPosition;
                    GetFirstNumberAfter("X", lineToParse, ref attemptedDestination.X);
                    GetFirstNumberAfter("Y", lineToParse, ref attemptedDestination.Y);
                    GetFirstNumberAfter("Z", lineToParse, ref attemptedDestination.Z);

                    double ePosition = lastEPosition;
                    GetFirstNumberAfter("E", lineToParse, ref ePosition);

                    deltaPositionThisLine  = attemptedDestination - lastPrinterPosition;
                    deltaEPositionThisLine = Math.Abs(ePosition - lastEPosition);

                    lastPrinterPosition = attemptedDestination;
                    lastEPosition       = ePosition;
                }
                else if (lineToParse.StartsWith("G92"))
                {
                    double ePosition = 0;
                    if (GetFirstNumberAfter("E", lineToParse, ref ePosition))
                    {
                        lastEPosition = ePosition;
                    }
                }

                if (feedRateMmPerMin > 0)
                {
                    var timeForE = Estimator.GetSecondsForMovement(deltaEPositionThisLine,
                                                                   feedRateMmPerMin / 60.0,
                                                                   maxAccelerationMmPerS2[3],
                                                                   maxVelocityMmPerS[3],
                                                                   velocitySameAsStopMmPerS[3],
                                                                   speedMultiplier[3]);

                    var timeForPosition = Estimator.GetSecondsForMovement(deltaPositionThisLine,
                                                                          feedRateMmPerMin / 60.0,
                                                                          new Vector3(maxAccelerationMmPerS2),
                                                                          new Vector3(maxVelocityMmPerS),
                                                                          new Vector3(velocitySameAsStopMmPerS),
                                                                          new Vector3(speedMultiplier));

                    instruction.SecondsThisLine = (float)Math.Max(timeForE, timeForPosition);
                }

                if (progressReporter != null && maxProgressReport.ElapsedMilliseconds > 200)
                {
                    progressReporter(((double)lineIndex / GCodeCommandQueue.Count / 2) + .5, "");
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    maxProgressReport.Restart();
                }
            }

            double accumulatedTime = 0;

            for (int i = GCodeCommandQueue.Count - 1; i >= 0; i--)
            {
                PrinterMachineInstruction line = GCodeCommandQueue[i];
                accumulatedTime          += line.SecondsThisLine;
                line.SecondsToEndFromHere = (float)accumulatedTime;
            }
        }