ENH: enable arachne for concentric pattern

Enable arachne for concentric pattern by
referring to PrusaSlicer

Also remove useless pattern we added.

Signed-off-by: salt.wei <salt.wei@bambulab.com>
Change-Id: Ie2574f7fc4751ebdf1caab4de52013f3101e104f
This commit is contained in:
salt.wei 2022-09-16 21:40:41 +08:00 committed by Lane.Wei
parent df321f8cd9
commit db9ade2257
10 changed files with 153 additions and 167 deletions

View file

@ -67,8 +67,6 @@ set(lisbslic3r_sources
Fill/FillBase.hpp
Fill/FillConcentric.cpp
Fill/FillConcentric.hpp
Fill/FillConcentricWGapFill.cpp
Fill/FillConcentricWGapFill.hpp
Fill/FillConcentricInternal.cpp
Fill/FillConcentricInternal.hpp
Fill/FillHoneycomb.cpp

View file

@ -12,6 +12,7 @@
#include "FillBase.hpp"
#include "FillRectilinear.hpp"
#include "FillConcentricInternal.hpp"
#include "FillConcentric.hpp"
#define NARROW_INFILL_AREA_THRESHOLD 3
@ -407,6 +408,11 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
} else if (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
@ -434,6 +440,7 @@ 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 = surface_fill.params.pattern == ipConcentric;
// BBS
params.flow = surface_fill.params.flow;

View file

@ -9,6 +9,7 @@
#include "../PrintConfig.hpp"
#include "../Surface.hpp"
#include "../libslic3r.h"
#include "../VariableWidth.hpp"
#include "FillBase.hpp"
#include "FillConcentric.hpp"
@ -21,7 +22,6 @@
#include "FillAdaptive.hpp"
#include "FillLightning.hpp"
// BBS: new infill pattern header
#include "FillConcentricWGapFill.hpp"
#include "FillConcentricInternal.hpp"
// #define INFILL_DEBUG_OUTPUT
@ -58,7 +58,6 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipLightning: return new FillLightning::Filler();
#endif // HAS_LIGHTNING_INFILL
// BBS: for internal solid infill only
case ipConcentricGapFill: return new FillConcentricWGapFill();
case ipConcentricInternal: return new FillConcentricInternal();
// BBS: for bottom and top surface only
case ipMonotonicLine: return new FillMonotonicLineWGapFill();
@ -107,16 +106,31 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
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;
}
// BBS: this method is used to fill the ExtrusionEntityCollection. It call fill_surface by default
void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
Polylines polylines;
ThickPolylines thick_polylines;
try {
polylines = this->fill_surface(surface, params);
if (params.use_arachne)
thick_polylines = this->fill_surface_arachne(surface, params);
else
polylines = this->fill_surface(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 = params.flow.mm3_per_mm();
@ -136,10 +150,17 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
out.push_back(eec = new ExtrusionEntityCollection());
// Only concentric fills are not sorted.
eec->no_sort = this->no_sort();
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
params.extrusion_role,
flow_mm3_per_mm, float(flow_width), params.flow.height());
if (params.use_arachne) {
Flow new_flow = params.flow.with_spacing(float(this->spacing));
variable_width(thick_polylines, params.extrusion_role, new_flow, eec->entities);
thick_polylines.clear();
}
else {
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
params.extrusion_role,
flow_mm3_per_mm, float(flow_width), params.flow.height());
}
}
}

View file

@ -63,6 +63,9 @@ struct FillParams
// 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 };
// BBS
Flow flow;
ExtrusionRole extrusion_role{ ExtrusionRole(0) };
@ -121,6 +124,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);
// BBS: this method is used to fill the ExtrusionEntityCollection.
// It call fill_surface by default
@ -149,6 +153,13 @@ protected:
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,6 +1,8 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentric.hpp"
@ -61,4 +63,84 @@ void FillConcentric::_fill_surface_single(
// 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, float(min_spacing) / 2.f);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
input_params.min_feature_size = 0.1;
input_params.wall_transition_length = 1.0 * min_nozzle_diameter;
input_params.wall_transition_angle = 10;
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
input_params.wall_distribution_count = 1;
input_params.wall_add_middle_threshold = 0.75;
input_params.wall_split_middle_threshold = 0.5;
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, input_params);
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));
last_pos = thick_polylines_out.back().last_point();
}
// 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;
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

