Exemple #1
0
		public NewSupport(ConfigSettings config, List<ExtruderLayers> Extruders, double grabDistanceMm)
		{
			long supportWidth_um = (long)(config.extrusionWidth_um * (100-config.supportPercent) / 100);
            this.grabDistanceMm = grabDistanceMm;
			// create starting support outlines
			allPartOutlines = CalculateAllPartOutlines(config, Extruders);

			allPotentialSupportOutlines = FindAllPotentialSupportOutlines(allPartOutlines, supportWidth_um);

			allRequiredSupportOutlines = RemoveSelfSupportedSections(allPotentialSupportOutlines, supportWidth_um);

			if (!config.generateInternalSupport)
			{
				allRequiredSupportOutlines = RemoveSupportFromInternalSpaces(allRequiredSupportOutlines, allPartOutlines);
			}

			easyGrabDistanceOutlines = ExpandToEasyGrabDistance(allRequiredSupportOutlines, (int)(grabDistanceMm * 1000));

			//pushedUpTopOutlines = PushUpTops(easyGrabDistanceOutlines, numLayers, config);

			interfaceLayers = CreateInterfaceLayers(easyGrabDistanceOutlines, config.supportInterfaceLayers);
			interfaceLayers = ClipToXyDistance(interfaceLayers, allPartOutlines, config);

			supportOutlines = AccumulateDownPolygons(config, easyGrabDistanceOutlines, allPartOutlines);
			supportOutlines = ClipToXyDistance(supportOutlines, allPartOutlines, config);

			// remove the interface layers from the normal support layers
			supportOutlines = CalculateDifferencePerLayer(supportOutlines, interfaceLayers);

			airGappedBottomOutlines = CreateAirGappedBottomLayers(supportOutlines, allPartOutlines);
			// remove the airGappedBottomOutlines layers from the normal support layers
			supportOutlines = CalculateDifferencePerLayer(supportOutlines, airGappedBottomOutlines);
		}
		public void GenerateRaftOutlines(int extraDistanceAroundPart_um, ConfigSettings config)
		{
			LayerDataStorage storage = this;
			for (int extruderIndex = 0; extruderIndex < storage.Extruders.Count; extruderIndex++)
			{
				if (config.continuousSpiralOuterPerimeter && extruderIndex > 0)
				{
					continue;
				}

				if (storage.Extruders[extruderIndex].Layers.Count < 1)
				{
					continue;
				}

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

					storage.raftOutline = storage.raftOutline.CreateUnion(layer.Islands[partIndex].IslandOutline.Offset(extraDistanceAroundPart_um));
				}
			}

			storage.raftOutline = storage.raftOutline.CreateUnion(storage.wipeTower.Offset(extraDistanceAroundPart_um));
			if (storage.support != null)
			{
				storage.raftOutline = storage.raftOutline.CreateUnion(storage.support.GetBedOutlines().Offset(extraDistanceAroundPart_um));
			}
		}
Exemple #3
0
        public static void GenerateConcentricInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, long extrusionWidthOverride_um = 0)
        {
            if (extrusionWidthOverride_um == 0)
            {
                extrusionWidthOverride_um = config.ExtrusionWidth_um;
            }

            Polygons outlineCopy = new Polygons(partOutline);
            foreach (Polygon outline in outlineCopy)
            {
                if (outline.Count > 0)
                {
                    outline.Add(outline[0]);
                }
            }
            int linespacing_um = (int)(extrusionWidthOverride_um / (config.InfillPercent / 100));
            while (outlineCopy.Count > 0)
            {
                for (int outlineIndex = 0; outlineIndex < outlineCopy.Count; outlineIndex++)
                {
                    Polygon r = outlineCopy[outlineIndex];
                    fillPolygons.Add(r);
                }
                outlineCopy = outlineCopy.Offset(-linespacing_um);
                foreach (Polygon outline in outlineCopy)
                {
                    if (outline.Count > 0)
                    {
                        outline.Add(outline[0]);
                    }
                }
            }
        }
Exemple #4
0
        public static void GenerateGridInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, double fillAngle, int linespacing_um = 0)
        {
            if (linespacing_um == 0)
            {
                if (config.InfillPercent <= 0)
                {
                    throw new Exception("infillPercent must be greater than 0.");
                }
                linespacing_um = (int)(config.ExtrusionWidth_um / (config.InfillPercent / 100) * 2);
            }

            Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle);

            fillAngle += 90;
            if (fillAngle > 360)
            {
                fillAngle -= 360;
            }

            Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle);
        }
        public void CreateWipeShield(int totalLayers, ConfigSettings config)
        {
            if (config.WipeShieldDistanceFromShapes_um <= 0)
            {
                return;
            }

            for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
            {
                Polygons wipeShield = new Polygons();
                for (int extruderIndex = 0; extruderIndex < this.Extruders.Count; extruderIndex++)
                {
                    for (int islandIndex = 0; islandIndex < this.Extruders[extruderIndex].Layers[layerIndex].Islands.Count; islandIndex++)
                    {
                        wipeShield = wipeShield.CreateUnion(this.Extruders[extruderIndex].Layers[layerIndex].Islands[islandIndex].IslandOutline.Offset(config.WipeShieldDistanceFromShapes_um));
                    }
                }
                this.wipeShield.Add(wipeShield);
            }

            for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++)
            {
                this.wipeShield[layerIndex] = this.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 layerIndex = 1; layerIndex < totalLayers; layerIndex++)
            {
                this.wipeShield[layerIndex] = this.wipeShield[layerIndex].CreateUnion(this.wipeShield[layerIndex - 1].Offset(-offsetAngle));
            }

            for (int layerIndex = totalLayers - 1; layerIndex > 0; layerIndex--)
            {
                this.wipeShield[layerIndex - 1] = this.wipeShield[layerIndex - 1].CreateUnion(this.wipeShield[layerIndex].Offset(-offsetAngle));
            }
        }
Exemple #6
0
		private static List<Polygons> PushUpTops(List<Polygons> inputPolys, ConfigSettings config)
		{
			int numLayers = inputPolys.Count;

			return inputPolys;
			int layersFor2Mm = 2000 / config.layerThickness_um;
			List<Polygons> pushedUpPolys = CreateEmptyPolygons(numLayers);
			for (int layerIndex = numLayers - 1; layerIndex >= 0; layerIndex--)
			{
				for (int layerToAddToIndex = Math.Min(layerIndex + layersFor2Mm, numLayers - 1); layerToAddToIndex >= 0; layerToAddToIndex--)
				{
				}

				Polygons curLayerPolys = inputPolys[layerIndex];
				pushedUpPolys[layerIndex] = Clipper.CleanPolygons(curLayerPolys.Offset(config.extrusionWidth_um + config.supportXYDistance_um), cleanDistance_um);
			}

			return pushedUpPolys;
		}
Exemple #7
0
		private static List<Polygons> ClipToXyDistance(List<Polygons> inputPolys, List<Polygons> allPartOutlines, ConfigSettings config)
		{
			int numLayers = inputPolys.Count;

			List<Polygons> clippedToXyOutlines = CreateEmptyPolygons(numLayers);
			for (int layerIndex = numLayers - 2; layerIndex >= 0; layerIndex--)
			{
				Polygons curRequiredSupport = inputPolys[layerIndex];
				Polygons expandedlayerPolys = allPartOutlines[layerIndex].Offset(config.supportXYDistance_um);
				Polygons totalSupportThisLayer = curRequiredSupport.CreateDifference(expandedlayerPolys);

				clippedToXyOutlines[layerIndex] = Clipper.CleanPolygons(totalSupportThisLayer, cleanDistance_um);
			}

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

            SliceLayer layer = storage.layers[layerIndex];

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

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

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

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

                    part.SolidBottomOutlines = bottomOutlines;
                }

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

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

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

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

                    part.SolidTopOutlines = topOutlines;
                }

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

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

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

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

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

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

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

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

                    Polygons totalInfillOutlines = null;
                    double totalInfillArea = 0.0;

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

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

                RemoveSmallAreas(extrusionWidth, infillOutlines);
                infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                part.InfillOutlines = infillOutlines;
            }
        }
Exemple #9
0
        private Polygons MakeFuzzy(Polygons polygons, Polygons fuzzyBounds, ConfigSettings config)
        {
            if (polygons.Count < 1 ||
                fuzzyBounds?.Count > 0 != true)
            {
                return(polygons);
            }

            var fuzziness      = config.FuzzyThickness_um;
            var fuzzyFrequency = config.FuzzyFrequency_um;
            // min 3/4 max 5/4
            var minDist  = fuzzyFrequency * 3 / 4;
            var variance = fuzzyFrequency / 2;

            var fuzzyPolygons = new Polygons();

            foreach (var polygon in polygons)
            {
                var fuzzyPolygon = new Polygon();
                fuzzyPolygons.Add(fuzzyPolygon);

                // get every line segment including the last
                for (int pointIndex = 0; pointIndex < polygon.Count; pointIndex++)
                {
                    var nextIndex = (pointIndex + 1) % polygon.Count;
                    // create the line segment
                    var start = polygon[pointIndex];
                    var line  = new Polygon()
                    {
                        start, polygon[nextIndex]
                    };
                    var outsideLines = fuzzyBounds.CreateLineDifference(new Polygons()
                    {
                        line
                    });
                    if (outsideLines.Count == 1 &&
                        ((outsideLines[0][0] == line[0] &&
                          outsideLines[0][1] == line[1]) ||
                         (outsideLines[0][0] == line[1] &&
                          outsideLines[0][1] == line[0])))
                    {
                        // it is not in the area needing fuzzing, add it directly
                        fuzzyPolygon.Add(line[0]);
                        fuzzyPolygon.Add(line[1]);
                    }
                    else
                    {
                        Polygon SortPoints(Polygon poly)
                        {
                            if ((poly[0] - start).LengthSquared() > (poly[1] - start).LengthSquared())
                            {
                                var hold = poly[0];
                                poly[0] = poly[1];
                                poly[1] = hold;
                            }

                            return(poly);
                        }

                        var allLines = new List <(Polygon poly, bool outside)>();
                        foreach (var outside in outsideLines)
                        {
                            allLines.Add((SortPoints(outside), true));
                        }
                        var insideLines = fuzzyBounds.CreateLineIntersections(new Polygons()
                        {
                            line
                        });
                        foreach (var inside in insideLines)
                        {
                            allLines.Add((SortPoints(inside), false));
                        }


                        // sort the segments along the line direction
                        allLines.Sort((a, b) =>
                        {
                            var distToA = (a.poly[0] - start).LengthSquared();
                            var distToB = (b.poly[0] - start).LengthSquared();
                            return(distToA.CompareTo(distToB));
                        });

                        // iterate over all the sorted segments
                        foreach (var(poly, outside) in allLines)
                        {
                            if (outside)
                            {
                                // just add it
                                fuzzyPolygon.Add(poly[0]);
                                fuzzyPolygon.Add(poly[1]);
                            }
                            else
                            {
                                // if it does not need fuzzing add it to the output
                                // else fuzz the segments and add all the fuzzed pieces

                                // generate points in between p0 and p1
                                var dist_left_over = (minDist / 4) + rand.Next() % (minDist / 4);                                 // the distance to be traversed on the line before making the first new point
                                var p0             = poly[0];
                                var p1             = poly[1];

                                var p0p1      = p1 - p0;
                                var p0p1_size = p0p1.Length();
                                var p0pa_dist = dist_left_over;
                                if (p0pa_dist >= p0p1_size)
                                {
                                    fuzzyPolygon.Add(p1 - (p0p1 / 2));
                                }

                                for (; p0pa_dist < p0p1_size; p0pa_dist += minDist + rand.Next() % variance)
                                {
                                    var r            = rand.Next() % (fuzziness * 2) - fuzziness;
                                    var perp_to_p0p1 = p0p1.GetPerpendicularLeftXY();
                                    var fuzz         = perp_to_p0p1.Normal(r);
                                    fuzzyPolygon.Add(p0 + p0p1.Normal(p0pa_dist) + fuzz);
                                }
                            }
                        }
                    }
                }
            }

            return(fuzzyPolygons);
        }
