mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-01 15:22:21 +00:00
Enabling object skirt. (#6487)
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  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:  Print by layer, object skirt:  Print by object , object skirt:  Print by object , common skirt (any meaning?): 
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Print.hpp"
|
||||
|
||||
#define BED_SHRINK_SEQ_PRINT 5
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -3356,10 +3356,10 @@ namespace ProcessLayer
|
||||
} // namespace ProcessLayer
|
||||
|
||||
namespace Skirt {
|
||||
static void skirt_loops_per_extruder_all_printing(const Print &print, const LayerTools &layer_tools, std::map<unsigned int, std::pair<size_t, size_t>> &skirt_loops_per_extruder_out)
|
||||
static void skirt_loops_per_extruder_all_printing(const Print &print, const ExtrusionEntityCollection &skirt, const LayerTools &layer_tools, std::map<unsigned int, std::pair<size_t, size_t>> &skirt_loops_per_extruder_out)
|
||||
{
|
||||
// Prime all extruders printing over the 1st layer over the skirt lines.
|
||||
size_t n_loops = print.skirt().entities.size();
|
||||
size_t n_loops = skirt.entities.size();
|
||||
size_t n_tools = layer_tools.extruders.size();
|
||||
size_t lines_per_extruder = (n_loops + n_tools - 1) / n_tools;
|
||||
|
||||
@@ -3377,6 +3377,7 @@ namespace Skirt {
|
||||
|
||||
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_1st_layer(
|
||||
const Print &print,
|
||||
const ExtrusionEntityCollection &skirt,
|
||||
const LayerTools &layer_tools,
|
||||
// Heights (print_z) at which the skirt has already been extruded.
|
||||
std::vector<coordf_t> &skirt_done)
|
||||
@@ -3386,8 +3387,8 @@ namespace Skirt {
|
||||
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
|
||||
//For sequential print, the following test may fail when extruding the 2nd and other objects.
|
||||
// assert(skirt_done.empty());
|
||||
if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt) {
|
||||
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
|
||||
if (skirt_done.empty() && print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt) {
|
||||
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
|
||||
skirt_done.emplace_back(layer_tools.print_z);
|
||||
}
|
||||
return skirt_loops_per_extruder_out;
|
||||
@@ -3395,6 +3396,7 @@ namespace Skirt {
|
||||
|
||||
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_other_layers(
|
||||
const Print &print,
|
||||
const ExtrusionEntityCollection &skirt,
|
||||
const LayerTools &layer_tools,
|
||||
// Heights (print_z) at which the skirt has already been extruded.
|
||||
std::vector<coordf_t> &skirt_done)
|
||||
@@ -3402,7 +3404,7 @@ namespace Skirt {
|
||||
// Extrude skirt at the print_z of the raft layers and normal object layers
|
||||
// not at the print_z of the interlaced support material layers.
|
||||
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
|
||||
if (print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt &&
|
||||
if (print.has_skirt() && ! skirt.entities.empty() && layer_tools.has_skirt &&
|
||||
// Not enough skirt layers printed yet.
|
||||
//FIXME infinite or high skirt does not make sense for sequential print!
|
||||
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) {
|
||||
@@ -3416,7 +3418,7 @@ namespace Skirt {
|
||||
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirt_loops.value);
|
||||
#else
|
||||
// Prime all extruders planned for this layer, see
|
||||
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
|
||||
skirt_loops_per_extruder_all_printing(print, skirt, layer_tools, skirt_loops_per_extruder_out);
|
||||
#endif
|
||||
assert(!skirt_done.empty());
|
||||
skirt_done.emplace_back(layer_tools.print_z);
|
||||
@@ -3425,6 +3427,33 @@ namespace Skirt {
|
||||
return skirt_loops_per_extruder_out;
|
||||
}
|
||||
|
||||
static Point find_start_point(ExtrusionLoop& loop, float start_angle) {
|
||||
coord_t min_x = std::numeric_limits<coord_t>::max();
|
||||
coord_t max_x = std::numeric_limits<coord_t>::min();
|
||||
coord_t min_y = min_x;
|
||||
coord_t max_y = max_x;
|
||||
|
||||
Points pts;
|
||||
loop.collect_points(pts);
|
||||
for (Point pt: pts) {
|
||||
if (pt.x() < min_x)
|
||||
min_x = pt.x();
|
||||
else if (pt.x() > max_x)
|
||||
max_x = pt.x();
|
||||
if (pt.y() < min_y)
|
||||
min_y = pt.y();
|
||||
else if (pt.y() > max_y)
|
||||
max_y = pt.y();
|
||||
}
|
||||
|
||||
Point center((min_x + max_x)/2., (min_y + max_y)/2.);
|
||||
double r = center.distance_to(Point(min_x, min_y));
|
||||
double deg = start_angle * PI / 180;
|
||||
double shift_x = r * std::cos(deg);
|
||||
double shift_y = r * std::sin(deg);
|
||||
return Point(center.x()+shift_x, center.y() + shift_y);
|
||||
}
|
||||
|
||||
} // namespace Skirt
|
||||
|
||||
// Orca: Klipper can't parse object names with spaces and other spetical characters
|
||||
@@ -3452,6 +3481,57 @@ inline std::string get_instance_name(const PrintObject *object, const PrintInsta
|
||||
return get_instance_name(object, inst.id);
|
||||
}
|
||||
|
||||
std::string GCode::generate_skirt(const Print &print,
|
||||
const ExtrusionEntityCollection &skirt,
|
||||
const Point& offset,
|
||||
const LayerTools &layer_tools,
|
||||
const Layer& layer,
|
||||
unsigned int extruder_id)
|
||||
{
|
||||
|
||||
bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON);
|
||||
std::string gcode;
|
||||
// Extrude skirt at the print_z of the raft layers and normal object layers
|
||||
// not at the print_z of the interlaced support material layers.
|
||||
// Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
|
||||
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
|
||||
skirt_loops_per_extruder = first_layer ?
|
||||
Skirt::make_skirt_loops_per_extruder_1st_layer(print, skirt, layer_tools, m_skirt_done) :
|
||||
Skirt::make_skirt_loops_per_extruder_other_layers(print, skirt, layer_tools, m_skirt_done);
|
||||
|
||||
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
||||
const std::pair<size_t, size_t> loops = loops_it->second;
|
||||
|
||||
set_origin(unscaled(offset));
|
||||
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
|
||||
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
||||
for (size_t i = first_layer ? loops.first : loops.second - 1; i < loops.second; ++i) {
|
||||
// Adjust flow according to this layer's layer height.
|
||||
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(skirt.entities[i]);
|
||||
for (ExtrusionPath &path : loop.paths) {
|
||||
path.height = layer_skirt_flow.height();
|
||||
path.mm3_per_mm = mm3_per_mm;
|
||||
}
|
||||
|
||||
//set skirt start point location
|
||||
if (first_layer && i==loops.first)
|
||||
this->set_last_pos(Skirt::find_start_point(loop, layer.object()->config().skirt_start_angle));
|
||||
|
||||
//FIXME using the support_speed of the 1st object printed.
|
||||
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value);
|
||||
if (!first_layer)
|
||||
break;
|
||||
}
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
|
||||
if (first_layer && loops.first == 0)
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// In sequential mode, process_layer is called once per each object and its copy,
|
||||
// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
|
||||
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
|
||||
@@ -3703,18 +3783,10 @@ LayerResult GCode::process_layer(
|
||||
m_second_layer_things_done = true;
|
||||
}
|
||||
|
||||
// Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
|
||||
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
|
||||
|
||||
if (single_object_instance_idx == size_t(-1)) {
|
||||
// Normal (non-sequential) print.
|
||||
gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config());
|
||||
}
|
||||
// Extrude skirt at the print_z of the raft layers and normal object layers
|
||||
// not at the print_z of the interlaced support material layers.
|
||||
skirt_loops_per_extruder = first_layer ?
|
||||
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
|
||||
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
|
||||
|
||||
// BBS: get next extruder according to flush and soluble
|
||||
auto get_next_extruder = [&](int current_extruder,const std::vector<unsigned int>&extruders) {
|
||||
@@ -3981,28 +4053,9 @@ LayerResult GCode::process_layer(
|
||||
// let analyzer tag generator aware of a role type change
|
||||
if (layer_tools.has_wipe_tower && m_wipe_tower)
|
||||
m_last_processor_extrusion_role = erWipeTower;
|
||||
|
||||
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
||||
const std::pair<size_t, size_t> loops = loops_it->second;
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
|
||||
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
||||
for (size_t i = (layer.id() == 0) ? loops.first : loops.second - 1; i < loops.second; ++i) {
|
||||
// Adjust flow according to this layer's layer height.
|
||||
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
|
||||
for (ExtrusionPath &path : loop.paths) {
|
||||
path.height = layer_skirt_flow.height();
|
||||
path.mm3_per_mm = mm3_per_mm;
|
||||
}
|
||||
//FIXME using the support_speed of the 1st object printed.
|
||||
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value);
|
||||
}
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
|
||||
if (first_layer && loops.first == 0)
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
}
|
||||
|
||||
if (print.config().skirt_type == stCombined && !print.skirt().empty())
|
||||
gcode += generate_skirt(print, print.skirt(), Point(0,0), layer_tools, layer, extruder_id);
|
||||
|
||||
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
||||
if (objects_by_extruder_it == by_extruder.end())
|
||||
@@ -4037,8 +4090,17 @@ LayerResult GCode::process_layer(
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (print.has_skirt() && print.config().print_sequence == PrintSequence::ByObject && prime_extruder && first_layer && extruder_id == first_extruder_id) {
|
||||
if (print.config().skirt_type == stPerObject &&
|
||||
print.config().print_sequence == PrintSequence::ByObject &&
|
||||
!layer.object()->object_skirt().empty() &&
|
||||
((layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled))
|
||||
)
|
||||
{
|
||||
for (InstanceToPrint& instance_to_print : instances_to_print) {
|
||||
|
||||
if (instance_to_print.print_object.object_skirt().empty())
|
||||
continue;
|
||||
|
||||
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() &&
|
||||
print.m_supportBrimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
|
||||
continue;
|
||||
@@ -4046,12 +4108,14 @@ LayerResult GCode::process_layer(
|
||||
if (this->m_objsWithBrim.find(instance_to_print.print_object.id()) != this->m_objsWithBrim.end() &&
|
||||
print.m_brimMap.at(instance_to_print.print_object.id()).entities.size() > 0)
|
||||
continue;
|
||||
if (first_layer)
|
||||
m_skirt_done.clear();
|
||||
|
||||
if (layer.id() == 1 && m_skirt_done.size() > 1)
|
||||
m_skirt_done.erase(m_skirt_done.begin()+1,m_skirt_done.end());
|
||||
|
||||
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
set_origin(unscaled(offset));
|
||||
for (ExtrusionEntity* ee : layer.object()->object_skirt().entities)
|
||||
//FIXME using the support_speed of the 1st object printed.
|
||||
gcode += this->extrude_entity(*ee, "skirt", m_config.support_speed.value);
|
||||
gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, layer_tools, layer, extruder_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4060,7 +4124,22 @@ LayerResult GCode::process_layer(
|
||||
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||
if (is_anything_overridden && print_wipe_extrusions == 0)
|
||||
gcode+="; PURGING FINISHED\n";
|
||||
|
||||
for (InstanceToPrint &instance_to_print : instances_to_print) {
|
||||
if (print.config().skirt_type == stPerObject &&
|
||||
!instance_to_print.print_object.object_skirt().empty() &&
|
||||
print.config().print_sequence == PrintSequence::ByLayer
|
||||
&&
|
||||
(layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled))
|
||||
{
|
||||
if (first_layer)
|
||||
m_skirt_done.clear();
|
||||
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, layer_tools, layer, extruder_id);
|
||||
if (instances_to_print.size() > 1 && &instance_to_print != &*(instances_to_print.end() - 1))
|
||||
m_skirt_done.pop_back();
|
||||
}
|
||||
|
||||
const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
|
||||
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
|
||||
// To control print speed of the 1st object layer printed over raft interface.
|
||||
|
||||
@@ -308,6 +308,13 @@ private:
|
||||
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
|
||||
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
|
||||
|
||||
std::string generate_skirt(const Print &print,
|
||||
const ExtrusionEntityCollection &skirt,
|
||||
const Point& offset,
|
||||
const LayerTools &layer_tools,
|
||||
const Layer& layer,
|
||||
unsigned int extruder_id);
|
||||
|
||||
LayerResult process_layer(
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
|
||||
@@ -774,7 +774,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
|
||||
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
|
||||
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
|
||||
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height", "draft_shield",
|
||||
"brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers",
|
||||
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
|
||||
"support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style",
|
||||
|
||||
@@ -219,12 +219,14 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||
// These steps have no influence on the G-code whatsoever. Just ignore them.
|
||||
} else if (
|
||||
opt_key == "skirt_loops"
|
||||
opt_key == "skirt_type"
|
||||
|| opt_key == "skirt_loops"
|
||||
|| opt_key == "skirt_speed"
|
||||
|| opt_key == "skirt_height"
|
||||
|| opt_key == "min_skirt_length"
|
||||
|| opt_key == "draft_shield"
|
||||
|| opt_key == "skirt_distance"
|
||||
|| opt_key == "skirt_start_angle"
|
||||
|| opt_key == "ooze_prevention"
|
||||
|| opt_key == "wipe_tower_x"
|
||||
|| opt_key == "wipe_tower_y"
|
||||
@@ -507,7 +509,7 @@ bool Print::has_infinite_skirt() const
|
||||
|
||||
bool Print::has_skirt() const
|
||||
{
|
||||
return (m_config.skirt_height > 0 && m_config.skirt_loops > 0) || m_config.draft_shield != dsDisabled;
|
||||
return (m_config.skirt_height > 0);
|
||||
}
|
||||
|
||||
bool Print::has_brim() const
|
||||
@@ -570,6 +572,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
auto [object_skirt_offset, _] = print.object_skirt_offset();
|
||||
std::vector<struct print_instance_info> print_instance_with_bounding_box;
|
||||
{
|
||||
// sequential_print_horizontal_clearance_valid
|
||||
@@ -578,10 +582,9 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
|
||||
polygons->clear();
|
||||
std::vector<size_t> intersecting_idxs;
|
||||
|
||||
bool all_objects_are_short = print.is_all_objects_are_short();
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float obj_distance = all_objects_are_short ? scale_(0.5*MAX_OUTER_NOZZLE_DIAMETER-0.1) : scale_(0.5*print.config().extruder_clearance_radius.value-0.1);
|
||||
float obj_distance = print.is_all_objects_are_short() ? scale_(std::max(0.5f * MAX_OUTER_NOZZLE_DIAMETER, object_skirt_offset) - 0.1) : scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset - 0.1);
|
||||
|
||||
for (const PrintObject *print_object : print.objects()) {
|
||||
assert(! print_object->model_object()->instances.empty());
|
||||
@@ -714,6 +717,7 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
|
||||
auto inter_y = inter_max - inter_min;
|
||||
|
||||
// 如果y方向的重合超过轮廓的膨胀量,说明两个物体在一行,应该先打左边的物体,即先比较二者的x坐标。
|
||||
// If the overlap in the y direction exceeds the expansion of the contour, it means that the two objects are in a row and the object on the left should be hit first, that is, the x coordinates of the two should be compared first.
|
||||
if (inter_y > scale_(0.5 * print.config().extruder_clearance_radius.value)) {
|
||||
if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) {
|
||||
if (lx1 > rx1) {
|
||||
@@ -814,7 +818,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
|
||||
{
|
||||
auto inst = print_instance_with_bounding_box[k].print_instance;
|
||||
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
|
||||
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value));
|
||||
// Only the offset from the nozzle to the slide bar needs to be considered, which is much smaller than the collision radius of the entire tool head.
|
||||
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset));
|
||||
auto iy1 = bbox.min.y();
|
||||
auto iy2 = bbox.max.y();
|
||||
(const_cast<ModelInstance*>(inst->model_instance))->arrange_order = k+1;
|
||||
@@ -2300,59 +2305,58 @@ void Print::_make_skirt()
|
||||
}
|
||||
}
|
||||
|
||||
// Number of skirt loops per skirt layer.
|
||||
size_t n_skirts = m_config.skirt_loops.value;
|
||||
if (this->has_infinite_skirt() && n_skirts == 0)
|
||||
n_skirts = 1;
|
||||
|
||||
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
||||
// The skirt will touch the brim if the brim is extruded.
|
||||
auto distance = float(scale_(m_config.skirt_distance.value) - spacing/2.);
|
||||
auto distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
// Draw outlines from outside to inside.
|
||||
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
||||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||||
for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) {
|
||||
this->throw_if_canceled();
|
||||
// Offset the skirt outside.
|
||||
distance += float(scale_(spacing));
|
||||
// Generate the skirt centerline.
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
loop = loops.front();
|
||||
}
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||
m_skirt.append(eloop);
|
||||
if (m_config.min_skirt_length.value > 0) {
|
||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
|
||||
// Not extruded enough yet with the current extruder. Add another loop.
|
||||
if (i == 1)
|
||||
++ i;
|
||||
} else {
|
||||
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
|
||||
// Enough extruded with the current extruder. Extrude with the next one,
|
||||
// until the prescribed number of skirt loops is extruded.
|
||||
if (extruder_idx + 1 < extruders.size())
|
||||
++ extruder_idx;
|
||||
if (m_config.skirt_type == stCombined) {
|
||||
for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
|
||||
this->throw_if_canceled();
|
||||
// Offset the skirt outside.
|
||||
distance += float(scale_(spacing));
|
||||
// Generate the skirt centerline.
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
loop = loops.front();
|
||||
}
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||
m_skirt.append(eloop);
|
||||
if (m_config.min_skirt_length.value > 0) {
|
||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
|
||||
// Not extruded enough yet with the current extruder. Add another loop.
|
||||
if (i == 1)
|
||||
++ i;
|
||||
} else {
|
||||
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
|
||||
// Enough extruded with the current extruder. Extrude with the next one,
|
||||
// until the prescribed number of skirt loops is extruded.
|
||||
if (extruder_idx + 1 < extruders.size())
|
||||
++ extruder_idx;
|
||||
}
|
||||
} else {
|
||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||||
}
|
||||
} else {
|
||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||||
}
|
||||
} else {
|
||||
m_skirt.clear();
|
||||
}
|
||||
// Brims were generated inside out, reverse to print the outmost contour first.
|
||||
m_skirt.reverse();
|
||||
@@ -2361,34 +2365,56 @@ void Print::_make_skirt()
|
||||
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||||
append(m_skirt_convex_hull, std::move(poly.points));
|
||||
|
||||
// BBS
|
||||
const int n_object_skirts = 1;
|
||||
const double object_skirt_distance = scale_(1.0);
|
||||
for (auto obj_cvx_hull : object_convex_hulls) {
|
||||
PrintObject* object = obj_cvx_hull.first;
|
||||
for (int i = 0; i < n_object_skirts; i++) {
|
||||
distance += float(scale_(spacing));
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
loop = loops.front();
|
||||
}
|
||||
if (m_config.skirt_type == stPerObject) {
|
||||
// BBS
|
||||
for (auto obj_cvx_hull : object_convex_hulls) {
|
||||
double object_skirt_distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
PrintObject* object = obj_cvx_hull.first;
|
||||
object->m_skirt.clear();
|
||||
extruded_length.assign(extruded_length.size(), 0.);
|
||||
for (size_t i = m_config.skirt_loops.value, extruder_idx = 0; i > 0; -- i) {
|
||||
object_skirt_distance += float(scale_(spacing));
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
loop = loops.front();
|
||||
}
|
||||
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||
object->m_skirt.append(std::move(eloop));
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||
object->m_skirt.append(std::move(eloop));
|
||||
if (m_config.min_skirt_length.value > 0) {
|
||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
|
||||
// Not extruded enough yet with the current extruder. Add another loop.
|
||||
if (i == 1)
|
||||
++ i;
|
||||
} else {
|
||||
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
|
||||
// Enough extruded with the current extruder. Extrude with the next one,
|
||||
// until the prescribed number of skirt loops is extruded.
|
||||
if (extruder_idx + 1 < extruders.size())
|
||||
++ extruder_idx;
|
||||
}
|
||||
} else {
|
||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||||
}
|
||||
|
||||
}
|
||||
object->m_skirt.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2924,6 +2950,29 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
|
||||
}
|
||||
|
||||
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
|
||||
{
|
||||
if (config().skirt_loops == 0 || config().skirt_type != stPerObject)
|
||||
return std::make_tuple(0, 0);
|
||||
|
||||
float max_nozzle_diameter = *std::max_element(m_config.nozzle_diameter.values.begin(), m_config.nozzle_diameter.values.end());
|
||||
float max_layer_height = *std::max_element(config().max_layer_height.values.begin(), config().max_layer_height.values.end());
|
||||
float line_width = m_config.initial_layer_line_width.get_abs_value(max_nozzle_diameter);
|
||||
float object_skirt_witdh = skirt_flow().width() + (config().skirt_loops - 1) * skirt_flow().spacing();
|
||||
float object_skirt_offset = 0;
|
||||
|
||||
if (is_all_objects_are_short())
|
||||
object_skirt_offset = config().skirt_distance + object_skirt_witdh;
|
||||
else if (config().draft_shield == dsEnabled || config().skirt_height * max_layer_height > config().nozzle_height - margin_height)
|
||||
object_skirt_offset = config().skirt_distance + line_width;
|
||||
else if (config().skirt_distance + object_skirt_witdh > config().extruder_clearance_radius/2)
|
||||
object_skirt_offset = (config().skirt_distance + object_skirt_witdh - config().extruder_clearance_radius/2);
|
||||
else
|
||||
return std::make_tuple(0, 0);
|
||||
|
||||
return std::make_tuple(object_skirt_offset, object_skirt_witdh);
|
||||
}
|
||||
|
||||
DynamicConfig PrintStatistics::config() const
|
||||
{
|
||||
DynamicConfig config;
|
||||
|
||||
@@ -38,7 +38,6 @@ class SupportLayer;
|
||||
class TreeSupportData;
|
||||
class TreeSupport;
|
||||
|
||||
#define MARGIN_HEIGHT 1.5
|
||||
#define MAX_OUTER_NOZZLE_DIAMETER 4
|
||||
// BBS: move from PrintObjectSlice.cpp
|
||||
struct VolumeSlices
|
||||
@@ -989,6 +988,8 @@ public:
|
||||
// Returns scaling for each axis representing shrinkage compensations in each axis.
|
||||
Vec3d shrinkage_compensation() const;
|
||||
|
||||
std::tuple<float, float> object_skirt_offset(double margin_height = 0) const;
|
||||
|
||||
protected:
|
||||
// Invalidates the step, and its depending steps in Print.
|
||||
bool invalidate_step(PrintStep step);
|
||||
|
||||
@@ -317,9 +317,14 @@ static const t_config_enum_values s_keys_map_TimelapseType = {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TimelapseType)
|
||||
|
||||
static const t_config_enum_values s_keys_map_SkirtType = {
|
||||
{ "combined", stCombined },
|
||||
{ "perobject", stPerObject }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SkirtType)
|
||||
|
||||
static const t_config_enum_values s_keys_map_DraftShield = {
|
||||
{ "disabled", dsDisabled },
|
||||
{ "limited", dsLimited },
|
||||
{ "enabled", dsEnabled }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield)
|
||||
@@ -1614,7 +1619,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comDevelop;
|
||||
def->set_default_value(new ConfigOptionFloat(4));
|
||||
def->set_default_value(new ConfigOptionFloat(2.5));
|
||||
|
||||
def = this->add("bed_mesh_min", coPoint);
|
||||
def->label = L("Bed mesh min");
|
||||
@@ -4017,6 +4022,15 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(2));
|
||||
|
||||
def = this->add("skirt_start_angle", coFloat);
|
||||
def->label = L("Skirt start point");
|
||||
def->tooltip = L("Angle from the object center to skirt start point. Zero is the most right position, counter clockwise is positive angle.");
|
||||
def->sidetext = L("°");
|
||||
def->min = -180;
|
||||
def->max = 180;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(-135));
|
||||
|
||||
def = this->add("skirt_height", coInt);
|
||||
def->label = L("Skirt height");
|
||||
//def->label = "Skirt height";
|
||||
@@ -4030,21 +4044,29 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Draft shield");
|
||||
def->tooltip = L("A draft shield is useful to protect an ABS or ASA print from warping and detaching from print bed due to wind draft. "
|
||||
"It is usually needed only with open frame printers, i.e. without an enclosure. \n\n"
|
||||
"Options:\n"
|
||||
"Enabled = skirt is as tall as the highest printed object.\n"
|
||||
"Limited = skirt is as tall as specified by skirt height.\n\n"
|
||||
"Enabled = skirt is as tall as the highest printed object. Otherwise 'Skirt height' is used.\n"
|
||||
"Note: With the draft shield active, the skirt will be printed at skirt distance from the object. Therefore, if brims "
|
||||
"are active it may intersect with them. To avoid this, increase the skirt distance value.\n");
|
||||
def->enum_keys_map = &ConfigOptionEnum<DraftShield>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("limited");
|
||||
def->enum_values.push_back("enabled");
|
||||
def->enum_labels.push_back(L("Disabled"));
|
||||
def->enum_labels.push_back(L("Limited"));
|
||||
def->enum_labels.push_back(L("Enabled"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<DraftShield>(dsDisabled));
|
||||
|
||||
def = this->add("skirt_type", coEnum);
|
||||
def->label = L("Skirt type");
|
||||
def->full_label = L("Skirt type");
|
||||
def->tooltip = L("Combined - single skirt for all objects, Per object - individual object skirt.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<SkirtType>::get_enum_values();
|
||||
def->enum_values.push_back("combined");
|
||||
def->enum_values.push_back("perobject");
|
||||
def->enum_labels.push_back(L("Combined"));
|
||||
def->enum_labels.push_back(L("Per object"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<SkirtType>(stCombined));
|
||||
|
||||
def = this->add("skirt_loops", coInt);
|
||||
def->label = L("Skirt loops");
|
||||
def->full_label = L("Skirt loops");
|
||||
@@ -4067,7 +4089,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Skirt minimum extrusion length");
|
||||
def->full_label = L("Skirt minimum extrusion length");
|
||||
def->tooltip = L("Minimum filament extrusion length in mm when printing the skirt. Zero means this feature is disabled.\n\n"
|
||||
"Using a non zero value is useful if the printer is set up to print without a prime line.");
|
||||
"Using a non zero value is useful if the printer is set up to print without a prime line.\n"
|
||||
"Final number of loops is not taling into account whli arranging or validating objects distance. Increase loop number in such case. ");
|
||||
def->min = 0;
|
||||
def->sidetext = L("mm");
|
||||
def->mode = comAdvanced;
|
||||
@@ -6175,9 +6198,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
else if(opt_key == "ironing_direction") {
|
||||
opt_key = "ironing_angle";
|
||||
}
|
||||
else if(opt_key == "counterbole_hole_bridging"){
|
||||
else if(opt_key == "counterbole_hole_bridging") {
|
||||
opt_key = "counterbore_hole_bridging";
|
||||
}
|
||||
else if (opt_key == "draft_shield" && value == "limited") {
|
||||
value = "disabled";
|
||||
}
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> ignore = {
|
||||
|
||||
@@ -224,8 +224,12 @@ enum TimelapseType : int {
|
||||
tlSmooth
|
||||
};
|
||||
|
||||
enum SkirtType {
|
||||
stCombined, stPerObject
|
||||
};
|
||||
|
||||
enum DraftShield {
|
||||
dsDisabled, dsLimited, dsEnabled
|
||||
dsDisabled, dsEnabled
|
||||
};
|
||||
|
||||
enum class PerimeterGeneratorType
|
||||
@@ -393,6 +397,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TimelapseType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BedType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SkirtType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
|
||||
@@ -739,6 +744,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, brim_width))
|
||||
((ConfigOptionFloat, brim_ears_detection_length))
|
||||
((ConfigOptionFloat, brim_ears_max_angle))
|
||||
((ConfigOptionFloat, skirt_start_angle))
|
||||
((ConfigOptionBool, bridge_no_support))
|
||||
((ConfigOptionFloat, elefant_foot_compensation))
|
||||
((ConfigOptionInt, elefant_foot_compensation_layers))
|
||||
@@ -1224,6 +1230,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionFloat, skirt_distance))
|
||||
((ConfigOptionInt, skirt_height))
|
||||
((ConfigOptionInt, skirt_loops))
|
||||
((ConfigOptionEnum<SkirtType>, skirt_type))
|
||||
((ConfigOptionFloat, skirt_speed))
|
||||
((ConfigOptionFloat, min_skirt_length))
|
||||
((ConfigOptionFloats, slow_down_layer_time))
|
||||
|
||||
Reference in New Issue
Block a user