Beispiel #1
0
        public void writePolygonsByOptimizer(Polygons polygons, GCodePathConfig config)
        {
            PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(lastPosition);

            orderOptimizer.AddPolygons(polygons);

            orderOptimizer.Optimize(config);

            for (int i = 0; i < orderOptimizer.bestPolygonOrderIndex.Count; i++)
            {
                int polygonIndex = orderOptimizer.bestPolygonOrderIndex[i];
                writePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config);
            }
        }
        public bool QueuePolygonByOptimizer(Polygon polygon, PathFinder pathFinder, GCodePathConfig config, int layerIndex)
        {
            PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition);

            orderOptimizer.AddPolygon(polygon);

            orderOptimizer.Optimize(pathFinder, layerIndex, config);

            for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++)
            {
                int polygonIndex = orderOptimizer.bestIslandOrderIndex[i];
                QueuePolygon(polygon, orderOptimizer.startIndexInPolygon[polygonIndex], config);
            }

            return(true);
        }
Beispiel #3
0
        public void writePolygonsByOptimizer(Polygons polygons, GCodePathConfig config)
        {
            PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(lastPosition);

            for (int i = 0; i < polygons.Count; i++)
            {
                orderOptimizer.addPolygon(polygons[i]);
            }

            orderOptimizer.optimize();

            for (int i = 0; i < orderOptimizer.polyOrder.Count; i++)
            {
                int polygonIndex = orderOptimizer.polyOrder[i];
                writePolygon(polygons[polygonIndex], orderOptimizer.polyStart[polygonIndex], config);
            }
        }
Beispiel #4
0
        public void QueuePolygonsByOptimizer(Polygons polygons, GCodePathConfig config)
        {
            if (polygons.Count == 0)
            {
                return;
            }

            PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition);

            orderOptimizer.AddPolygons(polygons);

            orderOptimizer.Optimize(config);

            for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++)
            {
                int polygonIndex = orderOptimizer.bestIslandOrderIndex[i];
                QueuePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config);
            }
        }
Beispiel #5
0
        private void WriteSupportPolygons(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config, Polygons supportPolygons, SupportType interfaceLayer)
        {
            for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
            {
                SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex];
                for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
                {
                    supportPolygons = supportPolygons.CreateDifference(layer.parts[partIndex].TotalOutline.Offset(config.supportXYDistance_um));
                }
            }

            //Contract and expand the support polygons so small sections are removed and the final polygon is smoothed a bit.
            supportPolygons = supportPolygons.Offset(-config.extrusionWidth_um * 1);
            supportPolygons = supportPolygons.Offset(config.extrusionWidth_um * 1);

            List<Polygons> supportIslands = supportPolygons.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd);
            PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(gcode.GetPositionXY());

            for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++)
            {
                islandOrderOptimizer.AddPolygon(supportIslands[islandIndex][0]);
            }
            islandOrderOptimizer.Optimize();

            for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++)
            {
                Polygons island = supportIslands[islandOrderOptimizer.bestPolygonOrderIndex[islandIndex]];
                Polygons supportLines = new Polygons();
                if (config.supportLineSpacing_um > 0)
                {
                    switch (interfaceLayer)
                    {
                        case SupportType.Interface:
                            Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle + 90, config.extrusionWidth_um);
                            break;

                        case SupportType.General:
                            switch (config.supportType)
                            {
                                case ConfigConstants.SUPPORT_TYPE.GRID:
                                    Infill.GenerateGridInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
                                    break;

                                case ConfigConstants.SUPPORT_TYPE.LINES:
                                    Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
                                    break;
                            }
                            break;

                        default:
                            throw new NotImplementedException();
                    }
                }

                if (config.avoidCrossingPerimeters)
                {
                    gcodeLayer.SetOuterPerimetersToAvoidCrossing(island);
                }

                switch (interfaceLayer)
                {
                    case SupportType.Interface:
                        gcodeLayer.WritePolygonsByOptimizer(supportLines, supportInterfaceConfig);
                        break;

                    case SupportType.General:
                        if (config.supportType == ConfigConstants.SUPPORT_TYPE.GRID)
                        {
                            gcodeLayer.WritePolygonsByOptimizer(island, supportNormalConfig);
                        }
                        gcodeLayer.WritePolygonsByOptimizer(supportLines, supportNormalConfig);
                        break;

                    default:
                        throw new NotImplementedException();
                }

                gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
            }
        }
