Disabling wipe for avoid crossing perimeters
This commit is contained in:
parent
7f94e9fa59
commit
ef9de07740
4 changed files with 119 additions and 32 deletions
|
@ -2662,14 +2662,16 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
travel.append(point);
|
||||
|
||||
// check whether a straight travel move would need retraction
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
// check whether wipe could be disabled without causing visible stringing
|
||||
bool could_be_wipe_disabled = false;
|
||||
|
||||
// if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
|
||||
// multi-hop travel path inside the configuration space
|
||||
if (needs_retraction
|
||||
&& m_config.avoid_crossing_perimeters
|
||||
&& ! m_avoid_crossing_perimeters.disable_once) {
|
||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point);
|
||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
|
||||
|
||||
// check again whether the new travel path still needs a retraction
|
||||
needs_retraction = this->needs_retraction(travel, role);
|
||||
|
@ -2683,6 +2685,9 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
// generate G-code for the travel move
|
||||
std::string gcode;
|
||||
if (needs_retraction) {
|
||||
if (m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once && could_be_wipe_disabled)
|
||||
m_wipe.reset_path();
|
||||
|
||||
Point last_post_before_retract = this->last_pos();
|
||||
gcode += this->retract();
|
||||
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
|
||||
|
|
|
@ -156,6 +156,7 @@ public:
|
|||
const FullPrintConfig &config() const { return m_config; }
|
||||
const Layer* layer() const { return m_layer; }
|
||||
GCodeWriter& writer() { return m_writer; }
|
||||
const GCodeWriter& writer() const { return m_writer; }
|
||||
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
||||
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
|
||||
// Process a template through the placeholder parser, collect error messages to be reported
|
||||
|
|
|
@ -115,6 +115,7 @@ static Matrix2d rotation_by_direction(const Point &direction)
|
|||
|
||||
static Point find_first_different_vertex(const Polygon &polygon, const size_t point_idx, const Point &point, bool forward)
|
||||
{
|
||||
assert(point_idx < polygon.size());
|
||||
if (point != polygon.points[point_idx])
|
||||
return polygon.points[point_idx];
|
||||
|
||||
|
@ -137,8 +138,9 @@ static Vec2d three_points_inward_normal(const Point &left, const Point &middle,
|
|||
normal_2.normalize();
|
||||
|
||||
return (normal_1 + normal_2).normalized();
|
||||
};
|
||||
}
|
||||
|
||||
// Compute normal of the polygon's vertex in an inward direction
|
||||
static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size_t point_idx)
|
||||
{
|
||||
const size_t left_idx = (point_idx <= 0) ? (polygon.size() - 1) : (point_idx - 1);
|
||||
|
@ -149,12 +151,13 @@ static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size
|
|||
return three_points_inward_normal(left, middle, right);
|
||||
}
|
||||
|
||||
// Compute offset of polygon's in a direction inward normal
|
||||
// Compute offset of point_idx of the polygon in a direction of inward normal
|
||||
static Point get_polygon_vertex_offset(const Polygon &polygon, const size_t point_idx, const int offset)
|
||||
{
|
||||
return polygon.points[point_idx] + (get_polygon_vertex_inward_normal(polygon, point_idx) * double(offset)).cast<coord_t>();
|
||||
}
|
||||
|
||||
// Compute offset (in the direction of inward normal) of the point(passed on "middle") based on the nearest points laying on the polygon (left_idx and right_idx).
|
||||
static Point get_middle_point_offset(const Polygon &polygon, const size_t left_idx, const size_t right_idx, const Point &middle, const int offset)
|
||||
{
|
||||
const Point &left = find_first_different_vertex(polygon, left_idx, middle, false);
|
||||
|
@ -243,6 +246,15 @@ static coord_t get_perimeter_spacing_external(const Layer &layer)
|
|||
return perimeter_spacing;
|
||||
}
|
||||
|
||||
// Check if anyone of ExPolygons contains whole travel.
|
||||
template<class T> static bool any_expolygon_contains(const ExPolygons &ex_polygons, const T &travel)
|
||||
{
|
||||
for (const ExPolygon &ex_polygon : ex_polygons)
|
||||
if (ex_polygon.contains(travel)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer)
|
||||
{
|
||||
const coord_t perimeter_spacing = get_perimeter_spacing(layer);
|
||||
|
@ -321,6 +333,7 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary_external(const Layer &layer)
|
|||
|
||||
Polygons contours;
|
||||
Polygons holes;
|
||||
contours.reserve(boundary.size());
|
||||
for (ExPolygon &poly : boundary) {
|
||||
contours.emplace_back(poly.contour);
|
||||
append(holes, poly.holes);
|
||||
|
@ -429,10 +442,11 @@ Polyline AvoidCrossingPerimeters2::simplify_travel(const EdgeGrid::Grid &edge_gr
|
|||
return optimized_comb_path;
|
||||
}
|
||||
|
||||
Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &boundaries,
|
||||
const EdgeGrid::Grid &edge_grid,
|
||||
const Point &start,
|
||||
const Point &end)
|
||||
size_t AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &boundaries,
|
||||
const EdgeGrid::Grid &edge_grid,
|
||||
const Point &start,
|
||||
const Point &end,
|
||||
Polyline *result_out)
|
||||
{
|
||||
const Point direction = end - start;
|
||||
Matrix2d transform_to_x_axis = rotation_by_direction(direction);
|
||||
|
@ -496,12 +510,12 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &bounda
|
|||
// Append the nearest intersection into the path
|
||||
size_t left_idx = intersection_first.line_idx;
|
||||
size_t right_idx = (intersection_first.line_idx >= (boundaries[intersection_first.border_idx].points.size() - 1)) ? 0 : (intersection_first.line_idx + 1);
|
||||
// Offset of the polygon's point using get_middle_point_offset is used to simplify calculation of intersection between boundary
|
||||
result.append(get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, SCALED_EPSILON));
|
||||
|
||||
Direction shortest_direction = get_shortest_direction(border_lines, intersection_first.line_idx, intersection_second.line_idx,
|
||||
intersection_first.point, intersection_second.point);
|
||||
// Append the path around the border into the path
|
||||
// Offset of the polygon's point is used to simplify calculation of intersection between boundary
|
||||
if (shortest_direction == Direction::Forward)
|
||||
for (int line_idx = intersection_first.line_idx; line_idx != int(intersection_second.line_idx);
|
||||
line_idx = (((line_idx + 1) < int(border_lines.size())) ? (line_idx + 1) : 0))
|
||||
|
@ -524,10 +538,48 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &bounda
|
|||
}
|
||||
|
||||
result.append(end);
|
||||
return simplify_travel(edge_grid, result);
|
||||
if(!intersections.empty()) {
|
||||
result = simplify_travel(edge_grid, result);
|
||||
}
|
||||
|
||||
append(result_out->points, result.points);
|
||||
return intersections.size();
|
||||
}
|
||||
|
||||
Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point)
|
||||
bool AvoidCrossingPerimeters2::needs_wipe(const GCode & gcodegen,
|
||||
const Line & original_travel,
|
||||
const Polyline &result_travel,
|
||||
const size_t intersection_count)
|
||||
{
|
||||
bool z_lift_enabled = gcodegen.config().retract_lift.get_at(gcodegen.writer().extruder()->id()) > 0.;
|
||||
bool wipe_needed = false;
|
||||
|
||||
// If the original unmodified path doesn't have any intersection with boundary, then it is entirely inside the object otherwise is entirely
|
||||
// outside the object.
|
||||
if (intersection_count > 0) {
|
||||
// The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
|
||||
// If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
|
||||
if (z_lift_enabled) {
|
||||
if (any_expolygon_contains(m_slice, original_travel)) {
|
||||
// Check if original_travel and are not same result_travel
|
||||
if (result_travel.size() == 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) {
|
||||
wipe_needed = false;
|
||||
} else {
|
||||
wipe_needed = !any_expolygon_contains(m_slice, result_travel);
|
||||
}
|
||||
} else {
|
||||
wipe_needed = true;
|
||||
}
|
||||
} else {
|
||||
wipe_needed = !any_expolygon_contains(m_slice, result_travel);
|
||||
}
|
||||
}
|
||||
|
||||
return wipe_needed;
|
||||
}
|
||||
|
||||
// Plan travel, which avoids perimeter crossings by following the boundaries of the layer.
|
||||
Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled)
|
||||
{
|
||||
// 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.
|
||||
|
@ -536,14 +588,16 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point
|
|||
Point start = gcodegen.last_pos() + scaled_origin;
|
||||
Point end = point + scaled_origin;
|
||||
Polyline result;
|
||||
size_t travel_intersection_count = 0;
|
||||
if (!check_if_could_cross_perimeters(use_external ? m_bbox_external : m_bbox, start, end)) {
|
||||
result = Polyline({start, end});
|
||||
result = Polyline({start, end});
|
||||
travel_intersection_count = 0;
|
||||
} else {
|
||||
auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end);
|
||||
if (use_external)
|
||||
result = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped);
|
||||
travel_intersection_count = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, &result);
|
||||
else
|
||||
result = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped);
|
||||
travel_intersection_count = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, &result);
|
||||
}
|
||||
|
||||
result.points.front() = start;
|
||||
|
@ -554,33 +608,32 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point
|
|||
if ((max_detour_length > 0) && ((result.length() - travel.length()) > max_detour_length)) {
|
||||
result = Polyline({start, end});
|
||||
}
|
||||
if (use_external)
|
||||
if (use_external) {
|
||||
result.translate(-scaled_origin);
|
||||
*could_be_wipe_disabled = false;
|
||||
} else
|
||||
*could_be_wipe_disabled = !needs_wipe(gcodegen, travel, result, travel_intersection_count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AvoidCrossingPerimeters2::init_layer(const Layer &layer)
|
||||
{
|
||||
m_slice.clear();
|
||||
m_boundaries.clear();
|
||||
m_boundaries_external.clear();
|
||||
|
||||
ExPolygons boundaries = get_boundary(layer);
|
||||
ExPolygons boundaries_external = get_boundary_external(layer);
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
append(m_slice, (ExPolygons) layer_region->slices);
|
||||
|
||||
m_bbox = get_extents(boundaries);
|
||||
m_boundaries = to_polygons(get_boundary(layer));
|
||||
m_boundaries_external = to_polygons(get_boundary_external(layer));
|
||||
|
||||
m_bbox = get_extents(m_boundaries);
|
||||
m_bbox.offset(SCALED_EPSILON);
|
||||
m_bbox_external = get_extents(boundaries_external);
|
||||
m_bbox_external = get_extents(m_boundaries_external);
|
||||
m_bbox_external.offset(SCALED_EPSILON);
|
||||
|
||||
for (const ExPolygon &ex_poly : boundaries) {
|
||||
m_boundaries.emplace_back(ex_poly.contour);
|
||||
append(m_boundaries, ex_poly.holes);
|
||||
}
|
||||
for (const ExPolygon &ex_poly : boundaries_external) {
|
||||
m_boundaries_external.emplace_back(ex_poly.contour);
|
||||
append(m_boundaries_external, ex_poly.holes);
|
||||
}
|
||||
|
||||
m_grid.set_bbox(m_bbox);
|
||||
m_grid.create(m_boundaries, scale_(1.));
|
||||
m_grid_external.set_bbox(m_bbox_external);
|
||||
|
|
|
@ -37,11 +37,17 @@ public:
|
|||
m_external_mp.reset();
|
||||
m_layer_mp.reset();
|
||||
}
|
||||
void init_external_mp(const Print &print);
|
||||
void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique<MotionPlanner>(islands); }
|
||||
virtual void init_external_mp(const Print &print);
|
||||
virtual void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique<MotionPlanner>(islands); }
|
||||
|
||||
virtual Polyline travel_to(const GCode &gcodegen, const Point &point);
|
||||
|
||||
virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled)
|
||||
{
|
||||
*could_be_wipe_disabled = true;
|
||||
return this->travel_to(gcodegen, point);
|
||||
}
|
||||
|
||||
protected:
|
||||
// For initializing the regions to avoid.
|
||||
static Polygons collect_contours_all_layers(const PrintObjectPtrs &objects);
|
||||
|
@ -55,9 +61,13 @@ class AvoidCrossingPerimeters2 : public AvoidCrossingPerimeters
|
|||
protected:
|
||||
struct Intersection
|
||||
{
|
||||
// Index of the polygon containing this point of intersection.
|
||||
size_t border_idx;
|
||||
// Index of the line on the polygon containing this point of intersection.
|
||||
size_t line_idx;
|
||||
// Point of intersection projected on the travel path.
|
||||
Point point_transformed;
|
||||
// Point of intersection.
|
||||
Point point;
|
||||
|
||||
Intersection(size_t border_idx, size_t line_idx, const Point &point_transformed, const Point &point)
|
||||
|
@ -78,11 +88,19 @@ private:
|
|||
|
||||
static Polyline simplify_travel(const EdgeGrid::Grid &edge_grid, const Polyline &travel);
|
||||
|
||||
static Polyline avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end);
|
||||
static size_t avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end, Polyline *result_out);
|
||||
|
||||
bool needs_wipe(const GCode &gcodegen, const Line &original_travel, const Polyline &result_travel, const size_t intersection_count);
|
||||
|
||||
// Slice of layer with elephant foot compensation
|
||||
ExPolygons m_slice;
|
||||
// Collection of boundaries used for detection of crossing perimetrs for travels inside object
|
||||
Polygons m_boundaries;
|
||||
// Collection of boundaries used for detection of crossing perimetrs for travels outside object
|
||||
Polygons m_boundaries_external;
|
||||
// Bounding box of m_boundaries
|
||||
BoundingBox m_bbox;
|
||||
// Bounding box of m_boundaries_external
|
||||
BoundingBox m_bbox_external;
|
||||
EdgeGrid::Grid m_grid;
|
||||
EdgeGrid::Grid m_grid_external;
|
||||
|
@ -92,7 +110,17 @@ public:
|
|||
|
||||
virtual ~AvoidCrossingPerimeters2() = default;
|
||||
|
||||
virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override;
|
||||
// Used for disabling unnecessary calling collect_contours_all_layers
|
||||
virtual void init_external_mp(const Print &print) override {};
|
||||
virtual void init_layer_mp(const ExPolygons &islands) override {};
|
||||
|
||||
virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override
|
||||
{
|
||||
bool could_be_wipe_disabled;
|
||||
return this->travel_to(gcodegen, point, &could_be_wipe_disabled);
|
||||
}
|
||||
|
||||
virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *needs_wipe) override;
|
||||
|
||||
void init_layer(const Layer &layer);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue