mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 00:37: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;
|
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()
|
void PerimeterGenerator::process_classic()
|
||||||
{
|
{
|
||||||
|
@ -994,76 +1086,7 @@ void PerimeterGenerator::process_classic()
|
||||||
//BBS: refer to superslicer
|
//BBS: refer to superslicer
|
||||||
//store surface for top infill if only_one_wall_top
|
//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) {
|
if (i == 0 && i!=loop_number && config->only_one_wall_top && this->upper_slices != NULL) {
|
||||||
//split the polygons with top/not_top
|
this->split_top_surfaces(last, top_fills, last, fill_clip);
|
||||||
//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();
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == loop_number && (! has_gap_fill || this->config->sparse_infill_density.value == 0)) {
|
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),
|
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
|
||||||
config->precise_outer_wall ? -float(ext_perimeter_width / 2. - bead_width_0 / 2.)
|
config->precise_outer_wall ? -float(ext_perimeter_width / 2. - bead_width_0 / 2.)
|
||||||
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 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());
|
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||||||
Arachne::WallToolPathsParams input_params;
|
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 -
|
// wall_0_inset = 0.5 * (ext_perimeter_width + this->perimeter_flow.scaled_width() - ext_perimeter_spacing -
|
||||||
// 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),
|
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);
|
||||||
|
|
||||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
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;
|
loop_number = int(perimeters.size()) - 1;
|
||||||
|
|
||||||
#ifdef ARACHNE_DEBUG
|
#ifdef ARACHNE_DEBUG
|
||||||
|
@ -1567,25 +1640,34 @@ void PerimeterGenerator::process_arachne()
|
||||||
perimeter_spacing;
|
perimeter_spacing;
|
||||||
|
|
||||||
inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
|
inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
|
||||||
|
// simplify infill contours according to resolution
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
for (ExPolygon& ex : infill_contour)
|
for (ExPolygon& ex : infill_contour)
|
||||||
ex.simplify_p(m_scaled_resolution, &pp);
|
ex.simplify_p(m_scaled_resolution, &pp);
|
||||||
|
ExPolygons not_filled_exp = union_ex(pp);
|
||||||
// collapse too narrow infill areas
|
// collapse too narrow infill areas
|
||||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||||
// append infill areas to fill_surfaces
|
|
||||||
this->fill_surfaces->append(
|
ExPolygons infill_exp = offset2_ex(
|
||||||
offset2_ex(
|
not_filled_exp,
|
||||||
union_ex(pp),
|
|
||||||
float(-min_perimeter_infill_spacing / 2.),
|
float(-min_perimeter_infill_spacing / 2.),
|
||||||
float(inset + min_perimeter_infill_spacing / 2.)),
|
float(inset + min_perimeter_infill_spacing / 2.));
|
||||||
stInternal);
|
// append infill areas to fill_surfaces
|
||||||
|
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
|
// BBS: get the no-overlap infill expolygons
|
||||||
{
|
{
|
||||||
append(*this->fill_no_overlap, offset2_ex(
|
ExPolygons polyWithoutOverlap;
|
||||||
union_ex(pp),
|
polyWithoutOverlap = offset2_ex(
|
||||||
|
not_filled_exp,
|
||||||
float(-min_perimeter_infill_spacing / 2.),
|
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:
|
private:
|
||||||
std::map<int, Polygons> generate_lower_polygons_series(float width);
|
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:
|
private:
|
||||||
bool m_spiral_vase;
|
bool m_spiral_vase;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue