2017-05-03 16:28:22 +00:00
|
|
|
#include "../GCode.hpp"
|
2016-12-21 22:09:58 +00:00
|
|
|
#include "CoolingBuffer.hpp"
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
#include <iostream>
|
2017-06-30 15:14:13 +00:00
|
|
|
#include <float.h>
|
2016-12-21 22:09:58 +00:00
|
|
|
|
2017-06-30 15:05:58 +00:00
|
|
|
#if 0
|
|
|
|
#define DEBUG
|
|
|
|
#define _DEBUG
|
|
|
|
#undef NDEBUG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
2016-12-21 22:09:58 +00:00
|
|
|
namespace Slic3r {
|
|
|
|
|
2017-06-30 15:05:58 +00:00
|
|
|
CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_extruder(0)
|
2016-12-21 22:09:58 +00:00
|
|
|
{
|
2017-06-30 15:05:58 +00:00
|
|
|
this->reset();
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 15:05:58 +00:00
|
|
|
void CoolingBuffer::reset()
|
2016-12-21 22:09:58 +00:00
|
|
|
{
|
2017-06-30 15:05:58 +00:00
|
|
|
m_current_pos.assign(5, 0.f);
|
|
|
|
Pointf3 pos = m_gcodegen.writer().get_position();
|
|
|
|
m_current_pos[0] = float(pos.x);
|
|
|
|
m_current_pos[1] = float(pos.y);
|
|
|
|
m_current_pos[2] = float(pos.z);
|
|
|
|
m_current_pos[4] = float(m_gcodegen.config().travel_speed.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
|
|
|
|
|
|
|
|
std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_id)
|
|
|
|
{
|
2017-06-30 18:01:32 +00:00
|
|
|
const FullPrintConfig &config = m_gcodegen.config();
|
|
|
|
const std::vector<Extruder> &extruders = m_gcodegen.writer().extruders();
|
|
|
|
const size_t num_extruders = extruders.size();
|
2017-06-30 15:05:58 +00:00
|
|
|
|
|
|
|
// Calculate the required per extruder time stretches.
|
|
|
|
struct Adjustment {
|
2017-06-30 18:01:32 +00:00
|
|
|
Adjustment(unsigned int extruder_id = 0) : extruder_id(extruder_id) {}
|
2017-06-30 15:05:58 +00:00
|
|
|
// Calculate the total elapsed time per this extruder, adjusted for the slowdown.
|
|
|
|
float elapsed_time_total() {
|
|
|
|
float time_total = 0.f;
|
|
|
|
for (const Line &line : lines)
|
|
|
|
time_total += line.time;
|
|
|
|
return time_total;
|
|
|
|
}
|
|
|
|
// Calculate the maximum time when slowing down.
|
|
|
|
float maximum_time(bool slowdown_external_perimeters) {
|
|
|
|
float time_total = 0.f;
|
|
|
|
for (const Line &line : lines)
|
|
|
|
if (line.adjustable(slowdown_external_perimeters)) {
|
|
|
|
if (line.time_max == FLT_MAX)
|
|
|
|
return FLT_MAX;
|
|
|
|
else
|
|
|
|
time_total += line.time_max;
|
|
|
|
} else
|
|
|
|
time_total += line.time;
|
|
|
|
return time_total;
|
|
|
|
}
|
|
|
|
// Calculate the non-adjustable part of the total time.
|
|
|
|
float non_adjustable_time(bool slowdown_external_perimeters) {
|
|
|
|
float time_total = 0.f;
|
|
|
|
for (const Line &line : lines)
|
|
|
|
if (! line.adjustable(slowdown_external_perimeters))
|
|
|
|
time_total += line.time;
|
|
|
|
return time_total;
|
|
|
|
}
|
|
|
|
float slow_down_maximum(bool slowdown_external_perimeters) {
|
|
|
|
float time_total = 0.f;
|
|
|
|
for (Line &line : lines) {
|
|
|
|
if (line.adjustable(slowdown_external_perimeters)) {
|
|
|
|
assert(line.time_max >= 0.f && line.time_max < FLT_MAX);
|
|
|
|
line.slowdown = true;
|
|
|
|
line.time = line.time_max;
|
|
|
|
}
|
|
|
|
time_total += line.time;
|
2017-06-22 10:59:23 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
return time_total;
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
float slow_down_proportional(float factor, bool slowdown_external_perimeters) {
|
|
|
|
assert(factor >= 1.f);
|
|
|
|
float time_total = 0.f;
|
|
|
|
for (Line &line : lines) {
|
|
|
|
if (line.adjustable(slowdown_external_perimeters)) {
|
|
|
|
line.slowdown = true;
|
|
|
|
line.time = std::min(line.time_max, line.time * factor);
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
time_total += line.time;
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
return time_total;
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
|
2017-06-30 18:01:32 +00:00
|
|
|
bool operator<(const Adjustment &rhs) const { return this->extruder_id < rhs.extruder_id; }
|
|
|
|
|
2017-06-30 15:05:58 +00:00
|
|
|
struct Line
|
|
|
|
{
|
|
|
|
enum Type {
|
|
|
|
TYPE_SET_TOOL = 1 << 0,
|
|
|
|
TYPE_EXTRUDE_END = 1 << 1,
|
|
|
|
TYPE_BRIDGE_FAN_START = 1 << 2,
|
|
|
|
TYPE_BRIDGE_FAN_END = 1 << 3,
|
|
|
|
TYPE_G0 = 1 << 4,
|
|
|
|
TYPE_G1 = 1 << 5,
|
|
|
|
TYPE_ADJUSTABLE = 1 << 6,
|
|
|
|
TYPE_EXTERNAL_PERIMETER = 1 << 7,
|
|
|
|
TYPE_WIPE = 1 << 8,
|
|
|
|
TYPE_G4 = 1 << 9,
|
|
|
|
TYPE_G92 = 1 << 10,
|
|
|
|
};
|
|
|
|
|
|
|
|
Line(unsigned int type, size_t line_start, size_t line_end) :
|
|
|
|
type(type), line_start(line_start), line_end(line_end),
|
|
|
|
length(0.f), time(0.f), time_max(0.f), slowdown(false) {}
|
|
|
|
|
|
|
|
bool adjustable(bool slowdown_external_perimeters) const {
|
|
|
|
return (this->type & TYPE_ADJUSTABLE) &&
|
|
|
|
(! (this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) &&
|
|
|
|
this->time < this->time_max;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t type;
|
|
|
|
// Start of this line at the G-code snippet.
|
|
|
|
size_t line_start;
|
|
|
|
// End of this line at the G-code snippet.
|
|
|
|
size_t line_end;
|
|
|
|
// XY Euclidian length of this segment.
|
|
|
|
float length;
|
|
|
|
// Current duration of this segment.
|
|
|
|
float time;
|
|
|
|
// Maximum duration of this segment.
|
|
|
|
float time_max;
|
|
|
|
// If marked with the "slowdown" flag, the line has been slowed down.
|
|
|
|
bool slowdown;
|
|
|
|
};
|
|
|
|
|
2017-06-30 18:01:32 +00:00
|
|
|
// Extruder, for which the G-code will be adjusted.
|
|
|
|
unsigned int extruder_id;
|
2017-06-30 15:05:58 +00:00
|
|
|
// Parsed lines.
|
|
|
|
std::vector<Line> lines;
|
|
|
|
};
|
|
|
|
std::vector<Adjustment> adjustments(num_extruders, Adjustment());
|
2017-06-30 18:01:32 +00:00
|
|
|
for (size_t i = 0; i < num_extruders; ++ i)
|
|
|
|
adjustments[i].extruder_id = extruders[i].id();
|
2017-06-30 15:05:58 +00:00
|
|
|
const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
|
|
|
|
// Parse the layer G-code for the moves, which could be adjusted.
|
|
|
|
{
|
|
|
|
float min_print_speed = float(EXTRUDER_CONFIG(min_print_speed));
|
2017-06-30 18:01:32 +00:00
|
|
|
auto adjustment = std::lower_bound(adjustments.begin(), adjustments.end(), Adjustment(m_current_extruder));
|
2017-06-30 15:05:58 +00:00
|
|
|
unsigned int initial_extruder = m_current_extruder;
|
|
|
|
const char *line_start = gcode.c_str();
|
|
|
|
const char *line_end = line_start;
|
|
|
|
const char extrusion_axis = config.get_extrusion_axis()[0];
|
|
|
|
// Index of an existing Adjustment::Line of the current adjustment, which holds the feedrate setting command
|
|
|
|
// for a sequence of extrusion moves.
|
|
|
|
size_t active_speed_modifier = size_t(-1);
|
|
|
|
for (; *line_start != 0; line_start = line_end) {
|
|
|
|
while (*line_end != '\n' && *line_end != 0)
|
|
|
|
++ line_end;
|
2017-07-13 10:11:00 +00:00
|
|
|
// sline will not contain the trailing '\n'.
|
2017-06-30 15:05:58 +00:00
|
|
|
std::string sline(line_start, line_end);
|
2017-07-13 10:11:00 +00:00
|
|
|
// Adjustment::Line will contain the trailing '\n'.
|
|
|
|
if (*line_end == '\n')
|
|
|
|
++ line_end;
|
2017-06-30 15:05:58 +00:00
|
|
|
Adjustment::Line line(0, line_start - gcode.c_str(), line_end - gcode.c_str());
|
|
|
|
if (boost::starts_with(sline, "G0 "))
|
|
|
|
line.type = Adjustment::Line::TYPE_G0;
|
|
|
|
else if (boost::starts_with(sline, "G1 "))
|
|
|
|
line.type = Adjustment::Line::TYPE_G1;
|
|
|
|
else if (boost::starts_with(sline, "G92 "))
|
|
|
|
line.type = Adjustment::Line::TYPE_G92;
|
|
|
|
if (line.type) {
|
|
|
|
// G0, G1 or G92
|
|
|
|
// Parse the G-code line.
|
|
|
|
std::vector<float> new_pos(m_current_pos);
|
|
|
|
const char *c = sline.data() + 3;
|
|
|
|
for (;;) {
|
|
|
|
// Skip whitespaces.
|
|
|
|
for (; *c == ' ' || *c == '\t'; ++ c);
|
|
|
|
if (*c == 0 || *c == ';')
|
|
|
|
break;
|
|
|
|
// Parse the axis.
|
|
|
|
size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') :
|
|
|
|
(*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
|
|
|
|
if (axis != size_t(-1)) {
|
|
|
|
new_pos[axis] = float(atof(++c));
|
|
|
|
if (axis == 4)
|
|
|
|
// Convert mm/min to mm/sec.
|
|
|
|
new_pos[4] /= 60.f;
|
|
|
|
}
|
|
|
|
// Skip this word.
|
|
|
|
for (; *c != ' ' && *c != '\t' && *c != 0; ++ c);
|
|
|
|
}
|
|
|
|
bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
|
|
|
|
bool wipe = boost::contains(sline, ";_WIPE");
|
|
|
|
if (external_perimeter)
|
|
|
|
line.type |= Adjustment::Line::TYPE_EXTERNAL_PERIMETER;
|
|
|
|
if (wipe)
|
|
|
|
line.type |= Adjustment::Line::TYPE_WIPE;
|
|
|
|
if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
|
|
|
|
line.type |= Adjustment::Line::TYPE_ADJUSTABLE;
|
|
|
|
active_speed_modifier = adjustment->lines.size();
|
|
|
|
}
|
|
|
|
if ((line.type & Adjustment::Line::TYPE_G92) == 0) {
|
|
|
|
// G0 or G1. Calculate the duration.
|
|
|
|
if (config.use_relative_e_distances.value)
|
|
|
|
// Reset extruder accumulator.
|
|
|
|
m_current_pos[3] = 0.f;
|
|
|
|
float dif[4];
|
|
|
|
for (size_t i = 0; i < 4; ++ i)
|
|
|
|
dif[i] = new_pos[i] - m_current_pos[i];
|
|
|
|
float dxy2 = dif[0] * dif[0] + dif[1] * dif[1];
|
|
|
|
float dxyz2 = dxy2 + dif[2] * dif[2];
|
|
|
|
if (dxyz2 > 0.f) {
|
|
|
|
// Movement in xyz, calculate time from the xyz Euclidian distance.
|
|
|
|
line.length = sqrt(dxyz2);
|
|
|
|
} else if (std::abs(dif[3]) > 0.f) {
|
|
|
|
// Movement in the extruder axis.
|
|
|
|
line.length = std::abs(dif[3]);
|
|
|
|
}
|
|
|
|
if (line.length > 0)
|
|
|
|
line.time = line.length / new_pos[4]; // current F
|
|
|
|
line.time_max = line.time;
|
|
|
|
if ((line.type & Adjustment::Line::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
|
|
|
|
line.time_max = (min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / min_print_speed);
|
|
|
|
if (active_speed_modifier < adjustment->lines.size() && (line.type & Adjustment::Line::TYPE_G1)) {
|
|
|
|
Adjustment::Line &sm = adjustment->lines[active_speed_modifier];
|
|
|
|
sm.length += line.length;
|
|
|
|
sm.time += line.time;
|
|
|
|
if (sm.time_max != FLT_MAX) {
|
|
|
|
if (line.time_max == FLT_MAX)
|
|
|
|
sm.time_max = FLT_MAX;
|
|
|
|
else
|
|
|
|
sm.time_max += line.time_max;
|
|
|
|
}
|
|
|
|
// Don't store this line.
|
|
|
|
line.type = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_current_pos = std::move(new_pos);
|
|
|
|
} else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
|
|
|
|
line.type = Adjustment::Line::TYPE_EXTRUDE_END;
|
|
|
|
active_speed_modifier = size_t(-1);
|
|
|
|
} else if (boost::starts_with(sline, toolchange_prefix)) {
|
|
|
|
// Switch the tool.
|
2017-06-30 17:07:14 +00:00
|
|
|
line.type = Adjustment::Line::TYPE_SET_TOOL;
|
2017-06-30 15:05:58 +00:00
|
|
|
unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size());
|
|
|
|
if (new_extruder != m_current_extruder) {
|
|
|
|
m_current_extruder = new_extruder;
|
|
|
|
min_print_speed = float(EXTRUDER_CONFIG(min_print_speed));
|
2017-06-30 18:01:32 +00:00
|
|
|
adjustment = std::lower_bound(adjustments.begin(), adjustments.end(), Adjustment(m_current_extruder));
|
2017-06-30 15:05:58 +00:00
|
|
|
}
|
|
|
|
} else if (boost::starts_with(sline, ";_BRIDGE_FAN_START")) {
|
|
|
|
line.type = Adjustment::Line::TYPE_BRIDGE_FAN_START;
|
|
|
|
} else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) {
|
|
|
|
line.type = Adjustment::Line::TYPE_BRIDGE_FAN_END;
|
|
|
|
} else if (boost::starts_with(sline, "G4 ")) {
|
|
|
|
// Parse the wait time.
|
|
|
|
line.type = Adjustment::Line::TYPE_G4;
|
|
|
|
size_t pos_S = sline.find('S', 3);
|
|
|
|
size_t pos_P = sline.find('P', 3);
|
|
|
|
line.time = line.time_max = float(
|
|
|
|
(pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
|
|
|
|
(pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
|
|
|
|
}
|
|
|
|
if (line.type != 0)
|
|
|
|
adjustment->lines.emplace_back(std::move(line));
|
|
|
|
}
|
|
|
|
m_current_extruder = initial_extruder;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort the extruders by the increasing slowdown_below_layer_time.
|
|
|
|
std::vector<size_t> by_slowdown_layer_time;
|
|
|
|
by_slowdown_layer_time.reserve(num_extruders);
|
|
|
|
// Only insert entries, which are adjustable (have cooling enabled and non-zero stretchable time).
|
|
|
|
// Collect total print time of non-adjustable extruders.
|
|
|
|
float elapsed_time_total_non_adjustable = 0.f;
|
|
|
|
for (size_t i = 0; i < num_extruders; ++ i) {
|
2017-06-30 18:01:32 +00:00
|
|
|
if (config.cooling.get_at(extruders[i].id()))
|
2017-06-30 15:05:58 +00:00
|
|
|
by_slowdown_layer_time.emplace_back(i);
|
|
|
|
else
|
|
|
|
elapsed_time_total_non_adjustable += adjustments[i].elapsed_time_total();
|
|
|
|
}
|
|
|
|
std::sort(by_slowdown_layer_time.begin(), by_slowdown_layer_time.end(),
|
2017-06-30 18:01:32 +00:00
|
|
|
[&config, &extruders](const size_t idx1, const size_t idx2){
|
|
|
|
return config.slowdown_below_layer_time.get_at(extruders[idx1].id()) <
|
|
|
|
config.slowdown_below_layer_time.get_at(extruders[idx2].id());
|
2017-06-30 15:05:58 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Elapsed time after adjustment.
|
|
|
|
float elapsed_time_total = 0.f;
|
|
|
|
{
|
|
|
|
// Elapsed time for the already adjusted extruders.
|
|
|
|
float elapsed_time_total0 = elapsed_time_total_non_adjustable;
|
|
|
|
for (size_t i_by_slowdown_layer_time = 0; i_by_slowdown_layer_time < by_slowdown_layer_time.size(); ++ i_by_slowdown_layer_time) {
|
2017-06-30 18:01:32 +00:00
|
|
|
// Idx in adjustments.
|
2017-06-30 15:05:58 +00:00
|
|
|
size_t idx = by_slowdown_layer_time[i_by_slowdown_layer_time];
|
|
|
|
// Macro to sum or adjust all sections starting with i_by_slowdown_layer_time.
|
|
|
|
#define FORALL_UNPROCESSED(ACCUMULATOR, ACTION) \
|
|
|
|
ACCUMULATOR = elapsed_time_total0;\
|
|
|
|
for (size_t j = i_by_slowdown_layer_time; j < by_slowdown_layer_time.size(); ++ j) \
|
|
|
|
ACCUMULATOR += adjustments[by_slowdown_layer_time[j]].ACTION
|
|
|
|
// Calculate the current adjusted elapsed_time_total over the non-finalized extruders.
|
|
|
|
float total;
|
|
|
|
FORALL_UNPROCESSED(total, elapsed_time_total());
|
2017-06-30 18:01:32 +00:00
|
|
|
float slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(adjustments[idx].extruder_id)) * 1.001f;
|
2017-06-30 15:05:58 +00:00
|
|
|
if (total > 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.
|
|
|
|
// Sum maximum slow down time as if everything was slowed down including the external perimeters.
|
|
|
|
float max_time;
|
|
|
|
FORALL_UNPROCESSED(max_time, maximum_time(true));
|
|
|
|
if (max_time > slowdown_below_layer_time) {
|
|
|
|
// By slowing every possible movement, the layer time could be reached. Now decide
|
|
|
|
// whether the external perimeters shall be slowed down as well.
|
|
|
|
float max_time_nep;
|
|
|
|
FORALL_UNPROCESSED(max_time_nep, maximum_time(false));
|
|
|
|
if (max_time_nep > slowdown_below_layer_time) {
|
|
|
|
// It is sufficient to slow down the non-external perimeter moves to reach the target layer time.
|
|
|
|
// Slow down the non-external perimeters proportionally.
|
|
|
|
float non_adjustable_time;
|
|
|
|
FORALL_UNPROCESSED(non_adjustable_time, non_adjustable_time(false));
|
|
|
|
// The following step is a linear programming task due to the minimum movement speeds of the print moves.
|
|
|
|
// Run maximum 5 iterations until a good enough approximation is reached.
|
|
|
|
for (size_t iter = 0; iter < 5; ++ iter) {
|
|
|
|
float factor = (slowdown_below_layer_time - non_adjustable_time) / (total - non_adjustable_time);
|
|
|
|
assert(factor > 1.f);
|
|
|
|
FORALL_UNPROCESSED(total, slow_down_proportional(factor, false));
|
|
|
|
if (total > 0.95f * slowdown_below_layer_time)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Slow down everything. First slow down the non-external perimeters to maximum.
|
|
|
|
FORALL_UNPROCESSED(total, slow_down_maximum(false));
|
|
|
|
// Slow down the external perimeters proportionally.
|
|
|
|
float non_adjustable_time;
|
|
|
|
FORALL_UNPROCESSED(non_adjustable_time, non_adjustable_time(true));
|
|
|
|
for (size_t iter = 0; iter < 5; ++ iter) {
|
|
|
|
float factor = (slowdown_below_layer_time - non_adjustable_time) / (total - non_adjustable_time);
|
|
|
|
assert(factor > 1.f);
|
|
|
|
FORALL_UNPROCESSED(total, slow_down_proportional(factor, true));
|
|
|
|
if (total > 0.95f * slowdown_below_layer_time)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Slow down to maximum possible.
|
|
|
|
FORALL_UNPROCESSED(total, slow_down_maximum(true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#undef FORALL_UNPROCESSED
|
|
|
|
// Sum the final elapsed time for all extruders up to i_by_slowdown_layer_time.
|
|
|
|
if (i_by_slowdown_layer_time + 1 == by_slowdown_layer_time.size())
|
|
|
|
// Optimization for single extruder prints.
|
|
|
|
elapsed_time_total0 = total;
|
|
|
|
else
|
|
|
|
elapsed_time_total0 += adjustments[idx].elapsed_time_total();
|
|
|
|
}
|
|
|
|
elapsed_time_total = elapsed_time_total0;
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
|
|
|
|
// Transform the G-code.
|
|
|
|
// First sort the adjustment lines by their position in the source G-code.
|
|
|
|
std::vector<const Adjustment::Line*> lines;
|
|
|
|
{
|
|
|
|
size_t n_lines = 0;
|
|
|
|
for (const Adjustment &adj : adjustments)
|
|
|
|
n_lines += adj.lines.size();
|
|
|
|
lines.reserve(n_lines);
|
|
|
|
for (const Adjustment &adj : adjustments)
|
|
|
|
for (const Adjustment::Line &line : adj.lines)
|
|
|
|
lines.emplace_back(&line);
|
|
|
|
std::sort(lines.begin(), lines.end(), [](const Adjustment::Line *ln1, const Adjustment::Line *ln2) { return ln1->line_start < ln2->line_start; } );
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
2017-06-30 15:05:58 +00:00
|
|
|
// Second generate the adjusted G-code.
|
|
|
|
std::string new_gcode;
|
|
|
|
new_gcode.reserve(gcode.size() * 2);
|
|
|
|
int fan_speed = -1;
|
|
|
|
bool bridge_fan_control = false;
|
|
|
|
int bridge_fan_speed = 0;
|
|
|
|
auto change_extruder_set_fan = [ this, layer_id, elapsed_time_total, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() {
|
|
|
|
const FullPrintConfig &config = m_gcodegen.config();
|
|
|
|
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
|
|
|
|
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
|
|
|
|
if (layer_id >= EXTRUDER_CONFIG(disable_fan_first_layers)) {
|
|
|
|
int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed);
|
|
|
|
float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time));
|
|
|
|
float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time));
|
|
|
|
if (EXTRUDER_CONFIG(cooling)) {
|
|
|
|
if (elapsed_time_total < slowdown_below_layer_time) {
|
|
|
|
// Layer time very short. Enable the fan to a full throttle.
|
|
|
|
fan_speed_new = max_fan_speed;
|
|
|
|
} else if (elapsed_time_total < fan_below_layer_time) {
|
|
|
|
// Layer time quite short. Enable the fan proportionally according to the current layer time.
|
|
|
|
assert(elapsed_time_total >= slowdown_below_layer_time);
|
|
|
|
double t = (elapsed_time_total - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
|
|
|
|
fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed);
|
|
|
|
bridge_fan_control = bridge_fan_speed > fan_speed_new;
|
|
|
|
} else {
|
|
|
|
bridge_fan_control = false;
|
|
|
|
bridge_fan_speed = 0;
|
2017-07-13 10:11:00 +00:00
|
|
|
fan_speed_new = 0;
|
2017-06-30 15:05:58 +00:00
|
|
|
}
|
|
|
|
if (fan_speed_new != fan_speed) {
|
|
|
|
fan_speed = fan_speed_new;
|
|
|
|
new_gcode += m_gcodegen.writer().set_fan(fan_speed);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
change_extruder_set_fan();
|
|
|
|
|
|
|
|
size_t pos = 0;
|
|
|
|
for (const Adjustment::Line *line : lines) {
|
|
|
|
if (line->line_start > pos)
|
|
|
|
new_gcode.append(gcode.c_str() + pos, line->line_start - pos);
|
|
|
|
if (line->type & Adjustment::Line::TYPE_SET_TOOL) {
|
|
|
|
unsigned int new_extruder = (unsigned int)atoi(gcode.c_str() + line->line_start + toolchange_prefix.size());
|
|
|
|
if (new_extruder != m_current_extruder) {
|
|
|
|
m_current_extruder = new_extruder;
|
|
|
|
change_extruder_set_fan();
|
|
|
|
}
|
2017-06-30 17:07:14 +00:00
|
|
|
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start);
|
2017-06-30 15:05:58 +00:00
|
|
|
} else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_START) {
|
|
|
|
if (bridge_fan_control)
|
2017-07-13 10:11:00 +00:00
|
|
|
new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true);
|
2017-06-30 15:05:58 +00:00
|
|
|
} else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_END) {
|
|
|
|
if (bridge_fan_control)
|
2017-07-13 10:11:00 +00:00
|
|
|
new_gcode += m_gcodegen.writer().set_fan(fan_speed, true);
|
2017-06-30 15:05:58 +00:00
|
|
|
} else if (line->type & Adjustment::Line::TYPE_EXTRUDE_END) {
|
|
|
|
// Just remove this comment.
|
|
|
|
} else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) {
|
|
|
|
// Start of the comment. The line type indicates there must be some comment present.
|
|
|
|
const char *end = strchr(gcode.c_str() + line->line_start, ';');
|
|
|
|
if (line->slowdown) {
|
|
|
|
// Replace the feedrate.
|
|
|
|
const char *pos = strstr(gcode.c_str() + line->line_start + 2, " F") + 2;
|
|
|
|
new_gcode.append(gcode.c_str() + line->line_start, pos - gcode.c_str() - line->line_start);
|
|
|
|
char buf[64];
|
|
|
|
sprintf(buf, "%d", int(floor(60. * (line->length / line->time) + 0.5)));
|
|
|
|
new_gcode += buf;
|
|
|
|
// Skip the non-whitespaces up to the comment.
|
|
|
|
for (; *pos != ' ' && *pos != ';'; ++ pos);
|
|
|
|
// Append the rest of the line without the comment.
|
|
|
|
if (pos < end)
|
|
|
|
new_gcode.append(pos, end - pos);
|
|
|
|
} else {
|
|
|
|
// Append the line without the comment.
|
|
|
|
new_gcode.append(gcode.c_str() + line->line_start, end - gcode.c_str() - line->line_start);
|
|
|
|
}
|
|
|
|
// Process the comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
|
|
|
|
std::string comment(end, gcode.c_str() + line->line_end);
|
|
|
|
boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
|
|
|
|
if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
|
|
|
|
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
|
|
|
|
if (line->type & Adjustment::Line::TYPE_WIPE)
|
|
|
|
boost::replace_all(comment, ";_WIPE", "");
|
|
|
|
new_gcode += comment;
|
|
|
|
} else {
|
|
|
|
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start);
|
|
|
|
}
|
|
|
|
pos = line->line_end;
|
|
|
|
}
|
|
|
|
if (pos < gcode.size())
|
|
|
|
new_gcode.append(gcode.c_str() + pos, gcode.size() - pos);
|
|
|
|
|
|
|
|
return new_gcode;
|
2016-12-21 22:09:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 15:05:58 +00:00
|
|
|
} // namespace Slic3r
|