Skip to content

Commit

Permalink
single wall top Arachne
Browse files Browse the repository at this point in the history
  • Loading branch information
vovodroid authored and mjonuschat committed Feb 19, 2024
1 parent 964d63f commit 93e5b81
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 3 deletions.
12 changes: 12 additions & 0 deletions src/libslic3r/ClipperUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,18 @@ namespace ClipperUtils {
out.end());
return out;
}
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox)
{
Polygons out;
out.reserve(number_polygons(src));
for (const ExPolygon &p : src) {
Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox);
out.insert(out.end(), temp.begin(), temp.end());
}

out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) {return polygon.empty(); }), out.end());
return out;
}
}

static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree)
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/ClipperUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ namespace ClipperUtils {
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox);
}

// offset Polygons
Expand Down
145 changes: 144 additions & 1 deletion src/libslic3r/PerimeterGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,10 +1121,62 @@ void PerimeterGenerator::process_arachne(
loop_number = 0;

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);

//Orca
std::vector<Arachne::VariableWidthLines> out_shell;
ExPolygons top_fills;
if (loop_number > 0 && params.config.only_one_perimeter_top && !surface.is_bridge() && upper_slices != nullptr) {
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
ExPolygons non_top_polygons;
ExPolygons fill_clip;

split_top_surfaces(params, lower_slices, upper_slices, 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, ext_perimeter_spacing, perimeter_spacing, coord_t(1),
0, params.layer_height, params.object_config, params.print_config);
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, 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();

//Orca
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
Expand Down Expand Up @@ -1307,6 +1359,11 @@ void PerimeterGenerator::process_arachne(
float(- min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.));

// append infill areas to fill_surfaces
if (!top_fills.empty()) {
infill_areas = union_ex(infill_areas, offset_ex(top_fills, double(inset)));
}

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
Expand Down Expand Up @@ -1685,4 +1742,90 @@ void PerimeterGenerator::process_classic(
append(out_fill_expolygons, std::move(infill_areas));
}

//Orca
void PerimeterGenerator::split_top_surfaces(const Parameters &params,const ExPolygons *lower_slices,const ExPolygons *upper_slices,const ExPolygons &orig_polygons, ExPolygons &top_fills,
ExPolygons &non_top_polygons, ExPolygons &fill_clip) {
// other perimeters
coord_t perimeter_width = params.perimeter_flow.scaled_width();
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();

bool has_gap_fill = params.config.gap_fill_enabled;

// split the polygons with top/not_top
// get the offset from solid surface anchor
coord_t offset_top_surface = ext_perimeter_width;

// don't takes into account too thin areas
// skip if the exposed area is smaller than "min_width_top_surface"
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_(params.config.min_width_top_surface.get_abs_value(unscale_(perimeter_width))));

Polygons grown_upper_slices = offset(*upper_slices, min_width_top_surface);

// get boungding box of last
BoundingBox last_box = get_extents(orig_polygons);
last_box.offset(SCALED_EPSILON);

// 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 = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder);
// Check whether surface be bridge or not
if (lower_slices != NULL) {
// BBS: get the Polygons below the polygon this layer
Polygons lower_polygons_series_clipped =
ClipperUtils::clip_clipper_polygons_with_subject_bbox(*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 = params.config.infill_extrusion_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();
//}
}

}
3 changes: 3 additions & 0 deletions src/libslic3r/PerimeterGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ void process_arachne(

ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);

void split_top_surfaces(const Parameters &params, const ExPolygons *lower_slices,const ExPolygons *upper_slices, const ExPolygons &orig_polygons, ExPolygons &top_fills,
ExPolygons &non_top_polygons, ExPolygons &fill_clip);

} // namespace PerimeterGenerator
} // namespace Slic3r

Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/libslic3r.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ static constexpr double INSET_OVERLAP_TOLERANCE = 0.4;
// 3mm ring around the top / bottom / bridging areas.
//FIXME This is quite a lot.
static constexpr double EXTERNAL_INFILL_MARGIN = 3.;
static constexpr double BRIDGE_INFILL_MARGIN = 1.;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)
#define unscale_(val) ((val) * SCALING_FACTOR)

#define SCALED_EPSILON scale_(EPSILON)

Expand Down
3 changes: 1 addition & 2 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("min_bead_width", have_arachne);
toggle_field("thin_walls", !have_arachne);

toggle_field("only_one_perimeter_top", !have_arachne);
toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top") && !have_arachne);
toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top"));
}

void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
Expand Down

0 comments on commit 93e5b81

Please sign in to comment.