//Add a single layer from a single extruder to the GCode
        private void QueueExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner layerGcodePlanner, int extruderIndex, int layerIndex, int extrusionWidth_um, long currentZ_um)
        {
            if(extruderIndex > slicingData.Extruders.Count-1)
            {
                return;
            }

            SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex];

            if(layer.AllOutlines.Count == 0
                && config.WipeShieldDistanceFromObject == 0)
            {
                // don't do anything on this layer
                return;
            }

            if (slicingData.wipeShield.Count > 0 && slicingData.Extruders.Count > 1)
            {
                layerGcodePlanner.SetAlwaysRetract(true);
                layerGcodePlanner.QueuePolygonsByOptimizer(slicingData.wipeShield[layerIndex], skirtConfig);
                layerGcodePlanner.SetAlwaysRetract(!config.AvoidCrossingPerimeters);
            }

            PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(new IntPoint());
            for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++)
            {
                if (config.ContinuousSpiralOuterPerimeter && partIndex > 0)
                {
                    continue;
                }

                islandOrderOptimizer.AddPolygon(layer.Islands[partIndex].InsetToolPaths[0][0]);
            }
            islandOrderOptimizer.Optimize();

            List<Polygons> bottomFillIslandPolygons = new List<Polygons>();

            for (int islandOrderIndex = 0; islandOrderIndex < islandOrderOptimizer.bestIslandOrderIndex.Count; islandOrderIndex++)
            {
                if (config.ContinuousSpiralOuterPerimeter && islandOrderIndex > 0)
                {
                    continue;
                }

                LayerIsland island = layer.Islands[islandOrderOptimizer.bestIslandOrderIndex[islandOrderIndex]];

                if (config.AvoidCrossingPerimeters)
                {
                    layerGcodePlanner.SetOuterPerimetersToAvoidCrossing(island.AvoidCrossingBoundary);
                }
                else
                {
                    layerGcodePlanner.SetAlwaysRetract(true);
                }

                Polygons fillPolygons = new Polygons();
                Polygons topFillPolygons = new Polygons();
                Polygons bridgePolygons = new Polygons();

                Polygons bottomFillPolygons = new Polygons();

                CalculateInfillData(slicingData, extruderIndex, layerIndex, island, bottomFillPolygons, fillPolygons, topFillPolygons, bridgePolygons);
                bottomFillIslandPolygons.Add(bottomFillPolygons);

                // Write the bridge polygons out first so the perimeter will have more to hold to while bridging the gaps.
                // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder.
                if (bridgePolygons.Count > 0)
                {
                    QueuePolygonsConsideringSupport(layerIndex, layerGcodePlanner, bridgePolygons, bridgeConfig, SupportWriteType.UnsupportedAreas);
                }

                if (config.NumberOfPerimeters > 0)
                {
                    if (islandOrderIndex != lastPartIndex)
                    {
                        // force a retract if changing islands
                        if (config.RetractWhenChangingIslands)
                        {
                            layerGcodePlanner.ForceRetract();
                        }
                        lastPartIndex = islandOrderIndex;
                    }

                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        if (layerIndex >= config.NumberOfBottomLayers)
                        {
                            inset0Config.spiralize = true;
                        }
                    }

                    // Figure out where the seam hiding start point is for inset 0 and move to that spot so
                    // we have the minimum travel while starting inset 0 after printing the rest of the insets
                    if (island?.InsetToolPaths?[0]?[0]?.Count > 0
                        && !config.ContinuousSpiralOuterPerimeter)
                    {
                        int bestPoint = PathOrderOptimizer.GetBestIndex(island.InsetToolPaths[0][0], config.ExtrusionWidth_um);
                        layerGcodePlanner.QueueTravel(island.InsetToolPaths[0][0][bestPoint]);
                    }

                    // Put all the insets into a new list so we can keep track of what has been printed.
                    List<Polygons> insetsToPrint = new List<Polygons>(island.InsetToolPaths.Count);
                    for (int insetIndex = 0; insetIndex < island.InsetToolPaths.Count; insetIndex++)
                    {
                        insetsToPrint.Add(new Polygons());
                        for (int polygonIndex = 0; polygonIndex < island.InsetToolPaths[insetIndex].Count; polygonIndex++)
                        {
                            if (island.InsetToolPaths[insetIndex][polygonIndex].Count > 0)
                            {
                                insetsToPrint[insetIndex].Add(island.InsetToolPaths[insetIndex][polygonIndex]);
                            }
                        }
                    }

                    // If we are on the very first layer we start with the outside so that we can stick to the bed better.
                    if (config.OutsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize)
                    {
                        if (inset0Config.spiralize)
                        {
                            if (island.InsetToolPaths.Count > 0)
                            {
                                Polygon outsideSinglePolygon = island.InsetToolPaths[0][0];
                                layerGcodePlanner.QueuePolygonsByOptimizer(new Polygons() { outsideSinglePolygon }, inset0Config);
                            }
                        }
                        else
                        {
                            int insetCount = CountInsetsToPrint(insetsToPrint);
                            while (insetCount > 0)
                            {
                                bool limitDistance = false;
                                if (island.InsetToolPaths.Count > 0)
                                {
                                    QueueClosetsInset(insetsToPrint[0], limitDistance, inset0Config, layerIndex, layerGcodePlanner);
                                }

                                if (island.InsetToolPaths.Count > 1)
                                {
                                    // Move to the closest inset 1 and print it
                                    limitDistance = QueueClosetsInset(insetsToPrint[1], limitDistance, insetXConfig, layerIndex, layerGcodePlanner);
                                    for (int insetIndex = 2; insetIndex < island.InsetToolPaths.Count; insetIndex++)
                                    {
                                        limitDistance = QueueClosetsInset(
                                            insetsToPrint[insetIndex],
                                            limitDistance,
                                            insetIndex == 0 ? inset0Config : insetXConfig,
                                            layerIndex,
                                            layerGcodePlanner);
                                    }
                                }

                                insetCount = CountInsetsToPrint(insetsToPrint);
                            }
                        }
                    }
                    else // This is so we can do overhangs better (the outside can stick a bit to the inside).
                    {
                        int insetCount = CountInsetsToPrint(insetsToPrint);
                        while (insetCount > 0)
                        {
                            bool limitDistance = false;
                            if (island.InsetToolPaths.Count > 0)
                            {
                                // Move to the closest inset 1 and print it
                                for (int insetIndex = island.InsetToolPaths.Count-1; insetIndex >= 0; insetIndex--)
                                {
                                    limitDistance = QueueClosetsInset(
                                        insetsToPrint[insetIndex],
                                        limitDistance,
                                        insetIndex == 0 ? inset0Config : insetXConfig,
                                        layerIndex,
                                        layerGcodePlanner);
                                }
                            }

                            insetCount = CountInsetsToPrint(insetsToPrint);
                        }
                    }
                }

                // TODO: Put all of these segments into a list that can be queued together and still preserver their individual config settings.
                // This will make the total amount of travel while printing infill much less.
                layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, fillConfig);
                QueuePolygonsConsideringSupport(layerIndex, layerGcodePlanner, bottomFillPolygons, bottomFillConfig, SupportWriteType.UnsupportedAreas);
                layerGcodePlanner.QueuePolygonsByOptimizer(topFillPolygons, topFillConfig);

                //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
                if (!config.ContinuousSpiralOuterPerimeter || layerIndex < config.NumberOfBottomLayers)
                {
                    layerGcodePlanner.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
                }
            }

            // Find the thin lines for this layer and add them to the queue
            if (false) // this code is just for test. LBB
            {
                Polygons fillPolygons = new Polygons();
                foreach (var island in layer.Islands)
                {
                    List<Point3> path = new List<Point3>();
                    List<PathAndWidth> thinLines;
                    foreach (var outline in island.IslandOutline.Offset(-extrusionWidth_um * 0))
                    {
                        foreach (var point in outline)
                        {
                            path.Add(new Point3(point, currentZ_um));
                        }
                    }

                    if (layerGcodePlanner.FindThinLines(path, extrusionWidth_um - 2, out thinLines))
                    {
                        foreach (var widthPath in thinLines)
                        {
                            Polygon thinPath = new Polygon();
                            foreach (var point in widthPath.Path)
                            {
                                thinPath.Add(new IntPoint(point.x, point.y));
                            }
                            fillPolygons.Add(thinPath);
                        }
                    }
                }
                layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, fillConfig);
            }

            layerGcodePlanner.SetOuterPerimetersToAvoidCrossing(null);
        }