Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::CoolingBuffer Class Reference

#include <src/libslic3r/GCode/CoolingBuffer.hpp>

+ Collaboration diagram for Slic3r::CoolingBuffer:

Public Member Functions

 CoolingBuffer (GCode &gcodegen)
 
void reset (const Vec3d &position)
 
void set_current_extruder (unsigned int extruder_id)
 
std::string process_layer (std::string &&gcode, size_t layer_id, bool flush)
 
std::string process_layer (const std::string &gcode, size_t layer_id, bool flush)
 

Private Member Functions

CoolingBufferoperator= (const CoolingBuffer &)=delete
 
std::vector< PerExtruderAdjustmentsparse_layer_gcode (const std::string &gcode, std::vector< float > &current_pos) const
 
float calculate_layer_slowdown (std::vector< PerExtruderAdjustments > &per_extruder_adjustments)
 
std::string apply_layer_cooldown (const std::string &gcode, size_t layer_id, float layer_time, std::vector< PerExtruderAdjustments > &per_extruder_adjustments)
 

Private Attributes

std::string m_gcode
 
std::vector< char > m_axis
 
std::vector< float > m_current_pos
 
int m_fan_speed
 
std::vector< unsigned int > m_extruder_ids
 
unsigned int m_num_extruders { 0 }
 
const std::string m_toolchange_prefix
 
const PrintConfig & m_config
 
unsigned int m_current_extruder
 
bool m_cooling_logic_proportional = false
 

Detailed Description

Constructor & Destructor Documentation

◆ CoolingBuffer()

Slic3r::CoolingBuffer::CoolingBuffer ( GCode gcodegen)
22 : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0)
23{
24 this->reset(gcodegen.writer().get_position());
25
26 const std::vector<Extruder> &extruders = gcodegen.writer().extruders();
27 m_extruder_ids.reserve(extruders.size());
28 for (const Extruder &ex : extruders) {
29 m_num_extruders = std::max(ex.id() + 1, m_num_extruders);
30 m_extruder_ids.emplace_back(ex.id());
31 }
32}
const PrintConfig & m_config
Definition CoolingBuffer.hpp:56
std::vector< unsigned int > m_extruder_ids
Definition CoolingBuffer.hpp:50
const std::string m_toolchange_prefix
Definition CoolingBuffer.hpp:53
unsigned int m_current_extruder
Definition CoolingBuffer.hpp:57
void reset(const Vec3d &position)
Definition CoolingBuffer.cpp:34
unsigned int m_num_extruders
Definition CoolingBuffer.hpp:52

References Slic3r::GCodeWriter::extruders(), Slic3r::GCodeWriter::get_position(), m_extruder_ids, m_num_extruders, reset(), and Slic3r::GCode::writer().

+ Here is the call graph for this function:

Member Function Documentation

◆ apply_layer_cooldown()

