Added concentric infill generated using Arachne.

This commit is contained in:
Lukáš Hejl 2022-05-24 12:28:14 +02:00
parent ac23a369d5
commit 454e6496ce
11 changed files with 217 additions and 40 deletions

View File

@ -8,10 +8,12 @@
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Surface.hpp"
#include "../PerimeterGenerator.hpp"
#include "FillBase.hpp"
#include "FillRectilinear.hpp"
#include "FillLightning.hpp"
#include "FillConcentric.hpp"
namespace Slic3r {
@ -329,9 +331,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
std::vector<SurfaceFill> surface_fills = group_fills(*this);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().gcode_resolution.value;
std::vector<SurfaceFill> surface_fills = group_fills(*this);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().gcode_resolution.value;
const auto slicing_engine = this->object()->config().slicing_engine;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
@ -352,6 +355,13 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
if (surface_fill.params.pattern == ipLightning)
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
if (object()->config().slicing_engine.value == SlicingEngine::Arachne && surface_fill.params.pattern == ipConcentric) {
FillConcentric *fill_concentric = dynamic_cast<FillConcentric *>(f.get());
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
}
// calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.;
@ -372,23 +382,28 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
params.density = float(0.01 * surface_fill.params.density);
params.dont_adjust = false; // surface_fill.params.dont_adjust;
params.density = float(0.01 * surface_fill.params.density);
params.dont_adjust = false; // surface_fill.params.dont_adjust;
params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.use_arachne = slicing_engine == SlicingEngine::Arachne && surface_fill.params.pattern == ipConcentric;
for (ExPolygon &expoly : surface_fill.expolygons) {
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);
Polylines polylines;
Polylines polylines;
ThickPolylines thick_polylines;
try {
polylines = f->fill_surface(&surface_fill.surface, params);
if (params.use_arachne)
thick_polylines = f->fill_surface_arachne(&surface_fill.surface, params);
else
polylines = f->fill_surface(&surface_fill.surface, params);
} catch (InfillFailedException &) {
}
if (! polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill
if (!polylines.empty() || !thick_polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator)
double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
double flow_width = surface_fill.params.flow.width();
@ -406,10 +421,28 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
m_regions[surface_fill.region_id]->fills.entities.push_back(eec = new ExtrusionEntityCollection());
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
surface_fill.params.extrusion_role,
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
if (params.use_arachne) {
for (const ThickPolyline &thick_polyline : thick_polylines) {
Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing));
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled<float>(0.05), 0);
// Append paths to collection.
if (!paths.empty()) {
if (paths.front().first_point() == paths.back().last_point())
eec->entities.emplace_back(new ExtrusionLoop(std::move(paths)));
else
for (ExtrusionPath &path : paths)
eec->entities.emplace_back(new ExtrusionPath(std::move(path)));
}
}
thick_polylines.clear();
} else {
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
surface_fill.params.extrusion_role,
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
}
}
}
}
@ -618,6 +651,7 @@ void Layer::make_ironing()
surface_fill.expolygon = std::move(expoly);
Polylines polylines;
try {
assert(!fill_params.use_arachne);
polylines = fill.fill_surface(&surface_fill, fill_params);
} catch (InfillFailedException &) {
}

View File

@ -82,16 +82,22 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 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),
std::move(expp[i]),
polylines_out);
for (ExPolygon &expoly : expp)
_fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), polylines_out);
return polylines_out;
}
ThickPolylines Fill::fill_surface_arachne(const Surface *surface, const FillParams &params)
{
// Perform offset.
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
// Create the infills for each of the regions.
ThickPolylines thick_polylines_out;
for (ExPolygon &expoly : expp)
_fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), thick_polylines_out);
return thick_polylines_out;
}
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases,

View File

