예제 #1
0
        public void QueueAirGappedBottomLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
        {
            // normal support
            Polygons currentAirGappedBottoms = airGappedBottomOutlines[layerIndex];

            currentAirGappedBottoms = currentAirGappedBottoms.Offset(-config.ExtrusionWidth_um / 2);
            List <Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparatIslands();

            foreach (Polygons islandOutline in supportIslands)
            {
                Polygons islandInfillLines = new Polygons();
                // render a grid of support
                if (config.GenerateSupportPerimeter)
                {
                    Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4);
                    gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig);
                }
                Polygons infillOutline = islandOutline.Offset(-config.ExtrusionWidth_um / 2);
                switch (config.SupportType)
                {
                case ConfigConstants.SUPPORT_TYPE.GRID:
                    Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                    break;

                case ConfigConstants.SUPPORT_TYPE.LINES:
                    Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                    break;
                }
                gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig);
            }
        }
예제 #2
0
        public bool QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
        {
            // normal support
            Polygons currentSupportOutlines = supportOutlines[layerIndex];

            currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2);
            List <Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands();

            bool outputPaths = false;

            foreach (Polygons islandOutline in supportIslands)
            {
                // force a retract if changing islands
                if (config.RetractWhenChangingIslands)
                {
                    gcodeLayer.ForceRetract();
                }

                Polygons islandInfillLines = new Polygons();
                // render a grid of support
                if (config.GenerateSupportPerimeter || layerIndex == 0)
                {
                    Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4);
                    if (gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig))
                    {
                        outputPaths = true;
                    }
                }

                Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2);

                if (layerIndex == 0)
                {
                    // on the first layer print this as solid
                    Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um);
                }
                else
                {
                    switch (config.SupportType)
                    {
                    case ConfigConstants.SUPPORT_TYPE.GRID:
                        Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                        break;

                    case ConfigConstants.SUPPORT_TYPE.LINES:
                        Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                        break;
                    }
                }

                if (gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig))
                {
                    outputPaths |= true;
                }
            }

            return(outputPaths);
        }
예제 #3
0
        public void QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig)
        {
            // interface
            Polygons currentInterfaceOutlines = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2);

            if (currentInterfaceOutlines.Count > 0)
            {
                Polygons supportLines = new Polygons();
                Infill.GenerateLineInfill(config, currentInterfaceOutlines, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um);
                gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig);
            }
        }
예제 #4
0
        private void AddSupportToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config)
        {
            if (!storage.support.generated)
            {
                return;
            }

            if (config.supportExtruder > -1)
            {
                int prevExtruder = gcodeLayer.getExtruder();
                if (gcodeLayer.SetExtruder(config.supportExtruder))
                {
                    addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, config.extrusionWidth_um);
                }

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

            int currentZHeight_um = config.firstLayerThickness_um;

            if (layerIndex == 0)
            {
                currentZHeight_um /= 2;
            }
            else
            {
                if (layerIndex > 1)
                {
                    currentZHeight_um += (layerIndex - 1) * config.layerThickness_um;
                }
                currentZHeight_um += config.layerThickness_um / 2;
            }

            SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, currentZHeight_um);

            WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.supportPolygons, SupportType.General);

            if (config.supportInterfaceExtruder != -1 &&
                config.supportInterfaceExtruder != config.supportExtruder)
            {
                gcodeLayer.SetExtruder(config.supportInterfaceExtruder);
            }
            WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.interfacePolygons, SupportType.Interface);
        }
        public void PrimeOnWipeTower(int extruderIndex, int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (!HaveWipeTower(config) ||
                layerIndex > LastLayerWithChange(config) + 1)
            {
                return;
            }

            //If we changed extruder, print the wipe/prime tower for this nozzle;
            Polygons fillPolygons = new Polygons();

            GenerateWipeTowerInfill(extruderIndex, this.wipeTower, fillPolygons, fillConfig.lineWidth_um, config);
            gcodeLayer.QueuePolygons(fillPolygons, fillConfig);

            extrudersThatHaveBeenPrimed[extruderIndex] = true;
        }
예제 #6
0
		private void addWipeTower(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerNr, int prevExtruder, int extrusionWidth_um)
		{
			if (config.wipeTowerSize_um < 1)
			{
				return;
			}

			//If we changed extruder, print the wipe/prime tower for this nozzle;
			gcodeLayer.WritePolygonsByOptimizer(storage.wipeTower, supportInterfaceConfig);
			Polygons fillPolygons = new Polygons();
			Infill.GenerateLinePaths(storage.wipeTower, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, 45 + 90 * (layerNr % 2));
			gcodeLayer.WritePolygonsByOptimizer(fillPolygons, supportInterfaceConfig);

			//Make sure we wipe the old extruder on the wipe tower.
			gcodeLayer.WriteTravel(storage.wipePoint - config.extruderOffsets[prevExtruder] + config.extruderOffsets[gcodeLayer.getExtruder()]);
		}
예제 #7
0
		private int GetFanSpeed(int layerIndex, GCodePlanner gcodeLayer)
		{
			int fanSpeedPercent = config.fanSpeedMinPercent;
			if (gcodeLayer.getExtrudeSpeedFactor() <= 50)
			{
				fanSpeedPercent = config.fanSpeedMaxPercent;
			}
			else
			{
				int n = gcodeLayer.getExtrudeSpeedFactor() - 50;
				fanSpeedPercent = config.fanSpeedMinPercent * n / 50 + config.fanSpeedMaxPercent * (50 - n) / 50;
			}

			if (layerIndex < config.firstLayerToAllowFan)
			{
				// Don't allow the fan below this layer
				fanSpeedPercent = 0;
			}
			return fanSpeedPercent;
		}
        public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (layerIndex >= LastLayerWithChange(config) ||
                extrudersThatHaveBeenPrimed == null)
            {
                return;
            }

            // print all of the extruder loops that have not already been printed
            for (int extruderIndex = 0; extruderIndex < config.MaxExtruderCount(); extruderIndex++)
            {
                if (!extrudersThatHaveBeenPrimed[extruderIndex])
                {
                    // write the loops for this extruder, but don't change to it. We are just filling the prime tower.
                    PrimeOnWipeTower(extruderIndex, 0, gcodeLayer, fillConfig, config);
                }

                // clear the history of printer extruders for the next layer
                extrudersThatHaveBeenPrimed[extruderIndex] = false;
            }
        }
