no perimeters on bridged area : algo changed, more options and less settings.

This commit is contained in:
supermerill 2019-03-01 19:46:52 +01:00
parent cd9e99da25
commit b3157c5b1e
13 changed files with 297 additions and 163 deletions

View File

@ -35,7 +35,7 @@ BridgeDetector::BridgeDetector(
void BridgeDetector::initialize()
{
// 5 degrees stepping
this->resolution = PI/36.0;
this->resolution = PI/(36.0*5);
// output angle not known
this->angle = -1.;
@ -235,10 +235,10 @@ Polygons BridgeDetector::coverage(double angle, bool precise) const {
if (!precise) expoly.get_trapezoids2(&trapezoids, PI / 2);
else expoly.get_trapezoids3_half(&trapezoids, float(this->spacing));
for (Polygon &trapezoid : trapezoids) {
// not nice, we need a more robust non-numeric check
// imporvment 1: take into account when we go in the supported area.
size_t n_supported = 0;
if (!precise) {
// not nice, we need a more robust non-numeric check
// imporvment 1: take into account when we go in the supported area.
for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors))
if (supported_line.length() >= this->spacing)
++n_supported;
@ -286,7 +286,7 @@ Polygons BridgeDetector::coverage(double angle, bool precise) const {
covered = union_(covered);
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle));
covered = intersection(covered, to_polygons(this->expolygons));
//covered = intersection(covered, to_polygons(this->expolygons));
#if 0
{
my @lines = map @{$_->lines}, @$trapezoids;

View File

@ -33,7 +33,7 @@ public:
BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
// If bridge_direction_override != 0, then the angle is used instead of auto-detect.
bool detect_angle(double bridge_direction_override = 0.);
Polygons coverage(double angle = -1, bool precise = false) const;
Polygons coverage(double angle = -1, bool precise = true) const;
void unsupported_edges(double angle, Polylines* unsupported) const;
Polylines unsupported_edges(double angle = -1) const;

View File

@ -63,7 +63,7 @@ public:
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void make_perimeters(SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;

View File

@ -30,6 +30,7 @@ Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
void LayerRegion::slices_to_fill_surfaces_clipped()
{
//if (this->region()->config().no_perimeter_full_bridge) return;
// Note: this method should be idempotent, but fill_surfaces gets modified
// in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case
@ -45,11 +46,14 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
this->fill_surfaces.surfaces.clear();
for (auto const& entry : polygons_by_surface) {
if (!entry.second.empty())
this->fill_surfaces.append(intersection_ex(entry.second, fill_boundaries), entry.first);
//if (entry.first & stModBridge == stModBridge && this->region()->config().no_perimeter_full_bridge)
// this->fill_surfaces.append(entry.second, entry.first);
//else
this->fill_surfaces.append(intersection_ex(entry.second, fill_boundaries), entry.first);
}
}
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
void LayerRegion::make_perimeters(SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{
this->perimeters.clear();
this->thin_fills.clear();
@ -91,16 +95,17 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
void LayerRegion::process_external_surfaces(const Layer* lower_layer)
{
coord_t max_margin = 0;
if ((this->region()->config().perimeters > 0)) {
max_margin = scale_(this->flow(frExternalPerimeter).width) + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1);
}
const Surfaces &surfaces = this->fill_surfaces.surfaces;
const bool has_infill = this->region()->config().fill_density.value > 0.;
coord_t margin = scale_(this->region()->config().external_infill_margin.getFloat());
coord_t margin_bridged = scale_(this->region()->config().bridged_infill_margin.getFloat());
coord_t margin = scale_(this->region()->config().external_infill_margin.get_abs_value(unscaled(max_margin)));
coord_t margin_bridged = scale_(this->region()->config().bridged_infill_margin.get_abs_value(this->flow(frExternalPerimeter).width));
//if no infill, reduce the margin for everything to only the perimeter
if (!has_infill) {
coord_t max_margin = 0;
if ((this->region()->config().perimeters > 0)) {
max_margin = scale_(this->flow(frExternalPerimeter).width) + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1);
}
margin = std::min(margin, max_margin);
margin_bridged = std::min(margin_bridged, max_margin);
}
@ -284,7 +289,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->config().support_material) {
polygons_append(this->bridged, bd.coverage());
polygons_append(this->bridged, intersection(bd.coverage(), to_polygons(initial)));
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
}

View File

@ -76,7 +76,202 @@ void PerimeterGenerator::process()
// we need to process each island separately because we might have different
// extra perimeters for each one
int surface_idx = 0;
for (const Surface &surface : this->slices->surfaces) {
Surfaces all_surfaces = this->slices->surfaces;
//store surface for bridge infill to avoid unsupported perimeters (but the first one, this one is always good)
if (this->config->no_perimeter_unsupported_algo != npuaNone
&& this->lower_slices != NULL && !this->lower_slices->expolygons.empty()) {
for (surface_idx = 0; surface_idx < all_surfaces.size(); surface_idx++) {
Surface *surface = &all_surfaces[surface_idx];
ExPolygons last = union_ex(surface->expolygon.simplify_p(SCALED_RESOLUTION));
//compute our unsupported surface
ExPolygons unsupported = diff_ex(last, this->lower_slices->expolygons, true);
if (!unsupported.empty()) {
//remove small overhangs
ExPolygons unsupported_filtered = offset2_ex(unsupported, -(float)(perimeter_spacing), (float)(perimeter_spacing));
if (!unsupported_filtered.empty()) {
//to_draw.insert(to_draw.end(), last.begin(), last.end());
//extract only the useful part of the lower layer. The safety offset is really needed here.
ExPolygons support = diff_ex(last, unsupported, true);
if (!unsupported.empty()) {
//only consider the part that can be bridged (really, by the bridge algorithm)
//first, separate into islands (ie, each ExPlolygon)
int numploy = 0;
//only consider the bottom layer that intersect unsupported, to be sure it's only on our island.
ExPolygonCollection lower_island(support);
BridgeDetector detector(unsupported_filtered,
lower_island,
perimeter_spacing);
if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value))) {
ExPolygons bridgeable = union_ex(detector.coverage(-1, true));
if (!bridgeable.empty()) {
//check if we get everything or just the bridgeable area
if (this->config->no_perimeter_unsupported_algo == npuaNoPeri || this->config->no_perimeter_unsupported_algo == npuaFilled) {
//we bridge everything, even the not-bridgeable bits
for (size_t i = 0; i < unsupported_filtered.size();) {
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
if (contour_simplified.size() == 1 && contour_bigger.size() == 1 && contour_simplified[0].concave_points().size() == 0
&& intersection_ex(bridgeable, { poly_unsupp }).size() > 0
&& diff_ex({ poly_unsupp_bigger }, last, true).size() == 0) {
//ok, keep it
i++;
} else {
unsupported_filtered.erase(unsupported_filtered.begin() + i);
}
}
unsupported_filtered = intersection_ex(last,
offset2_ex(unsupported_filtered, (float)-perimeter_spacing / 2, (float)perimeter_spacing * 3 / 2));
if (this->config->no_perimeter_unsupported_algo == npuaFilled) {
for (ExPolygon &expol : unsupported_filtered) {
//check if the holes won't be covered by the upper layer
//TODO: if we want to do that, we must modify the geometry before making perimeters.
//if (this->upper_slices != nullptr && !this->upper_slices->expolygons.empty()) {
// for (Polygon &poly : expol.holes) poly.make_counter_clockwise();
// float perimeterwidth = this->config->perimeters == 0 ? 0 : (this->ext_perimeter_flow.scaled_width() + (this->config->perimeters - 1) + this->perimeter_flow.scaled_spacing());
// std::cout << "test upper slices with perimeterwidth=" << perimeterwidth << "=>" << offset_ex(this->upper_slices->expolygons, -perimeterwidth).size();
// if (intersection(Polygons() = { expol.holes }, to_polygons(offset_ex(this->upper_slices->expolygons, -this->ext_perimeter_flow.scaled_width() / 2))).empty()) {
// std::cout << " EMPTY§";
// expol.holes.clear();
// } else {
// }
// std::cout << "\n";
//} else {
expol.holes.clear();
//}
//detect inside volume
for (size_t surface_idx_other = 0; surface_idx_other < all_surfaces.size(); surface_idx_other++) {
if (surface_idx == surface_idx_other) continue;
if (intersection_ex(ExPolygons() = { expol }, ExPolygons() = { all_surfaces[surface_idx_other].expolygon }).size() > 0) {
//this means that other_surf was inside an expol holes
//as we removed them, we need to add a new one
ExPolygons new_poly = offset2_ex(all_surfaces[surface_idx_other].expolygon, -(float)perimeter_spacing * 2, (float)perimeter_spacing);
if (new_poly.size() == 1) {
all_surfaces[surface_idx_other].expolygon = new_poly[0];
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();
}
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 == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo == 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!
double overlap = scale_(this->config->get_abs_value("infill_overlap", unscale<double>(perimeter_spacing)));
//bridgeable_simplified = offset2_ex(bridgeable_simplified, (float)-perimeter_spacing, (float)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 == npuaBridges) {
ExPolygons unbridgeable = unsupported_filtered;
for (ExPolygon &expol : unbridgeable)expol.holes.clear();
unbridgeable = diff_ex(unbridgeable, bridgeable_simplified);
unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width*2, ext_perimeter_width*2);
ExPolygons bridges_temp = intersection_ex(last, diff_ex(unsupported_filtered, unbridgeable));
//remove the overhangs section form the surface polygons
last = diff_ex(last, unsupported_filtered);
//ExPolygons no_bridge = diff_ex(offset_ex(unbridgeable, ext_perimeter_width * 3 / 2), last);
//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));
} else {
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);
unsupported_filtered = unbridgeable;
////put the bridge area inside the unsupported_filtered variable
//unsupported_filtered = intersection_ex(last,
// diff_ex(
// offset_ex(bridgeable_simplified, (float)perimeter_spacing / 2),
// unbridgeable
// )
// );
}
}
} else {
unsupported_filtered.clear();
}
} else {
unsupported_filtered.clear();
}
}
if (!unsupported_filtered.empty()) {
//add this directly to the infill list.
// this will avoid to throw wrong offsets into a good polygons
this->fill_surfaces->append(
unsupported_filtered,
stPosInternal | stDensSparse);
// store the results
last = diff_ex(last, unsupported_filtered, true);
//remove "thin air" polygons (note: it assumes that all polygons below will be extruded)
for (int i = 0; i < last.size(); i++) {
if (intersection_ex(support, ExPolygons() = { last[i] }).empty()) {
this->fill_surfaces->append(
ExPolygons() = { last[i] },
stPosInternal | stDensSparse);
last.erase(last.begin() + i);
i--;
}
}
}
}
}
if (last.size() == 0) {
all_surfaces.erase(all_surfaces.begin() + surface_idx);
surface_idx--;
} else {
surface->expolygon = last[0];
for (size_t idx = 1; idx < last.size(); idx++) {
Surface new_surf = *surface;
new_surf.expolygon = last[idx];
all_surfaces.push_back(new_surf);
}
}
}
}
surface_idx = 0;
for (const Surface &surface : all_surfaces) {
// detect how many perimeters must be generated for this island
int loop_number = this->config->perimeters + surface.extra_perimeters - 1; // 0-indexed loops
surface_idx++;
@ -132,87 +327,7 @@ void PerimeterGenerator::process()
// we loop one time more than needed in order to find gaps after the last perimeter was applied
for (int i = 0;; ++ i) { // outer loop is 0
//store surface for bridge infill to avoid unsupported perimeters (but the first one, this one is always good)
if (this->config->no_perimeter_unsupported && i == this->config->min_perimeter_unsupported
&& this->lower_slices != NULL && !this->lower_slices->expolygons.empty()) {
//compute our unsupported surface
ExPolygons unsupported = diff_ex(last, this->lower_slices->expolygons, true);
if (!unsupported.empty()) {
//remove small overhangs
ExPolygons unsupported_filtered = offset2_ex(unsupported, -(float)(perimeter_spacing), (float)(perimeter_spacing));
if (!unsupported_filtered.empty()) {
//to_draw.insert(to_draw.end(), last.begin(), last.end());
//extract only the useful part of the lower layer. The safety offset is really needed here.
ExPolygons support = diff_ex(last, unsupported, true);
if (this->config->noperi_bridge_only && !unsupported.empty()) {
//only consider the part that can be bridged (really, by the bridge algorithm)
//first, separate into islands (ie, each ExPlolygon)
int numploy = 0;
//only consider the bottom layer that intersect unsupported, to be sure it's only on our island.
ExPolygonCollection lower_island(support);
BridgeDetector detector(unsupported_filtered,
lower_island,
perimeter_spacing);
if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value))) {
ExPolygons bridgeable = union_ex(detector.coverage(-1, true));
if (!bridgeable.empty()) {
//simplify to avoid most of artefacts from printing lines.
ExPolygons bridgeable_simplified;
for (ExPolygon &poly : bridgeable) {
poly.simplify(perimeter_spacing/2, &bridgeable_simplified);
}
//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
// add overlap (perimeter_spacing/4 was good in test, ie 25%)
double overlap = scale_(this->config->get_abs_value("infill_overlap", unscale<double>(perimeter_spacing)));
unsupported_filtered = intersection_ex(unsupported_filtered, offset_ex(bridgeable_simplified, (float) overlap));
} else {
unsupported_filtered.clear();
}
} else {
unsupported_filtered.clear();
}
} else {
//only consider the part that can be 'bridged' (inside the convex hull)
// it's not as precise as the bridge detector, but it's better than nothing, and quicker.
ExPolygonCollection coll_last(support);
ExPolygon hull;
hull.contour = coll_last.convex_hull();
unsupported_filtered = intersection_ex(unsupported_filtered, ExPolygons() = { hull });
}
if (!unsupported_filtered.empty()) {
//and we want at least 1 perimeter of overlap
ExPolygons bridge = unsupported_filtered;
unsupported_filtered = intersection_ex(offset_ex(unsupported_filtered, (float)(perimeter_spacing)), last);
// remove from the bridge & support the small imperfections of the union
ExPolygons bridge_and_support = offset2_ex(union_ex(bridge, support, true), (float)perimeter_spacing / 2, (float)-perimeter_spacing / 2);
// make him flush with perimeter area
unsupported_filtered = intersection_ex(offset_ex(unsupported_filtered, (float)(perimeter_spacing / 2)), bridge_and_support);
//add this directly to the infill list.
// this will avoid to throw wrong offsets into a good polygons
this->fill_surfaces->append(
unsupported_filtered,
stPosInternal | stDensSparse);
// store the results
last = diff_ex(last, unsupported_filtered, true);
//remove "thin air" polygons
for (int i = 0; i < last.size();i++) {
if (intersection_ex(support, ExPolygons() = { last[i] }).empty()) {
this->fill_surfaces->append(
ExPolygons() = { last[i] },
stPosInternal | stDensSparse);
last.erase(last.begin() + i);
i--;
}
}
}
}
}
}
// We can add more perimeters if there are uncovered overhangs
// improvement for future: find a way to add perimeters only where it's needed.
@ -515,7 +630,7 @@ void PerimeterGenerator::process()
// only apply infill overlap if we actually have one perimeter
coord_t overlap = 0;
if (inset > 0) {
overlap = scale_(this->config->get_abs_value("infill_overlap", unscale<coordf_t>(inset + solid_infill_spacing / 2)));
overlap = (coord_t)scale_(this->config->get_abs_value("infill_overlap", unscale<coordf_t>(inset + solid_infill_spacing / 2)));
}
// simplify infill contours according to resolution
Polygons not_filled_p;
@ -1205,10 +1320,10 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
outer_end->extruder_id = -1;
}*/
//add paths into my_loop => after that all ref are wrong!
for (int32_t i = travel_path_end.size() - 1; i >= 0; i--) {
for (size_t i = travel_path_end.size() - 1; i >= 0; i--) {
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + child_paths_size + 1, travel_path_end[i]);
}
for (int32_t i = travel_path_begin.size() - 1; i >= 0; i--) {
for (size_t i = travel_path_begin.size() - 1; i >= 0; i--) {
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, travel_path_begin[i]);
}
}

