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 "../Print.hpp"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "../PerimeterGenerator.hpp"
#include "FillBase.hpp" #include "FillBase.hpp"
#include "FillRectilinear.hpp" #include "FillRectilinear.hpp"
#include "FillLightning.hpp" #include "FillLightning.hpp"
#include "FillConcentric.hpp"
namespace Slic3r { namespace Slic3r {
@ -332,6 +334,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
std::vector<SurfaceFill> surface_fills = group_fills(*this); std::vector<SurfaceFill> surface_fills = group_fills(*this);
const Slic3r::BoundingBox bbox = this->object()->bounding_box(); const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().gcode_resolution.value; const auto resolution = this->object()->print()->config().gcode_resolution.value;
const auto slicing_engine = this->object()->config().slicing_engine;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #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) if (surface_fill.params.pattern == ipLightning)
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator; 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 // calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.; double link_max_length = 0.;
@ -377,17 +387,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length = surface_fill.params.anchor_length; params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max; params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution; params.resolution = resolution;
params.use_arachne = slicing_engine == SlicingEngine::Arachne && surface_fill.params.pattern == ipConcentric;
for (ExPolygon &expoly : surface_fill.expolygons) { for (ExPolygon &expoly : surface_fill.expolygons) {
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing; f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly); surface_fill.surface.expolygon = std::move(expoly);
Polylines polylines; Polylines polylines;
ThickPolylines thick_polylines;
try { try {
if (params.use_arachne)
thick_polylines = f->fill_surface_arachne(&surface_fill.surface, params);
else
polylines = f->fill_surface(&surface_fill.surface, params); polylines = f->fill_surface(&surface_fill.surface, params);
} catch (InfillFailedException &) { } catch (InfillFailedException &) {
} }
if (! polylines.empty()) { if (!polylines.empty() || !thick_polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill // calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator) // pattern generator)
double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm(); double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
@ -406,6 +421,23 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
m_regions[surface_fill.region_id]->fills.entities.push_back(eec = new ExtrusionEntityCollection()); m_regions[surface_fill.region_id]->fills.entities.push_back(eec = new ExtrusionEntityCollection());
// Only concentric fills are not sorted. // Only concentric fills are not sorted.
eec->no_sort = f->no_sort(); eec->no_sort = f->no_sort();
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( extrusion_entities_append_paths(
eec->entities, std::move(polylines), eec->entities, std::move(polylines),
surface_fill.params.extrusion_role, surface_fill.params.extrusion_role,
@ -413,6 +445,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
} }
} }
} }
}
// add thin fill regions // add thin fill regions
// Unpacks the collection, creates multiple collections per path. // Unpacks the collection, creates multiple collections per path.
@ -618,6 +651,7 @@ void Layer::make_ironing()
surface_fill.expolygon = std::move(expoly); surface_fill.expolygon = std::move(expoly);
Polylines polylines; Polylines polylines;
try { try {
assert(!fill_params.use_arachne);
polylines = fill.fill_surface(&surface_fill, fill_params); polylines = fill.fill_surface(&surface_fill, fill_params);
} catch (InfillFailedException &) { } 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))); Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
// Create the infills for each of the regions. // Create the infills for each of the regions.
Polylines polylines_out; Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++ i) for (ExPolygon &expoly : expp)
_fill_surface_single( _fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), polylines_out);
params,
surface->thickness_layers,
_infill_direction(surface),
std::move(expp[i]),
polylines_out);
return 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, // Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends. // the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases, // This function possibly increases the spacing, never decreases,

View File

@ -14,6 +14,7 @@
#include "../Exception.hpp" #include "../Exception.hpp"
#include "../Utils.hpp" #include "../Utils.hpp"
#include "../ExPolygon.hpp" #include "../ExPolygon.hpp"
#include "../PrintConfig.hpp"
namespace Slic3r { namespace Slic3r {
@ -57,6 +58,9 @@ struct FillParams
// we were requested to complete each loop; // we were requested to complete each loop;
// in this case we don't try to make more continuous paths // in this case we don't try to make more continuous paths
bool complete { false }; 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)."); static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
@ -103,6 +107,7 @@ public:
// Perform the fill. // Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams &params); virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
virtual ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams &params);
protected: protected:
Fill() : Fill() :
@ -127,6 +132,13 @@ protected:
ExPolygon /* expolygon */, ExPolygon /* expolygon */,
Polylines & /* polylines_out */) {}; 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 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; virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View File

@ -1,6 +1,7 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp" #include "../ExPolygon.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentric.hpp" #include "FillConcentric.hpp"
@ -16,11 +17,11 @@ void FillConcentric::_fill_surface_single(
// no rotation is supported for this infill pattern // no rotation is supported for this infill pattern
BoundingBox bounding_box = expolygon.contour.bounding_box(); BoundingBox bounding_box = expolygon.contour.bounding_box();
coord_t min_spacing = scale_(this->spacing); coord_t min_spacing = scaled<coord_t>(this->spacing);
coord_t distance = coord_t(min_spacing / params.density); coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) { 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); this->spacing = unscale<double>(distance);
} }
@ -55,10 +56,76 @@ void FillConcentric::_fill_surface_single(
} }
} }
if (j < polylines_out.size()) 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, //TODO: return ExtrusionLoop objects to get better chained paths,
// otherwise the outermost loop starts at the closest point to (0, 0). // 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. // 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 } // namespace Slic3r

View File

@ -19,7 +19,18 @@ protected:
ExPolygon expolygon, ExPolygon expolygon,
Polylines &polylines_out) override; Polylines &polylines_out) override;
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; } bool no_sort() const override { return true; }
const PrintConfig *print_config = nullptr;
const PrintObjectConfig *print_object_config = nullptr;
friend class Layer;
}; };
} // namespace Slic3r } // namespace Slic3r

View File

@ -10,7 +10,7 @@
namespace Slic3r { 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; ExtrusionPaths paths;
ExtrusionPath path(role); 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); double thickness_delta = fabs(line.a_width - line.b_width);
if (thickness_delta > tolerance) { 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; const coordf_t seg_len = line_len / segments;
Points pp; Points pp;
std::vector<coordf_t> width; std::vector<coordf_t> width;

View File

@ -72,6 +72,8 @@ private:
Polygons m_lower_slices_polygons; 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 #endif

View File

@ -235,6 +235,34 @@ ThickLines ThickPolyline::thicklines() const
return lines; 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 Polyline3::lines() const
{ {
Lines3 lines; Lines3 lines;

View File

@ -64,7 +64,7 @@ public:
const Point& leftmost_point() const; const Point& leftmost_point() const;
Lines lines() const override; Lines lines() const override;
void clip_end(double distance); virtual void clip_end(double distance);
void clip_start(double distance); void clip_start(double distance);
void extend_end(double distance); void extend_end(double distance);
void extend_start(double distance); void extend_start(double distance);
@ -172,10 +172,24 @@ public:
std::swap(this->endpoints.first, this->endpoints.second); std::swap(this->endpoints.first, this->endpoints.second);
} }
void clip_end(double distance) override;
std::vector<coordf_t> width; std::vector<coordf_t> width;
std::pair<bool,bool> endpoints; 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 class Polyline3 : public MultiPoint3
{ {
public: public:

View File

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

View File

@ -131,7 +131,7 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
FillParams fill_params; FillParams fill_params;
fill_params.density = 1.0; fill_params.density = 1.0;
filler->spacing = flow.spacing(); 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}) { for (auto angle : { 0.0, 45.0}) {
surface.expolygon.rotate(angle, Point(0,0)); surface.expolygon.rotate(angle, Point(0,0));
Polylines paths = filler->fill_surface(&surface, fill_params); Polylines paths = filler->fill_surface(&surface, fill_params);
@ -443,6 +443,8 @@ bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacin
fill_params.dont_adjust = false; fill_params.dont_adjust = false;
Surface surface(stBottom, expolygon); 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); Slic3r::Polylines paths = filler->fill_surface(&surface, fill_params);
// check whether any part was left uncovered // check whether any part was left uncovered