예제 #9
0
        public bool QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig)
        {
            // interface
            bool     outputPaths = false;
            Polygons currentInterfaceOutlines2 = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2);

            if (currentInterfaceOutlines2.Count > 0)
            {
                List <Polygons> interfaceIslands = currentInterfaceOutlines2.ProcessIntoSeparatIslands();

                foreach (Polygons interfaceOutline in interfaceIslands)
                {
                    // force a retract if changing islands
                    if (config.RetractWhenChangingIslands)
                    {
                        gcodeLayer.ForceRetract();
                    }

                    // make a border if layer 0
                    if (layerIndex == 0)
                    {
                        Polygons infillOutline = interfaceOutline.Offset(-supportInterfaceConfig.lineWidth_um / 2);
                        Polygons outlines      = Clipper.CleanPolygons(infillOutline, config.ExtrusionWidth_um / 4);
                        if (gcodeLayer.QueuePolygonsByOptimizer(outlines, supportInterfaceConfig))
                        {
                            outputPaths = true;
                        }
                    }

                    Polygons supportLines = new Polygons();
                    Infill.GenerateLineInfill(config, interfaceOutline, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um);
                    if (gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig))
                    {
                        outputPaths = true;
                    }
                }
            }

            return(outputPaths);
        }
예제 #10
0
        public void QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig)
        {
            // interface
            Polygons currentInterfaceOutlines2 = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2);

            if (currentInterfaceOutlines2.Count > 0)
            {
                List <Polygons> interfaceIslands = currentInterfaceOutlines2.ProcessIntoSeparatIslands();

                foreach (Polygons interfaceOutline in interfaceIslands)
                {
                    // force a retract if changing islands
                    if (config.RetractWhenChangingIslands)
                    {
                        gcodeLayer.ForceRetract();
                    }

                    Polygons supportLines = new Polygons();
                    Infill.GenerateLineInfill(config, interfaceOutline, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um);
                    gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig);
                }
            }
        }
예제 #11
0
		private void AddSkirtToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex)
		{
			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);
		}
예제 #12
0
		public void QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig, GCodePathConfig supportInterfaceConfig)
		{
			// normal support
			Polygons currentSupportOutlines = supportOutlines[layerIndex];
			currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2);
			List<Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands();

			foreach (Polygons islandOutline in supportIslands)
			{
				Polygons islandInfillLines = new Polygons();
				// render a grid of support
				if (config.generateSupportPerimeter)
				{
					gcodeLayer.QueuePolygonsByOptimizer(islandOutline, supportNormalConfig);
				}
				Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2);
				switch (config.supportType)
				{
					case ConfigConstants.SUPPORT_TYPE.GRID:
						Infill.GenerateGridInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
						break;

					case ConfigConstants.SUPPORT_TYPE.LINES:
						Infill.GenerateLineInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
						break;
				}
				gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig);
			}

			// interface
			Polygons currentInterfaceOutlines = interfaceLayers[layerIndex].Offset(-config.extrusionWidth_um / 2);
			if (currentInterfaceOutlines.Count > 0)
			{
				Polygons supportLines = new Polygons();
				Infill.GenerateLineInfill(config, currentInterfaceOutlines, ref supportLines, config.infillStartingAngle + 90, config.extrusionWidth_um);
				gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig);
			}
		}
예제 #13
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);
        }
예제 #14
0
        private void addWipeTower(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerNr, int prevExtruder, int extrusionWidth_um)
        {
            if (config.wipeTowerSize_um < 1)
            {
                return;
            }

            //If we changed extruder, print the wipe/prime tower for this nozzle;
            gcodeLayer.WritePolygonsByOptimizer(storage.wipeTower, supportInterfaceConfig);
            Polygons fillPolygons = new Polygons();
            Infill.GenerateLinePaths(storage.wipeTower, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, 45 + 90 * (layerNr % 2));
            gcodeLayer.WritePolygonsByOptimizer(fillPolygons, supportInterfaceConfig);

            //Make sure we wipe the old extruder on the wipe tower.
            gcodeLayer.WriteTravel(storage.wipePoint - config.extruderOffsets[prevExtruder] + config.extruderOffsets[gcodeLayer.getExtruder()]);
        }
예제 #15
0
        private void AddSupportToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config)
        {
            if (!storage.support.generated)
            {
                return;
            }

            if (config.supportExtruder > -1)
            {
                int prevExtruder = gcodeLayer.getExtruder();
                if (gcodeLayer.SetExtruder(config.supportExtruder))
                {
                    addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, config.extrusionWidth_um);
                }

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

            int currentZHeight_um = config.firstLayerThickness_um;
            if (layerIndex == 0)
            {
                currentZHeight_um /= 2;
            }
            else
            {
                if (layerIndex > 1)
                {
                    currentZHeight_um += (layerIndex - 1) * config.layerThickness_um;
                }
                currentZHeight_um += config.layerThickness_um / 2;
            }

            SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, currentZHeight_um);

            WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.supportPolygons, SupportType.General);

            if (config.supportInterfaceExtruder != -1
                && config.supportInterfaceExtruder != config.supportExtruder)
            {
                gcodeLayer.SetExtruder(config.supportInterfaceExtruder);
            }
            WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.interfacePolygons, SupportType.Interface);
        }
예제 #16
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);
        }
예제 #17
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);
		}