Beispiel #6
0
        //Add a single layer from a single mesh-volume to the GCode
        private void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent)
        {
            int prevExtruder = gcodeLayer.getExtruder();
            bool extruderChanged = gcodeLayer.SetExtruder(volumeIndex);

            SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex];
            if (extruderChanged)
            {
                addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um);
            }

            if (storage.wipeShield.Count > 0 && storage.volumes.Count > 1)
            {
                gcodeLayer.SetAlwaysRetract(true);
                gcodeLayer.WritePolygonsByOptimizer(storage.wipeShield[layerIndex], skirtConfig);
                gcodeLayer.SetAlwaysRetract(!config.avoidCrossingPerimeters);
            }

            PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint());
            for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
            {
                if (config.continuousSpiralOuterPerimeter && partIndex > 0)
                {
                    continue;
                }

                partOrderOptimizer.AddPolygon(layer.parts[partIndex].Insets[0][0]);
            }
            partOrderOptimizer.Optimize();

            for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++)
            {
                if (config.continuousSpiralOuterPerimeter && partIndex > 0)
                {
                    continue;
                }

                SliceLayerPart part = layer.parts[partOrderOptimizer.bestPolygonOrderIndex[partIndex]];

                if (config.avoidCrossingPerimeters)
                {
                    gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery);
                }
                else
                {
                    gcodeLayer.SetAlwaysRetract(true);
                }

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

                CalculateInfillData(storage, volumeIndex, layerIndex, part, ref fillPolygons, ref bridgePolygons);

                // Write the bridge polgons 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)
                {
                    gcode.WriteFanCommand(config.bridgeFanSpeedPercent);
                    gcodeLayer.WritePolygonsByOptimizer(bridgePolygons, bridgConfig);
                    gcode.WriteFanCommand(fanSpeedPercent);
                }

                if (config.numberOfPerimeters > 0)
                {
                    if (partIndex != lastPartIndex)
                    {
                        // force a retract if changing islands
                        gcodeLayer.ForceRetract();
                        lastPartIndex = partIndex;
                    }

                    if (config.continuousSpiralOuterPerimeter)
                    {
                        if (layerIndex >= config.numberOfBottomLayers)
                        {
                            inset0Config.spiralize = true;
                        }
                    }

                    // If we are on the very first layer we start with the outside in so that we can stick to the bed better.
                    if (config.outsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize)
                    {
                        // First the outside (this helps with accuracy)
                        if (part.Insets.Count > 0)
                        {
                            gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config);
                        }

                        if (!inset0Config.spiralize)
                        {
                            for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++)
                            {
                                gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig);
                            }
                        }
                    }
                    else // This is so we can do overhanges better (the outside can stick a bit to the inside).
                    {
                        // Print everything but the first perimeter from the outside in so the little parts have more to stick to.
                        for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++)
                        {
                            gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig);
                        }
                        // then 0
                        if (part.Insets.Count > 0)
                        {
                            gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config);
                        }
                    }
                }

                gcodeLayer.WritePolygonsByOptimizer(fillPolygons, fillConfig);

                //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)
                {
                    gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
                }
            }
            gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
        }
