ENH: support circle speed compensation

Jira: none

Change-Id: I05f31ff26463cdf6fd900f8f1ca6cf0e0b283925
This commit is contained in:
zhimin.zeng 2024-11-22 18:32:47 +08:00 committed by lane.wei
parent e617e933b8
commit 7d44a8bdca
18 changed files with 386 additions and 42 deletions

View File

@ -14,6 +14,7 @@
namespace Slic3r {
static const double slope_path_ratio = 0.3;
static const double slope_inner_outer_wall_gap = 0.4;
static const int overhang_threshold = 1;
void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{
@ -418,6 +419,17 @@ bool ExtrusionLoop::has_overhang_point(const Point &point) const
return false;
}
bool ExtrusionLoop::has_overhang_paths() const
{
for (const ExtrusionPath &path : this->paths) {
if (is_bridge(path.role()))
return true;
if (path.overhang_degree >= overhang_threshold)
return true;
}
return false;
}
void ExtrusionLoop::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
{
for (const ExtrusionPath &path : this->paths)

View File

@ -66,6 +66,11 @@ enum ExtrusionRole : uint8_t {
erCount
};
enum CustomizeFlag : uint8_t {
cfNone,
cfCircleCompensation // shaft hole tolerance compensation
};
// Special flags describing loop
enum ExtrusionLoopRole {
elrDefault = 1 << 0,
@ -119,6 +124,12 @@ inline bool is_bridge(ExtrusionRole role) {
class ExtrusionEntity
{
public:
ExtrusionEntity() = default;
ExtrusionEntity(const ExtrusionEntity &rhs) { m_customize_flag = rhs.m_customize_flag; };
ExtrusionEntity(ExtrusionEntity &&rhs) { m_customize_flag = rhs.m_customize_flag; };
ExtrusionEntity &operator=(const ExtrusionEntity &rhs) { m_customize_flag = rhs.m_customize_flag; return *this; }
ExtrusionEntity &operator=(ExtrusionEntity &&rhs) { m_customize_flag = rhs.m_customize_flag; return *this; }
virtual ExtrusionRole role() const = 0;
virtual bool is_collection() const { return false; }
virtual bool is_loop() const { return false; }
@ -154,6 +165,12 @@ public:
static std::string role_to_string(ExtrusionRole role);
static ExtrusionRole string_to_role(const std::string_view role);
virtual CustomizeFlag get_customize_flag() const { return m_customize_flag; };
virtual void set_customize_flag(CustomizeFlag flag) { m_customize_flag = flag; };
protected:
CustomizeFlag m_customize_flag{CustomizeFlag::cfNone};
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@ -178,7 +195,8 @@ public:
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)
: ExtrusionEntity(rhs)
, polyline(rhs.polyline)
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
@ -190,7 +208,8 @@ public:
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(ExtrusionPath &&rhs)
: polyline(std::move(rhs.polyline))
: ExtrusionEntity(rhs)
, polyline(std::move(rhs.polyline))
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
@ -202,7 +221,8 @@ public:
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs)
: polyline(polyline)
: ExtrusionEntity(rhs)
, polyline(polyline)
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
@ -214,7 +234,8 @@ public:
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs)
: polyline(std::move(polyline))
: ExtrusionEntity(rhs)
, polyline(std::move(polyline))
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
@ -227,6 +248,7 @@ public:
{}
ExtrusionPath& operator=(const ExtrusionPath& rhs) {
ExtrusionEntity::operator=(rhs);
m_can_reverse = rhs.m_can_reverse;
m_role = rhs.m_role;
m_no_extrusion = rhs.m_no_extrusion;
@ -240,6 +262,7 @@ public:
return *this;
}
ExtrusionPath& operator=(ExtrusionPath&& rhs) {
ExtrusionEntity::operator=(rhs);
m_can_reverse = rhs.m_can_reverse;
m_role = rhs.m_role;
m_no_extrusion = rhs.m_no_extrusion;
@ -441,6 +464,7 @@ public:
ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {}
ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {}
ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {}
ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role, CustomizeFlag flag) : paths(std::move(paths)), m_loop_role(role) { m_customize_flag = flag; }
ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
{ this->paths.push_back(path); }
ExtrusionLoop(const ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
@ -472,6 +496,7 @@ public:
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
bool has_overhang_paths() const;
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
ExtrusionLoopRole loop_role() const { return m_loop_role; }
void set_loop_role(ExtrusionLoopRole role) { m_loop_role = role; }

View File

@ -4388,6 +4388,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
// get paths
ExtrusionPaths paths;
bool set_holes_and_compensation_speed = loop.get_customize_flag() && !loop.has_overhang_paths();
loop.clip_end(clip_length, &paths);
if (paths.empty()) return "";
@ -4449,7 +4450,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
smooth_speed_discontinuity_area(new_loop.paths);
// Then extrude it
for (const auto &p : new_loop.get_all_paths()) {
gcode += this->_extrude(*p, description, speed_for_path(*p));
gcode += this->_extrude(*p, description, speed_for_path(*p), set_holes_and_compensation_speed);
}
set_last_scarf_seam_flag(true);
@ -4474,7 +4475,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
smooth_speed_discontinuity_area(paths);
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
gcode += this->_extrude(*path, description, speed_for_path(*path));
gcode += this->_extrude(*path, description, speed_for_path(*path), set_holes_and_compensation_speed);
}
set_last_scarf_seam_flag(false);
}
@ -5006,7 +5007,7 @@ void GCode::smooth_speed_discontinuity_area(ExtrusionPaths &paths) {
paths = std::move(inter_paths);
}
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool is_first_slope)
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool set_holes_and_compensation_speed, bool is_first_slope)
{
std::string gcode;
@ -5099,7 +5100,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (speed == -1) {
if (path.role() == erPerimeter) {
speed = m_config.inner_wall_speed.get_at(cur_extruder_index());
if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
//reset speed by auto compensation speed
if(set_holes_and_compensation_speed) {
speed = m_config.circle_compensation_speed;
}else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
speed = path.smooth_speed;
else if (m_config.enable_overhang_speed.get_at(cur_extruder_index())) {
double new_speed = 0;
@ -5108,12 +5112,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
} else if (path.role() == erExternalPerimeter) {
speed = m_config.outer_wall_speed.get_at(cur_extruder_index());
if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
// reset speed by auto compensation speed
if (set_holes_and_compensation_speed) {
speed = m_config.circle_compensation_speed;
} else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
speed = path.smooth_speed;
else if (m_config.enable_overhang_speed.get_at(cur_extruder_index())) {
double new_speed = 0;
new_speed = get_overhang_degree_corr_speed(speed, path.overhang_degree);
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.overhang_degree == 5) {
@ -5198,6 +5204,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
char buf[64];
assert(is_decimal_separator_point());
if (set_holes_and_compensation_speed)
gcode += "; Slow Down Start\n";
if (path.role() != m_last_processor_extrusion_role) {
m_last_processor_extrusion_role = path.role();
sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str());
@ -5349,6 +5359,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
}
if (set_holes_and_compensation_speed) {
gcode += "; Slow Down End\n";
}
this->set_last_pos(path.last_point());
return gcode;
}

View File

@ -560,7 +560,7 @@ private:
// BBS
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, bool is_first_slope = false);
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1, bool set_holes_and_compensation_speed = false, bool is_first_slope = false);
ExtrusionPaths set_speed_transition(ExtrusionPaths &paths);
ExtrusionPaths split_and_mapping_speed(double &other_path_v, double &final_v, ExtrusionPath &this_path, double max_smooth_length, bool split_from_left = true);
double get_path_speed(const ExtrusionPath &path);

View File

@ -128,7 +128,7 @@ std::vector<PerExtruderAdjustments> GCodeEditer::parse_layer_gcode(
std::string cooling_node_label = "; COOLING_NODE: ";
bool append_wall_ptr = false;
bool append_inner_wall_ptr = false;
bool not_join_cooling = false;
std::pair<int, int> node_pos;
int line_idx = -1;
for (; *line_start != 0; line_start = line_end)
@ -199,7 +199,7 @@ std::vector<PerExtruderAdjustments> GCodeEditer::parse_layer_gcode(
if (wipe)
line.type |= CoolingLine::TYPE_WIPE;
if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && !wipe && !not_join_cooling) {
line.type |= CoolingLine::TYPE_ADJUSTABLE;
active_speed_modifier = adjustment->lines.size();
}
@ -279,6 +279,10 @@ std::vector<PerExtruderAdjustments> GCodeEditer::parse_layer_gcode(
}
}
current_pos = std::move(new_pos);
} else if (boost::starts_with(sline, "; Slow Down Start")) {
not_join_cooling = true;
} else if (boost::starts_with(sline, "; Slow Down End")) {
not_join_cooling = false;
} else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
line.type = CoolingLine::TYPE_EXTRUDE_END;
active_speed_modifier = size_t(-1);

View File

@ -197,7 +197,13 @@ void Layer::make_perimeters()
if (layerms.size() == 1) { // optimization
(*layerm)->fill_surfaces.surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons, this->loop_nodes);
if (this->object()->config().enable_circle_compensation) {
SurfaceCollection copy_slices = (*layerm)->slices;
(*layerm)->auto_circle_compensation(copy_slices);
(*layerm)->make_perimeters(copy_slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons, this->loop_nodes);
} else
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons, this->loop_nodes);
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
} else {
SurfaceCollection new_slices;
@ -221,6 +227,8 @@ void Layer::make_perimeters()
SurfaceCollection fill_surfaces;
//BBS
ExPolygons fill_no_overlap;
if (this->object()->config().enable_circle_compensation)
layerm_config->auto_circle_compensation(new_slices);
layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap, this->loop_nodes);
// assign fill_surfaces to each layer

View File

@ -80,6 +80,7 @@ public:
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
//BBS
void auto_circle_compensation(SurfaceCollection &slices);
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap, std::vector<LoopNode> &loop_nodes);
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
double infill_area_threshold() const;

View File

@ -47,7 +47,7 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
void LayerRegion::slices_to_fill_surfaces_clipped()
{
// Note: this method should be idempotent, but fill_surfaces gets modified
// 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
// that combine_infill() turns some fill_surface into VOID surfaces.
@ -64,12 +64,73 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
}
void LayerRegion::auto_circle_compensation(SurfaceCollection& slices)
{
const PrintObjectConfig &object_config = this->layer()->object()->config();
double max_deviation = object_config.max_deviation * 1e6;
double max_variance = object_config.max_variance * 1e6;
double limited_speed = object_config.circle_compensation_speed;
double counter_speed_coef = object_config.counter_coef_1 / 1e6;
double counter_diameter_coef = object_config.counter_coef_2 / 1e6;
double counter_compensate_coef = object_config.counter_coef_3;
double hole_speed_coef = object_config.hole_coef_1 / 1e6;
double hole_diameter_coef = object_config.hole_coef_2 / 1e6;
double hole_compensate_coef = object_config.hole_coef_3;
double counter_limit_min_value = object_config.counter_limit_min;
double counter_limit_max_value = object_config.counter_limit_max;
double hole_limit_min_value = object_config.hole_limit_min;
double hole_limit_max_value = object_config.hole_limit_max;
double diameter_limit_value = object_config.diameter_limit;
for (Surface &surface : slices.surfaces) {
Point center;
double diameter = 0;
if (surface.expolygon.contour.is_approx_circle(max_deviation, max_variance, center, diameter)) {
double offset_value = counter_speed_coef * limited_speed + counter_diameter_coef * diameter + counter_compensate_coef;
if (offset_value < counter_limit_min_value) {
offset_value = counter_limit_min_value;
} else if (offset_value > counter_limit_max_value) {
offset_value = counter_limit_max_value;
}
Polygons offseted_polys = offset(surface.expolygon.contour, offset_value);
if (offseted_polys.size() == 1) {
surface.expolygon.contour = offseted_polys[0];
if (diameter < diameter_limit_value * 1e6)
surface.counter_circle_compensation = true;
}
}
for (size_t i = 0; i < surface.expolygon.holes.size(); ++i) {
Polygon &hole = surface.expolygon.holes[i];
if (hole.is_approx_circle(max_deviation, max_variance, center, diameter)) {
double offset_value = hole_speed_coef * limited_speed + hole_diameter_coef * diameter + hole_compensate_coef;
if (offset_value < hole_limit_min_value) {
offset_value = hole_limit_min_value;
} else if (offset_value > hole_limit_max_value) {
offset_value = hole_limit_max_value;
}
// positive value means shrinking hole, which oppsite to contour
offset_value = -offset_value;
Polygons offseted_polys = offset(hole, offset_value);
if (offseted_polys.size() == 1) {
hole = offseted_polys[0];
if (diameter < diameter_limit_value * 1e6)
surface.holes_circle_compensation.push_back(i);
}
}
}
}
}
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection *fill_surfaces, ExPolygons *fill_no_overlap, std::vector<LoopNode> &loop_nodes)
{
this->perimeters.clear();
this->thin_fills.clear();
const PrintConfig &print_config = this->layer()->object()->print()->config();
const PrintConfig & print_config = this->layer()->object()->print()->config();
const PrintRegionConfig &region_config = this->region().config();
const PrintObjectConfig& object_config = this->layer()->object()->config();
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
@ -87,7 +148,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
&this->layer()->object()->config(),
&print_config,
spiral_mode,
// output:
&this->perimeters,
&this->thin_fills,
@ -96,13 +156,13 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
fill_no_overlap,
&loop_nodes
);
if (this->layer()->lower_layer != nullptr)
// Cummulative sum of polygons over all the regions.
g.lower_slices = &this->layer()->lower_layer->lslices;
if (this->layer()->upper_layer != NULL)
g.upper_slices = &this->layer()->upper_layer->lslices;
g.layer_id = (int)this->layer()->id();
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
g.overhang_flow = this->bridging_flow(frPerimeter, object_config.thick_bridges);
@ -658,13 +718,13 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
static int iRun = 0;
SVG svg(debug_out_path("3_process_external_surfaces-fill_regions-%d.svg", iRun ++).c_str(), get_extents(fill_boundaries_ex));
svg.draw(fill_boundaries_ex);
svg.draw_outline(fill_boundaries_ex, "black", "blue", scale_(0.05));
svg.draw_outline(fill_boundaries_ex, "black", "blue", scale_(0.05));
svg.Close();
}
// export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
{
// Bridge expolygons, grown, to be tested for intersection with other bridge regions.
std::vector<BoundingBox> fill_boundaries_ex_bboxes = get_extents_vector(fill_boundaries_ex);
@ -675,7 +735,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
const Point pt = bridges[i].expolygon.contour.points.front();
int idx_island = -1;
for (int j = 0; j < int(fill_boundaries_ex.size()); ++ j)
if (fill_boundaries_ex_bboxes[j].contains(pt) &&
if (fill_boundaries_ex_bboxes[j].contains(pt) &&
fill_boundaries_ex[j].contains(pt)) {
idx_island = j;
break;
@ -697,7 +757,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// 2) Group the bridge surfaces by overlaps.
std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
size_t n_groups = 0;
size_t n_groups = 0;
for (size_t i = 0; i < bridges.size(); ++ i) {
// A grup id for this bridge.
size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i];
@ -821,7 +881,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
s1);
}
}
// Subtract the new top surfaces from the other non-top surfaces and re-add them.
Polygons new_polygons = to_polygons(new_surfaces);
for (size_t i = 0; i < internal.size(); ++ i) {
@ -841,7 +901,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
polygons_append(new_polygons, to_polygons(new_expolys));
surfaces_append(new_surfaces, std::move(new_expolys), s1);
}
this->fill_surfaces.surfaces = std::move(new_surfaces);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -855,12 +915,12 @@ void LayerRegion::prepare_fill_surfaces()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
export_region_fill_surfaces_to_svg_debug("2_prepare_fill_surfaces-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
/* Note: in order to make the psPrepareInfill step idempotent, we should never
alter fill_surfaces boundaries on which our idempotency relies since that's
the only meaningful information returned by psPerimeters. */
bool spiral_mode = this->layer()->object()->print()->config().spiral_mode;
#if 0
@ -960,7 +1020,7 @@ void LayerRegion::export_region_fill_surfaces_to_svg(const char *path) const
const float transparency = 0.5f;
for (const Surface &surface : this->fill_surfaces.surfaces) {
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
svg.draw_outline(surface.expolygon, "black", "blue", scale_(0.05));
svg.draw_outline(surface.expolygon, "black", "blue", scale_(0.05));
}
export_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
@ -1049,4 +1109,3 @@ void LayerRegion::simplify_loop(ExtrusionLoop* loop)
}
}

View File

@ -46,14 +46,16 @@ public:
// BBS: is perimeter using smaller width
bool is_smaller_width_perimeter;
// Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole.
unsigned short depth;
unsigned short depth;
// Should this contur be fuzzyfied on path generation?
bool fuzzify;
// Slow down speed for circle
bool need_circle_compensation = false;
// Children contour, may be both CCW and CW oriented (outer contours or holes).
std::vector<PerimeterGeneratorLoop> children;
PerimeterGeneratorLoop(const Polygon &polygon, unsigned short depth, bool is_contour, bool fuzzify, bool is_small_width_perimeter = false) :
polygon(polygon), is_contour(is_contour), is_smaller_width_perimeter(is_small_width_perimeter), depth(depth), fuzzify(fuzzify) {}
PerimeterGeneratorLoop(const Polygon &polygon, unsigned short depth, bool is_contour, bool fuzzify, bool is_small_width_perimeter = false, bool need_circle_compensation_ = false) :
polygon(polygon), is_contour(is_contour), is_smaller_width_perimeter(is_small_width_perimeter), depth(depth), fuzzify(fuzzify), need_circle_compensation(need_circle_compensation_) {}
// External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
bool is_external() const { return this->depth == 0; }
// An island, which may have holes, but it does not have another internal island.
@ -452,6 +454,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
for (const PerimeterGeneratorLoop &loop : loops) {
bool is_external = loop.is_external();
bool is_small_width = loop.is_smaller_width_perimeter;
CustomizeFlag flag = loop.need_circle_compensation ? CustomizeFlag::cfCircleCompensation : CustomizeFlag::cfNone;
ExtrusionRole role;
ExtrusionLoopRole loop_role;
@ -631,7 +634,11 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
paths.emplace_back(std::move(path));
}
coll.append(ExtrusionLoop(std::move(paths), loop_role));
for (ExtrusionPath& path : paths) {
path.set_customize_flag(flag);
}
coll.append(ExtrusionLoop(std::move(paths), loop_role, flag));
}
// Append thin walls to the nearest-neighbor search (only for first iteration)
@ -1185,7 +1192,26 @@ void PerimeterGenerator::process_classic()
if (loop_number > 0 && ((this->object_config->top_one_wall_type != TopOneWallType::None && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0)))
loop_number = 0;
bool counter_circle_compensation = surface.counter_circle_compensation;
std::vector<Point> compensation_holes_centers;
for (size_t i = 0; i < surface.holes_circle_compensation.size(); ++i) {
Point center = surface.expolygon.holes[i].centroid();
compensation_holes_centers.emplace_back(center);
}
double eps = 1000;
auto is_compensation_hole = [&compensation_holes_centers, &eps](const Polygon &hole) -> bool {
auto iter = std::find_if(compensation_holes_centers.begin(), compensation_holes_centers.end(), [&hole, &eps](const Point &item) {
double distance = std::sqrt(std::pow(hole.centroid().x() - item.x(), 2) + std::pow(hole.centroid().y() - item.y(), 2));
return distance < eps;
});
return iter != compensation_holes_centers.end();
};
ExPolygons last = union_ex(surface.expolygon.simplify_p(surface_simplify_resolution));
if (last.size() != 1)
counter_circle_compensation = false;
ExPolygons gaps;
ExPolygons top_fills;
ExPolygons fill_clip;
@ -1304,12 +1330,11 @@ void PerimeterGenerator::process_classic()
// outer contour may overlap with itself.
//FIXME evaluate the overlaps, annotate each point with an overlap depth,
// compensate for the depth of intersection.
contours[i].emplace_back(expolygon.contour, i, true, fuzzify_contours);
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, false, counter_circle_compensation));
if (!expolygon.holes.empty()) {
holes[i].reserve(holes[i].size() + expolygon.holes.size());
for (const Polygon& hole : expolygon.holes)
holes[i].emplace_back(hole, i, false, fuzzify_holes);
for (const Polygon &hole : expolygon.holes)
holes[i].emplace_back(hole, i, false, fuzzify_holes, false, is_compensation_hole(hole));
}
}
@ -1345,11 +1370,11 @@ void PerimeterGenerator::process_classic()
}
for (const ExPolygon& expolygon : offsets_with_smaller_width) {
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true));
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true, counter_circle_compensation));
if (!expolygon.holes.empty()) {
holes[i].reserve(holes[i].size() + expolygon.holes.size());
for (const Polygon& hole : expolygon.holes)
holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false, fuzzify_contours, true));
holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false, fuzzify_contours, true, is_compensation_hole(hole)));
}
}
}

