示例#1
0
		private void CreateWipeShields(SliceDataStorage storage, int totalLayers)
		{
			for (int layerNr = 0; layerNr < totalLayers; layerNr++)
			{
				Polygons wipeShield = new Polygons();
				for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
				{
					for (int partNr = 0; partNr < storage.volumes[volumeIdx].layers[layerNr].parts.Count; partNr++)
					{
						wipeShield = wipeShield.CreateUnion(storage.volumes[volumeIdx].layers[layerNr].parts[partNr].TotalOutline.Offset(config.wipeShieldDistanceFromShapes_um));
					}
				}
				storage.wipeShield.Add(wipeShield);
			}

			for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
			{
				storage.wipeShield[layerIndex] = storage.wipeShield[layerIndex].Offset(-1000).Offset(1000);
			}

			int offsetAngle = (int)Math.Tan(60.0 * Math.PI / 180) * config.layerThickness_um;//Allow for a 60deg angle in the wipeShield.
			for (int layerNr = 1; layerNr < totalLayers; layerNr++)
			{
				storage.wipeShield[layerNr] = storage.wipeShield[layerNr].CreateUnion(storage.wipeShield[layerNr - 1].Offset(-offsetAngle));
			}

			for (int layerNr = totalLayers - 1; layerNr > 0; layerNr--)
			{
				storage.wipeShield[layerNr - 1] = storage.wipeShield[layerNr - 1].CreateUnion(storage.wipeShield[layerNr].Offset(-offsetAngle));
			}
		}
示例#2
0
        public static void GenerateRaftOutlines(SliceDataStorage storage, int extraDistanceAroundPart_um)
        {
            for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
            {
                if (storage.volumes[volumeIdx].layers.Count < 1)
                {
                    continue;
                }

                SliceLayer layer = storage.volumes[volumeIdx].layers[0];
                // let's find the first layer that has something in it for the raft rather than a zero layer
                if (layer.parts.Count == 0 && storage.volumes[volumeIdx].layers.Count > 2)
                {
                    layer = storage.volumes[volumeIdx].layers[1];
                }
                for (int i = 0; i < layer.parts.Count; i++)
                {
                    storage.raftOutline = storage.raftOutline.CreateUnion(layer.parts[i].outline.Offset(extraDistanceAroundPart_um));
                }
            }

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

            storage.raftOutline = storage.raftOutline.CreateUnion(storage.wipeTower.Offset(extraDistanceAroundPart_um));
            storage.raftOutline = storage.raftOutline.CreateUnion(supportGenerator.supportPolygons.Offset(extraDistanceAroundPart_um));
        }
示例#3
0
        public static void DumpLayerparts(SliceDataStorage storage, string filename)
        {
            StreamWriter streamToWriteTo = new StreamWriter(filename);
            streamToWriteTo.Write("<!DOCTYPE html><html><body>");
            Point3 modelSize = storage.modelSize;
            Point3 modelMin = storage.modelMin;

            for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
            {
                for (int layerNr = 0; layerNr < storage.volumes[volumeIdx].layers.Count; layerNr++)
                {
                    streamToWriteTo.Write("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
                    SliceLayer layer = storage.volumes[volumeIdx].layers[layerNr];
                    for (int i = 0; i < layer.parts.Count; i++)
                    {
                        SliceLayerPart part = layer.parts[i];
                        for (int j = 0; j < part.TotalOutline.Count; j++)
                        {
                            streamToWriteTo.Write("<polygon points=\"");
                            for (int k = 0; k < part.TotalOutline[j].Count; k++)
                                streamToWriteTo.Write("{0},{1} ".FormatWith((float)(part.TotalOutline[j][k].X - modelMin.x) / modelSize.x * 500, (float)(part.TotalOutline[j][k].Y - modelMin.y) / modelSize.y * 500));
                            if (j == 0)
                                streamToWriteTo.Write("\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
                            else
                                streamToWriteTo.Write("\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
                        }
                    }
                    streamToWriteTo.Write("</svg>\n");
                }
            }
            streamToWriteTo.Write("</body></html>");
            streamToWriteTo.Close();
        }
示例#4
0
        public static void generateSkirt(SliceDataStorage storage, int distance, int extrusionWidth_um, int numberOfLoops, int minLength, int initialLayerHeight)
        {
            bool externalOnly = (distance > 0);

            for (int skirtLoop = 0; skirtLoop < numberOfLoops; skirtLoop++)
            {
                int offsetDistance = distance + extrusionWidth_um * skirtLoop + extrusionWidth_um / 2;

                Polygons skirtPolygons = new Polygons(storage.wipeTower.Offset(offsetDistance));
                for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
                {
                    if (storage.volumes[volumeIndex].layers.Count < 1)
                    {
                        continue;
                    }

                    SliceLayer layer = storage.volumes[volumeIndex].layers[0];
                    for (int i = 0; i < layer.parts.Count; i++)
                    {
                        if (externalOnly)
                        {
                            Polygons p = new Polygons();
                            p.Add(layer.parts[i].outline[0]);
                            skirtPolygons = skirtPolygons.CreateUnion(p.Offset(offsetDistance));
                        }
                        else
                        {
                            skirtPolygons = skirtPolygons.CreateUnion(layer.parts[i].outline.Offset(offsetDistance));
                        }
                    }
                }

                SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, initialLayerHeight);
                skirtPolygons = skirtPolygons.CreateUnion(supportGenerator.supportPolygons.Offset(offsetDistance));

                //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
                for (int n = 0; n < skirtPolygons.Count; n++)
                {
                    double area = skirtPolygons[n].Area();
                    if (area < 0 && area > -extrusionWidth_um * extrusionWidth_um * 100)
                    {
                        skirtPolygons.RemoveAt(n--);
                    }
                }

                storage.skirt.AddAll(skirtPolygons);

                int lenght = (int)storage.skirt.polygonLength();
                if (skirtLoop + 1 >= numberOfLoops && lenght > 0 && lenght < minLength)
                {
                    // add more loops for as long as we have not extruded enough length
                    numberOfLoops++;
                }
            }
        }
示例#5
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);
        }