Exemple #10
0
		public void GenerateFillConsideringBridging(Polygons bottomFillIsland, ref Polygons bottomFillLines, ConfigSettings config, string debugName = "")
		{
			double bridgeAngle = 0;
			if (this.BridgeAngle(bottomFillIsland, out bridgeAngle))
			{
				// TODO: Make this code handle very complex pathing between different sizes or layouts of support under the island to fill.
				Infill.GenerateLinePaths(bottomFillIsland, ref bottomFillLines, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle);
			}
			else
			{
				Infill.GenerateLinePaths(bottomFillIsland, ref bottomFillLines, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle);
			}
		}
        public static int ProcessArgs(string[] args, ConfigSettings config, fffProcessor processor)
        {
            for (int argn = 0; argn < args.Length; argn++)
            {
                string str = args[argn];
                if (str[0] == '-')
                {
                    for (int stringIndex = 1; stringIndex < str.Length; stringIndex++)
                    {
                        switch (str[stringIndex])
                        {
                        case 'h':
                            print_usage();
                            return(0);

                        case 'v':
                            LogOutput.verbose_level++;
                            break;

                        case 'o':
                            argn++;
                            if (!processor.SetTargetFile(args[argn]))
                            {
                                LogOutput.LogError("Failed to open {0} for output.\n".FormatWith(args[argn]));
                                return(1);
                            }
                            break;

                        case 'c':
                        {
                            // Read a config file from the given path
                            argn++;
                            if (!config.ReadSettings(args[argn]))
                            {
                                LogOutput.LogError("Failed to read config '{0}'\n".FormatWith(args[argn]));
                            }

                            // process any matrix and mesh requested by config file
                            List <string> commands = new List <string>();
                            foreach (string command in SplitCommandLine.DoSplit(config.AdditionalArgsToProcess))
                            {
                                commands.Add(command);
                            }
                            string[] subArgs = commands.ToArray();
                            ProcessArgs(subArgs, config, processor);
                        }
                        break;

                        case 'd':
                            config.DumpSettings("settings.ini");
                            break;

                        case 's':
                        {
                            argn++;
                            int equalsPos = args[argn].IndexOf('=');
                            if (equalsPos != -1)
                            {
                                string key   = args[argn].Substring(0, equalsPos);
                                string value = args[argn].Substring(equalsPos + 1);
                                if (key.Length > 1)
                                {
                                    if (!config.SetSetting(key, value))
                                    {
                                        LogOutput.LogError("Setting not found: {0} {1}\n".FormatWith(key, value));
                                    }
                                }
                            }
                        }
                        break;

                        case 'm':
                            argn++;
                            string[] matrixValues = args[argn].Split(',');
                            var      loadedMatrix = Matrix4X4.Identity;
                            for (int i = 0; i < 4; i++)
                            {
                                for (int j = 0; j < 4; j++)
                                {
                                    string valueString = matrixValues[i * 4 + j];
                                    double value;
                                    if (double.TryParse(valueString, out value))
                                    {
                                        loadedMatrix[i, j] = value;
                                    }
                                }
                            }
                            config.ModelMatrix = loadedMatrix;
                            break;

                        default:
                            throw new NotImplementedException("Unknown option: {0}\n".FormatWith(str));
                            //LogOutput.logError("Unknown option: {0}\n".FormatWith(str));
                            //break;
                        }
                    }
                }
                else
                {
                    processor.LoadStlFile(args[argn]);
                }
            }

            return(1);
        }
        public void GenerateFillConsideringBridging(Polygons bottomFillIsland, Polygons bottomFillLines, ConfigSettings config, Polygons bridgePolygons, string debugName = "")
        {
            double bridgeAngle = 0;

            if (bridgePolygons != null && this.BridgeAngle(bottomFillIsland, out bridgeAngle))
            {
                // TODO: Make this code handle very complex pathing between different sizes or layouts of support under the island to fill.
                Infill.GenerateLinePaths(bottomFillIsland, bridgePolygons, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, bridgeAngle);
            }
            else
            {
                Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, config.InfillStartingAngle);
            }
        }
Exemple #13
0
        public static void RemoveExtruderIntersections(List <ExtruderLayers> extruders, ConfigSettings config)
        {
            var start = extruders.Count - 1;

            if (config.BooleanOperations.Contains("F"))
            {
                start--;
            }

            // Go trough all the extruders, and remove the previous extruders outlines from our own outline, so we never have overlapped areas.
            for (int extruderIndex = start; extruderIndex >= 0; extruderIndex--)
            {
                for (int otherExtruderIndex = extruderIndex - 1; otherExtruderIndex >= 0; otherExtruderIndex--)
                {
                    for (int layerIndex = 0; layerIndex < extruders[extruderIndex].Layers.Count; layerIndex++)
                    {
                        SliceLayer layerToRemoveFrom = extruders[extruderIndex].Layers[layerIndex];
                        SliceLayer layerToRemove     = extruders[otherExtruderIndex].Layers[layerIndex];
                        layerToRemoveFrom.AllOutlines = layerToRemoveFrom.AllOutlines.CreateDifference(layerToRemove.AllOutlines);
                    }
                }
            }
        }
        private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, long distance_um, long extrusionWidth_um, int brimCount)
        {
            bool hasWipeTower = storage.WipeLayer(0).PolygonLength() > 0;

            var skirtPolygons = new Polygons();

            if (config.EnableRaft)
            {
                skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline);
            }
            else
            {
                var allOutlines = hasWipeTower ? new Polygons(storage.WipeLayer(0).Offset(-extrusionWidth_um / 2)) : new Polygons();

                if (storage.WipeShield.Count > 0 &&
                    storage.WipeShield[0].Count > 0)
                {
                    allOutlines = allOutlines.CreateUnion(storage.WipeShield[0].Offset(-extrusionWidth_um / 2));
                }

                // Loop over every extruder
                for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
                {
                    // Only process the first extruder on spiral vase or
                    // skip extruders that have empty layers
                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0];
                        if (layer0.Islands.Count > 0)
                        {
                            allOutlines.AddAll(layer0.Islands[0]?.IslandOutline);
                        }
                    }
                    else
                    {
                        // Add the layers outline to allOutlines
                        SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
                        foreach (var island in layer.Islands)
                        {
                            if (island.IslandOutline?.Count > 0)
                            {
                                allOutlines.Add(island.IslandOutline[0]);
                            }
                        }
                    }
                }

                if (brimCount > 0)
                {
                    Polygons brimIslandOutlines = new Polygons();

                    // Grow each island by the current brim distance
                    // Union the island brims
                    brimIslandOutlines = brimIslandOutlines.CreateUnion(allOutlines);

                    if (storage.Support != null)
                    {
                        brimIslandOutlines = brimIslandOutlines.CreateUnion(storage.Support.GetBedOutlines());
                    }

                    Polygons brimLoops = new Polygons();
                    for (int brimIndex = 0; brimIndex < brimCount; brimIndex++)
                    {
                        // Extend the polygons to account for the brim (ensures convex hull takes this data into account)
                        brimLoops.AddAll(brimIslandOutlines.Offset(extrusionWidth_um * brimIndex + extrusionWidth_um / 2));
                    }

                    storage.Brims.AddAll(brimLoops);

                    // and extend the bounds of the skirt polygons
                    skirtPolygons = skirtPolygons.CreateUnion(brimIslandOutlines.Offset(extrusionWidth_um * brimCount));
                }

                skirtPolygons = skirtPolygons.CreateUnion(allOutlines);

                if (storage.Support != null)
                {
                    skirtPolygons = skirtPolygons.CreateUnion(storage.Support.GetBedOutlines());
                }
            }

            return(skirtPolygons);
        }
Exemple #15
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++;
                }
            }
        }
        public bool PrimeOnWipeTower(int layerIndex, LayerGCodePlanner layerGcodePlanner, PathFinder pathFinder, GCodePathConfig fillConfig, ConfigSettings config, bool airGapped)
        {
            if (!HaveWipeTower(config, layerIndex) ||
                (layerIndex == 0 && !config.EnableRaft))
            {
                return(false);
            }

            if (airGapped)
            {
                // don't print the wipe tower with air gap height
                layerGcodePlanner.CurrentZ -= config.SupportAirGap_um;
            }

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

            GenerateWipeTowerInfill(primesThisLayer, this.WipeLayer(layerIndex), fillPolygons, fillConfig.LineWidth_um, config);

            if (fillPolygons.Count > 0)
            {
                // move over to the wipe tower with the layer planner in place
                layerGcodePlanner.QueueTravel(WipeCenter_um, pathFinder);

                // print the wipe tower with no planning
                layerGcodePlanner.QueuePolygons(fillPolygons, null, fillConfig);

                if (airGapped)
                {
                    // don't print the wipe tower with air gap height
                    layerGcodePlanner.CurrentZ += config.SupportAirGap_um;
                }

                primesThisLayer++;
                return(true);
            }

            return(false);
        }
 private int MaxPrimingLoops(ConfigSettings config)
 {
     return(Support != null ? config.ExtruderCount * 2 - 2 : config.ExtruderCount - 1);
 }
        public bool EnsureWipeTowerIsSolid(int layerIndex, PathFinder pathFinder, LayerGCodePlanner layerGcodePlanner, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (layerIndex >= LastLayerWithChange(config))
            {
                return(false);
            }

            // TODO: if layer index == 0 do all the loops from the outside-in, in order (no lines should be in the wipe tower)
            if (layerIndex == 0 && !config.EnableRaft)
            {
                CheckNoExtruderPrimed(config);

                long insetPerLoop    = fillConfig.LineWidth_um;
                int  maxPrimingLoops = MaxPrimingLoops(config);

                Polygons outlineForExtruder = this.WipeLayer(layerIndex);

                var fillPolygons = new Polygons();
                while (outlineForExtruder.Count > 0)
                {
                    for (int polygonIndex = 0; polygonIndex < outlineForExtruder.Count; polygonIndex++)
                    {
                        Polygon newInset = outlineForExtruder[polygonIndex];
                        newInset.Add(newInset[0]);                         // add in the last move so it is a solid polygon
                        fillPolygons.Add(newInset);
                    }

                    outlineForExtruder = outlineForExtruder.Offset(-insetPerLoop);
                }

                // set the path planner to avoid islands
                if (this.HaveWipeTower(config, layerIndex))
                {
                    layerGcodePlanner.QueueTravel(WipeCenter_um, pathFinder);
                }

                // turn off the planner for the wipe tower
                layerGcodePlanner.QueuePolygons(fillPolygons, null, fillConfig);
            }
            else
            {
                bool allowPartialInfill = false;
                // check if there was a change, if not partial fill the wipe tower
                if (primesThisLayer == 0 &&
                    allowPartialInfill)
                {
                    var outlinePolygons = new Polygons();

                    for (int i = 0; i < config.NumberOfPerimeters; i++)
                    {
                        var insets = this.WipeLayer(layerIndex).Offset(i * -fillConfig.LineWidth_um);
                        foreach (var inset in insets)
                        {
                            outlinePolygons.Add(inset);
                            // Add the first point back onto the polygon
                            outlinePolygons.Last().Add(outlinePolygons.Last()[0]);
                        }
                    }

                    layerGcodePlanner.QueuePolygons(outlinePolygons, null, fillConfig);

                    // print sparse infill on the wipe tower
                    var fillPolygons = new Polygons();

                    Infill.GenerateTriangleInfill(
                        config,
                        this.WipeLayer(layerIndex).Offset(config.NumberOfPerimeters * -fillConfig.LineWidth_um),
                        fillPolygons,
                        config.InfillStartingAngle);

                    layerGcodePlanner.QueuePolygonsByOptimizer(fillPolygons, null, fillConfig, layerIndex);
                }
                else
                {
                    // print all of the extruder loops that have not already been printed
                    int maxPrimingLoops = MaxPrimingLoops(config);

                    for (int primeLoop = primesThisLayer; primeLoop < maxPrimingLoops; primeLoop++)
                    {
                        // write the loops for this extruder, but don't change to it. We are just filling the prime tower.
                        PrimeOnWipeTower(layerIndex, layerGcodePlanner, pathFinder, fillConfig, config, false);
                    }
                }

                // clear the history of printer extruders for the next layer
                primesThisLayer = 0;
            }

            return(true);
        }