Beispiel #7
0
		private void WriteSupportPolygons(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config, Polygons supportPolygons, SupportType interfaceLayer)
		{
			for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
			{
				SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex];
				for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
				{
					supportPolygons = supportPolygons.CreateDifference(layer.parts[partIndex].TotalOutline.Offset(config.supportXYDistance_um));
				}
			}

			//Contract and expand the support polygons so small sections are removed and the final polygon is smoothed a bit.
			supportPolygons = supportPolygons.Offset(-config.extrusionWidth_um * 1);
			supportPolygons = supportPolygons.Offset(config.extrusionWidth_um * 1);

			List<Polygons> supportIslands = supportPolygons.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd);
			PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(gcode.GetPositionXY());

			for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++)
			{
				islandOrderOptimizer.AddPolygon(supportIslands[islandIndex][0]);
			}
			islandOrderOptimizer.Optimize();

			for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++)
			{
				Polygons island = supportIslands[islandOrderOptimizer.bestPolygonOrderIndex[islandIndex]];
				Polygons supportLines = new Polygons();
				if (config.supportLineSpacing_um > 0)
				{
					switch (interfaceLayer)
					{
						case SupportType.Interface:
							Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle + 90, config.extrusionWidth_um);
							break;

						case SupportType.General:
							switch (config.supportType)
							{
								case ConfigConstants.SUPPORT_TYPE.GRID:
									Infill.GenerateGridInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
									break;

								case ConfigConstants.SUPPORT_TYPE.LINES:
									Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
									break;
							}
							break;

						default:
							throw new NotImplementedException();
					}
				}

				if (config.avoidCrossingPerimeters)
				{
					gcodeLayer.SetOuterPerimetersToAvoidCrossing(island);
				}

				switch (interfaceLayer)
				{
					case SupportType.Interface:
						gcodeLayer.WritePolygonsByOptimizer(supportLines, supportInterfaceConfig);
						break;

					case SupportType.General:
						if (config.supportType == ConfigConstants.SUPPORT_TYPE.GRID)
						{
							gcodeLayer.WritePolygonsByOptimizer(island, supportNormalConfig);
						}
						gcodeLayer.WritePolygonsByOptimizer(supportLines, supportNormalConfig);
						break;

					default:
						throw new NotImplementedException();
				}

				gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
			}
		}
Beispiel #8
0
		//Add a single layer from a single mesh-volume to the GCode
		private void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent)
		{
			int prevExtruder = gcodeLayer.getExtruder();
			bool extruderChanged = gcodeLayer.SetExtruder(volumeIndex);

			SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex];
			if (extruderChanged)
			{
				addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um);
			}

			if (storage.wipeShield.Count > 0 && storage.volumes.Count > 1)
			{
				gcodeLayer.SetAlwaysRetract(true);
				gcodeLayer.WritePolygonsByOptimizer(storage.wipeShield[layerIndex], skirtConfig);
				gcodeLayer.SetAlwaysRetract(!config.avoidCrossingPerimeters);
			}

			PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint());
			for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
			{
				if (config.continuousSpiralOuterPerimeter && partIndex > 0)
				{
					continue;
				}

				partOrderOptimizer.AddPolygon(layer.parts[partIndex].Insets[0][0]);
			}
			partOrderOptimizer.Optimize();

			for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++)
			{
				if (config.continuousSpiralOuterPerimeter && partIndex > 0)
				{
					continue;
				}

				SliceLayerPart part = layer.parts[partOrderOptimizer.bestPolygonOrderIndex[partIndex]];

				if (config.avoidCrossingPerimeters)
				{
					gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery);
				}
				else
				{
					gcodeLayer.SetAlwaysRetract(true);
				}

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

				CalculateInfillData(storage, volumeIndex, layerIndex, part, ref fillPolygons, ref bridgePolygons);

				// Write the bridge polgons 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)
				{
					gcode.WriteFanCommand(config.bridgeFanSpeedPercent);
					gcodeLayer.WritePolygonsByOptimizer(bridgePolygons, bridgConfig);
					gcode.WriteFanCommand(fanSpeedPercent);
				}

				if (config.numberOfPerimeters > 0)
				{
					if (partIndex != lastPartIndex)
					{
						// force a retract if changing islands
						gcodeLayer.ForceRetract();
						lastPartIndex = partIndex;
					}

					if (config.continuousSpiralOuterPerimeter)
					{
						if (layerIndex >= config.numberOfBottomLayers)
						{
							inset0Config.spiralize = true;
						}
					}

					// If we are on the very first layer we start with the outside in so that we can stick to the bed better.
					if (config.outsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize)
					{
						// First the outside (this helps with accuracy)
						if (part.Insets.Count > 0)
						{
							gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config);
						}

						if (!inset0Config.spiralize)
						{
							for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++)
							{
								gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig);
							}
						}
					}
					else // This is so we can do overhanges better (the outside can stick a bit to the inside).
					{
						// Print everything but the first perimeter from the outside in so the little parts have more to stick to.
						for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++)
						{
							gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig);
						}
						// then 0
						if (part.Insets.Count > 0)
						{
							gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config);
						}
					}
				}

				gcodeLayer.WritePolygonsByOptimizer(fillPolygons, fillConfig);

				//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)
				{
					gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
				}
			}
			gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
		}