示例#6
0
		private void sliceModels(SliceDataStorage storage)
		{
			timeKeeper.Restart();
#if false
            optomizedModel.saveDebugSTL("debug_output.stl");
#endif

			LogOutput.Log("Slicing model...\n");
			List<Slicer> slicerList = new List<Slicer>();
			for (int volumeIndex = 0; volumeIndex < optomizedModel.volumes.Count; volumeIndex++)
			{
				Slicer slicer = new Slicer(optomizedModel.volumes[volumeIndex], config);
				slicerList.Add(slicer);
			}

#if false
            slicerList[0].DumpSegmentsToGcode("Volume 0 Segments.gcode");
            slicerList[0].DumpPolygonsToGcode("Volume 0 Polygons.gcode");
            //slicerList[0].DumpPolygonsToHTML("Volume 0 Polygons.html");
#endif

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

			LogOutput.Log("Generating support map...\n");
			storage.support.GenerateSupportGrid(optomizedModel, config);

			storage.modelSize = optomizedModel.size_um;
			storage.modelMin = optomizedModel.minXYZ_um;
			storage.modelMax = optomizedModel.maxXYZ_um;

			LogOutput.Log("Generating layer parts...\n");
			for (int volumeIndex = 0; volumeIndex < slicerList.Count; volumeIndex++)
			{
				storage.volumes.Add(new SliceVolumeStorage());
				LayerPart.CreateLayerParts(storage.volumes[volumeIndex], slicerList[volumeIndex], config.repairOverlaps);

				if (config.enableRaft)
				{
					//Add the raft offset to each layer.
					for (int layerNr = 0; layerNr < storage.volumes[volumeIndex].layers.Count; layerNr++)
					{
						storage.volumes[volumeIndex].layers[layerNr].printZ += config.raftBaseThickness_um + config.raftInterfaceThicknes_um;
					}
				}
			}
			LogOutput.Log("Generated layer parts in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds));
			timeKeeper.Restart();
		}
示例#7
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()]);
		}
示例#8
0
        public static void GenerateRaftOutlines(SliceDataStorage storage, int extraDistanceAroundPart_um)
        {
            for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
            {
                if (storage.volumes[volumeIdx].layers.Count < 1)
                {
                    continue;
                }

                SliceLayer layer = storage.volumes[volumeIdx].layers[0];
                for (int i = 0; i < layer.parts.Count; i++)
                {
                    storage.raftOutline = storage.raftOutline.CreateUnion(layer.parts[i].outline.Offset(extraDistanceAroundPart_um));
                }
            }

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

            storage.raftOutline = storage.raftOutline.CreateUnion(storage.wipeTower.Offset(extraDistanceAroundPart_um));
            storage.raftOutline = storage.raftOutline.CreateUnion(supportGenerator.supportPolygons.Offset(extraDistanceAroundPart_um));
        }
