Skip to content

Commit

Permalink
Enabling object skirt. (#6487)
Browse files Browse the repository at this point in the history
Implements individual object skirts, as requested in  **Individual skirts when printing by object. #3486** and  **Skirt around each object when using "by object" sequence #2652**

Now skirt type could be
![image](https://github.com/user-attachments/assets/532ecd91-fe3e-4bdb-b22f-ef28439ab7a6)

This PR based on several refactorings, including  **Configure skirt start point #6490** PR. To make review more easy each step was committed individually. Obviously them could be separated to different PRs or dropped.

I removed `prime_extruder && first_layer && extruder_id == first_extruder_id` as explicit object skirt is available.

Open question : do we need both common and object skirt together, probably with separate loop number?


All four combinations are possible.

Print by layer, common skirt:
![image](https://github.com/user-attachments/assets/5509f10b-ed3b-4243-8763-7ddcb101c67a)

Print by layer, object skirt:
![image](https://github.com/user-attachments/assets/deca3ca6-8bf7-4e5b-a356-2f5dd6e81566)

Print by object , object skirt:
![image](https://github.com/user-attachments/assets/7d1b2ddc-e61f-4d77-9ed4-e33fdd62d290)

Print by object , common skirt (any meaning?):
![image](https://github.com/user-attachments/assets/e7883eb4-b1a3-4158-a7f4-ce0658bc1939)
  • Loading branch information
SoftFever authored Sep 7, 2024
2 parents 8d5fdf9 + 5165515 commit 415bede
Show file tree
Hide file tree
Showing 17 changed files with 338 additions and 377 deletions.
12 changes: 6 additions & 6 deletions src/OrcaSlicer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3028,7 +3028,7 @@ int CLI::run(int argc, char **argv)
double print_height = m_print_config.opt_float("printable_height");
double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
double cleareance_radius = m_print_config.opt_float("extruder_clearance_radius");
double clearance_radius = m_print_config.opt_float("extruder_clearance_radius");
//double plate_stride;
std::string bed_texture;

Expand Down Expand Up @@ -3748,12 +3748,12 @@ int CLI::run(int argc, char **argv)
{
if (((old_height_to_rod != 0.f) && (old_height_to_rod != height_to_rod))
|| ((old_height_to_lid != 0.f) && (old_height_to_lid != height_to_lid))
|| ((old_max_radius != 0.f) && (old_max_radius != cleareance_radius)))
|| ((old_max_radius != 0.f) && (old_max_radius != clearance_radius)))
{
if (is_seq_print_for_curr_plate) {
need_arrange = true;
BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%, old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, cleareance_radius %6%, need arrange!")
%old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %cleareance_radius;
BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%, old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, clearance_radius %6%, need arrange!")
%old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %clearance_radius;
}
}
}
Expand Down Expand Up @@ -3897,7 +3897,7 @@ int CLI::run(int argc, char **argv)
arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.cleareance_radius = cleareance_radius;
arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) {
Expand Down Expand Up @@ -4300,7 +4300,7 @@ int CLI::run(int argc, char **argv)
arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.cleareance_radius = cleareance_radius;
arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) {
Expand Down
18 changes: 8 additions & 10 deletions src/libslic3r/Arrange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ void update_arrange_params(ArrangeParams& params, const DynamicPrintConfig* prin
params.brim_skirt_distance = skirt_distance;
params.bed_shrink_x += params.brim_skirt_distance;
params.bed_shrink_y += params.brim_skirt_distance;
// for sequential print, we need to inflate the bed because cleareance_radius is so large
// for sequential print, we need to inflate the bed because clearance_radius is so large
if (params.is_seq_print) {
params.bed_shrink_x -= params.cleareance_radius / 2;
params.bed_shrink_y -= params.cleareance_radius / 2;
params.bed_shrink_x -= params.clearance_radius / 2;
params.bed_shrink_y -= params.clearance_radius / 2;
}
}

Expand All @@ -103,12 +103,10 @@ void update_selected_items_inflation(ArrangePolygons& selected, const DynamicPri
BoundingBox bedbb = Polygon(bedpts).bounding_box();
// set obj distance for auto seq_print
if (params.is_seq_print) {
bool all_objects_are_short = std::all_of(selected.begin(), selected.end(), [&](ArrangePolygon& ap) { return ap.height < params.nozzle_height; });
if (all_objects_are_short) {
params.min_obj_distance = std::max(params.min_obj_distance, scaled(double(MAX_OUTER_NOZZLE_DIAMETER)/2+0.001));
}
if (params.all_objects_are_short)
params.min_obj_distance = std::max(params.min_obj_distance, scaled(std::max(MAX_OUTER_NOZZLE_DIAMETER/2.f, params.object_skirt_offset*2)+0.001));
else
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.clearance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
}
double brim_max = 0;
bool plate_has_tree_support = false;
Expand All @@ -135,8 +133,8 @@ void update_unselected_items_inflation(ArrangePolygons& unselected, const Dynami
{
float exclusion_gap = 1.f;
if (params.is_seq_print) {
// bed_shrink_x is typically (-params.cleareance_radius / 2+5) for seq_print
exclusion_gap = std::max(exclusion_gap, params.cleareance_radius / 2 + params.bed_shrink_x + 1.f); // +1mm gap so the exclusion region is not too close
// bed_shrink_x is typically (-params.clearance_radius / 2+5) for seq_print
exclusion_gap = std::max(exclusion_gap, params.clearance_radius / 2 + params.bed_shrink_x + 1.f); // +1mm gap so the exclusion region is not too close
// dont forget to move the excluded region
for (auto& region : unselected) {
if (region.is_virt_object) region.poly.translate(scaled(params.bed_shrink_x), scaled(params.bed_shrink_y));
Expand Down
7 changes: 5 additions & 2 deletions src/libslic3r/Arrange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "ExPolygon.hpp"
#include "PrintConfig.hpp"
#include "Print.hpp"

#define BED_SHRINK_SEQ_PRINT 5

Expand Down Expand Up @@ -131,8 +132,10 @@ struct ArrangeParams {
float brim_skirt_distance = 0;
float clearance_height_to_rod = 0;
float clearance_height_to_lid = 0;
float cleareance_radius = 0;
float clearance_radius = 0;
float object_skirt_offset = 0;
float nozzle_height = 0;
bool all_objects_are_short = false;
float printable_height = 256.0;
Vec2d align_center{ 0.5,0.5 };

Expand Down Expand Up @@ -168,7 +171,7 @@ struct ArrangeParams {
ret += "\"brim_skirt_distance\":" + std::to_string(brim_skirt_distance) + ",";
ret += "\"clearance_height_to_rod\":" + std::to_string(clearance_height_to_rod) + ",";
ret += "\"clearance_height_to_lid\":" + std::to_string(clearance_height_to_lid) + ",";
ret += "\"cleareance_radius\":" + std::to_string(cleareance_radius) + ",";
ret += "\"clearance_radius\":" + std::to_string(clearance_radius) + ",";
ret += "\"printable_height\":" + std::to_string(printable_height) + ",";
return ret;
}
Expand Down
209 changes: 0 additions & 209 deletions src/libslic3r/Brim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1714,213 +1714,4 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;
}

// Produce brim lines around those objects, that have the brim enabled.
// Collect islands_area to be merged into the final 1st layer convex hull.
ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area)
{
double brim_width_max = 0;
std::map<ObjectID, double> brim_width_map;
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
Flow flow = print.brim_flow();
std::vector<ExPolygons> bottom_layers_expolygons = get_print_bottom_layers_expolygons(print);
ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons);
Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim, scaled_resolution);
ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()), brim_width_max, brim_width_map);
islands_area = to_polygons(islands_area_ex);

Polygons loops = tryExPolygonOffset(islands_area_ex, print);
size_t num_loops = size_t(floor(brim_width_max / flow.spacing()));
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;

loops = union_pt_chained_outside_in(loops);

std::vector<Polylines> loops_pl_by_levels;
{
Polylines loops_pl = to_polylines(loops);
loops_pl_by_levels.assign(loops_pl.size(), Polylines());
tbb::parallel_for(tbb::blocked_range<size_t>(0, loops_pl.size()),
[&loops_pl_by_levels, &loops_pl, &islands_area](const tbb::blocked_range<size_t> &range) {
for (size_t i = range.begin(); i < range.end(); ++i) {
loops_pl_by_levels[i] = chain_polylines(intersection_pl({ std::move(loops_pl[i]) }, islands_area));
}
});
}

// output
ExtrusionEntityCollection brim;

// Reduce down to the ordered list of polylines.
Polylines all_loops;
for (Polylines &polylines : loops_pl_by_levels)
append(all_loops, std::move(polylines));
loops_pl_by_levels.clear();

// Flip orientation of open polylines to minimize travel distance.
optimize_polylines_by_reversing(&all_loops);

#ifdef BRIM_DEBUG_TO_SVG
static int irun = 0;
++ irun;

{
SVG svg(debug_out_path("brim-%d.svg", irun).c_str(), get_extents(all_loops));
svg.draw(union_ex(islands), "blue");
svg.draw(islands_area_ex, "green");
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
}
#endif // BRIM_DEBUG_TO_SVG

all_loops = connect_brim_lines(std::move(all_loops), offset(islands_area_ex, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);

#ifdef BRIM_DEBUG_TO_SVG
{
SVG svg(debug_out_path("brim-connected-%d.svg", irun).c_str(), get_extents(all_loops));
svg.draw(union_ex(islands), "blue");
svg.draw(islands_area_ex, "green");
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
}
#endif // BRIM_DEBUG_TO_SVG

const bool could_brim_intersects_skirt = std::any_of(print.objects().begin(), print.objects().end(), [&print, &brim_width_map, brim_width_max](PrintObject *object) {
const BrimType &bt = object->config().brim_type;
return (bt == btOuterOnly || bt == btOuterAndInner || bt == btAutoBrim) && print.config().skirt_distance.value < brim_width_map[object->id()];
});

const bool draft_shield = print.config().draft_shield != dsDisabled;


// If there is a possibility that brim intersects skirt, go through loops and split those extrusions
// The result is either the original Polygon or a list of Polylines
if (draft_shield && ! print.skirt().empty() && could_brim_intersects_skirt)
{
// Find the bounding polygons of the skirt
const Polygons skirt_inners = offset(dynamic_cast<ExtrusionLoop*>(print.skirt().entities.back())->polygon(),
-float(scale_(print.skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));
const Polygons skirt_outers = offset(dynamic_cast<ExtrusionLoop*>(print.skirt().entities.front())->polygon(),
float(scale_(print.skirt_flow().spacing()))/2.f,
ClipperLib::jtRound,
float(scale_(0.1)));

// First calculate the trimming region.
ClipperLib_Z::Paths trimming;
{
ClipperLib_Z::Paths input_subject;
ClipperLib_Z::Paths input_clip;
for (const Polygon &poly : skirt_outers) {
input_subject.emplace_back();
ClipperLib_Z::Path &out = input_subject.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
for (const Polygon &poly : skirt_inners) {
input_clip.emplace_back();
ClipperLib_Z::Path &out = input_clip.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
// add polygons
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
// perform operation
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
}

// Second, trim the extrusion loops with the trimming regions.
ClipperLib_Z::Paths loops_trimmed;
{
// Produce ClipperLib_Z::Paths from polylines (not necessarily closed).
ClipperLib_Z::Paths input_clip;
for (const Polyline &loop_pl : all_loops) {
input_clip.emplace_back();
ClipperLib_Z::Path& out = input_clip.back();
out.reserve(loop_pl.points.size());
int64_t loop_idx = &loop_pl - &all_loops.front();
for (const Point& pt : loop_pl.points)
// The Z coordinate carries index of the source loop.
out.emplace_back(pt.x(), pt.y(), loop_idx + 1);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
// hat the Z coordinate not set to the contour coordinate.
pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
});
// add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
// perform operation
ClipperLib_Z::PolyTree loops_trimmed_tree;
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(loops_trimmed_tree), loops_trimmed);
}

// Third, produce the extrusions, sorted by the source loop indices.
{
std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
loops_trimmed_order.reserve(loops_trimmed.size());
for (const ClipperLib_Z::Path &path : loops_trimmed) {
size_t input_idx = 0;
for (const ClipperLib_Z::IntPoint &pt : path)
if (pt.z() > 0) {
input_idx = (size_t)pt.z();
break;
}
assert(input_idx != 0);
loops_trimmed_order.emplace_back(&path, input_idx);
}
std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
return l.second < r.second;
});

Point last_pt(0, 0);
for (size_t i = 0; i < loops_trimmed_order.size();) {
// Find all pieces that the initial loop was split into.
size_t j = i + 1;
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ;
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) {
auto *loop = new ExtrusionLoop();
brim.entities.emplace_back(loop);
loop->paths.emplace_back(erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
i = j;
} else {
//FIXME The path chaining here may not be optimal.
ExtrusionEntityCollection this_loop_trimmed;
this_loop_trimmed.entities.reserve(j - i);
for (; i < j; ++ i) {
this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = dynamic_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
}
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size());
append(brim.entities, std::move(this_loop_trimmed.entities));
this_loop_trimmed.entities.clear();
}
last_pt = brim.last_point();
}
}
} else {
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
}

make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim);
return brim;
}

} // namespace Slic3r
1 change: 0 additions & 1 deletion src/libslic3r/Brim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class ObjectID;

// Produce brim lines around those objects, that have the brim enabled.
// Collect islands_area to be merged into the final 1st layer convex hull.
ExtrusionEntityCollection make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_area);
void make_brim(const Print& print, PrintTryCancel try_cancel,
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
Expand Down
Loading

0 comments on commit 415bede

Please sign in to comment.