View File

@ -497,27 +497,27 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->default_value = new ConfigOptionBool(true);
def = this->add("external_infill_margin", coFloat);
def = this->add("external_infill_margin", coFloatOrPercent);
def->label = L("Default");
def->full_label = L("Default infill margin");
def->category = L("Infill");
def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified MM to anchor them into the part. Put 0 to deactivate it.");
def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified MM to anchor them into the part. Put 0 to deactivate it. Can be a % of the width of the perimeters.");
def->sidetext = L("mm");
def->cli = "top-layer-anchor=f";
def->cli = "top-layer-anchor=s";
def->min = 0;
def->mode = comExpert;
def->default_value = new ConfigOptionFloat(1.5);
def->default_value = new ConfigOptionFloatOrPercent(150, true);
def = this->add("bridged_infill_margin", coFloat);
def = this->add("bridged_infill_margin", coFloatOrPercent);
def->label = L("Bridged");
def->full_label = L("Bridge margin");
def->category = L("Infill");
def->tooltip = L("This parameter grows the bridged solid infill layers by the specified MM to anchor them into the part. Put 0 to deactivate it.");
def->tooltip = L("This parameter grows the bridged solid infill layers by the specified MM to anchor them into the part. Put 0 to deactivate it. Can be a % of the width of the external perimeter.");
def->sidetext = L("mm");
def->cli = "bridged-layer-anchor=f";
def->cli = "bridged-layer-anchor=s";
def->min = 0;
def->mode = comExpert;
def->default_value = new ConfigOptionFloat(2);
def->default_value = new ConfigOptionFloatOrPercent(200, true);
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
def->label = L("External perimeters");
@ -1254,7 +1254,7 @@ void PrintConfigDef::init_fff_params()
def->full_label = ("Dense infill algorithm");
def->tooltip = L("Choose the way the dense layer is lay out."
" The automatic option let it try to draw the smallest surface with only strait lines inside the sparse infill."
" The anchored just enlarge a bit (by bridged anchor) the surfaces that need a better support.");
" The anchored just enlarge a bit (by 'Default infill margin') the surfaces that need a better support.");
def->cli = "infill-dense-algo=s";
def->enum_keys_map = &ConfigOptionEnum<DenseInfillAlgo>::get_enum_values();
def->enum_values.push_back("automatic");
@ -1696,32 +1696,29 @@ void PrintConfigDef::init_fff_params()
def->cli = "overhangs!";
def->mode = comAdvanced;
def->default_value = new ConfigOptionBool(true);
def = this->add("no_perimeter_unsupported", coBool);
def->label = L("");
def->full_label = ("No perimeters on bridge areas");
def->category = L("Layers and Perimeters");
def->tooltip = L("Experimental option to remove perimeters where there is nothing under it and where a bridged infill should be better. Computationally intensive!");
def->cli = "no-perimeter-unsupported!";
def->mode = comExpert;
def->default_value = new ConfigOptionBool(false);
def = this->add("min_perimeter_unsupported", coInt);
def->label = L("Minimum perimeters");
def->category = L("Layers and Perimeters");
def->tooltip = L("Number of perimeters exluded from this option.");
def->cli = "min-perimeter-unsupported=i";
def->min = 0;
def->mode = comExpert;
def->default_value = new ConfigOptionInt(0);
def = this->add("noperi_bridge_only", coBool);
def->label = L("Only on bridged areas");
def->category = L("Layers and Perimeters");
def->tooltip = L("Only remove perimeters over areas marked as 'bridge'. Can be useful to let perimeter run over overhangs, but it's not very reliable.");
def->cli = "noperi-bridge-only!";
def->mode = comExpert;
def->default_value = new ConfigOptionBool(true);
def = this->add("no_perimeter_unsupported_algo", coEnum);
def->label = L("No perimeters on bridge areas");
def->tooltip = L("Experimental option to remove perimeters where there is nothing under it and where a bridged infill should be better. "
"\nRemove perimeters: remove the unsupported periemter, let the bridge area as-is."
"\nKeep only bridges: remove the unsupported periemter, kep only bridges that end in solid area."
"\nKeep bridges and overhangs: remove the unsupported periemter, keep only bridges that end in solid area, fill the rest with overhang perimeters+bridges."
"\nFill the voids with bridges: remove the unsupported periemter, draw bridges over the whole hole. !! can lead to problems with overhangs shape like /\\, consider carefully before using this option!"
"\n!!Computationally intensive!!. ");
def->cli = "no-perimeter-unsupported-algo=s";
def->enum_keys_map = &ConfigOptionEnum<NoPerimeterUnsupportedAlgo>::get_enum_values();
def->enum_values.push_back("none");
def->enum_values.push_back("noperi");
def->enum_values.push_back("bridges");
def->enum_values.push_back("bridgesoverhangs");
def->enum_values.push_back("filled");
def->enum_labels.push_back(L("Disabled"));
def->enum_labels.push_back(L("Remove perimeters"));
def->enum_labels.push_back(L("Keep only bridges"));
def->enum_labels.push_back(L("Keep bridges and overhangs"));
def->enum_labels.push_back(L("Fill the voids with bridges"));
def->mode = comAdvanced;
def->default_value = new ConfigOptionEnum<NoPerimeterUnsupportedAlgo>(npuaNone);
def = this->add("parking_pos_retraction", coFloat);
def->label = L("Filament parking position");

