diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 51dd9f929..a109a9b15 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1298,7 +1298,21 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). - + { + // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + auto pts = std::make_unique(); + pts->values.reserve(print.first_layer_convex_hull().size()); + for (const Point &pt : print.first_layer_convex_hull().points) + pts->values.emplace_back(unscale(pt)); + BoundingBoxf bbox(pts->values); + m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); + m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + } std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 54e4baf2c..c104d46da 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -166,6 +166,7 @@ class SupportLayer : public Layer { public: // Polygons covered by the supports: base, interface and contact areas. + // Used to suppress retraction if moving for a support extrusion over these support_islands. ExPolygonCollection support_islands; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index dad41a642..bd04aab15 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -244,7 +244,6 @@ bool Print::invalidate_step(PrintStep step) { bool invalidated = Inherited::invalidate_step(step); // Propagate to dependent steps. - //FIXME Why should skirt invalidate brim? Shouldn't it be vice versa? if (step == psSkirt) invalidated |= Inherited::invalidate_step(psBrim); if (step != psGCodeExport) @@ -1606,6 +1605,8 @@ void Print::process() } if (this->set_started(psSkirt)) { m_skirt.clear(); + m_skirt_convex_hull.clear(); + m_first_layer_convex_hull.points.clear(); if (this->has_skirt()) { this->set_status(88, L("Generating skirt")); this->_make_skirt(); @@ -1614,11 +1615,15 @@ void Print::process() } if (this->set_started(psBrim)) { m_brim.clear(); + m_first_layer_convex_hull.points.clear(); if (m_config.brim_width > 0) { this->set_status(88, L("Generating brim")); this->_make_brim(); } - this->set_done(psBrim); + // Brim depends on skirt (brim lines are trimmed by the skirt lines), therefore if + // the skirt gets invalidated, brim gets invalidated as well and the following line is called. + this->finalize_first_layer_convex_hull(); + this->set_done(psBrim); } BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } @@ -1697,22 +1702,7 @@ void Print::_make_skirt() } // Include the wipe tower. - if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { - double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; - double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; - Vec2d pt = Vec2d(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); - - std::vector pts; - pts.push_back(Vec2d(pt.x(), pt.y())); - pts.push_back(Vec2d(pt.x()+width, pt.y())); - pts.push_back(Vec2d(pt.x()+width, pt.y()+depth)); - pts.push_back(Vec2d(pt.x(), pt.y()+depth)); - for (Vec2d& pt : pts) { - pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; - pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); - points.push_back(Point(scale_(pt.x()), scale_(pt.y()))); - } - } + append(points, this->first_layer_wipe_tower_corners()); if (points.size() < 3) // At least three points required for a convex hull. @@ -1796,28 +1786,19 @@ void Print::_make_skirt() } // Brims were generated inside out, reverse to print the outmost contour first. m_skirt.reverse(); + + // Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull. + 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)); } void Print::_make_brim() { // Brim is only printed on first layer and uses perimeter extruder. + Polygons islands = this->first_layer_islands(); + Polygons loops; Flow flow = this->brim_flow(); - Polygons islands; - for (PrintObject *object : m_objects) { - Polygons object_islands; - for (ExPolygon &expoly : object->m_layers.front()->lslices) - object_islands.push_back(expoly.contour); - if (! object->support_layers().empty()) - object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); - islands.reserve(islands.size() + object_islands.size() * object->instances().size()); - for (const PrintInstance &instance : object->instances()) - for (Polygon &poly : object_islands) { - islands.push_back(poly); - islands.back().translate(instance.shift); - } - } - Polygons loops; - size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); + size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); for (size_t i = 0; i < num_loops; ++ i) { this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); @@ -1828,6 +1809,11 @@ void Print::_make_brim() p.pop_back(); poly.points = std::move(p); } + if (i + 1 == num_loops) { + // Remember the outer edge of the last brim line extruded as m_first_layer_convex_hull. + for (Polygon &poly : islands) + append(m_first_layer_convex_hull.points, poly.points); + } polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); } loops = union_pt_chained(loops, false); @@ -1967,6 +1953,58 @@ void Print::_make_brim() } } +Polygons Print::first_layer_islands() const +{ + Polygons islands; + for (PrintObject *object : m_objects) { + Polygons object_islands; + for (ExPolygon &expoly : object->m_layers.front()->lslices) + object_islands.push_back(expoly.contour); + if (! object->support_layers().empty()) + object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); + islands.reserve(islands.size() + object_islands.size() * object->instances().size()); + for (const PrintInstance &instance : object->instances()) + for (Polygon &poly : object_islands) { + islands.push_back(poly); + islands.back().translate(instance.shift); + } + } + return islands; +} + +std::vector Print::first_layer_wipe_tower_corners() const +{ + std::vector corners; + if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { + double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; + double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; + Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); + for (Vec2d pt : { + pt0, + Vec2d(pt0.x()+width, pt0.y() ), + Vec2d(pt0.x()+width, pt0.y()+depth), + Vec2d(pt0.x(), pt0.y()+depth) + }) { + pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; + pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); + corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); + } + } + return corners; +} + +void Print::finalize_first_layer_convex_hull() +{ + append(m_first_layer_convex_hull.points, m_skirt_convex_hull); + if (m_first_layer_convex_hull.empty()) { + // Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer. + for (Polygon &poly : this->first_layer_islands()) + append(m_first_layer_convex_hull.points, std::move(poly.points)); + } + append(m_first_layer_convex_hull.points, this->first_layer_wipe_tower_corners()); + m_first_layer_convex_hull = Geometry::convex_hull(m_first_layer_convex_hull.points); +} + // Wipe tower support. bool Print::has_wipe_tower() const { @@ -1991,7 +2029,6 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_l return m_wipe_tower_data; } - void Print::_make_wipe_tower() { m_wipe_tower_data.clear(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5fb395fb1..41f8fb041 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -402,6 +402,12 @@ public: const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } + // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; } const PrintStatistics& print_statistics() const { return m_print_statistics; } @@ -437,6 +443,12 @@ private: void _make_skirt(); void _make_brim(); void _make_wipe_tower(); + void finalize_first_layer_convex_hull(); + + // Islands of objects and their supports extruded at the 1st layer. + Polygons first_layer_islands() const; + // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. + std::vector first_layer_wipe_tower_corners() const; // Declared here to have access to Model / ModelObject / ModelInstance static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); @@ -450,6 +462,13 @@ private: // Ordered collections of extrusion paths to build skirt loops and brim. ExtrusionEntityCollection m_skirt; ExtrusionEntityCollection m_brim; + // Convex hull of the 1st layer extrusions. + // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. + // It does NOT encompass user extrusions generated by custom G-code, + // therefore it does NOT encompass the initial purge line. + // It does NOT encompass MMU/MMU2 starting (wipe) areas. + Polygon m_first_layer_convex_hull; + Points m_skirt_convex_hull; // Following section will be consumed by the GCodeGenerator. ToolOrdering m_tool_ordering;