diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d5bc95e8bd9..174c9f0e831 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -219,22 +219,19 @@ std::string Wipe::wipe(GCode& gcodegen, bool toolchange) } //if first layer, ask for a bigger lift for travel to object, to be on the safe side -static inline void set_extra_lift(const float previous_print_z, const int layer_id, const Print& print, GCodeWriter & writer, int extruder_id) { +static inline void set_extra_lift(const float previous_print_z, const int layer_id, const PrintConfig& print_config, GCodeWriter & writer, int extruder_id) { //if first layer, ask for a bigger lift for travel to object, to be on the safe side double extra_lift_value = 0; - if (layer_id == 0 && - (print.config().retract_lift.get_at(extruder_id) != 0 || print.config().retract_lift_first_layer.get_at(extruder_id))) { - //get biggest first layer height and set extra lift for first travel, to be safe. - for (const PrintObject* obj : print.objects()) - extra_lift_value = std::max(extra_lift_value, print.get_object_first_layer_height(*obj)); - if(previous_print_z == 0) - extra_lift_value *= 3; - else - extra_lift_value *= 2; - } - if (print.config().lift_min.value > 0) { - if (previous_print_z + extra_lift_value < print.config().lift_min.value) { - extra_lift_value = print.config().lift_min.value - previous_print_z; + if (print_config.lift_min.value > 0) { + double retract_lift = 0; + //get the current lift (imo, should be given by the writer... i'm duplicating stuff here) + if(//(previous_print_z == 0 && print_config.retract_lift_above.get_at(writer.tool()->id()) == 0) || + print_config.retract_lift_above.get_at(writer.tool()->id()) <= previous_print_z + EPSILON + || (layer_id == 0 && print_config.retract_lift_first_layer.get_at(writer.tool()->id()))) + retract_lift = writer.tool()->retract_lift(); + // see if it's positive + if (previous_print_z + extra_lift_value + retract_lift < print_config.lift_min.value) { + extra_lift_value = print_config.lift_min.value - previous_print_z - retract_lift; } } if(extra_lift_value > 0) @@ -1408,6 +1405,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Write the custom start G-code _writeln(file, start_gcode); + m_last_pos_defined = false; + //flush FanMover buffer to avoid modifying the start gcode if it's manual. if (this->config().start_gcode_manual && this->m_fan_mover.get() != nullptr) { std::string to_write = this->m_fan_mover.get()->process_gcode("", true); @@ -1524,7 +1523,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer m_avoid_crossing_perimeters.use_external_mp_once(); - set_extra_lift(m_last_layer_z, 0, print, m_writer, initial_extruder_id); + set_extra_lift(m_last_layer_z, 0, print.config(), m_writer, initial_extruder_id); _write(file, this->retract()); std::string gcode; //go to origin of the next object (it's 0,0 because we shifted the origin to it) @@ -1544,7 +1543,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); _writeln(file, between_objects_gcode); } else { - set_extra_lift(0, 0, print, m_writer, initial_extruder_id); + set_extra_lift(0, 0, print.config(), m_writer, initial_extruder_id); } //reinit the seam placer on the new object m_seam_placer.init(print); @@ -1569,7 +1568,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu m_second_layer_things_done = false; prev_object = &object; } - set_extra_lift(m_last_layer_z, prev_object->layers().back()->id(), print, m_writer, initial_extruder_id /* osef, it's only for the lift_min */); + set_extra_lift(m_last_layer_z, prev_object->layers().back()->id(), print.config(), m_writer, initial_extruder_id /* osef, it's only for the lift_min */); } else { // Sort layers by Z. // All extrusion moves with the same top layer height are extruded uninterrupted. @@ -2278,8 +2277,19 @@ void GCode::process_layer( print.config().before_layer_gcode.value, m_writer.tool()->id(), &config) + "\n"; } - gcode += this->change_layer(print_z); // this will increase m_layer_index - m_layer = &layer; + // print z move to next layer UNLESS + // if it's going to the first layer, then we may want to dealy the move in these condition: + // there is no "after layer change gcode" and it's the first move from the unknown + if (print.config().layer_gcode.value.empty() && m_last_pos_defined && ( + // there is a lift (on the first llyer, so the first move will bring us to the required height + (m_writer.tool()->retract_lift() > 0 && (m_config.retract_lift_above.get_at(m_writer.tool()->id()) == 0 || BOOL_EXTRUDER_CONFIG(retract_lift_first_layer))) + || // or lift_min is higher than the first layer height. + m_config.lift_min.value > layer.print_z + )) + m_delayed_layer_change = this->change_layer(print_z); //HACK for superslicer#1775 + else + gcode += this->change_layer(print_z); // this will increase m_layer_index + m_layer = &layer; if (! print.config().layer_gcode.value.empty()) { DynamicConfig config; config.set_key_value("previous_layer_z", new ConfigOptionFloat(previous_print_z)); @@ -2494,9 +2504,11 @@ void GCode::process_layer( //if first layer, ask for a bigger lift for travel to object, to be on the safe side if(single_object_instance_idx == size_t(-1)) - set_extra_lift(m_last_layer_z, layer.id(), print, m_writer, extruder_id); + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { + // before goin to and from a global skirt, please ensure you are a a safe height + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); const std::pair loops = loops_it->second; this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); @@ -2520,21 +2532,25 @@ void GCode::process_layer( // 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(); + // before goin to and from a global skirt, please ensure you are a a safe height + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); } // Extrude brim with the extruder of the 1st region. if (! m_brim_done) { - //if first layer, ask for a bigger lift for travel to object, to be on the safe side - if (single_object_instance_idx == size_t(-1)) - set_extra_lift(m_last_layer_z, layer.id(), print, m_writer, extruder_id); - this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); - gcode += this->extrude_entity(print.brim(), "Brim", m_config.support_material_speed.value); + for (const ExtrusionEntity* brim_entity : print.brim().entities) { + //if first layer, ask for a bigger lift for travel to each brim, to be on the safe side + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); + gcode += this->extrude_entity(*brim_entity, "Brim", m_config.support_material_speed.value); + } m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. m_avoid_crossing_perimeters.disable_once(); + //to go to the object-only skirt or brim, or to the object (May be overriden here but I don't care) + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); } //extrude object-only skirt //TODO: use it also for wiping like the other one (as they are exlusiev) @@ -2607,7 +2623,7 @@ void GCode::process_layer( } //if first layer, ask for a bigger lift for travel to object, to be on the safe side if (single_object_instance_idx == size_t(-1)) - set_extra_lift(m_last_layer_z, layer.id(), print, m_writer, extruder_id); + set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); // When starting a new object, use the external motion planner for the first travel move. const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; std::pair this_object_copy(&instance_to_print.print_object, offset); @@ -3916,7 +3932,10 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string if (m_config.travel_acceleration.value > 0) travel_acceleration = m_config.travel_acceleration.get_abs_value(acceleration); } - + if (m_writer.get_max_acceleration() > 0) { + acceleration = std::min((double)m_writer.get_max_acceleration(), acceleration); + travel_acceleration = std::min((double)m_writer.get_max_acceleration(), travel_acceleration); + } if (travel_acceleration == acceleration) { m_writer.set_acceleration((uint32_t)floor(acceleration + 0.5)); // go to first point of extrusion path (stop at midpoint to let us set the decel speed) @@ -3926,10 +3945,13 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string } } else { // go to midpoint to let us set the decel speed) - if (!m_last_pos_defined || m_last_pos != path.first_point()) { + if ( !m_last_pos_defined || m_last_pos != path.first_point()) { Polyline poly_start = this->travel_to(gcode, path.first_point(), path.role()); coordf_t length = poly_start.length(); - if (length > SCALED_EPSILON) { + // if length is enough, it's not the hack for first move, and the travel accel is different than the normal accel + // then cut the travel in two to change the accel in-between + // TODO: compute the real point where it should be cut, considering an infinite max speed. + if (length > std::max(scale_d(EPSILON), scale_d(m_config.min_length)) && m_last_pos_defined && floor(travel_acceleration) != floor(acceleration)) { Polyline poly_end; coordf_t min_length = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.5)) * 20; if (poly_start.size() > 2 && length > min_length * 3) { @@ -3968,7 +3990,18 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string _add_object_change_labels(gcode); // compensate retraction - gcode += this->unretract(); + if (m_delayed_layer_change.empty()) { + gcode += m_writer.unlift();//this->unretract(); + } else { + //check if an unlift happens + std::string unlift = m_writer.unlift(); + if (unlift.empty()) { + unlift = m_delayed_layer_change; + m_delayed_layer_change.clear(); + } + gcode += unlift; + } + gcode += m_writer.unretract(); speed = _compute_speed_mm_per_sec(path, speed); double F = speed * 60; // convert mm/sec to mm/min @@ -4199,7 +4232,9 @@ std::string GCode::retract(bool toolchange) methods even if we performed wipe, since this will ensure the entire retraction length is honored in case wipe path was too short. */ gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); - bool need_lift = !m_writer.tool_is_extruder() || toolchange || (BOOL_EXTRUDER_CONFIG(retract_lift_first_layer) && m_config.print_retract_lift.value != 0 && this->m_layer_index == 0) || m_config.lift_min.value > this->m_writer.get_position().z(); + bool need_lift = !m_writer.tool_is_extruder() || toolchange + || (BOOL_EXTRUDER_CONFIG(retract_lift_first_layer) && m_config.print_retract_lift.value != 0 && this->m_layer_index == 0) + || this->m_writer.get_extra_lift() > 0; bool last_fill_extusion_role_top_infill = (this->m_last_extrusion_role == ExtrusionRole::erTopSolidInfill || this->m_last_extrusion_role == ExtrusionRole::erIroning); if(this->m_last_extrusion_role == ExtrusionRole::erGapFill) last_fill_extusion_role_top_infill = (this->m_last_notgapfill_extrusion_role == ExtrusionRole::erTopSolidInfill || this->m_last_notgapfill_extrusion_role == ExtrusionRole::erIroning); @@ -4212,13 +4247,7 @@ std::string GCode::retract(bool toolchange) need_lift = true; } if (need_lift) - if (m_writer.tool()->retract_length() > 0 - || m_config.use_firmware_retraction - || (!m_writer.tool_is_extruder() && m_writer.tool()->retract_lift() != 0) - || (BOOL_EXTRUDER_CONFIG(retract_lift_first_layer) && this->m_layer_index == 0) - || m_config.lift_min.value > this->m_writer.get_position().z() - ) - gcode += m_writer.lift(); + gcode += m_writer.lift(this->m_layer_index); return gcode; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index a5bfb881e32..27b016b4ee7 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -349,6 +349,8 @@ class GCode : ExtrusionVisitorConst { // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; + // HACK to avoid multiple Z move. + std::string m_delayed_layer_change; // Keeps track of the last extrusion role passed to the processor ExtrusionRole m_last_processor_extrusion_role; // How many times will change_layer() be called? diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index d0d553605ea..bce90d4a6f6 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -510,14 +510,14 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment) /* If target Z is lower than current Z but higher than nominal Z we don't perform the move but we only adjust the nominal Z by reducing the lift amount that will be used for unlift. */ - if (!this->will_move_z(z)) { + // note that if we move but it's lower and we are lifted, we can wait a bit for unlifting, to avoid possible dance on layer change. + if (!this->will_move_z(z) || z < m_pos.z() && m_lifted > EPSILON) { double nominal_z = m_pos.z() - m_lifted; m_lifted -= (z - nominal_z); if (std::abs(m_lifted) < EPSILON) m_lifted = 0.; return ""; } - /* In all the other cases, we perform an actual Z move and cancel the lift. */ m_lifted = 0; @@ -548,7 +548,7 @@ bool GCodeWriter::will_move_z(double z) const we don't perform an actual Z move. */ if (m_lifted > 0) { double nominal_z = m_pos.z() - m_lifted; - if (z >= nominal_z && z <= m_pos.z()) + if (z >= nominal_z + EPSILON && z <= m_pos.z() - EPSILON) return false; } return true; @@ -698,15 +698,19 @@ std::string GCodeWriter::unretract() /* If this method is called more than once before calling unlift(), it will not perform subsequent lifts, even if Z was raised manually (i.e. with travel_to_z()) and thus _lifted was reduced. */ -std::string GCodeWriter::lift() +std::string GCodeWriter::lift(int layer_id) { // check whether the above/below conditions are met double target_lift = 0; if(this->tool_is_extruder()){ - //these two should be in the Tool class methods.... - double above = this->config.retract_lift_above.get_at(m_tool->id()); - double below = this->config.retract_lift_below.get_at(m_tool->id()); - if (m_pos.z() >= above && (below == 0 || m_pos.z() <= below)) + bool can_lift = this->config.retract_lift_first_layer.get_at(m_tool->id()) && layer_id == 0; + if (!can_lift) { + //these two should be in the Tool class methods.... + double above = this->config.retract_lift_above.get_at(m_tool->id()); + double below = this->config.retract_lift_below.get_at(m_tool->id()); + can_lift = (m_pos.z() >= above - EPSILON && (below == 0 || m_pos.z() <= below + EPSILON)); + } + if(can_lift) target_lift = m_tool->retract_lift(); } else { target_lift = m_tool->retract_lift(); @@ -717,15 +721,16 @@ std::string GCodeWriter::lift() target_lift = config_region->print_retract_lift.value; } - if (this->extra_lift > 0) { - target_lift += this->extra_lift; - this->extra_lift = 0; + // one-time extra lift (often for dangerous travels) + if (this->m_extra_lift > 0) { + target_lift += this->m_extra_lift; + this->m_extra_lift = 0; } // compare against epsilon because travel_to_z() does math on it // and subtracting layer_height from retract_lift might not give // exactly zero - if (std::abs(m_lifted) - target_lift < EPSILON && target_lift > 0) { + if (std::abs(m_lifted) < target_lift - EPSILON && target_lift > 0) { std::string str = this->_travel_to_z(m_pos.z() + target_lift - m_lifted, "lift Z"); m_lifted = target_lift; return str; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 72264aac3eb..71def452d03 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -51,6 +51,7 @@ class GCodeWriter { std::string set_fan(uint8_t speed, bool dont_save = false, uint16_t default_tool = 0); void set_acceleration(uint32_t acceleration); uint32_t get_acceleration() const; + uint32_t get_max_acceleration() { return this->m_max_acceleration; } std::string write_acceleration(); std::string reset_e(bool force = false); std::string update_progress(uint32_t num, uint32_t tot, bool allow_100 = false) const; @@ -73,11 +74,12 @@ class GCodeWriter { std::string retract(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false); std::string unretract(); - std::string lift(); + void set_extra_lift(double extra_zlift) { this->m_extra_lift = extra_zlift; } + double get_extra_lift() { return this->m_extra_lift; } + std::string lift(int layer_id); std::string unlift(); Vec3d get_position() const { return m_pos; } - void set_extra_lift(double extra_zlift) { this->extra_lift = extra_zlift; } private: // Extruders are sorted by their ID, so that binary search is possible. std::vector m_extruders; @@ -96,14 +98,15 @@ class GCodeWriter { int16_t m_last_temperature_with_offset; int16_t m_last_bed_temperature; bool m_last_bed_temperature_reached; + // if positive, it's set, and the next lift wil have this extra lift + double m_extra_lift = 0; + // current lift, to remove from m_pos to have the current height. double m_lifted; Vec3d m_pos = Vec3d::Zero(); std::string _travel_to_z(double z, const std::string &comment); std::string _retract(double length, double restart_extra, double restart_extra_toolchange, const std::string &comment); - // if positive, it's set, and the next lift wil have this extra lift - double extra_lift = 0; }; } /* namespace Slic3r */ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9db94604336..0e3375df3f3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2965,7 +2965,7 @@ void TabPrinter::toggle_options() // retract lift above / below only applies if using retract lift vec.resize(0); - vec = { "retract_lift_above", "retract_lift_below", "retract_lift_top" }; + vec = { "retract_lift_above", "retract_lift_below", "retract_lift_top", "retract_lift_first_layer" }; for (auto el : vec) { field = get_field(el, i); if (field)