mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-10 08:17:51 -06:00
Port single wall on top surface from classic wall generator to Arachne (#1583)
* Port single wall on top surface from classic wall generator to Arachne * Move the surface split code into a separate function to be shared between classic and Arachne. This also adds bridge detection to Arachne as well. * Remove unwanted infills * Fix insets calculating --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
c13a2e57e2
commit
d7a1241550
2 changed files with 163 additions and 80 deletions
|
@ -779,7 +779,99 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
|||
return extrusion_coll;
|
||||
}
|
||||
|
||||
void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills,
|
||||
ExPolygons &non_top_polygons, ExPolygons &fill_clip) const {
|
||||
// other perimeters
|
||||
coord_t perimeter_width = this->perimeter_flow.scaled_width();
|
||||
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
|
||||
|
||||
// external perimeters
|
||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
|
||||
bool has_gap_fill = this->config->gap_infill_speed.value > 0;
|
||||
|
||||
// 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
|
||||
? 0.
|
||||
: unscaled(double(ext_perimeter_width +
|
||||
perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
|
||||
// if possible, try to not push the extra perimeters inside the sparse infill
|
||||
if (offset_top_surface >
|
||||
0.9 * (config->wall_loops.value <= 1 ? 0. : (perimeter_spacing * (config->wall_loops.value - 1))))
|
||||
offset_top_surface -=
|
||||
coord_t(0.9 * (config->wall_loops.value <= 1 ? 0. : (perimeter_spacing * (config->wall_loops.value - 1))));
|
||||
else
|
||||
offset_top_surface = 0;
|
||||
// don't takes into account too thin areas
|
||||
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||
|
||||
Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface);
|
||||
|
||||
// BBS: get boungding box of last
|
||||
BoundingBox last_box = get_extents(orig_polygons);
|
||||
last_box.offset(SCALED_EPSILON);
|
||||
|
||||
// BBS: get the Polygons upper the polygon this layer
|
||||
Polygons upper_polygons_series_clipped =
|
||||
ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box);
|
||||
|
||||
// set the clip to a virtual "second perimeter"
|
||||
fill_clip = offset_ex(orig_polygons, -double(ext_perimeter_spacing));
|
||||
// get the real top surface
|
||||
ExPolygons grown_lower_slices;
|
||||
ExPolygons bridge_checker;
|
||||
auto nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1);
|
||||
// Check whether surface be bridge or not
|
||||
if (this->lower_slices != NULL) {
|
||||
// BBS: get the Polygons below the polygon this layer
|
||||
Polygons lower_polygons_series_clipped =
|
||||
ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, last_box);
|
||||
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
|
||||
// SoftFever: improve bridging
|
||||
const float bridge_margin =
|
||||
std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
|
||||
bridge_checker = offset_ex(diff_ex(orig_polygons, lower_polygons_series_clipped, ApplySafetyOffset::Yes),
|
||||
1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2);
|
||||
}
|
||||
ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes);
|
||||
|
||||
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
|
||||
// get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the
|
||||
// min_width_top_surface we removed a bit before)
|
||||
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
|
||||
ExPolygons inner_polygons =
|
||||
diff_ex(orig_polygons,
|
||||
offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)),
|
||||
ApplySafetyOffset::Yes);
|
||||
// get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly
|
||||
// the polygons to fill.
|
||||
top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||
// increase by half peri the inner space to fill the frontier between last and stored.
|
||||
top_fills = union_ex(top_fills, top_polygons);
|
||||
//set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap.
|
||||
double infill_spacing_unscaled = this->config->sparse_infill_line_width.get_abs_value(nozzle_diameter);
|
||||
if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter);
|
||||
fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||
// ExPolygons oldLast = last;
|
||||
|
||||
non_top_polygons = intersection_ex(inner_polygons, orig_polygons);
|
||||
if (has_gap_fill)
|
||||
non_top_polygons = union_ex(non_top_polygons, temp_gap);
|
||||
//{
|
||||
// std::stringstream stri;
|
||||
// stri << this->layer_id << "_1_"<< i <<"_only_one_peri"<< ".svg";
|
||||
// SVG svg(stri.str());
|
||||
// svg.draw(to_polylines(top_fills), "green");
|
||||
// svg.draw(to_polylines(inner_polygons), "yellow");
|
||||
// svg.draw(to_polylines(top_polygons), "cyan");
|
||||
// svg.draw(to_polylines(oldLast), "orange");
|
||||
// svg.draw(to_polylines(last), "red");
|
||||
// svg.Close();
|
||||
//}
|
||||
}
|
||||
|
||||
void PerimeterGenerator::process_classic()
|
||||
{
|
||||
|
@ -994,76 +1086,7 @@ void PerimeterGenerator::process_classic()
|
|||
//BBS: refer to superslicer
|
||||
//store surface for top infill if only_one_wall_top
|
||||
if (i == 0 && i!=loop_number && config->only_one_wall_top && this->upper_slices != NULL) {
|
||||
//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 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
|
||||
// if possible, try to not push the extra perimeters inside the sparse infill
|
||||
if (offset_top_surface > 0.9 * (config->wall_loops.value <= 1 ? 0. : (perimeter_spacing * (config->wall_loops.value - 1))))
|
||||
offset_top_surface -= coord_t(0.9 * (config->wall_loops.value <= 1 ? 0. : (perimeter_spacing * (config->wall_loops.value - 1))));
|
||||
else
|
||||
offset_top_surface = 0;
|
||||
//don't takes into account too thin areas
|
||||
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||
|
||||
Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface);
|
||||
|
||||
//BBS: get boungding box of last
|
||||
BoundingBox last_box = get_extents(last);
|
||||
last_box.offset(SCALED_EPSILON);
|
||||
|
||||
// BBS: get the Polygons upper the polygon this layer
|
||||
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box);
|
||||
|
||||
//set the clip to a virtual "second perimeter"
|
||||
fill_clip = offset_ex(last, -double(ext_perimeter_spacing));
|
||||
// get the real top surface
|
||||
ExPolygons grown_lower_slices;
|
||||
ExPolygons bridge_checker;
|
||||
auto nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1);
|
||||
// Check whether surface be bridge or not
|
||||
if (this->lower_slices != NULL) {
|
||||
// BBS: get the Polygons below the polygon this layer
|
||||
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, last_box);
|
||||
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
|
||||
// SoftFever: improve bridging
|
||||
const float bridge_margin =
|
||||
std::min(float(scale_(BRIDGE_INFILL_MARGIN)),
|
||||
float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
|
||||
bridge_checker = offset_ex(diff_ex(last, lower_polygons_series_clipped, ApplySafetyOffset::Yes),
|
||||
1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2);
|
||||
}
|
||||
ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes);
|
||||
|
||||
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
|
||||
//get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before)
|
||||
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);
|
||||
// get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly the polygons to fill.
|
||||
top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||
// increase by half peri the inner space to fill the frontier between last and stored.
|
||||
top_fills = union_ex(top_fills, top_polygons);
|
||||
//set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap.
|
||||
double infill_spacing_unscaled = this->config->sparse_infill_line_width.get_abs_value(nozzle_diameter);
|
||||
if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter);
|
||||
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||
//ExPolygons oldLast = last;
|
||||
|
||||
last = intersection_ex(inner_polygons, last);
|
||||
if (has_gap_fill)
|
||||
last = union_ex(last,temp_gap);
|
||||
//{
|
||||
// std::stringstream stri;
|
||||
// stri << this->layer_id << "_1_"<< i <<"_only_one_peri"<< ".svg";
|
||||
// SVG svg(stri.str());
|
||||
// svg.draw(to_polylines(top_fills), "green");
|
||||
// svg.draw(to_polylines(inner_polygons), "yellow");
|
||||
// svg.draw(to_polylines(top_polygons), "cyan");
|
||||
// svg.draw(to_polylines(oldLast), "orange");
|
||||
// svg.draw(to_polylines(last), "red");
|
||||
// svg.Close();
|
||||
//}
|
||||
this->split_top_surfaces(last, top_fills, last, fill_clip);
|
||||
}
|
||||
|
||||
if (i == loop_number && (! has_gap_fill || this->config->sparse_infill_density.value == 0)) {
|
||||
|
@ -1318,7 +1341,6 @@ void PerimeterGenerator::process_arachne()
|
|||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
|
||||
config->precise_outer_wall ? -float(ext_perimeter_width / 2. - bead_width_0 / 2.)
|
||||
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
Polygons last_p = to_polygons(last);
|
||||
|
||||
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||||
Arachne::WallToolPathsParams input_params;
|
||||
|
@ -1343,10 +1365,61 @@ void PerimeterGenerator::process_arachne()
|
|||
// wall_0_inset = 0.5 * (ext_perimeter_width + this->perimeter_flow.scaled_width() - ext_perimeter_spacing -
|
||||
// perimeter_spacing);
|
||||
|
||||
std::vector<Arachne::VariableWidthLines> out_shell;
|
||||
ExPolygons top_fills;
|
||||
ExPolygons fill_clip;
|
||||
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices != nullptr) {
|
||||
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
|
||||
ExPolygons non_top_polygons;
|
||||
this->split_top_surfaces(last, top_fills, non_top_polygons, fill_clip);
|
||||
|
||||
if (top_fills.empty()) {
|
||||
// No top surfaces, no special handling needed
|
||||
} else {
|
||||
// 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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &p : perimeters) {
|
||||
for (auto &l : p) {
|
||||
l.inset_idx += inset_offset;
|
||||
}
|
||||
}
|
||||
|
||||
perimeters.insert(perimeters.begin(), out_shell.begin(), out_shell.end());
|
||||
}
|
||||
loop_number = int(perimeters.size()) - 1;
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
|
@ -1567,25 +1640,34 @@ void PerimeterGenerator::process_arachne()
|
|||
perimeter_spacing;
|
||||
|
||||
inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
|
||||
// simplify infill contours according to resolution
|
||||
Polygons pp;
|
||||
for (ExPolygon& ex : infill_contour)
|
||||
ex.simplify_p(m_scaled_resolution, &pp);
|
||||
ExPolygons not_filled_exp = union_ex(pp);
|
||||
// collapse too narrow infill areas
|
||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
|
||||
ExPolygons infill_exp = offset2_ex(
|
||||
not_filled_exp,
|
||||
float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.));
|
||||
// append infill areas to fill_surfaces
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
union_ex(pp),
|
||||
float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.)),
|
||||
stInternal);
|
||||
if (!top_fills.empty()) {
|
||||
infill_exp = union_ex(infill_exp, offset_ex(top_fills, double(inset)));
|
||||
}
|
||||
this->fill_surfaces->append(infill_exp, stInternal);
|
||||
|
||||
// BBS: get the no-overlap infill expolygons
|
||||
{
|
||||
append(*this->fill_no_overlap, offset2_ex(
|
||||
union_ex(pp),
|
||||
ExPolygons polyWithoutOverlap;
|
||||
polyWithoutOverlap = offset2_ex(
|
||||
not_filled_exp,
|
||||
float(-min_perimeter_infill_spacing / 2.),
|
||||
float(+min_perimeter_infill_spacing / 2.)));
|
||||
float(+min_perimeter_infill_spacing / 2.));
|
||||
if (!top_fills.empty())
|
||||
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_fills);
|
||||
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
|
||||
private:
|
||||
std::map<int, Polygons> generate_lower_polygons_series(float width);
|
||||
void split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills, ExPolygons &non_top_polygons, ExPolygons &fill_clip) const;
|
||||
|
||||
private:
|
||||
bool m_spiral_vase;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue