Browse Source

Implement lift before obstacles that take into account already extruder extrusions on the current layer.

Ignore the first intersection of the travel path with the object from which the travel starts.

Created a new class TravelObstacleTracker, for wrapping all data structures related to lift before obstacles.
Lukáš Hejl 1 year ago
parent
commit
da57489874

+ 1 - 1
src/libslic3r/ExPolygon.hpp

@@ -446,7 +446,7 @@ inline void expolygons_rotate(ExPolygons &expolys, double angle)
         expoly.rotate(angle);
 }
 
-inline bool expolygons_contain(ExPolygons &expolys, const Point &pt, bool border_result = true)
+inline bool expolygons_contain(const ExPolygons &expolys, const Point &pt, bool border_result = true)
 {
     for (const ExPolygon &expoly : expolys)
         if (expoly.contains(pt, border_result))

+ 10 - 28
src/libslic3r/GCode.cpp

@@ -2060,26 +2060,6 @@ bool GCodeGenerator::line_distancer_is_required(const std::vector<unsigned int>&
     return false;
 }
 
-namespace GCode::Impl {
-AABBTreeLines::LinesDistancer<Linef> get_previous_layer_distancer(
-    const GCodeGenerator::ObjectsLayerToPrint& objects_to_print,
-    const ExPolygons& slices
-) {
-    std::vector<Linef> lines;
-    for (const GCodeGenerator::ObjectLayerToPrint &object_to_print : objects_to_print) {
-        for (const PrintInstance& instance: object_to_print.object()->instances()) {
-            for (const ExPolygon& polygon : slices) {
-                for (const Line& line : polygon.lines()) {
-                    lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}));
-                }
-            }
-
-        }
-    }
-    return AABBTreeLines::LinesDistancer{std::move(lines)};
-}
-}
-
 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>()),
@@ -2089,7 +2069,7 @@ std::string GCodeGenerator::get_layer_change_gcode(const Vec3d& from, const Vec3
     using namespace GCode::Impl::Travels;
 
     ElevatedTravelParams elevation_params{
-        get_elevated_traval_params(xy_path, this->m_config, extruder_id)};
+        get_elevated_traval_params(xy_path, this->m_config, extruder_id, this->m_travel_obstacle_tracker)};
 
     const double initial_elevation = from.z();
     const double z_change = to.z() - from.z();
@@ -2226,9 +2206,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_previous_layer_distancer(layers, layer.lower_layer->lslices);
-    }
+    if (this->line_distancer_is_required(layer_tools.extruders) && this->m_layer != nullptr && this->m_layer->lower_layer != nullptr)
+        m_travel_obstacle_tracker.init_layer(layer, layers);
+
     m_object_layer_over_raft = false;
     if (! print.config().layer_gcode.value.empty()) {
         DynamicConfig config;
@@ -2566,9 +2546,11 @@ void GCodeGenerator::process_layer_single_object(
                                 init_layer_delayed();
                                 m_config.apply(region.config());
                             }
-                            for (const ExtrusionEntity *ee : *eec)
+                            for (const ExtrusionEntity *ee : *eec) {
                                 // Don't reorder, don't flip.
-                                gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, comment_perimeter, -1.);
+                                gcode += this->extrude_entity({*ee, false}, smooth_path_cache, comment_perimeter, -1.);
+                                m_travel_obstacle_tracker.mark_extruded(ee, print_instance.object_layer_to_print_id, print_instance.instance_id);
+                            }
                         }
                     }
                 };
@@ -3383,10 +3365,10 @@ std::string GCodeGenerator::travel_to(
         GCode::Impl::Travels::generate_flat_travel(xy_path.points, initial_elevation) :
         GCode::Impl::Travels::generate_travel_to_extrusion(
             xy_path,
-            this->m_config,
+            m_config,
             extruder_id,
             initial_elevation,
-            this->m_previous_layer_distancer,
+            m_travel_obstacle_tracker,
             scaled(m_origin)
         )
     );

+ 3 - 2
src/libslic3r/GCode.hpp

@@ -38,8 +38,9 @@
 #include "GCode/WipeTowerIntegration.hpp"
 #include "GCode/SeamPlacer.hpp"
 #include "GCode/GCodeProcessor.hpp"