Exemple #19
0
        public static void Generate(ConfigSettings config,
                                    Polygons in_outline,
                                    Polygons result_lines,
                                    bool zigZag,
                                    int layerIndex)
        {
            // generate infill based on the gyroid equation: sin_x * cos_y + sin_y * cos_z + sin_z * cos_x = 0
            // kudos to the author of the Slic3r implementation equation code, the equation code here is based on that
            Polygons outline = in_outline.Offset(config.ExtrusionWidth_um / 2);
            var      aabb    = outline.GetBounds();

            var accelerator = new Pathfinding.PathingData(outline, config.ExtrusionWidth_um * 3, true);

            var linespacing_um = (int)(config.ExtrusionWidth_um / (config.InfillPercent / 100));
            var z = layerIndex * config.LayerThickness_um;

            int pitch     = (int)(linespacing_um * 2.41);         // this produces similar density to the "line" infill pattern
            int num_steps = 4;
            int step      = pitch / num_steps;

            while (step > 500 && num_steps < 16)
            {
                num_steps *= 2;
                step       = pitch / num_steps;
            }

            pitch = step * num_steps;             // recalculate to avoid precision errors
            double z_rads           = 2 * Math.PI * z / pitch;
            double cos_z            = Math.Cos(z_rads);
            double sin_z            = Math.Sin(z_rads);
            var    odd_line_coords  = new List <double>();
            var    even_line_coords = new List <double>();
            var    result           = new Polygons();
            var    chains           = new Polygon[] { new Polygon(), new Polygon() };          // [start_points[], end_points[]]
            var    connected_to     = new List <int>[] { new List <int>(), new List <int>() }; // [chain_indices[], chain_indices[]]
            var    line_numbers     = new List <int>();                                        // which row/column line a chain is part of

            if (Math.Abs(sin_z) <= Math.Abs(cos_z))
            {
                // "vertical" lines
                double phase_offset = ((cos_z < 0) ? Math.PI : 0) + Math.PI;
                for (long y = 0; y < pitch; y += step)
                {
                    double y_rads      = 2 * Math.PI * y / pitch;
                    double a           = cos_z;
                    double b           = Math.Sin(y_rads + phase_offset);
                    double odd_c       = sin_z * Math.Cos(y_rads + phase_offset);
                    double even_c      = sin_z * Math.Cos(y_rads + phase_offset + Math.PI);
                    double h           = Math.Sqrt(a * a + b * b);
                    double odd_x_rads  = ((h != 0) ? Math.Asin(odd_c / h) + Math.Asin(b / h) : 0) - Math.PI / 2;
                    double even_x_rads = ((h != 0) ? Math.Asin(even_c / h) + Math.Asin(b / h) : 0) - Math.PI / 2;
                    odd_line_coords.Add(odd_x_rads / Math.PI * pitch);
                    even_line_coords.Add(even_x_rads / Math.PI * pitch);
                }

                int num_coords  = odd_line_coords.Count;
                int num_columns = 0;
                for (long x = (long)((Math.Floor(aabb.minX / (double)pitch) - 2.25) * pitch); x <= aabb.maxX + pitch / 2; x += pitch / 2)
                {
                    bool     is_first_point  = true;
                    IntPoint last            = default(IntPoint);
                    bool     last_inside     = false;
                    int      chain_end_index = 0;
                    var      chain_end       = new IntPoint[2];
                    for (long y = (long)((Math.Floor(aabb.minY / (double)pitch) - 1) * pitch); y <= aabb.maxY + pitch; y += pitch)
                    {
                        for (int i = 0; i < num_coords; ++i)
                        {
                            var  current        = new IntPoint(x + (((num_columns & 1) == 1) ? odd_line_coords[i] : even_line_coords[i]) / 2 + pitch, y + (long)(i * step));
                            bool current_inside = accelerator.PointIsInside(current) == QTPolygonsExtensions.InsideState.Inside;
                            if (!is_first_point)
                            {
                                if (last_inside && current_inside)
                                {
                                    // line doesn't hit the boundary, add the whole line
                                    result.AddLine(last, current);
                                }
                                else if (last_inside != current_inside)
                                {
                                    // line hits the boundary, add the part that's inside the boundary
                                    var line = new Polygons();
                                    line.AddLine(last, current);
                                    line = outline.CreateLineIntersections(line);
                                    if (line.Count > 0)
                                    {
                                        // some of the line is inside the boundary
                                        result.AddLine(line[0][0], line[0][1]);

                                        var end        = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1];
                                        var lineNumber = num_columns;
                                        if (zigZag)
                                        {
                                            chain_end[chain_end_index] = end;
                                            if (++chain_end_index == 2)
                                            {
                                                chains[0].Add(chain_end[0]);
                                                chains[1].Add(chain_end[1]);
                                                chain_end_index = 0;
                                                connected_to[0].Add(int.MaxValue);
                                                connected_to[1].Add(int.MaxValue);
                                                line_numbers.Add(lineNumber);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // none of the line is inside the boundary so the point that's actually on the boundary
                                        // is the chain end
                                        var end        = last_inside ? last : current;
                                        var lineNumber = num_columns;
                                        if (zigZag)
                                        {
                                            chain_end[chain_end_index] = end;
                                            if (++chain_end_index == 2)
                                            {
                                                chains[0].Add(chain_end[0]);
                                                chains[1].Add(chain_end[1]);
                                                chain_end_index = 0;
                                                connected_to[0].Add(int.MaxValue);
                                                connected_to[1].Add(int.MaxValue);
                                                line_numbers.Add(lineNumber);
                                            }
                                        }
                                    }
                                }
                            }

                            last           = current;
                            last_inside    = current_inside;
                            is_first_point = false;
                        }
                    }

                    ++num_columns;
                }
            }
            else
            {
                // "horizontal" lines
                double phase_offset = (sin_z < 0) ? Math.PI : 0;
                for (long x = 0; x < pitch; x += step)
                {
                    double x_rads      = 2 * Math.PI * x / pitch;
                    double a           = sin_z;
                    double b           = Math.Cos(x_rads + phase_offset);
                    double odd_c       = cos_z * Math.Sin(x_rads + phase_offset + Math.PI);
                    double even_c      = cos_z * Math.Sin(x_rads + phase_offset);
                    double h           = Math.Sqrt(a * a + b * b);
                    double odd_y_rads  = ((h != 0) ? Math.Asin(odd_c / h) + Math.Asin(b / h) : 0) + Math.PI / 2;
                    double even_y_rads = ((h != 0) ? Math.Asin(even_c / h) + Math.Asin(b / h) : 0) + Math.PI / 2;
                    odd_line_coords.Add(odd_y_rads / Math.PI * pitch);
                    even_line_coords.Add(even_y_rads / Math.PI * pitch);
                }

                int num_coords = odd_line_coords.Count;
                int num_rows   = 0;
                for (long y = (long)((Math.Floor(aabb.minY / (double)pitch) - 1) * pitch); y <= aabb.maxY + pitch / 2; y += pitch / 2)
                {
                    bool     is_first_point  = true;
                    IntPoint last            = default(IntPoint);
                    bool     last_inside     = false;
                    int      chain_end_index = 0;
                    var      chain_end       = new IntPoint[2];
                    for (long x = (long)((Math.Floor(aabb.minX / (double)pitch) - 1) * pitch); x <= aabb.maxX + pitch; x += pitch)
                    {
                        for (int i = 0; i < num_coords; ++i)
                        {
                            var  current        = new IntPoint(x + (long)(i * step), y + (((num_rows & 1) == 1) ? odd_line_coords[i] : even_line_coords[i]) / 2);
                            bool current_inside = accelerator.PointIsInside(current) == QTPolygonsExtensions.InsideState.Inside;
                            if (!is_first_point)
                            {
                                if (last_inside && current_inside)
                                {
                                    // line doesn't hit the boundary, add the whole line
                                    result.AddLine(last, current);
                                }
                                else if (last_inside != current_inside)
                                {
                                    // line hits the boundary, add the part that's inside the boundary
                                    var line = new Polygons();
                                    line.AddLine(last, current);
                                    line = outline.CreateLineIntersections(line);
                                    if (line.Count > 0)
                                    {
                                        // some of the line is inside the boundary
                                        result.AddLine(line[0][0], line[0][1]);
                                        var end        = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1];
                                        var lineNumber = num_rows;
                                        if (zigZag)
                                        {
                                            chain_end[chain_end_index] = end;
                                            if (++chain_end_index == 2)
                                            {
                                                chains[0].Add(chain_end[0]);
                                                chains[1].Add(chain_end[1]);
                                                chain_end_index = 0;
                                                connected_to[0].Add(int.MaxValue);
                                                connected_to[1].Add(int.MaxValue);
                                                line_numbers.Add(lineNumber);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // none of the line is inside the boundary so the point that's actually on the boundary
                                        // is the chain end
                                        var end        = (last_inside) ? last : current;
                                        var lineNumber = num_rows;
                                        if (zigZag)
                                        {
                                            chain_end[chain_end_index] = end;
                                            if (++chain_end_index == 2)
                                            {
                                                chains[0].Add(chain_end[0]);
                                                chains[1].Add(chain_end[1]);
                                                chain_end_index = 0;
                                                connected_to[0].Add(int.MaxValue);
                                                connected_to[1].Add(int.MaxValue);
                                                line_numbers.Add(lineNumber);
                                            }
                                        }
                                    }
                                }
                            }

                            last           = current;
                            last_inside    = current_inside;
                            is_first_point = false;
                        }
                    }

                    ++num_rows;
                }
            }

            if (zigZag && chains[0].Count > 0)
            {
                // zig-zag connecting consists of joining alternate chain ends to make a chain of chains
                // the basic algorithm is that we follow the infill area boundary and as we progress we are either drawing a connector or not
                // whenever we come across the end of a chain we toggle the connector drawing state
                // things are made more complicated by the fact that we want to avoid generating loops and so we need to keep track
                // of the identity of the first chain in a connected sequence

                int chain_ends_remaining = chains[0].Count * 2;

                foreach (var outline_poly in outline)
                {
                    var connector_points = new Polygon();                     // the points that make up a connector line

                    // we need to remember the first chain processed and the path to it from the first outline point
                    // so that later we can possibly connect to it from the last chain processed
                    int first_chain_chain_index = int.MaxValue;
                    var path_to_first_chain     = new Polygon();

                    bool drawing = false;                     // true when a connector line is being (potentially) created

                    // keep track of the chain+point that a connector line started at
                    int connector_start_chain_index = int.MaxValue;
                    int connector_start_point_index = int.MaxValue;

                    IntPoint cur_point = default(IntPoint);                     // current point of interest - either an outline point or a chain end

                    // go round all of the region's outline and find the chain ends that meet it
                    // quit the loop early if we have seen all the chain ends and are not currently drawing a connector
                    for (int outline_point_index = 0; (chain_ends_remaining > 0 || drawing) && outline_point_index < outline_poly.Count; ++outline_point_index)
                    {
                        var op0 = outline_poly[outline_point_index];
                        var op1 = outline_poly[(outline_point_index + 1) % outline_poly.Count()];
                        var points_on_outline_chain_index = new List <int>();
                        var points_on_outline_point_index = new List <int>();

                        // collect the chain ends that meet this segment of the outline
                        for (int chain_index = 0; chain_index < chains[0].Count; ++chain_index)
                        {
                            for (int point_index = 0; point_index < 2; ++point_index)
                            {
                                // don't include chain ends that are close to the segment but are beyond the segment ends
                                int beyond = 0;
                                if (GetDist2FromLineSegment(op0, chains[point_index][chain_index], op1, ref beyond) < 10 &&
                                    beyond != 0)
                                {
                                    points_on_outline_point_index.Add(point_index);
                                    points_on_outline_chain_index.Add(chain_index);
                                }
                            }
                        }

                        if (outline_point_index == 0 || (op0 - cur_point).Length() > 100)
                        {
                            // this is either the first outline point or it is another outline point that is not too close to cur_point

                            if (first_chain_chain_index == int.MaxValue)
                            {
                                // include the outline point in the path to the first chain
                                path_to_first_chain.Add(op0);
                            }

                            cur_point = op0;
                            if (drawing)
                            {
                                // include the start point of this outline segment in the connector
                                connector_points.Add(op0);
                            }
                        }

                        // iterate through each of the chain ends that meet the current outline segment
                        while (points_on_outline_chain_index.Count > 0)
                        {
                            // find the nearest chain end to the current point
                            int nearest_point_index        = 0;
                            var nearest_point_dist_squared = double.MaxValue;
                            for (int pi = 0; pi < points_on_outline_chain_index.Count; ++pi)
                            {
                                var first        = chains[points_on_outline_point_index[pi]][points_on_outline_chain_index[pi]];
                                var dist_squared = (first - cur_point).LengthSquared();
                                if (dist_squared < nearest_point_dist_squared)
                                {
                                    nearest_point_dist_squared = dist_squared;
                                    nearest_point_index        = pi;
                                }
                            }

                            int point_index = points_on_outline_point_index[nearest_point_index];
                            int chain_index = points_on_outline_chain_index[nearest_point_index];

                            // make the chain end the current point and add it to the connector line
                            cur_point = chains[point_index][chain_index];

                            if (drawing && connector_points.Count > 0 &&
                                (cur_point - connector_points.Last()).Length() < 100)
                            {
                                // this chain end will be too close to the last connector point so throw away the last connector point
                                connector_points.RemoveAt(connector_points.Count - 1);
                            }

                            connector_points.Add(cur_point);

                            if (first_chain_chain_index == int.MaxValue)
                            {
                                // this is the first chain to be processed, remember it
                                first_chain_chain_index = chain_index;
                                path_to_first_chain.Add(cur_point);
                            }

                            if (drawing)
                            {
                                // add the connector line segments but only if
                                //  1 - the start/end points are not the opposite ends of the same chain
                                //  2 - the other end of the current chain is not connected to the chain the connector line is coming from

                                if (chain_index != connector_start_chain_index && connected_to[(point_index + 1) % 2][chain_index] != connector_start_chain_index)
                                {
                                    for (int pi = 1; pi < connector_points.Count; ++pi)
                                    {
                                        result.AddLine(connector_points[pi - 1], connector_points[pi]);
                                    }

                                    drawing = false;
                                    connector_points.Clear();
                                    // remember the connection
                                    connected_to[point_index][chain_index] = connector_start_chain_index;
                                    connected_to[connector_start_point_index][connector_start_chain_index] = chain_index;
                                }
                                else
                                {
                                    // start a new connector from the current location
                                    connector_points.Clear();
                                    connector_points.Add(cur_point);

                                    // remember the chain+point that the connector started from
                                    connector_start_chain_index = chain_index;
                                    connector_start_point_index = point_index;
                                }
                            }
                            else
                            {
                                // we have just jumped a gap so now we want to start drawing again
                                drawing = true;

                                // if this connector is the first to be created or we are not connecting chains from the same row/column,
                                // remember the chain+point that this connector is starting from
                                if (connector_start_chain_index == int.MaxValue || line_numbers[chain_index] != line_numbers[connector_start_chain_index])
                                {
                                    connector_start_chain_index = chain_index;
                                    connector_start_point_index = point_index;
                                }
                            }

                            // done with this chain end
                            if (points_on_outline_chain_index.Count > 0)
                            {
                                points_on_outline_chain_index.RemoveAt(Math.Min(points_on_outline_chain_index.Count - 1, points_on_outline_chain_index[0] + nearest_point_index));
                            }

                            if (points_on_outline_chain_index.Count > 0)
                            {
                                points_on_outline_chain_index.RemoveAt(Math.Min(points_on_outline_chain_index.Count - 1, points_on_outline_chain_index[0] + nearest_point_index));
                            }

                            // decrement total amount of work to do
                            --chain_ends_remaining;
                        }
                    }

                    // we have now visited all the points in the outline, if a connector was (potentially) being drawn
                    // check whether the first chain is already connected to the last chain and, if not, draw the
                    // connector between
                    if (drawing && first_chain_chain_index != int.MaxValue &&
                        first_chain_chain_index != connector_start_chain_index &&
                        connected_to[0][first_chain_chain_index] != connector_start_chain_index &&
                        connected_to[1][first_chain_chain_index] != connector_start_chain_index)
                    {
                        // output the connector line segments from the last chain to the first point in the outline
                        connector_points.Add(outline_poly[0]);
                        for (int pi = 1; pi < connector_points.Count; ++pi)
                        {
                            result.AddLine(connector_points[pi - 1], connector_points[pi]);
                        }

                        // output the connector line segments from the first point in the outline to the first chain
                        for (int pi = 1; pi < path_to_first_chain.Count; ++pi)
                        {
                            result.AddLine(path_to_first_chain[pi - 1], path_to_first_chain[pi]);
                        }
                    }

                    if (chain_ends_remaining < 1)
                    {
                        break;
                    }
                }
            }

            result_lines.AddRange(result);
        }
Exemple #20
0
        private static List <Polygons> AccumulateDownPolygons(ConfigSettings config, List <Polygons> inputPolys, List <Polygons> allPartOutlines)
        {
            int numLayers = inputPolys.Count;

            long nozzleSize     = config.ExtrusionWidth_um;
            long areaToTryAndBe = 20 * 20 * nozzleSize * nozzleSize;             // 10 x 10 mm approximately (assuming .5 nozzle)

            List <Polygons> allDownOutlines = CreateEmptyPolygons(numLayers);

            for (int layerIndex = numLayers - 2; layerIndex >= 0; layerIndex--)
            {
                Polygons aboveRequiredSupport = inputPolys[layerIndex + 1];

                // get all the polygons above us
                Polygons accumulatedAbove = allDownOutlines[layerIndex + 1].CreateUnion(aboveRequiredSupport);

                // experimental and not working well enough yet
                if (config.MinimizeSupportColumns)
                {
                    // reduce the amount of support material used
                    for (int i = accumulatedAbove.Count - 1; i >= 0; i--)
                    {
                        Polygon polygon  = accumulatedAbove[i];
                        double  polyArea = polygon.Area();
                        if (polyArea > areaToTryAndBe)
                        {
                            Polygons offsetPolygons = new Polygons()
                            {
                                polygon
                            }.Offset(-config.ExtrusionWidth_um / 2);
                            accumulatedAbove.RemoveAt(i);
                            foreach (Polygon polyToAdd in offsetPolygons)
                            {
                                accumulatedAbove.Insert(i, polyToAdd);
                            }
                        }
                        else if (polyArea < areaToTryAndBe * .9)
                        {
                            Polygons offsetPolygons = new Polygons()
                            {
                                polygon
                            }.Offset(config.ExtrusionWidth_um / 2);
                            accumulatedAbove.RemoveAt(i);
                            foreach (Polygon polyToAdd in offsetPolygons)
                            {
                                accumulatedAbove.Insert(i, polyToAdd);
                            }
                        }
                    }
                }

                // add in the support on this level
                Polygons curRequiredSupport = inputPolys[layerIndex];

                Polygons totalSupportThisLayer = accumulatedAbove.CreateUnion(curRequiredSupport);

                // remove the solid polygons on this level
                Polygons remainingAbove = totalSupportThisLayer.CreateDifference(allPartOutlines[layerIndex]);

                allDownOutlines[layerIndex] = Clipper.CleanPolygons(remainingAbove, cleanDistance_um);
            }

            return(allDownOutlines);
        }
Exemple #21
0
        public void GenerateInsets(ConfigSettings config, Polygons fuzzyBounds, long extrusionWidth_um, long outerExtrusionWidth_um, int insetCount, bool avoidCrossingPerimeters)
        {
            LayerIsland part = this;

            part.BoundingBox.Calculate(part.IslandOutline);

            if (avoidCrossingPerimeters)
            {
                part.PathFinder = new PathFinder(part.IslandOutline, extrusionWidth_um * 3 / 2, useInsideCache: avoidCrossingPerimeters, name: "inset island");
            }

            if (insetCount == 0)
            {
                // if we have no insets defined still create one
                part.InsetToolPaths.Add(part.IslandOutline);
            }
            else             // generate the insets
            {
                long currentOffset = 0;

                // Inset 0 will use the outerExtrusionWidth_um, everyone else will use extrusionWidth_um
                long offsetBy = outerExtrusionWidth_um / 2;

                for (int i = 0; i < insetCount; i++)
                {
                    // Increment by half the offset amount
                    currentOffset += offsetBy;

                    Polygons currentInset = part.IslandOutline.Offset(-currentOffset);

                    // make the outer perimeter fuzzy if needed
                    if (i == 0)
                    {
                        currentInset = MakeFuzzy(currentInset, fuzzyBounds, config);
                    }

                    // make sure our polygon data is reasonable
                    if (config.MergeOverlappingLines)
                    {
                        // be aggressive about maintaining small polygons
                        currentInset = Clipper.CleanPolygons(currentInset);
                    }
                    else
                    {
                        // clean the polygon to make it have less jaggies
                        currentInset = Clipper.CleanPolygons(currentInset, minimumDistanceToCreateNewPosition);
                    }

                    // check that we have actual paths
                    if (currentInset.Count > 0)
                    {
                        var run = true;
                        // if we are centering the seam put a point exactly in back
                        if (run && (config.SeamPlacement == SEAM_PLACEMENT.ALWAYS_CENTERED_IN_BACK ||
                                    config.SeamPlacement == SEAM_PLACEMENT.CENTERED_IN_BACK))
                        {
                            foreach (var polygon in currentInset)
                            {
                                var count = polygon.Count;
                                if (count > 2)
                                {
                                    // if we are going to center the seam in the back make sure there is a vertex to center on that is exactly in back
                                    var centeredIndex = polygon.GetCenteredInBackIndex(out IntPoint center);
                                    var start         = -1;
                                    var end           = -1;

                                    if (polygon[centeredIndex].X <= center.X)
                                    {
                                        // start should always be left of center
                                        start = centeredIndex;
                                        // is the next point right of center
                                        end = (centeredIndex + 1) % count;
                                        if (polygon[end].X < center.X)
                                        {
                                            // no it is left of center
                                            // is the previous point right of center
                                            end = (centeredIndex + count - 1) % count;
                                            if (polygon[end].X < center.X)
                                            {
                                                // it is still left of center (so no crossing)
                                                continue;                                                 // skip placing a seam at this point
                                            }
                                        }
                                    }
                                    else if (polygon[centeredIndex].X >= center.X)
                                    {
                                        // we are to the right of center so this is the end
                                        end = centeredIndex;
                                        // set start to the left of center
                                        start = (centeredIndex + 1) % count;
                                        if (polygon[start].X > center.X)
                                        {
                                            // start is also right of center it need to be left
                                            start = (centeredIndex + count - 1) % count;
                                            if (polygon[start].X > center.X)
                                            {
                                                // it is still right of center skip this point
                                                continue;
                                            }
                                        }
                                    }

                                    // find the y intercept
                                    var delta = polygon[end] - polygon[start];
                                    if (delta.X != 0)
                                    {
                                        var insert = Math.Max(start, end);
                                        if (insert == count - 1 && (start == 0 || end == 0))
                                        {
                                            insert = count;
                                        }
                                        var ratio = (center.X - polygon[start].X) / (double)delta.X;
                                        polygon.Insert(insert, new IntPoint(center.X, polygon[start].Y + (polygon[end].Y - polygon[start].Y) * ratio));
                                    }
                                }
                            }
                        }

                        part.InsetToolPaths.Add(currentInset);

                        // Increment by the second half
                        currentOffset += offsetBy;
                    }
                    else
                    {
                        // we are done making insets as we have no area left
                        break;
                    }

                    if (i == 0)
                    {
                        // Reset offset amount to half the standard extrusion width
                        offsetBy = extrusionWidth_um / 2;
                    }
                }
            }
        }
Exemple #22
0
        private static List <Polygons> ClipToXyDistance(List <Polygons> inputPolys, List <Polygons> allPartOutlines, ConfigSettings config)
        {
            int numLayers = inputPolys.Count;

            List <Polygons> clippedToXyOutlines = CreateEmptyPolygons(numLayers);

            for (int layerIndex = numLayers - 2; layerIndex >= 0; layerIndex--)
            {
                Polygons curRequiredSupport    = inputPolys[layerIndex];
                Polygons expandedlayerPolys    = allPartOutlines[layerIndex].Offset(config.SupportXYDistance_um);
                Polygons totalSupportThisLayer = curRequiredSupport.CreateDifference(expandedlayerPolys);

                clippedToXyOutlines[layerIndex] = Clipper.CleanPolygons(totalSupportThisLayer, cleanDistance_um);
            }

            return(clippedToXyOutlines);
        }
        int LastLayerWithChange(ConfigSettings config)
        {
            int numLayers = Extruders[0].Layers.Count;
            int firstExtruderWithData = -1;
            for (int checkLayer = numLayers - 1; checkLayer >= 0; checkLayer--)
            {
                for (int extruderToCheck = 0; extruderToCheck < config.MaxExtruderCount(); extruderToCheck++)
                {
                    if((extruderToCheck < Extruders.Count && Extruders[extruderToCheck].Layers[checkLayer].AllOutlines.Count > 0)
                        || (config.SupportExtruder == extruderToCheck && support != null && support.HasNormalSupport(checkLayer) )
                        || (config.SupportInterfaceExtruder == extruderToCheck && support != null && support.HasInterfaceSupport(checkLayer)) )
                    {
                        if(firstExtruderWithData == -1)
                        {
                            firstExtruderWithData = extruderToCheck;
                        }
                        else
                        {
                            if(firstExtruderWithData != extruderToCheck)
                            {
                                return checkLayer;
                            }
                        }
                    }
                }
            }

            return -1;
        }
Exemple #24
0
        public void GenerateTopAndBottoms(ConfigSettings config, int layerIndex, long extrusionWidth_um, long outerPerimeterWidth_um, int downLayerCount, int upLayerCount, long infillExtendIntoPerimeter_um)
        {
            var clippingOffset = infillExtendIntoPerimeter_um * 2;

            ExtruderLayers extruder = this;
            SliceLayer     layer    = extruder.Layers[layerIndex];

            for (int islandIndex = 0; islandIndex < layer.Islands.Count; islandIndex++)
            {
                LayerIsland island = layer.Islands[islandIndex];
                if (island.InsetToolPaths.Count == 0)
                {
                    return;
                }

                // this is the entire extrusion width to make sure we are outside of the extrusion line
                Polygons lastInset         = island.InsetToolPaths[island.InsetToolPaths.Count - 1];
                Polygons infillRegionPath  = lastInset.Offset(-extrusionWidth_um);
                Polygons sparseInfillPaths = new Polygons(infillRegionPath);

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

                    if (layerIndex - 1 >= 0)
                    {
                        var previousLayer = extruder.Layers[layerIndex - 1];

                        bottomOutlines = RemoveIslandsFromPolygons(previousLayer.Islands, island.BoundingBox, bottomOutlines);
                        bottomOutlines.RemoveSmallAreas(extrusionWidth_um);
                    }

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

                    island.BottomPaths = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(infillRegionPath);
                    topOutlines = topOutlines.CreateDifference(island.BottomPaths.Offset(clippingOffset));
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    if (layerIndex + 1 < extruder.Layers.Count)
                    {
                        // Remove the top layer that is above this one to get only the data that is a top layer on this layer.
                        topOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex + 1].Islands, island.BoundingBox, topOutlines);
                    }

                    topOutlines.RemoveSmallAreas(extrusionWidth_um);

                    sparseInfillPaths = sparseInfillPaths.CreateDifference(topOutlines.Offset(clippingOffset));
                    sparseInfillPaths = Clipper.CleanPolygons(sparseInfillPaths, cleanDistance_um);

                    island.TopPaths = topOutlines;
                }

                if (upLayerCount <= 0 && downLayerCount <= 0)
                {
                    // Assign infill directly if no top/bottom solid layers
                    island.SparseInfillPaths = sparseInfillPaths;
                }
                else
                {
                    // calculate the solid infill outlines
                    Polygons solidInfillPaths = new Polygons(infillRegionPath);

                    // remove all the top layers
                    solidInfillPaths = solidInfillPaths.CreateDifference(island.BottomPaths.Offset(clippingOffset));
                    solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um);

                    // remove all the bottom layers
                    solidInfillPaths = solidInfillPaths.CreateDifference(island.TopPaths.Offset(clippingOffset));
                    solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um);

                    int upEnd = layerIndex + upLayerCount + 1;
                    if (upEnd <= extruder.Layers.Count && layerIndex - downLayerCount >= 0)
                    {
                        // find all the regions that have more top and bottom layers than should be solid (will remain sparse)
                        Polygons regionsThatWillBeSparse = new Polygons(infillRegionPath);

                        int upStart = layerIndex + 2;

                        for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            regionsThatWillBeSparse = IntersectWithPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, regionsThatWillBeSparse);
                            regionsThatWillBeSparse = Clipper.CleanPolygons(regionsThatWillBeSparse, cleanDistance_um);
                        }

                        // find all the solid infill bottom layers
                        int downStart = Math.Max(0, layerIndex - 1);
                        int downEnd   = Math.Max(0, layerIndex - downLayerCount);

                        for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            regionsThatWillBeSparse = IntersectWithPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, regionsThatWillBeSparse);
                            regionsThatWillBeSparse = Clipper.CleanPolygons(regionsThatWillBeSparse, cleanDistance_um);
                        }

                        solidInfillPaths = solidInfillPaths.CreateDifference(regionsThatWillBeSparse);
                        solidInfillPaths.RemoveSmallAreas(extrusionWidth_um);
                        solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um);
                    }

                    // remove the solid infill from the sparse infill
                    sparseInfillPaths = sparseInfillPaths.CreateDifference(solidInfillPaths.Offset(clippingOffset));
                    sparseInfillPaths.RemoveSmallAreas(extrusionWidth_um);
                    sparseInfillPaths        = Clipper.CleanPolygons(sparseInfillPaths, cleanDistance_um);
                    island.SparseInfillPaths = sparseInfillPaths;

                    if (config == null ||                   // this is to make our tests test the bridgeOverInfill
                        config.BridgeOverInfill)
                    {
                        // now figure out what part of the solid infill is actually first top layers and switch it to that
                        // we can only have a first top y layer at the bottom of the top layers
                        if (layerIndex == extruder.Layers.Count - upLayerCount)
                        {
                            // all of it is first top layers
                            island.FirstTopPaths = solidInfillPaths;
                            solidInfillPaths     = new Polygons();
                        }
                        else if (layerIndex > 0 &&
                                 layerIndex < extruder.Layers.Count - upLayerCount)
                        {
                            // Intersect the current solid layer with the previous spars layer
                            // that will be all of the new solid layers that are currently on sparse layer

                            var firstTopPaths = new Polygons(solidInfillPaths);
                            firstTopPaths = IntersectWithSparsePolygons(extruder.Layers[layerIndex - 1].Islands, island.BoundingBox, firstTopPaths);
                            firstTopPaths.RemoveSmallAreas(extrusionWidth_um);
                            firstTopPaths = Clipper.CleanPolygons(firstTopPaths, cleanDistance_um);

                            if (firstTopPaths.Count > 0)
                            {
                                solidInfillPaths = solidInfillPaths.CreateDifference(firstTopPaths.Offset(clippingOffset));
                                solidInfillPaths.RemoveSmallAreas(extrusionWidth_um);
                                solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um);

                                island.FirstTopPaths = firstTopPaths;
                            }
                        }
                    }

                    island.SolidInfillPaths = solidInfillPaths;
                }
            }
        }
		public void GenerateSkirt(int distance, int extrusionWidth_um, int numberOfLoops, int minLength, int initialLayerHeight, ConfigSettings config)
		{
			LayerDataStorage storage = this;
			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 extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
				{
					if (config.continuousSpiralOuterPerimeter && extrudeIndex > 0)
					{
						continue;
					}

					if (storage.Extruders[extrudeIndex].Layers.Count < 1)
					{
						continue;
					}

					SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
					for (int partIndex = 0; partIndex < layer.Islands.Count; partIndex++)
					{
						if (config.continuousSpiralOuterPerimeter && partIndex > 0)
						{
							continue;
						}

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

				if (storage.support != null)
				{
					skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines().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++;
				}
			}
		}
        public void CreateRequiredInsets(ConfigSettings config, int outputLayerIndex, int extruderIndex)
        {
            if (extruderIndex < this.Extruders.Count)
            {
                var startIndex = Math.Max(0, outputLayerIndex - config.NumberOfBottomLayers - 1);
                // figure out how many layers we need to calculate
                var endIndex    = outputLayerIndex + config.NumberOfTopLayers + 1;
                var threadExtra = 10;
                endIndex = (endIndex / threadExtra) * threadExtra + threadExtra;

                // now bias more in to help multi threading
                // and clamp to the number there are to make sure we can do it
                endIndex = Math.Min(endIndex, this.Extruders[extruderIndex].Layers.Count);

                // free up the insets from the previous layer
                if (startIndex > config.NumberOfBottomLayers + 1)
                {
                    SliceLayer previousLayer = this.Extruders[extruderIndex].Layers[startIndex - 2];
                    previousLayer.FreeIslandMemory();
                }

                using (new QuickTimer2Report("GenerateInsets"))
                {
                    for (int layerIndex = startIndex; layerIndex < endIndex; layerIndex++)
                    {
                        SliceLayer layer = this.Extruders[extruderIndex].Layers[layerIndex];

                        if (layer.Islands.Count > 0 &&
                            !layer.CreatedInsets)
                        {
                            layer.CreatedInsets = true;
                            int insetCount = config.GetNumberOfPerimeters();
                            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 += 1;
                            }

                            Polygons fuzzyBounds = null;
                            if (layerIndex > 0 &&
                                FuzzyLayerBounds != null &&
                                FuzzyLayerBounds.Count > layerIndex)
                            {
                                fuzzyBounds = FuzzyLayerBounds[layerIndex];
                            }

                            if (layerIndex == 0)
                            {
                                layer.GenerateInsets(config, fuzzyBounds, config.FirstLayerExtrusionWidth_um, config.FirstLayerExtrusionWidth_um, insetCount);
                            }
                            else
                            {
                                layer.GenerateInsets(config, fuzzyBounds, config.ExtrusionWidth_um, config.OutsideExtrusionWidth_um, insetCount);
                            }
                        }
                    }
                }

                using (new QuickTimer2Report("GenerateTopAndBottoms"))
                {
                    // Only generate bottom and top layers and infill for the first X layers when spiralize is chosen.
                    if (!config.ContinuousSpiralOuterPerimeter || (int)outputLayerIndex < config.NumberOfBottomLayers)
                    {
                        if (outputLayerIndex == 0)
                        {
                            this.Extruders[extruderIndex].GenerateTopAndBottoms(config, outputLayerIndex, config.FirstLayerExtrusionWidth_um, config.FirstLayerExtrusionWidth_um, config.NumberOfBottomLayers, config.NumberOfTopLayers, config.InfillExtendIntoPerimeter_um);
                        }
                        else
                        {
                            this.Extruders[extruderIndex].GenerateTopAndBottoms(config, outputLayerIndex, config.ExtrusionWidth_um, config.OutsideExtrusionWidth_um, config.NumberOfBottomLayers, config.NumberOfTopLayers, config.InfillExtendIntoPerimeter_um);
                        }
                    }
                }
            }
        }