示例#9
0
        public static void dumpLayerparts(SliceDataStorage storage, string filename)
        {
            StreamWriter streamToWriteTo = new StreamWriter(filename);

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

            for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
            {
                for (int layerNr = 0; layerNr < storage.volumes[volumeIdx].layers.Count; layerNr++)
                {
                    streamToWriteTo.Write("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
                    SliceLayer layer = storage.volumes[volumeIdx].layers[layerNr];
                    for (int i = 0; i < layer.parts.Count; i++)
                    {
                        SliceLayerPart part = layer.parts[i];
                        for (int j = 0; j < part.outline.Count; j++)
                        {
                            streamToWriteTo.Write("<polygon points=\"");
                            for (int k = 0; k < part.outline[j].Count; k++)
                            {
                                streamToWriteTo.Write("{0},{1} ".FormatWith((float)(part.outline[j][k].X - modelMin.x) / modelSize.x * 500, (float)(part.outline[j][k].Y - modelMin.y) / modelSize.y * 500));
                            }
                            if (j == 0)
                            {
                                streamToWriteTo.Write("\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
                            }
                            else
                            {
                                streamToWriteTo.Write("\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
                            }
                        }
                    }
                    streamToWriteTo.Write("</svg>\n");
                }
            }
            streamToWriteTo.Write("</body></html>");
            streamToWriteTo.Close();
        }
示例#10
0
        public static void GenerateRaftOutlines(SliceDataStorage storage, int extraDistanceAroundPart_um, ConfigSettings config)
        {
            for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
            {
                if (config.continuousSpiralOuterPerimeter && volumeIndex > 0)
                {
                    continue;
                }

                if (storage.volumes[volumeIndex].layers.Count < 1)
                {
                    continue;
                }

                SliceLayer layer = storage.volumes[volumeIndex].layers[0];
                // let's find the first layer that has something in it for the raft rather than a zero layer
                if (layer.parts.Count == 0 && storage.volumes[volumeIndex].layers.Count > 2)
                {
                    layer = storage.volumes[volumeIndex].layers[1];
                }
                for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
                {
                    if (config.continuousSpiralOuterPerimeter && partIndex > 0)
                    {
                        continue;
                    }

                    storage.raftOutline = storage.raftOutline.CreateUnion(layer.parts[partIndex].TotalOutline.Offset(extraDistanceAroundPart_um));
                }
            }

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

            storage.raftOutline = storage.raftOutline.CreateUnion(storage.wipeTower.Offset(extraDistanceAroundPart_um));
            storage.raftOutline = storage.raftOutline.CreateUnion(supportGenerator.supportPolygons.Offset(extraDistanceAroundPart_um));
        }
示例#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
        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()]);
        }
示例#13
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);
        }
示例#14
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);
        }
示例#15
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);
        }
示例#16
0
        private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, int extrusionWidth_um, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons)
        {
            // generate infill for outline including bridging
            foreach (Polygons outline in part.skinOutline.SplitIntoParts())
            {
                double partFillAngle = config.infillStartingAngle;
                if ((layerIndex & 1) == 1)
                {
                    partFillAngle += 90;
                }
                if (layerIndex > 0)
                {
                    double bridgeAngle;
                    if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle))
                    {
                        Infill.GenerateLinePaths(outline, ref bridgePolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle);
                    }
                }
                else
                {
                    Infill.GenerateLinePaths(outline, ref fillPolygons, extrusionWidth_um, config.infillExtendIntoPerimeter_um, partFillAngle);
                }
            }

            double fillAngle = config.infillStartingAngle;

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

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

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

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

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

                default:
                    throw new NotImplementedException();
                }
            }
        }
示例#17
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);
                }
            }
        }
示例#18
0
        private void processSliceData(SliceDataStorage storage)
        {
            if (config.continuousSpiralOuterPerimeter)
            {
                config.numberOfTopLayers = 0;
                config.infillPercent = 0;
            }

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

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

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

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

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

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

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

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

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

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

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

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

                Skirt.generateSkirt(storage,
                    config.skirtDistance_um + config.raftBaseLineSpacing_um,
                    config.raftBaseLineSpacing_um,
                    config.numberOfSkirtLoops,
                    config.skirtMinLength_um,
                    config.raftBaseThickness_um, config);
            }
            else
            {
                Skirt.generateSkirt(storage,
                    config.skirtDistance_um,
                    config.firstLayerExtrusionWidth_um,
                    config.numberOfSkirtLoops,
                    config.skirtMinLength_um,
                    config.firstLayerThickness_um, config);
            }
        }
示例#19
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);
		}
示例#20
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);
        }