std::string Slic3r::CoolingBuffer::apply_layer_cooldown ( const std::string &  gcode,
size_t  layer_id,
float  layer_time,
std::vector< PerExtruderAdjustments > &  per_extruder_adjustments 
)
private
734{
735 // First sort the adjustment lines by of multiple extruders by their position in the source G-code.
736 std::vector<const CoolingLine*> lines;
737 {
738 size_t n_lines = 0;
739 for (const PerExtruderAdjustments &adj : per_extruder_adjustments)
740 n_lines += adj.lines.size();
741 lines.reserve(n_lines);
742 for (const PerExtruderAdjustments &adj : per_extruder_adjustments)
743 for (const CoolingLine &line : adj.lines)
744 lines.emplace_back(&line);
745 std::sort(lines.begin(), lines.end(), [](const CoolingLine *ln1, const CoolingLine *ln2) { return ln1->line_start < ln2->line_start; } );
746 }
747 // Second generate the adjusted G-code.
748 std::string new_gcode;
749 new_gcode.reserve(gcode.size() * 2);
750 bool bridge_fan_control = false;
751 int bridge_fan_speed = 0;
752 auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &bridge_fan_control, &bridge_fan_speed]() {
753#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
754 int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
755 int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
756 std::pair<int, int> custom_fan_speed_limits{fan_speed_new, 100 };
757 int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers);
758 // Is the fan speed ramp enabled?
759 int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
760 if (disable_fan_first_layers <= 0 && full_fan_speed_layer > 0) {
761 // When ramping up fan speed from disable_fan_first_layers to full_fan_speed_layer, force disable_fan_first_layers above zero,
762 // so there will be a zero fan speed at least at the 1st layer.
763 disable_fan_first_layers = 1;
764 }
765 if (int(layer_id) >= disable_fan_first_layers) {
766 int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed);
767 float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time));
768 float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time));
769 if (EXTRUDER_CONFIG(cooling)) {
770 if (layer_time < slowdown_below_layer_time) {
771 // Layer time very short. Enable the fan to a full throttle.
772 fan_speed_new = max_fan_speed;
773 custom_fan_speed_limits.first = fan_speed_new;
774 } else if (layer_time < fan_below_layer_time) {
775 // Layer time quite short. Enable the fan proportionally according to the current layer time.
776 assert(layer_time >= slowdown_below_layer_time);
777 double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
778 fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5);
779 custom_fan_speed_limits.first = fan_speed_new;
780 }
781 }
782 bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed);
783 if (int(layer_id) >= disable_fan_first_layers && int(layer_id) + 1 < full_fan_speed_layer) {
784 // Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer.
785 float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers);
786 fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 255);
787 bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 255);
788 custom_fan_speed_limits.second = fan_speed_new;
789 }
790#undef EXTRUDER_CONFIG
791 bridge_fan_control = bridge_fan_speed > fan_speed_new;
792 } else { // fan disabled
793 bridge_fan_control = false;
794 bridge_fan_speed = 0;
795 fan_speed_new = 0;
796 custom_fan_speed_limits.second = 0;
797 }
798 if (fan_speed_new != m_fan_speed) {
799 m_fan_speed = fan_speed_new;
800 new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed);
801 }
802 custom_fan_speed_limits.first = std::min(custom_fan_speed_limits.first, custom_fan_speed_limits.second);
803 return custom_fan_speed_limits;
804 };
805
806 const char *pos = gcode.c_str();
807 int current_feedrate = 0;
808 std::pair<int,int> fan_speed_limits = change_extruder_set_fan();
809 for (const CoolingLine *line : lines) {
810 const char *line_start = gcode.c_str() + line->line_start;
811 const char *line_end = gcode.c_str() + line->line_end;
812 if (line_start > pos)
813 new_gcode.append(pos, line_start - pos);
814 if (line->type & CoolingLine::TYPE_SET_TOOL) {
815 unsigned int new_extruder = 0;
816 auto res = std::from_chars(line_start + m_toolchange_prefix.size(), line_end, new_extruder);
817 if (res.ec != std::errc::invalid_argument && new_extruder != m_current_extruder) {
818 m_current_extruder = new_extruder;
819 fan_speed_limits = change_extruder_set_fan();
820 }
821 new_gcode.append(line_start, line_end - line_start);
822 } else if (line->type & CoolingLine::TYPE_SET_FAN_SPEED) {
823 int new_speed = std::clamp(line->fan_speed, fan_speed_limits.first, fan_speed_limits.second);
824 if (m_fan_speed != new_speed) {
825 new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, new_speed);
826 m_fan_speed = new_speed;
827 }
828 } else if (line->type & CoolingLine::TYPE_RESET_FAN_SPEED){
829 fan_speed_limits = change_extruder_set_fan();
830 } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) {
831 if (bridge_fan_control)
832 new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed);
833 } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_END) {
834 if (bridge_fan_control)
835 new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed);
836 } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) {
837 // Just remove this comment.
839 // Find the start of a comment, or roll to the end of line.
840 const char *end = line_start;
841 for (; end < line_end && *end != ';'; ++ end);
842 // Find the 'F' word.
843 const char *fpos = strstr(line_start + 2, " F") + 2;
844 int new_feedrate = current_feedrate;
845 // Modify the F word of the current G-code line.
846 bool modify = false;
847 // Remove the F word from the current G-code line.
848 bool remove = false;
849 assert(fpos != nullptr);
850 if (line->slowdown)
851 new_feedrate = int(floor(60. * line->feedrate + 0.5));
852 else
853 //auto res =
854 std::from_chars(fpos, line_end, new_feedrate);
855 if (new_feedrate == current_feedrate) {
856 // No need to change the F value.
858 // Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment.
859 end = line_end;
860 else
861 // Remove the feedrate from the G0/G1 line. The G-code line may become empty!
862 remove = true;
863 } else if (line->slowdown) {
864 // The F value will be overwritten.
865 modify = true;
866 } else {
867 // The F value is different from current_feedrate, but not slowed down, thus the G-code line will not be modified.
868 // Emit the line without the comment.
869 new_gcode.append(line_start, end - line_start);
870 current_feedrate = new_feedrate;
871 }
872 if (modify || remove) {
873 if (modify) {
874 // Replace the feedrate.
875 new_gcode.append(line_start, fpos - line_start);
876 current_feedrate = new_feedrate;
877 char buf[64];
878 sprintf(buf, "%d", int(current_feedrate));
879 new_gcode += buf;
880 } else {
881 // Remove the feedrate word.
882 const char *f = fpos;
883 // Roll the pointer before the 'F' word.
884 for (f -= 2; f > line_start && (*f == ' ' || *f == '\t'); -- f);
885 // Append up to the F word, without the trailing whitespace.
886 new_gcode.append(line_start, f - line_start + 1);
887 }
888 // Skip the non-whitespaces of the F parameter up the comment or end of line.
889 for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++ fpos);
890 // Append the rest of the line without the comment.
891 if (remove && (fpos == end || *fpos == '\n') && (new_gcode == "G1" || boost::ends_with(new_gcode, "\nG1"))) {
892 // The G-code line only contained the F word, now it is empty. Remove it completely including the comments.
893 new_gcode.resize(new_gcode.size() - 2);
894 end = line_end;
895 } else {
896 // The G-code line may not be empty yet. Emit the rest of it.
897 new_gcode.append(fpos, end - fpos);
898 }
899 }
900 // Process the rest of the line.
901 if (end < line_end) {
903 // Process comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
904 std::string comment(end, line_end);
905 boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
907 boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
908 if (line->type & CoolingLine::TYPE_WIPE)
909 boost::replace_all(comment, ";_WIPE", "");
910 new_gcode += comment;
911 } else {
912 // Just attach the rest of the source line.
913 new_gcode.append(end, line_end - end);
914 }
915 }
916 } else {
917 new_gcode.append(line_start, line_end - line_start);
918 }
919 pos = line_end;
920 }
921 const char *gcode_end = gcode.c_str() + gcode.size();
922 if (pos < gcode_end)
923 new_gcode.append(pos, gcode_end - pos);
924
925 // There should be no empty G1 lines emitted.
926 assert(new_gcode.find("G1\n") == std::string::npos);
927 return new_gcode;
928}
EIGEN_DEVICE_FUNC const FloorReturnType floor() const
Definition ArrayCwiseUnaryOps.h:388
#define EXTRUDER_CONFIG(OPT)
Definition GCode.cpp:434
int m_fan_speed
Definition CoolingBuffer.hpp:47
static std::string set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed)
Definition GCodeWriter.cpp:503
#define const
Definition getopt.c:38
#define comment
Definition lexer.c:1004
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:14
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:12
constexpr auto size(const C &c) -> decltype(c.size())
Definition span.hpp:183
S::iterator end(S &sh, const PathTag &)
Definition geometry_traits.hpp:620
@ TYPE_ADJUSTABLE
Definition CoolingBuffer.cpp:53
@ TYPE_WIPE
Definition CoolingBuffer.cpp:57
@ TYPE_BRIDGE_FAN_START
Definition CoolingBuffer.cpp:49
@ TYPE_HAS_F
Definition CoolingBuffer.cpp:56
@ TYPE_BRIDGE_FAN_END
Definition CoolingBuffer.cpp:50
@ TYPE_SET_FAN_SPEED
Definition CoolingBuffer.cpp:64
@ TYPE_EXTRUDE_END
Definition CoolingBuffer.cpp:48
@ TYPE_EXTERNAL_PERIMETER
Definition CoolingBuffer.cpp:54
@ TYPE_ADJUSTABLE_EMPTY
Definition CoolingBuffer.cpp:62
@ TYPE_SET_TOOL
Definition CoolingBuffer.cpp:47
@ TYPE_RESET_FAN_SPEED
Definition CoolingBuffer.cpp:65