Exemple #27
0
		public static void GenerateTriangleInfill(ConfigSettings config, Polygons partOutline, ref Polygons fillPolygons, double fillAngle)
		{
			if (config.infillPercent <= 0)
			{
				throw new Exception("infillPercent must be gerater than 0.");
			}

			int linespacing_um = (int)(config.extrusionWidth_um / (config.infillPercent / 100) * 3);

			long offset = linespacing_um / 2;

			Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset);

			fillAngle += 60;
			if (fillAngle > 360)
			{
				fillAngle -= 360;
			}

			Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset);

			fillAngle += 60;
			if (fillAngle > 360)
			{
				fillAngle -= 360;
			}

			Infill.GenerateLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, offset);
		}
        public void GenerateSkirt(int distance, int extrusionWidth_um, int numberOfLoops, int brimCount, int minLength, ConfigSettings config)
        {
            Polygons islandsToSkirtAround = GetSkirtBounds(config, this, (distance > 0), distance, extrusionWidth_um, brimCount);

            if (islandsToSkirtAround.Count > 0)
            {
                // Find convex hull for the skirt outline
                Polygons convexHull = new Polygons(new[] { islandsToSkirtAround.CreateConvexHull() });

                // Create skirt loops from the ConvexHull
                for (int skirtLoop = 0; skirtLoop < numberOfLoops; skirtLoop++)
                {
                    int offsetDistance = distance + extrusionWidth_um * skirtLoop + extrusionWidth_um / 2;

                    this.Skirt.AddAll(convexHull.Offset(offsetDistance));

                    int length = (int)this.Skirt.PolygonLength();
                    if (skirtLoop + 1 >= numberOfLoops && length > 0 && length < minLength)
                    {
                        // add more loops for as long as we have not extruded enough length
                        numberOfLoops++;
                    }
                }
            }
        }
 public fffProcessor(ConfigSettings config)
 {
     this.config = config;
     fileNumber = 1;
     maxObjectHeight = 0;
 }