예제 #18
0
        private bool QueueClosetsInset(Polygons insetsConsider, bool limitDistance, GCodePathConfig pathConfig, int layerIndex, GCodePlanner gcodeLayer)
        {
            long maxDist_um = long.MaxValue;
            if (limitDistance)
            {
                maxDist_um = config.ExtrusionWidth_um * 4;
            }

            int polygonPrintedIndex = -1;

            for (int polygonIndex = 0; polygonIndex < insetsConsider.Count; polygonIndex++)
            {
                Polygon currentPolygon = insetsConsider[polygonIndex];
                int bestPoint = PathOrderOptimizer.GetClosestIndex(currentPolygon, gcodeLayer.LastPosition);
                if (bestPoint > -1)
                {
                    long distance = (currentPolygon[bestPoint] - gcodeLayer.LastPosition).Length();
                    if (distance < maxDist_um)
                    {
                        maxDist_um = distance;
                        polygonPrintedIndex = polygonIndex;
                    }
                }
            }

            if (polygonPrintedIndex > -1)
            {
                QueuePolygonsConsideringSupport(layerIndex, gcodeLayer, new Polygons() { insetsConsider[polygonPrintedIndex] }, pathConfig, SupportWriteType.UnsupportedAreas);
                insetsConsider.RemoveAt(polygonPrintedIndex);
                return true;
            }

            // Return the original limitDistance value if we didn't match a polygon
            return limitDistance;
        }
예제 #19
0
        public static void GenerateRaftGCodeIfRequired(SliceDataStorage storage, ConfigSettings config, GCodeExport gcode)
        {
            if (ShouldGenerateRaft(config))
            {
                GCodePathConfig raftBaseConfig    = new GCodePathConfig(config.firstLayerSpeed, config.extrusionWidth_um * 3, "SUPPORT");
                GCodePathConfig raftMiddleConfig  = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceLinewidth_um, "SUPPORT");
                GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceLinewidth_um, "SUPPORT");

                // create the raft base
                {
                    gcode.writeComment("LAYER:-3");
                    gcode.writeComment("RAFT BASE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    if (config.supportExtruder > 0)
                    {
                        gcodeLayer.setExtruder(config.supportExtruder);
                    }

                    gcode.setZ(config.raftBaseThickness_um);
                    gcode.setExtrusion(config.raftBaseThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);
                    gcodeLayer.writePolygonsByOptimizer(storage.raftOutline, raftBaseConfig);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseThickness_um, config.raftLineSpacing_um, config.infillExtendIntoPerimeter_um, 0);
                    gcodeLayer.writePolygonsByOptimizer(storage.skirt, raftBaseConfig);
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftBaseConfig);

                    gcodeLayer.writeGCode(false, config.raftBaseThickness_um);
                }

                if (config.raftFanSpeedPercent > 0)
                {
                    gcode.writeFanCommand(config.raftFanSpeedPercent);
                }

                // raft middle layers
                {
                    gcode.writeComment("LAYER:-2");
                    gcode.writeComment("RAFT MIDDLE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um);
                    gcode.setExtrusion(config.raftInterfaceThicknes_um, config.filamentDiameter_um, config.extrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftInterfaceLinewidth_um, config.raftInterfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 45);
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftMiddleConfig);

                    gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um);
                }

                for (int raftSurfaceLayer = 1; raftSurfaceLayer <= config.raftSurfaceLayers; raftSurfaceLayer++)
                {
                    gcode.writeComment("LAYER:-1");
                    gcode.writeComment("RAFT SURFACE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceThickness_um * raftSurfaceLayer);
                    gcode.setExtrusion(config.raftSurfaceThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLinewidth_um, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 90 * raftSurfaceLayer);
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftSurfaceConfig);

                    gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um);
                }
            }
        }
예제 #20
0
		private void QueuePolygonsConsideringSupport(int layerIndex, GCodePlanner gcodeLayer, Polygons polygonsToWrite, GCodePathConfig fillConfig, SupportWriteType supportWriteType)
		{
			if (config.generateSupport 
				&& layerIndex > 0
				&& !config.continuousSpiralOuterPerimeter)
			{
				Polygons supportOutlines = slicingData.support.GetRequiredSupportAreas(layerIndex);

				if (supportWriteType == SupportWriteType.UnsupportedAreas)
				{
					if (supportOutlines.Count > 0)
					{
						Polygons polygonsNotOnSupport;
						// don't write the bottoms that are sitting on supported areas (they will be written at air gap distance later).
						polygonsToWrite = PolygonsHelper.ConvertToLines(polygonsToWrite);

						polygonsNotOnSupport = polygonsToWrite.CreateLineDifference(supportOutlines);
						gcodeLayer.QueuePolygonsByOptimizer(polygonsNotOnSupport, fillConfig);
					}
					else
					{
						gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
					}
				}
				else
				{
					if (supportOutlines.Count > 0)
					{
						if (supportOutlines.Count > 0)
						{
							// write the bottoms that are sitting on supported areas.
							Polygons polygonsOnSupport;
							polygonsToWrite = PolygonsHelper.ConvertToLines(polygonsToWrite);

							polygonsOnSupport = supportOutlines.CreateLineIntersections(polygonsToWrite);
							// ensure that all the segments have only 2 points
							foreach(Polygon poly in polygonsOnSupport)
							{
								while(poly.Count > 2)
								{
									// This is an error and I'm not sure why it happened. It needs to be investigated. // LBB 2016 01 12
									poly.RemoveAt(poly.Count - 1);
								}
							}

							gcodeLayer.QueuePolygonsByOptimizer(polygonsOnSupport, fillConfig);
						}
						else
						{
							gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
						}
					}
				}
			}
			else if (supportWriteType == SupportWriteType.UnsupportedAreas)
			{
				gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
			}
		}
예제 #21
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);
		}
예제 #22
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);
		}
예제 #23
0
		private void QueueSkirtToGCode(LayerDataStorage slicingData, GCodePlanner gcodeLayer, int layerIndex)
		{
			if (slicingData.skirt.Count > 0
				&& slicingData.skirt[0].Count > 0)
			{
				IntPoint lowestPoint = slicingData.skirt[0][0];

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

				gcodeLayer.QueueTravel(lowestPoint);
			}

			gcodeLayer.QueuePolygonsByOptimizer(slicingData.skirt, skirtConfig);
		}
