mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-15 02:05:57 +08:00
concentric infill : now with gapfill
This commit is contained in:
parent
792627a95d
commit
60442badc2
@ -303,6 +303,18 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines
|
||||
polylines.clear();
|
||||
}
|
||||
|
||||
inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &loops, ExtrusionRole role, double mm3_per_mm, float width, float height) {
|
||||
dst.reserve(dst.size() + loops.size());
|
||||
for (Polygon &poly : loops) {
|
||||
if (poly.is_valid()) {
|
||||
ExtrusionPath path(role, mm3_per_mm, width, height);
|
||||
path.polyline.points = poly.points;
|
||||
path.polyline.points.push_back(path.polyline.points.front());
|
||||
dst.emplace_back(new ExtrusionLoop(std::move(path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + loops.size());
|
||||
|
@ -1,64 +1,105 @@
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
#include "../ExtrusionEntityCollection.hpp"
|
||||
#include "../MedialAxis.hpp"
|
||||
|
||||
#include "FillConcentric.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillConcentric::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
coord_t distance = coord_t(min_spacing / params.density);
|
||||
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
distance = this->_adjust_solid_spacing(bounding_box.size().x, distance);
|
||||
this->spacing = unscale(distance);
|
||||
}
|
||||
void FillConcentric::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms,
|
||||
const Flow &flow, const ExtrusionRole &role, ExtrusionEntitiesPtr &out) {
|
||||
|
||||
Polygons loops = (Polygons)expolygon;
|
||||
Polygons last = loops;
|
||||
while (! last.empty()) {
|
||||
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
loops.insert(loops.end(), last.begin(), last.end());
|
||||
}
|
||||
// Perform offset.
|
||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(0 - 0.5 * this->spacing)));
|
||||
// Create the infills for each of the regions.
|
||||
Polylines polylines_out;
|
||||
for (size_t i = 0; i < expp.size(); ++i) {
|
||||
//_fill_surface_single(
|
||||
//params,
|
||||
//surface->thickness_layers,
|
||||
//_infill_direction(surface),
|
||||
//expp[i],
|
||||
//polylines_out);
|
||||
ExPolygon expolygon = expp[i];
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
loops = union_pt_chained(loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
Point last_pos(0, 0);
|
||||
for (const Polygon &loop : loops) {
|
||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
|
||||
last_pos = polylines_out.back().last_point();
|
||||
}
|
||||
coordf_t init_spacing = this->spacing;
|
||||
|
||||
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
||||
// Keep valid paths only.
|
||||
size_t j = iPathFirst;
|
||||
for (size_t i = iPathFirst; i < polylines_out.size(); ++ i) {
|
||||
polylines_out[i].clip_end(this->loop_clipping);
|
||||
if (polylines_out[i].is_valid()) {
|
||||
if (j < i)
|
||||
polylines_out[j] = std::move(polylines_out[i]);
|
||||
++ j;
|
||||
// no rotation is supported for this infill pattern
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
coord_t distance = coord_t(min_spacing / params.density);
|
||||
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
distance = this->_adjust_solid_spacing(bounding_box.size().x, distance);
|
||||
this->spacing = unscale(distance);
|
||||
}
|
||||
|
||||
ExPolygons gaps;
|
||||
Polygons loops = (Polygons)expolygon;
|
||||
Polygons last = loops;
|
||||
while (!last.empty()) {
|
||||
Polygons next_onion = offset2(last, -(distance + min_spacing / 2), +min_spacing / 2);
|
||||
loops.insert(loops.end(), next_onion.begin(), next_onion.end());
|
||||
append(gaps, diff_ex(
|
||||
offset(last, -0.5f * distance),
|
||||
offset(next_onion, 0.5f * distance + 10))); // safety offset
|
||||
last = next_onion;
|
||||
}
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
//note: useless if we don't apply no_sort flag
|
||||
//loops = union_pt_chained(loops, false);
|
||||
|
||||
|
||||
//get the role
|
||||
ExtrusionRole good_role = role;
|
||||
if (good_role == erNone || good_role == erCustom) {
|
||||
good_role = (flow.bridge ? erBridgeInfill :
|
||||
(surface->is_solid() ?
|
||||
((surface->is_top()) ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill));
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection();
|
||||
coll_nosort->no_sort = true; //can be sorted inside the pass
|
||||
extrusion_entities_append_loops(
|
||||
coll_nosort->entities, loops,
|
||||
good_role,
|
||||
flow.mm3_per_mm() * params.flow_mult,
|
||||
flow.width * params.flow_mult,
|
||||
flow.height);
|
||||
|
||||
//add gapfills
|
||||
if (!gaps.empty() && params.density >= 1) {
|
||||
// collapse
|
||||
double min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
double max = 2. * distance;
|
||||
ExPolygons gaps_ex = diff_ex(
|
||||
offset2_ex(gaps, -min / 2, +min / 2),
|
||||
offset2_ex(gaps, -max / 2, +max / 2),
|
||||
true);
|
||||
ThickPolylines polylines;
|
||||
for (const ExPolygon &ex : gaps_ex) {
|
||||
//remove too small gaps that are too hard to fill.
|
||||
//ie one that are smaller than an extrusion with width of min and a length of max.
|
||||
if (ex.area() > min*max) {
|
||||
ex.medial_axis(ex, max, min, &polylines, flow.height);
|
||||
}
|
||||
}
|
||||
if (!polylines.empty()) {
|
||||
ExtrusionEntityCollection gap_fill = thin_variable_width(polylines, erGapFill, flow);
|
||||
coll_nosort->append(gap_fill.entities);
|
||||
}
|
||||
}
|
||||
|
||||
if (!coll_nosort->entities.empty())
|
||||
out.push_back(coll_nosort);
|
||||
else delete coll_nosort;
|
||||
}
|
||||
if (j < polylines_out.size())
|
||||
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
|
||||
//TODO: return ExtrusionLoop objects to get better chained paths,
|
||||
// otherwise the outermost loop starts at the closest point to (0, 0).
|
||||
// We want the loops to be split inside the G-code generator to get optimum path planning.
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -12,12 +12,8 @@ public:
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillConcentric(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms,
|
||||
const Flow &flow, const ExtrusionRole &role, ExtrusionEntitiesPtr &out);
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
@ -1502,4 +1502,105 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
||||
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) {
|
||||
// this value determines granularity of adaptive width, as G-code does not allow
|
||||
// variable extrusion within a single move; this value shall only affect the amount
|
||||
// of segments, and any pruning shall be performed before we apply this tolerance
|
||||
const double tolerance = scale_(0.05);
|
||||
|
||||
int id_line = 0;
|
||||
ExtrusionEntityCollection coll;
|
||||
for (const ThickPolyline &p : polylines) {
|
||||
id_line++;
|
||||
ExtrusionPaths paths;
|
||||
ExtrusionPath path(role);
|
||||
ThickLines lines = p.thicklines();
|
||||
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const ThickLine& line = lines[i];
|
||||
|
||||
const coordf_t line_len = line.length();
|
||||
if (line_len < SCALED_EPSILON) continue;
|
||||
|
||||
double thickness_delta = fabs(line.a_width - line.b_width);
|
||||
if (thickness_delta > tolerance) {
|
||||
const unsigned short segments = ceil(thickness_delta / tolerance);
|
||||
const coordf_t seg_len = line_len / segments;
|
||||
Points pp;
|
||||
std::vector<coordf_t> width;
|
||||
{
|
||||
pp.push_back(line.a);
|
||||
width.push_back(line.a_width);
|
||||
for (size_t j = 1; j < segments; ++j) {
|
||||
pp.push_back(line.point_at(j*seg_len));
|
||||
|
||||
coordf_t w = line.a_width + (j*seg_len) * (line.b_width - line.a_width) / line_len;
|
||||
width.push_back(w);
|
||||
width.push_back(w);
|
||||
}
|
||||
pp.push_back(line.b);
|
||||
width.push_back(line.b_width);
|
||||
|
||||
assert(pp.size() == segments + 1);
|
||||
assert(width.size() == segments * 2);
|
||||
}
|
||||
|
||||
// delete this line and insert new ones
|
||||
lines.erase(lines.begin() + i);
|
||||
for (size_t j = 0; j < segments; ++j) {
|
||||
ThickLine new_line(pp[j], pp[j + 1]);
|
||||
new_line.a_width = width[2 * j];
|
||||
new_line.b_width = width[2 * j + 1];
|
||||
lines.insert(lines.begin() + i + j, new_line);
|
||||
}
|
||||
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
const double w = fmax(line.a_width, line.b_width);
|
||||
if (path.polyline.points.empty()) {
|
||||
path.polyline.append(line.a);
|
||||
path.polyline.append(line.b);
|
||||
// Convert from spacing to extrusion width based on the extrusion model
|
||||
// of a square extrusion ended with semi circles.
|
||||
flow.width = unscale(w) + flow.height * (1. - 0.25 * PI);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" filling %f gap\n", flow.width);
|
||||
#endif
|
||||
path.mm3_per_mm = flow.mm3_per_mm();
|
||||
path.width = flow.width;
|
||||
path.height = flow.height;
|
||||
} else {
|
||||
thickness_delta = fabs(scale_(flow.width) - w);
|
||||
if (thickness_delta <= tolerance / 2) {
|
||||
// the width difference between this line and the current flow width is
|
||||
// within the accepted tolerance
|
||||
path.polyline.append(line.b);
|
||||
} else {
|
||||
// we need to initialize a new line
|
||||
paths.emplace_back(std::move(path));
|
||||
path = ExtrusionPath(role);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path.polyline.is_valid())
|
||||
paths.emplace_back(std::move(path));
|
||||
// Append paths to collection.
|
||||
if (!paths.empty()) {
|
||||
if (paths.front().first_point().coincides_with(paths.back().last_point())) {
|
||||
coll.append(ExtrusionLoop(paths));
|
||||
} else {
|
||||
//not a loop : avoid to "sort" it.
|
||||
ExtrusionEntityCollection unsortable_coll(paths);
|
||||
unsortable_coll.no_sort = true;
|
||||
coll.append(unsortable_coll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return coll;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include <vector>
|
||||
|
||||
#include "boost/polygon/voronoi.hpp"
|
||||
@ -58,6 +60,9 @@ namespace Slic3r {
|
||||
void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size);
|
||||
void ensure_not_overextrude(ThickPolylines& pp);
|
||||
};
|
||||
|
||||
ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -448,7 +448,7 @@ void PerimeterGenerator::process()
|
||||
|
||||
// append thin walls
|
||||
if (!thin_walls.empty()) {
|
||||
ExtrusionEntityCollection tw = this->_variable_width
|
||||
ExtrusionEntityCollection tw = thin_variable_width
|
||||
(thin_walls, erExternalPerimeter, this->ext_perimeter_flow);
|
||||
|
||||
entities.append(tw.entities);
|
||||
@ -488,7 +488,7 @@ void PerimeterGenerator::process()
|
||||
}
|
||||
}
|
||||
if (!polylines.empty()) {
|
||||
ExtrusionEntityCollection gap_fill = this->_variable_width(polylines,
|
||||
ExtrusionEntityCollection gap_fill = thin_variable_width(polylines,
|
||||
erGapFill, this->solid_infill_flow);
|
||||
this->gap_fill->append(gap_fill.entities);
|
||||
/* Make sure we don't infill narrow parts that are already gap-filled
|
||||
@ -608,7 +608,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
|
||||
|
||||
// append thin walls to the nearest-neighbor search (only for first iteration)
|
||||
if (!thin_walls.empty()) {
|
||||
ExtrusionEntityCollection tw = this->_variable_width
|
||||
ExtrusionEntityCollection tw = thin_variable_width
|
||||
(thin_walls, erExternalPerimeter, this->ext_perimeter_flow);
|
||||
|
||||
coll.append(tw.entities);
|
||||
@ -1222,109 +1222,6 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
return my_loop;
|
||||
}
|
||||
|
||||
|
||||
ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const
|
||||
{
|
||||
// this value determines granularity of adaptive width, as G-code does not allow
|
||||
// variable extrusion within a single move; this value shall only affect the amount
|
||||
// of segments, and any pruning shall be performed before we apply this tolerance
|
||||
const double tolerance = scale_(0.05);
|
||||
|
||||
int id_line = 0;
|
||||
ExtrusionEntityCollection coll;
|
||||
for (const ThickPolyline &p : polylines) {
|
||||
id_line++;
|
||||
ExtrusionPaths paths;
|
||||
ExtrusionPath path(role);
|
||||
ThickLines lines = p.thicklines();
|
||||
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const ThickLine& line = lines[i];
|
||||
|
||||
const coordf_t line_len = line.length();
|
||||
if (line_len < SCALED_EPSILON) continue;
|
||||
|
||||
double thickness_delta = fabs(line.a_width - line.b_width);
|
||||
if (thickness_delta > tolerance) {
|
||||
const unsigned short segments = ceil(thickness_delta / tolerance);
|
||||
const coordf_t seg_len = line_len / segments;
|
||||
Points pp;
|
||||
std::vector<coordf_t> width;
|
||||
{
|
||||
pp.push_back(line.a);
|
||||
width.push_back(line.a_width);
|
||||
for (size_t j = 1; j < segments; ++j) {
|
||||
pp.push_back(line.point_at(j*seg_len));
|
||||
|
||||
coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
|
||||
width.push_back(w);
|
||||
width.push_back(w);
|
||||
}
|
||||
pp.push_back(line.b);
|
||||
width.push_back(line.b_width);
|
||||
|
||||
assert(pp.size() == segments + 1);
|
||||
assert(width.size() == segments*2);
|
||||
}
|
||||
|
||||
// delete this line and insert new ones
|
||||
lines.erase(lines.begin() + i);
|
||||
for (size_t j = 0; j < segments; ++j) {
|
||||
ThickLine new_line(pp[j], pp[j+1]);
|
||||
new_line.a_width = width[2*j];
|
||||
new_line.b_width = width[2*j+1];
|
||||
lines.insert(lines.begin() + i + j, new_line);
|
||||
}
|
||||
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
const double w = fmax(line.a_width, line.b_width);
|
||||
if (path.polyline.points.empty()) {
|
||||
path.polyline.append(line.a);
|
||||
path.polyline.append(line.b);
|
||||
// Convert from spacing to extrusion width based on the extrusion model
|
||||
// of a square extrusion ended with semi circles.
|
||||
flow.width = unscale(w) + flow.height * (1. - 0.25 * PI);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" filling %f gap\n", flow.width);
|
||||
#endif
|
||||
path.mm3_per_mm = flow.mm3_per_mm();
|
||||
path.width = flow.width;
|
||||
path.height = flow.height;
|
||||
} else {
|
||||
thickness_delta = fabs(scale_(flow.width) - w);
|
||||
if (thickness_delta <= tolerance/2) {
|
||||
// the width difference between this line and the current flow width is
|
||||
// within the accepted tolerance
|
||||
path.polyline.append(line.b);
|
||||
} else {
|
||||
// we need to initialize a new line
|
||||
paths.emplace_back(std::move(path));
|
||||
path = ExtrusionPath(role);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path.polyline.is_valid())
|
||||
paths.emplace_back(std::move(path));
|
||||
// Append paths to collection.
|
||||
if (!paths.empty()) {
|
||||
if (paths.front().first_point().coincides_with(paths.back().last_point())) {
|
||||
coll.append(ExtrusionLoop(paths));
|
||||
} else {
|
||||
//not a loop : avoid to "sort" it.
|
||||
ExtrusionEntityCollection unsortable_coll(paths);
|
||||
unsortable_coll.no_sort = true;
|
||||
coll.append(unsortable_coll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return coll;
|
||||
}
|
||||
|
||||
bool PerimeterGeneratorLoop::is_internal_contour() const
|
||||
{
|
||||
// An internal contour is a contour containing no other contours
|
||||
|
@ -99,13 +99,10 @@ private:
|
||||
double _mm3_per_mm_overhang;
|
||||
Polygons _lower_slices_p;
|
||||
|
||||
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops,
|
||||
ThickPolylines &thin_walls) const;
|
||||
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const;
|
||||
ExtrusionLoop _traverse_and_join_loops(const PerimeterGeneratorLoop &loop, const PerimeterGeneratorLoops &childs, const Point entryPoint) const;
|
||||
ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, const Point entryPoint, const Line &direction = Line(Point(0,0),Point(0,0))) const;
|
||||
PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const;
|
||||
ExtrusionEntityCollection _variable_width
|
||||
(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user