From ea9dfa9a8d3b73712eac4b907fdd9165e2c04005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0ach?= Date: Wed, 13 Mar 2024 17:16:58 +0100 Subject: [PATCH] Use path generation even on layer change --- src/libslic3r/GCode.cpp | 162 +++++++++++++----- src/libslic3r/GCode.hpp | 20 ++- .../GCode/AvoidCrossingPerimeters.cpp | 2 +- .../GCode/AvoidCrossingPerimeters.hpp | 9 +- src/libslic3r/GCode/WipeTowerIntegration.cpp | 6 +- tests/fff_print/test_gcode.cpp | 6 + 6 files changed, 154 insertions(+), 51 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4f7e49d72a..496059ac30 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1245,7 +1245,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Move to the origin position for the copy we're going to print. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer - m_avoid_crossing_perimeters.use_external_mp_once(); + m_avoid_crossing_perimeters.use_external_mp_once = true; file.write(this->retract_and_wipe()); file.write(m_label_objects.maybe_stop_instance()); const double last_z{this->writer().get_position().z()}; @@ -2120,18 +2120,61 @@ bool GCodeGenerator::line_distancer_is_required(const std::vector& return false; } -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>()) - }; +Polyline GCodeGenerator::get_layer_change_xy_path(const Vec3d &from, const Vec3d &to) { + bool could_be_wipe_disabled{false}; + const bool needs_retraction{true}; + + const Point saved_last_position{*this->last_position}; + const bool saved_use_external_mp{this->m_avoid_crossing_perimeters.use_external_mp_once}; + const Vec2d saved_origin{this->origin()}; + const Layer* saved_layer{this->layer()}; + + this->m_avoid_crossing_perimeters.use_external_mp_once = m_layer_change_used_external_mp; + if (this->m_layer_change_origin) { + this->m_origin = *this->m_layer_change_origin; + } + this->m_layer = m_layer_change_layer; + this->m_avoid_crossing_perimeters.init_layer(*this->m_layer); + + const Point start_point{this->gcode_to_point(from.head<2>())}; + const Point end_point{this->gcode_to_point(to.head<2>())}; + this->last_position = start_point; + + Polyline xy_path{ + this->generate_travel_xy_path(start_point, end_point, needs_retraction, could_be_wipe_disabled)}; + std::vector gcode_xy_path; + gcode_xy_path.reserve(xy_path.size()); + for (const Point &point : xy_path.points) { + gcode_xy_path.push_back(this->point_to_gcode(point)); + } + + this->last_position = saved_last_position; + this->m_avoid_crossing_perimeters.use_external_mp_once = saved_use_external_mp; + this->m_origin = saved_origin; + this->m_layer = saved_layer; + + Polyline result; + for (const Vec2d& point : gcode_xy_path) { + result.points.push_back(gcode_to_point(point)); + } + + return result; +} + +GCode::Impl::Travels::ElevatedTravelParams get_ramping_layer_change_params( + const Vec3d &from, + const Vec3d &to, + const Polyline &xy_path, + const FullPrintConfig &config, + const unsigned extruder_id, + const GCode::TravelObstacleTracker &obstacle_tracker +) { using namespace GCode::Impl::Travels; ElevatedTravelParams elevation_params{ - get_elevated_traval_params(xy_path, this->m_config, extruder_id, this->m_travel_obstacle_tracker)}; + get_elevated_traval_params(xy_path, config, extruder_id, obstacle_tracker)}; - 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); @@ -2145,6 +2188,26 @@ std::string GCodeGenerator::get_layer_change_gcode(const Vec3d& from, const Vec3 elevation_params.slope_end = path_length; } + return elevation_params; +} + +std::string GCodeGenerator::get_ramping_layer_change_gcode(const Vec3d &from, const Vec3d &to, const unsigned extruder_id) { + const Polyline xy_path{this->get_layer_change_xy_path(from, to)}; + + const GCode::Impl::Travels::ElevatedTravelParams elevation_params{ + get_ramping_layer_change_params( + from, to, xy_path, m_config, extruder_id, m_travel_obstacle_tracker + )}; + return this->generate_ramping_layer_change_gcode(xy_path, from.z(), elevation_params); +} + +std::string GCodeGenerator::generate_ramping_layer_change_gcode( + const Polyline &xy_path, + const double initial_elevation, + const GCode::Impl::Travels::ElevatedTravelParams &elevation_params +) const { + using namespace GCode::Impl::Travels; + 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, @@ -2158,9 +2221,10 @@ std::string GCodeGenerator::get_layer_change_gcode(const Vec3d& from, const Vec3 std::string travel_gcode; Vec3d previous_point{this->point_to_gcode(travel.front())}; - for (const Vec3crd& point : travel) { + for (const Vec3crd &point : travel) { 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"); + travel_gcode += this->m_writer + .get_travel_to_xyz_gcode(previous_point, gcode_point, "layer change"); previous_point = gcode_point; } return travel_gcode; @@ -2448,7 +2512,9 @@ LayerResult GCodeGenerator::process_layer( if (first_layer) { layer_change_gcode = ""; // Explicit for readability. } else 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); + const Vec3d &from{*m_previous_layer_last_position}; + const Vec3d &to{*m_current_layer_first_position}; + layer_change_gcode = this->get_ramping_layer_change_gcode(from, to, *m_layer_change_extruder_id); } else { layer_change_gcode = this->writer().get_travel_to_z_gcode(print_z, "simple layer change"); } @@ -2479,7 +2545,7 @@ LayerResult GCodeGenerator::process_layer( const std::size_t end{end_tag_start + retraction_end_tag.size()}; gcode.replace(start, end - start, ""); - layer_change_gcode = this->get_layer_change_gcode(*m_previous_layer_last_position_before_wipe, *m_current_layer_first_position, *m_layer_change_extruder_id); + layer_change_gcode = this->get_ramping_layer_change_gcode(*m_previous_layer_last_position_before_wipe, *m_current_layer_first_position, *m_layer_change_extruder_id); removed_retraction = true; } @@ -2540,11 +2606,13 @@ void GCodeGenerator::process_layer_single_object( m_avoid_crossing_perimeters.init_layer(*m_layer); // When starting a new object, use the external motion planner for the first travel move. const Point &offset = print_object.instances()[print_instance.instance_id].shift; - - const bool updated{m_label_objects.update(&print_instance.print_object.instances()[print_instance.instance_id])}; - if (updated) - m_avoid_crossing_perimeters.use_external_mp_once(); + GCode::PrintObjectInstance next_instance = {&print_object, int(print_instance.instance_id)}; + if (m_current_instance != next_instance) { + m_avoid_crossing_perimeters.use_external_mp_once = true; + } + m_current_instance = next_instance; this->set_origin(unscale(offset)); + m_label_objects.update(&print_instance.print_object.instances()[print_instance.instance_id]); } }; @@ -3096,37 +3164,53 @@ void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...) va_end(args); } -std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const double from_z, const std::function& insert_gcode) { +std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const double from_z, const ExtrusionRole role, const std::function& insert_gcode) { std::string gcode; const Vec3d gcode_point = to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z())); - double lift{ - EXTRUDER_CONFIG(travel_ramping_lift) ? EXTRUDER_CONFIG(travel_max_lift) : - EXTRUDER_CONFIG(retract_lift)}; - const double upper_limit = EXTRUDER_CONFIG(retract_lift_below); - const double lower_limit = EXTRUDER_CONFIG(retract_lift_above); - if ((lower_limit > 0 && gcode_point.z() < lower_limit) || - (upper_limit > 0 && gcode_point.z() > upper_limit)) { - lift = 0.0; - } + if (!EXTRUDER_CONFIG(travel_ramping_lift) && this->last_position) { + Vec3d writer_position{this->writer().get_position()}; + writer_position.z() = 0.0; // Endofrce z generation! + this->writer().update_position(writer_position); + gcode = this->travel_to( + *this->last_position, point.head<2>(), role, "travel to first layer point", insert_gcode + ); + } else { + this->m_layer_change_used_external_mp = this->m_avoid_crossing_perimeters.use_external_mp_once; + this->m_layer_change_layer = this->layer(); + this->m_layer_change_origin = this->origin(); - if (EXTRUDER_CONFIG(retract_length) > 0 && (!this->last_position || (!EXTRUDER_CONFIG(travel_ramping_lift)))) { - if (!this->last_position || EXTRUDER_CONFIG(retract_before_travel) < (this->point_to_gcode(*this->last_position) - gcode_point.head<2>()).norm()) { - gcode += this->writer().retract(); - gcode += this->writer().get_travel_to_z_gcode(from_z + lift, "lift"); + double lift{ + EXTRUDER_CONFIG(travel_ramping_lift) ? EXTRUDER_CONFIG(travel_max_lift) : + EXTRUDER_CONFIG(retract_lift)}; + const double upper_limit = EXTRUDER_CONFIG(retract_lift_below); + const double lower_limit = EXTRUDER_CONFIG(retract_lift_above); + if ((lower_limit > 0 && gcode_point.z() < lower_limit) || + (upper_limit > 0 && gcode_point.z() > upper_limit)) { + lift = 0.0; } + + if (EXTRUDER_CONFIG(retract_length) > 0 && !this->last_position) { + if (!this->last_position || EXTRUDER_CONFIG(retract_before_travel) < (this->point_to_gcode(*this->last_position) - gcode_point.head<2>()).norm()) { + gcode += this->writer().retract(); + gcode += this->writer().get_travel_to_z_gcode(from_z + lift, "lift"); + } + } + + const std::string comment{"move to first layer point"}; + + gcode += insert_gcode(); + gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment); + gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment); + + this->m_avoid_crossing_perimeters.reset_once_modifiers(); + this->last_position = point.head<2>(); + this->writer().update_position(gcode_point); } - this->last_position = point.head<2>(); - this->writer().update_position(gcode_point); - - gcode += insert_gcode(); - - std::string comment{"move to first layer point"}; - gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment); - gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment); m_current_layer_first_position = gcode_point; + return gcode; } @@ -3160,7 +3244,7 @@ std::string GCodeGenerator::_extrude( if (!m_current_layer_first_position) { const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z)); - gcode += this->travel_to_first_position(point, unscaled(point.z()), [&](){ + gcode += this->travel_to_first_position(point, unscaled(point.z()), path_attr.role, [&](){ return m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer); }); } else { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 5e6df97590..a9fa197647 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -110,7 +110,7 @@ struct PrintObjectInstance int instance_idx = -1; bool operator==(const PrintObjectInstance &other) const {return print_object == other.print_object && instance_idx == other.instance_idx; } - bool operator!=(const PrintObjectInstance &other) const { return *this == other; } + bool operator!=(const PrintObjectInstance &other) const { return !(*this == other); } }; } // namespace GCode @@ -226,8 +226,16 @@ private: static ObjectsLayerToPrint collect_layers_to_print(const PrintObject &object); static std::vector> collect_layers_to_print(const Print &print); + Polyline get_layer_change_xy_path(const Vec3d &from, const Vec3d &to); + + std::string get_ramping_layer_change_gcode(const Vec3d &from, const Vec3d &to, const unsigned extruder_id); + /** @brief Generates ramping travel gcode for layer change. */ - std::string get_layer_change_gcode(const Vec3d& from, const Vec3d& to, const unsigned extruder_id); + std::string generate_ramping_layer_change_gcode( + const Polyline &xy_path, + const double initial_elevation, + const GCode::Impl::Travels::ElevatedTravelParams &elevation_params + ) const; LayerResult process_layer( const Print &print, @@ -336,7 +344,7 @@ private: const std::function& insert_gcode ); - std::string travel_to_first_position(const Vec3crd& point, const double from_z, const std::function& insert_gcode); + std::string travel_to_first_position(const Vec3crd& point, const double from_z, const ExtrusionRole role, const std::function& insert_gcode); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); @@ -431,6 +439,9 @@ private: // This needs to be populated during the layer processing! std::optional m_current_layer_first_position; std::optional m_layer_change_extruder_id; + bool m_layer_change_used_external_mp{false}; + const Layer* m_layer_change_layer{nullptr}; + std::optional m_layer_change_origin; bool m_already_unretracted{false}; std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; @@ -446,6 +457,9 @@ private: bool m_second_layer_things_done; // G-code that is due to be written before the next extrusion std::string m_pending_pre_extrusion_gcode; + // Pointer to currently exporting PrintObject and instance index. + GCode::PrintObjectInstance m_current_instance; + bool m_silent_time_estimator_enabled; // Processor diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 0b678ae4f7..062362d5ec 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1175,7 +1175,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, cons { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. - bool use_external = m_use_external_mp || m_use_external_mp_once; + bool use_external = m_use_external_mp || use_external_mp_once; Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0); const Point start = *gcodegen.last_position + scaled_origin; const Point end = point + scaled_origin; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index 932f0b5efb..2f4fac0110 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -21,11 +21,10 @@ class AvoidCrossingPerimeters public: // Routing around the objects vs. inside a single object. void use_external_mp(bool use = true) { m_use_external_mp = use; }; - void use_external_mp_once() { m_use_external_mp_once = true; } - bool used_external_mp_once() { return m_use_external_mp_once; } + bool used_external_mp_once() { return use_external_mp_once; } void disable_once() { m_disabled_once = true; } bool disabled_once() const { return m_disabled_once; } - void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; } + void reset_once_modifiers() { use_external_mp_once = false; m_disabled_once = false; } void init_layer(const Layer &layer); @@ -54,10 +53,10 @@ public: } }; + // just for the next travel move + bool use_external_mp_once { false }; private: bool m_use_external_mp { false }; - // just for the next travel move - bool m_use_external_mp_once { false }; // this flag disables avoid_crossing_perimeters just for the next travel move // we enable it by default for the first travel move in print bool m_disabled_once { true }; diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp index a7d6d0ec70..d0237c39e3 100644 --- a/src/libslic3r/GCode/WipeTowerIntegration.cpp +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -60,7 +60,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos); gcode += gcodegen.m_label_objects.maybe_stop_instance(); gcode += gcodegen.retract_and_wipe(); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; const std::string comment{"Travel to a Wipe Tower"}; if (gcodegen.m_current_layer_first_position) { if (gcodegen.last_position) { @@ -73,7 +73,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip } } else { const Vec3crd point = to_3d(xy_point, scaled(z)); - gcode += gcodegen.travel_to_first_position(point, current_z, [](){return "";}); + gcode += gcodegen.travel_to_first_position(point, current_z, ExtrusionRole::Mixed, [](){return "";}); } gcode += gcodegen.unretract(); } else { @@ -140,7 +140,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip } // Let the planner know we are traveling between objects. - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; return gcode; } diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index fc693b29d4..78e44c5861 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -145,6 +145,12 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") { Model model; Test::init_print({TestMesh::cube_20x20x20}, print, model, config, false, 2); std::string gcode = Test::gcode(print); + + if constexpr (debug_files) { + std::ofstream gcode_file{"sequential_print.gcode"}; + gcode_file << gcode; + } + parser.parse_buffer(gcode, [&] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) { INFO("Unexpected E argument"); CHECK(!line.has_e());