Disabling wipe for avoid crossing perimeters

This commit is contained in:
Lukáš Hejl 2020-10-22 06:20:38 +02:00
parent 7f94e9fa59
commit ef9de07740
4 changed files with 119 additions and 32 deletions

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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);
};