View File

@ -99,6 +99,39 @@ void Polygon::douglas_peucker(double tolerance)
this->points = std::move(p);
}
bool Polygon::is_approx_circle(double max_deviation, double max_variance, Point &center, double &diameter) const
{
if (this->points.size() < 8) {
return false;
}
center = centroid();
std::vector<double> distances;
for (const auto &point : this->points) {
double distance = std::sqrt(std::pow(point.x() - center.x(), 2) + std::pow(point.y() - center.y(), 2));
distances.push_back(distance);
}
double max_dist = *std::max_element(distances.begin(), distances.end());
double min_dist = *std::min_element(distances.begin(), distances.end());
if ((max_dist - min_dist) > max_deviation) {
return false;
}
double avg_dist = std::accumulate(distances.begin(), distances.end(), 0.0) / distances.size();
double variance = 0;
for (double d : distances) { variance += std::pow(d - avg_dist, 2); }
variance /= distances.size();
if (variance > max_variance) {
return false;
}
diameter = 2 * avg_dist;
return true;
}
Polygons Polygon::simplify(double tolerance) const
{
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!

View File

@ -61,6 +61,10 @@ public:
bool is_valid() const { return this->points.size() >= 3; }
void douglas_peucker(double tolerance);
// Point &center : out, the center of circle
// double &diameter: out, the diameter of circle
bool is_approx_circle(double max_deviation, double max_variance, Point &center, double &diameter) const;
// Does an unoriented polygon contain a point?
bool contains(const Point &point) const { return Slic3r::contains(*this, point, true); }
// Approximate on boundary test.

View File

@ -880,6 +880,9 @@ static std::vector<std::string> s_Preset_print_options {
"top_surface_line_width", "support_line_width", "infill_wall_overlap", "bridge_flow",
"elefant_foot_compensation", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
"prime_tower_width", "prime_tower_brim_width", "prime_tower_outer_first", "prime_volume",
"enable_circle_compensation", "circle_compensation_speed", "max_deviation", "max_variance",
"counter_coef_1", "counter_coef_2", "counter_coef_3", "hole_coef_1", "hole_coef_2", "hole_coef_3",
"counter_limit_min", "counter_limit_max", "hole_limit_min", "hole_limit_max", "diameter_limit",
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"flush_into_infill", "flush_into_objects", "flush_into_support","process_notes",
// BBS

View File

@ -4164,8 +4164,100 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("enable_circle_compensation", coBool);
def->label = L("Auto holes-contour compensation");
def->tooltip = L("enable_circle_compensation");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("circle_compensation_speed", coFloat);
def->label = L("Circle Compensation Speed");
def->tooltip = L("circle_compensation_speed");
def->sidetext = L("mm/s");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(200));
def = this->add("max_deviation", coFloat);
def->label = L("Deviation");
def->tooltip = L("max_deviation");
def->sidetext = L("mm");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("max_variance", coFloat);
def->label = L("Variance");
def->tooltip = L("max_variance");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(500));
def = this->add("counter_coef_1", coFloat);
def->label = L("Counter Coef 1");
def->tooltip = L("counter_coef_1");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("counter_coef_2", coFloat);
def->label = L("Counter Coef 2");
def->tooltip = L("counter_coef_2");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(25000));
def = this->add("counter_coef_3", coFloat);
def->label = L("Counter Coef 3");
def->tooltip = L("counter_coef_3");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(-110000));
def = this->add("hole_coef_1", coFloat);
def->label = L("Hole Coef 1");
def->tooltip = L("hole_coef_1");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("hole_coef_2", coFloat);
def->label = L("Hole Coef 2");
def->tooltip = L("hole_coef_2");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(-25000));
def = this->add("hole_coef_3", coFloat);
def->label = L("Hole Coef 3");
def->tooltip = L("hole_coef_3");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(280000));
def = this->add("counter_limit_min", coFloat);
def->label = L("Counter limit min");
def->tooltip = L("counter_limit_min");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(-40000));
def = this->add("counter_limit_max", coFloat);
def->label = L("Counter limit max");
def->tooltip = L("counter_limit_max");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(50000));
def = this->add("hole_limit_min", coFloat);
def->label = L("Hole limit min");
def->tooltip = L("hole_limit_min");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(80000));
def = this->add("hole_limit_max", coFloat);
def->label = L("Hole limit max");
def->tooltip = L("hole_limit_max");
def->sidetext = L("/1e6");
def->set_default_value(new ConfigOptionFloat(250000));
def = this->add("diameter_limit", coFloat);
def->label = L("Diameter limit");
def->tooltip = L("diameter_limit");
def->sidetext = L("mm");
def->set_default_value(new ConfigOptionFloat(50));
def = this->add("flush_volumes_vector", coFloats);
// BBS: remove _L()
// BBS: remove _L()w
def->label = ("Purging volumes - load/unload volumes");
//def->tooltip = L("This vector saves required volumes to change from/to each tool used on the "
// "wipe tower. These values are used to simplify creation of the full purging "