Beispiel #9
0
		//Add a single layer from a single extruder to the GCode
		private void QueueAirGappedExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent, long currentZ_um)
		{
			if (config.generateSupport
				&& !config.continuousSpiralOuterPerimeter
				&& layerIndex > 0)
			{
				int prevExtruder = gcodeLayer.getExtruder();
				bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex);

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

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

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

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

				for (int inlandIndex = 0; inlandIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; inlandIndex++)
				{
					if (config.continuousSpiralOuterPerimeter && inlandIndex > 0)
					{
						continue;
					}

					LayerIsland part = layer.Islands[partOrderOptimizer.bestPolygonOrderIndex[inlandIndex]];

					if (config.avoidCrossingPerimeters)
					{
						gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery);
					}
					else
					{
						gcodeLayer.SetAlwaysRetract(true);
					}

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

					Polygons bottomFillPolygons = new Polygons();

					CalculateInfillData(slicingData, extruderIndex, layerIndex, part, ref bottomFillPolygons, ref fillPolygons, ref topFillPolygons, ref 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)
					{
						gcode.WriteFanCommand(config.bridgeFanSpeedPercent);
						gcodeLayer.QueuePolygonsByOptimizer(bridgePolygons, bridgConfig);
					}

					if (config.numberOfPerimeters > 0)
					{
						if (inlandIndex != lastPartIndex)
						{
							// force a retract if changing islands
							gcodeLayer.ForceRetract();
							lastPartIndex = inlandIndex;
						}

						if (config.continuousSpiralOuterPerimeter)
						{
							if (layerIndex >= config.numberOfBottomLayers)
							{
								inset0Config.spiralize = true;
							}
						}
					}

					//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)
					{
						gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
					}

					// Print everything but the first perimeter from the outside in so the little parts have more to stick to.
					for (int perimeterIndex = 1; perimeterIndex < part.InsetToolPaths.Count; perimeterIndex++)
					{
						QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.SupportedAreas);
					}
					// then 0
					if (part.InsetToolPaths.Count > 0)
					{
						QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[0], inset0Config, SupportWriteType.SupportedAreas);
					}

					QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillIslandPolygons[inlandIndex], bottomFillConfig, SupportWriteType.SupportedAreas);
				}
			}

			gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
		}
Beispiel #10
0
		//Add a single layer from a single extruder to the GCode
		private void QueueExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent, long currentZ_um)
		{
			int prevExtruder = gcodeLayer.getExtruder();
			bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex);

			SliceLayer layer = slicingData.Extruders[extruderIndex].Layers[layerIndex];
			if (extruderChanged)
			{
				addWipeTower(slicingData, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um);
			}

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

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

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

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

			for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++)
			{
				if (config.continuousSpiralOuterPerimeter && partIndex > 0)
				{
					continue;
				}

				LayerIsland island = layer.Islands[partOrderOptimizer.bestPolygonOrderIndex[partIndex]];

				if (config.avoidCrossingPerimeters)
				{
					gcodeLayer.SetOuterPerimetersToAvoidCrossing(island.AvoidCrossingBoundery);
				}
				else
				{
					gcodeLayer.SetAlwaysRetract(true);
				}

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

				Polygons bottomFillPolygons = new Polygons();

				CalculateInfillData(slicingData, extruderIndex, layerIndex, island, ref bottomFillPolygons, ref fillPolygons, ref topFillPolygons, ref 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)
				{
					gcode.WriteFanCommand(config.bridgeFanSpeedPercent);
					gcodeLayer.QueuePolygonsByOptimizer(bridgePolygons, bridgConfig);
				}

				if (config.numberOfPerimeters > 0)
				{
					if (partIndex != lastPartIndex)
					{
						// force a retract if changing islands
						if (config.retractWhenChangingIslands)
						{
							gcodeLayer.ForceRetract();
						}
						lastPartIndex = partIndex;
					}

					if (config.continuousSpiralOuterPerimeter)
					{
						if (layerIndex >= config.numberOfBottomLayers)
						{
							inset0Config.spiralize = true;
						}
					}

					// 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];
								gcodeLayer.QueuePolygonsByOptimizer(new Polygons() { outsideSinglePolygon }, inset0Config);
							}
						}
						else
						{
							// First the outside (this helps with accuracy)
							if (island.InsetToolPaths.Count > 0)
							{
								QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[0], inset0Config, SupportWriteType.UnsupportedAreas);
							}

							for (int perimeterIndex = 1; perimeterIndex < island.InsetToolPaths.Count; perimeterIndex++)
							{
								QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.UnsupportedAreas);
							}
						}
					}
					else // This is so we can do overhangs better (the outside can stick a bit to the inside).
					{
						// 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)
						{
							int bestPoint = PathOrderOptimizer.GetBestEdgeIndex(island.InsetToolPaths[0][0]);
							gcodeLayer.QueueTravel(island.InsetToolPaths[0][0][bestPoint]);
						}

						// Print everything but the first perimeter from the outside in so the little parts have more to stick to.
						for (int perimeterIndex = 1; perimeterIndex < island.InsetToolPaths.Count; perimeterIndex++)
						{
							QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[perimeterIndex], insetXConfig, SupportWriteType.UnsupportedAreas);
						}
						// then 0
						if (island.InsetToolPaths.Count > 0)
						{
							QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, island.InsetToolPaths[0], inset0Config, SupportWriteType.UnsupportedAreas);
						}
					}
				}

				gcodeLayer.QueuePolygonsByOptimizer(fillPolygons, fillConfig);

				QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillPolygons, bottomFillConfig, SupportWriteType.UnsupportedAreas);

				gcodeLayer.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)
				{
					gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
				}
			}

			gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
		}
