Added concentric infill generated using Arachne.
This commit is contained in:
parent
ac23a369d5
commit
454e6496ce
@ -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 {
|
||||
|
||||
@ -332,6 +334,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
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.;
|
||||
@ -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_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;
|
||||
ThickPolylines thick_polylines;
|
||||
try {
|
||||
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()) {
|
||||
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();
|
||||
@ -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());
|
||||
// Only concentric fills are not sorted.
|
||||
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(
|
||||
eec->entities, std::move(polylines),
|
||||
surface_fill.params.extrusion_role,
|
||||
@ -413,6 +445,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add thin fill regions
|
||||
// Unpacks the collection, creates multiple collections per path.
|
||||
@ -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 &) {
|
||||
}
|
||||
|
@ -82,16 +82,22 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
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 ¶ms)
|
||||
{
|
||||
// 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,
|
||||
|
@ -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 ¶ms);
|
||||
virtual ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
Fill() :
|
||||
@ -127,6 +132,13 @@ protected:
|
||||
ExPolygon /* expolygon */,
|
||||
Polylines & /* polylines_out */) {};
|
||||
|
||||
// Used for concentric infill to generate ThickPolylines using Arachne.
|
||||
virtual void _fill_surface_single(const FillParams ¶ms,
|
||||
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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
#include "Arachne/WallToolPaths.hpp"
|
||||
|
||||
#include "FillConcentric.hpp"
|
||||
|
||||
@ -16,11 +17,11 @@ void FillConcentric::_fill_surface_single(
|
||||
// no rotation is supported for this infill pattern
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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 ¶ms,
|
||||
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
|
||||
|
@ -19,7 +19,18 @@ protected:
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
void _fill_surface_single(const FillParams ¶ms,
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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 &) {
|
||||
}
|
||||
|
@ -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);
|
||||
@ -443,6 +443,8 @@ bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacin
|
||||
fill_params.dont_adjust = false;
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user