@ -14,6 +14,7 @@
#include "../Exception.hpp"
#include "../Utils.hpp"
#include "../ExPolygon.hpp"
#include "../PrintConfig.hpp"
namespace Slic3r {
@ -57,6 +58,9 @@ struct FillParams
// we were requested to complete each loop;
// in this case we don't try to make more continuous paths
bool complete { false };
// For Concentric infill, to switch between Classic and Arachne.
bool use_arachne { false };
};
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
@ -103,6 +107,7 @@ public:
// Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
virtual ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams &params);
protected:
Fill() :
@ -121,12 +126,19 @@ protected:
// The expolygon may be modified by the method to avoid a copy.
virtual void _fill_surface_single(
const FillParams & /* params */,
const FillParams & /* params */,
unsigned int /* thickness_layers */,
const std::pair<float, Point> & /* direction */,
const std::pair<float, Point> & /* direction */,
ExPolygon /* expolygon */,
Polylines & /* polylines_out */) {};
// Used for concentric infill to generate ThickPolylines using Arachne.
virtual void _fill_surface_single(const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
ThickPolylines &thick_polylines_out) {}
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View File

@ -1,26 +1,27 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentric.hpp"
namespace Slic3r {
void FillConcentric::_fill_surface_single(
const FillParams &params,
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
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);
coord_t min_spacing = scaled<coord_t>(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()(0), distance);
distance = Slic3r::FillConcentric::_adjust_solid_spacing(bounding_box.size()(0), distance);
this->spacing = unscale<double>(distance);
}
@ -34,7 +35,7 @@ void FillConcentric::_fill_surface_single(
// generate paths from the outermost to the innermost, to avoid
// adhesion problems of the first central tiny loops
loops = union_pt_chained_outside_in(loops);
// split paths using a nearest neighbor search
size_t iPathFirst = polylines_out.size();
Point last_pos(0, 0);
@ -55,10 +56,76 @@ void FillConcentric::_fill_surface_single(
}
}
if (j < polylines_out.size())
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
polylines_out.erase(polylines_out.begin() + int(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.
}
void FillConcentric::_fill_surface_single(const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
ThickPolylines &thick_polylines_out)
{
assert(params.use_arachne);
assert(this->print_config != nullptr && this->print_object_config != nullptr);
// no rotation is supported for this infill pattern
Point bbox_size = expolygon.contour.bounding_box().size();
coord_t min_spacing = scaled<coord_t>(this->spacing);
if (params.density > 0.9999f && !params.dont_adjust) {
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, min_spacing / 2);
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, *this->print_object_config, *this->print_config);
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
std::vector<const Arachne::ExtrusionLine *> all_extrusions;
for (Arachne::VariableWidthLines &loop : loops) {
if (loop.empty())
continue;
for (const Arachne::ExtrusionLine &wall : loop)
all_extrusions.emplace_back(&wall);
}
// Split paths using a nearest neighbor search.
size_t firts_poly_idx = thick_polylines_out.size();
Point last_pos(0, 0);
for (const Arachne::ExtrusionLine *extrusion : all_extrusions) {
if (extrusion->empty())
continue;
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
if (extrusion->is_closed && thick_polyline.points.front() == thick_polyline.points.back() && thick_polyline.width.front() == thick_polyline.width.back()) {
thick_polyline.points.pop_back();
assert(thick_polyline.points.size() * 2 == thick_polyline.width.size());
int nearest_idx = last_pos.nearest_point_index(thick_polyline.points);
std::rotate(thick_polyline.points.begin(), thick_polyline.points.begin() + nearest_idx, thick_polyline.points.end());
std::rotate(thick_polyline.width.begin(), thick_polyline.width.begin() + 2 * nearest_idx, thick_polyline.width.end());
thick_polyline.points.emplace_back(thick_polyline.points.front());
}
thick_polylines_out.emplace_back(std::move(thick_polyline));
}
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
// Keep valid paths only.
size_t j = firts_poly_idx;
for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
thick_polylines_out[i].clip_end(this->loop_clipping);
if (thick_polylines_out[i].is_valid()) {
if (j < i)
thick_polylines_out[j] = std::move(thick_polylines_out[i]);
++j;
}
}
if (j < thick_polylines_out.size())
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
} else {
Polylines polylines;
this->_fill_surface_single(params, thickness_layers, direction, expolygon, polylines);
append(thick_polylines_out, to_thick_polylines(std::move(polylines), min_spacing));
}
}
} // namespace Slic3r

View File

