Fix of G-code path planning: Infill lines were incorrectly ordered

for islands with another islands in their holes.

Improvement of chaining of infill lines for 3D honeycomb, Gyroid and
Honeycomb infill: New TSP chaining algorithm is used.
This commit is contained in:
bubnikv 2019-09-27 09:51:07 +02:00
parent d06831076d
commit e65ab90c16
9 changed files with 58 additions and 46 deletions

View File

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "Fill3DHoneycomb.hpp" #include "Fill3DHoneycomb.hpp"
@ -175,27 +175,24 @@ void Fill3DHoneycomb::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;
const Point &first_point = it_polyline->points.front(); const Point &first_point = polyline.points.front();
const Point &last_point = pts_end.back(); const Point &last_point = pts_end.back();
// TODO: we should also check that both points are on a fill_boundary to avoid // TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions // connecting paths on the boundaries of internal regions
if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance && if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&
expolygon_off.contains(Line(last_point, first_point))) { expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline. // Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
continue; continue;
} }
} }
// The lines cannot be connected. // The lines cannot be connected.
polylines_out.emplace_back(std::move(*it_polyline)); polylines_out.emplace_back(std::move(polyline));
first = false; first = false;
} }
} }

View File

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
@ -166,11 +166,8 @@ void FillGyroid::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polyline &polyline : chained) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;

View File

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "FillHoneycomb.hpp" #include "FillHoneycomb.hpp"
@ -93,22 +93,20 @@ void FillHoneycomb::_fill_surface_single(
// connect paths // connect paths
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = PolylineCollection::chained_path_from( Polylines chained = chain_infill_polylines(std::move(paths));
std::move(paths),
PolylineCollection::leftmost_point(paths), false);
assert(paths.empty()); assert(paths.empty());
paths.clear(); paths.clear();
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { for (Polyline &path : chained) {
if (! paths.empty()) { if (! paths.empty()) {
// distance between first point of this path and last point of last path // distance between first point of this path and last point of last path
double distance = (it_path->first_point() - paths.back().last_point()).cast<double>().norm(); double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
if (distance <= m.hex_width) { if (distance <= m.hex_width) {
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
continue; continue;
} }
} }
// Don't connect the paths. // Don't connect the paths.
paths.push_back(*it_path); paths.push_back(std::move(path));
} }
} }

View File

@ -1,6 +1,6 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp" #include "../ExPolygon.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "FillRectilinear.hpp" #include "FillRectilinear.hpp"
@ -92,15 +92,12 @@ void FillRectilinear::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;
const Point &first_point = it_polyline->points.front(); const Point &first_point = polyline.points.front();
const Point &last_point = pts_end.back(); const Point &last_point = pts_end.back();
// Distance in X, Y. // Distance in X, Y.
const Vector distance = last_point - first_point; const Vector distance = last_point - first_point;
@ -109,12 +106,12 @@ void FillRectilinear::_fill_surface_single(
if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
expolygon_off.contains(Line(last_point, first_point))) { expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline. // Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
continue; continue;
} }
} }
// The lines cannot be connected. // The lines cannot be connected.
polylines_out.emplace_back(std::move(*it_polyline)); polylines_out.emplace_back(std::move(polyline));
first = false; first = false;
} }
} }

View File

@ -1807,6 +1807,17 @@ void GCode::process_layer(
layer_surface_bboxes.reserve(n_slices); layer_surface_bboxes.reserve(n_slices);
for (const ExPolygon &expoly : layer.slices.expolygons) for (const ExPolygon &expoly : layer.slices.expolygons)
layer_surface_bboxes.push_back(get_extents(expoly.contour)); layer_surface_bboxes.push_back(get_extents(expoly.contour));
// Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
// so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
std::vector<size_t> slices_test_order;
slices_test_order.reserve(n_slices);
for (size_t i = 0; i < n_slices; ++ i)
slices_test_order.emplace_back(i);
std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) {
const Vec2d s1 = layer_surface_bboxes[i].size().cast<double>();
const Vec2d s2 = layer_surface_bboxes[j].size().cast<double>();
return s1.x() * s1.y() < s2.x() * s2.y();
});
auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) {
const BoundingBox &bbox = layer_surface_bboxes[i]; const BoundingBox &bbox = layer_surface_bboxes[i];
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
@ -1854,20 +1865,23 @@ void GCode::process_layer(
extruder, extruder,
&layer_to_print - layers.data(), &layer_to_print - layers.data(),
layers.size(), n_slices+1); layers.size(), n_slices+1);
for (size_t i = 0; i <= n_slices; ++i) for (size_t i = 0; i <= n_slices; ++ i) {
bool last = i == n_slices;
size_t island_idx = last ? n_slices : slices_test_order[i];
if (// fill->first_point does not fit inside any slice if (// fill->first_point does not fit inside any slice
i == n_slices || last ||
// fill->first_point fits inside ith slice // fill->first_point fits inside ith slice
point_inside_surface(i, fill->first_point())) { point_inside_surface(island_idx, fill->first_point())) {
if (islands[i].by_region.empty()) if (islands[island_idx].by_region.empty())
islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
break; break;
} }
} }
} }
} }
} }
}
} // for regions } // for regions
} }
} // for objects } // for objects
@ -2574,12 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
std::string gcode; std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) { for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions()[&region - &by_region.front()]->config()); m_config.apply(print.regions()[&region - &by_region.front()]->config());
ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
for (ExtrusionEntity *fill : chained.entities) {
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill); auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
if (eec) { if (eec) {
ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false); for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
for (ExtrusionEntity *ee : chained2.entities)
gcode += this->extrude_entity(*ee, "infill"); gcode += this->extrude_entity(*ee, "infill");
} else } else
gcode += this->extrude_entity(*fill, "infill"); gcode += this->extrude_entity(*fill, "infill");

View File

@ -23,12 +23,6 @@ Polyline::operator Line() const
return Line(this->points.front(), this->points.back()); return Line(this->points.front(), this->points.back());
} }
Point
Polyline::last_point() const
{
return this->points.back();
}
Point Point
Polyline::leftmost_point() const Polyline::leftmost_point() const
{ {

View File

@ -62,7 +62,8 @@ public:
operator Polylines() const; operator Polylines() const;
operator Line() const; operator Line() const;
Point last_point() const; Point last_point() const override { return this->points.back(); }
Point leftmost_point() const; Point leftmost_point() const;
virtual Lines lines() const; virtual Lines lines() const;
void clip_end(double distance); void clip_end(double distance);

View File

@ -339,6 +339,20 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
return out; return out;
} }
Polylines chain_infill_polylines(Polylines &polylines)
{
auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
Polylines out;
out.reserve(polylines.size());
for (auto &segment_and_reversal : ordered) {
out.emplace_back(std::move(polylines[segment_and_reversal.first]));
if (segment_and_reversal.second)
out.back().reverse();
}
return out;
}
template<class T> static inline T chain_path_items(const Points &points, const T &items) template<class T> static inline T chain_path_items(const Points &points, const T &items)
{ {
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; }; auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };

View File

@ -18,6 +18,8 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain); void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr); void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
Polylines chain_infill_polylines(Polylines &src);
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items); std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
// Chain instances of print objects by an approximate shortest path. // Chain instances of print objects by an approximate shortest path.