예제 #24
0
		private void writeGCode(LayerDataStorage slicingData)
		{
			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);
			}
			else
			{
				gcode.WriteFanCommand(0);
				gcode.ResetExtrusionValue();
				gcode.WriteRetraction();
				gcode.setZ(maxObjectHeight + 5000);
				gcode.WriteMove(gcode.GetPosition(), config.travelSpeed, 0);
				gcode.WriteMove(new Point3(slicingData.modelMin.x, slicingData.modelMin.y, gcode.CurrentZ), config.travelSpeed, 0);
			}
			fileNumber++;

			int totalLayers = slicingData.Extruders[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 (ExtruderLayers currentExtruder in slicingData.Extruders)
					{
						SliceLayer currentLayer = currentExtruder.Layers[layerIndex];
						for (int partIndex = 0; partIndex < currentExtruder.Layers[layerIndex].Islands.Count; partIndex++)
						{
							LayerIsland currentIsland = currentLayer.Islands[partIndex];
							if (currentIsland.IslandOutline.Count > 0)
							{
								layerHasData = true;
								break;
							}
						}
					}

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

			// keep the raft generation code inside of raft
			slicingData.WriteRaftGCodeIfRequired(config, gcode);

			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);

				if (layerIndex == 0)
				{
					skirtConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "SKIRT");
					inset0Config.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "WALL-OUTER");
					insetXConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "WALL-INNER");

					fillConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "FILL", false);
					topFillConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "TOP-FILL", false);
					bottomFillConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "BOTTOM-FILL", false);
					bridgConfig.SetData(config.firstLayerSpeed, config.firstLayerExtrusionWidth_um, "BRIDGE");

					supportNormalConfig.SetData(config.firstLayerSpeed, config.supportExtrusionWidth_um, "SUPPORT");
					supportInterfaceConfig.SetData(config.firstLayerSpeed, config.extrusionWidth_um, "SUPPORT-INTERFACE");
				}
				else
				{
					skirtConfig.SetData(config.insidePerimetersSpeed, config.extrusionWidth_um, "SKIRT");
					inset0Config.SetData(config.outsidePerimeterSpeed, config.outsideExtrusionWidth_um, "WALL-OUTER");
					insetXConfig.SetData(config.insidePerimetersSpeed, config.extrusionWidth_um, "WALL-INNER");

					fillConfig.SetData(config.infillSpeed, config.extrusionWidth_um, "FILL", false);
					topFillConfig.SetData(config.topInfillSpeed, config.firstLayerExtrusionWidth_um, "TOP-FILL", false);
					bottomFillConfig.SetData(config.infillSpeed, config.firstLayerExtrusionWidth_um, "BOTTOM-FILL", false);
					bridgConfig.SetData(config.bridgeSpeed, config.extrusionWidth_um, "BRIDGE");

					supportNormalConfig.SetData(config.supportMaterialSpeed, config.supportExtrusionWidth_um, "SUPPORT");
					supportInterfaceConfig.SetData(config.supportMaterialSpeed - 5, 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.
				if (layerIndex == 0 && !config.ShouldGenerateRaft())
				{
					QueueSkirtToGCode(slicingData, gcodeLayer, layerIndex);
				}

				if (slicingData.support != null)
				{
					slicingData.support.QueueNormalSupportLayer(config, gcodeLayer, layerIndex, supportNormalConfig, supportInterfaceConfig);
				}

				int fanSpeedPercent = GetFanSpeed(layerIndex, gcodeLayer);

				for (int extruderIndex = 0; extruderIndex < slicingData.Extruders.Count; extruderIndex++)
				{
					if (layerIndex == 0)
					{
						QueueExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.firstLayerExtrusionWidth_um, fanSpeedPercent, z);
					}
					else
					{
						QueueExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.extrusionWidth_um, fanSpeedPercent, z);
					}
				}

				if (slicingData.support != null)
				{
					z += config.supportAirGap_um;
					gcode.setZ(z);

					for (int extruderIndex = 0; extruderIndex < slicingData.Extruders.Count; extruderIndex++)
					{
						QueueAirGappedExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.extrusionWidth_um, fanSpeedPercent, z);
					}

					slicingData.support.QueueAirGappedBottomLayer(config, gcodeLayer, layerIndex, supportNormalConfig);
				}

				//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.WriteQueuedGCode(currentLayerThickness_um);
			}

			LogOutput.Log("Wrote layers in {0:0.00}s.\n".FormatWith(timeKeeper.Elapsed.TotalSeconds));
			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, slicingData.modelSize.z);
		}
예제 #25
0
        public static void GenerateRaftGCodeIfRequired(SliceDataStorage storage, ConfigSettings config, GCodeExport gcode)
        {
            if (ShouldGenerateRaft(config))
            {
                GCodePathConfig raftBaseConfig    = new GCodePathConfig(config.firstLayerSpeed, config.raftBaseExtrusionWidth_um, "SUPPORT");
                GCodePathConfig raftMiddleConfig  = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceExtrusionWidth_um, "SUPPORT");
                GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceExtrusionWidth_um, "SUPPORT");

                // create the raft base
                {
                    gcode.writeComment("LAYER:-3");
                    gcode.writeComment("RAFT BASE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    if (config.raftExtruder > 0)
                    {
                        // if we have a specified raft extruder use it
                        gcodeLayer.setExtruder(config.raftExtruder);
                    }
                    else if (config.supportExtruder > 0)
                    {
                        // else preserve the old behavior of using the support extruder if set.
                        gcodeLayer.setExtruder(config.supportExtruder);
                    }

                    gcode.setZ(config.raftBaseThickness_um);
                    gcode.setExtrusion(config.raftBaseThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);
                    gcodeLayer.writePolygonsByOptimizer(storage.raftOutline, raftBaseConfig);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseLineSpacing_um, config.infillExtendIntoPerimeter_um, 0);
                    gcodeLayer.writePolygonsByOptimizer(storage.skirt, raftBaseConfig);
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftBaseConfig);

                    gcodeLayer.writeGCode(false, config.raftBaseThickness_um);
                }

                if (config.raftFanSpeedPercent > 0)
                {
                    gcode.writeFanCommand(config.raftFanSpeedPercent);
                }

                // raft middle layers
                {
                    gcode.writeComment("LAYER:-2");
                    gcode.writeComment("RAFT MIDDLE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um);
                    gcode.setExtrusion(config.raftInterfaceThicknes_um, config.filamentDiameter_um, config.extrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftInterfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 45);
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftMiddleConfig);

                    gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um);
                }

                for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.raftSurfaceLayers; raftSurfaceIndex++)
                {
                    gcode.writeComment("LAYER:-1");
                    gcode.writeComment("RAFT SURFACE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
                    gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceThickness_um * raftSurfaceIndex);
                    gcode.setExtrusion(config.raftSurfaceThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    if (raftSurfaceIndex == config.raftSurfaceLayers)
                    {
                        // make sure the top layer of the raft is 90 degrees offset to the first layer of the part so that it has minimum contact points.
                        Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 90 * raftSurfaceIndex);
                    }
                    gcodeLayer.writePolygonsByOptimizer(raftLines, raftSurfaceConfig);

                    gcodeLayer.writeGCode(false, config.raftInterfaceThicknes_um);
                }
            }
        }