@ -19,7 +19,18 @@ protected:
ExPolygon expolygon,
Polylines &polylines_out) override;
bool no_sort() const override { return true; }
void _fill_surface_single(const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
ThickPolylines &thick_polylines_out) override;
bool no_sort() const override { return true; }
const PrintConfig *print_config = nullptr;
const PrintObjectConfig *print_object_config = nullptr;
friend class Layer;
};
} // namespace Slic3r

View File

@ -10,7 +10,7 @@
namespace Slic3r {
static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance)
ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance)
{
ExtrusionPaths paths;
ExtrusionPath path(role);
@ -24,7 +24,7 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi
double thickness_delta = fabs(line.a_width - line.b_width);
if (thickness_delta > tolerance) {
const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance);
const auto segments = (unsigned int)ceil(thickness_delta / tolerance);
const coordf_t seg_len = line_len / segments;
Points pp;
std::vector<coordf_t> width;

View File

@ -72,6 +72,8 @@ private:
Polygons m_lower_slices_polygons;
};
ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance);
}
#endif

View File

@ -235,6 +235,34 @@ ThickLines ThickPolyline::thicklines() const
return lines;
}
// Removes the given distance from the end of the ThickPolyline
void ThickPolyline::clip_end(double distance)
{
while (distance > 0) {
Vec2d last_point = this->last_point().cast<double>();
coordf_t last_width = this->width.back();
this->points.pop_back();
this->width.pop_back();
if (this->points.empty())
break;
Vec2d vec = this->last_point().cast<double>() - last_point;
coordf_t width_diff = this->width.back() - last_width;
double vec_length_sqr = vec.squaredNorm();
if (vec_length_sqr > distance * distance) {
double t = (distance / std::sqrt(vec_length_sqr));
this->points.emplace_back((last_point + vec * t).cast<coord_t>());
this->width.emplace_back(last_width + width_diff * t);
assert(this->width.size() == (this->points.size() - 1) * 2);
return;
} else
this->width.pop_back();
distance -= std::sqrt(vec_length_sqr);
}
assert(this->width.size() == (this->points.size() - 1) * 2);
}
Lines3 Polyline3::lines() const
{
Lines3 lines;

View File

@ -64,7 +64,7 @@ public:
const Point& leftmost_point() const;
Lines lines() const override;
void clip_end(double distance);
virtual void clip_end(double distance);
void clip_start(double distance);
void extend_end(double distance);
void extend_start(double distance);
@ -172,10 +172,24 @@ public:
std::swap(this->endpoints.first, this->endpoints.second);
}
void clip_end(double distance) override;
std::vector<coordf_t> width;
std::pair<bool,bool> endpoints;
};
inline ThickPolylines to_thick_polylines(Polylines &&polylines, const coordf_t width)
{
ThickPolylines out;
out.reserve(polylines.size());
for (Polyline &polyline : polylines) {
out.emplace_back();
out.back().width.assign((polyline.points.size() - 1) * 2, width);
out.back().points = std::move(polyline.points);
}
return out;
}
class Polyline3 : public MultiPoint3
{
public:

View File

@ -3243,6 +3243,7 @@ static inline void fill_expolygon_generate_paths(
Surface surface(stInternal, std::move(expolygon));
Polylines polylines;
try {
assert(!fill_params.use_arachne);
polylines = filler->fill_surface(&surface, fill_params);
} catch (InfillFailedException &) {
}

View File

@ -131,7 +131,7 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
FillParams fill_params;
fill_params.density = 1.0;
filler->spacing = flow.spacing();
REQUIRE(!fill_params.use_arachne); // Make this test fail when Arachne is used because this test is not ready for it.
for (auto angle : { 0.0, 45.0}) {
surface.expolygon.rotate(angle, Point(0,0));
Polylines paths = filler->fill_surface(&surface, fill_params);
@ -442,8 +442,10 @@ bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacin
fill_params.density = float(density);
fill_params.dont_adjust = false;
Surface surface(stBottom, expolygon);
Slic3r::Polylines paths = filler->fill_surface(&surface, fill_params);
Surface surface(stBottom, expolygon);
if (fill_params.use_arachne) // Make this test fail when Arachne is used because this test is not ready for it.
return false;
Slic3r::Polylines paths = filler->fill_surface(&surface, fill_params);
// check whether any part was left uncovered
Polygons grown_paths;