Thin_wall / medial axis:

- reworked thin_variable_width (discretization into segments of constant width)
 - bugfix taper_ends
 - add setting thin_walls_overlap to control the perimeter/thin wall overlap
This commit is contained in:
supermerill 2019-02-07 10:59:10 +01:00
parent bc0b249803
commit 75775bc909
11 changed files with 111 additions and 65 deletions

View File

@ -111,6 +111,7 @@ void Layer::make_perimeters()
&& config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0 && config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0
&& config.thin_walls == other_config.thin_walls && config.thin_walls == other_config.thin_walls
&& config.thin_walls_min_width == other_config.thin_walls_min_width && config.thin_walls_min_width == other_config.thin_walls_min_width
&& config.thin_walls_overlap == other_config.thin_walls_overlap
&& config.external_perimeters_first == other_config.external_perimeters_first && config.external_perimeters_first == other_config.external_perimeters_first
&& config.perimeter_loop == other_config.perimeter_loop) { && config.perimeter_loop == other_config.perimeter_loop) {
layerms.push_back(other_layerm); layerms.push_back(other_layerm);

View File

@ -343,13 +343,8 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify)
double percent_dist = (percent_length - percent_length_other_before) / (percent_length_other - percent_length_other_before); double percent_dist = (percent_length - percent_length_other_before) / (percent_length_other - percent_length_other_before);
coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist); coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist);
new_width += to_modify->width[idx_other] * (percent_dist); new_width += to_modify->width[idx_other] * (percent_dist);
Point new_point;
new_point.x() = (coord_t)((double)(to_modify->points[idx_other - 1].x()) * (1 - percent_dist));
new_point.x() += (coord_t)((double)(to_modify->points[idx_other].x()) * (percent_dist));
new_point.y() = (coord_t)((double)(to_modify->points[idx_other - 1].y()) * (1 - percent_dist));
new_point.y() += (coord_t)((double)(to_modify->points[idx_other].y()) * (percent_dist));
to_modify->width.insert(to_modify->width.begin() + idx_other, new_width); to_modify->width.insert(to_modify->width.begin() + idx_other, new_width);
to_modify->points.insert(to_modify->points.begin() + idx_other, new_point); to_modify->points.insert(to_modify->points.begin() + idx_other, to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other]));
} }
} }
} }
@ -1049,14 +1044,11 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) { while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
//try to split if possible //try to split if possible
if (polyline.width[1] > min_width) { if (polyline.width[1] > min_width) {
double percent_can_keep = 1 - (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > SCALED_RESOLUTION) { if (polyline.points.front().distance_to(polyline.points[1]) * (1-percent_can_keep) > SCALED_RESOLUTION) {
//Can split => move the first point and assign a new weight. //Can split => move the first point and assign a new weight.
//the update of endpoints wil be performed in concatThickPolylines //the update of endpoints wil be performed in concatThickPolylines
polyline.points.front().x() = polyline.points.front().x() + polyline.points.front() = polyline.points.front().interpolate(percent_can_keep, polyline.points[1]);
(coord_t)((polyline.points[1].x() - polyline.points.front().x()) * (1 - percent_can_keep));
polyline.points.front().y() = polyline.points.front().y() +
(coord_t)((polyline.points[1].y() - polyline.points.front().y()) * (1 - percent_can_keep));
polyline.width.front() = min_width; polyline.width.front() = min_width;
} else { } else {
/// almost 0-length, Remove /// almost 0-length, Remove
@ -1073,14 +1065,11 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) { while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
//try to split if possible //try to split if possible
if (polyline.width[polyline.points.size() - 2] > min_width) { if (polyline.width[polyline.points.size() - 2] > min_width) {
double percent_can_keep = 1 - (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back()); double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > SCALED_RESOLUTION) { if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > SCALED_RESOLUTION) {
//Can split => move the first point and assign a new weight. //Can split => move the first point and assign a new weight.
//the update of endpoints wil be performed in concatThickPolylines //the update of endpoints wil be performed in concatThickPolylines
polyline.points.back().x() = polyline.points.back().x() + polyline.points.back() = polyline.points.back().interpolate(percent_can_keep, polyline.points[polyline.points.size() - 2]);
(coord_t)((polyline.points[polyline.points.size() - 2].x() - polyline.points.back().x()) * (1 - percent_can_keep));
polyline.points.back().y() = polyline.points.back().y() +
(coord_t)((polyline.points[polyline.points.size() - 2].y() - polyline.points.back().y()) * (1 - percent_can_keep));
polyline.width.back() = min_width; polyline.width.back() = min_width;
} else { } else {
/// almost 0-length, Remove /// almost 0-length, Remove
@ -1108,6 +1097,7 @@ void
MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
{ {
// concatenate, but even where multiple thickpolyline join, to create nice long strait polylines // concatenate, but even where multiple thickpolyline join, to create nice long strait polylines
/* If we removed any short polylines we now try to connect consecutive polylines /* If we removed any short polylines we now try to connect consecutive polylines
in order to allow loop detection. Note that this algorithm is greedier than in order to allow loop detection. Note that this algorithm is greedier than
@ -1155,7 +1145,11 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
best_dot = other_dot; best_dot = other_dot;
} }
} }
if (best_candidate != nullptr) { if (best_candidate != nullptr && best_candidate->size() > 1) {
//intersections may create ever-ertusion because the included circle can be a bit larger. We have to make it short again.
if (polyline.width.back() > polyline.width[polyline.width.size() - 2] && polyline.width.back() > best_candidate->width[1]) {
polyline.width.back() = std::min(polyline.width[polyline.width.size() - 2], best_candidate->width[1]);
}
polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end()); polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end());
polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end()); polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end());
polyline.endpoints.second = best_candidate->endpoints.second; polyline.endpoints.second = best_candidate->endpoints.second;
@ -1455,7 +1449,6 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// svg.draw(pp); // svg.draw(pp);
// svg.Close(); // svg.Close();
//} //}
concatenate_polylines_with_crossing(pp); concatenate_polylines_with_crossing(pp);
//{ //{
// stringstream stri; // stringstream stri;
@ -1518,26 +1511,49 @@ MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchor
void void
MedialAxis::taper_ends(ThickPolylines& pp) { MedialAxis::taper_ends(ThickPolylines& pp) {
const coord_t min_size = this->nozzle_diameter * 0.1;
const coordf_t length = std::min(this->anchor_size, (this->nozzle_diameter - min_size) / 2);
if (length <= SCALED_RESOLUTION) return;
//ensure the width is not lower than 0.4. //ensure the width is not lower than 0.4.
for (ThickPolyline& polyline : pp) { for (ThickPolyline& polyline : pp) {
if (polyline.length() < nozzle_diameter * 2) continue; if (polyline.length() < length * 2.2) continue;
if (polyline.endpoints.first) { if (polyline.endpoints.first) {
polyline.width[0] = min_width; polyline.width[0] = min_size;
coord_t current_dist = min_width; coord_t current_dist = min_size;
coord_t last_dist = min_size;
for (size_t i = 1; i<polyline.width.size(); ++i) { for (size_t i = 1; i<polyline.width.size(); ++i) {
current_dist += (coord_t) polyline.points[i - 1].distance_to(polyline.points[i]); current_dist += (coord_t) polyline.points[i - 1].distance_to(polyline.points[i]);
if (current_dist > polyline.width[i]) break; if (current_dist > length) {
polyline.width[i] = current_dist; //create new point if not near enough
if (current_dist > polyline.width[i] + SCALED_RESOLUTION) {
coordf_t percent_dist = (polyline.width[i] - polyline.width[i - 1]) / (current_dist - last_dist);
polyline.points.insert(polyline.points.begin() + i, polyline.points[i - 1].interpolate(percent_dist, polyline.points[i]));
polyline.width.insert(polyline.width.begin() + i, polyline.width[i]);
}
break;
}
polyline.width[i] = std::max((coordf_t)min_size, min_size + (polyline.width[i] - min_size) * current_dist / length);
last_dist = current_dist;
} }
} }
if (polyline.endpoints.second) { if (polyline.endpoints.second) {
size_t last_idx = polyline.width.size() - 1; const size_t back_idx = polyline.width.size() - 1;
polyline.width[last_idx] = min_width; polyline.width[back_idx] = min_size;
coord_t current_dist = min_width; coord_t current_dist = min_size;
coord_t last_dist = min_size;
for (size_t i = 1; i<polyline.width.size(); ++i) { for (size_t i = 1; i<polyline.width.size(); ++i) {
current_dist += (coord_t) polyline.points[last_idx - i + 1].distance_to(polyline.points[last_idx - i]); current_dist += (coord_t)polyline.points[back_idx - i + 1].distance_to(polyline.points[back_idx - i]);
if (current_dist > polyline.width[last_idx - i]) break; if (current_dist > length) {
polyline.width[last_idx - i] = current_dist; //create new point if not near enough
if (current_dist > polyline.width[back_idx - i] + SCALED_RESOLUTION) {
coordf_t percent_dist = (polyline.width[back_idx - i] - polyline.width[back_idx - i + 1]) / (current_dist - last_dist);
polyline.points.insert(polyline.points.begin() + back_idx - i + 1, polyline.points[back_idx - i + 1].interpolate(percent_dist, polyline.points[back_idx - i]));
polyline.width.insert(polyline.width.begin() + back_idx - i + 1, polyline.width[back_idx - i]);
}
break;
}
polyline.width[back_idx - i] = std::max((coordf_t)min_size, min_size + (polyline.width[back_idx - i] - min_size) * current_dist / length);
last_dist = current_dist;
} }
} }
} }
@ -1547,65 +1563,67 @@ ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, E
// this value determines granularity of adaptive width, as G-code does not allow // this value determines granularity of adaptive width, as G-code does not allow
// variable extrusion within a single move; this value shall only affect the amount // variable extrusion within a single move; this value shall only affect the amount
// of segments, and any pruning shall be performed before we apply this tolerance // of segments, and any pruning shall be performed before we apply this tolerance
const double tolerance = scale_(0.05); const double tolerance = 4*SCALED_RESOLUTION;//scale_(0.05);
int id_line = 0;
ExtrusionEntityCollection coll; ExtrusionEntityCollection coll;
for (const ThickPolyline &p : polylines) { for (const ThickPolyline &p : polylines) {
id_line++;
ExtrusionPaths paths; ExtrusionPaths paths;
ExtrusionPath path(role); ExtrusionPath path(role);
ThickLines lines = p.thicklines(); ThickLines lines = p.thicklines();
for (int i = 0; i < (int)lines.size(); ++i) { for (int i = 0; i < (int)lines.size(); ++i) {
const ThickLine& line = lines[i]; ThickLine& line = lines[i];
const coordf_t line_len = line.length(); const coordf_t line_len = line.length();
if (line_len < SCALED_EPSILON) continue; if (line_len < SCALED_EPSILON) continue;
double thickness_delta = fabs(line.a_width - line.b_width); double thickness_delta = fabs(line.a_width - line.b_width);
if (thickness_delta > tolerance) { if (thickness_delta > tolerance && ceil(thickness_delta / tolerance) > 2) {
const uint16_t segments = (uint16_t) std::min(16000.0, ceil(thickness_delta / tolerance)); const uint16_t segments = 1+(uint16_t) std::min(16000.0, ceil(thickness_delta / tolerance));
const coordf_t seg_len = line_len / segments;
Points pp; Points pp;
std::vector<coordf_t> width; std::vector<coordf_t> width;
{ {
pp.push_back(line.a); for (size_t j = 0; j < segments; ++j) {
width.push_back(line.a_width); pp.push_back(line.a.interpolate(((double)j) / segments, line.b));
for (size_t j = 1; j < segments; ++j) { double percent_width = ((double)j) / (segments-1);
pp.push_back(line.point_at(j*seg_len)); width.push_back(line.a_width*(1 - percent_width) + line.b_width*percent_width);
coordf_t w = line.a_width + (j*seg_len) * (line.b_width - line.a_width) / line_len;
width.push_back(w);
width.push_back(w);
} }
pp.push_back(line.b); pp.push_back(line.b);
width.push_back(line.b_width);
assert(pp.size() == segments + 1); assert(pp.size() == segments + 1);
assert(width.size() == segments * 2); assert(width.size() == segments);
} }
// delete this line and insert new ones // delete this line and insert new ones
lines.erase(lines.begin() + i); lines.erase(lines.begin() + i);
for (size_t j = 0; j < segments; ++j) { for (size_t j = 0; j < segments; ++j) {
ThickLine new_line(pp[j], pp[j + 1]); ThickLine new_line(pp[j], pp[j + 1]);
new_line.a_width = width[2 * j]; new_line.a_width = width[j];
new_line.b_width = width[2 * j + 1]; new_line.b_width = width[j];
lines.insert(lines.begin() + i + j, new_line); lines.insert(lines.begin() + i + j, new_line);
} }
--i;
continue;
} else if (thickness_delta > 0) {
//create a middle point
ThickLine new_line(line.a.interpolate(0.5, line.b), line.b);
new_line.a_width = line.b_width;
new_line.b_width = line.b_width;
line.b = new_line.a;
line.b_width = line.a_width;
lines.insert(lines.begin() + i + 1, new_line);
--i; --i;
continue; continue;
} }
const double w = std::fmax(line.a_width, line.b_width);
if (path.polyline.points.empty()) { if (path.polyline.points.empty()) {
path.polyline.append(line.a); path.polyline.append(line.a);
path.polyline.append(line.b); path.polyline.append(line.b);
// Convert from spacing to extrusion width based on the extrusion model // Convert from spacing to extrusion width based on the extrusion model
// of a square extrusion ended with semi circles. // of a square extrusion ended with semi circles.
flow.width = (float)unscaled(w) + flow.height * (1. - 0.25 * PI); flow.width = (float)unscaled(line.a_width) + flow.height * (1. - 0.25 * PI);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf(" filling %f gap\n", flow.width); printf(" filling %f gap\n", flow.width);
#endif #endif
@ -1613,7 +1631,7 @@ ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, E
path.width = flow.width; path.width = flow.width;
path.height = flow.height; path.height = flow.height;
} else { } else {
thickness_delta = fabs(scale_(flow.width) - w); thickness_delta = fabs(flow.scaled_spacing() - line.a_width);
if (thickness_delta <= tolerance / 2) { if (thickness_delta <= tolerance / 2) {
// the width difference between this line and the current flow width is // the width difference between this line and the current flow width is
// within the accepted tolerance // within the accepted tolerance

View File

@ -25,10 +25,12 @@ namespace Slic3r {
const coord_t min_width; const coord_t min_width;
const coord_t height; const coord_t height;
coord_t nozzle_diameter; coord_t nozzle_diameter;
coord_t anchor_size;
bool stop_at_min_width = true; bool stop_at_min_width = true;
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const coord_t _max_width, const coord_t _min_width, const coord_t _height) MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) { : surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) {
nozzle_diameter = _min_width; nozzle_diameter = _min_width;
anchor_size = 0;
}; };
void build(ThickPolylines* polylines_out); void build(ThickPolylines* polylines_out);
void build(Polylines* polylines); void build(Polylines* polylines);

View File

@ -228,7 +228,7 @@ void PerimeterGenerator::process()
} }
// Calculate next onion shell of perimeters. // Calculate next onion shell of perimeters.
//this variable stored the nexyt onion //this variable stored the next onion
ExPolygons next_onion; ExPolygons next_onion;
if (i == 0) { if (i == 0) {
// compute next onion, without taking care of thin_walls : destroy too thin areas. // compute next onion, without taking care of thin_walls : destroy too thin areas.
@ -240,23 +240,21 @@ void PerimeterGenerator::process()
if (this->config->thin_walls) { if (this->config->thin_walls) {
// the minimum thickness of a single loop is: // the minimum thickness of a single loop is:
// ext_width/2 + ext_spacing/2 + spacing/2 + width/2 // ext_width/2 + ext_spacing/2 + spacing/2 + width/2
next_onion = offset2_ex( next_onion = offset2_ex(
last, last,
-(float)(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1), -(float)(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1),
+(float)(ext_min_spacing / 2 - 1)); +(float)(ext_min_spacing / 2 - 1));
// detect edge case where a curve can be split in multiple small chunks. // detect edge case where a curve can be split in multiple small chunks.
ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2)); ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2));
float div = 2; float div = 2;
while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) { while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) {
div += 0.5; div += 0.5;
//use a sightly smaller spacing to try to drastically improve the split //use a sightly smaller spacing to try to drastically improve the split, but with a little bit of over-extrusion
ExPolygons next_onion_secondTry = offset2_ex( ExPolygons next_onion_secondTry = offset2_ex(
last, last,
-(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1), -(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1),
+(float)(ext_min_spacing / div - 1)); +(float)(ext_min_spacing / div - 1));
if (next_onion.size() > next_onion_secondTry.size()) { if (next_onion.size() > next_onion_secondTry.size() * 1.1) {
next_onion = next_onion_secondTry; next_onion = next_onion_secondTry;
} }
if (div > 3) break; if (div > 3) break;
@ -281,13 +279,13 @@ void PerimeterGenerator::process()
no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - (float) SCALED_EPSILON), true); no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - (float) SCALED_EPSILON), true);
} }
// compute a bit of overlap to anchor thin walls inside the print. // compute a bit of overlap to anchor thin walls inside the print.
ExPolygons thin_zones_extruded;
for (ExPolygon &half_thin : half_thins) { for (ExPolygon &half_thin : half_thins) {
//growing back the polygon //growing back the polygon
ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2)); ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2));
assert(thin.size() == 1); assert(thin.size() == 1);
coord_t overlap = (coord_t)scale_(this->config->thin_walls_overlap.get_abs_value(this->ext_perimeter_flow.nozzle_diameter));
ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) + ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) +
(float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true); (float)(overlap), jtSquare), no_thin_zone, true);
ExPolygons bounds = union_ex(thin, anchor, true); ExPolygons bounds = union_ex(thin, anchor, true);
for (ExPolygon &bound : bounds) { for (ExPolygon &bound : bounds) {
if (!intersection_ex(thin[0], bound).empty()) { if (!intersection_ex(thin[0], bound).empty()) {
@ -298,14 +296,13 @@ void PerimeterGenerator::process()
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
Slic3r::MedialAxis ma(thin[0], bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, this->layer_height); Slic3r::MedialAxis ma(thin[0], bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, this->layer_height);
ma.nozzle_diameter = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter); ma.nozzle_diameter = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter);
ma.anchor_size = overlap;
ma.build(&thin_walls); ma.build(&thin_walls);
thin_zones_extruded.emplace_back(thin[0]);
} }
break; break;
} }
} }
} }
next_onion = diff_ex(offset_ex(last, -(float)(ext_perimeter_width / 2)), thin_zones_extruded, true);
} }
} else { } else {
//FIXME Is this offset correct if the line width of the inner perimeters differs //FIXME Is this offset correct if the line width of the inner perimeters differs

View File

@ -209,6 +209,19 @@ Point Point::projection_onto(const Line &line) const
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b; return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
} }
/// This method create a new point on the line defined by this and p2.
/// The new point is place at position defined by |p2-this| * percent, starting from this
/// \param percent the proportion of the segment length to place the point
/// \param p2 the second point, forming a segment with this
/// \return a new point, == this if percent is 0 and == p2 if percent is 1
Point Point::interpolate(const double percent, const Point &p2) const
{
Point p_out;
p_out.x() = this->x()*(1 - percent) + p2.x()*(percent);
p_out.y() = this->y()*(1 - percent) + p2.y()*(percent);
return p_out;
}
std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf) std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
{ {
return stm << pointf(0) << "," << pointf(1); return stm << pointf(0) << "," << pointf(1);

View File

@ -121,6 +121,7 @@ public:
double ccw_angle(const Point &p1, const Point &p2) const; double ccw_angle(const Point &p1, const Point &p2) const;
Point projection_onto(const MultiPoint &poly) const; Point projection_onto(const MultiPoint &poly) const;
Point projection_onto(const Line &line) const; Point projection_onto(const Line &line) const;
Point interpolate(const double percent, const Point &p) const;
double distance_to(const Point &point) const { return (point - *this).cast<double>().norm(); } double distance_to(const Point &point) const { return (point - *this).cast<double>().norm(); }
double distance_to_square(const Point &point) const { double distance_to_square(const Point &point) const {

View File

@ -2426,7 +2426,16 @@ void PrintConfigDef::init_fff_params()
def->cli = "thin-walls-min-width=s"; def->cli = "thin-walls-min-width=s";
def->mode = comExpert; def->mode = comExpert;
def->min = 0; def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(33,true); def->default_value = new ConfigOptionFloatOrPercent(33, true);
def = this->add("thin_walls_overlap", coFloatOrPercent);
def->label = L("overlap");
def->category = L("Layers and Perimeters");
def->tooltip = L("Overlap between the thin wall and the perimeters. Can be a % of the external perimeter width (default 50%)");
def->cli = "thin-walls-overlap=s";
def->mode = comExpert;
def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(50, true);
def = this->add("threads", coInt); def = this->add("threads", coInt);
def->label = L("Threads"); def->label = L("Threads");

View File

@ -573,6 +573,7 @@ public:
// Detect thin walls. // Detect thin walls.
ConfigOptionBool thin_walls; ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent thin_walls_min_width; ConfigOptionFloatOrPercent thin_walls_min_width;
ConfigOptionFloatOrPercent thin_walls_overlap;
ConfigOptionFloatOrPercent top_infill_extrusion_width; ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers; ConfigOptionInt top_solid_layers;
ConfigOptionFloatOrPercent top_solid_infill_speed; ConfigOptionFloatOrPercent top_solid_infill_speed;
@ -629,6 +630,7 @@ protected:
OPT_PTR(solid_infill_speed); OPT_PTR(solid_infill_speed);
OPT_PTR(thin_walls); OPT_PTR(thin_walls);
OPT_PTR(thin_walls_min_width); OPT_PTR(thin_walls_min_width);
OPT_PTR(thin_walls_overlap);
OPT_PTR(top_infill_extrusion_width); OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed); OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers); OPT_PTR(top_solid_layers);

View File

@ -463,6 +463,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "infill_overlap" || opt_key == "infill_overlap"
|| opt_key == "thin_walls" || opt_key == "thin_walls"
|| opt_key == "thin_walls_min_width" || opt_key == "thin_walls_min_width"
|| opt_key == "thin_walls_overlap"
|| opt_key == "external_perimeters_first" || opt_key == "external_perimeters_first"
|| opt_key == "perimeter_loop" || opt_key == "perimeter_loop"
|| opt_key == "perimeter_loop_seam" || opt_key == "perimeter_loop_seam"

View File

@ -360,6 +360,7 @@ const std::vector<std::string>& Preset::print_options()
, "infill_not_connected" , "infill_not_connected"
, "first_layer_infill_speed" , "first_layer_infill_speed"
, "thin_walls_min_width" , "thin_walls_min_width"
, "thin_walls_overlap"
}; };
return s_opts; return s_opts;
} }

View File

@ -945,6 +945,7 @@ void TabPrint::build()
line = { _(L("Thin walls")), "" }; line = { _(L("Thin walls")), "" };
line.append_option(optgroup->get_option("thin_walls")); line.append_option(optgroup->get_option("thin_walls"));
line.append_option(optgroup->get_option("thin_walls_min_width")); 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_line(line);
optgroup->append_single_option_line("overhangs"); optgroup->append_single_option_line("overhangs");
line = { _(L("Avoid unsupported perimeters")), "" }; line = { _(L("Avoid unsupported perimeters")), "" };
@ -1363,11 +1364,11 @@ void TabPrint::update()
bool have_perimeters = m_config->opt_int("perimeters") > 0; bool have_perimeters = m_config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", for (auto el : { "extra_perimeters", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", "thin_walls_min_width", "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" }) "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
get_field(el)->toggle(have_perimeters); get_field(el)->toggle(have_perimeters);
get_field("thin_walls_min_width")->toggle(m_config->opt_bool("thin_walls")); 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")); 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"); bool have_no_perimeter_unsupported = have_perimeters && m_config->opt_bool("no_perimeter_unsupported");