Exemple #30
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));
        }
Exemple #31
0
		private static List<Polygons> CalculateAllPartOutlines(ConfigSettings config, List<ExtruderLayers> Extruders)
		{
			int numLayers = Extruders[0].Layers.Count;

			List<Polygons> allPartOutlines = CreateEmptyPolygons(numLayers);

			foreach (var extruder in Extruders)
			{
				// calculate the combined outlines for everything
				for (int layerIndex = numLayers - 1; layerIndex >= 0; layerIndex--)
				{
					allPartOutlines[layerIndex] = allPartOutlines[layerIndex].CreateUnion(extruder.Layers[layerIndex].AllOutlines);
				}
			}

			return allPartOutlines;
		}
Exemple #32
0
 public static bool ShouldGenerateRaft(ConfigSettings config)
 {
     return(config.enableRaft &&
            config.raftBaseThickness_um > 0 &&
            config.raftInterfaceThicknes_um > 0);
 }
Exemple #33
0
		private static List<Polygons> AccumulateDownPolygons(ConfigSettings config, List<Polygons> inputPolys, List<Polygons> allPartOutlines)
		{
			int numLayers = inputPolys.Count;

			long nozzleSize = config.extrusionWidth_um;
			long areaToTryAndBe = 20 * 20 * nozzleSize * nozzleSize; // 10 x 10 mm approximately (assuming .5 nozzle)

			List<Polygons> allDownOutlines = CreateEmptyPolygons(numLayers);
			for (int layerIndex = numLayers - 2; layerIndex >= 0; layerIndex--)
			{
				Polygons aboveRequiredSupport = inputPolys[layerIndex + 1];

				// get all the polygons above us
				Polygons accumulatedAbove = allDownOutlines[layerIndex + 1].CreateUnion(aboveRequiredSupport);

				// experimental and not working well enough yet
				if (config.minimizeSupportColumns)
				{
					// reduce the amount of support material used
					for (int i = accumulatedAbove.Count - 1; i >= 0; i--)
					{
						Polygon polygon = accumulatedAbove[i];
						double polyArea = polygon.Area();
						if (polyArea > areaToTryAndBe)
						{
							Polygons offsetPolygons = new Polygons() { polygon }.Offset(-config.extrusionWidth_um / 2);
							accumulatedAbove.RemoveAt(i);
							foreach (Polygon polyToAdd in offsetPolygons)
							{
								accumulatedAbove.Insert(i, polyToAdd);
							}
						}
						else if (polyArea < areaToTryAndBe * .9)
						{
							Polygons offsetPolygons = new Polygons() { polygon }.Offset(config.extrusionWidth_um / 2);
							accumulatedAbove.RemoveAt(i);
							foreach (Polygon polyToAdd in offsetPolygons)
							{
								accumulatedAbove.Insert(i, polyToAdd);
							}
						}
					}
				}

				// add in the support on this level
				Polygons curRequiredSupport = inputPolys[layerIndex];

				Polygons totalSupportThisLayer = accumulatedAbove.CreateUnion(curRequiredSupport);

				// remove the solid polygons on this level
				Polygons remainingAbove = totalSupportThisLayer.CreateDifference(allPartOutlines[layerIndex]);

				allDownOutlines[layerIndex] = Clipper.CleanPolygons(remainingAbove, cleanDistance_um);
			}

			return allDownOutlines;
		}
        public void CreateWipeTower(int totalLayers, ConfigSettings config)
        {
            if (config.WipeTowerSize_um < 1
                || LastLayerWithChange(config) == -1)
            {
                return;
            }

            extrudersThatHaveBeenPrimed = new bool[config.MaxExtruderCount()];

            Polygon wipeTowerShape = new Polygon();
            wipeTowerShape.Add(new IntPoint(this.modelMin.x - 3000, this.modelMax.y + 3000));
            wipeTowerShape.Add(new IntPoint(this.modelMin.x - 3000, this.modelMax.y + 3000 + config.WipeTowerSize_um));
            wipeTowerShape.Add(new IntPoint(this.modelMin.x - 3000 - config.WipeTowerSize_um, this.modelMax.y + 3000 + config.WipeTowerSize_um));
            wipeTowerShape.Add(new IntPoint(this.modelMin.x - 3000 - config.WipeTowerSize_um, this.modelMax.y + 3000));

            this.wipeTower.Add(wipeTowerShape);
            this.wipePoint = new IntPoint(this.modelMin.x - 3000 - config.WipeTowerSize_um / 2, this.modelMax.y + 3000 + config.WipeTowerSize_um / 2);
        }