View File

@ -822,6 +822,22 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, support_object_first_layer_gap))
((ConfigOptionFloat, xy_hole_compensation))
((ConfigOptionFloat, xy_contour_compensation))
//BBS auto hole contour compensation
((ConfigOptionBool, enable_circle_compensation))
((ConfigOptionFloat, circle_compensation_speed))
((ConfigOptionFloat, max_deviation))
((ConfigOptionFloat, max_variance))
((ConfigOptionFloat, counter_coef_1))
((ConfigOptionFloat, counter_coef_2))
((ConfigOptionFloat, counter_coef_3))
((ConfigOptionFloat, hole_coef_1))
((ConfigOptionFloat, hole_coef_2))
((ConfigOptionFloat, hole_coef_3))
((ConfigOptionFloat, counter_limit_min))
((ConfigOptionFloat, counter_limit_max))
((ConfigOptionFloat, hole_limit_min))
((ConfigOptionFloat, hole_limit_max))
((ConfigOptionFloat, diameter_limit))
((ConfigOptionBool, flush_into_objects))
// BBS
((ConfigOptionBool, flush_into_infill))

View File

@ -798,7 +798,22 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "only_one_wall_first_layer"
|| opt_key == "initial_layer_line_width"
|| opt_key == "inner_wall_line_width"
|| opt_key == "infill_wall_overlap") {
|| opt_key == "infill_wall_overlap"
|| opt_key == "enable_circle_compensation"
|| opt_key == "circle_compensation_speed"
|| opt_key == "max_deviation"
|| opt_key == "max_variance"
|| opt_key == "counter_coef_1"
|| opt_key == "counter_coef_2"
|| opt_key == "counter_coef_3"
|| opt_key == "hole_coef_1"
|| opt_key == "hole_coef_2"
|| opt_key == "hole_coef_3"
|| opt_key == "counter_limit_min"
|| opt_key == "counter_limit_max"
|| opt_key == "hole_limit_min"
|| opt_key == "hole_limit_max"
|| opt_key == "diameter_limit") {
steps.emplace_back(posPerimeters);
} else if (opt_key == "gap_infill_speed" || opt_key == "filter_out_gap_fill") {
// Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero.

View File

@ -37,6 +37,8 @@ public:
unsigned short thickness_layers; // in layers
double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined)
unsigned short extra_perimeters;
bool counter_circle_compensation{false};
std::vector<int> holes_circle_compensation; // hole index
Surface(SurfaceType _surface_type = stInternal)
: surface_type(_surface_type),