示例#21
0
        public static void generateSkirt(SliceDataStorage storage, int distance, int extrusionWidth_um, int numberOfLoops, int minLength, int initialLayerHeight, ConfigSettings config)
        {
            bool externalOnly = (distance > 0);
            for (int skirtLoop = 0; skirtLoop < numberOfLoops; skirtLoop++)
            {
                int offsetDistance = distance + extrusionWidth_um * skirtLoop + extrusionWidth_um / 2;

                Polygons skirtPolygons = new Polygons(storage.wipeTower.Offset(offsetDistance));
                for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
                {
                    if (config.continuousSpiralOuterPerimeter && volumeIndex > 0)
                    {
                        continue;
                    }

                    if (storage.volumes[volumeIndex].layers.Count < 1)
                    {
                        continue;
                    }

                    SliceLayer layer = storage.volumes[volumeIndex].layers[0];
                    for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
                    {
                        if (config.continuousSpiralOuterPerimeter && partIndex > 0)
                        {
                            continue;
                        }

                        if (externalOnly)
                        {
                            Polygons p = new Polygons();
                            p.Add(layer.parts[partIndex].TotalOutline[0]);
                            //p.Add(IntPointHelper.CreateConvexHull(layer.parts[partIndex].outline[0]));
                            skirtPolygons = skirtPolygons.CreateUnion(p.Offset(offsetDistance));
                        }
                        else
                        {
                            skirtPolygons = skirtPolygons.CreateUnion(layer.parts[partIndex].TotalOutline.Offset(offsetDistance));
                        }
                    }
                }

                SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, initialLayerHeight);
                skirtPolygons = skirtPolygons.CreateUnion(supportGenerator.supportPolygons.Offset(offsetDistance));

                //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
                for (int n = 0; n < skirtPolygons.Count; n++)
                {
                    double area = skirtPolygons[n].Area();
                    if (area < 0 && area > -extrusionWidth_um * extrusionWidth_um * 100)
                    {
                        skirtPolygons.RemoveAt(n--);
                    }
                }

                storage.skirt.AddAll(skirtPolygons);

                int lenght = (int)storage.skirt.PolygonLength();
                if (skirtLoop + 1 >= numberOfLoops && lenght > 0 && lenght < minLength)
                {
                    // add more loops for as long as we have not extruded enough length
                    numberOfLoops++;
                }
            }
        }
示例#22
0
		private void processSliceData(SliceDataStorage storage)
		{
			if (config.continuousSpiralOuterPerimeter)
			{
				config.numberOfTopLayers = 0;
				config.infillPercent = 0;
			}

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

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

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

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

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

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

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

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

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

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

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

				Skirt.generateSkirt(storage,
					config.skirtDistance_um + config.raftBaseLineSpacing_um,
					config.raftBaseLineSpacing_um,
					config.numberOfSkirtLoops,
					config.skirtMinLength_um,
					config.raftBaseThickness_um, config);
			}
			else
			{
				Skirt.generateSkirt(storage,
					config.skirtDistance_um,
					config.firstLayerExtrusionWidth_um,
					config.numberOfSkirtLoops,
					config.skirtMinLength_um,
					config.firstLayerThickness_um, config);
			}
		}
示例#23
0
        public static void GenerateRaftOutlines(SliceDataStorage storage, int extraDistanceAroundPart_um, ConfigSettings config)
        {
            for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++)
            {
                if (config.continuousSpiralOuterPerimeter && volumeIndex > 0)
                {
                    continue;
                }

                if (storage.volumes[volumeIndex].layers.Count < 1)
                {
                    continue;
                }

                SliceLayer layer = storage.volumes[volumeIndex].layers[0];
                // let's find the first layer that has something in it for the raft rather than a zero layer
                if (layer.parts.Count == 0 && storage.volumes[volumeIndex].layers.Count > 2) layer = storage.volumes[volumeIndex].layers[1];
                for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
                {
                    if (config.continuousSpiralOuterPerimeter && partIndex > 0)
                    {
                        continue;
                    }

                    storage.raftOutline = storage.raftOutline.CreateUnion(layer.parts[partIndex].TotalOutline.Offset(extraDistanceAroundPart_um));
                }
            }

            SupportPolyGenerator supportGenerator = new SupportPolyGenerator(storage.support, 0);
            storage.raftOutline = storage.raftOutline.CreateUnion(storage.wipeTower.Offset(extraDistanceAroundPart_um));
            storage.raftOutline = storage.raftOutline.CreateUnion(supportGenerator.supportPolygons.Offset(extraDistanceAroundPart_um));
        }