Exemple #35
0
		public void QueueAirGappedBottomLayer(ConfigSettings config, GCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
		{
			// normal support
			Polygons currentAirGappedBottoms = airGappedBottomOutlines[layerIndex];
			currentAirGappedBottoms = currentAirGappedBottoms.Offset(-config.extrusionWidth_um / 2);
			List<Polygons> supportIslands = currentAirGappedBottoms.ProcessIntoSeparatIslands();

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

					case ConfigConstants.SUPPORT_TYPE.LINES:
						Infill.GenerateLineInfill(config, infillOutline, ref islandInfillLines, config.supportInfillStartingAngle, config.supportLineSpacing_um);
						break;
				}
				gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, supportNormalConfig);
			}
		}
        public void GenerateSkirt(int distance, int extrusionWidth_um, int numberOfLoops, int brimCount, int minLength, int initialLayerHeight, ConfigSettings config)
        {
            LayerDataStorage storage = this;
            bool externalOnly = (distance > 0);

            List<Polygons> skirtLoops = new List<Polygons>();

            Polygons skirtPolygons = GetSkirtBounds(config, storage, externalOnly, distance, extrusionWidth_um, brimCount);

            // Find convex hull for the skirt outline
            Polygons convexHull = new Polygons(new[] { skirtPolygons.CreateConvexHull() });

            // Create skirt loops from the ConvexHull
            for (int skirtLoop = 0; skirtLoop < numberOfLoops; skirtLoop++)
            {
                int offsetDistance = distance + extrusionWidth_um * skirtLoop + extrusionWidth_um / 2;

                storage.skirt.AddAll(convexHull.Offset(offsetDistance));

                int length = (int)storage.skirt.PolygonLength();
                if (skirtLoop + 1 >= numberOfLoops && length > 0 && length < minLength)
                {
                    // add more loops for as long as we have not extruded enough length
                    numberOfLoops++;
                }
            }
        }
Exemple #37
0
        public Slicer(OptimizedVolume ov, ConfigSettings config)
        {
            int initialLayerThickness_um = config.firstLayerThickness_um;
            int layerThickness_um = config.layerThickness_um;
            ConfigConstants.REPAIR_OUTLINES outlineRepairTypes = config.repairOutlines;

            modelSize = ov.parentModel.size_um;
            modelMin = ov.parentModel.minXYZ_um;

            int heightWithoutFirstLayer = modelSize.z - initialLayerThickness_um - config.bottomClipAmount_um;
            int countOfNormalThicknessLayers = (int)((heightWithoutFirstLayer / (double)layerThickness_um) + .5);

            int layerCount = countOfNormalThicknessLayers;
            if (initialLayerThickness_um > 0)
            {
                // we have to add in the first layer (that is a differnt size)
                layerCount++;
            }

            LogOutput.Log(string.Format("Layer count: {0}\n", layerCount));
            layers.Capacity = layerCount;
            for (int layerIndex = 0; layerIndex < layerCount; layerIndex++)
            {
                int z;
                if (layerIndex == 0)
                {
                    z = initialLayerThickness_um / 2;
                }
                else
                {
                    z = initialLayerThickness_um + layerThickness_um / 2 + layerThickness_um * (layerIndex - 1);
                }
                layers.Add(new SlicerLayer(z));
            }

            for (int faceIndex = 0; faceIndex < ov.facesTriangle.Count; faceIndex++)
            {
                Point3 p0 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[0]].position;
                Point3 p1 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[1]].position;
                Point3 p2 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[2]].position;
                int minZ = p0.z;
                int maxZ = p0.z;
                if (p1.z < minZ) minZ = p1.z;
                if (p2.z < minZ) minZ = p2.z;
                if (p1.z > maxZ) maxZ = p1.z;
                if (p2.z > maxZ) maxZ = p2.z;

                for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++)
                {
                    int z = layers[layerIndex].Z;
                    if (z < minZ || z > maxZ)
                    {
                        continue;
                    }

                    SlicerSegment polyCrossingAtThisZ;
                    if (p0.z < z && p1.z >= z && p2.z >= z)
                    {
                        // p1   p2
                        // --------
                        //   p0
                        polyCrossingAtThisZ = GetCrossingAtZ(p0, p2, p1, z);
                    }
                    else if (p0.z >= z && p1.z < z && p2.z < z)
                    {
                        //   p0
                        // --------
                        // p1  p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p0, p1, p2, z);
                    }
                    else if (p1.z < z && p0.z >= z && p2.z >= z)
                    {
                        // p0   p2
                        // --------
                        //   p1
                        polyCrossingAtThisZ = GetCrossingAtZ(p1, p0, p2, z);
                    }
                    else if (p1.z >= z && p0.z < z && p2.z < z)
                    {
                        //   p1
                        // --------
                        // p0  p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p1, p2, p0, z);
                    }
                    else if (p2.z < z && p1.z >= z && p0.z >= z)
                    {
                        // p1   p0
                        // --------
                        //   p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p2, p1, p0, z);
                    }
                    else if (p2.z >= z && p1.z < z && p0.z < z)
                    {
                        //   p2
                        // --------
                        // p1  p0
                        polyCrossingAtThisZ = GetCrossingAtZ(p2, p0, p1, z);
                    }
                    else
                    {
                        //Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
                        //  on the slice would create two segments
                        continue;
                    }

                    polyCrossingAtThisZ.hasBeenAddedToPolygon = false;
                    layers[layerIndex].SegmentList.Add(polyCrossingAtThisZ);
                }
            }

            for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++)
            {
                layers[layerIndex].MakePolygons(outlineRepairTypes);
            }
        }
        public void PrimeOnWipeTower(int extruderIndex, int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (config.WipeTowerSize_um < 1
                || extrudersThatHaveBeenPrimed == null
                || layerIndex > LastLayerWithChange(config) + 1)
            {
                return;
            }

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

            extrudersThatHaveBeenPrimed[extruderIndex] = true;
        }
Exemple #39
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);
                }
            }
        }
Exemple #40
0
        public bool QueueNormalSupportLayer(ConfigSettings config, LayerGCodePlanner gcodeLayer, int layerIndex, GCodePathConfig supportNormalConfig)
        {
            // normal support
            Polygons        currentSupportOutlines = SparseSupportOutlines[layerIndex];
            List <Polygons> supportIslands         = currentSupportOutlines.ProcessIntoSeparateIslands();

            List <PathFinder> pathFinders    = new List <PathFinder>();
            List <Polygons>   infillOutlines = new List <Polygons>();

            for (int i = 0; i < supportIslands.Count; i++)
            {
                pathFinders.Add(null);
                infillOutlines.Add(null);
            }

            Agg.Parallel.For(0, supportIslands.Count, (index) =>
            {
                var infillOffset      = -config.ExtrusionWidth_um + config.InfillExtendIntoPerimeter_um;
                var supportIsland     = supportIslands[index];
                infillOutlines[index] = supportIsland.Offset(infillOffset);

                if (config.AvoidCrossingPerimeters)
                {
                    pathFinders[index] = new PathFinder(infillOutlines[index], -config.ExtrusionWidth_um / 2, useInsideCache: config.AvoidCrossingPerimeters, name: "normal support");
                }
            });

            bool outputPaths = false;

            for (int i = 0; i < supportIslands.Count; i++)
            {
                var supportIsland = supportIslands[i];

                // force a retract if changing islands
                if (config.RetractWhenChangingIslands)
                {
                    gcodeLayer.ForceRetract();
                }

                // make a border if layer 0
                if (config.GenerateSupportPerimeter || layerIndex == 0)
                {
                    if (gcodeLayer.QueuePolygonsByOptimizer(supportIsland.Offset(-config.ExtrusionWidth_um / 2), pathFinders[i], supportNormalConfig, 0))
                    {
                        outputPaths = true;
                    }
                }

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

                    case ConfigConstants.SUPPORT_TYPE.LINES:
                        Infill.GenerateLineInfill(config, infillOutlines[i], islandInfillLines, config.SupportInfillStartingAngle, config.SupportLineSpacing_um);
                        break;
                    }
                }

                if (gcodeLayer.QueuePolygonsByOptimizer(islandInfillLines, pathFinders[i], supportNormalConfig, 0))
                {
                    outputPaths |= true;
                }
            }

            return(outputPaths);
        }
        public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if(layerIndex >= LastLayerWithChange(config)
                || extrudersThatHaveBeenPrimed == null)
            {
                return;
            }

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

                // clear the history of printer extruders for the next layer
                extrudersThatHaveBeenPrimed[extruderIndex] = false;
            }
        }
