mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-05-29 17:44:36 +08:00
AvoidCrossingPerimeters: Refactored for better encapsulation.
This commit is contained in:
parent
04c2fde671
commit
62ab17bf6e
@ -46,8 +46,6 @@ using namespace std::literals::string_view_literals;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <unordered_set>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -232,7 +230,7 @@ namespace Slic3r {
|
|||||||
// Move over the wipe tower.
|
// Move over the wipe tower.
|
||||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||||
gcode += gcodegen.retract(true);
|
gcode += gcodegen.retract(true);
|
||||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||||
gcode += gcodegen.travel_to(
|
gcode += gcodegen.travel_to(
|
||||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||||
erMixed,
|
erMixed,
|
||||||
@ -327,7 +325,7 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let the planner know we are traveling between objects.
|
// Let the planner know we are traveling between objects.
|
||||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1175,12 +1173,12 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||||||
// Move to the origin position for the copy we're going to print.
|
// Move to the origin position for the copy we're going to print.
|
||||||
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
||||||
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
||||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
m_avoid_crossing_perimeters.use_external_mp_once();
|
||||||
_write(file, this->retract());
|
_write(file, this->retract());
|
||||||
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||||
m_enable_cooling_markers = true;
|
m_enable_cooling_markers = true;
|
||||||
// Disable motion planner when traveling to first object point.
|
// Disable motion planner when traveling to first object point.
|
||||||
m_avoid_crossing_perimeters.disable_once = true;
|
m_avoid_crossing_perimeters.disable_once();
|
||||||
// Ff we are printing the bottom layer of an object, and we have already finished
|
// Ff we are printing the bottom layer of an object, and we have already finished
|
||||||
// another one, set first layer temperatures. This happens before the Z move
|
// another one, set first layer temperatures. This happens before the Z move
|
||||||
// is triggered, so machine has more time to reach such temperatures.
|
// is triggered, so machine has more time to reach such temperatures.
|
||||||
@ -1998,7 +1996,7 @@ void GCode::process_layer(
|
|||||||
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
||||||
const std::pair<size_t, size_t> loops = loops_it->second;
|
const std::pair<size_t, size_t> loops = loops_it->second;
|
||||||
this->set_origin(0., 0.);
|
this->set_origin(0., 0.);
|
||||||
m_avoid_crossing_perimeters.use_external_mp = true;
|
m_avoid_crossing_perimeters.use_external_mp();
|
||||||
Flow layer_skirt_flow(print.skirt_flow());
|
Flow layer_skirt_flow(print.skirt_flow());
|
||||||
layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]));
|
layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]));
|
||||||
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
||||||
@ -2012,23 +2010,23 @@ void GCode::process_layer(
|
|||||||
//FIXME using the support_material_speed of the 1st object printed.
|
//FIXME using the support_material_speed of the 1st object printed.
|
||||||
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
|
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
|
||||||
}
|
}
|
||||||
m_avoid_crossing_perimeters.use_external_mp = false;
|
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||||
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
|
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
|
||||||
if (first_layer && loops.first == 0)
|
if (first_layer && loops.first == 0)
|
||||||
m_avoid_crossing_perimeters.disable_once = true;
|
m_avoid_crossing_perimeters.disable_once();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrude brim with the extruder of the 1st region.
|
// Extrude brim with the extruder of the 1st region.
|
||||||
if (! m_brim_done) {
|
if (! m_brim_done) {
|
||||||
this->set_origin(0., 0.);
|
this->set_origin(0., 0.);
|
||||||
m_avoid_crossing_perimeters.use_external_mp = true;
|
m_avoid_crossing_perimeters.use_external_mp();
|
||||||
for (const ExtrusionEntity *ee : print.brim().entities) {
|
for (const ExtrusionEntity *ee : print.brim().entities) {
|
||||||
gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
|
gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
|
||||||
}
|
}
|
||||||
m_brim_done = true;
|
m_brim_done = true;
|
||||||
m_avoid_crossing_perimeters.use_external_mp = false;
|
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||||
// Allow a straight travel move to the first object point.
|
// Allow a straight travel move to the first object point.
|
||||||
m_avoid_crossing_perimeters.disable_once = true;
|
m_avoid_crossing_perimeters.disable_once();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2055,7 +2053,7 @@ void GCode::process_layer(
|
|||||||
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||||
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
||||||
if (m_last_obj_copy != this_object_copy)
|
if (m_last_obj_copy != this_object_copy)
|
||||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
m_avoid_crossing_perimeters.use_external_mp_once();
|
||||||
m_last_obj_copy = this_object_copy;
|
m_last_obj_copy = this_object_copy;
|
||||||
this->set_origin(unscale(offset));
|
this->set_origin(unscale(offset));
|
||||||
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
|
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
|
||||||
@ -2647,9 +2645,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||||||
/* Define the travel move as a line between current position and the taget point.
|
/* Define the travel move as a line between current position and the taget point.
|
||||||
This is expressed in print coordinates, so it will need to be translated by
|
This is expressed in print coordinates, so it will need to be translated by
|
||||||
this->origin in order to get G-code coordinates. */
|
this->origin in order to get G-code coordinates. */
|
||||||
Polyline travel;
|
Polyline travel { this->last_pos(), point };
|
||||||
travel.append(this->last_pos());
|
|
||||||
travel.append(point);
|
|
||||||
|
|
||||||
// check whether a straight travel move would need retraction
|
// 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);
|
||||||
@ -2660,31 +2656,28 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||||||
// multi-hop travel path inside the configuration space
|
// multi-hop travel path inside the configuration space
|
||||||
if (needs_retraction
|
if (needs_retraction
|
||||||
&& m_config.avoid_crossing_perimeters
|
&& m_config.avoid_crossing_perimeters
|
||||||
&& ! m_avoid_crossing_perimeters.disable_once) {
|
&& ! m_avoid_crossing_perimeters.disabled_once()) {
|
||||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
|
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
|
||||||
|
|
||||||
// check again whether the new travel path still needs a retraction
|
// check again whether the new travel path still needs a retraction
|
||||||
needs_retraction = this->needs_retraction(travel, role);
|
needs_retraction = this->needs_retraction(travel, role);
|
||||||
//if (needs_retraction && m_layer_index > 1) exit(0);
|
//if (needs_retraction && m_layer_index > 1) exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-allow avoid_crossing_perimeters for the next travel moves
|
// Re-allow avoid_crossing_perimeters for the next travel moves
|
||||||
m_avoid_crossing_perimeters.disable_once = false;
|
m_avoid_crossing_perimeters.reset_once_modifiers();
|
||||||
m_avoid_crossing_perimeters.use_external_mp_once = false;
|
|
||||||
|
|
||||||
// generate G-code for the travel move
|
// generate G-code for the travel move
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
if (needs_retraction) {
|
if (needs_retraction) {
|
||||||
if (m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once && could_be_wipe_disabled)
|
if (m_config.avoid_crossing_perimeters && could_be_wipe_disabled)
|
||||||
m_wipe.reset_path();
|
m_wipe.reset_path();
|
||||||
|
|
||||||
Point last_post_before_retract = this->last_pos();
|
Point last_post_before_retract = this->last_pos();
|
||||||
gcode += this->retract();
|
gcode += this->retract();
|
||||||
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
|
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
|
||||||
// Because of it, it is necessary to call avoid crossing perimeters for the path between previous last_post and last_post after calling retraction()
|
// Because of it, it is necessary to call avoid crossing perimeters for the path between previous last_post and last_post after calling retraction()
|
||||||
if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once) {
|
if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) {
|
||||||
Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, last_post_before_retract);
|
Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, last_post_before_retract);
|
||||||
retract_travel.points.reserve(retract_travel.points.size() + travel.points.size());
|
|
||||||
append(retract_travel.points, travel.points);
|
append(retract_travel.points, travel.points);
|
||||||
travel = std::move(retract_travel);
|
travel = std::move(retract_travel);
|
||||||
}
|
}
|
||||||
@ -2693,11 +2686,10 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||||||
m_wipe.reset_path();
|
m_wipe.reset_path();
|
||||||
|
|
||||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||||
Lines lines = travel.lines();
|
if (travel.size() >= 2) {
|
||||||
if (! lines.empty()) {
|
for (size_t i = 1; i < travel.size(); ++ i)
|
||||||
for (const Line &line : lines)
|
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
|
||||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(line.b), comment);
|
this->set_last_pos(travel.points.back());
|
||||||
this->set_last_pos(lines.back().b);
|
|
||||||
}
|
}
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#include "../Layer.hpp"
|
#include "../Layer.hpp"
|
||||||
#include "../GCode.hpp"
|
#include "../GCode.hpp"
|
||||||
#include "../EdgeGrid.hpp"
|
#include "../EdgeGrid.hpp"
|
||||||
#include "../Geometry.hpp"
|
|
||||||
#include "../ShortestPath.hpp"
|
|
||||||
#include "../Print.hpp"
|
#include "../Print.hpp"
|
||||||
#include "../Polygon.hpp"
|
#include "../Polygon.hpp"
|
||||||
#include "../ExPolygon.hpp"
|
#include "../ExPolygon.hpp"
|
||||||
@ -10,15 +8,80 @@
|
|||||||
#include "../SVG.hpp"
|
#include "../SVG.hpp"
|
||||||
#include "AvoidCrossingPerimeters.hpp"
|
#include "AvoidCrossingPerimeters.hpp"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct TravelPoint
|
||||||
|
{
|
||||||
|
Point point;
|
||||||
|
// Index of the polygon containing this point. A negative value indicates that the point is not on any border
|
||||||
|
int border_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
: border_idx(border_idx), line_idx(line_idx), point_transformed(point_transformed), point(point){};
|
||||||
|
|
||||||
|
inline bool operator<(const Intersection &other) const { return this->point_transformed.x() < other.point_transformed.x(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AllIntersectionsVisitor
|
||||||
|
{
|
||||||
|
AllIntersectionsVisitor(const EdgeGrid::Grid &grid, std::vector<Intersection> &intersections)
|
||||||
|
: grid(grid), intersections(intersections)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AllIntersectionsVisitor(const EdgeGrid::Grid &grid,
|
||||||
|
std::vector<Intersection> &intersections,
|
||||||
|
const Matrix2d &transform_to_x_axis,
|
||||||
|
const Line &travel_line)
|
||||||
|
: grid(grid), intersections(intersections), transform_to_x_axis(transform_to_x_axis), travel_line(travel_line)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
intersection_set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(coord_t iy, coord_t ix)
|
||||||
|
{
|
||||||
|
// Called with a row and colum of the grid cell, which is intersected by a line.
|
||||||
|
auto cell_data_range = grid.cell_data_range(iy, ix);
|
||||||
|
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second;
|
||||||
|
++it_contour_and_segment) {
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
auto segment = grid.segment(*it_contour_and_segment);
|
||||||
|
|
||||||
|
Point intersection_point;
|
||||||
|
if (travel_line.intersection(Line(segment.first, segment.second), &intersection_point) &&
|
||||||
|
intersection_set.find(*it_contour_and_segment) == intersection_set.end()) {
|
||||||
|
intersections.emplace_back(it_contour_and_segment->first, it_contour_and_segment->second,
|
||||||
|
(transform_to_x_axis * intersection_point.cast<double>()).cast<coord_t>(), intersection_point);
|
||||||
|
intersection_set.insert(*it_contour_and_segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Continue traversing the grid along the edge.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EdgeGrid::Grid &grid;
|
||||||
|
std::vector<Intersection> &intersections;
|
||||||
|
Matrix2d transform_to_x_axis;
|
||||||
|
Line travel_line;
|
||||||
|
std::unordered_set<std::pair<size_t, size_t>, boost::hash<std::pair<size_t, size_t>>> intersection_set;
|
||||||
|
};
|
||||||
|
|
||||||
// Create a rotation matrix for projection on the given vector
|
// Create a rotation matrix for projection on the given vector
|
||||||
static Matrix2d rotation_by_direction(const Point &direction)
|
static Matrix2d rotation_by_direction(const Point &direction)
|
||||||
{
|
{
|
||||||
@ -185,16 +248,16 @@ static std::pair<Polygons, Polygons> split_expolygon(const ExPolygons &ex_polygo
|
|||||||
return std::make_pair(std::move(contours), std::move(holes));
|
return std::make_pair(std::move(contours), std::move(holes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Polyline to_polyline(const std::vector<AvoidCrossingPerimeters::TravelPoint> &travel)
|
static Polyline to_polyline(const std::vector<TravelPoint> &travel)
|
||||||
{
|
{
|
||||||
Polyline result;
|
Polyline result;
|
||||||
result.points.reserve(travel.size());
|
result.points.reserve(travel.size());
|
||||||
for (const AvoidCrossingPerimeters::TravelPoint &t_point : travel)
|
for (const TravelPoint &t_point : travel)
|
||||||
result.append(t_point.point);
|
result.append(t_point.point);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double travel_length(const std::vector<AvoidCrossingPerimeters::TravelPoint> &travel) {
|
static double travel_length(const std::vector<TravelPoint> &travel) {
|
||||||
double total_length = 0;
|
double total_length = 0;
|
||||||
for (size_t idx = 1; idx < travel.size(); ++idx)
|
for (size_t idx = 1; idx < travel.size(); ++idx)
|
||||||
total_length += (travel[idx].point - travel[idx - 1].point).cast<double>().norm();
|
total_length += (travel[idx].point - travel[idx - 1].point).cast<double>().norm();
|
||||||
@ -203,11 +266,11 @@ static double travel_length(const std::vector<AvoidCrossingPerimeters::TravelPoi
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||||
static void export_travel_to_svg(const Polygons &boundary,
|
static void export_travel_to_svg(const Polygons &boundary,
|
||||||
const Line &original_travel,
|
const Line &original_travel,
|
||||||
const Polyline &result_travel,
|
const Polyline &result_travel,
|
||||||
const std::vector<AvoidCrossingPerimeters::Intersection> &intersections,
|
const std::vector<Intersection> &intersections,
|
||||||
const std::string &path)
|
const std::string &path)
|
||||||
{
|
{
|
||||||
BoundingBox bbox = get_extents(boundary);
|
BoundingBox bbox = get_extents(boundary);
|
||||||
::Slic3r::SVG svg(path, bbox);
|
::Slic3r::SVG svg(path, bbox);
|
||||||
@ -217,21 +280,21 @@ static void export_travel_to_svg(const Polygons
|
|||||||
svg.draw(original_travel.a, "black");
|
svg.draw(original_travel.a, "black");
|
||||||
svg.draw(original_travel.b, "grey");
|
svg.draw(original_travel.b, "grey");
|
||||||
|
|
||||||
for (const AvoidCrossingPerimeters::Intersection &intersection : intersections)
|
for (const Intersection &intersection : intersections)
|
||||||
svg.draw(intersection.point, "lightseagreen");
|
svg.draw(intersection.point, "lightseagreen");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void export_travel_to_svg(const Polygons &boundary,
|
static void export_travel_to_svg(const Polygons &boundary,
|
||||||
const Line &original_travel,
|
const Line &original_travel,
|
||||||
const std::vector<AvoidCrossingPerimeters::TravelPoint> &result_travel,
|
const std::vector<TravelPoint> &result_travel,
|
||||||
const std::vector<AvoidCrossingPerimeters::Intersection> &intersections,
|
const std::vector<Intersection> &intersections,
|
||||||
const std::string &path)
|
const std::string &path)
|
||||||
{
|
{
|
||||||
export_travel_to_svg(boundary, original_travel, to_polyline(result_travel), intersections, path);
|
export_travel_to_svg(boundary, original_travel, to_polyline(result_travel), intersections, path);
|
||||||
}
|
}
|
||||||
#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
|
#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
|
||||||
|
|
||||||
ExPolygons AvoidCrossingPerimeters::get_boundary(const Layer &layer)
|
static ExPolygons get_boundary(const Layer &layer)
|
||||||
{
|
{
|
||||||
const float perimeter_spacing = get_perimeter_spacing(layer);
|
const float perimeter_spacing = get_perimeter_spacing(layer);
|
||||||
const float perimeter_offset = perimeter_spacing / 2.f;
|
const float perimeter_offset = perimeter_spacing / 2.f;
|
||||||
@ -291,7 +354,7 @@ ExPolygons AvoidCrossingPerimeters::get_boundary(const Layer &layer)
|
|||||||
return result_boundary;
|
return result_boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExPolygons AvoidCrossingPerimeters::get_boundary_external(const Layer &layer)
|
static ExPolygons get_boundary_external(const Layer &layer)
|
||||||
{
|
{
|
||||||
const float perimeter_spacing = get_perimeter_spacing_external(layer);
|
const float perimeter_spacing = get_perimeter_spacing_external(layer);
|
||||||
const float perimeter_offset = perimeter_spacing / 2.f;
|
const float perimeter_offset = perimeter_spacing / 2.f;
|
||||||
@ -328,11 +391,12 @@ ExPolygons AvoidCrossingPerimeters::get_boundary_external(const Layer &layer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a direction of the shortest path along the polygon boundary
|
// Returns a direction of the shortest path along the polygon boundary
|
||||||
AvoidCrossingPerimeters::Direction AvoidCrossingPerimeters::get_shortest_direction(const Lines &lines,
|
enum class Direction { Forward, Backward };
|
||||||
const size_t start_idx,
|
static Direction get_shortest_direction(const Lines &lines,
|
||||||
const size_t end_idx,
|
const size_t start_idx,
|
||||||
const Point &intersection_first,
|
const size_t end_idx,
|
||||||
const Point &intersection_last)
|
const Point &intersection_first,
|
||||||
|
const Point &intersection_last)
|
||||||
{
|
{
|
||||||
double total_length_forward = (lines[start_idx].b - intersection_first).cast<double>().norm();
|
double total_length_forward = (lines[start_idx].b - intersection_first).cast<double>().norm();
|
||||||
double total_length_backward = (lines[start_idx].a - intersection_first).cast<double>().norm();
|
double total_length_backward = (lines[start_idx].a - intersection_first).cast<double>().norm();
|
||||||
@ -358,165 +422,14 @@ AvoidCrossingPerimeters::Direction AvoidCrossingPerimeters::get_shortest_directi
|
|||||||
return (total_length_forward < total_length_backward) ? Direction::Forward : Direction::Backward;
|
return (total_length_forward < total_length_backward) ? Direction::Forward : Direction::Backward;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AvoidCrossingPerimeters::TravelPoint> AvoidCrossingPerimeters::simplify_travel(const EdgeGrid::Grid &edge_grid,
|
static std::vector<TravelPoint> simplify_travel(const EdgeGrid::Grid& edge_grid, const std::vector<TravelPoint>& travel, const Polygons& boundaries, const bool use_heuristics);
|
||||||
const std::vector<TravelPoint> &travel,
|
|
||||||
const Polygons &boundaries,
|
|
||||||
const bool use_heuristics)
|
|
||||||
{
|
|
||||||
struct Visitor
|
|
||||||
{
|
|
||||||
Visitor(const EdgeGrid::Grid &grid) : grid(grid) {}
|
|
||||||
|
|
||||||
bool operator()(coord_t iy, coord_t ix)
|
static size_t avoid_perimeters(const Polygons &boundaries,
|
||||||
{
|
const EdgeGrid::Grid &edge_grid,
|
||||||
assert(pt_current != nullptr);
|
const Point &start,
|
||||||
assert(pt_next != nullptr);
|
const Point &end,
|
||||||
// Called with a row and colum of the grid cell, which is intersected by a line.
|
const bool use_heuristics,
|
||||||
auto cell_data_range = grid.cell_data_range(iy, ix);
|
std::vector<TravelPoint> *result_out)
|
||||||
this->intersect = false;
|
|
||||||
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) {
|
|
||||||
// End points of the line segment and their vector.
|
|
||||||
auto segment = grid.segment(*it_contour_and_segment);
|
|
||||||
if (Geometry::segments_intersect(segment.first, segment.second, *pt_current, *pt_next)) {
|
|
||||||
this->intersect = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Continue traversing the grid along the edge.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EdgeGrid::Grid &grid;
|
|
||||||
const Slic3r::Point *pt_current = nullptr;
|
|
||||||
const Slic3r::Point *pt_next = nullptr;
|
|
||||||
bool intersect = false;
|
|
||||||
} visitor(edge_grid);
|
|
||||||
|
|
||||||
std::vector<TravelPoint> simplified_path;
|
|
||||||
simplified_path.reserve(travel.size());
|
|
||||||
simplified_path.emplace_back(travel.front());
|
|
||||||
|
|
||||||
// Try to skip some points in the path.
|
|
||||||
for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) {
|
|
||||||
const Point ¤t_point = travel[point_idx - 1].point;
|
|
||||||
TravelPoint next = travel[point_idx];
|
|
||||||
|
|
||||||
visitor.pt_current = ¤t_point;
|
|
||||||
|
|
||||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
|
||||||
if (travel[point_idx_2].point == current_point) {
|
|
||||||
next = travel[point_idx_2];
|
|
||||||
point_idx = point_idx_2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitor.pt_next = &travel[point_idx_2].point;
|
|
||||||
edge_grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
|
||||||
// Check if deleting point causes crossing a boundary
|
|
||||||
if (!visitor.intersect) {
|
|
||||||
next = travel[point_idx_2];
|
|
||||||
point_idx = point_idx_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
simplified_path.emplace_back(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(use_heuristics) {
|
|
||||||
simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries);
|
|
||||||
std::reverse(simplified_path.begin(),simplified_path.end());
|
|
||||||
simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries);
|
|
||||||
std::reverse(simplified_path.begin(),simplified_path.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return simplified_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<AvoidCrossingPerimeters::TravelPoint> AvoidCrossingPerimeters::simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid,
|
|
||||||
const std::vector<TravelPoint> &travel,
|
|
||||||
const Polygons &boundaries)
|
|
||||||
{
|
|
||||||
std::vector<TravelPoint> simplified_path;
|
|
||||||
std::vector<Intersection> intersections;
|
|
||||||
AllIntersectionsVisitor visitor(edge_grid, intersections);
|
|
||||||
simplified_path.reserve(travel.size());
|
|
||||||
simplified_path.emplace_back(travel.front());
|
|
||||||
for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) {
|
|
||||||
// Skip all indexes on the same polygon
|
|
||||||
while (point_idx < travel.size() && travel[point_idx - 1].border_idx == travel[point_idx].border_idx) {
|
|
||||||
simplified_path.emplace_back(travel[point_idx]);
|
|
||||||
point_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (point_idx < travel.size()) {
|
|
||||||
const TravelPoint ¤t = travel[point_idx - 1];
|
|
||||||
const TravelPoint &next = travel[point_idx];
|
|
||||||
TravelPoint new_next = next;
|
|
||||||
size_t new_point_idx = point_idx;
|
|
||||||
double path_length = (next.point - current.point).cast<double>().norm();
|
|
||||||
double new_path_shorter_by = 0.;
|
|
||||||
size_t border_idx_change_count = 0;
|
|
||||||
std::vector<TravelPoint> shortcut;
|
|
||||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
|
||||||
const TravelPoint &possible_new_next = travel[point_idx_2];
|
|
||||||
if (travel[point_idx_2 - 1].border_idx != travel[point_idx_2].border_idx)
|
|
||||||
border_idx_change_count++;
|
|
||||||
|
|
||||||
if (border_idx_change_count >= 2)
|
|
||||||
break;
|
|
||||||
|
|
||||||
path_length += (possible_new_next.point - travel[point_idx_2 - 1].point).cast<double>().norm();
|
|
||||||
double shortcut_length = (possible_new_next.point - current.point).cast<double>().norm();
|
|
||||||
if ((path_length - shortcut_length) <= scale_(10.0))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
intersections.clear();
|
|
||||||
visitor.reset();
|
|
||||||
visitor.travel_line.a = current.point;
|
|
||||||
visitor.travel_line.b = possible_new_next.point;
|
|
||||||
visitor.transform_to_x_axis = rotation_by_direction(visitor.travel_line.vector());
|
|
||||||
edge_grid.visit_cells_intersecting_line(visitor.travel_line.a, visitor.travel_line.b, visitor);
|
|
||||||
if (!intersections.empty()) {
|
|
||||||
std::sort(intersections.begin(), intersections.end());
|
|
||||||
size_t last_border_idx_count = 0;
|
|
||||||
for (const Intersection &intersection : intersections)
|
|
||||||
if (int(intersection.border_idx) == possible_new_next.border_idx)
|
|
||||||
++last_border_idx_count;
|
|
||||||
|
|
||||||
if (last_border_idx_count > 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::vector<TravelPoint> possible_shortcut;
|
|
||||||
avoid_perimeters(boundaries, edge_grid, current.point, possible_new_next.point, false, &possible_shortcut);
|
|
||||||
double shortcut_travel = travel_length(possible_shortcut);
|
|
||||||
if (path_length > shortcut_travel && (path_length - shortcut_travel) > new_path_shorter_by) {
|
|
||||||
new_path_shorter_by = path_length - shortcut_travel;
|
|
||||||
shortcut = possible_shortcut;
|
|
||||||
new_next = possible_new_next;
|
|
||||||
new_point_idx = point_idx_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shortcut.empty()) {
|
|
||||||
assert(shortcut.size() >= 2);
|
|
||||||
simplified_path.insert(simplified_path.end(), shortcut.begin() + 1, shortcut.end() - 1);
|
|
||||||
point_idx = new_point_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
simplified_path.emplace_back(new_next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return simplified_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AvoidCrossingPerimeters::avoid_perimeters(const Polygons &boundaries,
|
|
||||||
const EdgeGrid::Grid &edge_grid,
|
|
||||||
const Point &start,
|
|
||||||
const Point &end,
|
|
||||||
const bool use_heuristics,
|
|
||||||
std::vector<TravelPoint> *result_out)
|
|
||||||
{
|
{
|
||||||
const Point direction = end - start;
|
const Point direction = end - start;
|
||||||
Matrix2d transform_to_x_axis = rotation_by_direction(direction);
|
Matrix2d transform_to_x_axis = rotation_by_direction(direction);
|
||||||
@ -608,10 +521,164 @@ size_t AvoidCrossingPerimeters::avoid_perimeters(const Polygons &bound
|
|||||||
return intersections.size();
|
return intersections.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvoidCrossingPerimeters::need_wipe(const GCode & gcodegen,
|
static std::vector<TravelPoint> simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid,
|
||||||
const Line & original_travel,
|
const std::vector<TravelPoint> &travel,
|
||||||
const Polyline &result_travel,
|
const Polygons &boundaries)
|
||||||
const size_t intersection_count)
|
{
|
||||||
|
std::vector<TravelPoint> simplified_path;
|
||||||
|
std::vector<Intersection> intersections;
|
||||||
|
AllIntersectionsVisitor visitor(edge_grid, intersections);
|
||||||
|
simplified_path.reserve(travel.size());
|
||||||
|
simplified_path.emplace_back(travel.front());
|
||||||
|
for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) {
|
||||||
|
// Skip all indexes on the same polygon
|
||||||
|
while (point_idx < travel.size() && travel[point_idx - 1].border_idx == travel[point_idx].border_idx) {
|
||||||
|
simplified_path.emplace_back(travel[point_idx]);
|
||||||
|
point_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point_idx < travel.size()) {
|
||||||
|
const TravelPoint ¤t = travel[point_idx - 1];
|
||||||
|
const TravelPoint &next = travel[point_idx];
|
||||||
|
TravelPoint new_next = next;
|
||||||
|
size_t new_point_idx = point_idx;
|
||||||
|
double path_length = (next.point - current.point).cast<double>().norm();
|
||||||
|
double new_path_shorter_by = 0.;
|
||||||
|
size_t border_idx_change_count = 0;
|
||||||
|
std::vector<TravelPoint> shortcut;
|
||||||
|
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||||
|
const TravelPoint &possible_new_next = travel[point_idx_2];
|
||||||
|
if (travel[point_idx_2 - 1].border_idx != travel[point_idx_2].border_idx)
|
||||||
|
border_idx_change_count++;
|
||||||
|
|
||||||
|
if (border_idx_change_count >= 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
path_length += (possible_new_next.point - travel[point_idx_2 - 1].point).cast<double>().norm();
|
||||||
|
double shortcut_length = (possible_new_next.point - current.point).cast<double>().norm();
|
||||||
|
if ((path_length - shortcut_length) <= scale_(10.0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
intersections.clear();
|
||||||
|
visitor.reset();
|
||||||
|
visitor.travel_line.a = current.point;
|
||||||
|
visitor.travel_line.b = possible_new_next.point;
|
||||||
|
visitor.transform_to_x_axis = rotation_by_direction(visitor.travel_line.vector());
|
||||||
|
edge_grid.visit_cells_intersecting_line(visitor.travel_line.a, visitor.travel_line.b, visitor);
|
||||||
|
if (!intersections.empty()) {
|
||||||
|
std::sort(intersections.begin(), intersections.end());
|
||||||
|
size_t last_border_idx_count = 0;
|
||||||
|
for (const Intersection &intersection : intersections)
|
||||||
|
if (int(intersection.border_idx) == possible_new_next.border_idx)
|
||||||
|
++last_border_idx_count;
|
||||||
|
|
||||||
|
if (last_border_idx_count > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<TravelPoint> possible_shortcut;
|
||||||
|
avoid_perimeters(boundaries, edge_grid, current.point, possible_new_next.point, false, &possible_shortcut);
|
||||||
|
double shortcut_travel = travel_length(possible_shortcut);
|
||||||
|
if (path_length > shortcut_travel && (path_length - shortcut_travel) > new_path_shorter_by) {
|
||||||
|
new_path_shorter_by = path_length - shortcut_travel;
|
||||||
|
shortcut = possible_shortcut;
|
||||||
|
new_next = possible_new_next;
|
||||||
|
new_point_idx = point_idx_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shortcut.empty()) {
|
||||||
|
assert(shortcut.size() >= 2);
|
||||||
|
simplified_path.insert(simplified_path.end(), shortcut.begin() + 1, shortcut.end() - 1);
|
||||||
|
point_idx = new_point_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
simplified_path.emplace_back(new_next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return simplified_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<TravelPoint> simplify_travel(const EdgeGrid::Grid &edge_grid,
|
||||||
|
const std::vector<TravelPoint> &travel,
|
||||||
|
const Polygons &boundaries,
|
||||||
|
const bool use_heuristics)
|
||||||
|
{
|
||||||
|
struct Visitor
|
||||||
|
{
|
||||||
|
Visitor(const EdgeGrid::Grid &grid) : grid(grid) {}
|
||||||
|
|
||||||
|
bool operator()(coord_t iy, coord_t ix)
|
||||||
|
{
|
||||||
|
assert(pt_current != nullptr);
|
||||||
|
assert(pt_next != nullptr);
|
||||||
|
// Called with a row and colum of the grid cell, which is intersected by a line.
|
||||||
|
auto cell_data_range = grid.cell_data_range(iy, ix);
|
||||||
|
this->intersect = false;
|
||||||
|
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) {
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
auto segment = grid.segment(*it_contour_and_segment);
|
||||||
|
if (Geometry::segments_intersect(segment.first, segment.second, *pt_current, *pt_next)) {
|
||||||
|
this->intersect = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Continue traversing the grid along the edge.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EdgeGrid::Grid &grid;
|
||||||
|
const Slic3r::Point *pt_current = nullptr;
|
||||||
|
const Slic3r::Point *pt_next = nullptr;
|
||||||
|
bool intersect = false;
|
||||||
|
} visitor(edge_grid);
|
||||||
|
|
||||||
|
std::vector<TravelPoint> simplified_path;
|
||||||
|
simplified_path.reserve(travel.size());
|
||||||
|
simplified_path.emplace_back(travel.front());
|
||||||
|
|
||||||
|
// Try to skip some points in the path.
|
||||||
|
for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) {
|
||||||
|
const Point ¤t_point = travel[point_idx - 1].point;
|
||||||
|
TravelPoint next = travel[point_idx];
|
||||||
|
|
||||||
|
visitor.pt_current = ¤t_point;
|
||||||
|
|
||||||
|
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||||
|
if (travel[point_idx_2].point == current_point) {
|
||||||
|
next = travel[point_idx_2];
|
||||||
|
point_idx = point_idx_2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.pt_next = &travel[point_idx_2].point;
|
||||||
|
edge_grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||||
|
// Check if deleting point causes crossing a boundary
|
||||||
|
if (!visitor.intersect) {
|
||||||
|
next = travel[point_idx_2];
|
||||||
|
point_idx = point_idx_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simplified_path.emplace_back(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(use_heuristics) {
|
||||||
|
simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries);
|
||||||
|
std::reverse(simplified_path.begin(),simplified_path.end());
|
||||||
|
simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries);
|
||||||
|
std::reverse(simplified_path.begin(),simplified_path.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return simplified_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool need_wipe(const GCode &gcodegen,
|
||||||
|
const ExPolygons &slice,
|
||||||
|
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 z_lift_enabled = gcodegen.config().retract_lift.get_at(gcodegen.writer().extruder()->id()) > 0.;
|
||||||
bool wipe_needed = false;
|
bool wipe_needed = false;
|
||||||
@ -622,19 +689,19 @@ bool AvoidCrossingPerimeters::need_wipe(const GCode & gcodegen,
|
|||||||
// The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
|
// 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 the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
|
||||||
if (z_lift_enabled) {
|
if (z_lift_enabled) {
|
||||||
if (any_expolygon_contains(m_slice, original_travel)) {
|
if (any_expolygon_contains(slice, original_travel)) {
|
||||||
// Check if original_travel and result_travel are not same.
|
// Check if original_travel and result_travel are not same.
|
||||||
// If both are the same, then it is possible to skip testing of result_travel
|
// If both are the same, then it is possible to skip testing of result_travel
|
||||||
if (result_travel.size() == 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) {
|
if (result_travel.size() == 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) {
|
||||||
wipe_needed = false;
|
wipe_needed = false;
|
||||||
} else {
|
} else {
|
||||||
wipe_needed = !any_expolygon_contains(m_slice, result_travel);
|
wipe_needed = !any_expolygon_contains(slice, result_travel);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wipe_needed = true;
|
wipe_needed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wipe_needed = !any_expolygon_contains(m_slice, result_travel);
|
wipe_needed = !any_expolygon_contains(slice, result_travel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,7 +713,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||||||
{
|
{
|
||||||
// If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset).
|
// 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.
|
// Otherwise perform the path planning in the coordinate system of the active object.
|
||||||
bool use_external = this->use_external_mp || this->use_external_mp_once;
|
bool use_external = m_use_external_mp || m_use_external_mp_once;
|
||||||
Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
|
Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
|
||||||
Point start = gcodegen.last_pos() + scaled_origin;
|
Point start = gcodegen.last_pos() + scaled_origin;
|
||||||
Point end = point + scaled_origin;
|
Point end = point + scaled_origin;
|
||||||
@ -659,9 +726,9 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||||||
std::vector<TravelPoint> result;
|
std::vector<TravelPoint> result;
|
||||||
auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end);
|
auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end);
|
||||||
if (use_external)
|
if (use_external)
|
||||||
travel_intersection_count = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, true, &result);
|
travel_intersection_count = avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, true, &result);
|
||||||
else
|
else
|
||||||
travel_intersection_count = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, true, &result);
|
travel_intersection_count = avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, true, &result);
|
||||||
|
|
||||||
result_pl = to_polyline(result);
|
result_pl = to_polyline(result);
|
||||||
}
|
}
|
||||||
@ -678,7 +745,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||||||
result_pl.translate(-scaled_origin);
|
result_pl.translate(-scaled_origin);
|
||||||
*could_be_wipe_disabled = false;
|
*could_be_wipe_disabled = false;
|
||||||
} else
|
} else
|
||||||
*could_be_wipe_disabled = !need_wipe(gcodegen, travel, result_pl, travel_intersection_count);
|
*could_be_wipe_disabled = !need_wipe(gcodegen, m_slice, travel, result_pl, travel_intersection_count);
|
||||||
|
|
||||||
return result_pl;
|
return result_pl;
|
||||||
}
|
}
|
||||||
|
@ -5,120 +5,40 @@
|
|||||||
#include "../ExPolygon.hpp"
|
#include "../ExPolygon.hpp"
|
||||||
#include "../EdgeGrid.hpp"
|
#include "../EdgeGrid.hpp"
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
// Forward declarations.
|
// Forward declarations.
|
||||||
class GCode;
|
class GCode;
|
||||||
class Layer;
|
class Layer;
|
||||||
class Point;
|
class Point;
|
||||||
class Print;
|
|
||||||
class PrintObject;
|
|
||||||
|
|
||||||
struct PrintInstance;
|
|
||||||
using PrintObjectPtrs = std::vector<PrintObject *>;
|
|
||||||
|
|
||||||
class AvoidCrossingPerimeters
|
class AvoidCrossingPerimeters
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Intersection
|
// Routing around the objects vs. inside a single object.
|
||||||
|
void use_external_mp(bool use = true) { m_use_external_mp = use; };
|
||||||
|
void use_external_mp_once() { m_use_external_mp_once = true; }
|
||||||
|
void disable_once() { m_disabled_once = true; }
|
||||||
|
bool disabled_once() const { return m_disabled_once; }
|
||||||
|
void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; }
|
||||||
|
|
||||||
|
void init_layer(const Layer &layer);
|
||||||
|
|
||||||
|
Polyline travel_to(const GCode& gcodegen, const Point& point)
|
||||||
{
|
{
|
||||||
// Index of the polygon containing this point of intersection.
|
bool could_be_wipe_disabled;
|
||||||
size_t border_idx;
|
return this->travel_to(gcodegen, point, &could_be_wipe_disabled);
|
||||||
// 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)
|
Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled);
|
||||||
: border_idx(border_idx), line_idx(line_idx), point_transformed(point_transformed), point(point){};
|
|
||||||
|
|
||||||
inline bool operator<(const Intersection &other) const { return this->point_transformed.x() < other.point_transformed.x(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TravelPoint
|
|
||||||
{
|
|
||||||
Point point;
|
|
||||||
// Index of the polygon containing this point. A negative value indicates that the point is not on any border
|
|
||||||
int border_idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AllIntersectionsVisitor
|
|
||||||
{
|
|
||||||
AllIntersectionsVisitor(const EdgeGrid::Grid &grid, std::vector<AvoidCrossingPerimeters::Intersection> &intersections)
|
|
||||||
: grid(grid), intersections(intersections)
|
|
||||||
{}
|
|
||||||
|
|
||||||
AllIntersectionsVisitor(const EdgeGrid::Grid &grid,
|
|
||||||
std::vector<AvoidCrossingPerimeters::Intersection> &intersections,
|
|
||||||
const Matrix2d &transform_to_x_axis,
|
|
||||||
const Line &travel_line)
|
|
||||||
: grid(grid), intersections(intersections), transform_to_x_axis(transform_to_x_axis), travel_line(travel_line)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
intersection_set.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(coord_t iy, coord_t ix)
|
|
||||||
{
|
|
||||||
// Called with a row and colum of the grid cell, which is intersected by a line.
|
|
||||||
auto cell_data_range = grid.cell_data_range(iy, ix);
|
|
||||||
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second;
|
|
||||||
++it_contour_and_segment) {
|
|
||||||
// End points of the line segment and their vector.
|
|
||||||
auto segment = grid.segment(*it_contour_and_segment);
|
|
||||||
|
|
||||||
Point intersection_point;
|
|
||||||
if (travel_line.intersection(Line(segment.first, segment.second), &intersection_point) &&
|
|
||||||
intersection_set.find(*it_contour_and_segment) == intersection_set.end()) {
|
|
||||||
intersections.emplace_back(it_contour_and_segment->first, it_contour_and_segment->second,
|
|
||||||
(transform_to_x_axis * intersection_point.cast<double>()).cast<coord_t>(), intersection_point);
|
|
||||||
intersection_set.insert(*it_contour_and_segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Continue traversing the grid along the edge.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EdgeGrid::Grid &grid;
|
|
||||||
std::vector<AvoidCrossingPerimeters::Intersection> &intersections;
|
|
||||||
Matrix2d transform_to_x_axis;
|
|
||||||
Line travel_line;
|
|
||||||
std::unordered_set<std::pair<size_t, size_t>, boost::hash<std::pair<size_t, size_t>>> intersection_set;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Direction { Forward, Backward };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ExPolygons get_boundary(const Layer &layer);
|
bool m_use_external_mp { false };
|
||||||
|
// just for the next travel move
|
||||||
static ExPolygons get_boundary_external(const Layer &layer);
|
bool m_use_external_mp_once { false };
|
||||||
|
// this flag disables avoid_crossing_perimeters just for the next travel move
|
||||||
static Direction get_shortest_direction(
|
// we enable it by default for the first travel move in print
|
||||||
const Lines &lines, const size_t start_idx, const size_t end_idx, const Point &intersection_first, const Point &intersection_last);
|
bool m_disabled_once { true };
|
||||||
|
|
||||||
static std::vector<AvoidCrossingPerimeters::TravelPoint> simplify_travel(const EdgeGrid::Grid &edge_grid,
|
|
||||||
const std::vector<TravelPoint> &travel,
|
|
||||||
const Polygons &boundaries,
|
|
||||||
const bool use_heuristics);
|
|
||||||
|
|
||||||
static std::vector<AvoidCrossingPerimeters::TravelPoint> simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid,
|
|
||||||
const std::vector<TravelPoint> &travel,
|
|
||||||
const Polygons &boundaries);
|
|
||||||
|
|
||||||
static size_t avoid_perimeters(const Polygons &boundaries,
|
|
||||||
const EdgeGrid::Grid &grid,
|
|
||||||
const Point &start,
|
|
||||||
const Point &end,
|
|
||||||
const bool use_heuristics,
|
|
||||||
std::vector<TravelPoint> *result_out);
|
|
||||||
|
|
||||||
bool need_wipe(const GCode &gcodegen, const Line &original_travel, const Polyline &result_travel, const size_t intersection_count);
|
|
||||||
|
|
||||||
// Slice of layer with elephant foot compensation
|
// Slice of layer with elephant foot compensation
|
||||||
ExPolygons m_slice;
|
ExPolygons m_slice;
|
||||||
@ -132,27 +52,6 @@ private:
|
|||||||
BoundingBox m_bbox_external;
|
BoundingBox m_bbox_external;
|
||||||
EdgeGrid::Grid m_grid;
|
EdgeGrid::Grid m_grid;
|
||||||
EdgeGrid::Grid m_grid_external;
|
EdgeGrid::Grid m_grid_external;
|
||||||
|
|
||||||
public:
|
|
||||||
// this flag triggers the use of the external configuration space
|
|
||||||
bool use_external_mp { false };
|
|
||||||
// just for the next travel move
|
|
||||||
bool use_external_mp_once { false };
|
|
||||||
// this flag disables avoid_crossing_perimeters just for the next travel move
|
|
||||||
// we enable it by default for the first travel move in print
|
|
||||||
bool disable_once { true };
|
|
||||||
|
|
||||||
AvoidCrossingPerimeters() = default;
|
|
||||||
|
|
||||||
Polyline travel_to(const GCode& gcodegen, const Point& point)
|
|
||||||
{
|
|
||||||
bool could_be_wipe_disabled;
|
|
||||||
return this->travel_to(gcodegen, point, &could_be_wipe_disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled);
|
|
||||||
|
|
||||||
void init_layer(const Layer &layer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
Loading…
x
Reference in New Issue
Block a user