231 lines
13 KiB
C++
231 lines
13 KiB
C++
#include "CoolingBuffer.hpp"
|
|
|
|
namespace Slic3r {
|
|
float new_feedrate_to_reach_time_stretch(std::vector<PerExtruderAdjustments *>::const_iterator it_begin,
|
|
std::vector<PerExtruderAdjustments *>::const_iterator it_end,
|
|
float min_feedrate,
|
|
float time_stretch,
|
|
size_t max_iter = 20)
|
|
{
|
|
float new_feedrate = min_feedrate;
|
|
for (size_t iter = 0; iter < max_iter; ++iter) {
|
|
double nomin = 0;
|
|
double denom = time_stretch;
|
|
for (auto it = it_begin; it != it_end; ++it) {
|
|
assert((*it)->slow_down_min_speed < min_feedrate + EPSILON);
|
|
for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) {
|
|
const CoolingLine &line = (*it)->lines[i];
|
|
if (line.feedrate > min_feedrate) {
|
|
nomin += (double) line.time * (double) line.feedrate;
|
|
denom += (double) line.time;
|
|
}
|
|
}
|
|
}
|
|
assert(denom > 0);
|
|
if (denom < 0) return min_feedrate;
|
|
new_feedrate = (float) (nomin / denom);
|
|
assert(new_feedrate > min_feedrate - EPSILON);
|
|
if (new_feedrate < min_feedrate + EPSILON) goto finished;
|
|
for (auto it = it_begin; it != it_end; ++it)
|
|
for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) {
|
|
const CoolingLine &line = (*it)->lines[i];
|
|
if (line.feedrate > min_feedrate && line.feedrate < new_feedrate)
|
|
// Some of the line segments taken into account in the calculation of nomin / denom are now slower than new_feedrate,
|
|
// which makes the new_feedrate lower than it should be.
|
|
// Re-run the calculation with a new min_feedrate limit, so that the segments with current feedrate lower than new_feedrate
|
|
// are not taken into account.
|
|
goto not_finished_yet;
|
|
}
|
|
goto finished;
|
|
not_finished_yet:
|
|
min_feedrate = new_feedrate;
|
|
}
|
|
// Failed to find the new feedrate for the time_stretch.
|
|
|
|
finished :
|
|
// Test whether the time_stretch was achieved.
|
|
#ifndef NDEBUG
|
|
{
|
|
float time_stretch_final = 0.f;
|
|
for (auto it = it_begin; it != it_end; ++it) time_stretch_final += (*it)->time_stretch_when_slowing_down_to_feedrate(new_feedrate);
|
|
assert(std::abs(time_stretch - time_stretch_final) < EPSILON);
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
return new_feedrate;
|
|
}
|
|
// Slow down an extruder range proportionally down to slow_down_layer_time.
|
|
// Return the total time for the complete layer.
|
|
static inline float extruder_range_slow_down_proportional(std::vector<PerExtruderAdjustments *>::iterator it_begin,
|
|
std::vector<PerExtruderAdjustments *>::iterator it_end,
|
|
// Elapsed time for the extruders already processed.
|
|
float elapsed_time_total0,
|
|
// Initial total elapsed time before slow down.
|
|
float elapsed_time_before_slowdown,
|
|
// Target time for the complete layer (all extruders applied).
|
|
float slow_down_layer_time)
|
|
{
|
|
// Total layer time after the slow down has been applied.
|
|
float total_after_slowdown = elapsed_time_before_slowdown;
|
|
// Now decide, whether the external perimeters shall be slowed down as well.
|
|
float max_time_nep = elapsed_time_total0;
|
|
for (auto it = it_begin; it != it_end; ++it) max_time_nep += (*it)->maximum_time_after_slowdown(false);
|
|
if (max_time_nep > slow_down_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 = elapsed_time_total0;
|
|
for (auto it = it_begin; it != it_end; ++it) non_adjustable_time += (*it)->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 = (slow_down_layer_time - non_adjustable_time) / (total_after_slowdown - non_adjustable_time);
|
|
assert(factor > 1.f);
|
|
total_after_slowdown = elapsed_time_total0;
|
|
for (auto it = it_begin; it != it_end; ++it) total_after_slowdown += (*it)->slow_down_proportional(factor, false);
|
|
if (total_after_slowdown > 0.95f * slow_down_layer_time) break;
|
|
}
|
|
} else {
|
|
// Slow down everything. First slow down the non-external perimeters to maximum.
|
|
for (auto it = it_begin; it != it_end; ++it) (*it)->slowdown_to_minimum_feedrate(false);
|
|
// Slow down the external perimeters proportionally.
|
|
float non_adjustable_time = elapsed_time_total0;
|
|
for (auto it = it_begin; it != it_end; ++it) non_adjustable_time += (*it)->non_adjustable_time(true);
|
|
for (size_t iter = 0; iter < 5; ++iter) {
|
|
float factor = (slow_down_layer_time - non_adjustable_time) / (total_after_slowdown - non_adjustable_time);
|
|
assert(factor > 1.f);
|
|
total_after_slowdown = elapsed_time_total0;
|
|
for (auto it = it_begin; it != it_end; ++it) total_after_slowdown += (*it)->slow_down_proportional(factor, true);
|
|
if (total_after_slowdown > 0.95f * slow_down_layer_time) break;
|
|
}
|
|
}
|
|
return total_after_slowdown;
|
|
}
|
|
|
|
// Slow down an extruder range to slow_down_layer_time.
|
|
// Return the total time for the complete layer.
|
|
static inline void extruder_range_slow_down_non_proportional(std::vector<PerExtruderAdjustments *>::iterator it_begin,
|
|
std::vector<PerExtruderAdjustments *>::iterator it_end,
|
|
float time_stretch)
|
|
{
|
|
// Slow down. Try to equalize the feedrates.
|
|
std::vector<PerExtruderAdjustments *> by_min_print_speed(it_begin, it_end);
|
|
// Find the next highest adjustable feedrate among the extruders.
|
|
float feedrate = 0;
|
|
for (PerExtruderAdjustments *adj : by_min_print_speed) {
|
|
adj->idx_line_begin = 0;
|
|
adj->idx_line_end = 0;
|
|
assert(adj->idx_line_begin < adj->n_lines_adjustable);
|
|
if (adj->lines[adj->idx_line_begin].feedrate > feedrate) feedrate = adj->lines[adj->idx_line_begin].feedrate;
|
|
}
|
|
assert(feedrate > 0.f);
|
|
// Sort by slow_down_min_speed, maximum speed first.
|
|
std::sort(by_min_print_speed.begin(), by_min_print_speed.end(),
|
|
[](const PerExtruderAdjustments *p1, const PerExtruderAdjustments *p2) { return p1->slow_down_min_speed > p2->slow_down_min_speed; });
|
|
// Slow down, fast moves first.
|
|
for (;;) {
|
|
// For each extruder, find the span of lines with a feedrate close to feedrate.
|
|
for (PerExtruderAdjustments *adj : by_min_print_speed) {
|
|
for (adj->idx_line_end = adj->idx_line_begin; adj->idx_line_end < adj->n_lines_adjustable && adj->lines[adj->idx_line_end].feedrate > feedrate - EPSILON;
|
|
++adj->idx_line_end)
|
|
;
|
|
}
|
|
// Find the next highest adjustable feedrate among the extruders.
|
|
float feedrate_next = 0.f;
|
|
for (PerExtruderAdjustments *adj : by_min_print_speed)
|
|
if (adj->idx_line_end < adj->n_lines_adjustable && adj->lines[adj->idx_line_end].feedrate > feedrate_next) feedrate_next = adj->lines[adj->idx_line_end].feedrate;
|
|
// Slow down, limited by max(feedrate_next, slow_down_min_speed).
|
|
for (auto adj = by_min_print_speed.begin(); adj != by_min_print_speed.end();) {
|
|
// Slow down at most by time_stretch.
|
|
if ((*adj)->slow_down_min_speed == 0.f) {
|
|
// All the adjustable speeds are now lowered to the same speed,
|
|
// and the minimum speed is set to zero.
|
|
float time_adjustable = 0.f;
|
|
for (auto it = adj; it != by_min_print_speed.end(); ++it) time_adjustable += (*it)->adjustable_time(true);
|
|
float rate = (time_adjustable + time_stretch) / time_adjustable;
|
|
for (auto it = adj; it != by_min_print_speed.end(); ++it) (*it)->slow_down_proportional(rate, true);
|
|
return;
|
|
} else {
|
|
float feedrate_limit = std::max(feedrate_next, (*adj)->slow_down_min_speed);
|
|
bool done = false;
|
|
float time_stretch_max = 0.f;
|
|
for (auto it = adj; it != by_min_print_speed.end(); ++it) time_stretch_max += (*it)->time_stretch_when_slowing_down_to_feedrate(feedrate_limit);
|
|
if (time_stretch_max >= time_stretch) {
|
|
feedrate_limit = new_feedrate_to_reach_time_stretch(adj, by_min_print_speed.end(), feedrate_limit, time_stretch, 20);
|
|
done = true;
|
|
} else
|
|
time_stretch -= time_stretch_max;
|
|
for (auto it = adj; it != by_min_print_speed.end(); ++it) (*it)->slow_down_to_feedrate(feedrate_limit);
|
|
if (done) return;
|
|
}
|
|
// Skip the other extruders with nearly the same slow_down_min_speed, as they have been processed already.
|
|
auto next = adj;
|
|
for (++next; next != by_min_print_speed.end() && (*next)->slow_down_min_speed > (*adj)->slow_down_min_speed - EPSILON; ++next)
|
|
;
|
|
adj = next;
|
|
}
|
|
if (feedrate_next == 0.f)
|
|
// There are no other extrusions available for slow down.
|
|
break;
|
|
for (PerExtruderAdjustments *adj : by_min_print_speed) {
|
|
adj->idx_line_begin = adj->idx_line_end;
|
|
feedrate = feedrate_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate slow down for all the extruders.
|
|
float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments)
|
|
{
|
|
// Sort the extruders by an increasing slow_down_layer_time.
|
|
// The layers with a lower slow_down_layer_time are slowed down
|
|
// together with all the other layers with slow_down_layer_time above.
|
|
std::vector<PerExtruderAdjustments *> by_slowdown_time;
|
|
by_slowdown_time.reserve(per_extruder_adjustments.size());
|
|
// 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_total0 = 0.f;
|
|
for (PerExtruderAdjustments &adj : per_extruder_adjustments) {
|
|
// Curren total time for this extruder.
|
|
adj.time_total = adj.elapsed_time_total();
|
|
// Maximum time for this extruder, when all extrusion moves are slowed down to min_extrusion_speed.
|
|
adj.time_maximum = adj.maximum_time_after_slowdown(true);
|
|
if (adj.cooling_slow_down_enabled && adj.lines.size() > 0) {
|
|
by_slowdown_time.emplace_back(&adj);
|
|
if (!m_cooling_logic_proportional)
|
|
// sorts the lines, also sets adj.time_non_adjustable
|
|
adj.sort_lines_by_decreasing_feedrate();
|
|
} else
|
|
elapsed_time_total0 += adj.elapsed_time_total();
|
|
}
|
|
std::sort(by_slowdown_time.begin(), by_slowdown_time.end(),
|
|
[](const PerExtruderAdjustments *adj1, const PerExtruderAdjustments *adj2) { return adj1->slow_down_layer_time < adj2->slow_down_layer_time; });
|
|
|
|
for (auto cur_begin = by_slowdown_time.begin(); cur_begin != by_slowdown_time.end(); ++cur_begin) {
|
|
PerExtruderAdjustments &adj = *(*cur_begin);
|
|
// Calculate the current adjusted elapsed_time_total over the non-finalized extruders.
|
|
float total = elapsed_time_total0;
|
|
for (auto it = cur_begin; it != by_slowdown_time.end(); ++it) total += (*it)->time_total;
|
|
float slow_down_layer_time = adj.slow_down_layer_time * 1.001f;
|
|
if (total > slow_down_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 m_config.slow_down_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) max_time += (*it)->time_maximum;
|
|
if (max_time > slow_down_layer_time) {
|
|
if (m_cooling_logic_proportional)
|
|
extruder_range_slow_down_proportional(cur_begin, by_slowdown_time.end(), elapsed_time_total0, total, slow_down_layer_time);
|
|
else
|
|
extruder_range_slow_down_non_proportional(cur_begin, by_slowdown_time.end(), slow_down_layer_time - total);
|
|
} else {
|
|
// Slow down to maximum possible.
|
|
for (auto it = cur_begin; it != by_slowdown_time.end(); ++it) (*it)->slowdown_to_minimum_feedrate(true);
|
|
}
|
|
}
|
|
elapsed_time_total0 += adj.elapsed_time_total();
|
|
}
|
|
|
|
return elapsed_time_total0;
|
|
}
|
|
} |