예제 #26
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);
		}
예제 #27
0
        public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if(layerIndex >= LastLayerWithChange(config)
                || extrudersThatHaveBeenPrimed == null)
            {
                return;
            }

            // print all of the extruder loops that have not already been printed
            for (int extruderIndex = 0; extruderIndex < config.MaxExtruderCount(); extruderIndex++)
            {
                if (!extrudersThatHaveBeenPrimed[extruderIndex])
                {
                    // write the loops for this extruder, but don't change to it. We are just filling the prime tower.
                    PrimeOnWipeTower(extruderIndex, 0, gcodeLayer, fillConfig, config);
                }

                // clear the history of printer extruders for the next layer
                extrudersThatHaveBeenPrimed[extruderIndex] = false;
            }
        }
예제 #28
0
        public void PrimeOnWipeTower(int extruderIndex, int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (config.WipeTowerSize_um < 1
                || extrudersThatHaveBeenPrimed == null
                || layerIndex > LastLayerWithChange(config) + 1)
            {
                return;
            }

            //If we changed extruder, print the wipe/prime tower for this nozzle;
            Polygons fillPolygons = new Polygons();
            GenerateWipeTowerInfill(extruderIndex, this.wipeTower, fillPolygons, fillConfig.lineWidth_um, config);
            gcodeLayer.QueuePolygons(fillPolygons, fillConfig);

            extrudersThatHaveBeenPrimed[extruderIndex] = true;
        }
예제 #29
0
        public void QueueNormalSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
        {
            // normal support
            Polygons currentSupportOutlines = supportOutlines[layerIndex];
            currentSupportOutlines = currentSupportOutlines.Offset(-supportNormalConfig.lineWidth_um / 2);
            List<Polygons> supportIslands = currentSupportOutlines.ProcessIntoSeparatIslands();

            foreach (Polygons islandOutline in supportIslands)
            {
                // force a retract if changing islands
                if (config.RetractWhenChangingIslands)
                {
                    gcodeLayer.ForceRetract();
                }

                Polygons islandInfillLines = new Polygons();
                // render a grid of support
                if (config.GenerateSupportPerimeter)
                {
                    Polygons outlines = Clipper.CleanPolygons(islandOutline, config.ExtrusionWidth_um / 4);
                    gcodeLayer.QueuePolygonsByOptimizer(outlines, supportNormalConfig);
                }

                Polygons infillOutline = islandOutline.Offset(-supportNormalConfig.lineWidth_um / 2);

                if (layerIndex == 0)
                {
                    // on the first layer print this as solid
                    Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.ExtrusionWidth_um);
                }
                else
                {
                    switch (config.SupportType)
                    {
                        case ConfigConstants.SUPPORT_TYPE.GRID:
                            Infill.GenerateGridInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                            break;

                        case ConfigConstants.SUPPORT_TYPE.LINES:
                            Infill.GenerateLineInfill(config, infillOutline, islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                            break;
                    }
                }

                gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig);
            }
        }
예제 #30
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);
			}
		}
예제 #31
0
        private void writeGCode(LayerDataStorage slicingData)
        {
            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)
            {
                gcode.WriteCode(config.StartCode);
            }
            else
            {
                gcode.WriteFanCommand(0);
                gcode.ResetExtrusionValue();
                gcode.WriteRetraction();
                gcode.setZ(maxObjectHeight + 5000);
                gcode.WriteMove(gcode.GetPosition(), config.TravelSpeed, 0);
                gcode.WriteMove(new Point3(slicingData.modelMin.x, slicingData.modelMin.y, gcode.CurrentZ), config.TravelSpeed, 0);
            }
            fileNumber++;

            int totalLayers = slicingData.Extruders[0].Layers.Count;
            if (config.outputOnlyFirstLayer)
            {
                totalLayers = 1;
            }

            // let's remove any of the layers on top that are empty
            {
                for (int layerIndex = totalLayers - 1; layerIndex >= 0; layerIndex--)
                {
                    bool layerHasData = false;
                    foreach (ExtruderLayers currentExtruder in slicingData.Extruders)
                    {
                        SliceLayer currentLayer = currentExtruder.Layers[layerIndex];
                        for (int partIndex = 0; partIndex < currentExtruder.Layers[layerIndex].Islands.Count; partIndex++)
                        {
                            LayerIsland currentIsland = currentLayer.Islands[partIndex];
                            if (currentIsland.IslandOutline.Count > 0)
                            {
                                layerHasData = true;
                                break;
                            }
                        }
                    }

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

            // keep the raft generation code inside of raft
            slicingData.WriteRaftGCodeIfRequired(gcode, config);

            for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
            {
                if (MatterSlice.Canceled)
                {
                    return;
                }

                if(config.outputOnlyFirstLayer && layerIndex > 0)
                {
                    break;
                }

                LogOutput.Log("Writing Layers {0}/{1}\n".FormatWith(layerIndex + 1, totalLayers));

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

                if (layerIndex == 0)
                {
                    skirtConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "SKIRT");
                    inset0Config.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "WALL-OUTER");
                    insetXConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "WALL-INNER");

                    fillConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "FILL", false);
                    topFillConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "TOP-FILL", false);
                    bottomFillConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "BOTTOM-FILL", false);
                    airGappedBottomConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "AIR-GAP", false);
                    bridgeConfig.SetData(config.FirstLayerSpeed, config.FirstLayerExtrusionWidth_um, "BRIDGE");

                    supportNormalConfig.SetData(config.FirstLayerSpeed, config.SupportExtrusionWidth_um, "SUPPORT");
                    supportInterfaceConfig.SetData(config.FirstLayerSpeed, config.ExtrusionWidth_um, "SUPPORT-INTERFACE");
                }
                else
                {
                    skirtConfig.SetData(config.InsidePerimetersSpeed, config.ExtrusionWidth_um, "SKIRT");
                    inset0Config.SetData(config.OutsidePerimeterSpeed, config.OutsideExtrusionWidth_um, "WALL-OUTER");
                    insetXConfig.SetData(config.InsidePerimetersSpeed, config.ExtrusionWidth_um, "WALL-INNER");

                    fillConfig.SetData(config.InfillSpeed, config.ExtrusionWidth_um, "FILL", false);
                    topFillConfig.SetData(config.TopInfillSpeed, config.ExtrusionWidth_um, "TOP-FILL", false);
                    bottomFillConfig.SetData(config.InfillSpeed, config.ExtrusionWidth_um, "BOTTOM-FILL", false);
                    airGappedBottomConfig.SetData(config.FirstLayerSpeed, config.ExtrusionWidth_um, "AIR-GAP", false);
                    bridgeConfig.SetData(config.BridgeSpeed, config.ExtrusionWidth_um, "BRIDGE");

                    supportNormalConfig.SetData(config.SupportMaterialSpeed, config.SupportExtrusionWidth_um, "SUPPORT");
                    supportInterfaceConfig.SetData(config.FirstLayerSpeed - 1, 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, config.PerimeterStartEndOverlapRatio);
                if (layerIndex == 0
                    && config.RetractionZHop > 0)
                {
                    gcodeLayer.ForceRetract();
                }

                // 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.
                if (layerIndex == 0 && !config.ShouldGenerateRaft())
                {
                    QueueSkirtToGCode(slicingData, gcodeLayer, layerIndex);
                }

                int fanSpeedPercent = GetFanSpeed(layerIndex, gcodeLayer);

                for (int extruderIndex = 0; extruderIndex < config.MaxExtruderCount(); extruderIndex++)
                {
                    int prevExtruder = gcodeLayer.GetExtruder();
                    bool extruderChanged = gcodeLayer.SetExtruder(extruderIndex);

                    if (extruderChanged)
                    {
                        slicingData.PrimeOnWipeTower(extruderIndex, layerIndex, gcodeLayer, fillConfig, config);
                        //Make sure we wipe the old extruder on the wipe tower.
                        gcodeLayer.QueueTravel(slicingData.wipePoint - config.ExtruderOffsets[prevExtruder] + config.ExtruderOffsets[gcodeLayer.GetExtruder()]);
                    }

                    if (layerIndex == 0)
                    {
                        QueueExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.FirstLayerExtrusionWidth_um, z);
                    }
                    else
                    {
                        QueueExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.ExtrusionWidth_um, z);
                    }

                    if (slicingData.support != null)
                    {
                        if ((config.SupportExtruder <= 0 && extruderIndex == 0)
                            || config.SupportExtruder == extruderIndex)
                        {
                            slicingData.support.QueueNormalSupportLayer(config, gcodeLayer, layerIndex, supportNormalConfig);
                        }
                        if ((config.SupportInterfaceExtruder <= 0 && extruderIndex == 0)
                            || config.SupportInterfaceExtruder == extruderIndex)
                        {
                            slicingData.support.QueueInterfaceSupportLayer(config, gcodeLayer, layerIndex, supportInterfaceConfig);
                        }
                    }
                }

                slicingData.EnsureWipeTowerIsSolid(layerIndex, gcodeLayer, fillConfig, config);

                if (slicingData.support != null)
                {
                    z += config.SupportAirGap_um;
                    gcode.setZ(z);
                    gcodeLayer.QueueTravel(gcodeLayer.LastPosition);

                    for (int extruderIndex = 0; extruderIndex < slicingData.Extruders.Count; extruderIndex++)
                    {
                        QueueAirGappedExtruderLayerToGCode(slicingData, gcodeLayer, extruderIndex, layerIndex, config.ExtrusionWidth_um, z);
                    }

                    slicingData.support.QueueAirGappedBottomLayer(config, gcodeLayer, layerIndex, airGappedBottomConfig);
                }

                //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;
                }

                // Move to the best point for the next layer
                if (!config.ContinuousSpiralOuterPerimeter
                    && layerIndex > 0
                    && layerIndex < totalLayers - 2)
                {
                    // 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
                    SliceLayer layer = slicingData?.Extruders?[0]?.Layers?[layerIndex + 1];
                    if (layer.Islands.Count == 1)
                    {
                        LayerIsland island = layer?.Islands?[0];
                        if (island?.InsetToolPaths?[0]?[0]?.Count > 0)
                        {
                            int bestPoint = PathOrderOptimizer.GetBestIndex(island.InsetToolPaths[0][0], config.ExtrusionWidth_um);
                            gcodeLayer.SetOuterPerimetersToAvoidCrossing(island.AvoidCrossingBoundary);
                            gcodeLayer.QueueTravel(island.InsetToolPaths[0][0][bestPoint]);
                            // Now move up to the next layer so we don't start the extrusion one layer too low.
                            gcode.setZ(z + config.LayerThickness_um);
                            gcodeLayer.QueueTravel(island.InsetToolPaths[0][0][bestPoint]);
                        }
                    }
                }

                gcodeLayer.WriteQueuedGCode(currentLayerThickness_um, fanSpeedPercent, config.BridgeFanSpeedPercent);
            }

            LogOutput.Log("Wrote layers in {0:0.00}s.\n".FormatWith(timeKeeper.Elapsed.TotalSeconds));
            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, slicingData.modelSize.z);
        }
예제 #32
0
        private void AddSkirtToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex)
        {
            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);
        }