@ -1,135 +0,0 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "../ShortestPath.hpp"
#include "FillConcentricWGapFill.hpp"
namespace Slic3r {
const float concentric_overlap_threshold = 0.02;
void FillConcentricWGapFill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
//BBS: FillConcentricWGapFill.cpp is absolutely newly add by BBL for narrow internal solid infill area to reduce vibration
// Because the area is narrow, we should not use the surface->expolygon which has overlap with perimeter, but
// use no_overlap_expolygons instead to avoid overflow in narrow area.
//Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->spacing)));
float min_spacing = this->spacing * (1 - concentric_overlap_threshold);
Slic3r::ExPolygons expp = offset2_ex(this->no_overlap_expolygons, -double(scale_(0.5 * this->spacing + 0.5 * min_spacing) - 1),
+double(scale_(0.5 * min_spacing) - 1));
// Create the infills for each of the regions.
Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++i) {
ExPolygon expolygon = expp[i];
coord_t distance = scale_(this->spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
distance = scale_(this->spacing);
}
ExPolygons gaps;
Polygons loops = (Polygons)expolygon;
ExPolygons last = { expolygon };
bool first = true;
while (!last.empty()) {
ExPolygons next_onion = offset2_ex(last, -double(distance + scale_(this->spacing) / 2), +double(scale_(this->spacing) / 2));
for (auto it = next_onion.begin(); it != next_onion.end(); it++) {
Polygons temp_loops = (Polygons)(*it);
loops.insert(loops.end(), temp_loops.begin(), temp_loops.end());
}
append(gaps, diff_ex(
offset(last, -0.5f * distance),
offset(next_onion, 0.5f * distance + 10))); // 10 is safty offset
last = next_onion;
if (first && !this->no_overlap_expolygons.empty()) {
gaps = intersection_ex(gaps, this->no_overlap_expolygons);
}
first = false;
}
ExtrusionRole good_role = params.extrusion_role;
ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection();
coll_nosort->no_sort = this->no_sort(); //can be sorted inside the pass
extrusion_entities_append_loops(
coll_nosort->entities, std::move(loops),
good_role,
params.flow.mm3_per_mm(),
params.flow.width(),
params.flow.height());
//BBS: add internal gapfills between infill loops
if (!gaps.empty() && params.density >= 1) {
double min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * distance;
ExPolygons gaps_ex = diff_ex(
offset2_ex(gaps, -float(min / 2), float(min / 2)),
offset2_ex(gaps, -float(max / 2), float(max / 2)),
ApplySafetyOffset::Yes);
//BBS: sort the gap_ex to avoid mess travel
Points ordering_points;
ordering_points.reserve(gaps_ex.size());
ExPolygons gaps_ex_sorted;
gaps_ex_sorted.reserve(gaps_ex.size());
for (const ExPolygon &ex : gaps_ex)
ordering_points.push_back(ex.contour.first_point());
std::vector<Points::size_type> order = chain_points(ordering_points);
for (size_t i : order)
gaps_ex_sorted.emplace_back(std::move(gaps_ex[i]));
ThickPolylines polylines;
for (ExPolygon& ex : gaps_ex_sorted) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines);
}
if (!polylines.empty() && !is_bridge(good_role)) {
ExtrusionEntityCollection gap_fill;
variable_width(polylines, erGapFill, params.flow, gap_fill.entities);
coll_nosort->append(std::move(gap_fill.entities));
}
}
if (!coll_nosort->entities.empty())
out.push_back(coll_nosort);
else
delete coll_nosort;
}
//BBS: add external gapfill between perimeter and infill
ExPolygons external_gaps = diff_ex(this->no_overlap_expolygons, offset_ex(expp, double(scale_(0.5 * this->spacing))), ApplySafetyOffset::Yes);
external_gaps = union_ex(external_gaps);
if (!this->no_overlap_expolygons.empty())
external_gaps = intersection_ex(external_gaps, this->no_overlap_expolygons);
if (!external_gaps.empty()) {
double min = 0.4 * scale_(params.flow.nozzle_diameter()) * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * params.flow.scaled_width();
//BBS: collapse, be sure we don't gapfill where the perimeters are already touching each other (negative spacing).
min = std::max(min, (double)Flow::rounded_rectangle_extrusion_width_from_spacing((float)EPSILON, (float)params.flow.height()));
ExPolygons external_gaps_collapsed = offset2_ex(external_gaps, double(-min / 2), double(+min / 2));
ThickPolylines polylines;
for (ExPolygon& ex : external_gaps_collapsed) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines);
}
ExtrusionEntityCollection* coll_external_gapfill = new ExtrusionEntityCollection();
coll_external_gapfill->no_sort = this->no_sort();
if (!polylines.empty() && !is_bridge(params.extrusion_role)) {
ExtrusionEntityCollection gap_fill;
variable_width(polylines, erGapFill, params.flow, gap_fill.entities);
coll_external_gapfill->append(std::move(gap_fill.entities));
}
if (!coll_external_gapfill->entities.empty())
out.push_back(coll_external_gapfill);
else
delete coll_external_gapfill;
}
}
}

View file

@ -1,21 +0,0 @@
#ifndef slic3r_FillConcentricWGapFil_hpp_
#define slic3r_FillConcentricWGapFil_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillConcentricWGapFill : public Fill
{
public:
~FillConcentricWGapFill() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
protected:
Fill* clone() const override { return new FillConcentricWGapFill(*this); };
bool no_sort() const override { return true; }
};
} // namespace Slic3r
#endif // slic3r_FillConcentricWGapFil_hpp_

View file

@ -239,6 +239,18 @@ public:
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

@ -46,7 +46,7 @@ enum class FuzzySkinType {
enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricGapFill, ipConcentricInternal,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
#if HAS_LIGHTNING_INFILL
ipLightning,
#endif // HAS_LIGHTNING_INFILL