diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index f21a0948c2..917ea14c84 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -104,7 +104,7 @@ void update_selected_items_inflation(ArrangePolygons& selected, const DynamicPri // set obj distance for auto seq_print if (params.is_seq_print) { if (params.all_objects_are_short) - params.min_obj_distance = std::max(params.min_obj_distance, scaled(double(MAX_OUTER_NOZZLE_DIAMETER)/2+0.001)); + 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.clearance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error } diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 9330c4049b..8781477bcb 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -3,6 +3,7 @@ #include "ExPolygon.hpp" #include "PrintConfig.hpp" +#include "Print.hpp" #define BED_SHRINK_SEQ_PRINT 5 @@ -132,6 +133,7 @@ struct ArrangeParams { float clearance_height_to_rod = 0; float clearance_height_to_lid = 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; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4e01dd7bd6..7b3b661e8e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -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> &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> &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> 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 &skirt_done) @@ -3386,8 +3387,8 @@ namespace Skirt { std::map> 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> 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 &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> 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(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); @@ -3479,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 index of skirt loops to be extruded with that extruder. + std::map> 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 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(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. @@ -3730,18 +3783,10 @@ LayerResult GCode::process_layer( m_second_layer_things_done = true; } - // Map from extruder ID to index of skirt loops to be extruded with that extruder. - std::map> 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&extruders) { @@ -4008,33 +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 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(print.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); - } - 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 == stCommon && !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()) @@ -4069,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 == stObject && + 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; @@ -4078,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); } } @@ -4092,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 == stObject && + !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. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 256853c48c..843b4a39da 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -308,6 +308,13 @@ private: static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> 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. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 947c0c3288..6b27bb3205 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -774,7 +774,7 @@ static std::vector 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_start_angle", "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", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d689315df1..53ff2b3c8c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -219,7 +219,8 @@ 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" @@ -507,7 +508,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 +571,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print } return -1; }; + + auto [object_skirt_offset, _] = print.object_skirt_offset(); std::vector print_instance_with_bounding_box; { // sequential_print_horizontal_clearance_valid @@ -580,7 +583,7 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print // 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 = print.is_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()); @@ -815,7 +818,7 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print auto inst = print_instance_with_bounding_box[k].print_instance; // 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多 // 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)); + 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(inst->model_instance))->arrange_order = k+1; @@ -2287,48 +2290,52 @@ void Print::_make_skirt() // 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 extruded_length(extruders.size(), 0.); - 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(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 == stCommon) { + 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(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(); @@ -2337,34 +2344,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 double object_skirt_distance = scale_(1.0); - for (auto obj_cvx_hull : object_convex_hulls) { - PrintObject* object = obj_cvx_hull.first; - object->m_skirt.clear(); - for (int i = 0; i < 1; 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 == stObject) { + // 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(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(); } } } @@ -2900,6 +2929,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 Print::object_skirt_offset(double margin_height) const +{ + if (config().skirt_loops == 0 || config().skirt_type != stObject) + 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; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 43aac7d87d..15c783100b 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -981,6 +981,8 @@ public: return std::all_of(this->objects().begin(), this->objects().end(), [&](PrintObject* obj) { return obj->height() < scale_(this->config().nozzle_height.value); }); } + std::tuple object_skirt_offset(double margin_height = 0) const; + protected: // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 63b20a6ff8..cc797427f9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -317,6 +317,12 @@ 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 = { + { "common", stCommon }, + { "object", stObject } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SkirtType) + static const t_config_enum_values s_keys_map_DraftShield = { { "disabled", dsDisabled }, { "enabled", dsEnabled } @@ -4025,6 +4031,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(dsDisabled)); + def = this->add("skirt_type", coEnum); + def->label = L("Skirt type"); + def->full_label = L("Skirt type"); + def->tooltip = L("Common - single skirt for all objects, Object - individual per object skirt."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("common"); + def->enum_values.push_back("object"); + def->enum_labels.push_back(L("Common")); + def->enum_labels.push_back(L("Object")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(stCommon)); + def = this->add("skirt_loops", coInt); def->label = L("Skirt loops"); def->full_label = L("Skirt loops"); @@ -4047,7 +4065,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; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d601c13f47..683f2d74db 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -224,6 +224,10 @@ enum TimelapseType : int { tlSmooth }; +enum SkirtType { + stCommon, stObject +}; + enum DraftShield { dsDisabled, dsEnabled }; @@ -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)) @@ -1220,9 +1226,9 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloats, retraction_minimum_travel)) ((ConfigOptionBools, retract_when_changing_layer)) ((ConfigOptionFloat, skirt_distance)) - ((ConfigOptionFloat, skirt_start_angle)) ((ConfigOptionInt, skirt_height)) ((ConfigOptionInt, skirt_loops)) + ((ConfigOptionEnum, skirt_type)) ((ConfigOptionFloat, skirt_speed)) ((ConfigOptionFloat, min_skirt_length)) ((ConfigOptionFloats, slow_down_layer_time)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index c79810f34a..9100935eac 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -449,17 +449,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } } - if (config->opt_enum("print_sequence") == PrintSequence::ByObject && config->opt_int("skirt_height") > 1 && config->opt_int("skirt_loops") > 0) { - const wxString msg_text = _(L("While printing by Object, the extruder may collide skirt.\nThus, reset the skirt layer to 1 to avoid that.")); - MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK); - DynamicPrintConfig new_conf = *config; - is_msg_dlg_already_exist = true; - dialog.ShowModal(); - new_conf.set_key_value("skirt_height", new ConfigOptionInt(1)); - apply(config, &new_conf); - is_msg_dlg_already_exist = false; - } - if (config->opt_enum("seam_slope_type") != SeamScarfType::None && config->get_abs_value("seam_slope_start_height") >= layer_height) { const wxString msg_text = _(L("seam_slope_start_height need to be smaller than layer_height.\nReset to 0.")); @@ -569,7 +558,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool have_skirt = config->opt_int("skirt_loops") > 0; toggle_field("skirt_height", have_skirt && config->opt_enum("draft_shield") != dsEnabled); - for (auto el : { "skirt_distance", "skirt_start_angle", "draft_shield"}) + for (auto el : {"skirt_type", "skirt_distance", "skirt_start_angle", "draft_shield"}) toggle_field(el, have_skirt); bool have_brim = (config->opt_enum("brim_type") != btNoBrim); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7921b6e2df..2d4048cef9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5228,7 +5228,7 @@ void GLCanvas3D::update_sequential_clearance() if (fff_print()->is_all_objects_are_short()) shrink_factor = scale_(std::max(0.5f * MAX_OUTER_NOZZLE_DIAMETER, object_skirt_offset) - 0.1); else - shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - 0.1)); + shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value + object_skirt_offset - 0.1)); double mitter_limit = scale_(0.1); m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 86ef530d25..90657aada4 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -766,18 +766,21 @@ arrangement::ArrangeParams init_arrange_params(Plater *p) auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print(); const PrintConfig &print_config = print.config(); + auto [object_skirt_offset, object_skirt_witdh] = print.object_skirt_offset(); + params.clearance_height_to_rod = print_config.extruder_clearance_height_to_rod.value; params.clearance_height_to_lid = print_config.extruder_clearance_height_to_lid.value; - params.clearance_radius = print_config.extruder_clearance_radius.value; + params.clearance_radius = print_config.extruder_clearance_radius.value + object_skirt_offset * 2; + params.object_skirt_offset = object_skirt_offset; params.printable_height = print_config.printable_height.value; params.allow_rotations = settings.enable_rotation; - params.nozzle_height = print.config().nozzle_height.value; + params.nozzle_height = print_config.nozzle_height.value; params.all_objects_are_short = print.is_all_objects_are_short(); params.align_center = print_config.best_object_pos.value; params.allow_multi_materials_on_same_plate = settings.allow_multi_materials_on_same_plate; params.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region; params.is_seq_print = settings.is_seq_print; - params.min_obj_distance = scaled(settings.distance); + params.min_obj_distance = settings.distance; params.align_to_y_axis = settings.align_to_y_axis; int state = p->get_prepare_state(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4d2ebb95fa..cc294ddda9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2770,7 +2770,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ "printable_area", "bed_exclude_area", "bed_custom_texture", "bed_custom_model", "print_sequence", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", - "nozzle_height", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", + "nozzle_height", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "brim_width", "brim_object_gap", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "preferred_orientation", "enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_volume", "extruder_colour", "filament_colour", "material_colour", "printable_height", "printer_model", "printer_technology", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bb23a435a2..86ee6f1f11 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2318,6 +2318,7 @@ void TabPrint::build() page = add_options_page(L("Others"), "custom-gcode_other"); // ORCA: icon only visible on placeholders optgroup = page->new_optgroup(L("Skirt"), L"param_skirt"); + optgroup->append_single_option_line("skirt_type"); optgroup->append_single_option_line("skirt_loops"); optgroup->append_single_option_line("min_skirt_length"); optgroup->append_single_option_line("skirt_distance");