diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 69a856d47..1a771da44 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -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) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 4d4fcf475..2d8039da4 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -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 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; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e88f8f8a6..d75366ae6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -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; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7ce7ed95f..b98fc42ce 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -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); diff --git a/src/libslic3r/GCode/GCodeEditer.cpp b/src/libslic3r/GCode/GCodeEditer.cpp index 50ba2d7ce..5c0905fea 100644 --- a/src/libslic3r/GCode/GCodeEditer.cpp +++ b/src/libslic3r/GCode/GCodeEditer.cpp @@ -128,7 +128,7 @@ std::vector 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 node_pos; int line_idx = -1; for (; *line_start != 0; line_start = line_end) @@ -199,7 +199,7 @@ std::vector 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 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); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index a9dbf7371..761614831 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -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 diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index c0be3b110..50d030e18 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -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 &loop_nodes); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6eb0aa32e..3a1f5a632 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -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 &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 ®ion_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 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 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) } } - diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index eb07bf89d..0926a9986 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -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 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 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))); } } } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index a13b6a482..e256aadee 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -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 ¢er, double &diameter) const +{ + if (this->points.size() < 8) { + return false; + } + + center = centroid(); + std::vector 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()! diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 793228c9b..370df82cf 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -61,6 +61,10 @@ public: bool is_valid() const { return this->points.size() >= 3; } void douglas_peucker(double tolerance); + // Point ¢er : out, the center of circle + // double &diameter: out, the diameter of circle + bool is_approx_circle(double max_deviation, double max_variance, Point ¢er, 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. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 361bbd8c5..8e86eb0f5 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -880,6 +880,9 @@ static std::vector 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 diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0afc71caa..9ce4dc221 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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 " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 279ee194e..d0069530d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -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)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 85cadf3dc..48fad2ed1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -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. diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index 96b4e7b8c..25cf0ff27 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -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 holes_circle_compensation; // hole index Surface(SurfaceType _surface_type = stInternal) : surface_type(_surface_type), diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index b92f35ecf..7de993603 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -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("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*/) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9fd18bf9e..bbd93faf5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -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");