Beispiel #11
0
        public void QueuePolygonsByOptimizer(Polygons polygons, GCodePathConfig config)
        {
            PathOrderOptimizer orderOptimizer = new PathOrderOptimizer(LastPosition);
            orderOptimizer.AddPolygons(polygons);

            orderOptimizer.Optimize(config);

            for (int i = 0; i < orderOptimizer.bestIslandOrderIndex.Count; i++)
            {
                int polygonIndex = orderOptimizer.bestIslandOrderIndex[i];
                QueuePolygon(polygons[polygonIndex], orderOptimizer.startIndexInPolygon[polygonIndex], config);
            }
        }
Beispiel #12
0
        //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);
        }
Beispiel #13
0
        //Add a single layer from a single extruder to the GCode
        private void QueueAirGappedExtruderLayerToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int extruderIndex, int layerIndex, int extrusionWidth_um, long currentZ_um)
        {
            if (config.GenerateSupport
                && !config.ContinuousSpiralOuterPerimeter
                && layerIndex > 0)
            {
                int prevExtruder = gcodeLayer.GetExtruder();
                bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex);

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

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

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

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

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

                    LayerIsland part = layer.Islands[partOrderOptimizer.bestIslandOrderIndex[inlandIndex]];

                    if (config.AvoidCrossingPerimeters)
                    {
                        gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundary);
                    }
                    else
                    {
                        gcodeLayer.SetAlwaysRetract(true);
                    }

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

                    Polygons bottomFillPolygons = new Polygons();

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

            #if DEBUG
                    if (bridgePolygons.Count > 0)
                    {
                        new Exception("Unexpected bridge polygons in air gapped region");
                    }
            #endif

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

                            lastPartIndex = inlandIndex;
                        }

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

                    //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)
                    {
                        gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2);
                    }

                    // Print everything but the first perimeter from the outside in so the little parts have more to stick to.
                    for (int insetIndex = 1; insetIndex < part.InsetToolPaths.Count; insetIndex++)
                    {
                        QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[insetIndex], airGappedBottomConfig, SupportWriteType.SupportedAreas);
                    }
                    // then 0
                    if (part.InsetToolPaths.Count > 0)
                    {
                        QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, part.InsetToolPaths[0], airGappedBottomConfig, SupportWriteType.SupportedAreas);
                    }

                    QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bottomFillIslandPolygons[inlandIndex], airGappedBottomConfig, SupportWriteType.SupportedAreas);
                }
            }

            gcodeLayer.SetOuterPerimetersToAvoidCrossing(null);
        }