mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-07-25 05:54:27 +08:00
no perimeters on bridged area : algo changed, more options and less settings.
This commit is contained in:
parent
cd9e99da25
commit
b3157c5b1e
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user