View File

@ -68,7 +68,11 @@ enum FilamentType {
};
enum DenseInfillAlgo {
dfaAutomatic, dfaAutoNotFull, dfaEnlarged,
dfaNone, dfaAutomatic, dfaAutoNotFull, dfaEnlarged,
};
enum NoPerimeterUnsupportedAlgo {
npuaNone, npuaNoPeri, npuaBridges, npuaBridgesOverhangs, npuaFilled,
};
enum SupportZDistanceType {
@ -208,6 +212,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::ge
template<> inline const t_config_enum_values& ConfigOptionEnum<DenseInfillAlgo>::get_enum_values() {
static const t_config_enum_values keys_map = {
{ "none", dfaNone },
{ "automatic", dfaAutomatic },
{ "autosmall", dfaAutoNotFull },
{ "enlarged", dfaEnlarged }
@ -215,6 +220,17 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<DenseInfillAlgo>:
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<NoPerimeterUnsupportedAlgo>::get_enum_values() {
static const t_config_enum_values keys_map = {
{ "none", npuaNone },
{ "noperi", npuaNoPeri },
{ "bridges", npuaBridges },
{ "bridgesoverhangs", npuaBridgesOverhangs },
{ "filled", npuaFilled }
};
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<SupportZDistanceType>::get_enum_values() {
static const t_config_enum_values keys_map = {
{ "filament", zdFilament },
@ -544,8 +560,8 @@ public:
ConfigOptionFloat bridge_speed;
ConfigOptionBool ensure_vertical_shell_thickness;
ConfigOptionBool enforce_full_fill_volume;
ConfigOptionFloat external_infill_margin;
ConfigOptionFloat bridged_infill_margin;
ConfigOptionFloatOrPercent external_infill_margin;
ConfigOptionFloatOrPercent bridged_infill_margin;
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
ConfigOptionFloatOrPercent external_perimeter_speed;
ConfigOptionBool external_perimeters_first;
@ -569,9 +585,7 @@ public:
ConfigOptionBool infill_first;
// Detect bridging perimeters
ConfigOptionBool overhangs;
ConfigOptionBool no_perimeter_unsupported;
ConfigOptionInt min_perimeter_unsupported;
ConfigOptionBool noperi_bridge_only;
ConfigOptionEnum<NoPerimeterUnsupportedAlgo> no_perimeter_unsupported_algo;
ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionFloat perimeter_speed;
@ -629,9 +643,7 @@ protected:
OPT_PTR(infill_dense_algo);
OPT_PTR(infill_first);
OPT_PTR(overhangs);
OPT_PTR(no_perimeter_unsupported);
OPT_PTR(min_perimeter_unsupported);
OPT_PTR(noperi_bridge_only);
OPT_PTR(no_perimeter_unsupported_algo);
OPT_PTR(perimeter_extruder);
OPT_PTR(perimeter_extrusion_width);
OPT_PTR(perimeter_speed);

View File

@ -476,9 +476,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "external_perimeters_first"
|| opt_key == "perimeter_loop"
|| opt_key == "perimeter_loop_seam"
|| opt_key == "no_perimeter_unsupported"
|| opt_key == "min_perimeter_unsupported"
|| opt_key == "noperi_bridge_only") {
|| opt_key == "no_perimeter_unsupported_algo") {
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
@ -761,7 +759,8 @@ void PrintObject::tag_under_bridge() {
if (layerm->region()->config().infill_dense_algo == dfaEnlarged) {
//expand the area a bit
intersect = offset_ex(intersect, (float)scale_(layerm->region()->config().bridged_infill_margin));
intersect = offset_ex(intersect, (float)scale_(layerm->region()->config().external_infill_margin.get_abs_value(
region->config().perimeters == 0 ? 0 : (layerm->flow(frExternalPerimeter).width + layerm->flow(frPerimeter).spacing() * (region->config().perimeters - 1)))));
} else if (layerm->region()->config().infill_dense_algo == dfaAutoNotFull
|| layerm->region()->config().infill_dense_algo == dfaAutomatic){
@ -900,7 +899,9 @@ void PrintObject::detect_surfaces_type()
// collapse very narrow parts (using the safety offset in the diff is not enough)
float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f;
Polygons layerm_slices_surfaces = to_polygons(layerm->slices.surfaces);
// no_perimeter_full_bridge allow to put bridges where there are nothing, hence adding area to silce, that's why we need to start from the result of PerimeterGenerator.
// i don't see the need tostart from layerm->slices.surfaces, but i'll keep that "in case of".
Polygons layerm_slices_surfaces = layerm->region()->config().no_perimeter_unsupported_algo == npuaFilled ? to_polygons(layerm->fill_surfaces) : to_polygons(layerm->slices.surfaces);
// find top surfaces (difference between current surfaces
// of current layer and upper one)
@ -933,11 +934,16 @@ void PrintObject::detect_surfaces_type()
offset2_ex(diff(layerm_slices_surfaces, lower_slices, true), -offset, offset),
surface_type_bottom_other);
#else
ExPolygons lower_slices = lower_layer->slices.expolygons;
//if we added new surfaces, we can use them as support
/*if (layerm->region()->config().no_perimeter_full_bridge) {
lower_slices = union_ex(lower_slices, lower_layer->get_region(idx_region)->fill_surfaces);
}*/
// Any surface lying on the void is a true bottom bridge (an overhang)
surfaces_append(
bottom,
offset2_ex(
diff(layerm_slices_surfaces, to_polygons(lower_layer->slices), true),
diff(layerm_slices_surfaces, to_polygons(lower_slices), true),
-offset, offset),
surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces
@ -949,7 +955,7 @@ void PrintObject::detect_surfaces_type()
bottom,
offset2_ex(
diff(
intersection(layerm_slices_surfaces, to_polygons(lower_layer->slices)), // supported
intersection(layerm_slices_surfaces, to_polygons(lower_slices)), // supported
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces),
true),
-offset, offset),

View File

@ -761,6 +761,8 @@ boost::any& Choice::get_value()
m_value = static_cast<PrintHostType>(ret_enum);
else if (m_opt_id.compare("infill_dense_algo") == 0)
m_value = static_cast<DenseInfillAlgo>(ret_enum);
else if (m_opt_id.compare("no_perimeter_unsupported_algo") == 0)
m_value = static_cast<NoPerimeterUnsupportedAlgo>(ret_enum);
else if (m_opt_id.compare("wipe_advanced_algo") == 0)
m_value = static_cast<WipeAlgo>(ret_enum);
else if (m_opt_id.compare("support_material_contact_distance_type") == 0)

View File

@ -226,6 +226,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
else if (opt_key.compare("infill_dense_algo") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<DenseInfillAlgo>(boost::any_cast<DenseInfillAlgo>(value)));
else if (opt_key.compare("no_perimeter_unsupported_algo") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<NoPerimeterUnsupportedAlgo>(boost::any_cast<NoPerimeterUnsupportedAlgo>(value)));
else if (opt_key.compare("wipe_advanced_algo") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<WipeAlgo>(boost::any_cast<WipeAlgo>(value)));
else if (opt_key.compare("support_material_contact_distance_type") == 0)

View File

@ -560,6 +560,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
else if (opt_key.compare("infill_dense_algo") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<DenseInfillAlgo>>(opt_key)->value);
}
else if (opt_key.compare("no_perimeter_unsupported_algo") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<NoPerimeterUnsupportedAlgo>>(opt_key)->value);
}
else if (opt_key.compare("wipe_advanced_algo") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<WipeAlgo>>(opt_key)->value);
}

View File

@ -355,7 +355,8 @@ const std::vector<std::string>& Preset::print_options()
"elefant_foot_compensation", "xy_size_compensation", "hole_size_compensation", "threads", "resolution",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging",
"only_one_perimeter_top", "single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits",
"infill_dense", "infill_dense_algo", "no_perimeter_unsupported", "min_perimeter_unsupported", "noperi_bridge_only",
"infill_dense", "infill_dense_algo",
"no_perimeter_unsupported_algo",
"support_material_solid_first_layer"
, "exact_last_layer_height"
, "perimeter_loop"

View File

@ -951,12 +951,8 @@ void TabPrint::build()
line.append_option(optgroup->get_option("thin_walls_min_width"));
line.append_option(optgroup->get_option("thin_walls_overlap"));
optgroup->append_line(line);
optgroup->append_single_option_line("overhangs");
line = { _(L("Avoid unsupported perimeters")), "" };
line.append_option(optgroup->get_option("no_perimeter_unsupported"));
//line.append_option(optgroup->get_option("min_perimeter_unsupported"));
//line.append_option(optgroup->get_option("noperi_bridge_only"));
optgroup->append_line(line);
optgroup->append_single_option_line("overhangs");
optgroup->append_single_option_line("no_perimeter_unsupported_algo");
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("remove_small_gaps");
@ -1390,11 +1386,6 @@ void TabPrint::update()
for (auto el : { "thin_walls_min_width", "thin_walls_overlap" }) get_field(el)->toggle(m_config->opt_bool("thin_walls"));
get_field("perimeter_loop_seam")->toggle(m_config->opt_bool("perimeter_loop"));
//bool have_no_perimeter_unsupported = have_perimeters && m_config->opt_bool("no_perimeter_unsupported");
//for (auto el : { "min_perimeter_unsupported", "noperi_bridge_only" })
// get_field(el)->toggle(have_no_perimeter_unsupported);
bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0;
// infill_extruder uses the same logic as in Print::extruders()
for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed",