update top_one_wall & top_gap_fills

This commit is contained in:
Wang YB
2024-03-30 15:14:10 +08:00
parent 5ccb55ff98
commit f33a08f704
6 changed files with 652 additions and 27 deletions

View File

@@ -231,14 +231,16 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
//w21
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 {
//w21
fill.expolygons.emplace_back(surface.expolygon);
auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id);
if (t == fill.region_id_group.end()) {
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);
}
}
}

View File

@@ -621,6 +621,8 @@ void Layer::make_perimeters()
std::vector<std::pair<ExtrusionRange, ExtrusionRange>> perimeter_and_gapfill_ranges;
ExPolygons fill_expolygons;
std::vector<ExPolygonRange> fill_expolygons_ranges;
//w21
ExPolygons fill_no_overlap_expolygons;
SurfacesPtr surfaces_to_merge;
SurfacesPtr surfaces_to_merge_temp;
@@ -690,7 +692,8 @@ void Layer::make_perimeters()
}
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);
} else {
SurfaceCollection new_slices;
@@ -725,8 +728,24 @@ void Layer::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);
//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);
}
}
}
}
}
}

View File

@@ -73,7 +73,9 @@ void LayerRegion::make_perimeters(
// All fill areas produced for all input slices above.
ExPolygons &fill_expolygons,
// 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_thin_fills.clear();
@@ -106,8 +108,12 @@ void LayerRegion::make_perimeters(
// Cummulative sum of polygons over all the regions.
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
Polygons lower_layer_polygons_cache;
//w16
Polygons upper_layer_polygons_cache;
for (const Surface &surface : slices) {
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());
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(
// input:
params,
surface,
lower_slices,
//w16
upper_slices,
lower_layer_polygons_cache,
// output:
m_perimeters,
m_thin_fills,
fill_expolygons);
fill_expolygons,
//w21
fill_no_overlap_expolygons);
else
PerimeterGenerator::process_classic(
@@ -132,11 +160,16 @@ void LayerRegion::make_perimeters(
params,
surface,
lower_slices,
//w16
upper_slices,
lower_layer_polygons_cache,
upper_layer_polygons_cache,
// output:
m_perimeters,
m_thin_fills,
fill_expolygons);
fill_expolygons,
//w21
fill_no_overlap_expolygons);
perimeter_and_gapfill_ranges.emplace_back(
ExtrusionRange{ perimeters_begin, uint32_t(m_perimeters.size()) },
ExtrusionRange{ gap_fills_begin, uint32_t(m_thin_fills.size()) });

View File

@@ -92,6 +92,8 @@ public:
// ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects)
[[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; }
//w21
ExPolygons fill_no_overlap_expolygons;
Flow flow(FlowRole role) const;
Flow flow(FlowRole role, double layer_height) const;
@@ -109,7 +111,9 @@ public:
// All fill areas produced for all input slices above.
ExPolygons &fill_expolygons,
// 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);
double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.

View File

@@ -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 &params,
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
// "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 &params,
const Surface &surface,
const ExPolygons *lower_slices,
const ExPolygons *upper_slices,
// Cache:
Polygons &lower_slices_polygons_cache,
// Output:
@@ -1086,7 +1121,9 @@ void PerimeterGenerator::process_arachne(
// Gaps without the thin walls
ExtrusionEntityCollection & /* out_gap_fill */,
// Infills without the gap fills
ExPolygons &out_fill_expolygons)
ExPolygons &out_fill_expolygons,
//w21
ExPolygons &out_fill_no_overlap)
{
// other perimeters
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.));
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);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
@@ -1269,10 +1309,21 @@ void PerimeterGenerator::process_arachne(
out_loops.append(extrusion_coll);
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;
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
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
// 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
@@ -1316,24 +1367,411 @@ void PerimeterGenerator::process_arachne(
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 &params,
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));
}
}
void PerimeterGenerator::process_classic(
// Inputs:
const Parameters &params,
const Surface &surface,
const ExPolygons *lower_slices,
//w16
const ExPolygons *upper_slices,
// Cache:
Polygons &lower_slices_polygons_cache,
Polygons &lower_layer_polygons_cache,
Polygons &upper_layer_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)
ExPolygons &out_fill_expolygons,
//w21
ExPolygons &out_fill_no_overlap)
{
// other perimeters
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;
// 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
// 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)));
lower_layer_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter / 2)));
}
// 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
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
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) {
// In case no perimeters are to be generated, loop_number will equal to -1.
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
// from the line width of the infill?
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
// 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
// reliable gap fill algorithm.
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
// the original.
offset2_ex(last,
- float(distance + min_spacing / 2. - 1.),
float(min_spacing / 2. - 1.)) :
//offset2_ex(last,
// - float(distance + min_spacing / 2. - 1.),
// float(min_spacing / 2. - 1.)) :
// If "detect thin walls" is not enabled, this paths will be entered, which
// leads to overflows, as in prusa3d/Slic3r GH #32
offset_ex(last, - float(distance));
// leads to overflows, as in qidi3d/Slic3r GH #32
// offset_ex(last, - float(distance));
// look for gaps
offsets = offset2_ex(last, -float(distance + min_spacing / 2. - 1.), float(min_spacing / 2. - 1.));
if (has_gap_fill)
// not using safety offset here would "detect" very narrow gaps
// (but still long enough to escape the area threshold) that gap fill
@@ -1465,6 +1916,51 @@ void PerimeterGenerator::process_classic(
}
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)) {
@@ -1529,7 +2025,7 @@ void PerimeterGenerator::process_classic(
}
}
// 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
// we continue inwards after having finished the brim
// TODO: add test for perimeter order
@@ -1580,9 +2076,14 @@ void PerimeterGenerator::process_classic(
ext_perimeter_spacing / 2 :
// two or more loops?
perimeter_spacing / 2;
//w21
coord_t infill_peri_overlap = 0;
// only apply infill overlap if we actually have one perimeter
if (inset > 0)
inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
//w21
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
Polygons pp;
for (ExPolygon &ex : last)
@@ -1596,12 +2097,34 @@ void PerimeterGenerator::process_classic(
float(- inset - 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 &&
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,
lower_layer_polygons_cache,
loop_number + 1,
params.overhang_flow, params.scaled_resolution,
params.object_config, params.print_config);
@@ -1619,4 +2142,5 @@ void PerimeterGenerator::process_classic(
append(out_fill_expolygons, std::move(infill_areas));
}
}

View File

@@ -70,21 +70,28 @@ void process_classic(
const Parameters &params,
const Surface &surface,
const ExPolygons *lower_slices,
//w16
const ExPolygons *upper_slices,
// Cache:
Polygons &lower_slices_polygons_cache,
Polygons &lower_layer_polygons_cache,
Polygons &upper_layer_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);
ExPolygons &out_fill_expolygons,
//w21
ExPolygons &out_fill_no_overlap);
void process_arachne(
// Inputs:
const Parameters &params,
const Surface & surface,
const ExPolygons *lower_slices,
//w16
const ExPolygons *upper_slices,
// Cache:
Polygons &lower_slices_polygons_cache,
// Output:
@@ -93,7 +100,43 @@ void process_arachne(
// Gaps without the thin walls
ExtrusionEntityCollection &out_gap_fill,
// 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 &params,
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 &params,
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);