Exemple #42
0
        public static int ProcessArgs(string[] args)
        {
            if (args.Length == 0)
            {
                print_usage();
                return(0);
            }

            ConfigSettings config    = new ConfigSettings();
            fffProcessor   processor = new fffProcessor(config);

            LogOutput.Log("\nMatterSlice version {0}\n\n".FormatWith(ConfigConstants.VERSION));

            for (int argn = 0; argn < args.Length; argn++)
            {
                string str = args[argn];
                if (str[0] == '-')
                {
                    for (int stringIndex = 1; stringIndex < str.Length; stringIndex++)
                    {
                        switch (str[stringIndex])
                        {
                        case 'h':
                            print_usage();
                            return(0);

                        case 'v':
                            LogOutput.verbose_level++;
                            break;

                        case 'o':
                            argn++;
                            if (!processor.SetTargetFile(args[argn]))
                            {
                                LogOutput.LogError("Failed to open {0} for output.\n".FormatWith(args[argn]));
                                return(1);
                            }
                            break;

                        case 'c':
                        {
                            // Read a config file from the given path
                            argn++;
                            if (!config.ReadSettings(args[argn]))
                            {
                                LogOutput.LogError("Failed to read config '{0}'\n".FormatWith(args[argn]));
                            }
                        }
                        break;

                        case 'b':
                            argn++;
                            config.BooleanOpperations = args[argn];
                            break;

                        case 'd':
                            config.DumpSettings("settings.ini");
                            break;

                        case 's':
                        {
                            argn++;
                            int equalsPos = args[argn].IndexOf('=');
                            if (equalsPos != -1)
                            {
                                string key   = args[argn].Substring(0, equalsPos);
                                string value = args[argn].Substring(equalsPos + 1);
                                if (key.Length > 1)
                                {
                                    if (!config.SetSetting(key, value))
                                    {
                                        LogOutput.LogError("Setting not found: {0} {1}\n".FormatWith(key, value));
                                    }
                                }
                            }
                        }
                        break;

                        case 'm':
                            argn++;
                            throw new NotImplementedException("m");
#if false
                            sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
                                   &config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
                                   &config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
                                   &config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
#endif
                        //break;

                        default:
                            throw new NotImplementedException("Unknown option: {0}\n".FormatWith(str));
                            //LogOutput.logError("Unknown option: {0}\n".FormatWith(str));
                            //break;
                        }
                    }
                }
                else
                {
                    processor.LoadStlFile(args[argn]);
                }
            }

            if (!Canceled)
            {
                processor.DoProcessing();
            }
            if (!Canceled)
            {
                processor.finalize();
            }
            if (Canceled)
            {
                processor.Cancel();
            }

            return(0);
        }
        public void GenerateWipeTowerInfill(int extruderIndex, Polygons partOutline, Polygons outputfillPolygons, long extrusionWidth_um, ConfigSettings config)
        {
            Polygons outlineForExtruder = partOutline.Offset(-extrusionWidth_um * extruderIndex);

            long insetPerLoop = extrusionWidth_um * config.MaxExtruderCount();
            while (outlineForExtruder.Count > 0)
            {
                for (int polygonIndex = 0; polygonIndex < outlineForExtruder.Count; polygonIndex++)
                {
                    Polygon newInset = outlineForExtruder[polygonIndex];
                    newInset.Add(newInset[0]); // add in the last move so it is a solid polygon
                    outputfillPolygons.Add(newInset);
                }
                outlineForExtruder = outlineForExtruder.Offset(-insetPerLoop);
            }

            outputfillPolygons.Reverse();
        }
        public void GenerateSupportGrid(OptimizedModel model, ConfigSettings config)
        {
            this.generated = false;
            if (config.supportEndAngle < 0)
            {
                return;
            }

            this.generated = true;

            this.gridOffset.X = model.minXYZ_um.x;
            this.gridOffset.Y = model.minXYZ_um.y;
            this.gridScale    = 200;
            this.gridWidth    = (model.size_um.x / this.gridScale) + 1;
            this.gridHeight   = (model.size_um.y / this.gridScale) + 1;
            int gridSize = this.gridWidth * this.gridHeight;

            this.xYGridOfSupportPoints = new List <List <SupportPoint> >(gridSize);
            for (int i = 0; i < gridSize; i++)
            {
                this.xYGridOfSupportPoints.Add(new List <SupportPoint>());
            }

            this.endAngleDegrees         = config.supportEndAngle;
            this.generateInternalSupport = config.generateInternalSupport;
            this.supportXYDistance_um    = config.supportXYDistance_um;
            this.supportLayerHeight_um   = config.layerThickness_um;
            this.supportZGapLayers       = config.supportNumberOfLayersToSkipInZ;
            this.supportInterfaceLayers  = config.supportInterfaceLayers;

            // This should really be a ray intersection as later code is going to count on it being an even odd list of bottoms and tops.
            // As it is we are finding the hit on the plane but not checking for good intersection with the triangle.
            for (int volumeIndex = 0; volumeIndex < model.volumes.Count; volumeIndex++)
            {
                OptimizedVolume vol = model.volumes[volumeIndex];
                for (int faceIndex = 0; faceIndex < vol.facesTriangle.Count; faceIndex++)
                {
                    OptimizedFace faceTriangle = vol.facesTriangle[faceIndex];
                    Point3        v0           = vol.vertices[faceTriangle.vertexIndex[0]].position;
                    Point3        v1           = vol.vertices[faceTriangle.vertexIndex[1]].position;
                    Point3        v2           = vol.vertices[faceTriangle.vertexIndex[2]].position;

                    // get the angle of this polygon
                    double angleFromHorizon;
                    {
                        FPoint3 v0f    = new FPoint3(v0);
                        FPoint3 v1f    = new FPoint3(v1);
                        FPoint3 v2f    = new FPoint3(v2);
                        FPoint3 normal = (v1f - v0f).Cross(v2f - v0f);
                        normal.z = Math.Abs(normal.z);

                        angleFromHorizon = (Math.PI / 2) - FPoint3.CalculateAngle(normal, FPoint3.Up);
                    }

                    v0.x = (int)((v0.x - this.gridOffset.X) / (double)this.gridScale + .5);
                    v0.y = (int)((v0.y - this.gridOffset.Y) / (double)this.gridScale + .5);
                    v1.x = (int)((v1.x - this.gridOffset.X) / (double)this.gridScale + .5);
                    v1.y = (int)((v1.y - this.gridOffset.Y) / (double)this.gridScale + .5);
                    v2.x = (int)((v2.x - this.gridOffset.X) / (double)this.gridScale + .5);
                    v2.y = (int)((v2.y - this.gridOffset.Y) / (double)this.gridScale + .5);

                    if (v0.x > v1.x)
                    {
                        swap(ref v0, ref v1);
                    }
                    if (v1.x > v2.x)
                    {
                        swap(ref v1, ref v2);
                    }
                    if (v0.x > v1.x)
                    {
                        swap(ref v0, ref v1);
                    }
                    for (long x = v0.x; x < v1.x; x++)
                    {
                        long y0 = (long)(v0.y + (v1.y - v0.y) * (x - v0.x) / (double)(v1.x - v0.x) + .5);
                        long y1 = (long)(v0.y + (v2.y - v0.y) * (x - v0.x) / (double)(v2.x - v0.x) + .5);
                        long z0 = (long)(v0.z + (v1.z - v0.z) * (x - v0.x) / (double)(v1.x - v0.x) + .5);
                        long z1 = (long)(v0.z + (v2.z - v0.z) * (x - v0.x) / (double)(v2.x - v0.x) + .5);

                        if (y0 > y1)
                        {
                            swap(ref y0, ref y1);
                            swap(ref z0, ref z1);
                        }

                        for (long y = y0; y < y1; y++)
                        {
                            SupportPoint newSupportPoint = new SupportPoint((int)(z0 + (z1 - z0) * (y - y0) / (double)(y1 - y0) + .5), angleFromHorizon);
                            this.xYGridOfSupportPoints[(int)(x + y * this.gridWidth)].Add(newSupportPoint);
                        }
                    }

                    for (int x = v1.x; x < v2.x; x++)
                    {
                        long y0 = (long)(v1.y + (v2.y - v1.y) * (x - v1.x) / (double)(v2.x - v1.x) + .5);
                        long y1 = (long)(v0.y + (v2.y - v0.y) * (x - v0.x) / (double)(v2.x - v0.x) + .5);
                        long z0 = (long)(v1.z + (v2.z - v1.z) * (x - v1.x) / (double)(v2.x - v1.x) + .5);
                        long z1 = (long)(v0.z + (v2.z - v0.z) * (x - v0.x) / (double)(v2.x - v0.x) + .5);

                        if (y0 > y1)
                        {
                            swap(ref y0, ref y1);
                            swap(ref z0, ref z1);
                        }

                        for (int y = (int)y0; y < y1; y++)
                        {
                            this.xYGridOfSupportPoints[x + y * this.gridWidth].Add(new SupportPoint((int)(z0 + (z1 - z0) * (double)(y - y0) / (double)(y1 - y0) + .5), angleFromHorizon));
                        }
                    }
                }
            }

            for (int x = 0; x < this.gridWidth; x++)
            {
                for (int y = 0; y < this.gridHeight; y++)
                {
                    int gridIndex = x + y * this.gridWidth;
                    List <SupportPoint> currentList = this.xYGridOfSupportPoints[gridIndex];
                    currentList.Sort(SortSupportsOnZ);

                    if (currentList.Count > 1)
                    {
                        // now remove duplicates (try to make it a better bottom and top list)
                        for (int i = currentList.Count - 1; i >= 1; i--)
                        {
                            if (currentList[i].z == currentList[i - 1].z)
                            {
                                currentList.RemoveAt(i);
                            }
                        }
                    }
                }
            }
            this.gridOffset.X += this.gridScale / 2;
            this.gridOffset.Y += this.gridScale / 2;
        }
        private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, int distance, int extrusionWidth_um, int brimCount)
        {
            bool hasWipeTower = storage.wipeTower.PolygonLength() > 0;

            Polygons skirtPolygons = hasWipeTower ? new Polygons(storage.wipeTower) : new Polygons();

            if (config.EnableRaft)
            {
                skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline);
            }
            else
            {
                Polygons allOutlines = new Polygons();

                // Loop over every extruder
                for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
                {
                    // Only process the first extruder on spiral vase or
                    // skip extruders that have empty layers
                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0];
                        allOutlines.AddAll(layer0.Islands[0]?.IslandOutline);

                        break;
                    }

                    // Add the layers outline to allOutlines
                    SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
                    allOutlines.AddAll(layer.AllOutlines);
                }

                if (brimCount > 0)
                {
                    Polygons brimLoops = new Polygons();

                    // Loop over the requested brimCount creating and unioning a new perimeter for each island
                    for (int brimIndex = 0; brimIndex < brimCount; brimIndex++)
                    {
                        int offsetDistance = extrusionWidth_um * brimIndex + extrusionWidth_um / 2;

                        Polygons unionedIslandOutlines = new Polygons();

                        // Grow each island by the current brim distance
                        foreach (var island in allOutlines)
                        {
                            var polygons = new Polygons();
                            polygons.Add(island);

                            // Union the island brims
                            unionedIslandOutlines = unionedIslandOutlines.CreateUnion(polygons.Offset(offsetDistance));
                        }

                        // Extend the polygons to account for the brim (ensures convex hull takes this data into account)
                        brimLoops.AddAll(unionedIslandOutlines);
                    }

                    // TODO: This is a quick hack, reuse the skirt data to stuff in the brim. Good enough from proof of concept
                    storage.skirt.AddAll(brimLoops);

                    skirtPolygons = skirtPolygons.CreateUnion(brimLoops);
                }
                else
                {
                    skirtPolygons = skirtPolygons.CreateUnion(allOutlines);
                }

                if (storage.support != null)
                {
                    skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines());
                }
            }

            return skirtPolygons;
        }
Exemple #46
0
        public Slicer(OptimizedMesh ov, ConfigSettings config)
        {
            int initialLayerThickness_um = config.FirstLayerThickness_um;
            int layerThickness_um        = config.LayerThickness_um;

            modelSize = ov.containingCollection.size_um;
            modelMin  = ov.containingCollection.minXYZ_um;

            long heightWithoutFirstLayer      = modelSize.z - initialLayerThickness_um - config.BottomClipAmount_um;
            int  countOfNormalThicknessLayers = Math.Max(0, (int)((heightWithoutFirstLayer / (double)layerThickness_um) + .5));

            int layerCount = countOfNormalThicknessLayers;

            if (initialLayerThickness_um > 0)
            {
                // we have to add in the first layer (that is a different size)
                layerCount++;
            }
            if (config.outputOnlyFirstLayer)
            {
                layerCount = 1;
            }

            LogOutput.Log(string.Format("Layer count: {0}\n", layerCount));
            layers.Capacity = layerCount;
            for (int layerIndex = 0; layerIndex < layerCount; layerIndex++)
            {
                int z;
                if (layerIndex == 0)
                {
                    z = initialLayerThickness_um / 2;
                }
                else
                {
                    z = initialLayerThickness_um + layerThickness_um / 2 + layerThickness_um * (layerIndex - 1);
                }
                layers.Add(new MeshProcessingLayer(z));
            }

            for (int faceIndex = 0; faceIndex < ov.facesTriangle.Count; faceIndex++)
            {
                Point3 p0   = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[0]].position;
                Point3 p1   = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[1]].position;
                Point3 p2   = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[2]].position;
                long   minZ = p0.z;
                long   maxZ = p0.z;
                if (p1.z < minZ)
                {
                    minZ = p1.z;
                }
                if (p2.z < minZ)
                {
                    minZ = p2.z;
                }
                if (p1.z > maxZ)
                {
                    maxZ = p1.z;
                }
                if (p2.z > maxZ)
                {
                    maxZ = p2.z;
                }

                for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++)
                {
                    int z = layers[layerIndex].Z;
                    if (z < minZ || z > maxZ)
                    {
                        continue;
                    }

                    SlicePerimeterSegment polyCrossingAtThisZ;
                    if (p0.z < z && p1.z >= z && p2.z >= z)
                    {
                        // p1   p2
                        // --------
                        //   p0
                        polyCrossingAtThisZ = GetCrossingAtZ(p0, p2, p1, z);
                    }
                    else if (p0.z >= z && p1.z < z && p2.z < z)
                    {
                        //   p0
                        // --------
                        // p1  p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p0, p1, p2, z);
                    }
                    else if (p1.z < z && p0.z >= z && p2.z >= z)
                    {
                        // p0   p2
                        // --------
                        //   p1
                        polyCrossingAtThisZ = GetCrossingAtZ(p1, p0, p2, z);
                    }
                    else if (p1.z >= z && p0.z < z && p2.z < z)
                    {
                        //   p1
                        // --------
                        // p0  p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p1, p2, p0, z);
                    }
                    else if (p2.z < z && p1.z >= z && p0.z >= z)
                    {
                        // p1   p0
                        // --------
                        //   p2
                        polyCrossingAtThisZ = GetCrossingAtZ(p2, p1, p0, z);
                    }
                    else if (p2.z >= z && p1.z < z && p0.z < z)
                    {
                        //   p2
                        // --------
                        // p1  p0
                        polyCrossingAtThisZ = GetCrossingAtZ(p2, p0, p1, z);
                    }
                    else
                    {
                        //Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
                        //  on the slice would create two segments
                        continue;
                    }

                    polyCrossingAtThisZ.hasBeenAddedToPolygon = false;
                    layers[layerIndex].SegmentList.Add(polyCrossingAtThisZ);
                }
            }

            for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++)
            {
                layers[layerIndex].MakePolygons();
            }
        }
Exemple #47
0
        public void GenerateSkirt(int distance, int extrusionWidth_um, int numberOfLoops, int brimCount, int minLength, int initialLayerHeight, ConfigSettings config)
        {
            LayerDataStorage storage      = this;
            bool             externalOnly = (distance > 0);

            List <Polygons> skirtLoops = new List <Polygons>();

            Polygons skirtPolygons = GetSkirtBounds(config, storage, externalOnly, distance, extrusionWidth_um, brimCount);

            // Find convex hull for the skirt outline
            Polygons convexHull = new Polygons(new[] { skirtPolygons.CreateConvexHull() });

            // Create skirt loops from the ConvexHull
            for (int skirtLoop = 0; skirtLoop < numberOfLoops; skirtLoop++)
            {
                int offsetDistance = distance + extrusionWidth_um * skirtLoop + extrusionWidth_um / 2;

                storage.skirt.AddAll(convexHull.Offset(offsetDistance));

                int length = (int)storage.skirt.PolygonLength();
                if (skirtLoop + 1 >= numberOfLoops && length > 0 && length < minLength)
                {
                    // add more loops for as long as we have not extruded enough length
                    numberOfLoops++;
                }
            }
        }
Exemple #48
0
        private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, int distance, int extrusionWidth_um, int brimCount)
        {
            bool hasWipeTower = storage.wipeTower.PolygonLength() > 0;

            Polygons skirtPolygons = hasWipeTower ? new Polygons(storage.wipeTower) : new Polygons();

            if (config.EnableRaft)
            {
                skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline);
            }
            else
            {
                Polygons allOutlines = new Polygons();

                // Loop over every extruder
                for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
                {
                    // Only process the first extruder on spiral vase or
                    // skip extruders that have empty layers
                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0];
                        allOutlines.AddAll(layer0.Islands[0]?.IslandOutline);

                        break;
                    }

                    // Add the layers outline to allOutlines
                    SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
                    allOutlines.AddAll(layer.AllOutlines);
                }

                if (brimCount > 0)
                {
                    Polygons brimLoops = new Polygons();

                    // Loop over the requested brimCount creating and unioning a new perimeter for each island
                    for (int brimIndex = 0; brimIndex < brimCount; brimIndex++)
                    {
                        int offsetDistance = extrusionWidth_um * brimIndex + extrusionWidth_um / 2;

                        Polygons unionedIslandOutlines = new Polygons();

                        // Grow each island by the current brim distance
                        foreach (var island in allOutlines)
                        {
                            var polygons = new Polygons();
                            polygons.Add(island);

                            // Union the island brims
                            unionedIslandOutlines = unionedIslandOutlines.CreateUnion(polygons.Offset(offsetDistance));
                        }

                        // Extend the polygons to account for the brim (ensures convex hull takes this data into account)
                        brimLoops.AddAll(unionedIslandOutlines);
                    }

                    // TODO: This is a quick hack, reuse the skirt data to stuff in the brim. Good enough from proof of concept
                    storage.skirt.AddAll(brimLoops);

                    skirtPolygons = skirtPolygons.CreateUnion(brimLoops);
                }
                else
                {
                    skirtPolygons = skirtPolygons.CreateUnion(allOutlines);
                }

                if (storage.support != null)
                {
                    skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines());
                }
            }

            return(skirtPolygons);
        }
