Follow-up to ee626eb65a
Refactored PerimeterGenerator for functional style, better constness with the goal of calling PerimeterGenerator::process_xxx() for each surface at once to collect its fill expolygons.
This commit is contained in:
parent
715a603ab4
commit
237e56c7ce
@ -268,13 +268,13 @@ void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extr
|
|||||||
{
|
{
|
||||||
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
|
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
|
||||||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
|
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
|
||||||
Slic3r::append(dst, thick_polyline_to_multi_path(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)).paths);
|
Slic3r::append(dst, PerimeterGenerator::thick_polyline_to_multi_path(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)).paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow)
|
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow)
|
||||||
{
|
{
|
||||||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion);
|
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion);
|
||||||
Slic3r::append(dst, thick_polyline_to_multi_path(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)).paths);
|
Slic3r::append(dst, PerimeterGenerator::thick_polyline_to_multi_path(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)).paths);
|
||||||
}
|
}
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
@ -8,6 +8,7 @@
|
|||||||
#include "../Print.hpp"
|
#include "../Print.hpp"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
#include "../Surface.hpp"
|
#include "../Surface.hpp"
|
||||||
|
// for Arachne based infills
|
||||||
#include "../PerimeterGenerator.hpp"
|
#include "../PerimeterGenerator.hpp"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "FillBase.hpp"
|
||||||
@ -427,7 +428,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||||||
for (const ThickPolyline &thick_polyline : thick_polylines) {
|
for (const ThickPolyline &thick_polyline : thick_polylines) {
|
||||||
Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing));
|
Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing));
|
||||||
|
|
||||||
ExtrusionMultiPath multi_path = thick_polyline_to_multi_path(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled<float>(0.05), float(SCALED_EPSILON));
|
ExtrusionMultiPath multi_path = PerimeterGenerator::thick_polyline_to_multi_path(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled<float>(0.05), float(SCALED_EPSILON));
|
||||||
// Append paths to collection.
|
// Append paths to collection.
|
||||||
if (!multi_path.empty()) {
|
if (!multi_path.empty()) {
|
||||||
if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point())
|
if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point())
|
||||||
|
@ -72,35 +72,46 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||||||
(this->layer()->id() >= size_t(region_config.bottom_solid_layers.value) &&
|
(this->layer()->id() >= size_t(region_config.bottom_solid_layers.value) &&
|
||||||
this->layer()->print_z >= region_config.bottom_solid_min_thickness - EPSILON);
|
this->layer()->print_z >= region_config.bottom_solid_min_thickness - EPSILON);
|
||||||
|
|
||||||
PerimeterGenerator g(
|
PerimeterGenerator::Parameters params(
|
||||||
// input:
|
|
||||||
&slices,
|
|
||||||
this->layer()->height,
|
this->layer()->height,
|
||||||
|
int(this->layer()->id()),
|
||||||
this->flow(frPerimeter),
|
this->flow(frPerimeter),
|
||||||
®ion_config,
|
this->flow(frExternalPerimeter),
|
||||||
&this->layer()->object()->config(),
|
this->bridging_flow(frPerimeter),
|
||||||
&print_config,
|
this->flow(frSolidInfill),
|
||||||
spiral_vase,
|
region_config,
|
||||||
|
this->layer()->object()->config(),
|
||||||
// output:
|
print_config,
|
||||||
&m_perimeters,
|
spiral_vase
|
||||||
&m_thin_fills,
|
|
||||||
fill_surfaces
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this->layer()->lower_layer != nullptr)
|
// Cummulative sum of polygons over all the regions.
|
||||||
// Cummulative sum of polygons over all the regions.
|
const ExPolygons *lower_slices = this->layer()->lower_layer ? &this->layer()->lower_layer->lslices : nullptr;
|
||||||
g.lower_slices = &this->layer()->lower_layer->lslices;
|
// Cache for offsetted lower_slices
|
||||||
|
Polygons lower_layer_polygons_cache;
|
||||||
g.layer_id = (int)this->layer()->id();
|
|
||||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
|
||||||
g.overhang_flow = this->bridging_flow(frPerimeter);
|
|
||||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
|
||||||
|
|
||||||
if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase)
|
if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase)
|
||||||
g.process_arachne();
|
PerimeterGenerator::process_arachne(
|
||||||
|
// input:
|
||||||
|
params,
|
||||||
|
&slices,
|
||||||
|
lower_slices,
|
||||||
|
lower_layer_polygons_cache,
|
||||||
|
// output:
|
||||||
|
m_perimeters,
|
||||||
|
m_thin_fills,
|
||||||
|
*fill_surfaces);
|
||||||
else
|
else
|
||||||
g.process_classic();
|
PerimeterGenerator::process_classic(
|
||||||
|
// input:
|
||||||
|
params,
|
||||||
|
&slices,
|
||||||
|
lower_slices,
|
||||||
|
lower_layer_polygons_cache,
|
||||||
|
// output:
|
||||||
|
m_perimeters,
|
||||||
|
m_thin_fills,
|
||||||
|
*fill_surfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
|
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance)
|
ExtrusionMultiPath PerimeterGenerator::thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance)
|
||||||
{
|
{
|
||||||
ExtrusionMultiPath multi_path;
|
ExtrusionMultiPath multi_path;
|
||||||
ExtrusionPath path(role);
|
ExtrusionPath path(role);
|
||||||
@ -121,7 +121,7 @@ static void variable_width(const ThickPolylines &polylines, ExtrusionRole role,
|
|||||||
// of segments, and any pruning shall be performed before we apply this tolerance.
|
// of segments, and any pruning shall be performed before we apply this tolerance.
|
||||||
const auto tolerance = float(scale_(0.05));
|
const auto tolerance = float(scale_(0.05));
|
||||||
for (const ThickPolyline &p : polylines) {
|
for (const ThickPolyline &p : polylines) {
|
||||||
ExtrusionMultiPath multi_path = thick_polyline_to_multi_path(p, role, flow, tolerance, tolerance);
|
ExtrusionMultiPath multi_path = PerimeterGenerator::thick_polyline_to_multi_path(p, role, flow, tolerance, tolerance);
|
||||||
// Append paths to collection.
|
// Append paths to collection.
|
||||||
if (!multi_path.paths.empty()) {
|
if (!multi_path.paths.empty()) {
|
||||||
for (auto it = std::next(multi_path.paths.begin()); it != multi_path.paths.end(); ++it) {
|
for (auto it = std::next(multi_path.paths.begin()); it != multi_path.paths.end(); ++it) {
|
||||||
@ -157,7 +157,15 @@ public:
|
|||||||
// External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
|
// External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
|
||||||
bool is_external() const { return this->depth == 0; }
|
bool is_external() const { return this->depth == 0; }
|
||||||
// An island, which may have holes, but it does not have another internal island.
|
// An island, which may have holes, but it does not have another internal island.
|
||||||
bool is_internal_contour() const;
|
bool is_internal_contour() const {
|
||||||
|
// An internal contour is a contour containing no other contours
|
||||||
|
if (! this->is_contour)
|
||||||
|
return false;
|
||||||
|
for (const PerimeterGeneratorLoop &loop : this->children)
|
||||||
|
if (loop.is_contour)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Thanks Cura developers for this function.
|
// Thanks Cura developers for this function.
|
||||||
@ -243,7 +251,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy
|
|||||||
|
|
||||||
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
|
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
|
||||||
|
|
||||||
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator::Parameters ¶ms, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
||||||
{
|
{
|
||||||
// loops is an arrayref of ::Loop objects
|
// loops is an arrayref of ::Loop objects
|
||||||
// turn each one into an ExtrusionLoop object
|
// turn each one into an ExtrusionLoop object
|
||||||
@ -269,30 +277,30 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||||||
const Polygon &polygon = loop.fuzzify ? fuzzified : loop.polygon;
|
const Polygon &polygon = loop.fuzzify ? fuzzified : loop.polygon;
|
||||||
if (loop.fuzzify) {
|
if (loop.fuzzify) {
|
||||||
fuzzified = loop.polygon;
|
fuzzified = loop.polygon;
|
||||||
fuzzy_polygon(fuzzified, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_dist.value));
|
fuzzy_polygon(fuzzified, scaled<float>(params.config.fuzzy_skin_thickness.value), scaled<float>(params.config.fuzzy_skin_point_dist.value));
|
||||||
}
|
}
|
||||||
if (perimeter_generator.config->overhangs && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers
|
if (params.config.overhangs && params.layer_id > params.object_config.raft_layers
|
||||||
&& ! ((perimeter_generator.object_config->support_material || perimeter_generator.object_config->support_material_enforce_layers > 0) &&
|
&& ! ((params.object_config.support_material || params.object_config.support_material_enforce_layers > 0) &&
|
||||||
perimeter_generator.object_config->support_material_contact_distance.value == 0)) {
|
params.object_config.support_material_contact_distance.value == 0)) {
|
||||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
extrusion_paths_append(
|
extrusion_paths_append(
|
||||||
paths,
|
paths,
|
||||||
intersection_pl({ polygon }, perimeter_generator.lower_slices_polygons()),
|
intersection_pl({ polygon }, lower_slices_polygons_cache),
|
||||||
role,
|
role,
|
||||||
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
is_external ? params.ext_mm3_per_mm : params.mm3_per_mm,
|
||||||
is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(),
|
is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(),
|
||||||
(float)perimeter_generator.layer_height);
|
float(params.layer_height));
|
||||||
|
|
||||||
// get overhang paths by checking what parts of this loop fall
|
// get overhang paths by checking what parts of this loop fall
|
||||||
// outside the grown lower slices (thus where the distance between
|
// outside the grown lower slices (thus where the distance between
|
||||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||||
extrusion_paths_append(
|
extrusion_paths_append(
|
||||||
paths,
|
paths,
|
||||||
diff_pl({ polygon }, perimeter_generator.lower_slices_polygons()),
|
diff_pl({ polygon }, lower_slices_polygons_cache),
|
||||||
erOverhangPerimeter,
|
erOverhangPerimeter,
|
||||||
perimeter_generator.mm3_per_mm_overhang(),
|
params.mm3_per_mm_overhang,
|
||||||
perimeter_generator.overhang_flow.width(),
|
params.overhang_flow.width(),
|
||||||
perimeter_generator.overhang_flow.height());
|
params.overhang_flow.height());
|
||||||
|
|
||||||
// Reapply the nearest point search for starting point.
|
// Reapply the nearest point search for starting point.
|
||||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||||
@ -300,9 +308,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||||||
} else {
|
} else {
|
||||||
ExtrusionPath path(role);
|
ExtrusionPath path(role);
|
||||||
path.polyline = polygon.split_at_first_point();
|
path.polyline = polygon.split_at_first_point();
|
||||||
path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm();
|
path.mm3_per_mm = is_external ? params.ext_mm3_per_mm : params.mm3_per_mm;
|
||||||
path.width = is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width();
|
path.width = is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width();
|
||||||
path.height = (float)perimeter_generator.layer_height;
|
path.height = float(params.layer_height);
|
||||||
paths.push_back(path);
|
paths.push_back(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +319,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||||||
|
|
||||||
// Append thin walls to the nearest-neighbor search (only for first iteration)
|
// Append thin walls to the nearest-neighbor search (only for first iteration)
|
||||||
if (! thin_walls.empty()) {
|
if (! thin_walls.empty()) {
|
||||||
variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities);
|
variable_width(thin_walls, erExternalPerimeter, params.ext_perimeter_flow, coll.entities);
|
||||||
thin_walls.clear();
|
thin_walls.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +339,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||||||
} else {
|
} else {
|
||||||
const PerimeterGeneratorLoop &loop = loops[idx.first];
|
const PerimeterGeneratorLoop &loop = loops[idx.first];
|
||||||
assert(thin_walls.empty());
|
assert(thin_walls.empty());
|
||||||
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
|
ExtrusionEntityCollection children = traverse_loops(params, lower_slices_polygons_cache, loop.children, thin_walls);
|
||||||
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
|
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
|
||||||
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
|
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
|
||||||
coll.entities[idx.first] = nullptr;
|
coll.entities[idx.first] = nullptr;
|
||||||
@ -436,7 +444,7 @@ struct PerimeterGeneratorArachneExtrusion
|
|||||||
bool fuzzify = false;
|
bool fuzzify = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions)
|
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters ¶ms, const Polygons &lower_slices_polygons_cache, std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions)
|
||||||
{
|
{
|
||||||
ExtrusionEntityCollection extrusion_coll;
|
ExtrusionEntityCollection extrusion_coll;
|
||||||
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
|
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
|
||||||
@ -448,13 +456,13 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
|
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
|
||||||
|
|
||||||
if (pg_extrusion.fuzzify)
|
if (pg_extrusion.fuzzify)
|
||||||
fuzzy_extrusion_line(*extrusion, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_dist.value));
|
fuzzy_extrusion_line(*extrusion, scaled<float>(params.config.fuzzy_skin_thickness.value), scaled<float>(params.config.fuzzy_skin_point_dist.value));
|
||||||
|
|
||||||
ExtrusionPaths paths;
|
ExtrusionPaths paths;
|
||||||
// detect overhanging/bridging perimeters
|
// detect overhanging/bridging perimeters
|
||||||
if (perimeter_generator.config->overhangs && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers
|
if (params.config.overhangs && params.layer_id > params.object_config.raft_layers
|
||||||
&& ! ((perimeter_generator.object_config->support_material || perimeter_generator.object_config->support_material_enforce_layers > 0) &&
|
&& ! ((params.object_config.support_material || params.object_config.support_material_enforce_layers > 0) &&
|
||||||
perimeter_generator.object_config->support_material_contact_distance.value == 0)) {
|
params.object_config.support_material_contact_distance.value == 0)) {
|
||||||
|
|
||||||
ClipperLib_Z::Path extrusion_path;
|
ClipperLib_Z::Path extrusion_path;
|
||||||
extrusion_path.reserve(extrusion->size());
|
extrusion_path.reserve(extrusion->size());
|
||||||
@ -462,8 +470,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w);
|
extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w);
|
||||||
|
|
||||||
ClipperLib_Z::Paths lower_slices_paths;
|
ClipperLib_Z::Paths lower_slices_paths;
|
||||||
lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size());
|
lower_slices_paths.reserve(lower_slices_polygons_cache.size());
|
||||||
for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) {
|
for (const Polygon &poly : lower_slices_polygons_cache) {
|
||||||
lower_slices_paths.emplace_back();
|
lower_slices_paths.emplace_back();
|
||||||
ClipperLib_Z::Path &out = lower_slices_paths.back();
|
ClipperLib_Z::Path &out = lower_slices_paths.back();
|
||||||
out.reserve(poly.points.size());
|
out.reserve(poly.points.size());
|
||||||
@ -473,13 +481,13 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
|
|
||||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
|
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
|
||||||
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
is_external ? params.ext_perimeter_flow : params.perimeter_flow);
|
||||||
|
|
||||||
// get overhang paths by checking what parts of this loop fall
|
// get overhang paths by checking what parts of this loop fall
|
||||||
// outside the grown lower slices (thus where the distance between
|
// outside the grown lower slices (thus where the distance between
|
||||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||||
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter,
|
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter,
|
||||||
perimeter_generator.overhang_flow);
|
params.overhang_flow);
|
||||||
|
|
||||||
// Reapply the nearest point search for starting point.
|
// Reapply the nearest point search for starting point.
|
||||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||||
@ -519,7 +527,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
chain_and_reorder_extrusion_paths(paths, &start_point);
|
chain_and_reorder_extrusion_paths(paths, &start_point);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
extrusion_paths_append(paths, *extrusion, role, is_external ? params.ext_perimeter_flow : params.perimeter_flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append paths to collection.
|
// Append paths to collection.
|
||||||
@ -590,42 +598,48 @@ static void export_perimeters_to_svg(const std::string &path, const Polygons &co
|
|||||||
|
|
||||||
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
||||||
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
||||||
void PerimeterGenerator::process_arachne()
|
void PerimeterGenerator::process_arachne(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const SurfaceCollection *slices,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
// Output:
|
||||||
|
// Loops with the external thin walls
|
||||||
|
ExtrusionEntityCollection &out_loops,
|
||||||
|
// Gaps without the thin walls
|
||||||
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
|
// Infills without the gap fills
|
||||||
|
SurfaceCollection &out_fill_surfaces)
|
||||||
{
|
{
|
||||||
// other perimeters
|
// other perimeters
|
||||||
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
|
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||||
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
|
|
||||||
|
|
||||||
// external perimeters
|
// external perimeters
|
||||||
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
|
||||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
|
||||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
|
||||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
|
||||||
|
|
||||||
// overhang perimeters
|
|
||||||
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
|
||||||
|
|
||||||
// solid infill
|
// solid infill
|
||||||
coord_t solid_infill_spacing = this->solid_infill_flow.scaled_spacing();
|
coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
|
||||||
|
|
||||||
// prepare grown lower layer slices for overhang detection
|
// prepare grown lower layer slices for overhang detection
|
||||||
if (this->lower_slices != nullptr && this->config->overhangs) {
|
if (lower_slices != nullptr && params.config.overhangs) {
|
||||||
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||||
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||||
// in the current layer
|
// in the current layer
|
||||||
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
|
double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
||||||
m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
|
lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to process each island separately because we might have different
|
// we need to process each island separately because we might have different
|
||||||
// extra perimeters for each one
|
// extra perimeters for each one
|
||||||
for (const Surface &surface : this->slices->surfaces) {
|
for (const Surface &surface : slices->surfaces) {
|
||||||
// detect how many perimeters must be generated for this island
|
// detect how many perimeters must be generated for this island
|
||||||
int loop_number = this->config->perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||||
Polygons last_p = to_polygons(last);
|
Polygons last_p = to_polygons(last);
|
||||||
|
|
||||||
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, *this->object_config, *this->print_config);
|
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||||
loop_number = int(perimeters.size()) - 1;
|
loop_number = int(perimeters.size()) - 1;
|
||||||
|
|
||||||
@ -651,7 +665,7 @@ void PerimeterGenerator::process_arachne()
|
|||||||
int end_perimeter = -1;
|
int end_perimeter = -1;
|
||||||
int direction = -1;
|
int direction = -1;
|
||||||
|
|
||||||
if (this->config->external_perimeters_first) {
|
if (params.config.external_perimeters_first) {
|
||||||
start_perimeter = 0;
|
start_perimeter = 0;
|
||||||
end_perimeter = int(perimeters.size());
|
end_perimeter = int(perimeters.size());
|
||||||
direction = 1;
|
direction = 1;
|
||||||
@ -672,7 +686,7 @@ void PerimeterGenerator::process_arachne()
|
|||||||
for (size_t idx = 0; idx < all_extrusions.size(); idx++)
|
for (size_t idx = 0; idx < all_extrusions.size(); idx++)
|
||||||
map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
|
map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
|
||||||
|
|
||||||
auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, this->config->external_perimeters_first);
|
auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
|
||||||
for (auto [before, after] : extrusions_constrains) {
|
for (auto [before, after] : extrusions_constrains) {
|
||||||
auto after_it = map_extrusion_to_idx.find(after);
|
auto after_it = map_extrusion_to_idx.find(after);
|
||||||
++blocked[after_it->second];
|
++blocked[after_it->second];
|
||||||
@ -736,18 +750,18 @@ void PerimeterGenerator::process_arachne()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->layer_id > 0 && this->config->fuzzy_skin != FuzzySkinType::None) {
|
if (params.layer_id > 0 && params.config.fuzzy_skin != FuzzySkinType::None) {
|
||||||
std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
|
std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
|
||||||
for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
|
for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
|
||||||
if (extrusion.extrusion->inset_idx == 0) {
|
if (extrusion.extrusion->inset_idx == 0) {
|
||||||
if (extrusion.extrusion->is_closed && this->config->fuzzy_skin == FuzzySkinType::External) {
|
if (extrusion.extrusion->is_closed && params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||||
closed_loop_extrusions.emplace_back(&extrusion);
|
closed_loop_extrusions.emplace_back(&extrusion);
|
||||||
} else {
|
} else {
|
||||||
extrusion.fuzzify = true;
|
extrusion.fuzzify = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->config->fuzzy_skin == FuzzySkinType::External) {
|
if (params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||||
ClipperLib_Z::Paths loops_paths;
|
ClipperLib_Z::Paths loops_paths;
|
||||||
loops_paths.reserve(closed_loop_extrusions.size());
|
loops_paths.reserve(closed_loop_extrusions.size());
|
||||||
for (const auto &cl_extrusion : closed_loop_extrusions) {
|
for (const auto &cl_extrusion : closed_loop_extrusions) {
|
||||||
@ -776,8 +790,8 @@ void PerimeterGenerator::process_arachne()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
|
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||||
this->loops->append(extrusion_coll);
|
out_loops.append(extrusion_coll);
|
||||||
|
|
||||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
@ -796,14 +810,14 @@ void PerimeterGenerator::process_arachne()
|
|||||||
// two or more loops?
|
// two or more loops?
|
||||||
perimeter_spacing;
|
perimeter_spacing;
|
||||||
|
|
||||||
inset = coord_t(scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset))));
|
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
for (ExPolygon &ex : infill_contour)
|
for (ExPolygon &ex : infill_contour)
|
||||||
ex.simplify_p(m_scaled_resolution, &pp);
|
ex.simplify_p(params.scaled_resolution, &pp);
|
||||||
// collapse too narrow infill areas
|
// collapse too narrow infill areas
|
||||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||||
// append infill areas to fill_surfaces
|
// append infill areas to fill_surfaces
|
||||||
this->fill_surfaces->append(
|
out_fill_surfaces.append(
|
||||||
offset2_ex(
|
offset2_ex(
|
||||||
union_ex(pp),
|
union_ex(pp),
|
||||||
float(- min_perimeter_infill_spacing / 2.),
|
float(- min_perimeter_infill_spacing / 2.),
|
||||||
@ -812,24 +826,30 @@ void PerimeterGenerator::process_arachne()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerimeterGenerator::process_classic()
|
void PerimeterGenerator::process_classic(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const SurfaceCollection *slices,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
// Output:
|
||||||
|
// Loops with the external thin walls
|
||||||
|
ExtrusionEntityCollection &out_loops,
|
||||||
|
// Gaps without the thin walls
|
||||||
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
|
// Infills without the gap fills
|
||||||
|
SurfaceCollection &out_fill_surfaces)
|
||||||
{
|
{
|
||||||
// other perimeters
|
// other perimeters
|
||||||
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
|
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||||
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
|
|
||||||
|
|
||||||
// external perimeters
|
// external perimeters
|
||||||
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
|
||||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
|
||||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
|
||||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
|
||||||
|
|
||||||
// overhang perimeters
|
|
||||||
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
|
||||||
|
|
||||||
// solid infill
|
// solid infill
|
||||||
coord_t solid_infill_spacing = this->solid_infill_flow.scaled_spacing();
|
coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
|
||||||
|
|
||||||
// Calculate the minimum required spacing between two adjacent traces.
|
// Calculate the minimum required spacing between two adjacent traces.
|
||||||
// This should be equal to the nominal flow spacing but we experiment
|
// This should be equal to the nominal flow spacing but we experiment
|
||||||
@ -843,23 +863,23 @@ void PerimeterGenerator::process_classic()
|
|||||||
// internal flow which is unrelated.
|
// internal flow which is unrelated.
|
||||||
coord_t min_spacing = coord_t(perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
coord_t min_spacing = coord_t(perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
||||||
coord_t ext_min_spacing = coord_t(ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
coord_t ext_min_spacing = coord_t(ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
|
||||||
bool has_gap_fill = this->config->gap_fill_enabled.value && this->config->gap_fill_speed.value > 0;
|
bool has_gap_fill = params.config.gap_fill_enabled.value && params.config.gap_fill_speed.value > 0;
|
||||||
|
|
||||||
// prepare grown lower layer slices for overhang detection
|
// prepare grown lower layer slices for overhang detection
|
||||||
if (this->lower_slices != NULL && this->config->overhangs) {
|
if (lower_slices != nullptr && params.config.overhangs) {
|
||||||
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||||
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||||
// in the current layer
|
// in the current layer
|
||||||
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
|
double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
||||||
m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
|
lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to process each island separately because we might have different
|
// we need to process each island separately because we might have different
|
||||||
// extra perimeters for each one
|
// extra perimeters for each one
|
||||||
for (const Surface &surface : this->slices->surfaces) {
|
for (const Surface &surface : slices->surfaces) {
|
||||||
// detect how many perimeters must be generated for this island
|
// detect how many perimeters must be generated for this island
|
||||||
int loop_number = this->config->perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||||
ExPolygons last = union_ex(surface.expolygon.simplify_p(m_scaled_resolution));
|
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
|
||||||
ExPolygons gaps;
|
ExPolygons gaps;
|
||||||
if (loop_number >= 0) {
|
if (loop_number >= 0) {
|
||||||
// In case no perimeters are to be generated, loop_number will equal to -1.
|
// In case no perimeters are to be generated, loop_number will equal to -1.
|
||||||
@ -873,17 +893,17 @@ void PerimeterGenerator::process_classic()
|
|||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// the minimum thickness of a single loop is:
|
// the minimum thickness of a single loop is:
|
||||||
// ext_width/2 + ext_spacing/2 + spacing/2 + width/2
|
// ext_width/2 + ext_spacing/2 + spacing/2 + width/2
|
||||||
offsets = this->config->thin_walls ?
|
offsets = params.config.thin_walls ?
|
||||||
offset2_ex(
|
offset2_ex(
|
||||||
last,
|
last,
|
||||||
- float(ext_perimeter_width / 2. + ext_min_spacing / 2. - 1),
|
- float(ext_perimeter_width / 2. + ext_min_spacing / 2. - 1),
|
||||||
+ float(ext_min_spacing / 2. - 1)) :
|
+ float(ext_min_spacing / 2. - 1)) :
|
||||||
offset_ex(last, - float(ext_perimeter_width / 2.));
|
offset_ex(last, - float(ext_perimeter_width / 2.));
|
||||||
// look for thin walls
|
// look for thin walls
|
||||||
if (this->config->thin_walls) {
|
if (params.config.thin_walls) {
|
||||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||||
// (actually, something larger than that still may exist due to mitering or other causes)
|
// (actually, something larger than that still may exist due to mitering or other causes)
|
||||||
coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3));
|
coord_t min_width = coord_t(scale_(params.ext_perimeter_flow.nozzle_diameter() / 3));
|
||||||
ExPolygons expp = opening_ex(
|
ExPolygons expp = opening_ex(
|
||||||
// medial axis requires non-overlapping geometry
|
// medial axis requires non-overlapping geometry
|
||||||
diff_ex(last, offset(offsets, float(ext_perimeter_width / 2.) + ClipperSafetyOffset)),
|
diff_ex(last, offset(offsets, float(ext_perimeter_width / 2.) + ClipperSafetyOffset)),
|
||||||
@ -892,7 +912,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
for (ExPolygon &ex : expp)
|
for (ExPolygon &ex : expp)
|
||||||
ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
|
ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
|
||||||
}
|
}
|
||||||
if (m_spiral_vase && offsets.size() > 1) {
|
if (params.spiral_vase && offsets.size() > 1) {
|
||||||
// Remove all but the largest area polygon.
|
// Remove all but the largest area polygon.
|
||||||
keep_largest_contour_only(offsets);
|
keep_largest_contour_only(offsets);
|
||||||
}
|
}
|
||||||
@ -900,7 +920,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
//FIXME Is this offset correct if the line width of the inner perimeters differs
|
//FIXME Is this offset correct if the line width of the inner perimeters differs
|
||||||
// from the line width of the infill?
|
// from the line width of the infill?
|
||||||
coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
offsets = this->config->thin_walls ?
|
offsets = params.config.thin_walls ?
|
||||||
// This path will ensure, that the perimeters do not overfill, as in
|
// This path will ensure, that the perimeters do not overfill, as in
|
||||||
// prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters
|
// prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters
|
||||||
// excessively, creating gaps, which then need to be filled in by the not very
|
// excessively, creating gaps, which then need to be filled in by the not very
|
||||||
@ -933,8 +953,8 @@ void PerimeterGenerator::process_classic()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const bool fuzzify_contours = this->config->fuzzy_skin != FuzzySkinType::None && i == 0 && this->layer_id > 0;
|
const bool fuzzify_contours = params.config.fuzzy_skin != FuzzySkinType::None && i == 0 && params.layer_id > 0;
|
||||||
const bool fuzzify_holes = fuzzify_contours && this->config->fuzzy_skin == FuzzySkinType::All;
|
const bool fuzzify_holes = fuzzify_contours && params.config.fuzzy_skin == FuzzySkinType::All;
|
||||||
for (const ExPolygon &expolygon : offsets) {
|
for (const ExPolygon &expolygon : offsets) {
|
||||||
// Outer contour may overlap with an inner contour,
|
// Outer contour may overlap with an inner contour,
|
||||||
// inner contour may overlap with another inner contour,
|
// inner contour may overlap with another inner contour,
|
||||||
@ -951,7 +971,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
last = std::move(offsets);
|
last = std::move(offsets);
|
||||||
if (i == loop_number && (! has_gap_fill || this->config->fill_density.value == 0)) {
|
if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) {
|
||||||
// The last run of this loop is executed to collect gaps for gap fill.
|
// The last run of this loop is executed to collect gaps for gap fill.
|
||||||
// As the gap fill is either disabled or not
|
// As the gap fill is either disabled or not
|
||||||
break;
|
break;
|
||||||
@ -1013,16 +1033,16 @@ void PerimeterGenerator::process_classic()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// at this point, all loops should be in contours[0]
|
// at this point, all loops should be in contours[0]
|
||||||
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls);
|
ExtrusionEntityCollection entities = traverse_loops(params, lower_slices_polygons_cache, contours.front(), thin_walls);
|
||||||
// if brim will be printed, reverse the order of perimeters so that
|
// if brim will be printed, reverse the order of perimeters so that
|
||||||
// we continue inwards after having finished the brim
|
// we continue inwards after having finished the brim
|
||||||
// TODO: add test for perimeter order
|
// TODO: add test for perimeter order
|
||||||
if (this->config->external_perimeters_first ||
|
if (params.config.external_perimeters_first ||
|
||||||
(this->layer_id == 0 && this->object_config->brim_width.value > 0))
|
(params.layer_id == 0 && params.object_config.brim_width.value > 0))
|
||||||
entities.reverse();
|
entities.reverse();
|
||||||
// append perimeters for this slice as a collection
|
// append perimeters for this slice as a collection
|
||||||
if (! entities.empty())
|
if (! entities.empty())
|
||||||
this->loops->append(entities);
|
out_loops.append(entities);
|
||||||
} // for each loop of an island
|
} // for each loop of an island
|
||||||
|
|
||||||
// fill gaps
|
// fill gaps
|
||||||
@ -1039,7 +1059,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
ex.medial_axis(max, min, &polylines);
|
ex.medial_axis(max, min, &polylines);
|
||||||
if (! polylines.empty()) {
|
if (! polylines.empty()) {
|
||||||
ExtrusionEntityCollection gap_fill;
|
ExtrusionEntityCollection gap_fill;
|
||||||
variable_width(polylines, erGapFill, this->solid_infill_flow, gap_fill.entities);
|
variable_width(polylines, erGapFill, params.solid_infill_flow, gap_fill.entities);
|
||||||
/* Make sure we don't infill narrow parts that are already gap-filled
|
/* Make sure we don't infill narrow parts that are already gap-filled
|
||||||
(we only consider this surface's gaps to reduce the diff() complexity).
|
(we only consider this surface's gaps to reduce the diff() complexity).
|
||||||
Growing actual extrusions ensures that gaps not filled by medial axis
|
Growing actual extrusions ensures that gaps not filled by medial axis
|
||||||
@ -1049,7 +1069,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
|
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
|
||||||
// therefore it may cover the area, but no the volume.
|
// therefore it may cover the area, but no the volume.
|
||||||
last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
|
last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
|
||||||
this->gap_fill->append(std::move(gap_fill.entities));
|
out_gap_fill.append(std::move(gap_fill.entities));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1066,15 +1086,15 @@ void PerimeterGenerator::process_classic()
|
|||||||
perimeter_spacing / 2;
|
perimeter_spacing / 2;
|
||||||
// only apply infill overlap if we actually have one perimeter
|
// only apply infill overlap if we actually have one perimeter
|
||||||
if (inset > 0)
|
if (inset > 0)
|
||||||
inset -= coord_t(scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
|
inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
|
||||||
// simplify infill contours according to resolution
|
// simplify infill contours according to resolution
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
for (ExPolygon &ex : last)
|
for (ExPolygon &ex : last)
|
||||||
ex.simplify_p(m_scaled_resolution, &pp);
|
ex.simplify_p(params.scaled_resolution, &pp);
|
||||||
// collapse too narrow infill areas
|
// collapse too narrow infill areas
|
||||||
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||||
// append infill areas to fill_surfaces
|
// append infill areas to fill_surfaces
|
||||||
this->fill_surfaces->append(
|
out_fill_surfaces.append(
|
||||||
offset2_ex(
|
offset2_ex(
|
||||||
union_ex(pp),
|
union_ex(pp),
|
||||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||||
@ -1083,15 +1103,4 @@ void PerimeterGenerator::process_classic()
|
|||||||
} // for each island
|
} // for each island
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PerimeterGeneratorLoop::is_internal_contour() const
|
|
||||||
{
|
|
||||||
// An internal contour is a contour containing no other contours
|
|
||||||
if (! this->is_contour)
|
|
||||||
return false;
|
|
||||||
for (const PerimeterGeneratorLoop &loop : this->children)
|
|
||||||
if (loop.is_contour)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,70 +10,93 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class PerimeterGenerator {
|
namespace PerimeterGenerator
|
||||||
public:
|
{
|
||||||
// Inputs:
|
|
||||||
const SurfaceCollection *slices;
|
struct Parameters {
|
||||||
const ExPolygons *lower_slices;
|
Parameters(
|
||||||
|
double layer_height,
|
||||||
|
int layer_id,
|
||||||
|
Flow perimeter_flow,
|
||||||
|
Flow ext_perimeter_flow,
|
||||||
|
Flow overhang_flow,
|
||||||
|
Flow solid_infill_flow,
|
||||||
|
const PrintRegionConfig &config,
|
||||||
|
const PrintObjectConfig &object_config,
|
||||||
|
const PrintConfig &print_config,
|
||||||
|
const bool spiral_vase) :
|
||||||
|
layer_height(layer_height),
|
||||||
|
layer_id(layer_id),
|
||||||
|
perimeter_flow(perimeter_flow),
|
||||||
|
ext_perimeter_flow(ext_perimeter_flow),
|
||||||
|
overhang_flow(overhang_flow),
|
||||||
|
solid_infill_flow(solid_infill_flow),
|
||||||
|
config(config),
|
||||||
|
object_config(object_config),
|
||||||
|
print_config(print_config),
|
||||||
|
spiral_vase(spiral_vase),
|
||||||
|
scaled_resolution(scaled<double>(print_config.gcode_resolution.value)),
|
||||||
|
mm3_per_mm(perimeter_flow.mm3_per_mm()),
|
||||||
|
ext_mm3_per_mm(ext_perimeter_flow.mm3_per_mm()),
|
||||||
|
mm3_per_mm_overhang(overhang_flow.mm3_per_mm())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input parameters
|
||||||
double layer_height;
|
double layer_height;
|
||||||
int layer_id;
|
int layer_id;
|
||||||
Flow perimeter_flow;
|
Flow perimeter_flow;
|
||||||
Flow ext_perimeter_flow;
|
Flow ext_perimeter_flow;
|
||||||
Flow overhang_flow;
|
Flow overhang_flow;
|
||||||
Flow solid_infill_flow;
|
Flow solid_infill_flow;
|
||||||
const PrintRegionConfig *config;
|
const PrintRegionConfig &config;
|
||||||
const PrintObjectConfig *object_config;
|
const PrintObjectConfig &object_config;
|
||||||
const PrintConfig *print_config;
|
const PrintConfig &print_config;
|
||||||
// Outputs:
|
|
||||||
ExtrusionEntityCollection *loops;
|
|
||||||
ExtrusionEntityCollection *gap_fill;
|
|
||||||
SurfaceCollection *fill_surfaces;
|
|
||||||
|
|
||||||
PerimeterGenerator(
|
|
||||||
// Input:
|
|
||||||
const SurfaceCollection* slices,
|
|
||||||
double layer_height,
|
|
||||||
Flow flow,
|
|
||||||
const PrintRegionConfig* config,
|
|
||||||
const PrintObjectConfig* object_config,
|
|
||||||
const PrintConfig* print_config,
|
|
||||||
const bool spiral_vase,
|
|
||||||
// Output:
|
|
||||||
// Loops with the external thin walls
|
|
||||||
ExtrusionEntityCollection* loops,
|
|
||||||
// Gaps without the thin walls
|
|
||||||
ExtrusionEntityCollection* gap_fill,
|
|
||||||
// Infills without the gap fills
|
|
||||||
SurfaceCollection* fill_surfaces)
|
|
||||||
: slices(slices), lower_slices(nullptr), layer_height(layer_height),
|
|
||||||
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
|
|
||||||
overhang_flow(flow), solid_infill_flow(flow),
|
|
||||||
config(config), object_config(object_config), print_config(print_config),
|
|
||||||
m_spiral_vase(spiral_vase),
|
|
||||||
m_scaled_resolution(scaled<double>(print_config->gcode_resolution.value)),
|
|
||||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
|
|
||||||
m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void process_classic();
|
// Derived parameters
|
||||||
void process_arachne();
|
bool spiral_vase;
|
||||||
|
double scaled_resolution;
|
||||||
double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; }
|
double ext_mm3_per_mm;
|
||||||
double mm3_per_mm() const { return m_mm3_per_mm; }
|
double mm3_per_mm;
|
||||||
double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; }
|
double mm3_per_mm_overhang;
|
||||||
Polygons lower_slices_polygons() const { return m_lower_slices_polygons; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_spiral_vase;
|
Parameters() = delete;
|
||||||
double m_scaled_resolution;
|
|
||||||
double m_ext_mm3_per_mm;
|
|
||||||
double m_mm3_per_mm;
|
|
||||||
double m_mm3_per_mm_overhang;
|
|
||||||
Polygons m_lower_slices_polygons;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void process_classic(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const SurfaceCollection *slices,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
// Output:
|
||||||
|
// Loops with the external thin walls
|
||||||
|
ExtrusionEntityCollection &out_loops,
|
||||||
|
// Gaps without the thin walls
|
||||||
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
|
// Infills without the gap fills
|
||||||
|
SurfaceCollection &out_fill_surfaces);
|
||||||
|
|
||||||
|
void process_arachne(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const SurfaceCollection *slices,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
// Output:
|
||||||
|
// Loops with the external thin walls
|
||||||
|
ExtrusionEntityCollection &out_loops,
|
||||||
|
// Gaps without the thin walls
|
||||||
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
|
// Infills without the gap fills
|
||||||
|
SurfaceCollection &out_fill_surfaces);
|
||||||
|
|
||||||
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);
|
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);
|
||||||
|
|
||||||
}
|
} // namespace PerimeterGenerator
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,21 +45,29 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
|
|||||||
ExtrusionEntityCollection loops;
|
ExtrusionEntityCollection loops;
|
||||||
ExtrusionEntityCollection gap_fill;
|
ExtrusionEntityCollection gap_fill;
|
||||||
SurfaceCollection fill_surfaces;
|
SurfaceCollection fill_surfaces;
|
||||||
PerimeterGenerator perimeter_generator(
|
Flow flow(1., 1., 1.);
|
||||||
&slices,
|
PerimeterGenerator::Parameters perimeter_generator_params(
|
||||||
1., // layer height
|
1., // layer height
|
||||||
Flow(1., 1., 1.),
|
-1, // layer ID
|
||||||
static_cast<const PrintRegionConfig*>(&config),
|
flow, flow, flow, flow,
|
||||||
static_cast<const PrintObjectConfig*>(&config),
|
static_cast<const PrintRegionConfig&>(config),
|
||||||
static_cast<const PrintConfig*>(&config),
|
static_cast<const PrintObjectConfig&>(config),
|
||||||
false, // spiral_vase
|
static_cast<const PrintConfig&>(config),
|
||||||
// output:
|
false); // spiral_vase
|
||||||
&loops, &gap_fill, &fill_surfaces);
|
Polygons lower_layer_polygons_cache;
|
||||||
// FIXME Lukas H.: Disable this test for Arachne because it is failing and needs more investigation.
|
// FIXME Lukas H.: Disable this test for Arachne because it is failing and needs more investigation.
|
||||||
// if (config.perimeter_generator == PerimeterGeneratorType::Arachne)
|
// if (config.perimeter_generator == PerimeterGeneratorType::Arachne)
|
||||||
// perimeter_generator.process_arachne();
|
// PerimeterGenerator::process_arachne();
|
||||||
// else
|
// else
|
||||||
perimeter_generator.process_classic();
|
PerimeterGenerator::process_classic(
|
||||||
|
// input:
|
||||||
|
perimeter_generator_params,
|
||||||
|
&slices,
|
||||||
|
nullptr,
|
||||||
|
// cache:
|
||||||
|
lower_layer_polygons_cache,
|
||||||
|
// output:
|
||||||
|
loops, gap_fill, fill_surfaces);
|
||||||
|
|
||||||
THEN("expected number of collections") {
|
THEN("expected number of collections") {
|
||||||
REQUIRE(loops.entities.size() == data.expolygons.size());
|
REQUIRE(loops.entities.size() == data.expolygons.size());
|
||||||
|
Loading…
Reference in New Issue
Block a user