diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index efea240e5..7e1afb5a8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1219,7 +1219,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Disable fan. if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) - file.write(m_writer.set_fan(0, true)); + file.write(m_writer.set_fan(0)); // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); @@ -1334,7 +1334,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.writeln(between_objects_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). - m_cooling_buffer->reset(); + m_cooling_buffer->reset(this->writer().get_position()); m_cooling_buffer->set_current_extruder(initial_extruder_id); // Pair the object layers with the support layers by z, extrude them. std::vector layers_to_print = collect_layers_to_print(object); @@ -1420,7 +1420,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Write end commands to file. file.write(this->retract()); - file.write(m_writer.set_fan(false)); + file.write(m_writer.set_fan(0)); // adds tag for processor file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 17f3e0b73..9ca85c728 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -16,19 +16,25 @@ namespace Slic3r { -CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_extruder(0) +CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) { - this->reset(); + this->reset(gcodegen.writer().get_position()); + + const std::vector &extruders = gcodegen.writer().extruders(); + m_extruder_ids.reserve(extruders.size()); + for (const Extruder &ex : extruders) { + m_num_extruders = std::max(ex.id() + 1, m_num_extruders); + m_extruder_ids.emplace_back(ex.id()); + } } -void CoolingBuffer::reset() +void CoolingBuffer::reset(const Vec3d &position) { m_current_pos.assign(5, 0.f); - Vec3d pos = m_gcodegen.writer().get_position(); - m_current_pos[0] = float(pos(0)); - m_current_pos[1] = float(pos(1)); - m_current_pos[2] = float(pos(2)); - m_current_pos[4] = float(m_gcodegen.config().travel_speed.value); + m_current_pos[0] = float(position.x()); + m_current_pos[1] = float(position.y()); + m_current_pos[2] = float(position.z()); + m_current_pos[4] = float(m_config.travel_speed.value); } struct CoolingLine @@ -303,30 +309,23 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b // Return the list of parsed lines, bucketed by an extruder. std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const { - const FullPrintConfig &config = m_gcodegen.config(); - const std::vector &extruders = m_gcodegen.writer().extruders(); - unsigned int num_extruders = 0; - for (const Extruder &ex : extruders) - num_extruders = std::max(ex.id() + 1, num_extruders); - - std::vector per_extruder_adjustments(extruders.size()); - std::vector map_extruder_to_per_extruder_adjustment(num_extruders, 0); - for (size_t i = 0; i < extruders.size(); ++ i) { + std::vector per_extruder_adjustments(m_extruder_ids.size()); + std::vector map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); + for (size_t i = 0; i < m_extruder_ids.size(); ++ i) { PerExtruderAdjustments &adj = per_extruder_adjustments[i]; - unsigned int extruder_id = extruders[i].id(); + unsigned int extruder_id = m_extruder_ids[i]; adj.extruder_id = extruder_id; - adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); - adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id)); - adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id)); + adj.cooling_slow_down_enabled = m_config.cooling.get_at(extruder_id); + adj.slowdown_below_layer_time = float(m_config.slowdown_below_layer_time.get_at(extruder_id)); + adj.min_print_speed = float(m_config.min_print_speed.get_at(extruder_id)); map_extruder_to_per_extruder_adjustment[extruder_id] = i; } - const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix(); unsigned int current_extruder = m_current_extruder; PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; const char *line_start = gcode.c_str(); const char *line_end = line_start; - const char extrusion_axis = get_extrusion_axis(config)[0]; + const char extrusion_axis = get_extrusion_axis(m_config)[0]; // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); @@ -387,7 +386,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } if ((line.type & CoolingLine::TYPE_G92) == 0) { // G0 or G1. Calculate the duration. - if (config.use_relative_e_distances.value) + if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. current_pos[3] = 0.f; float dif[4]; @@ -430,8 +429,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { line.type = CoolingLine::TYPE_EXTRUDE_END; active_speed_modifier = size_t(-1); - } else if (boost::starts_with(sline, toolchange_prefix)) { - unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size()); + } else if (boost::starts_with(sline, m_toolchange_prefix)) { + unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + m_toolchange_prefix.size()); // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) { if (new_extruder != current_extruder) { @@ -641,7 +640,7 @@ float CoolingBuffer::calculate_layer_slowdown(std::vector slowdown_below_layer_time) { // The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything. } else { - // Adjust this and all the following (higher config.slowdown_below_layer_time) extruders. + // Adjust this and all the following (higher m_config.slowdown_below_layer_time) extruders. // Sum maximum slow down time as if everything was slowed down including the external perimeters. float max_time = elapsed_time_total0; for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it) @@ -694,8 +693,7 @@ std::string CoolingBuffer::apply_layer_cooldown( bool bridge_fan_control = false; int bridge_fan_speed = 0; auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() { - const FullPrintConfig &config = m_gcodegen.config(); -#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder) +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); @@ -737,13 +735,12 @@ std::string CoolingBuffer::apply_layer_cooldown( } if (fan_speed_new != fan_speed) { fan_speed = fan_speed_new; - new_gcode += m_gcodegen.writer().set_fan(fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed); } }; const char *pos = gcode.c_str(); int current_feedrate = 0; - const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix(); change_extruder_set_fan(); for (const CoolingLine *line : lines) { const char *line_start = gcode.c_str() + line->line_start; @@ -751,7 +748,7 @@ std::string CoolingBuffer::apply_layer_cooldown( if (line_start > pos) new_gcode.append(pos, line_start - pos); if (line->type & CoolingLine::TYPE_SET_TOOL) { - unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size()); + unsigned int new_extruder = (unsigned int)atoi(line_start + m_toolchange_prefix.size()); if (new_extruder != m_current_extruder) { m_current_extruder = new_extruder; change_extruder_set_fan(); @@ -759,10 +756,10 @@ std::string CoolingBuffer::apply_layer_cooldown( new_gcode.append(line_start, line_end - line_start); } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) { if (bridge_fan_control) - new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed); } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_END) { if (bridge_fan_control) - new_gcode += m_gcodegen.writer().set_fan(fan_speed, true); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, fan_speed); } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) { // Just remove this comment. } else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) { diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 0932d62d3..5f49ef455 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -23,10 +23,9 @@ struct PerExtruderAdjustments; class CoolingBuffer { public: CoolingBuffer(GCode &gcodegen); - void reset(); + void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); - GCode* gcodegen() { return &m_gcodegen; } private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; @@ -36,17 +35,25 @@ private: // Returns the adjusted G-code. std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector &per_extruder_adjustments); - GCode& m_gcodegen; // G-code snippet cached for the support layers preceding an object layer. - std::string m_gcode; + std::string m_gcode; // Internal data. // X,Y,Z,E,F - std::vector m_axis; - std::vector m_current_pos; - unsigned int m_current_extruder; + std::vector m_axis; + std::vector m_current_pos; + // Cached from GCodeWriter. + // Printing extruder IDs, zero based. + std::vector m_extruder_ids; + // Highest of m_extruder_ids plus 1. + unsigned int m_num_extruders { 0 }; + const std::string m_toolchange_prefix; + // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, + // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. + const PrintConfig &m_config; + unsigned int m_current_extruder; // Old logic: proportional. - bool m_cooling_logic_proportional = false; + bool m_cooling_logic_proportional = false; }; } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index c97180982..793a66675 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -156,41 +156,6 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait return gcode.str(); } -std::string GCodeWriter::set_fan(unsigned int speed, bool dont_save) -{ - std::ostringstream gcode; - if (m_last_fan_speed != speed || dont_save) { - if (!dont_save) m_last_fan_speed = speed; - - if (speed == 0) { - if (FLAVOR_IS(gcfTeacup)) { - gcode << "M106 S0"; - } else if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - gcode << "M127"; - } else { - gcode << "M107"; - } - if (this->config.gcode_comments) gcode << " ; disable fan"; - gcode << "\n"; - } else { - if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - gcode << "M126"; - } else { - gcode << "M106 "; - if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) { - gcode << "P"; - } else { - gcode << "S"; - } - gcode << (255.0 * speed / 100.0); - } - if (this->config.gcode_comments) gcode << " ; enable fan"; - gcode << "\n"; - } - } - return gcode.str(); -} - std::string GCodeWriter::set_acceleration(unsigned int acceleration) { // Clamp the acceleration to the allowed maximum. @@ -611,4 +576,43 @@ std::string GCodeWriter::unlift() return gcode; } +std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed) +{ + std::ostringstream gcode; + if (speed == 0) { + switch (gcode_flavor) { + case gcfTeacup: + gcode << "M106 S0"; break; + case gcfMakerWare: + case gcfSailfish: + gcode << "M127"; break; + default: + gcode << "M107"; break; + } + if (gcode_comments) + gcode << " ; disable fan"; + gcode << "\n"; + } else { + switch (gcode_flavor) { + case gcfMakerWare: + case gcfSailfish: + gcode << "M126"; break; + case gcfMach3: + case gcfMachinekit: + gcode << "M106 P" << 255.0 * speed / 100.0; break; + default: + gcode << "M106 S" << 255.0 * speed / 100.0; break; + } + if (gcode_comments) + gcode << " ; enable fan"; + gcode << "\n"; + } + return gcode.str(); +} + +std::string GCodeWriter::set_fan(unsigned int speed) const +{ + return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed); +} + } diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 2de95ecc5..dd602ca80 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -18,7 +18,7 @@ public: GCodeWriter() : multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr), m_single_extruder_multi_material(false), - m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0), + m_last_acceleration(0), m_max_acceleration(0), m_last_bed_temperature(0), m_last_bed_temperature_reached(true), m_lifted(0) {} @@ -42,7 +42,6 @@ public: std::string postamble() const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; std::string set_bed_temperature(unsigned int temperature, bool wait = false); - std::string set_fan(unsigned int speed, bool dont_save = false); std::string set_acceleration(unsigned int acceleration); std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; @@ -69,6 +68,12 @@ public: std::string unlift(); Vec3d get_position() const { return m_pos; } + // To be called by the CoolingBuffer from another thread. + static std::string set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed); + // To be called by the main thread. It always emits the G-code, it does not remember the previous state. + // Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread. + std::string set_fan(unsigned int speed) const; + private: // Extruders are sorted by their ID, so that binary search is possible. std::vector m_extruders; @@ -79,7 +84,6 @@ private: // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware. // If set to zero, the limit is not in action. unsigned int m_max_acceleration; - unsigned int m_last_fan_speed; unsigned int m_last_bed_temperature; bool m_last_bed_temperature_reached; double m_lifted; diff --git a/t/cooling.t b/t/cooling.t index 2f444cf9d..a7720fd07 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -132,7 +132,7 @@ $config->set('disable_fan_first_layers', [ 0 ]); 'fan_below_layer_time' => [ $print_time2 + 1, $print_time2 + 1 ], 'slowdown_below_layer_time' => [ $print_time2 + 2, $print_time2 + 2 ] }); - $buffer->gcodegen->set_extruders([ 0, 1 ]); + $gcodegen->set_extruders([ 0, 1 ]); my $gcode = $buffer->process_layer($gcode1 . "T1\nG1 X0 E1 F3000\n", 0); like $gcode, qr/^M106/, 'fan is activated for the 1st tool'; like $gcode, qr/.*M107/, 'fan is disabled for the 2nd tool'; diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 78a5dde37..4c2583894 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -10,7 +10,6 @@ CoolingBuffer(GCode* gcode) %code{% RETVAL = new CoolingBuffer(*gcode); %}; ~CoolingBuffer(); - Ref gcodegen(); std::string process_layer(std::string gcode, size_t layer_id) %code{% RETVAL = THIS->process_layer(std::move(gcode), layer_id, true); %};