ENH: add arachne engine for narrow internal solid infill

ConcentricGapFill pattern was used for internal narrow
solid infill. Use arachne engine instead to remove
gap fill inside the pattern and improve the extrusion path

Signed-off-by: salt.wei <salt.wei@bambulab.com>
Change-Id: I758d7c72eb71cc37026b7cebf746cc345014c3f5
(cherry picked from commit 0b6bacd21a091afc13d7b36a69e5b10f155bc6f8)
This commit is contained in:
salt.wei 2022-08-10 16:11:39 +08:00 committed by Lane.Wei
parent cbefb77de3
commit aab8a12801
54 changed files with 7922 additions and 30 deletions

View file

@ -11,6 +11,7 @@
#include "FillBase.hpp"
#include "FillRectilinear.hpp"
#include "FillConcentricInternal.hpp"
#define NARROW_INFILL_AREA_THRESHOLD 3
@ -303,7 +304,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
}
}
// BBS: detect narrow internal solid infill area and use ipConcentricGapFill pattern instead
// BBS: detect narrow internal solid infill area and use ipConcentricInternal pattern instead
if (layer.object()->config().detect_narrow_internal_solid_infill) {
size_t surface_fills_size = surface_fills.size();
for (size_t i = 0; i < surface_fills_size; i++) {
@ -324,12 +325,12 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
}
else if (narrow_expolygons_index.size() == expolygons_size) {
// BBS: all expolygons are narrow, directly change the fill pattern
surface_fills[i].params.pattern = ipConcentricGapFill;
surface_fills[i].params.pattern = ipConcentricInternal;
}
else {
// BBS: some expolygons are narrow, spilit surface_fills[i] and rearrange the expolygons
params = surface_fills[i].params;
params.pattern = ipConcentricGapFill;
params.pattern = ipConcentricInternal;
surface_fills.emplace_back(params);
surface_fills.back().region_id = surface_fills[i].region_id;
surface_fills.back().surface.surface_type = stInternalSolid;
@ -401,6 +402,13 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->angle = surface_fill.params.angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
if (surface_fill.params.pattern == ipConcentricInternal) {
FillConcentricInternal *fill_concentric = dynamic_cast<FillConcentricInternal *>(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.;

View file

@ -22,6 +22,7 @@
#include "FillLightning.hpp"
// BBS: new infill pattern header
#include "FillConcentricWGapFill.hpp"
#include "FillConcentricInternal.hpp"
// #define INFILL_DEBUG_OUTPUT
@ -58,6 +59,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
#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();
default: throw Slic3r::InvalidArgument("unknown type");

View file

@ -0,0 +1,101 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentricInternal.hpp"
namespace Slic3r {
void FillConcentricInternal::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
assert(this->print_config != nullptr && this->print_object_config != nullptr);
ThickPolylines thick_polylines_out;
for (size_t i = 0; i < this->no_overlap_expolygons.size(); ++i) {
ExPolygon &expolygon = this->no_overlap_expolygons[i];
// no rotation is supported for this infill pattern
Point bbox_size = expolygon.contour.bounding_box().size();
coord_t min_spacing = params.flow.scaled_spacing();
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, 0);
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 = 0.4;
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));
}
// 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());
}
ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection();
coll_nosort->no_sort = this->no_sort(); //can be sorted inside the pass
if (!thick_polylines_out.empty()) {
Flow new_flow = params.flow.with_spacing(float(this->spacing));
ExtrusionEntityCollection gap_fill;
variable_width(thick_polylines_out, params.extrusion_role, new_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;
}
} // namespace Slic3r

View file

@ -0,0 +1,26 @@
#ifndef slic3r_FillConcentricInternal_hpp_
#define slic3r_FillConcentricInternal_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillConcentricInternal : public Fill
{
public:
~FillConcentricInternal() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
protected:
Fill* clone() const override { return new FillConcentricInternal(*this); };
bool no_sort() const override { return true; }
const PrintConfig *print_config = nullptr;
const PrintObjectConfig *print_object_config = nullptr;
friend class Layer;
};
} // namespace Slic3r
#endif // slic3r_FillConcentricInternal_hpp_