References comment, EXTRUDER_CONFIG, Slic3r::f(), floor(), m_config, m_current_extruder, m_fan_speed, m_toolchange_prefix, Slic3r::GCodeWriter::set_fan(), Slic3r::CoolingLine::TYPE_ADJUSTABLE, Slic3r::CoolingLine::TYPE_ADJUSTABLE_EMPTY, Slic3r::CoolingLine::TYPE_BRIDGE_FAN_END, Slic3r::CoolingLine::TYPE_BRIDGE_FAN_START, Slic3r::CoolingLine::TYPE_EXTERNAL_PERIMETER, Slic3r::CoolingLine::TYPE_EXTRUDE_END, Slic3r::CoolingLine::TYPE_HAS_F, Slic3r::CoolingLine::TYPE_RESET_FAN_SPEED, Slic3r::CoolingLine::TYPE_SET_FAN_SPEED, Slic3r::CoolingLine::TYPE_SET_TOOL, and Slic3r::CoolingLine::TYPE_WIPE.

Referenced by process_layer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculate_layer_slowdown()

float Slic3r::CoolingBuffer::calculate_layer_slowdown ( std::vector< PerExtruderAdjustments > &  per_extruder_adjustments)
private
665{
666 // Sort the extruders by an increasing slowdown_below_layer_time.
667 // The layers with a lower slowdown_below_layer_time are slowed down
668 // together with all the other layers with slowdown_below_layer_time above.
669 std::vector<PerExtruderAdjustments*> by_slowdown_time;
670 by_slowdown_time.reserve(per_extruder_adjustments.size());
671 // Only insert entries, which are adjustable (have cooling enabled and non-zero stretchable time).
672 // Collect total print time of non-adjustable extruders.
673 float elapsed_time_total0 = 0.f;
674 for (PerExtruderAdjustments &adj : per_extruder_adjustments) {
675 // Curren total time for this extruder.
676 adj.time_total = adj.elapsed_time_total();
677 // Maximum time for this extruder, when all extrusion moves are slowed down to min_extrusion_speed.
678 adj.time_maximum = adj.maximum_time_after_slowdown(true);
679 if (adj.cooling_slow_down_enabled && adj.lines.size() > 0) {
680 by_slowdown_time.emplace_back(&adj);
682 // sorts the lines, also sets adj.time_non_adjustable
683 adj.sort_lines_by_decreasing_feedrate();
684 } else
685 elapsed_time_total0 += adj.elapsed_time_total();
686 }
687 std::sort(by_slowdown_time.begin(), by_slowdown_time.end(),
688 [](const PerExtruderAdjustments *adj1, const PerExtruderAdjustments *adj2)
689 { return adj1->slowdown_below_layer_time < adj2->slowdown_below_layer_time; });
690
691 for (auto cur_begin = by_slowdown_time.begin(); cur_begin != by_slowdown_time.end(); ++ cur_begin) {
692 PerExtruderAdjustments &adj = *(*cur_begin);
693 // Calculate the current adjusted elapsed_time_total over the non-finalized extruders.
694 float total = elapsed_time_total0;
695 for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
696 total += (*it)->time_total;
697 float slowdown_below_layer_time = adj.slowdown_below_layer_time * 1.001f;
698 if (total > slowdown_below_layer_time) {
699 // The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything.
700 } else {
701 // Adjust this and all the following (higher m_config.slowdown_below_layer_time) extruders.
702 // Sum maximum slow down time as if everything was slowed down including the external perimeters.
703 float max_time = elapsed_time_total0;
704 for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
705 max_time += (*it)->time_maximum;
706 if (max_time > slowdown_below_layer_time) {
708 extruder_range_slow_down_proportional(cur_begin, by_slowdown_time.end(), elapsed_time_total0, total, slowdown_below_layer_time);
709 else
710 extruder_range_slow_down_non_proportional(cur_begin, by_slowdown_time.end(), slowdown_below_layer_time - total);
711 } else {
712 // Slow down to maximum possible.
713 for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
714 (*it)->slowdown_to_minimum_feedrate(true);
715 }
716 }
717 elapsed_time_total0 += adj.elapsed_time_total();
718 }
719
720 return elapsed_time_total0;
721}
bool m_cooling_logic_proportional
Definition CoolingBuffer.hpp:60
static float extruder_range_slow_down_proportional(std::vector< PerExtruderAdjustments * >::iterator it_begin, std::vector< PerExtruderAdjustments * >::iterator it_end, float elapsed_time_total0, float elapsed_time_before_slowdown, float slowdown_below_layer_time)
Definition CoolingBuffer.cpp:529
static void extruder_range_slow_down_non_proportional(std::vector< PerExtruderAdjustments * >::iterator it_begin, std::vector< PerExtruderAdjustments * >::iterator it_end, float time_stretch)
Definition CoolingBuffer.cpp:585

