public void GenerateInsets(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 sure our polygon data is reasonable currentInset = Clipper.CleanPolygons(currentInset, minimumDistanceToCreateNewPosition); // check that we have actual paths if (currentInset.Count > 0) { 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; } } } }
public void CalculateInfillData(ConfigSettings config, int extruderIndex, int layerIndex, LayerIsland part, Polygons bottomFillLines, Polygons sparseFillPolygons = null, Polygons solidFillPolygons = null, Polygons firstTopFillPolygons = null, Polygons topFillPolygons = null, Polygons bridgePolygons = null, Polygons bridgeAreas = null) { double alternatingInfillAngle = config.InfillStartingAngle; if ((layerIndex % 2) == 0) { alternatingInfillAngle += 90; } // generate infill for the bottom layer including bridging foreach (Polygons bottomFillIsland in part.BottomPaths.ProcessIntoSeparateIslands()) { if (layerIndex > 0) { if (this.Support != null) { double infillAngle = config.SupportInterfaceLayers > 0 ? config.InfillStartingAngle : config.InfillStartingAngle + 90; Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, infillAngle); } else { SliceLayer previousLayer = this.Extruders[extruderIndex].Layers[layerIndex - 1]; if (bridgePolygons != null && previousLayer.BridgeAngle(bottomFillIsland, config.GetNumberOfPerimeters() * config.ExtrusionWidth_um, out double bridgeAngle, bridgeAreas)) { // 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 // we still need to extrude at bridging speed { Infill.GenerateLinePaths(bottomFillIsland, bottomFillLines, config.ExtrusionWidth_um, config.InfillExtendIntoPerimeter_um, alternatingInfillAngle, 0, config.BridgeSpeed); } } }
public void DumpLayerparts(string filename) { var streamToWriteTo = new StreamWriter(filename); streamToWriteTo.Write("<!DOCTYPE html><html><body>"); for (int extruderIndex = 0; extruderIndex < this.Extruders.Count; extruderIndex++) { for (int layerNr = 0; layerNr < this.Extruders[extruderIndex].Layers.Count; layerNr++) { streamToWriteTo.Write("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n"); SliceLayer layer = this.Extruders[extruderIndex].Layers[layerNr]; for (int i = 0; i < layer.Islands.Count; i++) { LayerIsland part = layer.Islands[i]; for (int j = 0; j < part.IslandOutline.Count; j++) { streamToWriteTo.Write("<polygon points=\""); for (int k = 0; k < part.IslandOutline[j].Count; k++) { streamToWriteTo.Write("{0},{1} ".FormatWith((float)(part.IslandOutline[j][k].X - modelMin.X) / modelSize.X * 500, (float)(part.IslandOutline[j][k].Y - modelMin.Y) / modelSize.Y * 500)); } if (j == 0) { streamToWriteTo.Write("\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n"); } else { streamToWriteTo.Write("\" style=\"fill:red; stroke:black;stroke-width:1\" />\n"); } } } streamToWriteTo.Write("</svg>\n"); } } streamToWriteTo.Write("</body></html>"); streamToWriteTo.Close(); }
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]; Agg.Parallel.For(0, layer.Islands.Count, (islandIndex) => // 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; Agg.Parallel.For(upStart, upEnd, (layerToTest) => // 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); Agg.Parallel.For(downStart, downEnd, (layerToTest) => // 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 GenerateTopAndBottoms(int layerIndex, int extrusionWidth_um, int outerPerimeterWidth_um, int downLayerCount, int upLayerCount) { ExtruderLayers extruder = this; SliceLayer layer = extruder.Layers[layerIndex]; for (int islandIndex = 0; islandIndex < layer.Islands.Count; islandIndex++) { LayerIsland island = layer.Islands[islandIndex]; // 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 insetWithOffset = lastInset.Offset(-extrusionWidth_um); Polygons infillOutlines = new Polygons(insetWithOffset); // calculate the bottom outlines if (downLayerCount > 0) { Polygons bottomOutlines = new Polygons(insetWithOffset); if (layerIndex - 1 >= 0) { bottomOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex - 1].Islands, island.BoundingBox, bottomOutlines); bottomOutlines.RemoveSmallAreas(extrusionWidth_um); } infillOutlines = infillOutlines.CreateDifference(bottomOutlines); infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um); island.SolidBottomToolPaths = bottomOutlines; } // calculate the top outlines if (upLayerCount > 0) { Polygons topOutlines = new Polygons(insetWithOffset); topOutlines = topOutlines.CreateDifference(island.SolidBottomToolPaths); topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um); for (int insetIndex = 0; insetIndex < island.InsetToolPaths.Count - 1; insetIndex++) { // Add thin wall filling by taking the area between the insets. Polygons largerInset = island.InsetToolPaths[insetIndex].Offset(-extrusionWidth_um / 2); Polygons smallerInset = island.InsetToolPaths[insetIndex + 1].Offset(extrusionWidth_um / 2); Polygons thinWalls = largerInset.CreateDifference(smallerInset).Offset(-extrusionWidth_um / 4); if (thinWalls.Count > 0) { topOutlines.AddAll(thinWalls); } } 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); infillOutlines = infillOutlines.CreateDifference(topOutlines); infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um); island.SolidTopToolPaths = topOutlines; } // calculate the solid infill outlines if (upLayerCount > 1 || downLayerCount > 1) { Polygons solidInfillOutlines = new Polygons(insetWithOffset); solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidBottomToolPaths); solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um); solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidTopToolPaths); solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um); int upEnd = layerIndex + upLayerCount + 1; if (upEnd <= extruder.Layers.Count && layerIndex - downLayerCount >= 0) { Polygons totalPartsToRemove = new Polygons(insetWithOffset); int upStart = layerIndex + 2; for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++) { totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove); totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um); } int downStart = layerIndex - 1; int downEnd = layerIndex - downLayerCount; for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--) { totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove); totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um); } solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove); solidInfillOutlines.RemoveSmallAreas(extrusionWidth_um); solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um); } island.SolidInfillToolPaths = solidInfillOutlines; infillOutlines = infillOutlines.CreateDifference(solidInfillOutlines); } infillOutlines.RemoveSmallAreas(extrusionWidth_um); infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um); island.InfillToolPaths = infillOutlines; } }
private void CalculateInfillData(LayerDataStorage slicingData, int extruderIndex, int layerIndex, LayerIsland part, ref Polygons bottomFillPolygons, ref Polygons fillPolygons, ref Polygons topFillPolygons, ref Polygons bridgePolygons) { // generate infill the bottom layer including bridging foreach (Polygons outline in part.SolidBottomToolPaths.ProcessIntoSeparatIslands()) { if (layerIndex > 0) { double bridgeAngle = 0; SliceLayer previousLayer = slicingData.Extruders[extruderIndex].Layers[layerIndex - 1]; if (!config.generateSupport && previousLayer.BridgeAngle(outline, out bridgeAngle)) { Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle); } else { Infill.GenerateLinePaths(outline, ref bottomFillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } else { Infill.GenerateLinePaths(outline, ref bottomFillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } // generate infill for the top layer foreach (Polygons outline in part.SolidTopToolPaths.ProcessIntoSeparatIslands()) { Infill.GenerateLinePaths(outline, ref topFillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } // generate infill intermediate layers foreach (Polygons outline in part.SolidInfillToolPaths.ProcessIntoSeparatIslands()) { if (true) // use the old infill method { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90 * (layerIndex % 2)); } else // use the new concentric infill (not tested enough yet) have to handle some bad cases better { double oldInfillPercent = config.infillPercent; config.infillPercent = 100; Infill.GenerateConcentricInfill(config, outline, ref fillPolygons); config.infillPercent = oldInfillPercent; } } double fillAngle = config.infillStartingAngle; // generate the sparse infill for this part on this layer if (config.infillPercent > 0) { switch (config.infillType) { case ConfigConstants.INFILL_TYPE.LINES: if ((layerIndex & 1) == 1) { fillAngle += 90; } Infill.GenerateLineInfill(config, part.InfillToolPaths, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.GRID: Infill.GenerateGridInfill(config, part.InfillToolPaths, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.TRIANGLES: Infill.GenerateTriangleInfill(config, part.InfillToolPaths, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.HEXAGON: Infill.GenerateHexagonInfill(config, part.InfillToolPaths, ref fillPolygons, fillAngle, layerIndex); break; case ConfigConstants.INFILL_TYPE.CONCENTRIC: Infill.GenerateConcentricInfill(config, part.InfillToolPaths, ref fillPolygons); break; default: throw new NotImplementedException(); } } }
public void GenerateInsets(ConfigSettings config, 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 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; } } } }