add top one wall

This commit is contained in:
Wang YB
2024-01-17 10:59:37 +08:00
parent abbe9d06df
commit eba6ab9ac1
9 changed files with 566 additions and 9 deletions

View File

@@ -106,31 +106,56 @@ 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;
Polygons upper_layer_polygons_cache;
for (const Surface &surface : slices) {
auto perimeters_begin = uint32_t(m_perimeters.size());
auto gap_fills_begin = uint32_t(m_thin_fills.size());
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);
else
PerimeterGenerator::process_arachne(
// input:
params,
surface,
lower_slices,
upper_slices,
lower_layer_polygons_cache,
// output:
m_perimeters,
m_thin_fills,
fill_expolygons);
else
PerimeterGenerator::process_classic(
// input:
params,
surface,
lower_slices,
//w16
upper_slices,
lower_layer_polygons_cache,
upper_layer_polygons_cache,
// output:
m_perimeters,
m_thin_fills,

View File

@@ -1069,6 +1069,37 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
return {extra_perims, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
}
//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)
{
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, std::move(infill_areas));
}
// 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"
void PerimeterGenerator::process_arachne(
@@ -1076,6 +1107,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:
@@ -1111,6 +1143,10 @@ 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();
loop_number = int(perimeters.size()) - 1;
@@ -1317,13 +1353,387 @@ void PerimeterGenerator::process_arachne(
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)
{
// 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_(+upper_nozzle_diameter / 2)));
}
// 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);
}
double min_width_top_surface = 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) {
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);
}
if (remain_loops >= 0) {
if (!inner_infill_contour.empty())
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);
}
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);
}
}
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 &upper_layer_polygons_cache,
// Output:
// Loops with the external thin walls
ExtrusionEntityCollection &out_loops,
@@ -1371,6 +1781,11 @@ 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;
if (loop_number > 0 && params.object_config.top_one_wall_type != TopOneWallType::None && 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
@@ -1410,20 +1825,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
// 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 qidi3d/Slic3r GH #32
offset_ex(last, - float(distance));
// 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
@@ -1461,6 +1878,54 @@ 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;
double min_width_top_surface = (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 grown_lower_slices;
ExPolygons bridge_checker;
if (lower_slices != nullptr) {
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_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)) {
// The last run of this loop is executed to collect gaps for gap fill.
// As the gap fill is either disabled or not
@@ -1590,6 +2055,10 @@ 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)));
append(out_fill_expolygons, std::move(top_infill_exp));
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

View File

@@ -70,8 +70,11 @@ 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 &upper_slices_polygons_cache,
// Output:
// Loops with the external thin walls
ExtrusionEntityCollection &out_loops,
@@ -83,8 +86,10 @@ void process_classic(
void process_arachne(
// Inputs:
const Parameters &params,
const Surface &surface,
const Surface & surface,
const ExPolygons *lower_slices,
//w16
const ExPolygons *upper_slices,
// Cache:
Polygons &lower_slices_polygons_cache,
// Output:
@@ -95,6 +100,36 @@ void process_arachne(
// Infills without the gap fills
ExPolygons &out_fill_expolygons);
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);
//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);
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);
} // namespace PerimeterGenerator

View File

@@ -475,6 +475,8 @@ static std::vector<std::string> s_Preset_print_options {
,"detect_narrow_internal_solid_infill"
//Y21
,"seam_gap"
//w16
, "top_one_wall_type"
};
static std::vector<std::string> s_Preset_filament_options {

View File

@@ -236,6 +236,12 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
//w16
static t_config_enum_values s_keys_map_TopOneWallType{{"not apply", int(TopOneWallType::None)},
{"all top", int(TopOneWallType::Alltop)},
{"Only top most", int(TopOneWallType::Onlytopmost)}};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOneWallType)
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
{
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
@@ -3531,6 +3537,19 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
//w16
def = this->add("top_one_wall_type", coEnum);
def->label = L("Only one wall on top surfaces");
def->category = L("Layers and Perimeters");
def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern. Could be applyed on topmost surface or all top surface.");
def->set_enum<TopOneWallType>({
{ "none", L("None") },
{ "Only top most", L("Onlytopmost") },
{ "all top", L("Alltop") }
});
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<TopOneWallType>(TopOneWallType::None));
def = this->add("wall_transition_length", coFloatOrPercent);
def->label = L("Perimeter transition length");
def->category = L("Advanced");

View File

@@ -144,6 +144,9 @@ enum class PerimeterGeneratorType
//B3
enum class GCodeThumbnailsFormat { QIDI,PNG, JPG, QOI };
//w16
enum class TopOneWallType { None, Alltop, Onlytopmost };
#define CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NAME) \
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names(); \
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values();
@@ -587,6 +590,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, wipe_into_objects))
//w11
((ConfigOptionBool, detect_narrow_internal_solid_infill))
//w16
((ConfigOptionEnum<TopOneWallType>, top_one_wall_type))
)
PRINT_CONFIG_CLASS_DEFINE(

View File

@@ -864,12 +864,13 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wall_transition_angle"
|| opt_key == "wall_distribution_count"
|| opt_key == "min_feature_size"
|| opt_key == "min_bead_width") {
|| opt_key == "min_bead_width"
//w15
|| opt_key == "top_one_wall_type"){
steps.emplace_back(posSlice);
} else if (
opt_key == "seam_position"
//Y21
|| opt_key == "seam_gap"
|| opt_key == "seam_preferred_direction"
|| opt_key == "seam_preferred_direction_jitter"
|| opt_key == "support_material_speed"

View File

@@ -244,7 +244,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
{
bool have_perimeters = config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "thin_walls", "overhangs",
//Y21
"seam_position","seam_gap","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds"})
toggle_field(el, have_perimeters);

View File

@@ -1467,6 +1467,8 @@ void TabPrint::build()
optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first");
optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps");
optgroup->append_single_option_line("perimeter_generator");
//w16
optgroup->append_single_option_line("top_one_wall_type");
optgroup = page->new_optgroup(L("Fuzzy skin (experimental)"));
category_path = "fuzzy-skin_246186/#";