mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-05 02:11:52 +03:00
update top_one_wall & top_gap_fills
This commit is contained in:
@@ -231,14 +231,16 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||||||
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
|
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
|
||||||
//w21
|
//w21
|
||||||
fill.region_id_group.push_back(region_id);
|
fill.region_id_group.push_back(region_id);
|
||||||
//fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons;
|
//w21
|
||||||
|
fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons;
|
||||||
} else {
|
} else {
|
||||||
//w21
|
//w21
|
||||||
fill.expolygons.emplace_back(surface.expolygon);
|
fill.expolygons.emplace_back(surface.expolygon);
|
||||||
auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id);
|
auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id);
|
||||||
if (t == fill.region_id_group.end()) {
|
if (t == fill.region_id_group.end()) {
|
||||||
fill.region_id_group.push_back(region_id);
|
fill.region_id_group.push_back(region_id);
|
||||||
//fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons);
|
//w21
|
||||||
|
fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -621,6 +621,8 @@ void Layer::make_perimeters()
|
|||||||
std::vector<std::pair<ExtrusionRange, ExtrusionRange>> perimeter_and_gapfill_ranges;
|
std::vector<std::pair<ExtrusionRange, ExtrusionRange>> perimeter_and_gapfill_ranges;
|
||||||
ExPolygons fill_expolygons;
|
ExPolygons fill_expolygons;
|
||||||
std::vector<ExPolygonRange> fill_expolygons_ranges;
|
std::vector<ExPolygonRange> fill_expolygons_ranges;
|
||||||
|
//w21
|
||||||
|
ExPolygons fill_no_overlap_expolygons;
|
||||||
SurfacesPtr surfaces_to_merge;
|
SurfacesPtr surfaces_to_merge;
|
||||||
SurfacesPtr surfaces_to_merge_temp;
|
SurfacesPtr surfaces_to_merge_temp;
|
||||||
|
|
||||||
@@ -690,7 +692,8 @@ void Layer::make_perimeters()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (layer_region_ids.size() == 1) { // optimization
|
if (layer_region_ids.size() == 1) { // optimization
|
||||||
(*layerm)->make_perimeters((*layerm)->slices(), perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges);
|
//w21
|
||||||
|
(*layerm)->make_perimeters((*layerm)->slices(), perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges,(*layerm)->fill_no_overlap_expolygons);
|
||||||
this->sort_perimeters_into_islands((*layerm)->slices(), region_id, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
|
this->sort_perimeters_into_islands((*layerm)->slices(), region_id, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
|
||||||
} else {
|
} else {
|
||||||
SurfaceCollection new_slices;
|
SurfaceCollection new_slices;
|
||||||
@@ -725,8 +728,24 @@ void Layer::make_perimeters()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make perimeters
|
// make perimeters
|
||||||
layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges);
|
//w21
|
||||||
|
ExPolygons fill_no_overlap;
|
||||||
|
layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges,fill_no_overlap);
|
||||||
this->sort_perimeters_into_islands(new_slices, region_id_config, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
|
this->sort_perimeters_into_islands(new_slices, region_id_config, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
|
||||||
|
//w21
|
||||||
|
if (!new_slices.surfaces.empty()) {
|
||||||
|
for (size_t idx : layer_region_ids) {
|
||||||
|
// Separate the fill surfaces.
|
||||||
|
LayerRegion &layer = *m_regions[idx];
|
||||||
|
ExPolygons expp = intersection_ex(new_slices.surfaces, layer.slices().surfaces);
|
||||||
|
layer.m_fill_expolygons = std::move(expp);
|
||||||
|
if (!layer.m_fill_expolygons.empty()) {
|
||||||
|
layer.m_fill_surfaces.set(std::move(layer.m_fill_expolygons), layer.slices().surfaces.front());
|
||||||
|
layer.fill_no_overlap_expolygons = intersection_ex(layer.slices().surfaces, fill_no_overlap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ void LayerRegion::make_perimeters(
|
|||||||
// All fill areas produced for all input slices above.
|
// All fill areas produced for all input slices above.
|
||||||
ExPolygons &fill_expolygons,
|
ExPolygons &fill_expolygons,
|
||||||
// Ranges of fill areas above per input slice.
|
// Ranges of fill areas above per input slice.
|
||||||
std::vector<ExPolygonRange> &fill_expolygons_ranges)
|
std::vector<ExPolygonRange> &fill_expolygons_ranges,
|
||||||
|
//w21
|
||||||
|
ExPolygons &fill_no_overlap_expolygons)
|
||||||
{
|
{
|
||||||
m_perimeters.clear();
|
m_perimeters.clear();
|
||||||
m_thin_fills.clear();
|
m_thin_fills.clear();
|
||||||
@@ -106,8 +108,12 @@ void LayerRegion::make_perimeters(
|
|||||||
|
|
||||||
// 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;
|
const ExPolygons *lower_slices = this->layer()->lower_layer ? &this->layer()->lower_layer->lslices : nullptr;
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices = this->layer()->upper_layer ? &this->layer()->upper_layer->lslices : nullptr;
|
||||||
// Cache for offsetted lower_slices
|
// Cache for offsetted lower_slices
|
||||||
Polygons lower_layer_polygons_cache;
|
Polygons lower_layer_polygons_cache;
|
||||||
|
//w16
|
||||||
|
Polygons upper_layer_polygons_cache;
|
||||||
|
|
||||||
for (const Surface &surface : slices) {
|
for (const Surface &surface : slices) {
|
||||||
auto perimeters_begin = uint32_t(m_perimeters.size());
|
auto perimeters_begin = uint32_t(m_perimeters.size());
|
||||||
@@ -115,16 +121,38 @@ void LayerRegion::make_perimeters(
|
|||||||
auto fill_expolygons_begin = uint32_t(fill_expolygons.size());
|
auto fill_expolygons_begin = uint32_t(fill_expolygons.size());
|
||||||
if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase)
|
if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase)
|
||||||
|
|
||||||
|
//w16
|
||||||
|
if (this->layer()->object()->config().top_one_wall_type == TopOneWallType::Alltop)
|
||||||
|
PerimeterGenerator::process_with_one_wall_arachne(
|
||||||
|
// input:
|
||||||
|
params,
|
||||||
|
surface,
|
||||||
|
lower_slices,
|
||||||
|
//w16
|
||||||
|
upper_slices,
|
||||||
|
lower_layer_polygons_cache,
|
||||||
|
upper_layer_polygons_cache,
|
||||||
|
// output:
|
||||||
|
m_perimeters,
|
||||||
|
m_thin_fills,
|
||||||
|
fill_expolygons,
|
||||||
|
//w21
|
||||||
|
fill_no_overlap_expolygons);
|
||||||
|
else
|
||||||
PerimeterGenerator::process_arachne(
|
PerimeterGenerator::process_arachne(
|
||||||
// input:
|
// input:
|
||||||
params,
|
params,
|
||||||
surface,
|
surface,
|
||||||
lower_slices,
|
lower_slices,
|
||||||
|
//w16
|
||||||
|
upper_slices,
|
||||||
lower_layer_polygons_cache,
|
lower_layer_polygons_cache,
|
||||||
// output:
|
// output:
|
||||||
m_perimeters,
|
m_perimeters,
|
||||||
m_thin_fills,
|
m_thin_fills,
|
||||||
fill_expolygons);
|
fill_expolygons,
|
||||||
|
//w21
|
||||||
|
fill_no_overlap_expolygons);
|
||||||
|
|
||||||
else
|
else
|
||||||
PerimeterGenerator::process_classic(
|
PerimeterGenerator::process_classic(
|
||||||
@@ -132,11 +160,16 @@ void LayerRegion::make_perimeters(
|
|||||||
params,
|
params,
|
||||||
surface,
|
surface,
|
||||||
lower_slices,
|
lower_slices,
|
||||||
|
//w16
|
||||||
|
upper_slices,
|
||||||
lower_layer_polygons_cache,
|
lower_layer_polygons_cache,
|
||||||
|
upper_layer_polygons_cache,
|
||||||
// output:
|
// output:
|
||||||
m_perimeters,
|
m_perimeters,
|
||||||
m_thin_fills,
|
m_thin_fills,
|
||||||
fill_expolygons);
|
fill_expolygons,
|
||||||
|
//w21
|
||||||
|
fill_no_overlap_expolygons);
|
||||||
perimeter_and_gapfill_ranges.emplace_back(
|
perimeter_and_gapfill_ranges.emplace_back(
|
||||||
ExtrusionRange{ perimeters_begin, uint32_t(m_perimeters.size()) },
|
ExtrusionRange{ perimeters_begin, uint32_t(m_perimeters.size()) },
|
||||||
ExtrusionRange{ gap_fills_begin, uint32_t(m_thin_fills.size()) });
|
ExtrusionRange{ gap_fills_begin, uint32_t(m_thin_fills.size()) });
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ public:
|
|||||||
// ordered collection of extrusion paths to fill surfaces
|
// ordered collection of extrusion paths to fill surfaces
|
||||||
// (this collection contains only ExtrusionEntityCollection objects)
|
// (this collection contains only ExtrusionEntityCollection objects)
|
||||||
[[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; }
|
[[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; }
|
||||||
|
//w21
|
||||||
|
ExPolygons fill_no_overlap_expolygons;
|
||||||
|
|
||||||
Flow flow(FlowRole role) const;
|
Flow flow(FlowRole role) const;
|
||||||
Flow flow(FlowRole role, double layer_height) const;
|
Flow flow(FlowRole role, double layer_height) const;
|
||||||
@@ -109,7 +111,9 @@ public:
|
|||||||
// All fill areas produced for all input slices above.
|
// All fill areas produced for all input slices above.
|
||||||
ExPolygons &fill_expolygons,
|
ExPolygons &fill_expolygons,
|
||||||
// Ranges of fill areas above per input slice.
|
// Ranges of fill areas above per input slice.
|
||||||
std::vector<ExPolygonRange> &fill_expolygons_ranges);
|
std::vector<ExPolygonRange> &fill_expolygons_ranges,
|
||||||
|
//w21
|
||||||
|
ExPolygons &fill_no_overlap_expolygons);
|
||||||
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
|
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
|
||||||
double infill_area_threshold() const;
|
double infill_area_threshold() const;
|
||||||
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
|
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
|
||||||
|
|||||||
@@ -1070,6 +1070,40 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//w16
|
||||||
|
void PerimeterGenerator::add_infill_contour_for_arachne(ExPolygons infill_contour,
|
||||||
|
int loops,
|
||||||
|
coord_t ext_perimeter_spacing,
|
||||||
|
coord_t perimeter_spacing,
|
||||||
|
coord_t min_perimeter_infill_spacing,
|
||||||
|
coord_t spacing,
|
||||||
|
bool is_inner_part,
|
||||||
|
const Parameters ¶ms,
|
||||||
|
ExPolygons &infill_areas,
|
||||||
|
ExPolygons & out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons & out_fill_no_overlap)
|
||||||
|
{
|
||||||
|
if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) {
|
||||||
|
infill_contour.clear();
|
||||||
|
}
|
||||||
|
coord_t insert = (loops < 0) ? 0 : ext_perimeter_spacing;
|
||||||
|
if (is_inner_part || loops > 0)
|
||||||
|
insert = perimeter_spacing;
|
||||||
|
|
||||||
|
insert = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(insert))));
|
||||||
|
Polygons inner_pp;
|
||||||
|
for (ExPolygon &ex : infill_contour)
|
||||||
|
ex.simplify_p(params.scaled_resolution, &inner_pp);
|
||||||
|
ExPolygons inner_union = union_ex(inner_pp);
|
||||||
|
float offset1 = -min_perimeter_infill_spacing / 2.;
|
||||||
|
float offset2 = insert + min_perimeter_infill_spacing / 2.;
|
||||||
|
infill_areas = offset2_ex(inner_union, offset1, offset2);
|
||||||
|
append(out_fill_expolygons, offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(insert + min_perimeter_infill_spacing / 2.)));
|
||||||
|
//w21
|
||||||
|
append(out_fill_no_overlap,offset2_ex(inner_union, float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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"
|
||||||
@@ -1078,6 +1112,7 @@ void PerimeterGenerator::process_arachne(
|
|||||||
const Parameters ¶ms,
|
const Parameters ¶ms,
|
||||||
const Surface &surface,
|
const Surface &surface,
|
||||||
const ExPolygons *lower_slices,
|
const ExPolygons *lower_slices,
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
// Cache:
|
// Cache:
|
||||||
Polygons &lower_slices_polygons_cache,
|
Polygons &lower_slices_polygons_cache,
|
||||||
// Output:
|
// Output:
|
||||||
@@ -1086,7 +1121,9 @@ void PerimeterGenerator::process_arachne(
|
|||||||
// Gaps without the thin walls
|
// Gaps without the thin walls
|
||||||
ExtrusionEntityCollection & /* out_gap_fill */,
|
ExtrusionEntityCollection & /* out_gap_fill */,
|
||||||
// Infills without the gap fills
|
// Infills without the gap fills
|
||||||
ExPolygons &out_fill_expolygons)
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap)
|
||||||
{
|
{
|
||||||
// other perimeters
|
// other perimeters
|
||||||
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||||
@@ -1113,6 +1150,9 @@ void PerimeterGenerator::process_arachne(
|
|||||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.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);
|
||||||
|
|
||||||
|
//w16
|
||||||
|
if (upper_slices == nullptr && params.object_config.top_one_wall_type == TopOneWallType::Onlytopmost)
|
||||||
|
loop_number = 0;
|
||||||
|
|
||||||
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);
|
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();
|
||||||
@@ -1269,10 +1309,21 @@ void PerimeterGenerator::process_arachne(
|
|||||||
out_loops.append(extrusion_coll);
|
out_loops.append(extrusion_coll);
|
||||||
|
|
||||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
|
//w17
|
||||||
|
ExPolygons the_layer_surface = infill_contour;
|
||||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
||||||
infill_contour.clear(); // Infill region is too small, so let's filter it out.
|
infill_contour.clear(); // Infill region is too small, so let's filter it out.
|
||||||
|
if (params.object_config.top_one_wall_type != TopOneWallType::Disable) {
|
||||||
|
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||||
|
double min_width_top_surface = (params.object_config.top_area_threshold / 100) *
|
||||||
|
std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4));
|
||||||
|
infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width);
|
||||||
|
ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BBS: get real top surface
|
||||||
|
infill_contour = intersection_ex(infill_contour, the_layer_surface);
|
||||||
// create one more offset to be used as boundary for fill
|
// create one more offset to be used as boundary for fill
|
||||||
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||||
// and then we offset back and forth by half the infill spacing to only consider the
|
// and then we offset back and forth by half the infill spacing to only consider the
|
||||||
@@ -1316,24 +1367,411 @@ void PerimeterGenerator::process_arachne(
|
|||||||
infill_areas = diff_ex(infill_areas, filled_area);
|
infill_areas = diff_ex(infill_areas, filled_area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//w21
|
||||||
|
append(out_fill_no_overlap, offset2_ex(union_ex(pp),float(-min_perimeter_infill_spacing / 2.), float( min_perimeter_infill_spacing / 2.)));
|
||||||
|
append(out_fill_expolygons, std::move(infill_areas));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerimeterGenerator::process_with_one_wall_arachne(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const Surface &surface,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
Polygons &upper_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
|
||||||
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap)
|
||||||
|
{
|
||||||
|
// other perimeters
|
||||||
|
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||||
|
// external perimeters
|
||||||
|
coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
|
||||||
|
coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
|
||||||
|
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
|
||||||
|
// solid infill
|
||||||
|
coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
|
||||||
|
|
||||||
|
// prepare grown lower layer slices for overhang detection
|
||||||
|
if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) {
|
||||||
|
// 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
|
||||||
|
// in the current layer
|
||||||
|
double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
||||||
|
lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
|
||||||
|
}
|
||||||
|
if (params.config.overhangs && upper_slices != nullptr && upper_slices_polygons_cache.empty()) {
|
||||||
|
double upper_nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder - 1);
|
||||||
|
upper_slices_polygons_cache = offset(*upper_slices, float(scale_(EPSILON)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// we need to process each island separately because we might have different
|
||||||
|
// extra perimeters for each one
|
||||||
|
// detect how many perimeters must be generated for this island
|
||||||
|
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||||
|
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);
|
||||||
|
|
||||||
|
int remain_loops = -1;
|
||||||
|
if (params.object_config.top_one_wall_type == TopOneWallType::Alltop) {
|
||||||
|
if (upper_slices != nullptr)
|
||||||
|
remain_loops = loop_number - 1;
|
||||||
|
|
||||||
|
loop_number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
loop_number = int(perimeters.size()) - 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//w16
|
||||||
|
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
|
ExPolygons inner_infill_contour;
|
||||||
|
|
||||||
|
if (remain_loops >= 0) {
|
||||||
|
|
||||||
|
ExPolygons the_layer_surface = infill_contour;
|
||||||
|
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||||
|
infill_contour_box.offset(SCALED_EPSILON);
|
||||||
|
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slices_polygons_cache,
|
||||||
|
infill_contour_box);
|
||||||
|
|
||||||
|
infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped);
|
||||||
|
|
||||||
|
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||||
|
if (lower_slices != nullptr) {
|
||||||
|
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||||
|
infill_contour_box.offset(SCALED_EPSILON);
|
||||||
|
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache,
|
||||||
|
infill_contour_box);
|
||||||
|
|
||||||
|
ExPolygons bridge_area = offset_ex(diff_ex(infill_contour, lower_polygons_series_clipped),
|
||||||
|
std::max(ext_perimeter_spacing, perimeter_width));
|
||||||
|
infill_contour = diff_ex(infill_contour, bridge_area);
|
||||||
|
}
|
||||||
|
//w17
|
||||||
|
// double min_width_top_surface = std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4));
|
||||||
|
double min_width_top_surface = (params.object_config.top_area_threshold / 100) * std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4));
|
||||||
|
infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width);
|
||||||
|
|
||||||
|
ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour);
|
||||||
|
|
||||||
|
infill_contour = intersection_ex(infill_contour, the_layer_surface);
|
||||||
|
Polygons surface_not_export_to_top_p = to_polygons(surface_not_export_to_top);
|
||||||
|
Arachne::WallToolPaths innerWallToolPaths(surface_not_export_to_top_p, perimeter_spacing, perimeter_spacing,
|
||||||
|
coord_t(remain_loops + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||||
|
|
||||||
|
std::vector<Arachne::VariableWidthLines> perimeters_inner = innerWallToolPaths.getToolPaths();
|
||||||
|
remain_loops = int(perimeters_inner.size()) - 1;
|
||||||
|
if (!perimeters.empty()) {
|
||||||
|
for (int perimeter_idx = 0; perimeter_idx < perimeters_inner.size(); perimeter_idx++) {
|
||||||
|
if (perimeters_inner[perimeter_idx].empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (Arachne::ExtrusionLine &wall : perimeters_inner[perimeter_idx]) {
|
||||||
|
wall.inset_idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end());
|
||||||
|
|
||||||
|
inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// All closed ExtrusionLine should have the same the first and the last point.
|
||||||
|
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
|
||||||
|
// equal the first and the last point.
|
||||||
|
assert([&perimeters = std::as_const(perimeters)]() -> bool {
|
||||||
|
for (const Arachne::VariableWidthLines &perimeter : perimeters)
|
||||||
|
for (const Arachne::ExtrusionLine &el : perimeter)
|
||||||
|
if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
int start_perimeter = int(perimeters.size()) - 1;
|
||||||
|
int end_perimeter = -1;
|
||||||
|
int direction = -1;
|
||||||
|
|
||||||
|
if (params.config.external_perimeters_first) {
|
||||||
|
start_perimeter = 0;
|
||||||
|
end_perimeter = int(perimeters.size());
|
||||||
|
direction = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Arachne::ExtrusionLine *> all_extrusions;
|
||||||
|
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
|
||||||
|
if (perimeters[perimeter_idx].empty())
|
||||||
|
continue;
|
||||||
|
for (Arachne::ExtrusionLine &wall : perimeters[perimeter_idx])
|
||||||
|
all_extrusions.emplace_back(&wall);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find topological order with constraints from extrusions_constrains.
|
||||||
|
std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
|
||||||
|
std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
|
||||||
|
ankerl::unordered_dense::map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx;
|
||||||
|
for (size_t idx = 0; idx < all_extrusions.size(); idx++)
|
||||||
|
map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
|
||||||
|
|
||||||
|
Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
|
||||||
|
for (auto [before, after] : extrusions_constrains) {
|
||||||
|
auto after_it = map_extrusion_to_idx.find(after);
|
||||||
|
++blocked[after_it->second];
|
||||||
|
blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
|
||||||
|
Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
|
||||||
|
std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions; // To store our result in. At the end we'll std::swap.
|
||||||
|
ordered_extrusions.reserve(all_extrusions.size());
|
||||||
|
|
||||||
|
while (ordered_extrusions.size() < all_extrusions.size()) {
|
||||||
|
size_t best_candidate = 0;
|
||||||
|
double best_distance_sqr = std::numeric_limits<double>::max();
|
||||||
|
bool is_best_closed = false;
|
||||||
|
|
||||||
|
std::vector<size_t> available_candidates;
|
||||||
|
for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) {
|
||||||
|
if (processed[candidate] || blocked[candidate])
|
||||||
|
continue; // Not a valid candidate.
|
||||||
|
available_candidates.push_back(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool {
|
||||||
|
return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const size_t candidate_path_idx : available_candidates) {
|
||||||
|
auto& path = all_extrusions[candidate_path_idx];
|
||||||
|
|
||||||
|
if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end.
|
||||||
|
if (best_distance_sqr == std::numeric_limits<double>::max()) {
|
||||||
|
best_candidate = candidate_path_idx;
|
||||||
|
is_best_closed = path->is_closed;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point candidate_position = path->junctions.front().p;
|
||||||
|
double distance_sqr = (current_position - candidate_position).cast<double>().norm();
|
||||||
|
if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far.
|
||||||
|
if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits<double>::max()) || (!path->is_closed && !is_best_closed)) {
|
||||||
|
best_candidate = candidate_path_idx;
|
||||||
|
best_distance_sqr = distance_sqr;
|
||||||
|
is_best_closed = path->is_closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &best_path = all_extrusions[best_candidate];
|
||||||
|
ordered_extrusions.push_back({best_path, best_path->is_contour(), false});
|
||||||
|
processed[best_candidate] = true;
|
||||||
|
for (size_t unlocked_idx : blocking[best_candidate])
|
||||||
|
blocked[unlocked_idx]--;
|
||||||
|
|
||||||
|
if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then.
|
||||||
|
if(best_path->is_closed)
|
||||||
|
current_position = best_path->junctions[0].p; //We end where we started.
|
||||||
|
else
|
||||||
|
current_position = best_path->junctions.back().p; //Pick the other end from where we started.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.layer_id > 0 && params.config.fuzzy_skin != FuzzySkinType::None) {
|
||||||
|
std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
|
||||||
|
for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
|
||||||
|
if (extrusion.extrusion->inset_idx == 0) {
|
||||||
|
if (extrusion.extrusion->is_closed && params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||||
|
closed_loop_extrusions.emplace_back(&extrusion);
|
||||||
|
} else {
|
||||||
|
extrusion.fuzzify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||||
|
ClipperLib_Z::Paths loops_paths;
|
||||||
|
loops_paths.reserve(closed_loop_extrusions.size());
|
||||||
|
for (const auto &cl_extrusion : closed_loop_extrusions) {
|
||||||
|
assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
|
||||||
|
size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
|
||||||
|
ClipperLib_Z::Path loop_path;
|
||||||
|
loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
|
||||||
|
for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
|
||||||
|
loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
|
||||||
|
loops_paths.emplace_back(loop_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipperLib_Z::Clipper clipper;
|
||||||
|
clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
|
||||||
|
ClipperLib_Z::PolyTree loops_polytree;
|
||||||
|
clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||||
|
|
||||||
|
for (const ClipperLib_Z::PolyNode *child_node : loops_polytree.Childs) {
|
||||||
|
// The whole contour must have the same index.
|
||||||
|
coord_t polygon_idx = child_node->Contour.front().z();
|
||||||
|
bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
|
||||||
|
[&polygon_idx](const ClipperLib_Z::IntPoint &point) -> bool { return polygon_idx == point.z(); });
|
||||||
|
if (has_same_idx)
|
||||||
|
closed_loop_extrusions[polygon_idx]->fuzzify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||||
|
out_loops.append(extrusion_coll);
|
||||||
|
|
||||||
|
|
||||||
|
//w16
|
||||||
|
if (remain_loops >= 0) {
|
||||||
|
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
|
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
||||||
|
infill_contour.clear();
|
||||||
|
coord_t inset = (loop_number < 0) ? 0 :
|
||||||
|
(loop_number == 0) ?
|
||||||
|
// one loop
|
||||||
|
ext_perimeter_spacing :
|
||||||
|
// two or more loops?
|
||||||
|
perimeter_spacing;
|
||||||
|
|
||||||
|
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||||
|
Polygons pp;
|
||||||
|
for (ExPolygon &ex : infill_contour)
|
||||||
|
ex.simplify_p(params.scaled_resolution, &pp);
|
||||||
|
// collapse too narrow infill areas
|
||||||
|
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||||
|
// append infill areas to fill_surfaces
|
||||||
|
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||||
|
float(inset + min_perimeter_infill_spacing / 2.));
|
||||||
|
// ExPolygons infill_areas;
|
||||||
|
|
||||||
|
if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
|
||||||
|
params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
|
||||||
|
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||||
|
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache,
|
||||||
|
loop_number + 1, params.overhang_flow,
|
||||||
|
params.scaled_resolution, params.object_config,
|
||||||
|
params.print_config);
|
||||||
|
if (!extra_perimeters.empty()) {
|
||||||
|
ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection &>(*out_loops.entities.back());
|
||||||
|
ExtrusionEntitiesPtr old_entities;
|
||||||
|
old_entities.swap(this_islands_perimeters.entities);
|
||||||
|
for (ExtrusionPaths &paths : extra_perimeters)
|
||||||
|
this_islands_perimeters.append(std::move(paths));
|
||||||
|
append(this_islands_perimeters.entities, old_entities);
|
||||||
|
infill_areas = diff_ex(infill_areas, filled_area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inset = (loop_number < 0) ? 0 :
|
||||||
|
(loop_number == 0) ?
|
||||||
|
// one loop
|
||||||
|
ext_perimeter_spacing :
|
||||||
|
// two or more loops?
|
||||||
|
perimeter_spacing;
|
||||||
|
|
||||||
|
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||||
|
for (ExPolygon &ex : infill_contour)
|
||||||
|
ex.simplify_p(params.scaled_resolution, &pp);
|
||||||
|
// collapse too narrow infill areas
|
||||||
|
|
||||||
|
|
||||||
|
if (remain_loops >= 0) {
|
||||||
|
//w21
|
||||||
|
add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing,
|
||||||
|
min_perimeter_infill_spacing, spacing, true, params, infill_areas, out_fill_expolygons,out_fill_no_overlap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (remain_loops >= 0) {
|
||||||
|
if (!inner_infill_contour.empty())
|
||||||
|
//w21
|
||||||
|
add_infill_contour_for_arachne(inner_infill_contour, remain_loops, ext_perimeter_spacing, perimeter_spacing,
|
||||||
|
min_perimeter_infill_spacing, spacing, true, params, infill_areas, out_fill_expolygons,out_fill_no_overlap);
|
||||||
|
}
|
||||||
|
//w21
|
||||||
|
append(out_fill_no_overlap,
|
||||||
|
offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)));
|
||||||
|
append(out_fill_expolygons, std::move(infill_areas));
|
||||||
|
} else {
|
||||||
|
infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
|
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
|
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
||||||
|
infill_contour.clear(); // Infill region is too small, so let's filter it out.
|
||||||
|
|
||||||
|
coord_t inset = (loop_number < 0) ? 0 : (loop_number == 0) ? ext_perimeter_spacing : perimeter_spacing;
|
||||||
|
|
||||||
|
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||||
|
Polygons pp;
|
||||||
|
for (ExPolygon &ex : infill_contour)
|
||||||
|
ex.simplify_p(params.scaled_resolution, &pp);
|
||||||
|
// collapse too narrow infill areas
|
||||||
|
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||||
|
// append infill areas to fill_surfaces
|
||||||
|
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||||
|
float(inset + min_perimeter_infill_spacing / 2.));
|
||||||
|
|
||||||
|
if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
|
||||||
|
params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
|
||||||
|
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||||
|
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache,
|
||||||
|
loop_number + 1, params.overhang_flow,
|
||||||
|
params.scaled_resolution, params.object_config,
|
||||||
|
params.print_config);
|
||||||
|
if (!extra_perimeters.empty()) {
|
||||||
|
ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection &>(*out_loops.entities.back());
|
||||||
|
ExtrusionEntitiesPtr old_entities;
|
||||||
|
old_entities.swap(this_islands_perimeters.entities);
|
||||||
|
for (ExtrusionPaths &paths : extra_perimeters)
|
||||||
|
this_islands_perimeters.append(std::move(paths));
|
||||||
|
append(this_islands_perimeters.entities, old_entities);
|
||||||
|
infill_areas = diff_ex(infill_areas, filled_area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//w21
|
||||||
|
append(out_fill_no_overlap,offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)));
|
||||||
append(out_fill_expolygons, std::move(infill_areas));
|
append(out_fill_expolygons, std::move(infill_areas));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PerimeterGenerator::process_classic(
|
void PerimeterGenerator::process_classic(
|
||||||
// Inputs:
|
// Inputs:
|
||||||
const Parameters ¶ms,
|
const Parameters ¶ms,
|
||||||
const Surface &surface,
|
const Surface &surface,
|
||||||
const ExPolygons *lower_slices,
|
const ExPolygons *lower_slices,
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
// Cache:
|
// Cache:
|
||||||
Polygons &lower_slices_polygons_cache,
|
Polygons &lower_layer_polygons_cache,
|
||||||
|
Polygons &upper_layer_polygons_cache,
|
||||||
// Output:
|
// Output:
|
||||||
// Loops with the external thin walls
|
// Loops with the external thin walls
|
||||||
ExtrusionEntityCollection &out_loops,
|
ExtrusionEntityCollection &out_loops,
|
||||||
// Gaps without the thin walls
|
// Gaps without the thin walls
|
||||||
ExtrusionEntityCollection &out_gap_fill,
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
// Infills without the gap fills
|
// Infills without the gap fills
|
||||||
ExPolygons &out_fill_expolygons)
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap)
|
||||||
{
|
{
|
||||||
// other perimeters
|
// other perimeters
|
||||||
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||||
@@ -1360,12 +1798,12 @@ void PerimeterGenerator::process_classic(
|
|||||||
bool has_gap_fill = params.config.gap_fill_enabled.value && params.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 (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) {
|
if (params.config.overhangs && lower_slices != nullptr && lower_layer_polygons_cache.empty()) {
|
||||||
// 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 = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
||||||
lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
|
lower_layer_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
|
||||||
@@ -1374,6 +1812,17 @@ void PerimeterGenerator::process_classic(
|
|||||||
int loop_number = params.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(params.scaled_resolution));
|
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
|
||||||
ExPolygons gaps;
|
ExPolygons gaps;
|
||||||
|
//w16
|
||||||
|
ExPolygons fill_clip;
|
||||||
|
ExPolygons top_fills;
|
||||||
|
//w16
|
||||||
|
|
||||||
|
if (params.config.overhangs && upper_slices != nullptr && upper_layer_polygons_cache.empty()) {
|
||||||
|
double upper_nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder - 1);
|
||||||
|
upper_layer_polygons_cache = offset(*upper_slices, float(scale_(+upper_nozzle_diameter / 2)));
|
||||||
|
}
|
||||||
|
if (loop_number > 0 && params.object_config.top_one_wall_type != TopOneWallType::Disable && upper_slices == nullptr)
|
||||||
|
loop_number = 0;
|
||||||
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.
|
||||||
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
||||||
@@ -1413,20 +1862,22 @@ 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 = params.config.thin_walls ?
|
//w16
|
||||||
|
//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
|
// qidi3d/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
|
||||||
// reliable gap fill algorithm.
|
// reliable gap fill algorithm.
|
||||||
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
|
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
|
||||||
// the original.
|
// the original.
|
||||||
offset2_ex(last,
|
//offset2_ex(last,
|
||||||
- float(distance + min_spacing / 2. - 1.),
|
// - float(distance + min_spacing / 2. - 1.),
|
||||||
float(min_spacing / 2. - 1.)) :
|
// float(min_spacing / 2. - 1.)) :
|
||||||
// If "detect thin walls" is not enabled, this paths will be entered, which
|
// If "detect thin walls" is not enabled, this paths will be entered, which
|
||||||
// leads to overflows, as in prusa3d/Slic3r GH #32
|
// leads to overflows, as in qidi3d/Slic3r GH #32
|
||||||
offset_ex(last, - float(distance));
|
// offset_ex(last, - float(distance));
|
||||||
// look for gaps
|
// look for gaps
|
||||||
|
offsets = offset2_ex(last, -float(distance + min_spacing / 2. - 1.), float(min_spacing / 2. - 1.));
|
||||||
if (has_gap_fill)
|
if (has_gap_fill)
|
||||||
// not using safety offset here would "detect" very narrow gaps
|
// not using safety offset here would "detect" very narrow gaps
|
||||||
// (but still long enough to escape the area threshold) that gap fill
|
// (but still long enough to escape the area threshold) that gap fill
|
||||||
@@ -1465,6 +1916,51 @@ void PerimeterGenerator::process_classic(
|
|||||||
}
|
}
|
||||||
last = std::move(offsets);
|
last = std::move(offsets);
|
||||||
|
|
||||||
|
//w16
|
||||||
|
if (i == 0 && i != loop_number && params.object_config.top_one_wall_type == TopOneWallType::Alltop &&
|
||||||
|
upper_slices != nullptr) {
|
||||||
|
coord_t offset_top_surface = scale_(
|
||||||
|
1.5 * (params.config.perimeters.value == 0 ?
|
||||||
|
0. :
|
||||||
|
unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(params.config.perimeters.value) - int(1))))));
|
||||||
|
if (offset_top_surface > 0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1))))
|
||||||
|
offset_top_surface -= coord_t(
|
||||||
|
0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1))));
|
||||||
|
else
|
||||||
|
offset_top_surface = 0;
|
||||||
|
//w17
|
||||||
|
double min_width_top_surface = (params.object_config.top_area_threshold/100 ) *
|
||||||
|
std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||||
|
BoundingBox last_box = get_extents(last);
|
||||||
|
last_box.offset(SCALED_EPSILON);
|
||||||
|
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_layer_polygons_cache,
|
||||||
|
last_box);
|
||||||
|
upper_polygons_series_clipped = offset(upper_polygons_series_clipped, min_width_top_surface);
|
||||||
|
fill_clip = offset_ex(last, -double(ext_perimeter_spacing));
|
||||||
|
ExPolygons bridge_checker;
|
||||||
|
if (lower_slices != nullptr) {
|
||||||
|
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_layer_polygons_cache,
|
||||||
|
last_box);
|
||||||
|
|
||||||
|
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
|
||||||
|
bridge_checker = offset_ex(diff_ex(last, lower_polygons_series_clipped, ApplySafetyOffset::Yes), 1.5 * bridge_offset);
|
||||||
|
}
|
||||||
|
ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes);
|
||||||
|
|
||||||
|
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
|
||||||
|
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
|
||||||
|
ExPolygons inner_polygons = diff_ex(last,
|
||||||
|
offset_ex(top_polygons, offset_top_surface + min_width_top_surface -
|
||||||
|
double(ext_perimeter_spacing / 2)),
|
||||||
|
ApplySafetyOffset::Yes);
|
||||||
|
top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||||
|
top_fills = union_ex(top_fills, top_polygons);
|
||||||
|
double infill_spacing_unscaled = params.config.infill_extrusion_width.value; // this->config->sparse_infill_line_width.value;
|
||||||
|
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||||
|
last = intersection_ex(inner_polygons, last);
|
||||||
|
if (has_gap_fill)
|
||||||
|
last = union_ex(last, temp_gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) {
|
if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) {
|
||||||
@@ -1529,7 +2025,7 @@ 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_classic(params, lower_slices_polygons_cache, contours.front(), thin_walls);
|
ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_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
|
||||||
@@ -1580,9 +2076,14 @@ void PerimeterGenerator::process_classic(
|
|||||||
ext_perimeter_spacing / 2 :
|
ext_perimeter_spacing / 2 :
|
||||||
// two or more loops?
|
// two or more loops?
|
||||||
perimeter_spacing / 2;
|
perimeter_spacing / 2;
|
||||||
|
//w21
|
||||||
|
coord_t infill_peri_overlap = 0;
|
||||||
// only apply infill overlap if we actually have one perimeter
|
// only apply infill overlap if we actually have one perimeter
|
||||||
if (inset > 0)
|
//w21
|
||||||
inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
|
if (inset > 0) {
|
||||||
|
infill_peri_overlap = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>( solid_infill_spacing / 2))));
|
||||||
|
inset -= infill_peri_overlap;
|
||||||
|
}
|
||||||
// simplify infill contours according to resolution
|
// simplify infill contours according to resolution
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
for (ExPolygon &ex : last)
|
for (ExPolygon &ex : last)
|
||||||
@@ -1596,12 +2097,34 @@ void PerimeterGenerator::process_classic(
|
|||||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||||
float(min_perimeter_infill_spacing / 2.));
|
float(min_perimeter_infill_spacing / 2.));
|
||||||
|
|
||||||
|
ExPolygons top_infill_exp = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2)));
|
||||||
|
//w21
|
||||||
|
if (!top_fills.empty()) {
|
||||||
|
infill_areas = union_ex(infill_areas, offset_ex(top_infill_exp, double(infill_peri_overlap)));
|
||||||
|
}
|
||||||
|
append(out_fill_expolygons, std::move(top_infill_exp));
|
||||||
|
//w21
|
||||||
|
{
|
||||||
|
ExPolygons polyWithoutOverlap;
|
||||||
|
if (min_perimeter_infill_spacing / 2 > infill_peri_overlap)
|
||||||
|
polyWithoutOverlap = offset2_ex(
|
||||||
|
union_ex(pp),
|
||||||
|
float(-inset - min_perimeter_infill_spacing / 2.),
|
||||||
|
float(min_perimeter_infill_spacing / 2 - infill_peri_overlap));
|
||||||
|
else
|
||||||
|
polyWithoutOverlap = offset_ex(
|
||||||
|
union_ex(pp),
|
||||||
|
double(-inset - infill_peri_overlap));
|
||||||
|
if (!top_fills.empty())
|
||||||
|
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_infill_exp);
|
||||||
|
out_fill_no_overlap.insert(out_fill_no_overlap.end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
|
||||||
|
}
|
||||||
|
|
||||||
if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
|
if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
|
||||||
params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
|
params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
|
||||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||||
lower_slices_polygons_cache,
|
lower_layer_polygons_cache,
|
||||||
loop_number + 1,
|
loop_number + 1,
|
||||||
params.overhang_flow, params.scaled_resolution,
|
params.overhang_flow, params.scaled_resolution,
|
||||||
params.object_config, params.print_config);
|
params.object_config, params.print_config);
|
||||||
@@ -1619,4 +2142,5 @@ void PerimeterGenerator::process_classic(
|
|||||||
append(out_fill_expolygons, std::move(infill_areas));
|
append(out_fill_expolygons, std::move(infill_areas));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,21 +70,28 @@ void process_classic(
|
|||||||
const Parameters ¶ms,
|
const Parameters ¶ms,
|
||||||
const Surface &surface,
|
const Surface &surface,
|
||||||
const ExPolygons *lower_slices,
|
const ExPolygons *lower_slices,
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
// Cache:
|
// Cache:
|
||||||
Polygons &lower_slices_polygons_cache,
|
Polygons &lower_layer_polygons_cache,
|
||||||
|
Polygons &upper_layer_polygons_cache,
|
||||||
// Output:
|
// Output:
|
||||||
// Loops with the external thin walls
|
// Loops with the external thin walls
|
||||||
ExtrusionEntityCollection &out_loops,
|
ExtrusionEntityCollection &out_loops,
|
||||||
// Gaps without the thin walls
|
// Gaps without the thin walls
|
||||||
ExtrusionEntityCollection &out_gap_fill,
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
// Infills without the gap fills
|
// Infills without the gap fills
|
||||||
ExPolygons &out_fill_expolygons);
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap);
|
||||||
|
|
||||||
void process_arachne(
|
void process_arachne(
|
||||||
// Inputs:
|
// Inputs:
|
||||||
const Parameters ¶ms,
|
const Parameters ¶ms,
|
||||||
const Surface & surface,
|
const Surface & surface,
|
||||||
const ExPolygons *lower_slices,
|
const ExPolygons *lower_slices,
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
// Cache:
|
// Cache:
|
||||||
Polygons &lower_slices_polygons_cache,
|
Polygons &lower_slices_polygons_cache,
|
||||||
// Output:
|
// Output:
|
||||||
@@ -93,7 +100,43 @@ void process_arachne(
|
|||||||
// Gaps without the thin walls
|
// Gaps without the thin walls
|
||||||
ExtrusionEntityCollection &out_gap_fill,
|
ExtrusionEntityCollection &out_gap_fill,
|
||||||
// Infills without the gap fills
|
// Infills without the gap fills
|
||||||
ExPolygons &out_fill_expolygons);
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap);
|
||||||
|
|
||||||
|
void process_with_one_wall_arachne(
|
||||||
|
// Inputs:
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const Surface &surface,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
//w16
|
||||||
|
const ExPolygons *upper_slices,
|
||||||
|
// Cache:
|
||||||
|
Polygons &lower_slices_polygons_cache,
|
||||||
|
Polygons &upper_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
|
||||||
|
ExPolygons &out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons &out_fill_no_overlap);
|
||||||
|
|
||||||
|
//w16
|
||||||
|
void add_infill_contour_for_arachne(ExPolygons infill_contour,
|
||||||
|
int loops,
|
||||||
|
coord_t ext_perimeter_spacing,
|
||||||
|
coord_t perimeter_spacing,
|
||||||
|
coord_t min_perimeter_infill_spacing,
|
||||||
|
coord_t spacing,
|
||||||
|
bool is_inner_part,
|
||||||
|
const Parameters ¶ms,
|
||||||
|
ExPolygons & infill_areas,
|
||||||
|
ExPolygons & out_fill_expolygons,
|
||||||
|
//w21
|
||||||
|
ExPolygons & out_fill_no_overlap);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user