References Slic3r::PerExtruderAdjustments::elapsed_time_total(), Slic3r::extruder_range_slow_down_non_proportional(), Slic3r::extruder_range_slow_down_proportional(), m_cooling_logic_proportional, Slic3r::PerExtruderAdjustments::slowdown_below_layer_time, and Slic3r::PerExtruderAdjustments::time_total.

Referenced by process_layer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ operator=()

CoolingBuffer & Slic3r::CoolingBuffer::operator= ( const CoolingBuffer )
privatedelete

◆ parse_layer_gcode()

std::vector< PerExtruderAdjustments > Slic3r::CoolingBuffer::parse_layer_gcode ( const std::string &  gcode,
std::vector< float > &  current_pos 
) const
private
328{
329 std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size());
330 std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0);
331 for (size_t i = 0; i < m_extruder_ids.size(); ++ i) {
332 PerExtruderAdjustments &adj = per_extruder_adjustments[i];
333 unsigned int extruder_id = m_extruder_ids[i];
334 adj.extruder_id = extruder_id;
335 adj.cooling_slow_down_enabled = m_config.cooling.get_at(extruder_id);
336 adj.slowdown_below_layer_time = float(m_config.slowdown_below_layer_time.get_at(extruder_id));
337 adj.min_print_speed = float(m_config.min_print_speed.get_at(extruder_id));
338 map_extruder_to_per_extruder_adjustment[extruder_id] = i;
339 }
340
341 unsigned int current_extruder = m_current_extruder;
342 PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
343 const char *line_start = gcode.c_str();
344 const char *line_end = line_start;
346 // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
347 // for a sequence of extrusion moves.
348 size_t active_speed_modifier = size_t(-1);
349
350 std::vector<float> new_pos;
351 for (; *line_start != 0; line_start = line_end)
352 {
353 while (*line_end != '\n' && *line_end != 0)
354 ++ line_end;
355 // sline will not contain the trailing '\n'.
356 std::string_view sline(line_start, line_end - line_start);
357 // CoolingLine will contain the trailing '\n'.
358 if (*line_end == '\n')
359 ++ line_end;
360 CoolingLine line(0, line_start - gcode.c_str(), line_end - gcode.c_str());
361 if (boost::starts_with(sline, "G0 "))
362 line.type = CoolingLine::TYPE_G0;
363 else if (boost::starts_with(sline, "G1 "))
364 line.type = CoolingLine::TYPE_G1;
365 else if (boost::starts_with(sline, "G92 "))
366 line.type = CoolingLine::TYPE_G92;
367 if (line.type) {
368 // G0, G1 or G92
369 // Parse the G-code line.
370 new_pos = current_pos;
371 for (auto c = sline.begin() + 3;;) {
372 // Skip whitespaces.
373 for (; c != sline.end() && (*c == ' ' || *c == '\t'); ++ c);
374 if (c == sline.end() || *c == ';')
375 break;
376
377 // Parse the axis.
378 size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') :
379 (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
380 if (axis != size_t(-1)) {
381 //auto [pend, ec] =
382 fast_float::from_chars(&*(++ c), sline.data() + sline.size(), new_pos[axis]);
383 if (axis == 4) {
384 // Convert mm/min to mm/sec.
385 new_pos[4] /= 60.f;
386 if ((line.type & CoolingLine::TYPE_G92) == 0)
387 // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls.
388 line.type |= CoolingLine::TYPE_HAS_F;
389 }
390 }
391 // Skip this word.
392 for (; c != sline.end() && *c != ' ' && *c != '\t'; ++ c);
393 }
394 bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
395 bool wipe = boost::contains(sline, ";_WIPE");
396 if (external_perimeter)
398 if (wipe)
399 line.type |= CoolingLine::TYPE_WIPE;
400 if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
401 line.type |= CoolingLine::TYPE_ADJUSTABLE;
402 active_speed_modifier = adjustment->lines.size();
403 }
404 if ((line.type & CoolingLine::TYPE_G92) == 0) {
405 // G0 or G1. Calculate the duration.
406 if (m_config.use_relative_e_distances.value)
407 // Reset extruder accumulator.
408 current_pos[3] = 0.f;
409 float dif[4];
410 for (size_t i = 0; i < 4; ++ i)
411 dif[i] = new_pos[i] - current_pos[i];
412 float dxy2 = dif[0] * dif[0] + dif[1] * dif[1];
413 float dxyz2 = dxy2 + dif[2] * dif[2];
414 if (dxyz2 > 0.f) {
415 // Movement in xyz, calculate time from the xyz Euclidian distance.
416 line.length = sqrt(dxyz2);
417 } else if (std::abs(dif[3]) > 0.f) {
418 // Movement in the extruder axis.
419 line.length = std::abs(dif[3]);
420 }
421 line.feedrate = new_pos[4];
422 assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f);
423 if (line.length > 0) {
424 assert(line.feedrate > 0);
425 line.time = line.length / line.feedrate;
426 assert(line.time > 0);
427 }
428 line.time_max = line.time;
429 if ((line.type & CoolingLine::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1)) {
430 assert(adjustment->min_print_speed >= 0);
431 line.time_max = (adjustment->min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->min_print_speed);
432 }
433 if (active_speed_modifier < adjustment->lines.size() && (line.type & CoolingLine::TYPE_G1)) {
434 // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry.
435 assert((line.type & CoolingLine::TYPE_HAS_F) == 0);
436 CoolingLine &sm = adjustment->lines[active_speed_modifier];
437 assert(sm.feedrate > 0.f);
438 sm.length += line.length;
439 sm.time += line.time;
440 if (sm.time_max != FLT_MAX) {
441 if (line.time_max == FLT_MAX)
442 sm.time_max = FLT_MAX;
443 else
444 sm.time_max += line.time_max;
445 }
446 // Don't store this line.
447 line.type = 0;
448 }
449 }
450 current_pos = std::move(new_pos);
451 } else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
452 // Closing a block of non-zero length extrusion moves.
454 if (active_speed_modifier != size_t(-1)) {
455 assert(active_speed_modifier < adjustment->lines.size());
456 CoolingLine &sm = adjustment->lines[active_speed_modifier];
457 // There should be at least some extrusion move inside the adjustment block.
458 // However if the block has no extrusion (which is wrong), fix it for the cooling buffer to work.
459 assert(sm.length > 0);
460 assert(sm.time > 0);
461 if (sm.time <= 0) {
462 // Likely a zero length extrusion, it should not be emitted, however the zero extrusions should not confuse firmware either.
463 // Prohibit time adjustment of a block of zero length extrusions by the cooling buffer.
464 sm.type &= ~CoolingLine::TYPE_ADJUSTABLE;
465 // But the start / end comment shall be removed.
467 }
468 }
469 active_speed_modifier = size_t(-1);
470 } else if (boost::starts_with(sline, m_toolchange_prefix)) {
471 unsigned int new_extruder = 0;
472 auto res = std::from_chars(sline.data() + m_toolchange_prefix.size(), sline.data() + sline.size(), new_extruder);
473 if (res.ec != std::errc::invalid_argument) {
474 // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored.
475 if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) {
476 if (new_extruder != current_extruder) {
477 // Switch the tool.
478 line.type = CoolingLine::TYPE_SET_TOOL;
479 current_extruder = new_extruder;
480 adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
481 }
482 }
483 else {
484 // Only log the error in case of MM printer. Single extruder printers likely ignore any T anyway.
485 if (map_extruder_to_per_extruder_adjustment.size() > 1)
486 BOOST_LOG_TRIVIAL(error) << "CoolingBuffer encountered an invalid toolchange, maybe from a custom gcode: " << sline;
487 }
488 }
489 } else if (boost::starts_with(sline, ";_BRIDGE_FAN_START")) {
491 } else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) {
493 } else if (boost::starts_with(sline, "G4 ")) {
494 // Parse the wait time.
495 line.type = CoolingLine::TYPE_G4;
496 size_t pos_S = sline.find('S', 3);
497 size_t pos_P = sline.find('P', 3);
498 bool has_S = pos_S > 0;
499 bool has_P = pos_P > 0;
500 if (has_S || has_P) {
501 //auto [pend, ec] =
502 fast_float::from_chars(sline.data() + (has_S ? pos_S : pos_P) + 1, sline.data() + sline.size(), line.time);
503 if (has_P)
504 line.time *= 0.001f;
505 } else
506 line.time = 0;
507 line.time_max = line.time;
508 } else if (boost::contains(sline, ";_SET_FAN_SPEED")) {
509 auto speed_start = sline.find_last_of('D');
510 int speed = 0;
511 for (char num : sline.substr(speed_start + 1)) {
512 speed = speed * 10 + (num - '0');
513 }
515 line.fan_speed = speed;
516 } else if (boost::contains(sline, ";_RESET_FAN_SPEED")) {
518 }
519
520 if (line.type != 0)
521 adjustment->lines.emplace_back(std::move(line));
522 }
523
524 return per_extruder_adjustments;
525}
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
Definition ArrayCwiseUnaryOps.h:152
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half() max(const half &a, const half &b)
Definition Half.h:516
double length(const Points &pts)
Definition MultiPoint.hpp:116
extrusion_axis((ConfigOptionFloats, extrusion_multiplier))((ConfigOptionFloats
static template_custom_gcode std::string get_extrusion_axis(const GCodeConfig &cfg)
Definition PrintConfig.hpp:739
constexpr auto data(C &c) -> decltype(c.data())
Definition span.hpp:195
from_chars_result from_chars(const char *first, const char *last, T &value, chars_format fmt=chars_format::general) noexcept
Definition fast_float.h:2401
STL namespace.
@ TYPE_G4
Definition CoolingBuffer.cpp:58
@ TYPE_G92
Definition CoolingBuffer.cpp:59
@ TYPE_G0
Definition CoolingBuffer.cpp:51
@ TYPE_G1
Definition CoolingBuffer.cpp:52
static char error[256]
Definition tga.cpp:50

References Slic3r::PerExtruderAdjustments::cooling_slow_down_enabled, error, Slic3r::PerExtruderAdjustments::extruder_id, Slic3r::extrusion_axis(), Slic3r::CoolingLine::fan_speed, Slic3r::CoolingLine::feedrate, fast_float::from_chars(), Slic3r::get_extrusion_axis(), Slic3r::CoolingLine::length, Slic3r::PerExtruderAdjustments::lines, m_config, m_current_extruder, m_extruder_ids, m_num_extruders, m_toolchange_prefix, Slic3r::PerExtruderAdjustments::min_print_speed, Slic3r::PerExtruderAdjustments::slowdown_below_layer_time, sqrt(), Slic3r::CoolingLine::time, Slic3r::CoolingLine::time_max, Slic3r::CoolingLine::type, Slic3r::CoolingLine::TYPE_ADJUSTABLE, Slic3r::CoolingLine::TYPE_ADJUSTABLE_EMPTY, Slic3r::CoolingLine::TYPE_BRIDGE_FAN_END, Slic3r::CoolingLine::TYPE_BRIDGE_FAN_START, Slic3r::CoolingLine::TYPE_EXTERNAL_PERIMETER, Slic3r::CoolingLine::TYPE_EXTRUDE_END, Slic3r::CoolingLine::TYPE_G0, Slic3r::CoolingLine::TYPE_G1, Slic3r::CoolingLine::TYPE_G4, Slic3r::CoolingLine::TYPE_G92, Slic3r::CoolingLine::TYPE_HAS_F, Slic3r::CoolingLine::TYPE_RESET_FAN_SPEED, Slic3r::CoolingLine::TYPE_SET_FAN_SPEED, Slic3r::CoolingLine::TYPE_SET_TOOL, and Slic3r::CoolingLine::TYPE_WIPE.

Referenced by process_layer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ process_layer() [1/2]

std::string Slic3r::CoolingBuffer::process_layer ( const std::string &  gcode,
size_t  layer_id,
bool  flush 
)
inline
30 { return this->process_layer(std::string(gcode), layer_id, flush); }
std::string process_layer(std::string &&gcode, size_t layer_id, bool flush)
Definition CoolingBuffer.cpp:305

References process_layer().

+ Here is the call graph for this function:

◆ process_layer() [2/2]

std::string Slic3r::CoolingBuffer::process_layer ( std::string &&  gcode,
size_t  layer_id,
bool  flush 
)
306{
307 // Cache the input G-code.
308 if (m_gcode.empty())
309 m_gcode = std::move(gcode);
310 else
311 m_gcode += gcode;
312
313 std::string out;
314 if (flush) {
315 // This is either an object layer or the very last print layer. Calculate cool down over the collected support layers
316 // and one object layer.
317 std::vector<PerExtruderAdjustments> per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos);
318 float layer_time_stretched = this->calculate_layer_slowdown(per_extruder_adjustments);
319 out = this->apply_layer_cooldown(m_gcode, layer_id, layer_time_stretched, per_extruder_adjustments);
320 m_gcode.clear();
321 }
322 return out;
323}
float calculate_layer_slowdown(std::vector< PerExtruderAdjustments > &per_extruder_adjustments)
Definition CoolingBuffer.cpp:664
std::vector< float > m_current_pos
Definition CoolingBuffer.hpp:45
std::string m_gcode
Definition CoolingBuffer.hpp:41
std::vector< PerExtruderAdjustments > parse_layer_gcode(const std::string &gcode, std::vector< float > &current_pos) const
Definition CoolingBuffer.cpp:327
std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector< PerExtruderAdjustments > &per_extruder_adjustments)
Definition CoolingBuffer.cpp:725

