From 7f397cd7b3291a9d6a8c7d2696c07c14b2fadcb9 Mon Sep 17 00:00:00 2001 From: SachCZ Date: Fri, 5 Jan 2024 12:17:03 +0100 Subject: [PATCH] Implement ramping layer change using a tag in gcode During layer change, instead of generating the gcode, generate a placeholder tag. Then at the end of layer processing replace this tag with a ramping travel move. This solves the issue, that one does not know the starting point of the current layer where the layer change gcode would be originally generate. The ramping layer changes uses smoothing of the ramping travel. Also it is adjusted in such a way that it increases the ramp angle when the travel is too short, to always reach the next layer. --- src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/GCode.cpp | 216 +++++++++++-------- src/libslic3r/GCode.hpp | 38 ++-- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + src/libslic3r/GCode/GCodeProcessor.hpp | 1 + src/libslic3r/GCode/GCodeWriter.cpp | 60 ++++-- src/libslic3r/GCode/GCodeWriter.hpp | 17 +- src/libslic3r/GCode/LayerChanges.cpp | 53 ----- src/libslic3r/GCode/LayerChanges.hpp | 52 ----- src/libslic3r/GCode/Travels.cpp | 31 +-- src/libslic3r/GCode/Travels.hpp | 14 ++ src/libslic3r/GCode/WipeTowerIntegration.cpp | 20 +- tests/fff_print/CMakeLists.txt | 1 - tests/fff_print/test_gcode_layer_changes.cpp | 55 ----- 14 files changed, 241 insertions(+), 320 deletions(-) delete mode 100644 src/libslic3r/GCode/LayerChanges.cpp delete mode 100644 src/libslic3r/GCode/LayerChanges.hpp delete mode 100644 tests/fff_print/test_gcode_layer_changes.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 7921fccab1..ce24457cd1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -193,8 +193,6 @@ set(SLIC3R_SOURCES GCode/AvoidCrossingPerimeters.hpp GCode/Travels.cpp GCode/Travels.hpp - GCode/LayerChanges.cpp - GCode/LayerChanges.hpp GCode.cpp GCode.hpp GCodeReader.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 30df60c786..1a8efc7a35 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -35,7 +35,6 @@ #include "GCode/WipeTower.hpp" #include "GCode/WipeTowerIntegration.hpp" #include "GCode/Travels.hpp" -#include "GCode/LayerChanges.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" @@ -2081,6 +2080,50 @@ AABBTreeLines::LinesDistancer get_previous_layer_distancer( } } +std::string GCodeGenerator::get_layer_change_gcode(const Vec3d& from, const Vec3d& to, const unsigned extruder_id) { + const Polyline xy_path{ + this->gcode_to_point(from.head<2>()), + this->gcode_to_point(to.head<2>()) + }; + + using namespace GCode::Impl::Travels; + + ElevatedTravelParams elevation_params{ + get_elevated_traval_params(xy_path, this->m_config, extruder_id)}; + + const double initial_elevation = from.z(); + const double z_change = to.z() - from.z(); + elevation_params.lift_height = std::max(z_change, elevation_params.lift_height); + + const double path_length = unscaled(xy_path.length()); + const double lift_at_travel_end = + (elevation_params.lift_height / elevation_params.slope_end * path_length); + if (lift_at_travel_end < z_change) { + elevation_params.lift_height = z_change; + elevation_params.slope_end = path_length; + } + + const std::vector ensure_points_at_distances = linspace( + elevation_params.slope_end - elevation_params.blend_width / 2.0, + elevation_params.slope_end + elevation_params.blend_width / 2.0, + elevation_params.parabola_points_count + ); + + Points3 travel{generate_elevated_travel( + xy_path.points, ensure_points_at_distances, initial_elevation, + ElevatedTravelFormula{elevation_params} + )}; + + std::string travel_gcode; + Vec3d previous_point{this->point_to_gcode(travel.front())}; + for (const Vec3crd& point : tcb::span{travel}.subspan(1)) { + const Vec3d gcode_point{this->point_to_gcode(point)}; + travel_gcode += this->m_writer.get_travel_to_xyz_gcode(previous_point, gcode_point, "layer change"); + previous_point = gcode_point; + } + return travel_gcode; +} + // In sequential mode, process_layer is called once per each object and its copy, // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. @@ -2150,6 +2193,7 @@ LayerResult GCodeGenerator::process_layer( m_enable_loop_clipping = !enable; } + std::string gcode; assert(is_decimal_separator_point()); // for the sprintfs @@ -2168,6 +2212,7 @@ LayerResult GCodeGenerator::process_layer( m_last_layer_z = static_cast(print_z); m_max_layer_z = std::max(m_max_layer_z, m_last_layer_z); m_last_height = height; + m_current_layer_first_position = std::nullopt; // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. if (! print.config().before_layer_gcode.value.empty()) { @@ -2179,7 +2224,7 @@ LayerResult GCodeGenerator::process_layer( print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } - gcode += this->change_layer(previous_layer_z, print_z, result.spiral_vase_enable); // this will increase m_layer_index + gcode += this->change_layer(previous_layer_z, print_z); // this will increase m_layer_index m_layer = &layer; if (this->line_distancer_is_required(layer_tools.extruders) && this->m_layer != nullptr && this->m_layer->lower_layer != nullptr) { this->m_previous_layer_distancer = GCode::Impl::get_previous_layer_distancer(layers, layer.lower_layer->lslices); @@ -2305,6 +2350,33 @@ LayerResult GCodeGenerator::process_layer( is_anything_overridden, false /* print_wipe_extrusions */); } + + // During layer change the starting position of next layer is now known. + // The solution is thus to emplace a temporary tag to the gcode, cache the postion and + // replace the tag later. The tag is Layer_Change_Travel, the cached position is + // m_current_layer_first_position and it is replaced here. + const std::string tag = GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Travel); + std::string layer_change_gcode; + const bool do_ramping_layer_change = ( + m_previous_layer_last_position + && m_current_layer_first_position + && m_layer_change_extruder_id + && !result.spiral_vase_enable + && print_z > previous_layer_z + && EXTRUDER_CONFIG(travel_ramping_lift) + && EXTRUDER_CONFIG(travel_slope) > 0 && EXTRUDER_CONFIG(travel_slope) < 90 + ); + if (do_ramping_layer_change) { + layer_change_gcode = this->get_layer_change_gcode(*m_previous_layer_last_position, *m_current_layer_first_position, *m_layer_change_extruder_id); + } else { + if (!m_current_layer_first_position) { + throw std::runtime_error("Destination is required for layer change!"); + } + layer_change_gcode = this->writer().get_travel_to_z_gcode(m_current_layer_first_position->z(), "simple layer change"); + } + + boost::algorithm::replace_first(gcode, tag, layer_change_gcode); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); @@ -2604,63 +2676,10 @@ std::string GCodeGenerator::preamble() return gcode; } - -std::optional GCodeGenerator::get_helical_layer_change_gcode( - const coordf_t previous_layer_z, - const coordf_t print_z, - const std::string& comment -) { - - if (!this->last_pos_defined()) { - return std::nullopt; - } - - const double circle_radius{2}; - const unsigned n_gon_points_count{16}; - - const Point n_gon_start_point{this->last_pos()}; - - GCode::Impl::LayerChanges::Bed bed{ - this->m_config.bed_shape.values, - circle_radius * 2 - }; - if (!bed.contains_within_padding(this->point_to_gcode(n_gon_start_point))) { - return std::nullopt; - } - - const Vec2crd n_gon_vector{scaled(Vec2d{ - (bed.centroid - this->point_to_gcode(n_gon_start_point)).normalized() * circle_radius - })}; - const Point n_gon_centeroid{n_gon_start_point + n_gon_vector}; - - const Polygon n_gon{GCode::Impl::LayerChanges::generate_regular_polygon( - n_gon_centeroid, - n_gon_start_point, - n_gon_points_count - )}; - - const double n_gon_circumference = unscaled(n_gon.length()); - - const double z_change{print_z - previous_layer_z}; - Points3 helix{GCode::Impl::Travels::generate_elevated_travel( - n_gon.points, - {}, - previous_layer_z, - [&](const double distance){ - return distance / n_gon_circumference * z_change; - } - )}; - - helix.emplace_back(to_3d(this->last_pos(), scaled(print_z))); - - return this->generate_travel_gcode(helix, comment); -} - // called by GCodeGenerator::process_layer() std::string GCodeGenerator::change_layer( coordf_t previous_layer_z, - coordf_t print_z, - const bool spiral_vase_enabled + coordf_t print_z ) { std::string gcode; if (m_layer_count > 0) @@ -2670,31 +2689,16 @@ std::string GCodeGenerator::change_layer( if (EXTRUDER_CONFIG(retract_layer_change)) gcode += this->retract_and_wipe(); - const std::string comment{"move to next layer (" + std::to_string(m_layer_index) + ")"}; + Vec3d new_position = this->writer().get_position(); + new_position.z() = print_z; + this->writer().update_position(new_position); - bool do_helical_layer_change{ - !spiral_vase_enabled - && print_z > previous_layer_z - && EXTRUDER_CONFIG(retract_layer_change) - && EXTRUDER_CONFIG(retract_length) > 0 - && EXTRUDER_CONFIG(travel_ramping_lift) - && EXTRUDER_CONFIG(travel_slope) > 0 && EXTRUDER_CONFIG(travel_slope) < 90 - }; + m_previous_layer_last_position = this->m_last_pos_defined ? + std::optional{to_3d(this->point_to_gcode(this->last_pos()), previous_layer_z)} : + std::nullopt; - const std::optional helix_gcode{ - do_helical_layer_change ? - this->get_helical_layer_change_gcode( - m_config.z_offset.value + previous_layer_z, - m_config.z_offset.value + print_z, - comment - ) : - std::nullopt - }; - gcode += ( - helix_gcode ? - *helix_gcode : - m_writer.travel_to_z(m_config.z_offset.value + print_z, comment) - ); + gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Travel); + this->m_layer_change_extruder_id = m_writer.extruder()->id(); // forget last wiping path as wiping after raising Z is pointless m_wipe.reset_path(); @@ -2963,19 +2967,31 @@ std::string GCodeGenerator::_extrude( std::string gcode; const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; - // go to first point of extrusion path - if (!m_last_pos_defined) { - const double z = this->m_last_layer_z + this->m_config.z_offset.value; - const std::string comment{"move to print after unknown position"}; - gcode += this->retract_and_wipe(); - gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment); - gcode += this->m_writer.get_travel_to_z_gcode(z, comment); - } else if ( m_last_pos != path.front().point) { - std::string comment = "move to first "; - comment += description; - comment += description_bridge; - comment += " point"; - gcode += this->travel_to(path.front().point, path_attr.role, comment); + if (!m_current_layer_first_position) { + // Make the first travel just one G1. + const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z + this->m_config.z_offset.value)); + const Vec3d gcode_point = to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z())); + this->set_last_pos(path.front().point); + this->writer().update_position(gcode_point); + gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), "move to first layer point"); + gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), "move to first layer point"); + m_current_layer_first_position = gcode_point; + } else { + // go to first point of extrusion path + if (!m_last_pos_defined) { + const double z = this->m_last_layer_z + this->m_config.z_offset.value; + const std::string comment{"move to print after unknown position"}; + gcode += this->retract_and_wipe(); + gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment); + gcode += this->m_writer.get_travel_to_z_gcode(z, comment); + } else if ( m_last_pos != path.front().point) { + std::string comment = "move to first "; + comment += description; + comment += description_bridge; + comment += " point"; + const std::string travel_gcode{this->travel_to(path.front().point, path_attr.role, comment)}; + gcode += travel_gcode; + } } // compensate retraction @@ -3217,9 +3233,13 @@ std::string GCodeGenerator::generate_travel_gcode( // use G1 because we rely on paths being straight (G0 may make round paths) gcode += this->m_writer.set_travel_acceleration(acceleration); - for (const Vec3crd& point : travel) { - gcode += this->m_writer.travel_to_xyz(to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z())), comment); + Vec3d previous_point{this->point_to_gcode(travel.front())}; + for (const Vec3crd& point : tcb::span{travel}.subspan(1)) { + const Vec3d gcode_point{this->point_to_gcode(point)}; + + gcode += this->m_writer.travel_to_xyz(previous_point, gcode_point, comment); this->set_last_pos(point.head<2>()); + previous_point = gcode_point; } if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) { @@ -3351,6 +3371,14 @@ std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, st const double retract_length = this->m_config.retract_length.get_at(extruder_id); bool can_be_flat{!needs_retraction || retract_length == 0}; const double initial_elevation = this->m_last_layer_z + this->m_config.z_offset.value; + + const double upper_limit = this->m_config.retract_lift_below.get_at(extruder_id); + const double lower_limit = this->m_config.retract_lift_above.get_at(extruder_id); + if ((lower_limit > 0 && initial_elevation < lower_limit) || + (upper_limit > 0 && initial_elevation > upper_limit)) { + can_be_flat = true; + } + const Points3 travel = ( can_be_flat ? GCode::Impl::Travels::generate_flat_travel(xy_path.points, initial_elevation) : diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 8890ec6125..92586e8d16 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -127,11 +127,25 @@ public: const Point& last_pos() const { return m_last_pos; } // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset. template - Vec2d point_to_gcode(const Eigen::MatrixBase &point) const { - static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode(): first parameter is not a 2D vector"); - return Vec2d(unscaled(point.x()), unscaled(point.y())) + m_origin - - m_config.extruder_offset.get_at(m_writer.extruder()->id()); + Eigen::Matrix point_to_gcode(const Eigen::MatrixBase &point) const { + static_assert( + Derived::IsVectorAtCompileTime, + "GCodeGenerator::point_to_gcode(): first parameter is not a vector" + ); + static_assert( + int(Derived::SizeAtCompileTime) == 2 || int(Derived::SizeAtCompileTime) == 3, + "GCodeGenerator::point_to_gcode(): first parameter is not a 2D or 3D vector" + ); + + if constexpr (Derived::SizeAtCompileTime == 2) { + return Vec2d(unscaled(point.x()), unscaled(point.y())) + m_origin + - m_config.extruder_offset.get_at(m_writer.extruder()->id()); + } else { + const Vec2d gcode_point_xy{this->point_to_gcode(point.template head<2>())}; + return to_3d(gcode_point_xy, unscaled(point.z())); + } } + // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution. template Vec2d point_to_gcode_quantized(const Eigen::MatrixBase &point) const { @@ -216,6 +230,9 @@ private: static ObjectsLayerToPrint collect_layers_to_print(const PrintObject &object); static std::vector> collect_layers_to_print(const Print &print); + /** @brief Generates ramping travel gcode for layer change. */ + std::string get_layer_change_gcode(const Vec3d& from, const Vec3d& to, const unsigned extruder_id); + LayerResult process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. @@ -253,15 +270,9 @@ private: bool last_pos_defined() const { return m_last_pos_defined; } void set_extruders(const std::vector &extruder_ids); std::string preamble(); - std::optional get_helical_layer_change_gcode( - const coordf_t previous_layer_z, - const coordf_t print_z, - const std::string& comment - ); std::string change_layer( coordf_t previous_layer_z, - coordf_t print_z, - const bool spiral_vase_enabled + coordf_t print_z ); std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); @@ -412,7 +423,10 @@ private: Point m_last_pos; bool m_last_pos_defined; - + std::optional m_previous_layer_last_position; + // This needs to be populated during the layer processing! + std::optional m_current_layer_first_position; + std::optional m_layer_change_extruder_id; std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; std::unique_ptr m_find_replace; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 87e319b3b9..e3a576e520 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -57,6 +57,7 @@ const std::vector GCodeProcessor::Reserved_Tags = { "HEIGHT:", "WIDTH:", "LAYER_CHANGE", + "LAYER_CHANGE_TRAVEL", "COLOR_CHANGE", "PAUSE_PRINT", "CUSTOM_GCODE", diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a055eaa347..f5658fcec8 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -192,6 +192,7 @@ namespace Slic3r { Height, Width, Layer_Change, + Layer_Change_Travel, Color_Change, Pause_Print, Custom_Code, diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 5231f97cd8..4983b59374 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -275,10 +275,8 @@ std::string GCodeWriter::set_speed(double F, const std::string_view comment, con return w.string(); } -std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment) +std::string GCodeWriter::get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const { - m_pos.head<2>() = point.head<2>(); - GCodeG1Formatter w; w.emit_xy(point); w.emit_f(this->config.travel_speed.value * 60.0); @@ -286,6 +284,12 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view return w.string(); } +std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment) +{ + m_pos.head<2>() = point.head<2>(); + return this->get_travel_to_xy_gcode(point, comment); +} + std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment) { assert(std::abs(point.x()) < 1200.); @@ -303,35 +307,49 @@ std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij return w.string(); } -std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string_view comment) +std::string GCodeWriter::travel_to_xyz(const Vec3d& from, const Vec3d &to, const std::string_view comment) { - if (std::abs(point.x() - m_pos.x()) < EPSILON && std::abs(point.y() - m_pos.y()) < EPSILON) { - return this->travel_to_z(point.z(), comment); - } else if (std::abs(point.z() - m_pos.z()) < EPSILON) { - return this->travel_to_xy(point.head<2>(), comment); + if (std::abs(to.x() - m_pos.x()) < EPSILON && std::abs(to.y() - m_pos.y()) < EPSILON) { + return this->travel_to_z(to.z(), comment); + } else if (std::abs(to.z() - m_pos.z()) < EPSILON) { + return this->travel_to_xy(to.head<2>(), comment); } else { - m_pos = point; - - GCodeG1Formatter w; - w.emit_xyz(point); - - Vec2f speed {this->config.travel_speed_z.value, this->config.travel_speed.value}; - w.emit_f(speed.norm() * 60.0); - w.emit_comment(this->config.gcode_comments, comment); - return w.string(); + m_pos = to; + return this->get_travel_to_xyz_gcode(from, to, comment); } } +std::string GCodeWriter::get_travel_to_xyz_gcode(const Vec3d &from, const Vec3d &to, const std::string_view comment) const { + GCodeG1Formatter w; + w.emit_xyz(to); + + const double distance_xy{(to.head<2>() - from.head<2>()).norm()}; + const double distnace_z{std::abs(to.z() - from.z())}; + const double time_z = distnace_z / this->config.travel_speed_z.value; + const double time_xy = distance_xy / this->config.travel_speed.value; + const double factor = time_z > 0 ? time_xy / time_z : 1; + if (factor < 1) { + w.emit_f((this->config.travel_speed.value * factor + (1 - factor) * this->config.travel_speed_z.value) * 60.0); + } else { + w.emit_f(this->config.travel_speed.value * 60.0); + } + + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} std::string GCodeWriter::travel_to_z(double z, const std::string_view comment) { - return std::abs(m_pos.z() - z) < EPSILON ? "" : this->get_travel_to_z_gcode(z, comment); + if (std::abs(m_pos.z() - z) < EPSILON) { + return ""; + } else { + m_pos.z() = z; + return this->get_travel_to_z_gcode(z, comment); + } } -std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view comment) +std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view comment) const { - m_pos.z() = z; - double speed = this->config.travel_speed_z.value; if (speed == 0.) speed = this->config.travel_speed.value; diff --git a/src/libslic3r/GCode/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp index d91e67728d..f857d07e4f 100644 --- a/src/libslic3r/GCode/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -66,10 +66,23 @@ public: std::string toolchange_prefix() const; std::string toolchange(unsigned int extruder_id); std::string set_speed(double F, const std::string_view comment = {}, const std::string_view cooling_marker = {}) const; + + std::string get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const; std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {}); std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {}); - std::string travel_to_xyz(const Vec3d &point, const std::string_view comment = {}); - std::string get_travel_to_z_gcode(double z, const std::string_view comment); + + /** + * @brief Return gcode with all three axis defined. Optionally adds feedrate. + * + * Feedrate is added the starting point "from" is specified. + * + * @param from Optional starting point of the travel. + * @param to Where to travel to. + * @param comment Description of the travel purpose. + */ + std::string get_travel_to_xyz_gcode(const Vec3d &from, const Vec3d &to, const std::string_view comment) const; + std::string travel_to_xyz(const Vec3d &from, const Vec3d &to, const std::string_view comment = {}); + std::string get_travel_to_z_gcode(double z, const std::string_view comment) const; std::string travel_to_z(double z, const std::string_view comment = {}); std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {}); std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment); diff --git a/src/libslic3r/GCode/LayerChanges.cpp b/src/libslic3r/GCode/LayerChanges.cpp deleted file mode 100644 index bb6656383a..0000000000 --- a/src/libslic3r/GCode/LayerChanges.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "LayerChanges.hpp" -#include "libslic3r/ClipperUtils.hpp" - -namespace Slic3r::GCode::Impl::LayerChanges { - -Polygon generate_regular_polygon( - const Point ¢roid, const Point &start_point, const unsigned points_count -) { - Points points; - points.reserve(points_count); - const double part_angle{2 * M_PI / points_count}; - for (unsigned i = 0; i < points_count; ++i) { - const double current_angle{i * part_angle}; - points.emplace_back(scaled(std::cos(current_angle)), scaled(std::sin(current_angle))); - } - - Polygon regular_polygon{points}; - const Vec2d current_vector{unscaled(regular_polygon.points.front())}; - const Vec2d expected_vector{unscaled(start_point) - unscaled(centroid)}; - - const double current_scale = current_vector.norm(); - const double expected_scale = expected_vector.norm(); - regular_polygon.scale(expected_scale / current_scale); - - regular_polygon.rotate(angle(current_vector, expected_vector)); - - regular_polygon.translate(centroid); - - return regular_polygon; -} - -Bed::Bed(const std::vector &shape, const double padding) - : inner_offset(get_inner_offset(shape, padding)), centroid(unscaled(inner_offset.centroid())) {} - -bool Bed::contains_within_padding(const Vec2d &point) const { - return inner_offset.contains(scaled(point)); -} - -Polygon Bed::get_inner_offset(const std::vector &shape, const double padding) { - Points shape_scaled; - shape_scaled.reserve(shape.size()); - using std::begin, std::end, std::back_inserter, std::transform; - transform(begin(shape), end(shape), back_inserter(shape_scaled), [](const Vec2d &point) { - return scaled(point); - }); - const Polygons inner_offset{shrink({Polygon{shape_scaled}}, scaled(padding))}; - if (inner_offset.empty()) { - return Polygon{}; - } - return inner_offset.front(); -} - -} // namespace Slic3r::GCode::Impl::LayerChanges diff --git a/src/libslic3r/GCode/LayerChanges.hpp b/src/libslic3r/GCode/LayerChanges.hpp deleted file mode 100644 index 5eb178e3f1..0000000000 --- a/src/libslic3r/GCode/LayerChanges.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file - * @brief Utility functions for layer change gcode generation. - */ - -#ifndef slic3r_GCode_LayerChanges_hpp_ -#define slic3r_GCode_LayerChanges_hpp_ - -#include "libslic3r/Point.hpp" -#include "libslic3r/Polygon.hpp" - -namespace Slic3r::GCode::Impl::LayerChanges { -/** - * Generates a regular polygon - all angles are the same (e.g. typical hexagon). - * - * @param centroid Central point. - * @param start_point The polygon point are ordered. This is the first point. - * @param points_count Amount of nodes of the polygon (e.g. 6 for haxagon). - * - * Distance between centroid and start point sets the scale of the polygon. - */ -Polygon generate_regular_polygon( - const Point ¢roid, const Point &start_point, const unsigned points_count -); - -/** - * @brief A representation of the bed shape with inner padding. - * - * Its purpose is to facilitate the bed boundary checking. - */ -class Bed -{ -private: - Polygon inner_offset; - static Polygon get_inner_offset(const std::vector &shape, const double padding); - -public: - /** - * Bed shape with inner padding. - */ - Bed(const std::vector &shape, const double padding); - - Vec2d centroid; - - /** - * Returns true if the point is within the bed shape including inner padding. - */ - bool contains_within_padding(const Vec2d &point) const; -}; -} // namespace Slic3r::GCode::Impl::LayerChanges - -#endif // slic3r_GCode_LayerChanges_hpp_ diff --git a/src/libslic3r/GCode/Travels.cpp b/src/libslic3r/GCode/Travels.cpp index c894debbf4..1253b88845 100644 --- a/src/libslic3r/GCode/Travels.cpp +++ b/src/libslic3r/GCode/Travels.cpp @@ -43,8 +43,8 @@ double ElevatedTravelFormula::operator()(const double distance_from_start) const Points3 generate_flat_travel(tcb::span xy_path, const float elevation) { Points3 result; - result.reserve(xy_path.size() - 1); - for (const Point &point : xy_path.subspan(1)) { + result.reserve(xy_path.size()); + for (const Point &point : xy_path) { result.emplace_back(point.x(), point.y(), scaled(elevation)); } return result; @@ -140,21 +140,6 @@ std::optional get_first_crossed_line_distance( return {}; } -std::optional get_obstacle_adjusted_slope_end( - const Lines &xy_path, - const std::optional> &previous_layer_distancer -) { - if (!previous_layer_distancer) { - return std::nullopt; - } - std::optional first_obstacle_distance = - get_first_crossed_line_distance(xy_path, *previous_layer_distancer); - if (!first_obstacle_distance) { - return std::nullopt; - } - return *first_obstacle_distance; -} - struct SmoothingParams { double blend_width{}; @@ -212,7 +197,7 @@ SmoothingParams get_smoothing_params( } ElevatedTravelParams get_elevated_traval_params( - const Polyline &xy_path, + const Polyline& xy_path, const FullPrintConfig &config, const unsigned extruder_id, const std::optional> &previous_layer_distancer @@ -236,7 +221,10 @@ ElevatedTravelParams get_elevated_traval_params( } std::optional obstacle_adjusted_slope_end{ - get_obstacle_adjusted_slope_end(xy_path.lines(), previous_layer_distancer)}; + previous_layer_distancer ? + get_first_crossed_line_distance(xy_path.lines(), *previous_layer_distancer) : + std::nullopt + }; if (obstacle_adjusted_slope_end && obstacle_adjusted_slope_end < elevation_params.slope_end) { elevation_params.slope_end = *obstacle_adjusted_slope_end; @@ -252,11 +240,6 @@ ElevatedTravelParams get_elevated_traval_params( return elevation_params; } -/** - * @brief Generate regulary spaced points on 1 axis. Includes both from and to. - * - * If count is 1, the point is in the middle of the range. - */ std::vector linspace(const double from, const double to, const unsigned count) { if (count == 0) { return {}; diff --git a/src/libslic3r/GCode/Travels.hpp b/src/libslic3r/GCode/Travels.hpp index 84020bb2cc..5ccf30ec2f 100644 --- a/src/libslic3r/GCode/Travels.hpp +++ b/src/libslic3r/GCode/Travels.hpp @@ -89,6 +89,20 @@ std::vector slice_xy_path( tcb::span xy_path, tcb::span sorted_distances ); +/** + * @brief Generate regulary spaced points on 1 axis. Includes both from and to. + * + * If count is 1, the point is in the middle of the range. + */ +std::vector linspace(const double from, const double to, const unsigned count); + +ElevatedTravelParams get_elevated_traval_params( + const Polyline& xy_path, + const FullPrintConfig &config, + const unsigned extruder_id, + const std::optional> &previous_layer_distancer = std::nullopt +); + /** * @brief Simply return the xy_path with z coord set to elevation. */ diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp index 3b2bb4ddd3..04c398e078 100644 --- a/src/libslic3r/GCode/WipeTowerIntegration.cpp +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -57,12 +57,24 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip || is_ramming || will_go_down); // don't dig into the print if (should_travel_to_tower) { + + const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos); gcode += gcodegen.retract_and_wipe(); gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - gcode += gcodegen.travel_to( - wipe_tower_point_to_object_point(gcodegen, start_pos), - ExtrusionRole::Mixed, - "Travel to a Wipe Tower"); + if (gcodegen.m_current_layer_first_position) { + gcode += gcodegen.travel_to( + xy_point, + ExtrusionRole::Mixed, + "Travel to a Wipe Tower"); + } else { + const Vec3crd point = to_3d(xy_point, scaled(z)); + const Vec3d gcode_point = to_3d(gcodegen.point_to_gcode(point.head<2>()), z); + gcodegen.set_last_pos(point.head<2>()); + gcodegen.writer().update_position(gcode_point); + gcode += gcodegen.writer().get_travel_to_xy_gcode(gcode_point.head<2>(), "move to first layer point"); + gcode += gcodegen.writer().get_travel_to_z_gcode(gcode_point.z(), "move to first layer point"); + gcodegen.m_current_layer_first_position = gcode_point; + } gcode += gcodegen.unretract(); } else { // When this is multiextruder printer without any ramming, we can just change diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt index 43d314a01c..23be4ddedc 100644 --- a/tests/fff_print/CMakeLists.txt +++ b/tests/fff_print/CMakeLists.txt @@ -14,7 +14,6 @@ add_executable(${_TEST_NAME}_tests test_gaps.cpp test_gcode.cpp test_gcode_travels.cpp - test_gcode_layer_changes.cpp test_gcodefindreplace.cpp test_gcodewriter.cpp test_layers.cpp diff --git a/tests/fff_print/test_gcode_layer_changes.cpp b/tests/fff_print/test_gcode_layer_changes.cpp deleted file mode 100644 index 6621d38cbe..0000000000 --- a/tests/fff_print/test_gcode_layer_changes.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include - -using namespace Slic3r; -using namespace Slic3r::GCode::Impl::LayerChanges; - -TEST_CASE("Generate regular polygon", "[GCode]") { - const unsigned points_count{32}; - const Point centroid{scaled(Vec2d{5, -2})}; - const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)}; - const Point oposite_point{centroid * 2}; - - REQUIRE(result.size() == 32); - CHECK(result[16].x() == Approx(oposite_point.x())); - CHECK(result[16].y() == Approx(oposite_point.y())); - - std::vector angles; - angles.reserve(points_count); - for (unsigned index = 0; index < points_count; index++) { - const unsigned previous_index{index == 0 ? points_count - 1 : index - 1}; - const unsigned next_index{index == points_count - 1 ? 0 : index + 1}; - - const Point previous_point = result.points[previous_index]; - const Point current_point = result.points[index]; - const Point next_point = result.points[next_index]; - - angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point})); - } - - std::vector expected; - angles.reserve(points_count); - std::generate_n(std::back_inserter(expected), points_count, [&](){ - return angles.front(); - }); - - CHECK_THAT(angles, Catch::Matchers::Approx(expected)); -} - -TEST_CASE("Square bed with padding", "[GCode]") { - const Bed bed{ - { - Vec2d{0, 0}, - Vec2d{100, 0}, - Vec2d{100, 100}, - Vec2d{0, 100} - }, - 10.0 - }; - - CHECK(bed.centroid.x() == 50); - CHECK(bed.centroid.y() == 50); - CHECK(bed.contains_within_padding(Vec2d{10, 10})); - CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10})); - -}