mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 15:12:00 +08:00
Add basic implementation of an obstacle finding algorithm during travels.
A line distancer over previous layer is constructed. It is then queried during travel planing. If an obstacle is found in the travel path, the travel algorithm ensures minimal elevation before the obstacle.
This commit is contained in:
parent
a4a7ac4a4e
commit
384d245be7
@ -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<unsigned int>& 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<double> get_first_crossed_line_distance(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<double> get_obstacle_adjusted_slope_end(
|
||||
const Lines& xy_path,
|
||||
const std::optional<AABBTreeLines::LinesDistancer<Linef>>& previous_layer_distancer
|
||||
) {
|
||||
if (!previous_layer_distancer) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<double> 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<AABBTreeLines::LinesDistancer<Linef>>& 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<double> 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<AABBTreeLines::LinesDistancer<Linef>>& 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<double> 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<coord_t>(pt);
|
||||
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -135,6 +135,31 @@ Points3 generate_elevated_travel(
|
||||
const std::function<double(double)>& 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<Linef> 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<double> get_first_crossed_line_distance(
|
||||
tcb::span<const Line> xy_path,
|
||||
const AABBTreeLines::LinesDistancer<Linef>& 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<unsigned int>& 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<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?
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
class SpiralVase {
|
||||
public:
|
||||
SpiralVase(const PrintConfig &config) : m_config(config)
|
||||
|
@ -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<Linef> 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})};
|
||||
|
Loading…
x
Reference in New Issue
Block a user