예제 #33
0
        private void QueuePolygonsConsideringSupport(int layerIndex, GCodePlanner gcodeLayer, Polygons polygonsToWrite, GCodePathConfig fillConfig, SupportWriteType supportWriteType)
        {
            bool oldLoopValue = fillConfig.closedLoop;

            if (config.GenerateSupport
                && layerIndex > 0
                && !config.ContinuousSpiralOuterPerimeter)
            {
                Polygons supportOutlines = slicingData.support.GetRequiredSupportAreas(layerIndex).Offset(fillConfig.lineWidth_um/2);

                if (supportWriteType == SupportWriteType.UnsupportedAreas)
                {
                    if (supportOutlines.Count > 0)
                    {
                        // don't write the bottoms that are sitting on supported areas (they will be written at air gap distance later).
                        Polygons polygonsToWriteAsLines = PolygonsHelper.ConvertToLines(polygonsToWrite);

                        Polygons polygonsNotOnSupport = polygonsToWriteAsLines.CreateLineDifference(supportOutlines);
                        fillConfig.closedLoop = false;
                        gcodeLayer.QueuePolygonsByOptimizer(polygonsNotOnSupport, fillConfig);
                    }
                    else
                    {
                        gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
                    }
                }
                else
                {
                    if (supportOutlines.Count > 0)
                    {
                        if (supportOutlines.Count > 0)
                        {
                            // write the bottoms that are sitting on supported areas.
                            Polygons polygonsToWriteAsLines = PolygonsHelper.ConvertToLines(polygonsToWrite);

                            Polygons polygonsOnSupport = supportOutlines.CreateLineIntersections(polygonsToWriteAsLines);
                            fillConfig.closedLoop = false;
                            gcodeLayer.QueuePolygonsByOptimizer(polygonsOnSupport, fillConfig);
                        }
                        else
                        {
                            gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
                        }
                    }
                }
            }
            else if (supportWriteType == SupportWriteType.UnsupportedAreas)
            {
                gcodeLayer.QueuePolygonsByOptimizer(polygonsToWrite, fillConfig);
            }

            fillConfig.closedLoop = oldLoopValue;
        }
예제 #34
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);
        }
예제 #35
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);
        }
예제 #36
0
        private int GetFanSpeed(int layerIndex, GCodePlanner gcodeLayer)
        {
            int fanSpeedPercent = config.fanSpeedMinPercent;
            if (gcodeLayer.getExtrudeSpeedFactor() <= 50)
            {
                fanSpeedPercent = config.fanSpeedMaxPercent;
            }
            else
            {
                int n = gcodeLayer.getExtrudeSpeedFactor() - 50;
                fanSpeedPercent = config.fanSpeedMinPercent * n / 50 + config.fanSpeedMaxPercent * (50 - n) / 50;
            }

            if (layerIndex < config.firstLayerToAllowFan)
            {
                // Don't allow the fan below this layer
                fanSpeedPercent = 0;
            }
            return fanSpeedPercent;
        }
예제 #37
0
        public void WriteRaftGCodeIfRequired(GCodeExport gcode, ConfigSettings config)
        {
            LayerDataStorage storage = this;

            if (config.ShouldGenerateRaft())
            {
                GCodePathConfig raftBaseConfig = new GCodePathConfig("raftBaseConfig");
                raftBaseConfig.SetData(config.FirstLayerSpeed, config.RaftBaseExtrusionWidth_um, "SUPPORT");

                GCodePathConfig raftMiddleConfig = new GCodePathConfig("raftMiddleConfig");
                raftMiddleConfig.SetData(config.RaftPrintSpeed, config.RaftInterfaceExtrusionWidth_um, "SUPPORT");

                GCodePathConfig raftSurfaceConfig = new GCodePathConfig("raftMiddleConfig");
                raftSurfaceConfig.SetData((config.RaftSurfacePrintSpeed > 0) ? config.RaftSurfacePrintSpeed : config.RaftPrintSpeed, config.RaftSurfaceExtrusionWidth_um, "SUPPORT");

                // create the raft base
                {
                    gcode.WriteComment("RAFT BASE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio);
                    if (config.RaftExtruder >= 0)
                    {
                        // if we have a specified raft extruder use it
                        gcodeLayer.SetExtruder(config.RaftExtruder);
                    }
                    else if (config.SupportExtruder >= 0)
                    {
                        // else preserve the old behavior of using the support extruder if set.
                        gcodeLayer.SetExtruder(config.SupportExtruder);
                    }

                    gcode.SetZ(config.RaftBaseThickness_um);

                    gcode.LayerChanged(-3);

                    gcode.SetExtrusion(config.RaftBaseThickness_um, config.FilamentDiameter_um, config.ExtrusionMultiplier);

                    // write the skirt around the raft
                    gcodeLayer.QueuePolygonsByOptimizer(storage.skirt, raftBaseConfig);

                    List <Polygons> raftIslands = storage.raftOutline.ProcessIntoSeparatIslands();
                    foreach (var raftIsland in raftIslands)
                    {
                        // write the outline of the raft
                        gcodeLayer.QueuePolygonsByOptimizer(raftIsland, raftBaseConfig);

                        Polygons raftLines = new Polygons();
                        Infill.GenerateLinePaths(raftIsland.Offset(-config.RaftBaseExtrusionWidth_um), raftLines, config.RaftBaseLineSpacing_um, config.InfillExtendIntoPerimeter_um, 0);

                        // write the inside of the raft base
                        gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftBaseConfig);

                        if (config.RetractWhenChangingIslands)
                        {
                            gcodeLayer.ForceRetract();
                        }
                    }

                    gcodeLayer.WriteQueuedGCode(config.RaftBaseThickness_um);
                }

                if (config.RaftFanSpeedPercent > 0)
                {
                    gcode.WriteFanCommand(config.RaftFanSpeedPercent);
                }

                // raft middle layers
                {
                    gcode.WriteComment("RAFT MIDDLE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio);
                    gcode.SetZ(config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um);
                    gcode.LayerChanged(-2);
                    gcode.SetExtrusion(config.RaftInterfaceThicknes_um, config.FilamentDiameter_um, config.ExtrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftInterfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 45);
                    gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftMiddleConfig);

                    gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um);
                }

                for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.RaftSurfaceLayers; raftSurfaceIndex++)
                {
                    gcode.WriteComment("RAFT SURFACE");
                    GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um, config.PerimeterStartEndOverlapRatio);
                    gcode.SetZ(config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um + config.RaftSurfaceThickness_um * raftSurfaceIndex);
                    gcode.LayerChanged(-1);
                    gcode.SetExtrusion(config.RaftSurfaceThickness_um, config.FilamentDiameter_um, config.ExtrusionMultiplier);

                    Polygons raftLines = new Polygons();
                    if (raftSurfaceIndex == config.RaftSurfaceLayers)
                    {
                        // make sure the top layer of the raft is 90 degrees offset to the first layer of the part so that it has minimum contact points.
                        Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, config.InfillStartingAngle + 90);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 90 * raftSurfaceIndex);
                    }
                    gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftSurfaceConfig);

                    gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um);
                }
            }
        }
