Exemple #1
0
        public static void GenerateInsets(SliceLayerPart part, int offset, int insetCount)
        {
            part.AvoidCrossingBoundery = part.TotalOutline.Offset(-offset);
            if (insetCount == 0)
            {
                // if we have no insets defined still create one
                part.Insets.Add(part.TotalOutline);
            }
            else // generate the insets
            {
                for (int i = 0; i < insetCount; i++)
                {
                    part.Insets.Add(new Polygons());
                    part.Insets[i] = part.TotalOutline.Offset(-offset * i - offset / 2);

                    double minimumDistanceToCreateNewPosition = 10;
                    part.Insets[i] = Clipper.CleanPolygons(part.Insets[i], minimumDistanceToCreateNewPosition);

                    if (part.Insets[i].Count < 1)
                    {
                        part.Insets.RemoveAt(part.Insets.Count - 1);
                        break;
                    }
                }
            }
        }
Exemple #2
0
        public static void dumpLayerparts(SliceDataStorage storage, string filename)
        {
            StreamWriter streamToWriteTo = new StreamWriter(filename);

            streamToWriteTo.Write("<!DOCTYPE html><html><body>");
            Point3 modelSize = storage.modelSize;
            Point3 modelMin  = storage.modelMin;

            for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
            {
                for (int layerNr = 0; layerNr < storage.volumes[volumeIdx].layers.Count; layerNr++)
                {
                    streamToWriteTo.Write("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
                    SliceLayer layer = storage.volumes[volumeIdx].layers[layerNr];
                    for (int i = 0; i < layer.parts.Count; i++)
                    {
                        SliceLayerPart part = layer.parts[i];
                        for (int j = 0; j < part.outline.Count; j++)
                        {
                            streamToWriteTo.Write("<polygon points=\"");
                            for (int k = 0; k < part.outline[j].Count; k++)
                            {
                                streamToWriteTo.Write("{0},{1} ".FormatWith((float)(part.outline[j][k].X - modelMin.x) / modelSize.x * 500, (float)(part.outline[j][k].Y - modelMin.y) / modelSize.y * 500));
                            }
                            if (j == 0)
                            {
                                streamToWriteTo.Write("\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
                            }
                            else
                            {
                                streamToWriteTo.Write("\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
                            }
                        }
                    }
                    streamToWriteTo.Write("</svg>\n");
                }
            }
            streamToWriteTo.Write("</body></html>");
            streamToWriteTo.Close();
        }
Exemple #3
0
        private static Polygons AddAllOutlines(SliceLayer layerToAdd, SliceLayerPart partToUseAsBounds, Polygons polysToAddTo, ref bool makeInfillSolid, ConfigSettings config)
        {
            Polygons polysToIntersect = new Polygons();

            for (int partIndex = 0; partIndex < layerToAdd.parts.Count; partIndex++)
            {
                var partToConsider = layerToAdd.parts[partIndex];

                if (config.smallProtrusionProportion > 0)
                {
                    // If the part under consideration intersects the part from the current layer and
                    // the area of that intersection is less than smallProtrusionProportion % of the part under consideration solidify the infill
                    var intersection = partToUseAsBounds.TotalOutline.CreateIntersection(partToConsider.TotalOutline);
                    if (intersection.Count > 0) // They do intersect
                    {
                        if (intersection.TotalArea() < partToConsider.TotalOutline.TotalArea() * config.smallProtrusionProportion)
                        {
                            makeInfillSolid = true;
                            return(polysToAddTo);
                        }
                    }
                }

                if (partToUseAsBounds.BoundingBox.Hit(partToConsider.BoundingBox))
                {
                    polysToIntersect =
                        polysToIntersect.CreateUnion(
                            layerToAdd.parts[partIndex].Insets[partToConsider.Insets.Count - 1]);
                    polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um);
                }
            }

            polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect);

            return(polysToAddTo);
        }
        private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons)
        {
            // generate infill the bottom layers including bridging
            foreach (Polygons outline in part.SolidBottomOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
            {
                if (layerIndex > 0)
                {
                    double bridgeAngle;
                    if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle))
                    {
                        Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
                    }
                }
                else
                {
                    Infill.GenerateLinePaths(outline, ref fillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
                }
            }

            // generate infill for the top layers
            foreach (Polygons outline in part.SolidTopOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
            {
                Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
            }

            // generate infill intermediate layers
            foreach (Polygons outline in part.SolidInfillOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
            {
                if (true) // use the old infill method
                {
                    Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90 * (layerIndex % 2));
                }
                else // use the new concentric infill (not tested enough yet) have to handle some bad casses better
                {
                    double oldInfillPercent = config.infillPercent;
                    config.infillPercent = 100;
                    Infill.GenerateConcentricInfill(config, outline, ref fillPolygons);
                    config.infillPercent = oldInfillPercent;
                }
            }

            double fillAngle = config.infillStartingAngle;

            // generate the infill for this part on this layer
            if (config.infillPercent > 0)
            {
                switch (config.infillType)
                {
                    case ConfigConstants.INFILL_TYPE.LINES:
                        if ((layerIndex & 1) == 1)
                        {
                            fillAngle += 90;
                        }
                        Infill.GenerateLineInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
                        break;

                    case ConfigConstants.INFILL_TYPE.GRID:
                        Infill.GenerateGridInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
                        break;

                    case ConfigConstants.INFILL_TYPE.TRIANGLES:
                        Infill.GenerateTriangleInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
                        break;

                    case ConfigConstants.INFILL_TYPE.HEXAGON:
                        Infill.GenerateHexagonInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle, layerIndex);
                        break;

                    case ConfigConstants.INFILL_TYPE.CONCENTRIC:
                        Infill.GenerateConcentricInfill(config, part.InfillOutlines, ref fillPolygons);
                        break;

                    default:
                        throw new NotImplementedException();
                }
            }
        }
