diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 91bab8e93d..3e0e371886 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -21,6 +21,7 @@ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #include "Config.hpp" +#include "Geometry/Circle.hpp" #include "libslic3r.h" #include "GCode/ExtrusionProcessor.hpp" #include "I18N.hpp" @@ -2089,6 +2090,21 @@ namespace Skirt { } // namespace Skirt +bool GCodeGenerator::line_distancer_is_required(const std::vector& extruder_ids) { + for (const unsigned id : extruder_ids) { + const double travel_slope{this->m_config.travel_slope.get_at(id)}; + if ( + this->m_config.travel_lift_before_obstacle.get_at(id) + && this->m_config.travel_max_lift.get_at(id) > 0 + && travel_slope > 0 + && travel_slope < 90 + ) { + return true; + } + } + return false; +} + // 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. @@ -2189,6 +2205,9 @@ LayerResult GCodeGenerator::process_layer( } 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_expolygons_distancer(m_layer->lower_layer->lslices); + } m_object_layer_over_raft = false; if (! print.config().layer_gcode.value.empty()) { DynamicConfig config; @@ -3382,11 +3401,28 @@ 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; +} + ElevatedTravelParams get_elevated_traval_params( + const Lines& xy_path, const FullPrintConfig& config, - const unsigned extruder_id -) -{ + const unsigned extruder_id, + const std::optional>& previous_layer_distancer +) { ElevatedTravelParams elevation_params{}; if (!config.travel_ramping_lift.get_at(extruder_id)) { elevation_params.slope_end = 0; @@ -3404,6 +3440,15 @@ ElevatedTravelParams get_elevated_traval_params( elevation_params.slope_end = elevation_params.lift_height / std::tan(slope_rad); } + std::optional obstacle_adjusted_slope_end{get_obstacle_adjusted_slope_end( + xy_path, + previous_layer_distancer + )}; + + if (obstacle_adjusted_slope_end && obstacle_adjusted_slope_end < elevation_params.slope_end) { + elevation_params.slope_end = *obstacle_adjusted_slope_end; + } + return elevation_params; } @@ -3411,7 +3456,8 @@ Points3 generate_travel_to_extrusion( const Polyline& xy_path, const FullPrintConfig& config, const unsigned extruder_id, - const double initial_elevation + const double initial_elevation, + const std::optional>& previous_layer_distancer ) { const double upper_limit = config.retract_lift_below.get_at(extruder_id); const double lower_limit = config.retract_lift_above.get_at(extruder_id); @@ -3423,8 +3469,10 @@ Points3 generate_travel_to_extrusion( } ElevatedTravelParams elevation_params{get_elevated_traval_params( + xy_path.lines(), config, - extruder_id + extruder_id, + previous_layer_distancer )}; const std::vector ensure_points_at_distances{elevation_params.slope_end}; @@ -3600,7 +3648,8 @@ std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, st xy_path, this->m_config, extruder_id, - initial_elevation + initial_elevation, + this->m_previous_layer_distancer ) ); @@ -3747,7 +3796,6 @@ Point GCodeGenerator::gcode_to_point(const Vec2d &point) const // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet. pt += m_config.extruder_offset.get_at(extruder->id()); return scaled(pt); - } } // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 71c5c1ff13..bf47716f82 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -135,6 +135,31 @@ Points3 generate_elevated_travel( const std::function& elevation ); +/** + * @brief Takes a list o polygons and builds a AABBTree over all unscaled lines. + * + * @param polygons A list of polygons. + * @return AABB Tree over all lines of the polygons. + * + * Unscales the lines in the process! + */ +AABBTreeLines::LinesDistancer get_expolygons_distancer(const ExPolygons& polygons); + +/** + * @brief Given a AABB tree over lines find intersection with xy_path closest to the xy_path start. + * + * @param xy_path A path in 2D. + * @param distancer AABB Tree over lines. + * @return Distance to the first intersection if there is one. + * + * **Ignores intersection with xy_path starting point.** + */ +std::optional get_first_crossed_line_distance( + tcb::span xy_path, + const AABBTreeLines::LinesDistancer& distancer +); + + /** * Generates a regular polygon - all angles are the same (e.g. typical hexagon). * @@ -169,6 +194,7 @@ class Bed { bool contains_within_padding(const Vec2d& point) const; }; } + class GCodeGenerator { public: @@ -405,6 +431,7 @@ private: std::string retract_and_wipe(bool toolchange = false); std::string unretract() { return m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z); + bool line_distancer_is_required(const std::vector& extruder_ids); // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; @@ -473,6 +500,7 @@ private: // In non-sequential mode, all its copies will be printed. const Layer* m_layer; // m_layer is an object layer and it is being printed over raft surface. + std::optional> m_previous_layer_distancer; bool m_object_layer_over_raft; double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index ac1543f21c..6beaf83453 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -14,6 +14,7 @@ namespace Slic3r { + class SpiralVase { public: SpiralVase(const PrintConfig &config) : m_config(config) diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index f8d33e1461..7cccfe54f8 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -140,6 +140,55 @@ TEST_CASE("Generate elevated travel", "[GCode]") { }); } +TEST_CASE("Get first crossed line distance", "[GCode]") { + // A 2x2 square at 0, 0, with 1x1 square hole in its center. + ExPolygon square_with_hole{ + { + scaled(Vec2f{-1, -1}), + scaled(Vec2f{1, -1}), + scaled(Vec2f{1, 1}), + scaled(Vec2f{-1, 1}) + }, + { + scaled(Vec2f{-0.5, -0.5}), + scaled(Vec2f{0.5, -0.5}), + scaled(Vec2f{0.5, 0.5}), + scaled(Vec2f{-0.5, 0.5}) + } + }; + // A 2x2 square above the previous square at (0, 3). + ExPolygon square_above{ + { + scaled(Vec2f{-1, 2}), + scaled(Vec2f{1, 2}), + scaled(Vec2f{1, 4}), + scaled(Vec2f{-1, 4}) + } + }; + + // Bottom-up travel intersecting the squares. + Lines travel{Polyline{ + scaled(Vec2f{0, -2}), + scaled(Vec2f{0, -0.7}), + scaled(Vec2f{0, 0}), + scaled(Vec2f{0, 1}), + scaled(Vec2f{0, 1.3}), + scaled(Vec2f{0, 2.4}), + scaled(Vec2f{0, 4.5}), + scaled(Vec2f{0, 5}), + }.lines()}; + + // Try different cases by skipping lines in the travel. + AABBTreeLines::LinesDistancer distancer = get_expolygons_distancer({square_with_hole, square_above}); + CHECK(*get_first_crossed_line_distance(travel, distancer) == Approx(1)); + CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(1), distancer) == Approx(0.2)); + CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(2), distancer) == Approx(0.5)); + CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(3), distancer) == Approx(1.0)); //Edge case + CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(4), distancer) == Approx(0.7)); + CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(5), distancer) == Approx(1.6)); + CHECK_FALSE(get_first_crossed_line_distance(tcb::span{travel}.subspan(6), distancer)); +} + TEST_CASE("Generate regular polygon", "[GCode]") { const unsigned points_count{32}; const Point centroid{scaled(Vec2d{5, -2})};