-#include "EdgeGrid.hpp"
 #include "GCode/ThumbnailData.hpp"
+#include "GCode/Travels.hpp"
+#include "EdgeGrid.hpp"
 #include "tcbspan/span.hpp"
 
 #include <memory>
@@ -405,6 +406,7 @@ private:
     AvoidCrossingPerimeters             m_avoid_crossing_perimeters;
     JPSPathFinder                       m_avoid_crossing_curled_overhangs;
     RetractWhenCrossingPerimeters       m_retract_when_crossing_perimeters;
+    GCode::TravelObstacleTracker        m_travel_obstacle_tracker;
     bool                                m_enable_loop_clipping;
     // If enabled, the G-code generator will put following comments at the ends
     // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END
@@ -424,7 +426,6 @@ 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<AABBTreeLines::LinesDistancer<Linef>> m_previous_layer_distancer;
     bool                                m_object_layer_over_raft;
     double                              m_volumetric_speed;
     // Support for the extrusion role markers. Which marker is active?

+ 166 - 20
src/libslic3r/GCode/Travels.cpp

@@ -1,6 +1,104 @@
 #include "Travels.hpp"
 
 #include "libslic3r/PrintConfig.hpp"
+#include "libslic3r/Layer.hpp"
+#include "libslic3r/Print.hpp"
+
+#include "../GCode.hpp"
+
+namespace Slic3r::GCode {
+
+static Lines extrusion_entity_to_lines(const ExtrusionEntity &e_entity)
+{
+    if (const auto *path = dynamic_cast<const ExtrusionPath *>(&e_entity)) {
+        return to_lines(path->as_polyline());
+    } else if (const auto *multipath = dynamic_cast<const ExtrusionMultiPath *>(&e_entity)) {
+        return to_lines(multipath->as_polyline());
+    } else if (const auto *loop = dynamic_cast<const ExtrusionLoop *>(&e_entity)) {
+        return to_lines(loop->polygon());
+    } else {
+        throw Slic3r::InvalidArgument("Invalid argument supplied to TODO()");
+    }
+
+    return {};
+}
+
+AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> get_previous_layer_distancer(
+    const GCodeGenerator::ObjectsLayerToPrint &objects_to_print, const ExPolygons &slices
+) {
+    std::vector<ObjectOrExtrusionLinef> lines;
+    for (const GCodeGenerator::ObjectLayerToPrint &object_to_print : objects_to_print) {
+        if (const PrintObject *object = object_to_print.object(); object) {
+            const size_t object_layer_idx = &object_to_print - &objects_to_print.front();
+            for (const PrintInstance &instance : object->instances()) {
+                const size_t instance_idx = &instance - &object->instances().front();
+                for (const ExPolygon &polygon : slices)
+                    for (const Line &line : polygon.lines())
+                        lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}), object_layer_idx, instance_idx);
+            }
+        }
+    }
+
+    return AABBTreeLines::LinesDistancer{std::move(lines)};
+}
+
+std::pair<AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef>, size_t> get_current_layer_distancer(const ObjectsLayerToPrint &objects_to_print)
+{
+    std::vector<ObjectOrExtrusionLinef> lines;
+    size_t                              extrusion_entity_cnt = 0;
+    for (const ObjectLayerToPrint &object_to_print : objects_to_print) {
+        const size_t object_layer_idx = &object_to_print - &objects_to_print.front();
+        if (const Layer *layer = object_to_print.object_layer; layer) {
+            for (const PrintInstance &instance : layer->object()->instances()) {
+                const size_t instance_idx = &instance - &layer->object()->instances().front();
+                for (const LayerSlice &lslice : layer->lslices_ex) {
+                    for (const LayerIsland &island : lslice.islands) {
+                        const LayerRegion &layerm = *layer->get_region(island.perimeters.region());
+                        for (uint32_t perimeter_id : island.perimeters) {
+                            assert(dynamic_cast<const ExtrusionEntityCollection *>(layerm.perimeters().entities[perimeter_id]));
+                            const auto *eec = static_cast<const ExtrusionEntityCollection *>(layerm.perimeters().entities[perimeter_id]);
+                            for (const ExtrusionEntity *ee : *eec) {
+                                if (ee->role().is_external_perimeter()) {
+                                    for (const Line &line : extrusion_entity_to_lines(*ee))
+                                        lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}), object_layer_idx, instance_idx, ee);
+                                }
+
+                                ++extrusion_entity_cnt;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return {AABBTreeLines::LinesDistancer{std::move(lines)}, extrusion_entity_cnt};
+}
+
+void TravelObstacleTracker::init_layer(const Layer &layer, const ObjectsLayerToPrint &objects_to_print)
+{
+    size_t extrusion_entity_cnt = 0;
+    m_extruded_extrusion.clear();
+
+    m_objects_to_print         = objects_to_print;
+    m_previous_layer_distancer = get_previous_layer_distancer(m_objects_to_print, layer.lower_layer->lslices);
+
+    std::tie(m_current_layer_distancer, extrusion_entity_cnt) = get_current_layer_distancer(m_objects_to_print);
+    m_extruded_extrusion.reserve(extrusion_entity_cnt);
+}
+
+void TravelObstacleTracker::mark_extruded(const ExtrusionEntity *extrusion_entity, size_t object_layer_idx, size_t instance_idx)
+{
+    if (extrusion_entity->role().is_external_perimeter())
+        this->m_extruded_extrusion.insert({int(object_layer_idx), int(instance_idx), extrusion_entity});
+}
+
+bool TravelObstacleTracker::is_extruded(const ObjectOrExtrusionLinef &line) const
+{
+    return m_extruded_extrusion.find({line.object_layer_idx, line.instance_idx, line.extrusion_entity}) != m_extruded_extrusion.end();
+}
+
+} // namespace Slic3r::GCode
 
 namespace Slic3r::GCode::Impl::Travels {
 
@@ -114,33 +212,86 @@ Points3 generate_elevated_travel(
     return result;
 }
 
+struct Intersection
+{
+    int  object_layer_idx = -1;
+    int  instance_idx     = -1;
+    bool is_inside        = false;
+
+    bool is_print_instance_equal(const ObjectOrExtrusionLinef &print_istance) {
+        return this->object_layer_idx == print_istance.object_layer_idx && this->instance_idx == print_istance.instance_idx;
+    }
+};
+
 double get_first_crossed_line_distance(
-    tcb::span<const Line> xy_path, const AABBTreeLines::LinesDistancer<Linef> &distancer
+    tcb::span<const Line> xy_path,
+    const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &distancer,
+    const ObjectsLayerToPrint &objects_to_print,
+    const std::function<bool(const ObjectOrExtrusionLinef &)> &predicate,
+    const bool ignore_starting_object_intersection
 ) {
     assert(!xy_path.empty());
     if (xy_path.empty())
         return std::numeric_limits<double>::max();
 
+    const Point path_first_point = xy_path.front().a;
     double traversed_distance = 0;
+    bool skip_intersection = ignore_starting_object_intersection;
+    Intersection first_intersection;
+
     for (const Line &line : xy_path) {
-        const Linef unscaled_line = {unscaled(line.a), unscaled(line.b)};
-        auto intersections = distancer.intersections_with_line<true>(unscaled_line);
-        if (!intersections.empty()) {
-            const Vec2d intersection = intersections.front().first;
-            const double distance = traversed_distance + (unscaled_line.a - intersection).norm();
-            if (distance > EPSILON) {
-                return distance;
-            } else if (intersections.size() >= 2) { // Edge case
-                const Vec2d second_intersection = intersections[1].first;
-                return traversed_distance + (unscaled_line.a - second_intersection).norm();
+        const ObjectOrExtrusionLinef                    unscaled_line = {unscaled(line.a), unscaled(line.b)};
+        const std::vector<std::pair<Vec2d, size_t>>     intersections = distancer.intersections_with_line<true>(unscaled_line);
+
+        if (intersections.empty())
+            continue;
+
+        if (!objects_to_print.empty() && ignore_starting_object_intersection && first_intersection.object_layer_idx == -1) {
+            const ObjectOrExtrusionLinef &intersection_line = distancer.get_line(intersections.front().second);
+            const Point shift = objects_to_print[intersection_line.object_layer_idx].layer()->object()->instances()[intersection_line.instance_idx].shift;
+            const Point shifted_first_point = path_first_point - shift;
+            const bool contain_first_point = expolygons_contain(objects_to_print[intersection_line.object_layer_idx].layer()->lslices, shifted_first_point);
+
+            first_intersection = {intersection_line.object_layer_idx, intersection_line.instance_idx, contain_first_point};
+        }
+
+        for (const auto &intersection : intersections) {
+            const ObjectOrExtrusionLinef &intersection_line = distancer.get_line(intersection.second);
+            const double distance = traversed_distance + (unscaled_line.a - intersection.first).norm();
+            if (distance <= EPSILON)
+                continue;
+
+            // There is only one external border for each object, so when we cross this border,
+            // we definitely know that we are outside the object.
+            if (skip_intersection && first_intersection.is_print_instance_equal(intersection_line) && first_intersection.is_inside) {
+                skip_intersection = false;
+                continue;
             }
+
+            if (!predicate(intersection_line))
+                continue;
+
+            return distance;
         }
+
         traversed_distance += (unscaled_line.a - unscaled_line.b).norm();
     }
 
     return std::numeric_limits<double>::max();
 }
 
+double get_obstacle_adjusted_slope_end(const Lines &xy_path, const GCode::TravelObstacleTracker &obstacle_tracker) {
+    const double previous_layer_crossed_line = get_first_crossed_line_distance(
+        xy_path, obstacle_tracker.previous_layer_distancer(), obstacle_tracker.objects_to_print()
+    );
+    const double current_layer_crossed_line = get_first_crossed_line_distance(
+        xy_path, obstacle_tracker.current_layer_distancer(), obstacle_tracker.objects_to_print(),
+        [&obstacle_tracker](const ObjectOrExtrusionLinef &line) { return obstacle_tracker.is_extruded(line); }
+    );
+
+    return std::min(previous_layer_crossed_line, current_layer_crossed_line);
+}
+
 struct SmoothingParams
 {
     double blend_width{};
@@ -201,7 +352,7 @@ ElevatedTravelParams get_elevated_traval_params(
     const Polyline& xy_path,
     const FullPrintConfig &config,
     const unsigned extruder_id,
-    const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer
+    const GCode::TravelObstacleTracker &obstacle_tracker
 ) {
     ElevatedTravelParams elevation_params{};
     if (!config.travel_ramping_lift.get_at(extruder_id)) {
@@ -221,12 +372,7 @@ ElevatedTravelParams get_elevated_traval_params(
         elevation_params.slope_end = elevation_params.lift_height / std::tan(slope_rad);
     }
 
-    const double obstacle_adjusted_slope_end{
-        previous_layer_distancer ?
-            get_first_crossed_line_distance(xy_path.lines(), *previous_layer_distancer) :
-            std::numeric_limits<double>::max()
-    };
-
+    const double obstacle_adjusted_slope_end = get_obstacle_adjusted_slope_end(xy_path.lines(), obstacle_tracker);
     if (obstacle_adjusted_slope_end < elevation_params.slope_end)
         elevation_params.slope_end = obstacle_adjusted_slope_end;
 
@@ -263,7 +409,7 @@ Points3 generate_travel_to_extrusion(
     const FullPrintConfig &config,
     const unsigned extruder_id,
     const double initial_elevation,
-    const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer,
+    const GCode::TravelObstacleTracker &obstacle_tracker,
     const Point &xy_path_coord_origin
 ) {
     const double upper_limit = config.retract_lift_below.get_at(extruder_id);
@@ -279,7 +425,7 @@ Points3 generate_travel_to_extrusion(
     }
 
     ElevatedTravelParams elevation_params{get_elevated_traval_params(
-        Polyline{std::move(global_xy_path)}, config, extruder_id, previous_layer_distancer
+        Polyline{std::move(global_xy_path)}, config, extruder_id, obstacle_tracker
     )};
 
     const std::vector<double> ensure_points_at_distances = linspace(

+ 82 - 4
src/libslic3r/GCode/Travels.hpp

@@ -11,19 +11,91 @@
 #include <functional>
 #include <optional>
 
+#include <boost/functional/hash.hpp>
 #include <boost/math/special_functions/pow.hpp>
 
 #include "libslic3r/AABBTreeLines.hpp"
 
 // Forward declarations.
 namespace Slic3r {
+class Layer;
 class Point;
 class Linef;
 class Polyline;
 class FullPrintConfig;
+class ExtrusionEntity;
 
 } // namespace Slic3r
 
+namespace Slic3r::GCode {
+struct ObjectLayerToPrint;
+using ObjectsLayerToPrint = std::vector<ObjectLayerToPrint>;
+
+class ObjectOrExtrusionLinef : public Linef
+{
+public:
+    ObjectOrExtrusionLinef() = delete;
+    ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b) : Linef(a, b) {}
+    explicit ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b, size_t object_layer_idx, size_t instance_idx)
+        : Linef(a, b), object_layer_idx(int(object_layer_idx)), instance_idx(int(instance_idx)) {}
+    ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b, size_t object_layer_idx, size_t instance_idx, const ExtrusionEntity *extrusion_entity)
+        : Linef(a, b), object_layer_idx(int(object_layer_idx)), instance_idx(int(instance_idx)), extrusion_entity(extrusion_entity) {}
+
+    virtual ~ObjectOrExtrusionLinef() = default;
+
+    const int              object_layer_idx = -1;
+    const int              instance_idx     = -1;
+    const ExtrusionEntity *extrusion_entity = nullptr;
+};
+
+struct ExtrudedExtrusionEntity
+{
+    const int              object_layer_idx = -1;
+    const int              instance_idx     = -1;
+    const ExtrusionEntity *extrusion_entity = nullptr;
+
+    bool operator==(const ExtrudedExtrusionEntity &other) const
+    {
+        return extrusion_entity == other.extrusion_entity && object_layer_idx == other.object_layer_idx &&
+               instance_idx == other.instance_idx;
+    }
+};
+
+struct ExtrudedExtrusionEntityHash
+{
+    size_t operator()(const ExtrudedExtrusionEntity &eee) const noexcept
+    {
+        std::size_t seed = std::hash<const ExtrusionEntity *>{}(eee.extrusion_entity);
+        boost::hash_combine(seed, std::hash<int>{}(eee.object_layer_idx));
+        boost::hash_combine(seed, std::hash<int>{}(eee.instance_idx));
+        return seed;
+    }
+};
+
+class TravelObstacleTracker
+{
+public:
+    void init_layer(const Layer &layer, const ObjectsLayerToPrint &objects_to_print);
+
+    void mark_extruded(const ExtrusionEntity *extrusion_entity, size_t object_layer_idx, size_t instance_idx);
+
+    bool is_extruded(const ObjectOrExtrusionLinef &line) const;
+
+    const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &previous_layer_distancer() const { return m_previous_layer_distancer; }
+
+    const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &current_layer_distancer() const { return m_current_layer_distancer; }
+
+    const ObjectsLayerToPrint &objects_to_print() const { return m_objects_to_print; }
+
+private:
+    ObjectsLayerToPrint                                                      m_objects_to_print;
+    AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef>                    m_previous_layer_distancer;
+
+    AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef>                    m_current_layer_distancer;
+    std::unordered_set<ExtrudedExtrusionEntity, ExtrudedExtrusionEntityHash> m_extruded_extrusion;
+};
+} // namespace Slic3r::GCode
+
 namespace Slic3r::GCode::Impl::Travels {
 /**
  * @brief A point on a curve with a distance from start.
@@ -106,7 +178,7 @@ ElevatedTravelParams get_elevated_traval_params(
     const Polyline& xy_path,
     const FullPrintConfig &config,
     const unsigned extruder_id,
-    const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer = std::nullopt
+    const GCode::TravelObstacleTracker &obstacle_tracker
 );
 
 /**
@@ -137,13 +209,19 @@ Points3 generate_elevated_travel(
  *
  * @param xy_path A path in 2D.
  * @param distancer AABB Tree over lines.
+ * @param objects_to_print Objects to print are used to determine in which object xy_path starts.
+
+ * @param ignore_starting_object_intersection When it is true, then the first intersection during traveling from the object out is ignored.
  * @return Distance to the first intersection if there is one.
  *
  * **Ignores intersection with xy_path starting point.**
  */
 double get_first_crossed_line_distance(
-    tcb::span<const Line> xy_path, const AABBTreeLines::LinesDistancer<Linef> &distancer
-);
+    tcb::span<const Line> xy_path,
+    const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &distancer,
+    const ObjectsLayerToPrint &objects_to_print = {},
+    const std::function<bool(const ObjectOrExtrusionLinef &)> &predicate = [](const ObjectOrExtrusionLinef &) { return true; },
+    bool ignore_starting_object_intersection = true);
 
 /**
  * @brief Extract parameters and decide wheather the travel can be elevated.
@@ -154,7 +232,7 @@ Points3 generate_travel_to_extrusion(
     const FullPrintConfig &config,
     const unsigned extruder_id,
     const double initial_elevation,
-    const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer,
+    const GCode::TravelObstacleTracker &obstacle_tracker,
     const Point &xy_path_coord_origin
 );
 } // namespace Slic3r::GCode::Impl::Travels

+ 1 - 0
src/libslic3r/Line.hpp

@@ -280,6 +280,7 @@ class Linef
 public:
     Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
     Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
+    virtual ~Linef() = default;
 
     Vec2d a;
     Vec2d b;

+ 3 - 3
src/slic3r/GUI/Tab.cpp

@@ -1966,7 +1966,7 @@ std::vector<std::pair<std::string, std::vector<std::string>>> option_keys {
         "filament_travel_ramping_lift",
         "filament_travel_max_lift",
         "filament_travel_slope",
-        //"filament_travel_lift_before_obstacle",
+        "filament_travel_lift_before_obstacle",
         "filament_retract_lift_above",
         "filament_retract_lift_below"
     }},
@@ -3283,7 +3283,7 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders)
         optgroup->append_single_option_line("travel_ramping_lift", "", extruder_idx);
         optgroup->append_single_option_line("travel_max_lift", "", extruder_idx);
         optgroup->append_single_option_line("travel_slope", "", extruder_idx);
-        //optgroup->append_single_option_line("travel_lift_before_obstacle", "", extruder_idx);
+        optgroup->append_single_option_line("travel_lift_before_obstacle", "", extruder_idx);
 
         line = { L("Only lift"), "" };
         line.append_option(optgroup->get_option("retract_lift_above", extruder_idx));
@@ -3557,7 +3557,7 @@ void TabPrinter::toggle_options()
             load_config(new_conf);
         }
 
-        //toggle_option("travel_lift_before_obstacle", ramping_lift, i);
+        toggle_option("travel_lift_before_obstacle", ramping_lift, i);
 
         toggle_option("retract_length_toolchange", have_multiple_extruders, i);
 

+ 3 - 2
tests/fff_print/test_gcode_travels.cpp

@@ -1,6 +1,7 @@
 #include <catch2/catch.hpp>
 #include <libslic3r/GCode/Travels.hpp>
 #include <libslic3r/ExPolygon.hpp>
+#include <libslic3r/GCode.hpp>
 #include <boost/math/special_functions/pow.hpp>
 
 using namespace Slic3r;
@@ -162,14 +163,14 @@ TEST_CASE("Get first crossed line distance", "[GCode]") {
         scaled(Vec2f{0, 5}),
     }.lines()};
 
-    std::vector<Linef> lines;
+    std::vector<GCode::ObjectOrExtrusionLinef> lines;
     for (const ExPolygon& polygon : {square_with_hole, square_above}) {
         for (const Line& line : polygon.lines()) {
             lines.emplace_back(unscale(line.a), unscale(line.b));
         }
     }
     // Try different cases by skipping lines in the travel.
-    AABBTreeLines::LinesDistancer<Linef> distancer{std::move(lines)};
+    AABBTreeLines::LinesDistancer<GCode::ObjectOrExtrusionLinef> distancer{std::move(lines)};
 
     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));