References apply_layer_cooldown(), calculate_layer_slowdown(), m_current_pos, m_gcode, and parse_layer_gcode().

Referenced by process_layer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ reset()

void Slic3r::CoolingBuffer::reset ( const Vec3d position)
35{
36 m_current_pos.assign(5, 0.f);
37 m_current_pos[0] = float(position.x());
38 m_current_pos[1] = float(position.y());
39 m_current_pos[2] = float(position.z());
40 m_current_pos[4] = float(m_config.travel_speed.value);
41 m_fan_speed = -1;
42}

References m_config, m_current_pos, and m_fan_speed.

Referenced by CoolingBuffer().

+ Here is the caller graph for this function:

◆ set_current_extruder()

void Slic3r::CoolingBuffer::set_current_extruder ( unsigned int  extruder_id)
inline
27{ m_current_extruder = extruder_id; }

References m_current_extruder.

Member Data Documentation

◆ m_axis

std::vector<char> Slic3r::CoolingBuffer::m_axis
private

◆ m_config

const PrintConfig& Slic3r::CoolingBuffer::m_config
private

◆ m_cooling_logic_proportional

bool Slic3r::CoolingBuffer::m_cooling_logic_proportional = false
private

◆ m_current_extruder

unsigned int Slic3r::CoolingBuffer::m_current_extruder
private

◆ m_current_pos

std::vector<float> Slic3r::CoolingBuffer::m_current_pos
private

Referenced by process_layer(), and reset().

◆ m_extruder_ids

std::vector<unsigned int> Slic3r::CoolingBuffer::m_extruder_ids
private

Referenced by CoolingBuffer(), and parse_layer_gcode().

◆ m_fan_speed

int Slic3r::CoolingBuffer::m_fan_speed
private

Referenced by apply_layer_cooldown(), and reset().

◆ m_gcode

std::string Slic3r::CoolingBuffer::m_gcode
private

Referenced by process_layer().

◆ m_num_extruders

unsigned int Slic3r::CoolingBuffer::m_num_extruders { 0 }
private

Referenced by CoolingBuffer(), and parse_layer_gcode().

◆ m_toolchange_prefix

const std::string Slic3r::CoolingBuffer::m_toolchange_prefix
private

The documentation for this class was generated from the following files: