ENH: update overhang degree method on calssic mode

Jira: none

Signed-off-by: qing.zhang <qing.zhang@bambulab.com>
Change-Id: I90f6e4c2ef618fdaef00bdaf1ca309893f484c1e
This commit is contained in:
qing.zhang 2024-01-30 16:03:15 +08:00 committed by Lane.Wei
parent 3fbc9931cb
commit eace110b33
5 changed files with 309 additions and 63 deletions

View File

@ -131,7 +131,7 @@ class ExtrusionPath : public ExtrusionEntity
{
public:
Polyline polyline;
int overhang_degree = 0;
double overhang_degree = 0;
int curve_degree = 0;
// Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
double mm3_per_mm;
@ -143,7 +143,8 @@ public:
ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height, bool no_extrusion = false) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role), m_no_extrusion(no_extrusion) {}
ExtrusionPath(int overhang_degree, int curve_degree, ExtrusionRole role, double mm3_per_mm, float width, float height) : overhang_degree(overhang_degree), curve_degree(curve_degree), mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
ExtrusionPath(double overhang_degree, int curve_degree, ExtrusionRole role, double mm3_per_mm, float width, float height) : overhang_degree(overhang_degree), curve_degree(curve_degree), mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
ExtrusionPath(const ExtrusionPath &rhs)
: polyline(rhs.polyline)
, overhang_degree(rhs.overhang_degree)
@ -258,7 +259,7 @@ public:
int get_overhang_degree() const {
// only perimeter has overhang degree. Other return 0;
if (is_perimeter(m_role))
return overhang_degree;
return (int)overhang_degree;
return 0;
};
void set_curve_degree(int curve) {
@ -452,7 +453,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, Ex
}
}
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, int overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polyline &polyline : polylines)
@ -473,7 +474,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E
polylines.clear();
}
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, int overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polyline &polyline : polylines)
@ -484,6 +485,16 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, i
polylines.clear();
}
inline void extrusion_paths_append(ExtrusionPaths &dst, Polyline &&polyline, double overhang_degree, int curva_degree, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + 1);
if (polyline.is_valid()) {
dst.push_back(ExtrusionPath(overhang_degree, curva_degree, role, mm3_per_mm, width, height));
dst.back().polyline = std::move(polyline);
}
polyline.clear();
}
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());

View File

@ -4221,6 +4221,27 @@ static std::map<int, std::string> overhang_speed_key_map =
{5, "bridge_speed"},
};
double GCode::get_overhang_degree_corr_speed(float normal_speed, double path_degree) {
if (path_degree== 0)
return normal_speed;
int lower_degree_bound = int(path_degree);
if (path_degree==lower_degree_bound)
return m_config.get_abs_value(overhang_speed_key_map[lower_degree_bound].c_str());
int upper_degree_bound = lower_degree_bound + 1;
double lower_speed_bound = lower_degree_bound == 0 ? normal_speed : m_config.get_abs_value(overhang_speed_key_map[lower_degree_bound].c_str());
double upper_speed_bound = upper_speed_bound == 0 ? normal_speed : m_config.get_abs_value(overhang_speed_key_map[upper_degree_bound].c_str());
lower_speed_bound = lower_speed_bound == 0 ? normal_speed : lower_speed_bound;
upper_speed_bound = upper_speed_bound == 0 ? normal_speed : upper_speed_bound;
double speed_out = lower_speed_bound + (upper_speed_bound - lower_speed_bound) * (path_degree - lower_degree_bound);
return speed_out;
}
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
{
std::string gcode;
@ -4305,17 +4326,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double min_speed = double(m_config.slow_down_min_speed.get_at(m_writer.extruder()->id()));
// set speed
if (speed == -1) {
int overhang_degree = path.get_overhang_degree();
if (path.role() == erPerimeter) {
speed = m_config.get_abs_value("inner_wall_speed");
if (m_config.enable_overhang_speed.value && overhang_degree > 0 && overhang_degree <= 5) {
double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str());
if (m_config.enable_overhang_speed.value) {
double new_speed = 0;
new_speed = get_overhang_degree_corr_speed(speed, path.overhang_degree);
speed = new_speed == 0.0 ? speed : new_speed;
}
} else if (path.role() == erExternalPerimeter) {
speed = m_config.get_abs_value("outer_wall_speed");
if (m_config.enable_overhang_speed.value && overhang_degree > 0 && overhang_degree <= 5) {
double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str());
if (m_config.enable_overhang_speed.value ) {
double new_speed = 0;
new_speed = get_overhang_degree_corr_speed(speed, path.overhang_degree);
speed = new_speed == 0.0 ? speed : new_speed;
}
} else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill || path.role() == erSupportTransition) {

View File

@ -513,6 +513,7 @@ private:
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const;
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
double get_overhang_degree_corr_speed(float speed, double path_degree);
void print_machine_envelope(GCodeOutputStream &file, Print &print);
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);

View File

@ -6,12 +6,13 @@
#include "CurveAnalyzer.hpp"
#include "Clipper2Utils.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "Line.hpp"
#include <cmath>
#include <cassert>
#include "libslic3r/AABBTreeLines.hpp"
static const int overhang_sampling_number = 6;
static const double narrow_loop_length_threshold = 10;
static const double min_degree_gap = 0.1;
//BBS: when the width of expolygon is smaller than
//ext_perimeter_width + ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE),
//we think it's small detail area and will generate smaller line width for it
@ -212,6 +213,188 @@ static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) {
}
}
struct PolylineWithDegree
{
PolylineWithDegree(Polyline polyline, double overhang_degree) : polyline(polyline), overhang_degree(overhang_degree){};
Polyline polyline;
double overhang_degree = 0;
};
static std::deque<PolylineWithDegree> split_polyline_by_degree(const Polyline &polyline_with_insert_points, const std::deque<double> &points_overhang)
{
std::deque<PolylineWithDegree> out;
Polyline left;
Polyline right;
Polyline temp_copy = polyline_with_insert_points;
size_t poly_size = polyline_with_insert_points.size();
// BBS: merge degree in limited range
//find first degee base
double degree_base = int(points_overhang[points_overhang.size() - 1] / min_degree_gap) * min_degree_gap + min_degree_gap;
double short_poly_len = 0;
for (int point_idx = points_overhang.size() - 2; point_idx > 0; --point_idx) {
double degree = points_overhang[point_idx];
if ( degree <= degree_base && degree >= degree_base - min_degree_gap )
continue;
temp_copy.split_at_index(point_idx, &left, &right);
temp_copy = std::move(left);
out.push_back(PolylineWithDegree(right, degree_base));
degree_base = int(degree / min_degree_gap) * min_degree_gap + min_degree_gap;
degree_base = degree_base > overhang_sampling_number - 2 ? overhang_sampling_number - 2 : degree_base;
}
if (!temp_copy.empty()) {
out.push_back(PolylineWithDegree(temp_copy, degree_base));
}
return out;
}
static void insert_point_to_line( double left_point_degree,
Point left_point,
double right_point_degree,
Point right_point,
std::deque<double> &points_overhang,
Polyline& polyline,
double mini_length)
{
Line line_temp(left_point, right_point);
double line_length = line_temp.length();
if (std::abs(left_point_degree - right_point_degree) <= 0.5 * min_degree_gap || line_length<scale_(1.5))
return;
Point middle_pt((left_point + right_point) / 2);
std::deque<double> left_points_overhang;
std::deque<double> right_points_overhang;
double middle_degree = (left_point_degree + right_point_degree) / 2;
Polyline left_polyline;
Polyline right_polyline;
insert_point_to_line(left_point_degree, left_point, middle_degree, middle_pt, left_points_overhang, left_polyline, mini_length);
insert_point_to_line(middle_degree, middle_pt, right_point_degree, right_point, right_points_overhang, right_polyline, mini_length);
if (!left_polyline.empty()) {
polyline.points.insert(polyline.points.end(), std::make_move_iterator(left_polyline.points.begin()), std::make_move_iterator(left_polyline.points.end()));
points_overhang.insert(points_overhang.end(), std::make_move_iterator(left_points_overhang.begin()), std::make_move_iterator(left_points_overhang.end()));
}
polyline.append(middle_pt);
points_overhang.emplace_back(middle_degree);
if (!right_polyline.empty()) {
polyline.points.insert(polyline.points.end(), std::make_move_iterator(right_polyline.points.begin()), std::make_move_iterator(right_polyline.points.end()));
points_overhang.insert(points_overhang.end(), std::make_move_iterator(right_points_overhang.begin()), std::make_move_iterator(right_points_overhang.end()));
}
}
class OverhangDistancer
{
std::vector<Linef> lines;
AABBTreeIndirect::Tree<2, double> tree;
public:
OverhangDistancer(const Polygons layer_polygons)
{
for (const Polygon &island : layer_polygons) {
for (const auto &line : island.lines()) {
lines.emplace_back(line.a.cast<double>(), line.b.cast<double>());
}
}
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
}
float distance_from_perimeter(const Vec2f &point) const
{
Vec2d p = point.cast<double>();
size_t hit_idx_out{};
Vec2d hit_point_out = Vec2d::Zero();
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
if (distance < 0) { return std::numeric_limits<float>::max(); }
distance = sqrt(distance);
return distance;
}
};
static std::deque<PolylineWithDegree> detect_overahng_degree(Polygons lower_polygons,
Polylines middle_overhang_polyines,
const double &lower_bound,
const double &upper_bound,
Polylines &too_short_polylines)
{
// BBS: collect lower_polygons points
//Polylines;
Points lower_polygon_points;
std::vector<size_t> polygons_bound;
std::unique_ptr<OverhangDistancer> prev_layer_distancer;
prev_layer_distancer = std::make_unique<OverhangDistancer>(lower_polygons);
std::deque<PolylineWithDegree> out;
std::deque<double> points_overhang;
//BBS: get overhang degree and split path
for (size_t polyline_idx = 0; polyline_idx < middle_overhang_polyines.size(); ++polyline_idx) {
//filter too short polyline
Polyline middle_poly = middle_overhang_polyines[polyline_idx];
if (middle_poly.length() < scale_(1.0)) {
too_short_polylines.push_back(middle_poly);
continue;
}
Polyline polyline_with_insert_points;
points_overhang.clear();
double last_degree = 0;
// BBS : calculate overhang dist
for (size_t point_idx = 0; point_idx < middle_poly.points.size(); ++point_idx) {
Point pt = middle_poly.points[point_idx];
float overhang_dist = prev_layer_distancer->distance_from_perimeter(pt.cast<float>());
overhang_dist = overhang_dist > upper_bound ? upper_bound : overhang_dist;
// BBS : calculate overhang degree
int max_overhang = overhang_sampling_number - 2;
int min_overhang = 0;
double t = (overhang_dist - lower_bound) / (upper_bound - lower_bound);
t = t > 1.0 ? 1: t;
t = t < EPSILON ? 0 : t;
double this_degree = (1.0 - t) * min_overhang + t * max_overhang;
// BBS: intert points
if (point_idx > 0) {
insert_point_to_line(last_degree, middle_poly.points[point_idx - 1], this_degree, pt, points_overhang, polyline_with_insert_points,
upper_bound - lower_bound);
}
points_overhang.push_back(this_degree);
polyline_with_insert_points.append(pt);
last_degree = this_degree;
}
// BBS : split path by degree
std::deque<PolylineWithDegree> polyline_with_merged_degree = split_polyline_by_degree(polyline_with_insert_points, points_overhang);
out.insert(out.end(), std::make_move_iterator(polyline_with_merged_degree.begin()), std::make_move_iterator(polyline_with_merged_degree.end()));
}
return out;
}
std::pair<double, double> PerimeterGenerator::dist_boundary(double width)
{
std::pair<double, double> out;
float nozzle_diameter = print_config->nozzle_diameter.get_at(config->wall_filament - 1);
float start_offset = -0.5 * width;
float end_offset = 0.5 * nozzle_diameter;
double degree_0 = scale_(start_offset + 0.5 * (end_offset - start_offset) / (overhang_sampling_number - 1));
out.first = 0;
out.second = scale_(end_offset) - degree_0;
return out;
}
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
{
// loops is an arrayref of ::Loop objects
@ -238,24 +421,28 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
ExtrusionPaths paths;
// BBS: get lower polygons series, width, mm3_per_mm
const std::map<int, Polygons> *lower_polygons_series;
const std::vector<Polygons> *lower_polygons_series;
const std::pair<double, double> *overhang_dist_boundary;
double extrusion_mm3_per_mm;
double extrusion_width;
if (is_external) {
if (is_small_width) {
//BBS: smaller width external perimeter
lower_polygons_series = &perimeter_generator.m_smaller_external_lower_polygons_series;
overhang_dist_boundary = &perimeter_generator.m_smaller_external_overhang_dist_boundary;
extrusion_mm3_per_mm = perimeter_generator.smaller_width_ext_mm3_per_mm();
extrusion_width = perimeter_generator.smaller_ext_perimeter_flow.width();
} else {
//BBS: normal external perimeter
lower_polygons_series = &perimeter_generator.m_external_lower_polygons_series;
overhang_dist_boundary = &perimeter_generator.m_external_overhang_dist_boundary;
extrusion_mm3_per_mm = perimeter_generator.ext_mm3_per_mm();
extrusion_width = perimeter_generator.ext_perimeter_flow.width();
}
} else {
//BBS: normal perimeter
lower_polygons_series = &perimeter_generator.m_lower_polygons_series;
overhang_dist_boundary = &perimeter_generator.m_lower_overhang_dist_boundary;
extrusion_mm3_per_mm = perimeter_generator.mm3_per_mm();
extrusion_width = perimeter_generator.perimeter_flow.width();
}
@ -275,49 +462,72 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
Polylines remain_polines;
//BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning
if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
for (auto it = lower_polygons_series->begin();
it != lower_polygons_series->end(); it++)
{
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox);
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_polygons_series->back(), bbox);
Polylines inside_polines = (it == lower_polygons_series->begin()) ? intersection_pl({polygon}, lower_polygons_series_clipped) :
intersection_pl(remain_polines, lower_polygons_series_clipped);
Polylines inside_polines = intersection_pl({polygon}, lower_polygons_series_clipped);
remain_polines = diff_pl({polygon}, lower_polygons_series_clipped);
bool detect_overhang_degree = perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None;
if (!detect_overhang_degree) {
if (!inside_polines.empty())
extrusion_paths_append(
paths,
std::move(inside_polines),
it->first,
0,
int(0),
role,
extrusion_mm3_per_mm,
extrusion_width,
(float)perimeter_generator.layer_height);
remain_polines = (it == lower_polygons_series->begin()) ? diff_pl({polygon}, lower_polygons_series_clipped) :
diff_pl(remain_polines, lower_polygons_series_clipped);
if (remain_polines.size() == 0)
break;
}
} else {
auto it = lower_polygons_series->end();
it--;
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox);
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_polygons_series->front(), bbox);
Polylines inside_polines = intersection_pl({polygon}, lower_polygons_series_clipped);
extrusion_paths_append(
paths,
std::move(inside_polines),
int(0),
int(0),
role,
extrusion_mm3_per_mm,
extrusion_width,
(float)perimeter_generator.layer_height);
Polylines middle_overhang_polyines = diff_pl({inside_polines}, lower_polygons_series_clipped);
//BBS: add zero_degree_path
Polylines zero_degree_polines = intersection_pl({inside_polines}, lower_polygons_series_clipped);
if (!zero_degree_polines.empty())
extrusion_paths_append(
paths,
std::move(zero_degree_polines),
0,
int(0),
role,
extrusion_mm3_per_mm,
extrusion_width,
(float)perimeter_generator.layer_height);
//BBS: detect middle line overhang
if (!middle_overhang_polyines.empty()) {
Polylines too_short_polylines;
std::deque<PolylineWithDegree> polylines_degree_collection = detect_overahng_degree(lower_polygons_series->front(),
middle_overhang_polyines,
overhang_dist_boundary->first,
overhang_dist_boundary->second,
too_short_polylines);
if (!too_short_polylines.empty())
extrusion_paths_append(paths,
std::move(too_short_polylines),
0,
int(0),
role,
extrusion_mm3_per_mm,
extrusion_width,
(float) perimeter_generator.layer_height);
// BBS: add path with overhang degree
for (PolylineWithDegree polylines_collection : polylines_degree_collection) {
extrusion_paths_append(paths,
std::move(polylines_collection.polyline),
polylines_collection.overhang_degree,
int(0),
role,
extrusion_mm3_per_mm,
extrusion_width, (float) perimeter_generator.layer_height);
}
}
remain_polines = diff_pl({polygon}, lower_polygons_series_clipped);
}
// get 100% overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
@ -350,9 +560,6 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
// Reapply the nearest point search for starting point.
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
// smothing the overhang degree
// merge small path between paths which have same overhang degree
lowpass_filter_by_paths_overhang_degree(paths);
} else {
ExtrusionPath path(role);
//BBS.
@ -840,12 +1047,16 @@ void PerimeterGenerator::process_classic()
// prepare grown lower layer slices for overhang detection
m_lower_polygons_series = generate_lower_polygons_series(this->perimeter_flow.width());
if (ext_perimeter_width == perimeter_width)
m_lower_overhang_dist_boundary = dist_boundary(this->perimeter_flow.width());
if (ext_perimeter_width == perimeter_width){
m_external_lower_polygons_series = m_lower_polygons_series;
else
m_external_overhang_dist_boundary=m_lower_overhang_dist_boundary;
} else {
m_external_lower_polygons_series = generate_lower_polygons_series(this->ext_perimeter_flow.width());
m_external_overhang_dist_boundary = dist_boundary(this->ext_perimeter_flow.width());
}
m_smaller_external_lower_polygons_series = generate_lower_polygons_series(this->smaller_ext_perimeter_flow.width());
m_smaller_external_overhang_dist_boundary = dist_boundary(this->smaller_ext_perimeter_flow.width());
// we need to process each island separately because we might have different
// extra perimeters for each one
@ -1658,7 +1869,7 @@ bool PerimeterGeneratorLoop::is_internal_contour() const
return true;
}
std::map<int, Polygons> PerimeterGenerator::generate_lower_polygons_series(float width)
std::vector<Polygons> PerimeterGenerator::generate_lower_polygons_series(float width)
{
float nozzle_diameter = print_config->nozzle_diameter.get_at(config->wall_filament - 1);
float start_offset = -0.5 * width;
@ -1667,22 +1878,18 @@ std::map<int, Polygons> PerimeterGenerator::generate_lower_polygons_series(float
assert(overhang_sampling_number >= 3);
// generate offsets
std::vector<float> offset_series;
offset_series.reserve(overhang_sampling_number - 1);
for (int i = 0; i < overhang_sampling_number - 1; i++) {
offset_series.push_back(start_offset + (i + 0.5) * (end_offset - start_offset) / (overhang_sampling_number - 1));
}
// BBS: increase start_offset a little to avoid to calculate 90 degree as overhang
offset_series[0] = start_offset + 0.5 * (end_offset - start_offset) / (overhang_sampling_number - 1);
offset_series[overhang_sampling_number - 2] = end_offset;
offset_series.reserve(2);
std::map<int, Polygons> lower_polygons_series;
offset_series.push_back(start_offset + 0.5 * (end_offset - start_offset) / (overhang_sampling_number - 1));
offset_series.push_back(end_offset);
std::vector<Polygons> lower_polygons_series;
if (this->lower_slices == NULL) {
return lower_polygons_series;
}
// offset expolygon to generate series of polygons
for (int i = 0; i < offset_series.size(); i++) {
lower_polygons_series.insert(std::pair<int, Polygons>(i, offset(*this->lower_slices, float(scale_(offset_series[i])))));
lower_polygons_series.emplace_back(offset(*this->lower_slices, float(scale_(offset_series[i]))));
}
return lower_polygons_series;
}

View File

@ -34,9 +34,13 @@ public:
//BBS
Flow smaller_ext_perimeter_flow;
std::map<int, Polygons> m_lower_polygons_series;
std::map<int, Polygons> m_external_lower_polygons_series;
std::map<int, Polygons> m_smaller_external_lower_polygons_series;
std::vector<Polygons> m_lower_polygons_series;
std::vector<Polygons> m_external_lower_polygons_series;
std::vector<Polygons> m_smaller_external_lower_polygons_series;
std::pair<double, double> m_lower_overhang_dist_boundary;
std::pair<double, double> m_external_overhang_dist_boundary;
std::pair<double, double> m_smaller_external_overhang_dist_boundary;
PerimeterGenerator(
// Input:
@ -79,7 +83,8 @@ public:
Polygons lower_slices_polygons() const { return m_lower_slices_polygons; }
private:
std::map<int, Polygons> generate_lower_polygons_series(float width);
std::vector<Polygons> generate_lower_polygons_series(float width);
std::pair<double, double> dist_boundary(double width);
private:
bool m_spiral_vase;