Example #1
0
		private void processSliceData(SliceDataStorage storage)
		{
			if (config.continuousSpiralOuterPerimeter)
			{
				config.numberOfTopLayers = 0;
				config.infillPercent = 0;
			}

			MultiVolumes.RemoveVolumesIntersections(storage.volumes);
			MultiVolumes.OverlapMultipleVolumesSlightly(storage.volumes, config.multiVolumeOverlapPercent);
#if False
            LayerPart.dumpLayerparts(storage, "output.html");
#endif

			int totalLayers = storage.volumes[0].layers.Count;
#if DEBUG
			for (int volumeIndex = 1; volumeIndex < storage.volumes.Count; volumeIndex++)
			{
				if (totalLayers != storage.volumes[volumeIndex].layers.Count)
				{
					throw new Exception("All the valumes must have the same number of layers (they just can have empty layers).");
				}
			}
#endif

			for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
			{
				for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
				{
					if (MatterSlice.Canceled)
					{
						return;
					}
					int insetCount = config.numberOfPerimeters;
					if (config.continuousSpiralOuterPerimeter && (int)(layerIndex) < config.numberOfBottomLayers && layerIndex % 2 == 1)
					{
						//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
						insetCount += 5;
					}

					SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex];
					int extrusionWidth = config.extrusionWidth_um;
					if (layerIndex == 0)
					{
						extrusionWidth = config.firstLayerExtrusionWidth_um;
					}
					Inset.generateInsets(layer, extrusionWidth, insetCount);
				}
				LogOutput.Log("Creating Insets {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers));
			}

			if (config.wipeShieldDistanceFromShapes_um > 0)
			{
				CreateWipeShields(storage, totalLayers);
			}

			LogOutput.Log("Generated inset in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds));
			timeKeeper.Restart();

			for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
			{
				if (MatterSlice.Canceled)
				{
					return;
				}
				
				//Only generate bottom and top layers and infill for the first X layers when spiralize is choosen.
				if (!config.continuousSpiralOuterPerimeter || (int)(layerIndex) < config.numberOfBottomLayers)
				{
					for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
					{
						int extrusionWidth = config.extrusionWidth_um;
						if (layerIndex == 0)
						{
							extrusionWidth = config.firstLayerExtrusionWidth_um;
						}

						TopsAndBottoms.GenerateTopAndBottom(layerIndex, storage.volumes[volumeIndex], extrusionWidth, config.numberOfBottomLayers, config.numberOfTopLayers);
					}
				}
				LogOutput.Log("Creating Top & Bottom Layers {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers));
			}
			LogOutput.Log("Generated top bottom layers in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds));
			timeKeeper.Restart();

			if (config.wipeTowerSize_um > 0)
			{
				Polygon p = new Polygon();
				storage.wipeTower.Add(p);
				p.Add(new IntPoint(storage.modelMin.x - 3000, storage.modelMax.y + 3000));
				p.Add(new IntPoint(storage.modelMin.x - 3000, storage.modelMax.y + 3000 + config.wipeTowerSize_um));
				p.Add(new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um, storage.modelMax.y + 3000 + config.wipeTowerSize_um));
				p.Add(new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um, storage.modelMax.y + 3000));

				storage.wipePoint = new IntPoint(storage.modelMin.x - 3000 - config.wipeTowerSize_um / 2, storage.modelMax.y + 3000 + config.wipeTowerSize_um / 2);
			}

			if (config.enableRaft)
			{
				Raft.GenerateRaftOutlines(storage, config.raftExtraDistanceAroundPart_um, config);

				Skirt.generateSkirt(storage,
					config.skirtDistance_um + config.raftBaseLineSpacing_um,
					config.raftBaseLineSpacing_um,
					config.numberOfSkirtLoops,
					config.skirtMinLength_um,
					config.raftBaseThickness_um, config);
			}
			else
			{
				Skirt.generateSkirt(storage,
					config.skirtDistance_um,
					config.firstLayerExtrusionWidth_um,
					config.numberOfSkirtLoops,
					config.skirtMinLength_um,
					config.firstLayerThickness_um, config);
			}
		}
Example #2
0
		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);
		}
Example #3
0
        //Add a single layer from a single mesh-volume to the GCode
        void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent)
        {
            int  prevExtruder    = gcodeLayer.getExtruder();
            bool extruderChanged = gcodeLayer.setExtruder(volumeIndex);

            if (layerIndex == 0 && volumeIndex == 0 && !Raft.ShouldGenerateRaft(config))
            {
                if (storage.skirt.Count > 0 &&
                    storage.skirt[0].Count > 0)
                {
                    IntPoint lowestPoint = storage.skirt[0][0];

                    // lets make sure we start with the most outside loop
                    foreach (Polygon polygon in storage.skirt)
                    {
                        foreach (IntPoint position in polygon)
                        {
                            if (position.Y < lowestPoint.Y)
                            {
                                lowestPoint = polygon[0];
                            }
                        }
                    }

                    gcodeLayer.writeTravel(lowestPoint);
                }

                gcodeLayer.writePolygonsByOptimizer(storage.skirt, skirtConfig);
            }

            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++)
            {
                partOrderOptimizer.AddPolygon(layer.parts[partIndex].insets[0][0]);
            }
            partOrderOptimizer.Optimize();

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

                if (config.avoidCrossingPerimeters)
                {
                    gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.combBoundery);
                }
                else
                {
                    gcodeLayer.setAlwaysRetract(true);
                }

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

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

                // Write the bidge 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 (partCounter > 0)
                    {
                        gcodeLayer.forceRetract();
                    }

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

                        if (layerIndex == config.numberOfBottomLayers && part.insets.Count > 0)
                        {
                            gcodeLayer.writePolygonsByOptimizer(part.insets[0], insetXConfig);
                        }
                    }

                    // 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)
                    {
                        // First the outside (this helps with accuracy)
                        if (part.insets.Count > 0)
                        {
                            gcodeLayer.writePolygonsByOptimizer(part.insets[0], inset0Config);
                        }
                        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);
        }