Fix no_perimeter_unsupported_algo by detecting each bridged area independently.

Apply also the fix for extra overhang detection
bridge_angle now invalidate perimeters
supermerill/SuperSlicer#1595
This commit is contained in:
supermerill 2021-10-02 19:41:10 +02:00
parent 8790970bd6
commit a4caa5d1c3
2 changed files with 135 additions and 130 deletions

View File

@ -189,130 +189,133 @@ void PerimeterGenerator::process()
int numploy = 0; int numploy = 0;
//only consider the bottom layer that intersect unsupported, to be sure it's only on our island. //only consider the bottom layer that intersect unsupported, to be sure it's only on our island.
ExPolygonCollection lower_island(support); ExPolygonCollection lower_island(support);
BridgeDetector detector{ unsupported_filtered, //a detector per island
lower_island.expolygons, ExPolygons bridgeable;
perimeter_spacing }; for (ExPolygon unsupported : unsupported_filtered) {
if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value))) { BridgeDetector detector{ unsupported,
ExPolygons bridgeable = union_ex(detector.coverage(-1, true)); lower_island.expolygons,
if (!bridgeable.empty()) { perimeter_spacing };
//check if we get everything or just the bridgeable area if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value)))
if (this->config->no_perimeter_unsupported_algo.value == npuaNoPeri || this->config->no_perimeter_unsupported_algo.value == npuaFilled) { expolygons_append(bridgeable, union_ex(detector.coverage(-1, true)));
//we bridge everything, even the not-bridgeable bits }
for (size_t i = 0; i < unsupported_filtered.size();) { if (!bridgeable.empty()) {
ExPolygon &poly_unsupp = *(unsupported_filtered.begin() + i); //check if we get everything or just the bridgeable area
Polygons contour_simplified = poly_unsupp.contour.simplify(perimeter_spacing); if (this->config->no_perimeter_unsupported_algo.value == npuaNoPeri || this->config->no_perimeter_unsupported_algo.value == npuaFilled) {
ExPolygon poly_unsupp_bigger = poly_unsupp; //we bridge everything, even the not-bridgeable bits
Polygons contour_bigger = offset(poly_unsupp_bigger.contour, perimeter_spacing); for (size_t i = 0; i < unsupported_filtered.size();) {
if (contour_bigger.size() == 1) poly_unsupp_bigger.contour = contour_bigger[0]; ExPolygon &poly_unsupp = *(unsupported_filtered.begin() + i);
Polygons contour_simplified = poly_unsupp.contour.simplify(perimeter_spacing);
ExPolygon poly_unsupp_bigger = poly_unsupp;
Polygons contour_bigger = offset(poly_unsupp_bigger.contour, perimeter_spacing);
if (contour_bigger.size() == 1) poly_unsupp_bigger.contour = contour_bigger[0];
//check convex, has some bridge, not overhang //check convex, has some bridge, not overhang
if (contour_simplified.size() == 1 && contour_bigger.size() == 1 && contour_simplified[0].concave_points().size() == 0 if (contour_simplified.size() == 1 && contour_bigger.size() == 1 && contour_simplified[0].concave_points().size() == 0
&& intersection_ex(bridgeable, { poly_unsupp }).size() > 0 && intersection_ex(bridgeable, { poly_unsupp }).size() > 0
&& diff_ex({ poly_unsupp_bigger }, last, true).size() == 0) { && diff_ex({ poly_unsupp_bigger }, last, true).size() == 0) {
//ok, keep it //ok, keep it
i++; i++;
} else { } else {
unsupported_filtered.erase(unsupported_filtered.begin() + i); unsupported_filtered.erase(unsupported_filtered.begin() + i);
}
} }
unsupported_filtered = intersection_ex(last, }
offset2_ex(unsupported_filtered, double(-perimeter_spacing / 2), double(perimeter_spacing * 3 / 2))); unsupported_filtered = intersection_ex(last,
if (this->config->no_perimeter_unsupported_algo.value == npuaFilled) { offset2_ex(unsupported_filtered, double(-perimeter_spacing / 2), double(perimeter_spacing * 3 / 2)));
for (ExPolygon &expol : unsupported_filtered) { if (this->config->no_perimeter_unsupported_algo.value == npuaFilled) {
//check if the holes won't be covered by the upper layer for (ExPolygon &expol : unsupported_filtered) {
//TODO: if we want to do that, we must modify the geometry before making perimeters. //check if the holes won't be covered by the upper layer
//if (this->upper_slices != nullptr && !this->upper_slices->expolygons.empty()) { //TODO: if we want to do that, we must modify the geometry before making perimeters.
// for (Polygon &poly : expol.holes) poly.make_counter_clockwise(); //if (this->upper_slices != nullptr && !this->upper_slices->expolygons.empty()) {
// float perimeterwidth = this->config->perimeters == 0 ? 0 : (this->ext_perimeter_flow.scaled_width() + (this->config->perimeters - 1) + this->perimeter_flow.scaled_spacing()); // for (Polygon &poly : expol.holes) poly.make_counter_clockwise();
// std::cout << "test upper slices with perimeterwidth=" << perimeterwidth << "=>" << offset_ex(this->upper_slices->expolygons, -perimeterwidth).size(); // float perimeterwidth = this->config->perimeters == 0 ? 0 : (this->ext_perimeter_flow.scaled_width() + (this->config->perimeters - 1) + this->perimeter_flow.scaled_spacing());
// if (intersection(Polygons() = { expol.holes }, to_polygons(offset_ex(this->upper_slices->expolygons, -this->ext_perimeter_flow.scaled_width() / 2))).empty()) { // std::cout << "test upper slices with perimeterwidth=" << perimeterwidth << "=>" << offset_ex(this->upper_slices->expolygons, -perimeterwidth).size();
// std::cout << " EMPTY§"; // if (intersection(Polygons() = { expol.holes }, to_polygons(offset_ex(this->upper_slices->expolygons, -this->ext_perimeter_flow.scaled_width() / 2))).empty()) {
// expol.holes.clear(); // std::cout << " EMPTY§";
// } else { // expol.holes.clear();
// } // } else {
// std::cout << "\n"; // }
//} else { // std::cout << "\n";
expol.holes.clear(); //} else {
//} expol.holes.clear();
//}
//detect inside volume //detect inside volume
for (size_t surface_idx_other = 0; surface_idx_other < all_surfaces.size(); surface_idx_other++) { for (size_t surface_idx_other = 0; surface_idx_other < all_surfaces.size(); surface_idx_other++) {
if (surface_idx == surface_idx_other) continue; if (surface_idx == surface_idx_other) continue;
if (intersection_ex(ExPolygons() = { expol }, ExPolygons() = { all_surfaces[surface_idx_other].expolygon }).size() > 0) { if (intersection_ex(ExPolygons() = { expol }, ExPolygons() = { all_surfaces[surface_idx_other].expolygon }).size() > 0) {
//this means that other_surf was inside an expol holes //this means that other_surf was inside an expol holes
//as we removed them, we need to add a new one //as we removed them, we need to add a new one
ExPolygons new_poly = offset2_ex(all_surfaces[surface_idx_other].expolygon, double(-perimeter_spacing * 2), double(perimeter_spacing)); ExPolygons new_poly = offset2_ex(all_surfaces[surface_idx_other].expolygon, double(-perimeter_spacing * 2), double(perimeter_spacing));
if (new_poly.size() == 1) { if (new_poly.size() == 1) {
all_surfaces[surface_idx_other].expolygon = new_poly[0]; all_surfaces[surface_idx_other].expolygon = new_poly[0];
expol.holes.push_back(new_poly[0].contour); expol.holes.push_back(new_poly[0].contour);
expol.holes.back().make_clockwise();
} else {
for (size_t idx = 0; idx < new_poly.size(); idx++) {
Surface new_surf = all_surfaces[surface_idx_other];
new_surf.expolygon = new_poly[idx];
all_surfaces.push_back(new_surf);
expol.holes.push_back(new_poly[idx].contour);
expol.holes.back().make_clockwise(); expol.holes.back().make_clockwise();
} else {
for (size_t idx = 0; idx < new_poly.size(); idx++) {
Surface new_surf = all_surfaces[surface_idx_other];
new_surf.expolygon = new_poly[idx];
all_surfaces.push_back(new_surf);
expol.holes.push_back(new_poly[idx].contour);
expol.holes.back().make_clockwise();
}
all_surfaces.erase(all_surfaces.begin() + surface_idx_other);
if (surface_idx_other < surface_idx) {
surface_idx--;
surface = &all_surfaces[surface_idx];
}
surface_idx_other--;
} }
all_surfaces.erase(all_surfaces.begin() + surface_idx_other);
if (surface_idx_other < surface_idx) {
surface_idx--;
surface = &all_surfaces[surface_idx];
}
surface_idx_other--;
} }
} }
} }
} }
//TODO: add other polys as holes inside this one (-margin)
} else if (this->config->no_perimeter_unsupported_algo.value == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo.value == npuaBridges){
//simplify to avoid most of artefacts from printing lines.
ExPolygons bridgeable_simplified;
for (ExPolygon &poly : bridgeable) {
poly.simplify(perimeter_spacing, &bridgeable_simplified);
}
bridgeable_simplified = offset2_ex(bridgeable_simplified, -ext_perimeter_width, ext_perimeter_width);
//bridgeable_simplified = intersection_ex(bridgeable_simplified, unsupported_filtered);
//offset by perimeter spacing because the simplify may have reduced it a bit.
//it's not dangerous as it will be intersected by 'unsupported' later
//FIXME: add overlap in this->fill_surfaces->append
//FIXME: it overlap inside unsuppported not-bridgeable area!
//bridgeable_simplified = offset2_ex(bridgeable_simplified, (double)-perimeter_spacing, (double)perimeter_spacing * 2); }
//ExPolygons unbridgeable = offset_ex(diff_ex(unsupported, bridgeable_simplified), perimeter_spacing * 3 / 2); //TODO: add other polys as holes inside this one (-margin)
//ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2))); } else if (this->config->no_perimeter_unsupported_algo.value == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo.value == npuaBridges){
//unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width); //simplify to avoid most of artefacts from printing lines.
ExPolygons bridgeable_simplified;
for (ExPolygon &poly : bridgeable) {
poly.simplify(perimeter_spacing, &bridgeable_simplified);
}
bridgeable_simplified = offset2_ex(bridgeable_simplified, -ext_perimeter_width, ext_perimeter_width);
//bridgeable_simplified = intersection_ex(bridgeable_simplified, unsupported_filtered);
//offset by perimeter spacing because the simplify may have reduced it a bit.
//it's not dangerous as it will be intersected by 'unsupported' later
//FIXME: add overlap in this->fill_surfaces->append
//FIXME: it overlap inside unsuppported not-bridgeable area!
//bridgeable_simplified = offset2_ex(bridgeable_simplified, (double)-perimeter_spacing, (double)perimeter_spacing * 2);
//ExPolygons unbridgeable = offset_ex(diff_ex(unsupported, bridgeable_simplified), perimeter_spacing * 3 / 2);
//ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2)));
//unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width);
if (this->config->no_perimeter_unsupported_algo.value == npuaBridges) { if (this->config->no_perimeter_unsupported_algo.value == npuaBridges) {
ExPolygons unbridgeable = unsupported_filtered; ExPolygons unbridgeable = unsupported_filtered;
for (ExPolygon &expol : unbridgeable) for (ExPolygon &expol : unbridgeable)
expol.holes.clear(); expol.holes.clear();
unbridgeable = diff_ex(unbridgeable, bridgeable_simplified); unbridgeable = diff_ex(unbridgeable, bridgeable_simplified);
unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width*2, ext_perimeter_width*2); unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width*2, ext_perimeter_width*2);
ExPolygons bridges_temp = intersection_ex(last, diff_ex(unsupported_filtered, unbridgeable)); ExPolygons bridges_temp = intersection_ex(last, diff_ex(unsupported_filtered, unbridgeable));
//remove the overhangs section from the surface polygons //remove the overhangs section from the surface polygons
ExPolygons reference = last; ExPolygons reference = last;
last = diff_ex(last, unsupported_filtered); last = diff_ex(last, unsupported_filtered);
//ExPolygons no_bridge = diff_ex(offset_ex(unbridgeable, ext_perimeter_width * 3 / 2), last); //ExPolygons no_bridge = diff_ex(offset_ex(unbridgeable, ext_perimeter_width * 3 / 2), last);
//bridges_temp = diff_ex(bridges_temp, no_bridge); //bridges_temp = diff_ex(bridges_temp, no_bridge);
unsupported_filtered = diff_ex(offset_ex(bridges_temp, ext_perimeter_width * 3 / 2), offset_ex(unbridgeable, ext_perimeter_width * 2, jtSquare)); unsupported_filtered = diff_ex(offset_ex(bridges_temp, ext_perimeter_width * 3 / 2), offset_ex(unbridgeable, ext_perimeter_width * 2, jtSquare));
unsupported_filtered = intersection_ex(unsupported_filtered, reference); unsupported_filtered = intersection_ex(unsupported_filtered, reference);
} else { } else {
ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2))); ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2)));
unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width); unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width);
unsupported_filtered = unbridgeable; unsupported_filtered = unbridgeable;
////put the bridge area inside the unsupported_filtered variable ////put the bridge area inside the unsupported_filtered variable
//unsupported_filtered = intersection_ex(last, //unsupported_filtered = intersection_ex(last,
// diff_ex( // diff_ex(
// offset_ex(bridgeable_simplified, (double)perimeter_spacing / 2), // offset_ex(bridgeable_simplified, (double)perimeter_spacing / 2),
// unbridgeable // unbridgeable
// ) // )
// ); // );
}
} }
} else { } else {
unsupported_filtered.clear(); unsupported_filtered.clear();
@ -406,24 +409,26 @@ void PerimeterGenerator::process()
//first, separate into islands (ie, each ExPlolygon) //first, separate into islands (ie, each ExPlolygon)
//only consider the bottom layer that intersect unsupported, to be sure it's only on our island. //only consider the bottom layer that intersect unsupported, to be sure it's only on our island.
const ExPolygonCollection lower_island(diff_ex(last, overhangs_unsupported)); const ExPolygonCollection lower_island(diff_ex(last, overhangs_unsupported));
BridgeDetector detector{ overhangs_unsupported, ExPolygons bridgeable;
lower_island.expolygons, for (ExPolygon unsupported : overhangs_unsupported) {
perimeter_spacing }; BridgeDetector detector{ unsupported,
if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value))) { lower_island.expolygons,
const ExPolygons bridgeable = union_ex(detector.coverage(-1, true)); perimeter_spacing };
if (!bridgeable.empty()) { if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value)))
//simplify to avoid most of artefacts from printing lines. expolygons_append(bridgeable, union_ex(detector.coverage(-1, true)));
ExPolygons bridgeable_simplified; }
for (const ExPolygon &poly : bridgeable) { if (!bridgeable.empty()) {
poly.simplify(perimeter_spacing / 2, &bridgeable_simplified); //simplify to avoid most of artefacts from printing lines.
} ExPolygons bridgeable_simplified;
for (const ExPolygon &poly : bridgeable) {
poly.simplify(perimeter_spacing / 2, &bridgeable_simplified);
}
if (!bridgeable_simplified.empty()) //offset by perimeter spacing because the simplify may have reduced it a bit.
bridgeable_simplified = offset_ex(bridgeable_simplified, double(perimeter_spacing) / 1.9); if (!bridgeable_simplified.empty())
if (!bridgeable_simplified.empty()) { bridgeable_simplified = offset_ex(bridgeable_simplified, double(perimeter_spacing) / 1.9);
//offset by perimeter spacing because the simplify may have reduced it a bit. if (!bridgeable_simplified.empty()) {
overhangs_unsupported = diff_ex(overhangs_unsupported, bridgeable_simplified, true); overhangs_unsupported = diff_ex(overhangs_unsupported, bridgeable_simplified, true);
}
} }
} }
} }

View File

@ -815,7 +815,6 @@ namespace Slic3r {
} else if ( } else if (
opt_key == "bottom_solid_min_thickness" opt_key == "bottom_solid_min_thickness"
|| opt_key == "bridged_infill_margin" || opt_key == "bridged_infill_margin"
|| opt_key == "bridge_angle"
|| opt_key == "ensure_vertical_shell_thickness" || opt_key == "ensure_vertical_shell_thickness"
|| opt_key == "fill_density" || opt_key == "fill_density"
|| opt_key == "interface_shells" || opt_key == "interface_shells"
@ -856,7 +855,8 @@ namespace Slic3r {
|| opt_key == "top_infill_extrusion_width") { || opt_key == "top_infill_extrusion_width") {
steps.emplace_back(posInfill); steps.emplace_back(posInfill);
} else if ( } else if (
opt_key == "extra_perimeters" opt_key == "bridge_angle"
|| opt_key == "extra_perimeters"
|| opt_key == "extra_perimeters_odd_layers" || opt_key == "extra_perimeters_odd_layers"
|| opt_key == "external_infill_margin" || opt_key == "external_infill_margin"
|| opt_key == "external_perimeter_overlap" || opt_key == "external_perimeter_overlap"