1) New algorithm for connecting along the perimeters is now applied
   to Honeycomb, Hilbert and similar planar filling curves.
2) The old expensive path chaining is not applied if the new algorithm
   to connect along the perimeter lines is called afterwards.
This commit is contained in:
Vojtech Bubnik 2020-11-16 12:51:51 +01:00
parent e9fa36ea7d
commit 812cbade4d
7 changed files with 69 additions and 133 deletions

View file

@ -162,15 +162,13 @@ void Fill3DHoneycomb::_fill_surface_single(
pl.translate(bb.min); pl.translate(bb.min);
// clip pattern to boundaries, chain the clipped polylines // clip pattern to boundaries, chain the clipped polylines
Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon))); polylines = intersection_pl(polylines, to_polygons(expolygon));
// connect lines if needed // connect lines if needed
if (! polylines_chained.empty()) { if (params.dont_connect || polylines.size() <= 1)
if (params.dont_connect) append(polylines_out, chain_polylines(std::move(polylines)));
append(polylines_out, std::move(polylines_chained)); else
else this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, this->spacing, params);
}
} }
} // namespace Slic3r } // namespace Slic3r

View file

@ -1333,9 +1333,9 @@ void Filler::_fill_surface_single(
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */ #endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
if (params.dont_connect || all_polylines_with_hooks.size() <= 1) if (params.dont_connect || all_polylines_with_hooks.size() <= 1)
append(polylines_out, std::move(all_polylines_with_hooks)); append(polylines_out, chain_polylines(std::move(all_polylines_with_hooks)));
else else
connect_infill(chain_polylines(std::move(all_polylines_with_hooks)), expolygon, polylines_out, this->spacing, params); connect_infill(std::move(all_polylines_with_hooks), expolygon, polylines_out, this->spacing, params);
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT #ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
{ {

View file

@ -190,11 +190,10 @@ void FillGyroid::_fill_surface_single(
polylines.end()); polylines.end());
if (! polylines.empty()) { if (! polylines.empty()) {
polylines = chain_polylines(polylines);
// connect lines // connect lines
size_t polylines_out_first_idx = polylines_out.size(); size_t polylines_out_first_idx = polylines_out.size();
if (params.dont_connect) if (params.dont_connect)
append(polylines_out, std::move(polylines)); append(polylines_out, chain_polylines(polylines));
else else
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params); this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);

View file

@ -18,21 +18,21 @@ void FillHoneycomb::_fill_surface_single(
Cache::iterator it_m = this->cache.find(cache_id); Cache::iterator it_m = this->cache.find(cache_id);
if (it_m == this->cache.end()) { if (it_m == this->cache.end()) {
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData())); it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
CacheData &m = it_m->second; CacheData &m = it_m->second;
coord_t min_spacing = scale_(this->spacing); coord_t min_spacing = coord_t(scale_(this->spacing));
m.distance = min_spacing / params.density; m.distance = coord_t(min_spacing / params.density);
m.hex_side = m.distance / (sqrt(3)/2); m.hex_side = coord_t(m.distance / (sqrt(3)/2));
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
coord_t hex_height = m.hex_side * 2; coord_t hex_height = m.hex_side * 2;
m.pattern_height = hex_height + m.hex_side; m.pattern_height = hex_height + m.hex_side;
m.y_short = m.distance * sqrt(3)/3; m.y_short = coord_t(m.distance * sqrt(3)/3);
m.x_offset = min_spacing / 2; m.x_offset = min_spacing / 2;
m.y_offset = m.x_offset * sqrt(3)/3; m.y_offset = coord_t(m.x_offset * sqrt(3)/3);
m.hex_center = Point(m.hex_width/2, m.hex_side); m.hex_center = Point(m.hex_width/2, m.hex_side);
} }
CacheData &m = it_m->second; CacheData &m = it_m->second;
Polygons polygons; Polylines all_polylines;
{ {
// adjust actual bounding box to the nearest multiple of our hex pattern // adjust actual bounding box to the nearest multiple of our hex pattern
// and align it so that it matches across layers // and align it so that it matches across layers
@ -52,7 +52,7 @@ void FillHoneycomb::_fill_surface_single(
coord_t x = bounding_box.min(0); coord_t x = bounding_box.min(0);
while (x <= bounding_box.max(0)) { while (x <= bounding_box.max(0)) {
Polygon p; Polyline p;
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset }; coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
for (size_t i = 0; i < 2; ++ i) { for (size_t i = 0; i < 2; ++ i) {
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
@ -69,55 +69,15 @@ void FillHoneycomb::_fill_surface_single(
x += m.distance; x += m.distance;
} }
p.rotate(-direction.first, m.hex_center); p.rotate(-direction.first, m.hex_center);
polygons.push_back(p); all_polylines.push_back(p);
} }
} }
if (params.complete || true) { all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
// we were requested to complete each loop; if (params.dont_connect || all_polylines.size() <= 1)
// in this case we don't try to make more continuous paths append(polylines_out, chain_polylines(std::move(all_polylines)));
Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons); else
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it) connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params);
polylines_out.push_back(it->split_at_first_point());
} else {
// consider polygons as polylines without re-appending the initial point:
// this cuts the last segment on purpose, so that the jump to the next
// path is more straight
Polylines paths;
{
Polylines p;
for (Polygon &poly : polygons)
p.emplace_back(poly.points);
paths = intersection_pl(p, to_polygons(expolygon));
}
// connect paths
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = chain_polylines(std::move(paths));
assert(paths.empty());
paths.clear();
for (Polyline &path : chained) {
if (! paths.empty()) {
// distance between first point of this path and last point of last path
double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
if (distance <= m.hex_width) {
paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
continue;
}
}
// Don't connect the paths.
paths.push_back(std::move(path));
}
}
// clip paths again to prevent connection segments from crossing the expolygon boundaries
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
// Move the polylines to the output, avoid a deep copy.
size_t j = polylines_out.size();
polylines_out.resize(j + paths.size(), Polyline());
for (size_t i = 0; i < paths.size(); ++ i)
std::swap(polylines_out[j ++], paths[i]);
}
} }
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,4 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "FillPlanePath.hpp" #include "FillPlanePath.hpp"
@ -23,14 +24,14 @@ void FillPlanePath::_fill_surface_single(
Point shift = this->_centered() ? Point shift = this->_centered() ?
bounding_box.center() : bounding_box.center() :
bounding_box.min; bounding_box.min;
expolygon.translate(-shift(0), -shift(1)); expolygon.translate(-shift.x(), -shift.y());
bounding_box.translate(-shift(0), -shift(1)); bounding_box.translate(-shift.x(), -shift.y());
Pointfs pts = _generate( Pointfs pts = _generate(
coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)), coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)), coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)), coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines))); coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)));
Polylines polylines; Polylines polylines;
if (pts.size() >= 2) { if (pts.size() >= 2) {
@ -38,39 +39,24 @@ void FillPlanePath::_fill_surface_single(
polylines.push_back(Polyline()); polylines.push_back(Polyline());
Polyline &polyline = polylines.back(); Polyline &polyline = polylines.back();
polyline.points.reserve(pts.size()); polyline.points.reserve(pts.size());
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) for (const Vec2d &pt : pts)
polyline.points.push_back(Point( polyline.points.push_back(Point(
coord_t(floor((*it)(0) * distance_between_lines + 0.5)), coord_t(floor(pt.x() * distance_between_lines + 0.5)),
coord_t(floor((*it)(1) * distance_between_lines + 0.5)))); coord_t(floor(pt.y() * distance_between_lines + 0.5))));
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines); // intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
polylines = intersection_pl(polylines, to_polygons(expolygon)); polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
Polylines chained;
/* if (params.dont_connect || params.density > 0.5 || polylines.size() <= 1)
if (1) { chained = chain_polylines(std::move(polylines));
require "Slic3r/SVG.pm"; else
print "Writing fill.svg\n"; connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
Slic3r::SVG::output("fill.svg",
no_arrows => 1,
polygons => \@$expolygon,
green_polygons => [ $bounding_box->polygon ],
polylines => [ $polyline ],
red_polylines => \@paths,
);
}
*/
// paths must be repositioned and rotated back // paths must be repositioned and rotated back
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { for (Polyline &pl : chained) {
it->translate(shift(0), shift(1)); pl.translate(shift.x(), shift.y());
it->rotate(direction.first); pl.rotate(direction.first);
} }
append(polylines_out, std::move(chained));
} }
// Move the polylines to the output, avoid a deep copy.
size_t j = polylines_out.size();
polylines_out.resize(j + polylines.size(), Polyline());
for (size_t i = 0; i < polylines.size(); ++ i)
std::swap(polylines_out[j ++], polylines[i]);
} }
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta // Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
@ -85,13 +71,13 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
coordf_t r = 1; coordf_t r = 1;
Pointfs out; Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center. //FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
out.push_back(Vec2d(0, 0)); out.emplace_back(0, 0);
out.push_back(Vec2d(1, 0)); out.emplace_back(1, 0);
while (r < rmax) { while (r < rmax) {
// Discretization angle to achieve a discretization error lower than RESOLUTION. // Discretization angle to achieve a discretization error lower than RESOLUTION.
theta += 2. * acos(1. - RESOLUTION / r); theta += 2. * acos(1. - RESOLUTION / r);
r = a + b * theta; r = a + b * theta;
out.push_back(Vec2d(r * cos(theta), r * sin(theta))); out.emplace_back(r * cos(theta), r * sin(theta));
} }
return out; return out;
} }
@ -128,15 +114,12 @@ static inline Point hilbert_n_to_xy(const size_t n)
++ ndigits; ++ ndigits;
} }
} }
int state = (ndigits & 1) ? 4 : 0; int state = (ndigits & 1) ? 4 : 0;
// int dirstate = (ndigits & 1) ? 0 : 4;
coord_t x = 0; coord_t x = 0;
coord_t y = 0; coord_t y = 0;
for (int i = (int)ndigits - 1; i >= 0; -- i) { for (int i = (int)ndigits - 1; i >= 0; -- i) {
int digit = (n >> (i * 2)) & 3; int digit = (n >> (i * 2)) & 3;
state += digit; state += digit;
// if (digit != 3)
// dirstate = state; // lowest non-3 digit
x |= digit_to_x[state] << i; x |= digit_to_x[state] << i;
y |= digit_to_y[state] << i; y |= digit_to_y[state] << i;
state = next_state[state]; state = next_state[state];
@ -162,7 +145,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
line.reserve(sz2); line.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) { for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i); Point p = hilbert_n_to_xy(i);
line.push_back(Vec2d(p(0) + min_x, p(1) + min_y)); line.emplace_back(p.x() + min_x, p.y() + min_y);
} }
return line; return line;
} }
@ -175,27 +158,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_
coordf_t r = 0; coordf_t r = 0;
coordf_t r_inc = sqrt(2.); coordf_t r_inc = sqrt(2.);
Pointfs out; Pointfs out;
out.push_back(Vec2d(0, 0)); out.emplace_back(0., 0.);
while (r < rmax) { while (r < rmax) {
r += r_inc; r += r_inc;
coordf_t rx = r / sqrt(2.); coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx; coordf_t r2 = r + rx;
out.push_back(Vec2d( r, 0.)); out.emplace_back( r, 0.);
out.push_back(Vec2d( r2, rx)); out.emplace_back( r2, rx);
out.push_back(Vec2d( rx, rx)); out.emplace_back( rx, rx);
out.push_back(Vec2d( rx, r2)); out.emplace_back( rx, r2);
out.push_back(Vec2d(0., r)); out.emplace_back( 0., r);
out.push_back(Vec2d(-rx, r2)); out.emplace_back(-rx, r2);
out.push_back(Vec2d(-rx, rx)); out.emplace_back(-rx, rx);
out.push_back(Vec2d(-r2, rx)); out.emplace_back(-r2, rx);
out.push_back(Vec2d(-r, 0.)); out.emplace_back(- r, 0.);
out.push_back(Vec2d(-r2, -rx)); out.emplace_back(-r2, -rx);
out.push_back(Vec2d(-rx, -rx)); out.emplace_back(-rx, -rx);
out.push_back(Vec2d(-rx, -r2)); out.emplace_back(-rx, -r2);
out.push_back(Vec2d(0., -r)); out.emplace_back( 0., -r);
out.push_back(Vec2d( rx, -r2)); out.emplace_back( rx, -r2);
out.push_back(Vec2d( rx, -rx)); out.emplace_back( rx, -rx);
out.push_back(Vec2d( r2+r_inc, -rx)); out.emplace_back( r2+r_inc, -rx);
} }
return out; return out;
} }

