private void CreateWipeShields(SliceDataStorage storage, int totalLayers) { for (int layerNr = 0; layerNr < totalLayers; layerNr++) { Polygons wipeShield = new Polygons(); for (int volumeIdx = 0; volumeIdx < storage.volumes.Count; volumeIdx++) { for (int partNr = 0; partNr < storage.volumes[volumeIdx].layers[layerNr].parts.Count; partNr++) { wipeShield = wipeShield.CreateUnion(storage.volumes[volumeIdx].layers[layerNr].parts[partNr].outline.Offset(config.wipeShieldDistanceFromShapes_um)); } } storage.wipeShield.Add(wipeShield); } for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++) { storage.wipeShield[layerIndex] = storage.wipeShield[layerIndex].Offset(-1000).Offset(1000); } int offsetAngle = (int)Math.Tan(60.0 * Math.PI / 180) * config.layerThickness_um;//Allow for a 60deg angle in the wipeShield. for (int layerNr = 1; layerNr < totalLayers; layerNr++) { storage.wipeShield[layerNr] = storage.wipeShield[layerNr].CreateUnion(storage.wipeShield[layerNr - 1].Offset(-offsetAngle)); } for (int layerNr = totalLayers - 1; layerNr > 0; layerNr--) { storage.wipeShield[layerNr - 1] = storage.wipeShield[layerNr - 1].CreateUnion(storage.wipeShield[layerNr].Offset(-offsetAngle)); } }
private static List <Polygons> AccumulateDownPolygons(ConfigSettings config, List <Polygons> inputPolys, List <Polygons> allPartOutlines) { int numLayers = inputPolys.Count; long nozzleSize = config.ExtrusionWidth_um; 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); // 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); }
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes. //This generates some overlap in dual extrusion, for better bonding in touching parts. public static void OverlapMultipleVolumesSlightly(List <SliceVolumeStorage> volumes, int overlap) { if (volumes.Count < 2 || overlap <= 0) { return; } for (int layerIndex = 0; layerIndex < volumes[0].layers.Count; layerIndex++) { Polygons fullLayer = new Polygons(); for (int volIdx = 0; volIdx < volumes.Count; volIdx++) { SliceLayer layer1 = volumes[volIdx].layers[layerIndex]; for (int p1 = 0; p1 < layer1.parts.Count; p1++) { fullLayer = fullLayer.CreateUnion(layer1.parts[p1].outline.Offset(20)); } } fullLayer = fullLayer.Offset(-20); for (int volumeIndex = 0; volumeIndex < volumes.Count; volumeIndex++) { SliceLayer layer1 = volumes[volumeIndex].layers[layerIndex]; for (int partIndex = 0; partIndex < layer1.parts.Count; partIndex++) { layer1.parts[partIndex].outline = fullLayer.CreateIntersection(layer1.parts[partIndex].outline.Offset(overlap / 2)); } } } }
public static void generateTopAndBottomLayers(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downSkinCount, int upSkinCount) { SliceLayer layer = storage.layers[layerIndex]; for (int partNr = 0; partNr < layer.parts.Count; partNr++) { SliceLayerPart part = layer.parts[partNr]; Polygons upskin = part.insets[part.insets.Count - 1].Offset(-extrusionWidth / 2); Polygons downskin = upskin; 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)); upskin.AddAll(thinWalls); downskin.AddAll(thinWalls); } if (layerIndex - downSkinCount >= 0) { SliceLayer layer2 = storage.layers[layerIndex - downSkinCount]; for (int partIndex = 0; partIndex < layer2.parts.Count; partIndex++) { if (part.boundaryBox.hit(layer2.parts[partIndex].boundaryBox)) { downskin = downskin.CreateDifference(layer2.parts[partIndex].insets[layer2.parts[partIndex].insets.Count - 1]); } } } if (layerIndex + upSkinCount < storage.layers.Count) { SliceLayer layer2 = storage.layers[layerIndex + upSkinCount]; for (int partIndex = 0; partIndex < layer2.parts.Count; partIndex++) { if (part.boundaryBox.hit(layer2.parts[partIndex].boundaryBox)) { upskin = upskin.CreateDifference(layer2.parts[partIndex].insets[layer2.parts[partIndex].insets.Count - 1]); } } } part.skinOutline = upskin.CreateUnion(downskin); double minAreaSize = (2 * Math.PI * (extrusionWidth / 1000.0) * (extrusionWidth / 1000.0)) * 0.3; for (int outlineIndex = 0; outlineIndex < part.skinOutline.Count; outlineIndex++) { double area = Math.Abs(part.skinOutline[outlineIndex].Area()) / 1000.0 / 1000.0; if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" { part.skinOutline.RemoveAt(outlineIndex); outlineIndex -= 1; } } } }
private static Polygons AddAllOutlines(SliceLayer layerToAdd, SliceLayerPart partToUseAsBounds, Polygons polysToAddTo) { Polygons polysToIntersect = new Polygons(); for (int partIndex = 0; partIndex < layerToAdd.parts.Count; partIndex++) { if (partToUseAsBounds.BoundingBox.Hit(layerToAdd.parts[partIndex].BoundingBox)) { polysToIntersect = polysToIntersect.CreateUnion(layerToAdd.parts[partIndex].Insets[layerToAdd.parts[partIndex].Insets.Count - 1]); polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um); } } polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect); return(polysToAddTo); }
private static Polygons AddIslandsToPolygons(List <LayerIsland> islands, Aabb boundsToConsider, Polygons polysToAddTo) { Polygons polysToIntersect = new Polygons(); for (int islandIndex = 0; islandIndex < islands.Count; islandIndex++) { if (boundsToConsider.Hit(islands[islandIndex].BoundingBox)) { polysToIntersect = polysToIntersect.CreateUnion(islands[islandIndex].InsetToolPaths[islands[islandIndex].InsetToolPaths.Count - 1]); polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um); } } polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect); return(polysToAddTo); }
private static List <Polygons> RemoveSupportFromInternalSpaces(List <Polygons> inputPolys, List <Polygons> allPartOutlines) { int numLayers = inputPolys.Count; Polygons accumulatedLayers = new Polygons(); for (int layerIndex = 0; layerIndex < numLayers; layerIndex++) { accumulatedLayers = accumulatedLayers.CreateUnion(allPartOutlines[layerIndex]); accumulatedLayers = Clipper.CleanPolygons(accumulatedLayers, cleanDistance_um); inputPolys[layerIndex] = inputPolys[layerIndex].CreateDifference(accumulatedLayers); inputPolys[layerIndex] = Clipper.CleanPolygons(inputPolys[layerIndex], cleanDistance_um); } return(inputPolys); }
private static Polygons IntersectWithSparsePolygons(List <LayerIsland> islands, Aabb boundsToConsider, Polygons polysToIntersect) { Polygons polysFromIslands = new Polygons(); for (int islandIndex = 0; islandIndex < islands.Count; islandIndex++) { if (boundsToConsider.Hit(islands[islandIndex].BoundingBox)) { if (islands[islandIndex].InsetToolPaths.Count > 0) { polysFromIslands = polysFromIslands.CreateUnion(islands[islandIndex].SparseInfillPaths); polysFromIslands = Clipper.CleanPolygons(polysFromIslands, cleanDistance_um); } } } polysToIntersect = polysToIntersect.CreateIntersection(polysFromIslands); return(polysToIntersect); }
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)); } }
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other extruders. //This generates some overlap in dual extrusion, for better bonding in touching parts. public static void OverlapMultipleExtrudersSlightly(List <ExtruderLayers> extruders, int overlapUm) { if (extruders.Count < 2 || overlapUm <= 0) { return; } for (int layerIndex = 0; layerIndex < extruders[0].Layers.Count; layerIndex++) { Polygons fullLayer = new Polygons(); for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { SliceLayer layer1 = extruders[extruderIndex].Layers[layerIndex]; fullLayer = fullLayer.CreateUnion(layer1.AllOutlines.Offset(20)); } fullLayer = fullLayer.Offset(-20); for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { SliceLayer layer1 = extruders[extruderIndex].Layers[layerIndex]; layer1.AllOutlines = fullLayer.CreateIntersection(layer1.AllOutlines.Offset(overlapUm / 2)); } } }
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); }
private static List <Polygons> CreateInterfaceLayers(List <Polygons> inputPolys, int numInterfaceLayers) { int numLayers = inputPolys.Count; List <Polygons> allInterfaceLayers = CreateEmptyPolygons(numLayers); if (numInterfaceLayers > 0) { for (int layerIndex = 0; layerIndex < numLayers; layerIndex++) { Polygons accumulatedAbove = inputPolys[layerIndex].DeepCopy(); for (int addIndex = layerIndex + 1; addIndex < Math.Min(layerIndex + numInterfaceLayers, numLayers - 2); addIndex++) { accumulatedAbove = accumulatedAbove.CreateUnion(inputPolys[addIndex]); accumulatedAbove = Clipper.CleanPolygons(accumulatedAbove, cleanDistance_um); } allInterfaceLayers[layerIndex] = accumulatedAbove; } } return(allInterfaceLayers); }
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 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 static void generateSparse(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downSkinCount, int upSkinCount) { SliceLayer layer = storage.layers[layerIndex]; for (int partNr = 0; partNr < layer.parts.Count; partNr++) { SliceLayerPart part = layer.parts[partNr]; Polygons sparse = part.insets[part.insets.Count - 1].Offset(-extrusionWidth / 2); Polygons downskin = sparse; Polygons upskin = sparse; if ((int)(layerIndex - downSkinCount) >= 0) { SliceLayer layer2 = storage.layers[layerIndex - downSkinCount]; for (int partNr2 = 0; partNr2 < layer2.parts.Count; partNr2++) { if (part.boundaryBox.hit(layer2.parts[partNr2].boundaryBox)) { if (layer2.parts[partNr2].insets.Count > 1) { downskin = downskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 2]); } else { downskin = downskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 1]); } } } } if ((int)(layerIndex + upSkinCount) < (int)storage.layers.Count) { SliceLayer layer2 = storage.layers[layerIndex + upSkinCount]; for (int partNr2 = 0; partNr2 < layer2.parts.Count; partNr2++) { if (part.boundaryBox.hit(layer2.parts[partNr2].boundaryBox)) { if (layer2.parts[partNr2].insets.Count > 1) { upskin = upskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 2]); } else { upskin = upskin.CreateDifference(layer2.parts[partNr2].insets[layer2.parts[partNr2].insets.Count - 1]); } } } } Polygons result = upskin.CreateUnion(downskin); double minAreaSize = 3.0;//(2 * M_PI * ((double)(config.extrusionWidth) / 1000.0) * ((double)(config.extrusionWidth) / 1000.0)) * 3; for (int i = 0; i < result.Count; i++) { double area = Math.Abs(result[i].Area()) / 1000.0 / 1000.0; if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */ { result.RemoveAt(i); i -= 1; } } part.sparseOutline = sparse.CreateDifference(result); } }
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other extruders. //This generates some overlap in dual extrusion, for better bonding in touching parts. public static void OverlapMultipleExtrudersSlightly(List<ExtruderLayers> extruders, int overlapUm) { if (extruders.Count < 2 || overlapUm <= 0) { return; } for (int layerIndex = 0; layerIndex < extruders[0].Layers.Count; layerIndex++) { Polygons fullLayer = new Polygons(); for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { SliceLayer layer1 = extruders[extruderIndex].Layers[layerIndex]; fullLayer = fullLayer.CreateUnion(layer1.AllOutlines.Offset(20)); } fullLayer = fullLayer.Offset(-20); for (int extruderIndex = 0; extruderIndex < extruders.Count; extruderIndex++) { SliceLayer layer1 = extruders[extruderIndex].Layers[layerIndex]; layer1.AllOutlines = fullLayer.CreateIntersection(layer1.AllOutlines.Offset(overlapUm / 2)); } } }
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 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; } }
private static List<Polygons> RemoveSupportFromInternalSpaces(List<Polygons> inputPolys, List<Polygons> allPartOutlines) { int numLayers = inputPolys.Count; Polygons accumulatedLayers = new Polygons(); for (int layerIndex = 0; layerIndex < numLayers; layerIndex++) { accumulatedLayers = accumulatedLayers.CreateUnion(allPartOutlines[layerIndex]); accumulatedLayers = Clipper.CleanPolygons(accumulatedLayers, cleanDistance_um); inputPolys[layerIndex] = inputPolys[layerIndex].CreateDifference(accumulatedLayers); inputPolys[layerIndex] = Clipper.CleanPolygons(inputPolys[layerIndex], cleanDistance_um); } return inputPolys; }
private static Polygons AddIslandsToPolygons(List<LayerIsland> islands, Aabb boundsToConsider, Polygons polysToAddTo) { Polygons polysToIntersect = new Polygons(); for (int islandIndex = 0; islandIndex < islands.Count; islandIndex++) { if (boundsToConsider.Hit(islands[islandIndex].BoundingBox)) { polysToIntersect = polysToIntersect.CreateUnion(islands[islandIndex].InsetToolPaths[islands[islandIndex].InsetToolPaths.Count - 1]); polysToIntersect = Clipper.CleanPolygons(polysToIntersect, cleanDistance_um); } } polysToAddTo = polysToAddTo.CreateIntersection(polysToIntersect); return polysToAddTo; }
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 = new Polygons(); if (config.EnableRaft) { skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline); } else { Polygons allOutlines = hasWipeTower ? new Polygons(storage.wipeTower.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]; 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()); } brimIslandOutlines = brimIslandOutlines.Offset(extrusionWidth_um * (brimCount - 1)); // Loop over the requested brimCount creating and unioning a new perimeter for each island List <Polygons> brimIslands = brimIslandOutlines.ProcessIntoSeparateIslands(); foreach (var brimIsland in brimIslands) { Polygons brimLoops = new Polygons(); for (int brimIndex = brimCount - 1; brimIndex >= 0; brimIndex--) { int offsetDistance = extrusionWidth_um * brimIndex; // Extend the polygons to account for the brim (ensures convex hull takes this data into account) brimLoops.AddAll(brimIsland.Offset(-offsetDistance + extrusionWidth_um / 2)); } storage.Brims.Add(brimLoops); } // and extend the bonuds of the skirt polygons skirtPolygons = skirtPolygons.CreateUnion(brimIslandOutlines); } skirtPolygons = skirtPolygons.CreateUnion(allOutlines); if (storage.support != null) { skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines()); } } return(skirtPolygons); }
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 = new Polygons(); if (config.EnableRaft) { skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline); } else { Polygons allOutlines = hasWipeTower ? new Polygons(storage.wipeTower) : 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); } 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 unionedIslandOutlines = new Polygons(); // Grow each island by the current brim distance // Union the island brims unionedIslandOutlines = unionedIslandOutlines.CreateUnion(allOutlines); if (storage.support != null) { unionedIslandOutlines = unionedIslandOutlines.CreateUnion(storage.support.GetBedOutlines()); } 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; // Extend the polygons to account for the brim (ensures convex hull takes this data into account) brimLoops.AddAll(unionedIslandOutlines.Offset(offsetDistance)); } // 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); }
private void CreateWipeShields(LayerDataStorage slicingData, int totalLayers) { for (int layerNr = 0; layerNr < totalLayers; layerNr++) { Polygons wipeShield = new Polygons(); for (int extruderIndex = 0; extruderIndex < slicingData.Extruders.Count; extruderIndex++) { for (int partNr = 0; partNr < slicingData.Extruders[extruderIndex].Layers[layerNr].Islands.Count; partNr++) { wipeShield = wipeShield.CreateUnion(slicingData.Extruders[extruderIndex].Layers[layerNr].Islands[partNr].IslandOutline.Offset(config.wipeShieldDistanceFromShapes_um)); } } slicingData.wipeShield.Add(wipeShield); } for (int layerIndex = 0; layerIndex < totalLayers; layerIndex++) { slicingData.wipeShield[layerIndex] = slicingData.wipeShield[layerIndex].Offset(-1000).Offset(1000); } int offsetAngle = (int)Math.Tan(60.0 * Math.PI / 180) * config.layerThickness_um;//Allow for a 60deg angle in the wipeShield. for (int layerNr = 1; layerNr < totalLayers; layerNr++) { slicingData.wipeShield[layerNr] = slicingData.wipeShield[layerNr].CreateUnion(slicingData.wipeShield[layerNr - 1].Offset(-offsetAngle)); } for (int layerNr = totalLayers - 1; layerNr > 0; layerNr--) { slicingData.wipeShield[layerNr - 1] = slicingData.wipeShield[layerNr - 1].CreateUnion(slicingData.wipeShield[layerNr].Offset(-offsetAngle)); } }