Exemple #5
0
        public static void generateSparse(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downSkinCount, int upSkinCount)
        {
            SliceLayer layer = storage.layers[layerIndex];

            for (int partNr = 0; partNr < layer.parts.Count; partNr++)
            {
                SliceLayerPart part = layer.parts[partNr];

                Polygons sparse   = part.insets[part.insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons downskin = sparse;
                Polygons upskin   = sparse;

                if ((int)(layerIndex - downSkinCount) >= 0)
                {
                    SliceLayer layer2 = storage.layers[layerIndex - downSkinCount];
                    for (int partNr2 = 0; partNr2 < layer2.parts.Count; partNr2++)
                    {
                        if (part.boundaryBox.hit(layer2.parts[partNr2].boundaryBox))
                        {
                            if (layer2.parts[partNr2].insets.Count > 1)
                            {
                                downskin = downskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 2]);
                            }
                            else
                            {
                                downskin = downskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 1]);
                            }
                        }
                    }
                }
                if ((int)(layerIndex + upSkinCount) < (int)storage.layers.Count)
                {
                    SliceLayer layer2 = storage.layers[layerIndex + upSkinCount];
                    for (int partNr2 = 0; partNr2 < layer2.parts.Count; partNr2++)
                    {
                        if (part.boundaryBox.hit(layer2.parts[partNr2].boundaryBox))
                        {
                            if (layer2.parts[partNr2].insets.Count > 1)
                            {
                                upskin = upskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 2]);
                            }
                            else
                            {
                                upskin = upskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 1]);
                            }
                        }
                    }
                }

                Polygons result = upskin.CreateUnion(downskin);

                double minAreaSize = 3.0;//(2 * M_PI * ((double)(config.extrusionWidth) / 1000.0) * ((double)(config.extrusionWidth) / 1000.0)) * 3;
                for (int i = 0; i < result.Count; i++)
                {
                    double area = Math.Abs(result[i].Area()) / 1000.0 / 1000.0;
                    if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
                    {
                        result.RemoveAt(i);
                        i -= 1;
                    }
                }

                part.sparseOutline = sparse.CreateDifference(result);
            }
        }
        private static Polygons RemoveAdditionalOutlinesForPart(SliceLayer layerToSubtract, SliceLayerPart partToUseAsBounds, Polygons polygonsToSubtractFrom)
        {
            for (int partIndex = 0; partIndex < layerToSubtract.parts.Count; partIndex++)
            {
                if (partToUseAsBounds.BoundingBox.Hit(layerToSubtract.parts[partIndex].BoundingBox))
                {
                    polygonsToSubtractFrom = polygonsToSubtractFrom.CreateDifference(layerToSubtract.parts[partIndex].Insets[layerToSubtract.parts[partIndex].Insets.Count - 1]);

                    polygonsToSubtractFrom = Clipper.CleanPolygons(polygonsToSubtractFrom, cleanDistance_um);
                }
            }

            return polygonsToSubtractFrom;
        }
        private static Polygons AddAllOutlines(SliceLayer layerToAdd, SliceLayerPart partToUseAsBounds, Polygons polysToAddTo, ref bool makeInfillSolid, ConfigSettings config)
        {
            Polygons polysToIntersect = new Polygons();
            for (int partIndex = 0; partIndex < layerToAdd.parts.Count; partIndex++)
            {
                var partToConsider = layerToAdd.parts[partIndex];

                if (config.smallProtrusionProportion > 0)
                {
                    // If the part under consideration intersects the part from the current layer and
                    // the area of that intersection is less than smallProtrusionProportion % of the part under consideration solidify the infill
                    var intersection = partToUseAsBounds.TotalOutline.CreateIntersection(partToConsider.TotalOutline);
                    if (intersection.Count > 0) // They do intersect
                    {
                        if (intersection.TotalArea() < partToConsider.TotalOutline.TotalArea() * config.smallProtrusionProportion)
                        {
                            makeInfillSolid = true;
                            return polysToAddTo;
                        }
                    }
                }

                if (partToUseAsBounds.BoundingBox.Hit(partToConsider.BoundingBox))
                {
                    polysToIntersect =
                        polysToIntersect.CreateUnion(
                            layerToAdd.parts[partIndex].Insets[partToConsider.Insets.Count - 1]);
                    polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um);
                }
            }

            polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect);

            return polysToAddTo;
        }
		private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons)
		{
			// generate infill the bottom layers including bridging
			foreach (Polygons outline in part.SolidBottomOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
			{
				if (layerIndex > 0)
				{
					double bridgeAngle;
					if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle))
					{
						Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
					}
					else
					{
						Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
					}
				}
				else
				{
					Infill.GenerateLinePaths(outline, ref fillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
				}
			}

			// generate infill for the top layers
			foreach (Polygons outline in part.SolidTopOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
			{
				Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
			}

			// generate infill intermediate layers
			foreach (Polygons outline in part.SolidInfillOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
			{
				if (true) // use the old infill method
				{
					Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90 * (layerIndex % 2));
				}
				else // use the new concentric infill (not tested enough yet) have to handle some bad casses better
				{
					double oldInfillPercent = config.infillPercent;
					config.infillPercent = 100;
					Infill.GenerateConcentricInfill(config, outline, ref fillPolygons);
					config.infillPercent = oldInfillPercent;
				}
			}

			double fillAngle = config.infillStartingAngle;

			// generate the infill for this part on this layer
			if (config.infillPercent > 0)
			{
				switch (config.infillType)
				{
					case ConfigConstants.INFILL_TYPE.LINES:
						if ((layerIndex & 1) == 1)
						{
							fillAngle += 90;
						}
						Infill.GenerateLineInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
						break;

					case ConfigConstants.INFILL_TYPE.GRID:
						Infill.GenerateGridInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
						break;

					case ConfigConstants.INFILL_TYPE.TRIANGLES:
						Infill.GenerateTriangleInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle);
						break;

					case ConfigConstants.INFILL_TYPE.HEXAGON:
						Infill.GenerateHexagonInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle, layerIndex);
						break;

					case ConfigConstants.INFILL_TYPE.CONCENTRIC:
						Infill.GenerateConcentricInfill(config, part.InfillOutlines, ref fillPolygons);
						break;

					default:
						throw new NotImplementedException();
				}
			}
		}
		//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);
		}
		private void writeGCode(SliceDataStorage storage)
		{
			gcode.WriteComment("filamentDiameter = {0}".FormatWith(config.filamentDiameter));
			gcode.WriteComment("extrusionWidth = {0}".FormatWith(config.extrusionWidth));
			gcode.WriteComment("firstLayerExtrusionWidth = {0}".FormatWith(config.firstLayerExtrusionWidth));
			gcode.WriteComment("layerThickness = {0}".FormatWith(config.layerThickness));
			gcode.WriteComment("firstLayerThickness = {0}".FormatWith(config.firstLayerThickness));

			if (fileNumber == 1)
			{
				if (gcode.GetOutputType() == ConfigConstants.OUTPUT_TYPE.ULTIGCODE)
				{
					gcode.WriteComment("TYPE:UltiGCode");
					gcode.WriteComment("TIME:<__TIME__>");
					gcode.WriteComment("MATERIAL:<FILAMENT>");
					gcode.WriteComment("MATERIAL2:<FILAMEN2>");
				}
				gcode.WriteCode(config.startCode);
				if (gcode.GetOutputType() == ConfigConstants.OUTPUT_TYPE.BFB)
				{
					gcode.WriteComment("enable auto-retraction");
					gcode.WriteLine("M227 S{0} P{1}".FormatWith(config.retractionOnTravel * 2560, config.retractionOnTravel * 2560));
				}
			}
			else
			{
				gcode.WriteFanCommand(0);
				gcode.ResetExtrusionValue();
				gcode.WriteRetraction();
				gcode.setZ(maxObjectHeight + 5000);
				gcode.WriteMove(gcode.GetPositionXY(), config.travelSpeed, 0);
				gcode.WriteMove(new IntPoint(storage.modelMin.x, storage.modelMin.y), config.travelSpeed, 0);
			}
			fileNumber++;

			int totalLayers = storage.volumes[0].layers.Count;
			// let's remove any of the layers on top that are empty
			{
				for (int layerIndex = totalLayers - 1; layerIndex >= 0; layerIndex--)
				{
					bool layerHasData = false;
					foreach (SliceVolumeStorage currentVolume in storage.volumes)
					{
						SliceLayer currentLayer = currentVolume.layers[layerIndex];
						for (int partIndex = 0; partIndex < currentVolume.layers[layerIndex].parts.Count; partIndex++)
						{
							SliceLayerPart currentPart = currentLayer.parts[partIndex];
							if (currentPart.TotalOutline.Count > 0)
							{
								layerHasData = true;
								break;
							}
						}
					}

					if (layerHasData)
					{
						break;
					}
					totalLayers--;
				}
			}
			gcode.WriteComment("Layer count: {0}".FormatWith(totalLayers));

			// keep the raft generation code inside of raft
			Raft.GenerateRaftGCodeIfRequired(storage, config, gcode);

			int volumeIndex = 0;
			for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
			{
				if (MatterSlice.Canceled)
				{
					return;
				}
				LogOutput.Log("Writing Layers {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers));

				LogOutput.logProgress("export", layerIndex + 1, totalLayers);

				int extrusionWidth_um = config.extrusionWidth_um;
				if (layerIndex == 0)
				{
					extrusionWidth_um = config.firstLayerExtrusionWidth_um;
				}

				if (layerIndex == 0)
				{
					skirtConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "SKIRT");
					inset0Config.setData(config.firstLayerSpeed, extrusionWidth_um, "WALL-OUTER");
					insetXConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "WALL-INNER");
					fillConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "FILL", false);
					bridgConfig.setData(config.firstLayerSpeed, extrusionWidth_um, "BRIDGE");

					supportNormalConfig.setData(config.firstLayerSpeed, config.supportExtrusionWidth_um, "SUPPORT");
					supportInterfaceConfig.setData(config.firstLayerSpeed, config.extrusionWidth_um, "SUPPORT-INTERFACE");
				}
				else
				{
					skirtConfig.setData(config.insidePerimetersSpeed, extrusionWidth_um, "SKIRT");
					inset0Config.setData(config.outsidePerimeterSpeed, extrusionWidth_um, "WALL-OUTER");
					insetXConfig.setData(config.insidePerimetersSpeed, extrusionWidth_um, "WALL-INNER");
					fillConfig.setData(config.infillSpeed, extrusionWidth_um, "FILL", false);
					bridgConfig.setData(config.bridgeSpeed, extrusionWidth_um, "BRIDGE");

					supportNormalConfig.setData(config.supportMaterialSpeed, config.supportExtrusionWidth_um, "SUPPORT");
					supportInterfaceConfig.setData(config.supportMaterialSpeed, config.extrusionWidth_um, "SUPPORT-INTERFACE");
				}

				gcode.WriteComment("LAYER:{0}".FormatWith(layerIndex));
				if (layerIndex == 0)
				{
					gcode.SetExtrusion(config.firstLayerThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);
				}
				else
				{
					gcode.SetExtrusion(config.layerThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);
				}

				GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);

				// get the correct height for this layer
				int z = config.firstLayerThickness_um + layerIndex * config.layerThickness_um;
				if (config.enableRaft)
				{
					z += config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceLayers * config.raftSurfaceThickness_um;
					if (layerIndex == 0)
					{
						// We only raise the first layer of the print up by the air gap.
						// To give it:
						//   Less press into the raft
						//   More time to cool
						//   more surface area to air while extruding
						z += config.raftAirGap_um;
					}
				}

				gcode.setZ(z);

				// We only create the skirt if we are on layer 0 and the first volume and there is no raft.
				if (layerIndex == 0 && volumeIndex == 0 && !Raft.ShouldGenerateRaft(config))
				{
					AddSkirtToGCode(storage, gcodeLayer, volumeIndex, layerIndex);
				}

				bool printSupportFirst = (storage.support.generated && config.supportExtruder >= 0 && config.supportExtruder == gcodeLayer.getExtruder());
				if (printSupportFirst)
				{
					AddSupportToGCode(storage, gcodeLayer, layerIndex, config);
				}

				int fanSpeedPercent = GetFanSpeed(layerIndex, gcodeLayer);

				for (int volumeCnt = 0; volumeCnt < storage.volumes.Count; volumeCnt++)
				{
					if (volumeCnt > 0)
					{
						volumeIndex = (volumeIndex + 1) % storage.volumes.Count;
					}

					AddVolumeLayerToGCode(storage, gcodeLayer, volumeIndex, layerIndex, extrusionWidth_um, fanSpeedPercent);
				}

				if (!printSupportFirst)
				{
					AddSupportToGCode(storage, gcodeLayer, layerIndex, config);
				}

				//Finish the layer by applying speed corrections for minimum layer times.
				gcodeLayer.ForceMinimumLayerTime(config.minimumLayerTimeSeconds, config.minimumPrintingSpeed);

				gcode.WriteFanCommand(fanSpeedPercent);

				int currentLayerThickness_um = config.layerThickness_um;
				if (layerIndex <= 0)
				{
					currentLayerThickness_um = config.firstLayerThickness_um;
				}

				gcodeLayer.WriteGCode(config.doCoolHeadLift, currentLayerThickness_um);
			}

			LogOutput.Log("Wrote layers in {0:0.00}s.\n".FormatWith(timeKeeper.Elapsed.Seconds));
			timeKeeper.Restart();
			gcode.TellFileSize();
			gcode.WriteFanCommand(0);

			//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
			maxObjectHeight = Math.Max(maxObjectHeight, storage.modelSize.z);
		}
