Exemple #1
0
        public void CorrectSeamPlacement()
        {
            // coincident points return 0 angle
            {
                IntPoint p1 = new IntPoint(10, 0);
                IntPoint p2 = new IntPoint(0, 0);
                IntPoint p3 = new IntPoint(0, 0);
                Assert.IsTrue(IslandOrderOptimizer.GetTurnAmount(p1, p2, p3) == 0);
            }

            // no turn returns a 0 angle
            {
                IntPoint p1 = new IntPoint(10, 0);
                IntPoint p2 = new IntPoint(0, 0);
                IntPoint p3 = new IntPoint(-10, 0);
                Assert.IsTrue(IslandOrderOptimizer.GetTurnAmount(p1, p2, p3) == 0);
            }

            // 90 turn works
            {
                IntPoint p1 = new IntPoint(0, 0);
                IntPoint p2 = new IntPoint(10, 0);
                IntPoint p3 = new IntPoint(10, 10);
                Assert.AreEqual(IslandOrderOptimizer.GetTurnAmount(p1, p2, p3), Math.PI / 2, .001);

                IntPoint p4 = new IntPoint(0, 10);
                IntPoint p5 = new IntPoint(0, 0);
                IntPoint p6 = new IntPoint(10, 0);
                Assert.AreEqual(IslandOrderOptimizer.GetTurnAmount(p4, p5, p6), Math.PI / 2, .001);
            }

            // -90 turn works
            {
                IntPoint p1 = new IntPoint(0, 0);
                IntPoint p2 = new IntPoint(10, 0);
                IntPoint p3 = new IntPoint(10, -10);
                Assert.AreEqual(IslandOrderOptimizer.GetTurnAmount(p1, p2, p3), -Math.PI / 2, .001);
            }

            // 45 turn works
            {
                IntPoint p1 = new IntPoint(0, 0);
                IntPoint p2 = new IntPoint(10, 0);
                IntPoint p3 = new IntPoint(15, 5);
                Assert.AreEqual(Math.PI / 4, IslandOrderOptimizer.GetTurnAmount(p1, p2, p3), .001);

                IntPoint p4 = new IntPoint(0, 0);
                IntPoint p5 = new IntPoint(-10, 0);
                IntPoint p6 = new IntPoint(-15, -5);
                Assert.AreEqual(Math.PI / 4, IslandOrderOptimizer.GetTurnAmount(p4, p5, p6), .001);
            }

            // -45 turn works
            {
                IntPoint p1 = new IntPoint(0, 0);
                IntPoint p2 = new IntPoint(10, 0);
                IntPoint p3 = new IntPoint(15, -5);
                Assert.AreEqual(-Math.PI / 4, IslandOrderOptimizer.GetTurnAmount(p1, p2, p3), .001);
            }

            // find the right point wound ccw
            {
                // 4________3
                // |       /
                // |      /2
                // |      \
                // |0______\1
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 0), new IntPoint(100, 0), new IntPoint(70, 50), new IntPoint(100, 100), new IntPoint(0, 100)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 2);
            }

            // find the right point wound ccw
            {
                // 3________2
                // |       |
                // |       |
                // |       |
                // |0______|1
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 0), new IntPoint(100, 0), new IntPoint(100, 100), new IntPoint(0, 100)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 3);
            }

            // find the right point wound ccw
            {
                // 1________0
                // |       |
                // |       |
                // |       |
                // |2______|3
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(100, 100), new IntPoint(0, 100), new IntPoint(0, 0), new IntPoint(100, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 1);
            }

            // find the right point wound cw
            {
                // 1________2
                // |       |
                // |       |
                // |       |
                // |0______|3
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 0), new IntPoint(0, 100), new IntPoint(100, 100), new IntPoint(100, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 1);
            }

            // find the right point wound cw
            {
                // 0________1
                // |       |
                // |       |
                // |       |
                // |3______|2
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 100), new IntPoint(100, 100), new IntPoint(100, 0), new IntPoint(0, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 0);
            }

            // find the right point wound ccw
            {
                // 4________3
                // |       /
                // |      /2
                // |      \
                // |0______\1
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 0), new IntPoint(1000, 0), new IntPoint(900, 500), new IntPoint(1000, 1000), new IntPoint(0, 1000)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 2);
            }

            // ccw
            {
                // 2________1
                // |       /
                // |      /0
                // |      \
                // |3______\4
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(90, 50), new IntPoint(100, 100), new IntPoint(0, 100), new IntPoint(0, 0), new IntPoint(100, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 0);
            }

            // ccw
            {
                // 2________1
                //  \      /
                //   \3   /0
                //   /    \
                //  /4_____\5
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(90, 50), new IntPoint(100, 100), new IntPoint(0, 100), new IntPoint(10, 50), new IntPoint(0, 0), new IntPoint(100, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 3);
            }

            // ccw
            {
                // 5________4
                //  \      /
                //   \0   /3
                //   /    \
                //  /1_____\2
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(10, 50), new IntPoint(0, 0), new IntPoint(100, 0), new IntPoint(90, 50), new IntPoint(100, 100), new IntPoint(0, 100),
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 0);
            }

            // find the right point wound cw (inside hole loops)
            {
                // 1________2
                // |       /
                // |      /3
                // |      \
                // |0______\4
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(0, 0), new IntPoint(0, 100), new IntPoint(100, 100), new IntPoint(90, 50), new IntPoint(100, 0)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 2);
            }

            // find the right point wound cw
            {
                // 2________3
                // |       /
                // |      /4
                // |      \
                // |1______\0
                List <IntPoint> testPoints = new List <IntPoint> {
                    new IntPoint(100, 0), new IntPoint(0, 0), new IntPoint(0, 100), new IntPoint(100, 100), new IntPoint(90, 50)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 3);
            }

            // cw
            {
                // 4________5
                //  \      /
                //   \3   /0
                //   /    \
                //  /2_____\1
                List <IntPoint> testPoints = new List <IntPoint>
                {
                    new IntPoint(90, 50), new IntPoint(100, 0), new IntPoint(0, 0), new IntPoint(10, 50), new IntPoint(0, 100), new IntPoint(100, 100)
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 4);
            }

            // cw
            {
                // 1________2
                //  \      /
                //   \0   /3
                //   /    \
                //  /5_____\4
                List <IntPoint> testPoints = new List <IntPoint>
                {
                    new IntPoint(10, 50), new IntPoint(0, 100), new IntPoint(100, 100), new IntPoint(90, 50), new IntPoint(100, 0), new IntPoint(0, 0),
                };
                int bestPoint = IslandOrderOptimizer.GetBestEdgeIndex(testPoints);
                Assert.IsTrue(bestPoint == 1);
            }
        }
		//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];

				IslandOrderOptimizer partOrderOptimizer = new IslandOrderOptimizer(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.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);
						QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bridgePolygons, airGappedBottomConfig, SupportWriteType.SupportedAreas);
					}

					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], 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);
		}
		public void QueuePolygonsByOptimizer(Polygons polygons, GCodePathConfig config)
		{
			IslandOrderOptimizer orderOptimizer = new IslandOrderOptimizer(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);
			}
		}
		//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);
			}

			IslandOrderOptimizer islandOrderOptimizer = new IslandOrderOptimizer(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)
				{
					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);
					QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, bridgePolygons, airGappedBottomConfig, SupportWriteType.UnsupportedAreas);
				}

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

					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 = IslandOrderOptimizer.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);
		}