View File

@ -266,6 +266,16 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
is_msg_dlg_already_exist = false;
}
//if enable auto hole and contour compensation, disable the manner
if (config->opt_bool("enable_circle_compensation")) {
DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true;
new_conf.set_key_value("xy_contour_compensation", new ConfigOptionFloat(0));
new_conf.set_key_value("xy_hole_compensation", new ConfigOptionFloat(0));
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
if (config->option<ConfigOptionFloat>("elefant_foot_compensation")->value > 1)
{
const wxString msg_text = _(L("Too large elephant foot compensation is unreasonable.\n"
@ -715,6 +725,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("interlocking_beam_layer_count", use_beam_interlocking);
toggle_line("interlocking_depth", use_beam_interlocking);
toggle_line("interlocking_boundary_avoidance", use_beam_interlocking);
bool enable_auto_hole_and_contour_compensation = config->opt_bool("enable_circle_compensation");
for (auto el : {"max_deviation", "max_variance", "circle_compensation_speed", "counter_coef_1", "counter_coef_2", "counter_coef_3", "hole_coef_1", "hole_coef_2", "hole_coef_3", "counter_limit_min", "counter_limit_max", "hole_limit_min", "hole_limit_max", "diameter_limit"})
toggle_line(el, enable_auto_hole_and_contour_compensation);
toggle_field("xy_hole_compensation", !enable_auto_hole_and_contour_compensation);
toggle_field("xy_contour_compensation", !enable_auto_hole_and_contour_compensation);
}
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)

View File

@ -2006,6 +2006,21 @@ void TabPrint::build()
optgroup->append_single_option_line("xy_hole_compensation", "xy-hole-contour-compensation");
optgroup->append_single_option_line("xy_contour_compensation", "xy-hole-contour-compensation");
optgroup->append_single_option_line("elefant_foot_compensation", "parameter/elephant-foot");
optgroup->append_single_option_line("enable_circle_compensation");
optgroup->append_single_option_line("circle_compensation_speed");
optgroup->append_single_option_line("max_deviation");
optgroup->append_single_option_line("max_variance");
optgroup->append_single_option_line("counter_coef_1");
optgroup->append_single_option_line("counter_coef_2");
optgroup->append_single_option_line("counter_coef_3");
optgroup->append_single_option_line("hole_coef_1");
optgroup->append_single_option_line("hole_coef_2");
optgroup->append_single_option_line("hole_coef_3");
optgroup->append_single_option_line("counter_limit_min");
optgroup->append_single_option_line("counter_limit_max");
optgroup->append_single_option_line("hole_limit_min");
optgroup->append_single_option_line("hole_limit_max");
optgroup->append_single_option_line("diameter_limit");
optgroup->append_single_option_line("precise_z_height");
optgroup = page->new_optgroup(L("Ironing"), L"param_ironing");