示例#24
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);

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

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

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

                    // write the inside of the raft base
                    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);
                }
            }
        }
示例#25
0
        private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons bridgePolygons)
        {
            // generate infill the bottom layers including bridging
            foreach (Polygons outline in part.SolidBottomOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd))
            {
                if (layerIndex > 0)
                {
                    double bridgeAngle;
                    if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle))
                    {
                        Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
                    }
                    else
                    {
                        Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
                    }
                }
                else
                {
                    Infill.GenerateLinePaths(outline, ref fillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
                }
            }

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

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

            double fillAngle = config.infillStartingAngle;

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

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

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

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

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

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

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

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

			double fillAngle = config.infillStartingAngle;

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

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

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

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

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

					default:
						throw new NotImplementedException();
				}
			}
		}
示例#27
0
        private void CreateWipeShields(SliceDataStorage storage, int totalLayers)
        {
            for (int layerNr = 0; layerNr < totalLayers; layerNr++)
            {
                Polygons wipeShield = new Polygons();
                for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++)
                {
                    for (int partNr = 0; partNr < storage.volumes[volumeIdx].layers[layerNr].parts.Count; partNr++)
                    {
                        wipeShield = wipeShield.CreateUnion(storage.volumes[volumeIdx].layers[layerNr].parts[partNr].TotalOutline.Offset(config.wipeShieldDistanceFromShapes_um));
                    }
                }
                storage.wipeShield.Add(wipeShield);
            }

            for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
            {
                storage.wipeShield[layerIndex] = storage.wipeShield[layerIndex].Offset(-1000).Offset(1000);
            }

            int offsetAngle = (int)Math.Tan(60.0 * Math.PI / 180) * config.layerThickness_um;//Allow for a 60deg angle in the wipeShield.
            for (int layerNr = 1; layerNr < totalLayers; layerNr++)
            {
                storage.wipeShield[layerNr] = storage.wipeShield[layerNr].CreateUnion(storage.wipeShield[layerNr - 1].Offset(-offsetAngle));
            }

            for (int layerNr = totalLayers - 1; layerNr > 0; layerNr--)
            {
                storage.wipeShield[layerNr - 1] = storage.wipeShield[layerNr - 1].CreateUnion(storage.wipeShield[layerNr].Offset(-offsetAngle));
            }
        }
示例#28
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);
			}
		}
示例#29
0
        private void sliceModels(SliceDataStorage storage)
        {
            timeKeeper.Restart();
            #if false
            optomizedModel.saveDebugSTL("debug_output.stl");
            #endif

            LogOutput.Log("Slicing model...\n");
            List<Slicer> slicerList = new List<Slicer>();
            for (int volumeIndex = 0; volumeIndex < optomizedModel.volumes.Count; volumeIndex++)
            {
                Slicer slicer = new Slicer(optomizedModel.volumes[volumeIndex], config);
                slicerList.Add(slicer);
            }

            #if false
            slicerList[0].DumpSegmentsToGcode("Volume 0 Segments.gcode");
            slicerList[0].DumpPolygonsToGcode("Volume 0 Polygons.gcode");
            //slicerList[0].DumpPolygonsToHTML("Volume 0 Polygons.html");
            #endif

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

            LogOutput.Log("Generating support map...\n");
            storage.support.GenerateSupportGrid(optomizedModel, config);

            storage.modelSize = optomizedModel.size_um;
            storage.modelMin = optomizedModel.minXYZ_um;
            storage.modelMax = optomizedModel.maxXYZ_um;

            LogOutput.Log("Generating layer parts...\n");
            for (int volumeIndex = 0; volumeIndex < slicerList.Count; volumeIndex++)
            {
                storage.volumes.Add(new SliceVolumeStorage());
                LayerPart.CreateLayerParts(storage.volumes[volumeIndex], slicerList[volumeIndex], config.repairOverlaps);

                if (config.enableRaft)
                {
                    //Add the raft offset to each layer.
                    for (int layerNr = 0; layerNr < storage.volumes[volumeIndex].layers.Count; layerNr++)
                    {
                        storage.volumes[volumeIndex].layers[layerNr].printZ += config.raftBaseThickness_um + config.raftInterfaceThicknes_um;
                    }
                }
            }
            LogOutput.Log("Generated layer parts in {0:0.0}s\n".FormatWith(timeKeeper.Elapsed.Seconds));
            timeKeeper.Restart();
        }
示例#30
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);
                }
            }
        }
示例#31
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);
            }
        }
示例#32
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);
		}