View file

@ -2324,7 +2324,6 @@ static inline void fill_expolygons_generate_paths(
{ {
FillParams fill_params; FillParams fill_params;
fill_params.density = density; fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true; fill_params.dont_adjust = true;
for (const ExPolygon &expoly : expolygons) { for (const ExPolygon &expoly : expolygons) {
Surface surface(stInternal, expoly); Surface surface(stInternal, expoly);
@ -2351,7 +2350,6 @@ static inline void fill_expolygons_generate_paths(
{ {
FillParams fill_params; FillParams fill_params;
fill_params.density = density; fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true; fill_params.dont_adjust = true;
for (ExPolygon &expoly : expolygons) { for (ExPolygon &expoly : expolygons) {
Surface surface(stInternal, std::move(expoly)); Surface surface(stInternal, std::move(expoly));

View file

@ -36,8 +36,6 @@
%code{% THIS->params.density = density; %}; %code{% THIS->params.density = density; %};
void set_dont_adjust(bool dont_adjust) void set_dont_adjust(bool dont_adjust)
%code{% THIS->params.dont_adjust = dont_adjust; %}; %code{% THIS->params.dont_adjust = dont_adjust; %};
void set_complete(bool complete)
%code{% THIS->params.complete = complete; %};
PolylineCollection* _fill_surface(Surface *surface) PolylineCollection* _fill_surface(Surface *surface)
%code{% %code{%