Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arachne "one top wall" fix and refactor #6236

Merged
merged 6 commits into from
Sep 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 79 additions & 68 deletions src/libslic3r/PerimeterGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
// split the polygons with top/not_top
// get the offset from solid surface anchor
coord_t offset_top_surface =
scale_(1.5 * (config->wall_loops.value == 0
scale_(0.9 * (config->wall_loops.value == 0
? 0.
: unscaled(double(ext_perimeter_width +
perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
Expand Down Expand Up @@ -2889,77 +2889,89 @@ void PerimeterGenerator::process_arachne()
if (apply_precise_outer_wall)
wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);

std::vector<Arachne::VariableWidthLines> out_shell;
ExPolygons top_fills;
ExPolygons fill_clip;

// Check if we're on a top surface, and make adjustments where needed
if (!surface.is_bridge() && !is_topmost_layer) {
ExPolygons non_top_polygons;
// Temporary storage, in the event all we need to do is set is_top_or_bottom_layer
ExPolygons top_fills_tmp;
ExPolygons fill_clip_tmp;
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
this->split_top_surfaces(last, top_fills_tmp, non_top_polygons, fill_clip_tmp);

if (top_fills_tmp.empty()) {
// No top surfaces, no special handling needed
} else {
// Use single-wall on top-surfaces if configured
if (loop_number > 0 && config->only_one_wall_top) {
// Adjust arachne input params to prevent removal of larger short walls, which could lead to gaps
Arachne::WallToolPathsParams input_params_tmp = input_params;
input_params_tmp.is_top_or_bottom_layer = true;

// Swap in the temporary storage
top_fills.swap(top_fills_tmp);
fill_clip.swap(fill_clip_tmp);

// First we slice the outer shell
Polygons last_p = to_polygons(last);
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(1),
wall_0_inset, layer_height, input_params_tmp);
out_shell = wallToolPaths.getToolPaths();
// Make sure infill not overlap with wall
top_fills = intersection_ex(top_fills, wallToolPaths.getInnerContour());

if (!top_fills.empty()) {
// Then get the inner part that needs more walls
last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour());
loop_number--;
} else {
// Give up the outer shell because we don't have any meaningful top surface
out_shell.clear();
}
}
}
}
//PS: One wall top surface for Arachne
ExPolygons top_expolygons;
// Calculate how many inner loops remain when TopSurfaces is selected.
const int inner_loop_number = (config->only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1;

Polygons last_p = to_polygons(last);
// Set one perimeter when TopSurfaces is selected.
if (config->only_one_wall_top)
loop_number = 0;

Arachne::WallToolPathsParams input_params_tmp = input_params;

Polygons last_p = to_polygons(last);
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1),
wall_0_inset, layer_height, input_params);
wall_0_inset, layer_height, input_params_tmp);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());

// Check if there are some remaining perimeters to generate (the number of perimeters
// is greater than one together with enabled the single perimeter on top surface feature).
if (inner_loop_number >= 0) {
assert(upper_slices != nullptr);

// Infill contour bounding box.
BoundingBox infill_contour_bbox = get_extents(infill_contour);
infill_contour_bbox.offset(SCALED_EPSILON);

coord_t perimeter_width = this->perimeter_flow.scaled_width();

// Get top ExPolygons from current infill contour.
Polygons upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
top_expolygons = diff_ex(infill_contour, upper_slices_clipped);

std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
if (!top_expolygons.empty()) {
if (lower_slices != nullptr) {
const float bridge_offset = float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox);
const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset);

if (!out_shell.empty()) {
// Combine outer shells
size_t inset_offset = 0;
for (auto &p : out_shell) {
for (auto &l : p) {
if (l.inset_idx + 1 > inset_offset) {
inset_offset = l.inset_idx + 1;
// Remove bridges from top surface polygons.
top_expolygons = diff_ex(top_expolygons, current_slices_bridges);
}

// Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line.
// ORCA: skip if the top surface area is smaller than "min_width_top_surface"
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f);
// Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little.
// ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount,
// not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions
// due to thin lines being generated
top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));

// Get the not-top ExPolygons (including bridges) from current slices and expanded real top ExPolygons (without bridges).
const ExPolygons not_top_expolygons = diff_ex(infill_contour, top_expolygons);

// Get final top ExPolygons.
top_expolygons = intersection_ex(top_expolygons, infill_contour);

const Polygons not_top_polygons = to_polygons(not_top_expolygons);
Arachne::WallToolPaths inner_wall_tool_paths(not_top_polygons, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp);
std::vector<Arachne::VariableWidthLines> inner_perimeters = inner_wall_tool_paths.getToolPaths();

// Recalculate indexes of inner perimeters before merging them.
if (!perimeters.empty()) {
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) {
if (inner_perimeter.empty())
continue;
for (Arachne::ExtrusionLine &el : inner_perimeter)
++el.inset_idx;
}
}
}
for (auto &p : perimeters) {
for (auto &l : p) {
l.inset_idx += inset_offset;
}
}

perimeters.insert(perimeters.begin(), out_shell.begin(), out_shell.end());
perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
} else {
// There is no top surface ExPolygon, so we call Arachne again with parameters
// like when the single perimeter feature is disabled.
Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp);
perimeters = no_single_perimeter_tool_paths.getToolPaths();
infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour());
}
}
//PS

loop_number = int(perimeters.size()) - 1;

#ifdef ARACHNE_DEBUG
Expand Down Expand Up @@ -3183,7 +3195,6 @@ void PerimeterGenerator::process_arachne()
this->loops->append(extrusion_coll);
}

ExPolygons 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())
Expand Down Expand Up @@ -3221,8 +3232,8 @@ ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.));
// append infill areas to fill_surfaces
if (!top_fills.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_fills, double(top_inset)));
if (!top_expolygons.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset)));
}
this->fill_surfaces->append(infill_exp, stInternal);

Expand All @@ -3235,8 +3246,8 @@ ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
not_filled_exp,
float(-min_perimeter_infill_spacing / 2.),
float(+min_perimeter_infill_spacing / 2.));
if (!top_fills.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_fills);
if (!top_expolygons.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons);
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
}
}
Expand Down