예제 #38
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);
            }
        }
예제 #39
0
		public void WriteRaftGCodeIfRequired(ConfigSettings config, GCodeExport gcode)
		{
			LayerDataStorage storage = this;
			if (config.ShouldGenerateRaft())
			{
				GCodePathConfig raftBaseConfig = new GCodePathConfig(config.firstLayerSpeed, config.raftBaseExtrusionWidth_um, "SUPPORT");
				GCodePathConfig raftMiddleConfig = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceExtrusionWidth_um, "SUPPORT");
				GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceExtrusionWidth_um, "SUPPORT");

				// create the raft base
				{
					gcode.WriteComment("LAYER:-3");
					gcode.WriteComment("RAFT BASE");
					GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
					if (config.raftExtruder >= 0)
					{
						// if we have a specified raft extruder use it
						gcodeLayer.SetExtruder(config.raftExtruder);
					}
					else if (config.supportExtruder >= 0)
					{
						// else preserve the old behavior of using the support extruder if set.
						gcodeLayer.SetExtruder(config.supportExtruder);
					}

					gcode.setZ(config.raftBaseThickness_um);
					gcode.SetExtrusion(config.raftBaseThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);

					Polygons raftLines = new Polygons();
					Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftBaseLineSpacing_um, config.infillExtendIntoPerimeter_um, 0);

					// write the skirt around the raft
					gcodeLayer.QueuePolygonsByOptimizer(storage.skirt, raftBaseConfig);

					// write the outline of the raft
					gcodeLayer.QueuePolygonsByOptimizer(storage.raftOutline, raftBaseConfig);

					// write the inside of the raft base
					gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftBaseConfig);

					gcodeLayer.WriteQueuedGCode(config.raftBaseThickness_um);
				}

				if (config.raftFanSpeedPercent > 0)
				{
					gcode.WriteFanCommand(config.raftFanSpeedPercent);
				}

				// raft middle layers
				{
					gcode.WriteComment("LAYER:-2");
					gcode.WriteComment("RAFT MIDDLE");
					GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
					gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um);
					gcode.SetExtrusion(config.raftInterfaceThicknes_um, config.filamentDiameter_um, config.extrusionMultiplier);

					Polygons raftLines = new Polygons();
					Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftInterfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 45);
					gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftMiddleConfig);

					gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um);
				}

				for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.raftSurfaceLayers; raftSurfaceIndex++)
				{
					gcode.WriteComment("LAYER:-1");
					gcode.WriteComment("RAFT SURFACE");
					GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.travelSpeed, config.minimumTravelToCauseRetraction_um);
					gcode.setZ(config.raftBaseThickness_um + config.raftInterfaceThicknes_um + config.raftSurfaceThickness_um * raftSurfaceIndex);
					gcode.SetExtrusion(config.raftSurfaceThickness_um, config.filamentDiameter_um, config.extrusionMultiplier);

					Polygons raftLines = new Polygons();
					if (raftSurfaceIndex == config.raftSurfaceLayers)
					{
						// make sure the top layer of the raft is 90 degrees offset to the first layer of the part so that it has minimum contact points.
						Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90);
					}
					else
					{
						Infill.GenerateLinePaths(storage.raftOutline, ref raftLines, config.raftSurfaceLineSpacing_um, config.infillExtendIntoPerimeter_um, 90 * raftSurfaceIndex);
					}
					gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftSurfaceConfig);

					gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um);
				}
			}
		}
예제 #40
0
        public void QueueInterfaceSupportLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportInterfaceConfig)
        {
            // interface
            Polygons currentInterfaceOutlines2 = interfaceLayers[layerIndex].Offset(-config.ExtrusionWidth_um / 2);
            if (currentInterfaceOutlines2.Count > 0)
            {
                List<Polygons> interfaceIslands = currentInterfaceOutlines2.ProcessIntoSeparatIslands();

                foreach (Polygons interfaceOutline in interfaceIslands)
                {
                    // force a retract if changing islands
                    if (config.RetractWhenChangingIslands)
                    {
                        gcodeLayer.ForceRetract();
                    }

                    Polygons supportLines = new Polygons();
                    Infill.GenerateLineInfill(config, interfaceOutline, supportLines, config.InfillStartingAngle + 90, config.ExtrusionWidth_um);
                    gcodeLayer.QueuePolygonsByOptimizer(supportLines, supportInterfaceConfig);
                }
            }
        }
예제 #41
0
		public void QueueAirGappedBottomLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
		{
			// normal support
			Polygons currentAirGappedBottoms = airGappedBottomOutlines[layerIndex];
			currentAirGappedBottoms = currentAirGappedBottoms.Offset(-config.extrusionWidth_um / 2);
			List<Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparatIslands();

			foreach (Polygons islandOutline in supportIslands)
			{
				Polygons islandInfillLines = new Polygons();
				// render a grid of support
				gcodeLayer.QueuePolygonsByOptimizer(islandOutline, supportNormalConfig);
				Polygons infillOutline = islandOutline.Offset(-config.extrusionWidth_um / 2);
				switch (config.supportType)
				{
					case ConfigConstants.SUPPORT_TYPE.GRID:
						Infill.GenerateGridInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
						break;

					case ConfigConstants.SUPPORT_TYPE.LINES:
						Infill.GenerateLineInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
						break;
				}
				gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig);
			}
		}