Exemple #49
0
        public static int ProcessArgs(string[] args)
        {
            if (args.Length == 0)
            {
                print_usage();
                return 0;
            }

            ConfigSettings config = new ConfigSettings();
            fffProcessor processor = new fffProcessor(config);

            LogOutput.Log("\nMatterSlice version {0}\n\n".FormatWith(ConfigConstants.VERSION));

            for (int argn = 0; argn < args.Length; argn++)
            {
                string str = args[argn];
                if (str[0] == '-')
                {
                    for (int stringIndex = 1; stringIndex < str.Length; stringIndex++)
                    {
                        switch (str[stringIndex])
                        {
                            case 'h':
                                print_usage();
                                return 0;

                            case 'v':
                                LogOutput.verbose_level++;
                                break;

                            case 'o':
                                argn++;
                                if (!processor.SetTargetFile(args[argn]))
                                {
                                    LogOutput.LogError("Failed to open {0} for output.\n".FormatWith(args[argn]));
                                    return 1;
                                }
                                break;

                            case 'c':
                                {
                                    // Read a config file from the given path
                                    argn++;
                                    if (!config.ReadSettings(args[argn]))
                                    {
                                        LogOutput.LogError("Failed to read config '{0}'\n".FormatWith(args[argn]));
                                    }
                                }
                                break;

                            case 'b':
                                argn++;
                                config.BooleanOpperations = args[argn];
                                break;

                            case 'd':
                                config.DumpSettings("settings.ini");
                                break;

                            case 's':
                                {
                                    argn++;
                                    int equalsPos = args[argn].IndexOf('=');
                                    if (equalsPos != -1)
                                    {
                                        string key = args[argn].Substring(0, equalsPos);
                                        string value = args[argn].Substring(equalsPos + 1);
                                        if (key.Length > 1)
                                        {
                                            if (!config.SetSetting(key, value))
                                            {
                                                LogOutput.LogError("Setting not found: {0} {1}\n".FormatWith(key, value));
                                            }
                                        }
                                    }
                                }
                                break;

                            case 'm':
                                argn++;
                                throw new NotImplementedException("m");
            #if false
                        sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
                        &config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
                        &config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
                        &config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
            #endif
                            //break;

                            default:
                                throw new NotImplementedException("Unknown option: {0}\n".FormatWith(str));
                            //LogOutput.logError("Unknown option: {0}\n".FormatWith(str));
                            //break;
                        }
                    }
                }
                else
                {
                    processor.LoadStlFile(args[argn]);
                }
            }

            if (!Canceled)
            {
                processor.DoProcessing();
            }
            if (!Canceled)
            {
                processor.finalize();
            }
            if (Canceled)
            {
                processor.Cancel();
            }

            return 0;
        }
Exemple #50
0
        public void WriteRaftGCodeIfRequired(GCodeExport gcode, ConfigSettings config)
        {
            LayerDataStorage storage = this;

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

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

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

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

                    gcode.SetZ(config.RaftBaseThickness_um);

                    gcode.LayerChanged(-3);

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

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

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

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

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

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

                    gcodeLayer.WriteQueuedGCode(config.RaftBaseThickness_um);
                }

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

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

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

                    gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um);
                }

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

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

                    gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um);
                }
            }
        }
		public void WriteRaftGCodeIfRequired(ConfigSettings config, GCodeExport gcode)
		{
			LayerDataStorage storage = this;
			if (config.ShouldGenerateRaft())
			{
				GCodePathConfig raftBaseConfig = new GCodePathConfig(config.firstLayerSpeed, config.raftBaseExtrusionWidth_um, "SUPPORT");
				GCodePathConfig raftMiddleConfig = new GCodePathConfig(config.raftPrintSpeed, config.raftInterfaceExtrusionWidth_um, "SUPPORT");
				GCodePathConfig raftSurfaceConfig = new GCodePathConfig((config.raftSurfacePrintSpeed > 0) ? config.raftSurfacePrintSpeed : config.raftPrintSpeed, config.raftSurfaceExtrusionWidth_um, "SUPPORT");

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

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

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

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

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

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

					gcodeLayer.WriteQueuedGCode(config.raftBaseThickness_um);
				}

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

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

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

					gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um);
				}

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

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

					gcodeLayer.WriteQueuedGCode(config.raftInterfaceThicknes_um);
				}
			}
		}
Exemple #52
0
        public void GenerateWipeTowerInfill(int extruderIndex, Polygons partOutline, Polygons outputfillPolygons, long extrusionWidth_um, ConfigSettings config)
        {
            Polygons outlineForExtruder = partOutline.Offset(-extrusionWidth_um * extruderIndex);

            long insetPerLoop = extrusionWidth_um * config.MaxExtruderCount();

            while (outlineForExtruder.Count > 0)
            {
                for (int polygonIndex = 0; polygonIndex < outlineForExtruder.Count; polygonIndex++)
                {
                    Polygon newInset = outlineForExtruder[polygonIndex];
                    newInset.Add(newInset[0]);                     // add in the last move so it is a solid polygon
                    outputfillPolygons.Add(newInset);
                }
                outlineForExtruder = outlineForExtruder.Offset(-insetPerLoop);
            }

            outputfillPolygons.Reverse();
        }
Exemple #53
0
		public static void GenerateHexagonInfill(ConfigSettings config, Polygons partOutline, ref Polygons fillPolygons, double fillAngle, int layerIndex)
		{
			if (config.infillPercent <= 0)
			{
				throw new Exception("infillPercent must be gerater than 0.");
			}

			int linespacing_um = (int)(config.extrusionWidth_um / (config.infillPercent / 100) * 3 * .66);

			Infill.GenerateHexLinePaths(partOutline, ref fillPolygons, linespacing_um, config.infillExtendIntoPerimeter_um, fillAngle, layerIndex);
		}
Exemple #54
0
        public void PrimeOnWipeTower(int extruderIndex, int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (!HaveWipeTower(config) ||
                layerIndex > LastLayerWithChange(config) + 1)
            {
                return;
            }

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

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

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

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

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

            polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect);

            return polysToAddTo;
        }
Exemple #56
0
        public void EnsureWipeTowerIsSolid(int layerIndex, GCodePlanner gcodeLayer, GCodePathConfig fillConfig, ConfigSettings config)
        {
            if (layerIndex >= LastLayerWithChange(config) ||
                extrudersThatHaveBeenPrimed == null)
            {
                return;
            }

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

                // clear the history of printer extruders for the next layer
                extrudersThatHaveBeenPrimed[extruderIndex] = false;
            }
        }
        private void AddSupportToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config)
        {
            if (!storage.support.generated)
            {
                return;
            }

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

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

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

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

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

            if (config.supportInterfaceExtruder != -1
                && config.supportInterfaceExtruder != config.supportExtruder)
            {
                gcodeLayer.SetExtruder(config.supportInterfaceExtruder);
            }
            WriteSupportPolygons(storage, gcodeLayer, layerIndex, config, supportGenerator.interfacePolygons, SupportType.Interface);
        }
		public void TestCorrectSupportLayer()
		{
			// test the supports for a simple cube in the air
			{
				ConfigSettings config = new ConfigSettings();
				config.layerThickness = .5;
				config.supportXYDistanceFromObject = 0;
				config.supportInterfaceLayers = 0;
				config.minimizeSupportColumns = false;

				List<Polygons> partOutlines = new List<Polygons>();
				for (int i = 0; i < 5; i++)
				{
					partOutlines.Add(new Polygons());
				}

				Polygons cubeOutline = PolygonsHelper.CreateFromString("x:0, y:0,x:10000, y:0,x:10000, y:10000,x:0, y:10000,|");
				for (int i = 0; i < 5; i++)
				{
					partOutlines.Add(cubeOutline);
				}

				ExtruderLayers layerData = CreateLayerData(partOutlines);
				NewSupport supportGenerator = new NewSupport(config, new List<ExtruderLayers>() { layerData }, 0);

				Polygons cubeOutlineResults = PolygonsHelper.CreateFromString("x:200, y:200,x:9800, y:200,x:9800, y:9800,x:200, y:9800,|");

				// check the all part outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, null, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, };
					CheckLayers(supportGenerator.insetPartOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				// check the potential support outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, cubeOutlineResults, null, null, null, null, null };
					CheckLayers(supportGenerator.allPotentialSupportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				// check the required support outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, cubeOutlineResults, null, null, null, null, null };
					CheckLayers(supportGenerator.allRequiredSupportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				// check the generated support outlines
				{
					List<int> polygonsCounts = new List<int> { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, null, null, null, null, null };
					CheckLayers(supportGenerator.supportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				// check the interface support outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, null, null, null, null, null, null };
					CheckLayers(supportGenerator.interfaceLayers, polygonsCounts, polygon0Counts, poly0Paths);
				}
			}

			// test the supports for a cube that is 1/2 width just under the main part
			{
				ConfigSettings config = new ConfigSettings();
				config.supportInterfaceLayers = 0;
				config.layerThickness = .5;
				config.supportXYDistanceFromObject = .1;

				// 14 XXXXXXXXXXXXXXXXXXXX
				// 13 XXXXXXXXXXXXXXXXXXXX
				// 12 XXXXXXXXXXXXXXXXXXXX
				// 11 XXXXXXXXXXXXXXXXXXXX
				// 10 XXXXXXXXXXXXXXXXXXXX
				// 9  XXXXXXXXXX           <- interface layer
				// 8  XXXXXXXXXX           <- interface layer
				// 7  XXXXXXXXXX     ^ - requires support  
				// 6  XXXXXXXXXX
				// 5  XXXXXXXXXX
				// 4             <- interface layer
				// 3             <- interface layer
				// 2      ^ - requires support  
				// 1 
				// 0 

				List<Polygons> partOutlines = new List<Polygons>();
				for (int i = 0; i < 5; i++)
				{
					partOutlines.Add(new Polygons());
				}

				Polygons halfCubeOutline = PolygonsHelper.CreateFromString("x:0, y:0,x:5000, y:0,x:5000, y:10000,x:0, y:10000,|");
				Polygons halfCubeOutlineResults = halfCubeOutline.Offset(-200);
				for (int i = 0; i < 5; i++)
				{
					partOutlines.Add(halfCubeOutline);
				}

				Polygons cubeOutline = PolygonsHelper.CreateFromString("x:0, y:0,x:10000, y:0,x:10000, y:10000,x:0, y:10000,|");
				Polygons cubeOutlineResults = cubeOutline.Offset(-200);
				for (int i = 0; i < 5; i++)
				{
					partOutlines.Add(cubeOutline);
				}

				ExtruderLayers layerData = CreateLayerData(partOutlines);
				NewSupport supportGenerator = new NewSupport(config, new List<ExtruderLayers>() { layerData }, 1);

				// check the all part outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 0,
						1, 1, 1, 1, 1,
						1, 1, 1, 1, 1,};
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 0,
						4, 4, 4, 4, 4,
						4, 4, 4, 4, 4,};
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, null,
						halfCubeOutlineResults, halfCubeOutlineResults, halfCubeOutlineResults, halfCubeOutlineResults, halfCubeOutlineResults,
						cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, };
					CheckLayers(supportGenerator.insetPartOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				Polygons layer9Support = PolygonsHelper.CreateFromString("x:5000, y:200,x:9800, y:200,x:9800, y:9800,x:5000, y:9800,|");
				// check the potential support outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, halfCubeOutlineResults, null, null, null, null, layer9Support, null, null, null, null, null };
					CheckLayers(supportGenerator.allPotentialSupportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				// check the required support outlines
				{
					List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, };
					List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, };
					List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, halfCubeOutlineResults, null, null, null, null, layer9Support, null, null, null, null, null };
					CheckLayers(supportGenerator.allRequiredSupportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
				}

				if (false)
				{
					// check the generated support outlines
					{
						List<int> polygonsCounts = new List<int> { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, };
						List<int> polygon0Counts = new List<int> { 4, 4, 4, 4, 4, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, };
						List<Polygons> poly0Paths = new List<Polygons>() { cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, cubeOutlineResults, null, null, null, null, null };
						CheckLayers(supportGenerator.supportOutlines, polygonsCounts, polygon0Counts, poly0Paths);
					}

					// check the interface support outlines
					{
						List<int> polygonsCounts = new List<int> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
						List<int> polygon0Counts = new List<int> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
						List<Polygons> poly0Paths = new List<Polygons>() { null, null, null, null, null, null, null, null, null, null };
						CheckLayers(supportGenerator.interfaceLayers, polygonsCounts, polygon0Counts, poly0Paths);
					}
				}
			}
		}
        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);
            }
        }
Exemple #60
0
        public void PrimeOnWipeTower(int layerIndex, LayerGCodePlanner layerGcodePlanner, GCodePathConfig fillConfig, ConfigSettings config, bool airGapped)
        {
            if (!HaveWipeTower(config) ||
                layerIndex > LastLayerWithChange(config) + 1 ||
                layerIndex == 0)
            {
                return;
            }

            if (airGapped)
            {
                // don't print the wipe tower with air gap height
                layerGcodePlanner.CurrentZ -= config.SupportAirGap_um;
            }

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

            var oldPathFinder = layerGcodePlanner.PathFinder;

            layerGcodePlanner.PathFinder = null;
            GenerateWipeTowerInfill(primesThisLayer, this.WipeTower, fillPolygons, fillConfig.LineWidthUM, config);
            layerGcodePlanner.QueuePolygons(fillPolygons, fillConfig);
            layerGcodePlanner.PathFinder = oldPathFinder;

            if (airGapped)
            {
                // don't print the wipe tower with air gap height
                layerGcodePlanner.CurrentZ += config.SupportAirGap_um;
            }

            primesThisLayer++;
        }