Exemple #11
0
        public static void GenerateTopAndBottom(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downLayerCount, int upLayerCount)
        {
            SliceLayer layer = storage.layers[layerIndex];

            for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
            {
                SliceLayerPart part            = layer.parts[partIndex];
                Polygons       insetWithOffset = part.Insets[part.Insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons       infillOutlines  = new Polygons(insetWithOffset);

                // calculate the bottom outlines
                if (downLayerCount > 0)
                {
                    Polygons bottomOutlines = new Polygons(insetWithOffset);

                    if (layerIndex - 1 >= 0)
                    {
                        bottomOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex - 1], part, bottomOutlines);
                        RemoveSmallAreas(extrusionWidth, bottomOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidBottomOutlines = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(insetWithOffset);
                    topOutlines = topOutlines.CreateDifference(part.SolidBottomOutlines);
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    if (part.Insets.Count > 1)
                    {
                        // Add thin wall filling by taking the area between the insets.
                        Polygons thinWalls = part.Insets[0].Offset(-extrusionWidth / 2).CreateDifference(part.Insets[1].Offset(extrusionWidth / 2));
                        topOutlines.AddAll(thinWalls);
                    }

                    if (layerIndex + 1 < storage.layers.Count)
                    {
                        topOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex + 1], part, topOutlines);
                        RemoveSmallAreas(extrusionWidth, topOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(topOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidTopOutlines = topOutlines;
                }

                // calculate the solid infill outlines
                if (upLayerCount > 1 || downLayerCount > 1)
                {
                    Polygons solidInfillOutlines = new Polygons(insetWithOffset);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidBottomOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidTopOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

                    int upEnd = layerIndex + upLayerCount + 1;
                    if (upEnd <= storage.layers.Count && layerIndex - downLayerCount >= 0)
                    {
                        Polygons totalPartsToRemove = new Polygons(insetWithOffset);

                        int upStart = layerIndex + 2;

                        for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        int downStart = layerIndex - 1;
                        int downEnd   = layerIndex - downLayerCount;

                        for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
                        RemoveSmallAreas(extrusionWidth, solidInfillOutlines);

                        solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    }

                    part.SolidInfillOutlines = solidInfillOutlines;
                    infillOutlines           = infillOutlines.CreateDifference(solidInfillOutlines);
                }

                RemoveSmallAreas(extrusionWidth, infillOutlines);
                infillOutlines      = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                part.InfillOutlines = infillOutlines;
            }
        }
Exemple #12
0
        private static Polygons RemoveAdditionalOutlinesForPart(SliceLayer layerToSubtract, SliceLayerPart partToUseAsBounds, Polygons polygonsToSubtractFrom)
        {
            for (int partIndex = 0; partIndex < layerToSubtract.parts.Count; partIndex++)
            {
                if (partToUseAsBounds.BoundingBox.Hit(layerToSubtract.parts[partIndex].BoundingBox))
                {
                    polygonsToSubtractFrom = polygonsToSubtractFrom.CreateDifference(layerToSubtract.parts[partIndex].Insets[layerToSubtract.parts[partIndex].Insets.Count - 1]);

                    polygonsToSubtractFrom = Clipper.CleanPolygons(polygonsToSubtractFrom, cleanDistance_um);
                }
            }

            return(polygonsToSubtractFrom);
        }
Exemple #13
0
        public static void GenerateTopAndBottom(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, ConfigSettings config)
        {
            var downLayerCount = config.numberOfBottomLayers;
            var upLayerCount   = config.numberOfTopLayers;

            SliceLayer layer = storage.layers[layerIndex];

            for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
            {
                SliceLayerPart part            = layer.parts[partIndex];
                Polygons       insetWithOffset = part.Insets[part.Insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons       infillOutlines  = new Polygons(insetWithOffset);

                // calculate the bottom outlines
                if (downLayerCount > 0)
                {
                    Polygons bottomOutlines = new Polygons(insetWithOffset);

                    if (layerIndex - 1 >= 0)
                    {
                        bottomOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex - 1], part, bottomOutlines);
                        RemoveSmallAreas(extrusionWidth, bottomOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidBottomOutlines = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(insetWithOffset);
                    topOutlines = topOutlines.CreateDifference(part.SolidBottomOutlines);
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    if (part.Insets.Count > 1)
                    {
                        // Add thin wall filling by taking the area between the insets.
                        Polygons thinWalls = part.Insets[0].Offset(-extrusionWidth / 2).CreateDifference(part.Insets[1].Offset(extrusionWidth / 2));
                        topOutlines.AddAll(thinWalls);
                    }

                    if (layerIndex + 1 < storage.layers.Count)
                    {
                        topOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex + 1], part, topOutlines);
                        RemoveSmallAreas(extrusionWidth, topOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(topOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidTopOutlines = topOutlines;
                }

                // calculate the solid infill outlines
                if (upLayerCount > 1 || downLayerCount > 1)
                {
                    var solidInfillOutlines = new Polygons(insetWithOffset);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidBottomOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidTopOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

                    var upStart   = layerIndex + 2;
                    var upEnd     = layerIndex + upLayerCount + 1;
                    var downStart = layerIndex - 1;
                    var downEnd   = layerIndex - downLayerCount;

                    if (upEnd <= storage.layers.Count && downEnd >= 0)
                    {
                        var makeInfillSolid    = false;
                        var totalPartsToRemove = new Polygons(insetWithOffset);

                        for (var layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove, ref makeInfillSolid, config);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                            if (makeInfillSolid)
                            {
                                break;
                            }
                        }

                        for (var layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove, ref makeInfillSolid, config);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                            if (makeInfillSolid)
                            {
                                break;
                            }
                        }

                        if (!makeInfillSolid)
                        {
                            solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
                            RemoveSmallAreas(extrusionWidth, solidInfillOutlines);
                        }

                        solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    }

                    part.SolidInfillOutlines = solidInfillOutlines;
                    infillOutlines           = infillOutlines.CreateDifference(solidInfillOutlines);

                    Polygons totalInfillOutlines = null;
                    double   totalInfillArea     = 0.0;

                    if (config.infillSolidProportion > 0)
                    {
                        totalInfillOutlines = infillOutlines.CreateUnion(solidInfillOutlines);
                        totalInfillArea     = totalInfillOutlines.TotalArea();
                    }

                    if (config.infillSolidProportion > 0)
                    {
                        var solidInfillArea = solidInfillOutlines.TotalArea();
                        if (solidInfillArea > totalInfillArea * config.infillSolidProportion)
                        {
                            solidInfillOutlines      = solidInfillOutlines.CreateUnion(infillOutlines);
                            infillOutlines           = new Polygons();
                            part.SolidInfillOutlines = solidInfillOutlines;
                        }
                        var solidTopOutlinesArea = part.SolidTopOutlines.TotalArea();
                        if (totalInfillArea < solidTopOutlinesArea * config.infillSolidProportion / 2)
                        {
                            var totalSolidTop = totalInfillOutlines.CreateUnion(part.SolidTopOutlines);
                            part.SolidTopOutlines    = totalSolidTop;
                            part.SolidInfillOutlines = new Polygons();
                            infillOutlines           = part.InfillOutlines = new Polygons();
                        }
                        var solidBottomOutlinesArea = part.SolidBottomOutlines.TotalArea();
                        if (totalInfillArea < solidBottomOutlinesArea * config.infillSolidProportion / 2)
                        {
                            var totalSolidBottom = totalInfillOutlines.CreateUnion(part.SolidBottomOutlines);
                            part.SolidBottomOutlines = totalSolidBottom;
                            part.SolidInfillOutlines = new Polygons();
                            infillOutlines           = part.InfillOutlines = new Polygons();
                        }
                    }
                    if (config.minInfillArea_mm2 > 0)
                    {
                        var infillArea = infillOutlines.TotalArea() / 1e6; // convert from um2 to mm2
                        if (infillArea < config.minInfillArea_mm2)
                        {
                            solidInfillOutlines      = solidInfillOutlines.CreateUnion(infillOutlines);
                            infillOutlines           = new Polygons();
                            part.SolidInfillOutlines = solidInfillOutlines;
                        }
                    }
                }

                RemoveSmallAreas(extrusionWidth, infillOutlines);
                infillOutlines      = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                part.InfillOutlines = infillOutlines;
            }
        }
Exemple #14
0
        private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, int extrusionWidth_um, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons)
        {
            // generate infill for outline including bridging
            foreach (Polygons outline in part.skinOutline.SplitIntoParts())
            {
                double partFillAngle = config.infillStartingAngle;
                if ((layerIndex & 1) == 1)
                {
                    partFillAngle += 90;
                }
                if (layerIndex > 0)
                {
                    double bridgeAngle;
                    if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle))
                    {
                        Infill.GenerateLinePaths(outline, ref bridgePolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle);
                    }
                }
                else
                {
                    Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle);
                }
            }

            double fillAngle = config.infillStartingAngle;

            // generate the infill for this part on this layer
            if (config.infillPercent > 0)
            {
                switch (config.infillType)
                {
                case ConfigConstants.INFILL_TYPE.LINES:
                    if ((layerIndex & 1) == 1)
                    {
                        fillAngle += 90;
                    }
                    Infill.GenerateLineInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle);
                    break;

                case ConfigConstants.INFILL_TYPE.GRID:
                    Infill.GenerateGridInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle);
                    break;

                case ConfigConstants.INFILL_TYPE.TRIANGLES:
                    Infill.GenerateTriangleInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle);
                    break;

                case ConfigConstants.INFILL_TYPE.HEXAGON:
                    Infill.GenerateHexagonInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle, layerIndex);
                    break;

                case ConfigConstants.INFILL_TYPE.CONCENTRIC:
                    Infill.generateConcentricInfill(config, part.sparseOutline, ref fillPolygons, extrusionWidth_um, fillAngle);
                    break;

                default:
                    throw new NotImplementedException();
                }
            }
        }
        private static Polygons AddAllOutlines(SliceLayer layerToAdd, SliceLayerPart partToUseAsBounds, Polygons polysToAddTo)
        {
            Polygons polysToIntersect = new Polygons();
            for (int partIndex = 0; partIndex < layerToAdd.parts.Count; partIndex++)
            {
                if (partToUseAsBounds.BoundingBox.Hit(layerToAdd.parts[partIndex].BoundingBox))
                {
                    polysToIntersect = polysToIntersect.CreateUnion(layerToAdd.parts[partIndex].Insets[layerToAdd.parts[partIndex].Insets.Count - 1]);
                    polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um);
                }
            }

            polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect);

            return polysToAddTo;
        }