Merge branch 'lh_pressure_equalizer'
This commit is contained in:
commit
4b00b78e7f
@ -104,8 +104,8 @@ add_library(libslic3r STATIC
|
||||
GCode/FindReplace.hpp
|
||||
GCode/PostProcessor.cpp
|
||||
GCode/PostProcessor.hpp
|
||||
# GCode/PressureEqualizer.cpp
|
||||
# GCode/PressureEqualizer.hpp
|
||||
GCode/PressureEqualizer.cpp
|
||||
GCode/PressureEqualizer.hpp
|
||||
GCode/PrintExtents.cpp
|
||||
GCode/PrintExtents.hpp
|
||||
GCode/SpiralVase.cpp
|
||||
|
@ -1108,14 +1108,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
if (print.config().spiral_vase.value)
|
||||
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
|
||||
if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 ||
|
||||
print.config().max_volumetric_extrusion_rate_slope_negative.value > 0)
|
||||
m_pressure_equalizer = make_unique<PressureEqualizer>(&print.config());
|
||||
m_pressure_equalizer = make_unique<PressureEqualizer>(print.config());
|
||||
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
|
||||
#else /* HAS_PRESSURE_EQUALIZER */
|
||||
m_enable_extrusion_role_markers = false;
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
// Write information on the generator.
|
||||
file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
@ -1364,10 +1361,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file);
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
++ finished_objects;
|
||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||
// Reset it when starting another object from 1st layer.
|
||||
@ -1424,10 +1417,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file);
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
file.write(m_wipe_tower->finalize(*this));
|
||||
@ -1540,11 +1529,18 @@ void GCode::process_layers(
|
||||
{
|
||||
// The pipeline is variable: The vase mode filter is optional.
|
||||
size_t layer_to_print_idx = 0;
|
||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
const auto generator = tbb::make_filter<void, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> LayerResult {
|
||||
if (layer_to_print_idx >= layers_to_print.size()) {
|
||||
if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
// Pressure equalizer need insert empty input. Because it returns one layer back.
|
||||
// Insert NOP (no operation) layer;
|
||||
++layer_to_print_idx;
|
||||
return LayerResult::make_nop_layer_result();
|
||||
}
|
||||
} else {
|
||||
const std::pair<coordf_t, std::vector<LayerToPrint>>& layer = layers_to_print[layer_to_print_idx++];
|
||||
const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||
@ -1554,17 +1550,27 @@ void GCode::process_layers(
|
||||
return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1));
|
||||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in) -> GCode::LayerResult {
|
||||
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase](LayerResult in) -> LayerResult {
|
||||
if (in.nop_layer_result)
|
||||
return in;
|
||||
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
|
||||
});
|
||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
||||
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer.process_layer(std::move(in));
|
||||
});
|
||||
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer](LayerResult in) -> std::string {
|
||||
if (in.nop_layer_result)
|
||||
return in.gcode;
|
||||
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace.get()](std::string s) -> std::string {
|
||||
[&self = *this->m_find_replace](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
@ -1577,12 +1583,20 @@ void GCode::process_layers(
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
output_stream.find_replace_supress();
|
||||
if (m_spiral_vase && m_find_replace)
|
||||
if (m_spiral_vase && m_find_replace && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output);
|
||||
else if (m_spiral_vase && m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
|
||||
else if (m_spiral_vase && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output);
|
||||
else if (m_find_replace && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output);
|
||||
else if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else if (m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
|
||||
else if (m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
output_stream.find_replace_enable();
|
||||
@ -1600,28 +1614,43 @@ void GCode::process_layers(
|
||||
{
|
||||
// The pipeline is variable: The vase mode filter is optional.
|
||||
size_t layer_to_print_idx = 0;
|
||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
const auto generator = tbb::make_filter<void, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> LayerResult {
|
||||
if (layer_to_print_idx >= layers_to_print.size()) {
|
||||
if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
// Pressure equalizer need insert empty input. Because it returns one layer back.
|
||||
// Insert NOP (no operation) layer;
|
||||
++layer_to_print_idx;
|
||||
return LayerResult::make_nop_layer_result();
|
||||
}
|
||||
} else {
|
||||
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
|
||||
print.throw_if_canceled();
|
||||
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
|
||||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in)->GCode::LayerResult {
|
||||
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase](LayerResult in)->LayerResult {
|
||||
if (in.nop_layer_result)
|
||||
return in;
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
});
|
||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
|
||||
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer.process_layer(std::move(in));
|
||||
});
|
||||
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer](LayerResult in)->std::string {
|
||||
if (in.nop_layer_result)
|
||||
return in.gcode;
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace.get()](std::string s) -> std::string {
|
||||
[&self = *this->m_find_replace](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
@ -1634,12 +1663,20 @@ void GCode::process_layers(
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
output_stream.find_replace_supress();
|
||||
if (m_spiral_vase && m_find_replace)
|
||||
if (m_spiral_vase && m_find_replace && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output);
|
||||
else if (m_spiral_vase && m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
|
||||
else if (m_spiral_vase && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output);
|
||||
else if (m_find_replace && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output);
|
||||
else if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else if (m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
|
||||
else if (m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
output_stream.find_replace_enable();
|
||||
@ -2066,7 +2103,7 @@ namespace Skirt {
|
||||
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
|
||||
// For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
|
||||
// and performing the extruder specific extrusions together.
|
||||
GCode::LayerResult GCode::process_layer(
|
||||
LayerResult GCode::process_layer(
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
@ -2097,7 +2134,7 @@ GCode::LayerResult GCode::process_layer(
|
||||
}
|
||||
}
|
||||
const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer;
|
||||
GCode::LayerResult result { {}, layer.id(), false, last_layer };
|
||||
LayerResult result { {}, layer.id(), false, last_layer, false};
|
||||
if (layer_tools.extruders.empty())
|
||||
// Nothing to extrude.
|
||||
return result;
|
||||
@ -2480,13 +2517,11 @@ GCode::LayerResult GCode::process_layer(
|
||||
// Flush the cooling buffer at each object layer or possibly at the last layer, even if it contains just supports (This should not happen).
|
||||
object_layer || last_layer);
|
||||
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
// Apply pressure equalization if enabled;
|
||||
// printf("G-code before filter:\n%s\n", gcode.c_str());
|
||||
if (m_pressure_equalizer)
|
||||
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
|
||||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
file.write(gcode);
|
||||
#endif
|
||||
|
@ -23,9 +23,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
#include "GCode/PressureEqualizer.hpp"
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -116,6 +114,20 @@ public:
|
||||
static const std::vector<std::string>& get() { return Colors; }
|
||||
};
|
||||
|
||||
struct LayerResult {
|
||||
std::string gcode;
|
||||
size_t layer_id;
|
||||
// Is spiral vase post processing enabled for this layer?
|
||||
bool spiral_vase_enable { false };
|
||||
// Should the cooling buffer content be flushed at the end of this layer?
|
||||
bool cooling_buffer_flush { false };
|
||||
// Is indicating if this LayerResult should be processed, or it is just inserted artificial LayerResult.
|
||||
// It is used for the pressure equalizer because it needs to buffer one layer back.
|
||||
bool nop_layer_result { false };
|
||||
|
||||
static LayerResult make_nop_layer_result() { return {"", std::numeric_limits<coord_t>::max(), false, false, true}; }
|
||||
};
|
||||
|
||||
class GCode {
|
||||
public:
|
||||
GCode() :
|
||||
@ -228,14 +240,6 @@ private:
|
||||
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
|
||||
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
|
||||
|
||||
struct LayerResult {
|
||||
std::string gcode;
|
||||
size_t layer_id;
|
||||
// Is spiral vase post processing enabled for this layer?
|
||||
bool spiral_vase_enable { false };
|
||||
// Should the cooling buffer content be flushed at the end of this layer?
|
||||
bool cooling_buffer_flush { false };
|
||||
};
|
||||
LayerResult process_layer(
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
@ -406,9 +410,7 @@ private:
|
||||
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||
std::unique_ptr<SpiralVase> m_spiral_vase;
|
||||
std::unique_ptr<GCodeFindReplace> m_find_replace;
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
|
||||
|
||||
// Heights (print_z) at which the skirt has already been extruded.
|
||||
@ -448,6 +450,7 @@ private:
|
||||
|
||||
friend class Wipe;
|
||||
friend class WipeTowerIntegration;
|
||||
friend class PressureEqualizer;
|
||||
};
|
||||
|
||||
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print);
|
||||
|
@ -5,28 +5,21 @@
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../LocalesUtils.hpp"
|
||||
#include "../GCode.hpp"
|
||||
|
||||
#include "PressureEqualizer.hpp"
|
||||
#include "fast_float/fast_float.h"
|
||||
#include "GCodeWriter.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
|
||||
m_config(config)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
static const std::string EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:";
|
||||
static const std::string EXTRUDE_END_TAG = ";_EXTRUDE_END";
|
||||
static const std::string EXTRUDE_SET_SPEED_TAG = ";_EXTRUDE_SET_SPEED";
|
||||
static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER";
|
||||
|
||||
PressureEqualizer::~PressureEqualizer()
|
||||
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value)
|
||||
{
|
||||
}
|
||||
|
||||
void PressureEqualizer::reset()
|
||||
{
|
||||
circular_buffer_pos = 0;
|
||||
circular_buffer_size = 100;
|
||||
circular_buffer_items = 0;
|
||||
circular_buffer.assign(circular_buffer_size, GCodeLine());
|
||||
|
||||
// Preallocate some data, so that output_buffer.data() will return an empty string.
|
||||
output_buffer.assign(32, 0);
|
||||
output_buffer_length = 0;
|
||||
@ -40,8 +33,7 @@ void PressureEqualizer::reset()
|
||||
|
||||
// Calculate filamet crossections for the multiple extruders.
|
||||
m_filament_crossections.clear();
|
||||
for (size_t i = 0; i < m_config->filament_diameter.values.size(); ++ i) {
|
||||
double r = m_config->filament_diameter.values[i];
|
||||
for (double r : config.filament_diameter.values) {
|
||||
double a = 0.25f * M_PI * r * r;
|
||||
m_filament_crossections.push_back(float(a));
|
||||
}
|
||||
@ -50,82 +42,82 @@ void PressureEqualizer::reset()
|
||||
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min
|
||||
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min
|
||||
// Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2
|
||||
m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f :
|
||||
m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
|
||||
m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f :
|
||||
m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
|
||||
m_max_volumetric_extrusion_rate_slope_positive = float(config.max_volumetric_extrusion_rate_slope_positive.value) * 60.f * 60.f;
|
||||
m_max_volumetric_extrusion_rate_slope_negative = float(config.max_volumetric_extrusion_rate_slope_negative.value) * 60.f * 60.f;
|
||||
|
||||
for (size_t i = 0; i < numExtrusionRoles; ++ i) {
|
||||
m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative;
|
||||
m_max_volumetric_extrusion_rate_slopes[i].positive = m_max_volumetric_extrusion_rate_slope_positive;
|
||||
for (ExtrusionRateSlope &extrusion_rate_slope : m_max_volumetric_extrusion_rate_slopes) {
|
||||
extrusion_rate_slope.negative = m_max_volumetric_extrusion_rate_slope_negative;
|
||||
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
|
||||
}
|
||||
|
||||
// Don't regulate the pressure in infill.
|
||||
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].negative = 0;
|
||||
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].positive = 0;
|
||||
// Don't regulate the pressure in gap fill.
|
||||
m_max_volumetric_extrusion_rate_slopes[erGapFill].negative = 0;
|
||||
m_max_volumetric_extrusion_rate_slopes[erGapFill].positive = 0;
|
||||
// Don't regulate the pressure in infill and gap fill.
|
||||
// TODO: Do we want to regulate pressure in erWipeTower, erCustom and erMixed?
|
||||
for (const ExtrusionRole er : {erBridgeInfill, erGapFill}) {
|
||||
m_max_volumetric_extrusion_rate_slopes[er].negative = 0;
|
||||
m_max_volumetric_extrusion_rate_slopes[er].positive = 0;
|
||||
}
|
||||
|
||||
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||
m_stat.reset();
|
||||
#endif
|
||||
|
||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||
line_idx = 0;
|
||||
}
|
||||
|
||||
const char* PressureEqualizer::process(const char *szGCode, bool flush)
|
||||
{
|
||||
// Reset length of the output_buffer.
|
||||
output_buffer_length = 0;
|
||||
|
||||
if (szGCode != 0) {
|
||||
const char *p = szGCode;
|
||||
while (*p != 0) {
|
||||
// Find end of the line.
|
||||
const char *endl = p;
|
||||
// Slic3r always generates end of lines in a Unix style.
|
||||
for (; *endl != 0 && *endl != '\n'; ++ endl) ;
|
||||
if (circular_buffer_items == circular_buffer_size)
|
||||
// Buffer is full. Push out the oldest line.
|
||||
output_gcode_line(circular_buffer[circular_buffer_pos]);
|
||||
else
|
||||
++ circular_buffer_items;
|
||||
// Process a G-code line, store it into the provided GCodeLine object.
|
||||
size_t idx_tail = circular_buffer_pos;
|
||||
circular_buffer_pos = circular_buffer_idx_next(circular_buffer_pos);
|
||||
if (! process_line(p, endl - p, circular_buffer[idx_tail])) {
|
||||
// The line has to be forgotten. It contains comment marks, which shall be
|
||||
// filtered out of the target g-code.
|
||||
circular_buffer_pos = idx_tail;
|
||||
-- circular_buffer_items;
|
||||
}
|
||||
p = endl;
|
||||
if (*p == '\n')
|
||||
++ p;
|
||||
}
|
||||
}
|
||||
|
||||
if (flush) {
|
||||
// Flush the remaining valid lines of the circular buffer.
|
||||
for (size_t idx = circular_buffer_idx_head(); circular_buffer_items > 0; -- circular_buffer_items) {
|
||||
output_gcode_line(circular_buffer[idx]);
|
||||
if (++ idx == circular_buffer_size)
|
||||
idx = 0;
|
||||
}
|
||||
// Reset the index pointer.
|
||||
assert(circular_buffer_items == 0);
|
||||
circular_buffer_pos = 0;
|
||||
|
||||
#if 1
|
||||
printf("Statistics: \n");
|
||||
printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min);
|
||||
printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max);
|
||||
if (m_stat.extrusion_length > 0)
|
||||
m_stat.volumetric_extrusion_rate_avg /= m_stat.extrusion_length;
|
||||
printf("Average volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_avg);
|
||||
m_stat.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
return output_buffer.data();
|
||||
void PressureEqualizer::process_layer(const std::string &gcode)
|
||||
{
|
||||
if (!gcode.empty()) {
|
||||
const char *gcode_begin = gcode.c_str();
|
||||
while (*gcode_begin != 0) {
|
||||
// Find end of the line.
|
||||
const char *gcode_end = gcode_begin;
|
||||
// Slic3r always generates end of lines in a Unix style.
|
||||
for (; *gcode_end != 0 && *gcode_end != '\n'; ++gcode_end);
|
||||
|
||||
m_gcode_lines.emplace_back();
|
||||
if (!this->process_line(gcode_begin, gcode_end, m_gcode_lines.back())) {
|
||||
// The line has to be forgotten. It contains comment marks, which shall be filtered out of the target g-code.
|
||||
m_gcode_lines.pop_back();
|
||||
}
|
||||
gcode_begin = gcode_end;
|
||||
if (*gcode_begin == '\n')
|
||||
++gcode_begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LayerResult PressureEqualizer::process_layer(LayerResult &&input)
|
||||
{
|
||||
const bool is_first_layer = m_layer_results.empty();
|
||||
const size_t next_layer_first_idx = m_gcode_lines.size();
|
||||
|
||||
if (!input.nop_layer_result) {
|
||||
this->process_layer(input.gcode);
|
||||
input.gcode.clear(); // GCode is already processed, so it isn't needed to store it.
|
||||
m_layer_results.emplace(new LayerResult(input));
|
||||
}
|
||||
|
||||
if (is_first_layer) // Buffer previous input result and output NOP.
|
||||
return LayerResult::make_nop_layer_result();
|
||||
|
||||
// Export previous layer.
|
||||
LayerResult *prev_layer_result = m_layer_results.front();
|
||||
m_layer_results.pop();
|
||||
|
||||
output_buffer_length = 0;
|
||||
for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx)
|
||||
output_gcode_line(m_gcode_lines[line_idx]);
|
||||
m_gcode_lines.erase(m_gcode_lines.begin(), m_gcode_lines.begin() + int(next_layer_first_idx));
|
||||
|
||||
if (output_buffer_length > 0)
|
||||
prev_layer_result->gcode = std::string(output_buffer.data());
|
||||
|
||||
assert(!input.nop_layer_result || m_layer_results.empty());
|
||||
LayerResult out = *prev_layer_result;
|
||||
delete prev_layer_result;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Is a white space?
|
||||
@ -146,35 +138,45 @@ static void eatws(const char *&line)
|
||||
// If succeeded, the line pointer is advanced.
|
||||
static inline int parse_int(const char *&line)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
char *endptr = nullptr;
|
||||
long result = strtol(line, &endptr, 10);
|
||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||
if (endptr == nullptr || !is_ws_or_eol(*endptr))
|
||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int");
|
||||
line = endptr;
|
||||
return int(result);
|
||||
};
|
||||
|
||||
float string_to_float_decimal_point(const char *line, const size_t str_len, size_t* pos)
|
||||
{
|
||||
float out;
|
||||
size_t p = fast_float::from_chars(line, line + str_len, out).ptr - line;
|
||||
if (pos)
|
||||
*pos = p;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse an int starting at the current position of a line.
|
||||
// If succeeded, the line pointer is advanced.
|
||||
static inline float parse_float(const char *&line)
|
||||
static inline float parse_float(const char *&line, const size_t line_length)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
float result = string_to_double_decimal_point(line, &endptr);
|
||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||
size_t endptr = 0;
|
||||
auto result = string_to_float_decimal_point(line, line_length, &endptr);
|
||||
if (endptr == 0 || !is_ws_or_eol(*(line + endptr)))
|
||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
||||
line = endptr;
|
||||
line = line + endptr;
|
||||
return result;
|
||||
};
|
||||
|
||||
bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
|
||||
bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf)
|
||||
{
|
||||
static constexpr const char *EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:";
|
||||
|
||||
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
|
||||
line += strlen(EXTRUSION_ROLE_TAG);
|
||||
const size_t len = line_end - line;
|
||||
if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG.length()) == 0) {
|
||||
line += EXTRUSION_ROLE_TAG.length();
|
||||
int role = atoi(line);
|
||||
m_current_extrusion_role = ExtrusionRole(role);
|
||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||
++line_idx;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -233,8 +235,8 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||
if (i == -1)
|
||||
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
|
||||
buf.pos_provided[i] = true;
|
||||
new_pos[i] = parse_float(line);
|
||||
if (i == 3 && m_config->use_relative_e_distances.value)
|
||||
new_pos[i] = parse_float(line, line_end - line);
|
||||
if (i == 3 && m_use_relative_e_distances)
|
||||
new_pos[i] += m_current_pos[i];
|
||||
changed[i] = new_pos[i] != m_current_pos[i];
|
||||
eatws(line);
|
||||
@ -263,15 +265,17 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||
buf.volumetric_extrusion_rate = rate;
|
||||
buf.volumetric_extrusion_rate_start = rate;
|
||||
buf.volumetric_extrusion_rate_end = rate;
|
||||
|
||||
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||
m_stat.update(rate, sqrt(len2));
|
||||
#endif
|
||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||
if (rate < 40.f) {
|
||||
printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n",
|
||||
rate,
|
||||
int(line_idx),
|
||||
sqrt(len2), sqrt((diff[3]*diff[3])/len2),
|
||||
m_current_pos[0], m_current_pos[1], m_current_pos[2],
|
||||
rate, int(line_idx), sqrt(len2), sqrt((diff[3] * diff[3]) / len2), m_current_pos[0], m_current_pos[1], m_current_pos[2],
|
||||
new_pos[0], new_pos[1], new_pos[2]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (changed[0] || changed[1] || changed[2]) {
|
||||
// Moving without extrusion.
|
||||
@ -292,11 +296,11 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
|
||||
m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f;
|
||||
set = true;
|
||||
break;
|
||||
case 'E':
|
||||
m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
|
||||
m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f;
|
||||
set = true;
|
||||
break;
|
||||
default:
|
||||
@ -339,7 +343,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||
{
|
||||
// Activate an extruder head.
|
||||
int new_extruder = parse_int(line);
|
||||
if (new_extruder != m_current_extruder) {
|
||||
if (new_extruder != int(m_current_extruder)) {
|
||||
m_current_extruder = new_extruder;
|
||||
m_retracted = true;
|
||||
buf.type = GCODELINETYPE_TOOL_CHANGE;
|
||||
@ -354,7 +358,9 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
|
||||
|
||||
adjust_volumetric_rate();
|
||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||
++line_idx;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -370,14 +376,11 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
const char *comment = line.raw.data();
|
||||
while (*comment != ';' && *comment != 0) ++comment;
|
||||
if (*comment != ';')
|
||||
comment = NULL;
|
||||
comment = nullptr;
|
||||
|
||||
// Emit the line with lowered extrusion rates.
|
||||
float l2 = line.dist_xyz2();
|
||||
float l = sqrt(l2);
|
||||
size_t nSegments = size_t(ceil(l / m_max_segment_length));
|
||||
if (nSegments == 1) {
|
||||
// Just update this segment.
|
||||
float l = line.dist_xyz();
|
||||
if (auto nSegments = size_t(ceil(l / m_max_segment_length)); nSegments == 1) { // Just update this segment.
|
||||
push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment);
|
||||
} else {
|
||||
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
||||
@ -386,8 +389,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate;
|
||||
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
|
||||
// Limiting volumetric extrusion rate slope for this segment.
|
||||
float max_volumetric_extrusion_rate_slope = accelerating ?
|
||||
line.max_volumetric_extrusion_rate_slope_positive : line.max_volumetric_extrusion_rate_slope_negative;
|
||||
float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive :
|
||||
line.max_volumetric_extrusion_rate_slope_negative;
|
||||
// Total time for the segment, corrected for the possibly lowered volumetric feed rate,
|
||||
// if accelerating / decelerating over the complete segment.
|
||||
float t_total = line.dist_xyz() / feed_avg;
|
||||
@ -398,8 +401,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
float l_steady = 0.f;
|
||||
if (t_acc < t_total) {
|
||||
// One may achieve higher print speeds if part of the segment is not speed limited.
|
||||
float l_acc = t_acc * feed_avg;
|
||||
float l_steady = l - l_acc;
|
||||
l_acc = t_acc * feed_avg;
|
||||
l_steady = l - l_acc;
|
||||
if (l_steady < 0.5f * m_max_segment_length) {
|
||||
l_acc = l;
|
||||
l_steady = 0.f;
|
||||
@ -429,9 +432,15 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
line.pos_provided[i] = true;
|
||||
}
|
||||
push_line_to_output(line, pos_start[4], comment);
|
||||
comment = NULL;
|
||||
comment = nullptr;
|
||||
|
||||
float new_pos_start_feedrate = pos_start[4];
|
||||
|
||||
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||
memcpy(pos_start, line.pos_end, sizeof(float)*5);
|
||||
|
||||
line.pos_start[4] = new_pos_start_feedrate;
|
||||
pos_start[4] = new_pos_start_feedrate;
|
||||
}
|
||||
}
|
||||
// Split the segment into pieces.
|
||||
@ -443,7 +452,7 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
}
|
||||
// Interpolate the feed rate at the center of the segment.
|
||||
push_line_to_output(line, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
|
||||
comment = NULL;
|
||||
comment = nullptr;
|
||||
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||
}
|
||||
if (l_steady > 0.f && accelerating) {
|
||||
@ -452,60 +461,68 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||
line.pos_provided[i] = true;
|
||||
}
|
||||
push_line_to_output(line, pos_end[4], comment);
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++ i) {
|
||||
line.pos_end[i] = pos_end[i];
|
||||
line.pos_provided[i] = true;
|
||||
}
|
||||
push_line_to_output(line, pos_end[4], comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PressureEqualizer::adjust_volumetric_rate()
|
||||
{
|
||||
if (circular_buffer_items < 2)
|
||||
if (m_gcode_lines.size() < 2)
|
||||
return;
|
||||
|
||||
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||
const size_t idx_head = circular_buffer_idx_head();
|
||||
const size_t idx_tail = circular_buffer_idx_prev(circular_buffer_idx_tail());
|
||||
size_t idx = idx_tail;
|
||||
if (idx == idx_head || ! circular_buffer[idx].extruding())
|
||||
// size_t fist_line_idx = size_t(std::max<int>(0, int(m_gcode_lines.size()) - 100));
|
||||
size_t fist_line_idx = 0;
|
||||
const size_t last_line_idx = m_gcode_lines.size() - 1;
|
||||
|
||||
size_t line_idx = last_line_idx;
|
||||
if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding())
|
||||
// Nothing to do, the last move is not extruding.
|
||||
return;
|
||||
|
||||
float feedrate_per_extrusion_role[numExtrusionRoles];
|
||||
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
||||
feedrate_per_extrusion_role[i] = FLT_MAX;
|
||||
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_start;
|
||||
std::array<float, erCount> feedrate_per_extrusion_role{};
|
||||
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
||||
feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||
|
||||
bool modified = true;
|
||||
while (modified && idx != idx_head) {
|
||||
size_t idx_prev = circular_buffer_idx_prev(idx);
|
||||
for (; ! circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)) ;
|
||||
if (! circular_buffer[idx_prev].extruding())
|
||||
while (line_idx != fist_line_idx) {
|
||||
size_t idx_prev = line_idx - 1;
|
||||
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
|
||||
if (!m_gcode_lines[idx_prev].extruding())
|
||||
break;
|
||||
// Volumetric extrusion rate at the start of the succeding segment.
|
||||
float rate_succ = circular_buffer[idx].volumetric_extrusion_rate_start;
|
||||
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||
idx = idx_prev;
|
||||
GCodeLine &line = circular_buffer[idx];
|
||||
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
||||
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
|
||||
if (rate_slope == 0)
|
||||
// The negative rate is unlimited.
|
||||
continue;
|
||||
line_idx = idx_prev;
|
||||
GCodeLine &line = m_gcode_lines[line_idx];
|
||||
|
||||
for (size_t iRole = 1; iRole < erCount; ++ iRole) {
|
||||
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
|
||||
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
|
||||
continue; // The negative rate is unlimited or the rate for ExtrusionRole iRole is unlimited.
|
||||
|
||||
float rate_end = feedrate_per_extrusion_role[iRole];
|
||||
if (iRole == line.extrusion_role && rate_succ < rate_end)
|
||||
// Limit by the succeeding volumetric flow rate.
|
||||
rate_end = rate_succ;
|
||||
if (line.volumetric_extrusion_rate_end > rate_end) {
|
||||
|
||||
if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill) {
|
||||
rate_end = line.volumetric_extrusion_rate_end;
|
||||
} else if (line.volumetric_extrusion_rate_end > rate_end) {
|
||||
line.volumetric_extrusion_rate_end = rate_end;
|
||||
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
||||
line.modified = true;
|
||||
} else if (iRole == line.extrusion_role) {
|
||||
rate_end = line.volumetric_extrusion_rate_end;
|
||||
} else if (rate_end == FLT_MAX) {
|
||||
// The rate for ExtrusionRole iRole is unlimited.
|
||||
continue;
|
||||
} else {
|
||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||
}
|
||||
// modified = false;
|
||||
|
||||
float rate_start = rate_end + rate_slope * line.time_corrected();
|
||||
if (rate_start < line.volumetric_extrusion_rate_start) {
|
||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||
@ -513,47 +530,46 @@ void PressureEqualizer::adjust_volumetric_rate()
|
||||
line.volumetric_extrusion_rate_start = rate_start;
|
||||
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
||||
line.modified = true;
|
||||
// modified = true;
|
||||
}
|
||||
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
|
||||
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
|
||||
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
|
||||
}
|
||||
}
|
||||
|
||||
// Go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
||||
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
||||
feedrate_per_extrusion_role[i] = FLT_MAX;
|
||||
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end;
|
||||
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
||||
feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
|
||||
|
||||
assert(circular_buffer[idx].extruding());
|
||||
while (idx != idx_tail) {
|
||||
size_t idx_next = circular_buffer_idx_next(idx);
|
||||
for (; ! circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)) ;
|
||||
if (! circular_buffer[idx_next].extruding())
|
||||
assert(m_gcode_lines[line_idx].extruding());
|
||||
while (line_idx != last_line_idx) {
|
||||
size_t idx_next = line_idx + 1;
|
||||
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
|
||||
if (!m_gcode_lines[idx_next].extruding())
|
||||
break;
|
||||
float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end;
|
||||
float rate_prec = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
|
||||
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||
idx = idx_next;
|
||||
GCodeLine &line = circular_buffer[idx];
|
||||
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
||||
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
|
||||
if (rate_slope == 0)
|
||||
// The positive rate is unlimited.
|
||||
continue;
|
||||
line_idx = idx_next;
|
||||
GCodeLine &line = m_gcode_lines[line_idx];
|
||||
|
||||
for (size_t iRole = 1; iRole < erCount; ++ iRole) {
|
||||
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
|
||||
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
|
||||
continue; // The positive rate is unlimited or the rate for ExtrusionRole iRole is unlimited.
|
||||
|
||||
float rate_start = feedrate_per_extrusion_role[iRole];
|
||||
if (iRole == line.extrusion_role && rate_prec < rate_start)
|
||||
if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill) {
|
||||
rate_start = line.volumetric_extrusion_rate_start;
|
||||
} else if (iRole == line.extrusion_role && rate_prec < rate_start)
|
||||
rate_start = rate_prec;
|
||||
if (line.volumetric_extrusion_rate_start > rate_start) {
|
||||
line.volumetric_extrusion_rate_start = rate_start;
|
||||
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
||||
line.modified = true;
|
||||
} else if (iRole == line.extrusion_role) {
|
||||
rate_start = line.volumetric_extrusion_rate_start;
|
||||
} else if (rate_start == FLT_MAX) {
|
||||
// The rate for ExtrusionRole iRole is unlimited.
|
||||
continue;
|
||||
} else {
|
||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||
}
|
||||
float rate_end = (rate_slope == 0) ? FLT_MAX : rate_start + rate_slope * line.time_corrected();
|
||||
float rate_end = rate_start + rate_slope * line.time_corrected();
|
||||
if (rate_end < line.volumetric_extrusion_rate_end) {
|
||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||
// of ExtrusionType iRole, which was extruded before.
|
||||
@ -561,21 +577,23 @@ void PressureEqualizer::adjust_volumetric_rate()
|
||||
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
||||
line.modified = true;
|
||||
}
|
||||
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
|
||||
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
|
||||
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
|
||||
inline void PressureEqualizer::push_to_output(GCodeG1Formatter &formatter)
|
||||
{
|
||||
char buf[2048];
|
||||
int len = sprintf(buf,
|
||||
(axis == 'E') ? " %c%.3f" : " %c%.5f",
|
||||
axis, value);
|
||||
push_to_output(buf, len, add_eol);
|
||||
return this->push_to_output(formatter.string(), false);
|
||||
}
|
||||
|
||||
void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
|
||||
inline void PressureEqualizer::push_to_output(const std::string &text, bool add_eol)
|
||||
{
|
||||
return this->push_to_output(text.data(), text.size(), add_eol);
|
||||
}
|
||||
|
||||
inline void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
|
||||
{
|
||||
// New length of the output buffer content.
|
||||
size_t len_new = output_buffer_length + len + 1;
|
||||
@ -609,15 +627,25 @@ void PressureEqualizer::push_to_output(const char *text, const size_t len, bool
|
||||
|
||||
void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
|
||||
{
|
||||
push_to_output("G1", 2, false);
|
||||
for (char i = 0; i < 3; ++ i)
|
||||
if (line.pos_provided[i])
|
||||
push_axis_to_output('X'+i, line.pos_end[i]);
|
||||
push_axis_to_output('E', m_config->use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]);
|
||||
// if (line.pos_provided[4] || fabs(line.feedrate() - new_feedrate) > 1e-5)
|
||||
push_axis_to_output('F', new_feedrate);
|
||||
// output comment and EOL
|
||||
push_to_output(comment, (comment == NULL) ? 0 : strlen(comment), true);
|
||||
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
|
||||
|
||||
GCodeG1Formatter feedrate_formatter;
|
||||
feedrate_formatter.emit_f(new_feedrate);
|
||||
feedrate_formatter.emit_string(std::string(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG.length()));
|
||||
if (line.extrusion_role == erExternalPerimeter)
|
||||
feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length()));
|
||||
push_to_output(feedrate_formatter);
|
||||
|
||||
GCodeG1Formatter extrusion_formatter;
|
||||
for (size_t axis_idx = 0; axis_idx < 3; ++axis_idx)
|
||||
if (line.pos_provided[axis_idx])
|
||||
extrusion_formatter.emit_axis(char('X' + axis_idx), line.pos_end[axis_idx], GCodeFormatter::XYZF_EXPORT_DIGITS);
|
||||
extrusion_formatter.emit_axis('E', m_use_relative_e_distances ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3], GCodeFormatter::E_EXPORT_DIGITS);
|
||||
|
||||
if (comment != nullptr)
|
||||
extrusion_formatter.emit_string(std::string(comment));
|
||||
|
||||
push_to_output(extrusion_formatter);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -5,33 +5,46 @@
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct LayerResult;
|
||||
|
||||
class GCodeG1Formatter;
|
||||
|
||||
//#define PRESSURE_EQUALIZER_STATISTIC
|
||||
//#define PRESSURE_EQUALIZER_DEBUG
|
||||
|
||||
// Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions
|
||||
// between these paths to limit fast changes in the volumetric extrusion speed.
|
||||
class PressureEqualizer
|
||||
{
|
||||
public:
|
||||
PressureEqualizer(const Slic3r::GCodeConfig *config);
|
||||
~PressureEqualizer();
|
||||
|
||||
void reset();
|
||||
|
||||
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
|
||||
const char* process(const char *szGCode, bool flush);
|
||||
|
||||
size_t get_output_buffer_length() const { return output_buffer_length; }
|
||||
PressureEqualizer() = delete;
|
||||
explicit PressureEqualizer(const Slic3r::GCodeConfig &config);
|
||||
~PressureEqualizer() = default;
|
||||
|
||||
// Process a next batch of G-code lines.
|
||||
// The last LayerResult must be LayerResult::make_nop_layer_result() because it always returns GCode for the previous layer.
|
||||
// When process_layer is called for the first layer, then LayerResult::make_nop_layer_result() is returned.
|
||||
LayerResult process_layer(LayerResult &&input);
|
||||
private:
|
||||
|
||||
void process_layer(const std::string &gcode);
|
||||
|
||||
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||
struct Statistics
|
||||
{
|
||||
void reset() {
|
||||
void reset()
|
||||
{
|
||||
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
|
||||
volumetric_extrusion_rate_max = 0.f;
|
||||
volumetric_extrusion_rate_avg = 0.f;
|
||||
extrusion_length = 0.f;
|
||||
}
|
||||
void update(float volumetric_extrusion_rate, float length) {
|
||||
void update(float volumetric_extrusion_rate, float length)
|
||||
{
|
||||
volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate);
|
||||
volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate);
|
||||
volumetric_extrusion_rate_avg += volumetric_extrusion_rate * length;
|
||||
@ -44,9 +57,7 @@ private:
|
||||
};
|
||||
|
||||
struct Statistics m_stat;
|
||||
|
||||
// Keeps the reference, does not own the config.
|
||||
const Slic3r::GCodeConfig *m_config;
|
||||
#endif
|
||||
|
||||
// Private configuration values
|
||||
// How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2
|
||||
@ -54,8 +65,7 @@ private:
|
||||
float positive;
|
||||
float negative;
|
||||
};
|
||||
enum { numExtrusionRoles = erSupportMaterialInterface + 1 };
|
||||
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[numExtrusionRoles];
|
||||
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[erCount];
|
||||
float m_max_volumetric_extrusion_rate_slope_positive;
|
||||
float m_max_volumetric_extrusion_rate_slope_negative;
|
||||
// Maximum segment length to split a long segment, if the initial and the final flow rate differ.
|
||||
@ -71,9 +81,9 @@ private:
|
||||
size_t m_current_extruder;
|
||||
ExtrusionRole m_current_extrusion_role;
|
||||
bool m_retracted;
|
||||
bool m_use_relative_e_distances;
|
||||
|
||||
enum GCodeLineType
|
||||
{
|
||||
enum GCodeLineType {
|
||||
GCODELINETYPE_INVALID,
|
||||
GCODELINETYPE_NOOP,
|
||||
GCODELINETYPE_OTHER,
|
||||
@ -128,8 +138,6 @@ private:
|
||||
// or maybe the line needs to be split into multiple lines.
|
||||
bool modified;
|
||||
|
||||
// float timeStart;
|
||||
// float timeEnd;
|
||||
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
||||
float pos_start[5];
|
||||
float pos_end[5];
|
||||
@ -154,23 +162,16 @@ private:
|
||||
float max_volumetric_extrusion_rate_slope_negative;
|
||||
};
|
||||
|
||||
// Circular buffer of GCode lines. The circular buffer size will be limited to circular_buffer_size.
|
||||
std::vector<GCodeLine> circular_buffer;
|
||||
// Current position of the circular buffer (index, where to write the next line to, the line has to be pushed out before it is overwritten).
|
||||
size_t circular_buffer_pos;
|
||||
// Circular buffer size, configuration value.
|
||||
size_t circular_buffer_size;
|
||||
// Number of valid lines in the circular buffer. Lower or equal to circular_buffer_size.
|
||||
size_t circular_buffer_items;
|
||||
|
||||
// Output buffer will only grow. It will not be reallocated over and over.
|
||||
std::vector<char> output_buffer;
|
||||
size_t output_buffer_length;
|
||||
|
||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||
// For debugging purposes. Index of the G-code line processed.
|
||||
size_t line_idx;
|
||||
#endif
|
||||
|
||||
bool process_line(const char *line, const size_t len, GCodeLine &buf);
|
||||
bool process_line(const char *line, const char *line_end, GCodeLine &buf);
|
||||
void output_gcode_line(GCodeLine &buf);
|
||||
|
||||
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||
@ -178,33 +179,16 @@ private:
|
||||
void adjust_volumetric_rate();
|
||||
|
||||
// Push the text to the end of the output_buffer.
|
||||
void push_to_output(const char *text, const size_t len, bool add_eol = true);
|
||||
// Push an axis assignment to the end of the output buffer.
|
||||
void push_axis_to_output(const char axis, const float value, bool add_eol = false);
|
||||
// Push a G-code line to the output,
|
||||
void push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment);
|
||||
inline void push_to_output(GCodeG1Formatter &formatter);
|
||||
inline void push_to_output(const std::string &text, bool add_eol);
|
||||
inline void push_to_output(const char *text, size_t len, bool add_eol = true);
|
||||
// Push a G-code line to the output.
|
||||
void push_line_to_output(const GCodeLine &line, float new_feedrate, const char *comment);
|
||||
|
||||
size_t circular_buffer_idx_head() const {
|
||||
size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items;
|
||||
if (idx >= circular_buffer_size)
|
||||
idx -= circular_buffer_size;
|
||||
return idx;
|
||||
}
|
||||
public:
|
||||
std::queue<LayerResult*> m_layer_results;
|
||||
|
||||
size_t circular_buffer_idx_tail() const { return circular_buffer_pos; }
|
||||
|
||||
size_t circular_buffer_idx_prev(size_t idx) const {
|
||||
idx += circular_buffer_size - 1;
|
||||
if (idx >= circular_buffer_size)
|
||||
idx -= circular_buffer_size;
|
||||
return idx;
|
||||
}
|
||||
|
||||
size_t circular_buffer_idx_next(size_t idx) const {
|
||||
if (++ idx >= circular_buffer_size)
|
||||
idx -= circular_buffer_size;
|
||||
return idx;
|
||||
}
|
||||
std::vector<GCodeLine> m_gcode_lines;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -427,9 +427,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
|
||||
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
|
||||
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
|
||||
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
||||
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
|
||||
|
@ -103,10 +103,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"min_print_speed",
|
||||
"max_print_speed",
|
||||
"max_volumetric_speed",
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
"max_volumetric_extrusion_rate_slope_positive",
|
||||
"max_volumetric_extrusion_rate_slope_negative",
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
"notes",
|
||||
"only_retract_when_crossing_perimeters",
|
||||
"output_filename_format",
|
||||
|
@ -1802,10 +1802,10 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat);
|
||||
def->label = L("Max volumetric slope positive");
|
||||
def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. "
|
||||
def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate"
|
||||
"for a transition from lower speed to higher speed. "
|
||||
"A value of 1.8 mm³/s² ensures, that a change from the extrusion rate "
|
||||
"of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) "
|
||||
"to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds.");
|
||||
@ -1816,15 +1816,15 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat);
|
||||
def->label = L("Max volumetric slope negative");
|
||||
def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. "
|
||||
def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate"
|
||||
"for a transition from higher speed to lower speed. "
|
||||
"A value of 1.8 mm³/s² ensures, that a change from the extrusion rate "
|
||||
"of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) "
|
||||
"to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds.");
|
||||
"of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 60 mm/s) "
|
||||
"to 5.4 mm³/s (feedrate 20 mm/s) will take at least 2 seconds.");
|
||||
def->sidetext = L("mm³/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
def = this->add("min_fan_speed", coInts);
|
||||
def->label = L("Min");
|
||||
@ -4008,9 +4008,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
|
||||
#ifndef HAS_PRESSURE_EQUALIZER
|
||||
, "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
"serial_port", "serial_speed",
|
||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include <boost/preprocessor/tuple/elem.hpp>
|
||||
#include <boost/preprocessor/tuple/to_seq.hpp>
|
||||
|
||||
// #define HAS_PRESSURE_EQUALIZER
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum GCodeFlavor : unsigned char {
|
||||
@ -670,10 +668,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionString, layer_gcode))
|
||||
((ConfigOptionFloat, max_print_speed))
|
||||
((ConfigOptionFloat, max_volumetric_speed))
|
||||
//#ifdef HAS_PRESSURE_EQUALIZER
|
||||
// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive))
|
||||
// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative))
|
||||
//#endif
|
||||
((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive))
|
||||
((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative))
|
||||
((ConfigOptionPercents, retract_before_wipe))
|
||||
((ConfigOptionFloats, retract_length))
|
||||
((ConfigOptionFloats, retract_length_toolchange))
|
||||
|
@ -1611,10 +1611,10 @@ void TabPrint::build()
|
||||
optgroup = page->new_optgroup(L("Autospeed (advanced)"));
|
||||
optgroup->append_single_option_line("max_print_speed", "max-volumetric-speed_127176");
|
||||
optgroup->append_single_option_line("max_volumetric_speed", "max-volumetric-speed_127176");
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
|
||||
optgroup = page->new_optgroup(L("Pressure equalizer (experimental)"));
|
||||
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive");
|
||||
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative");
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
page = add_options_page(L("Multiple Extruders"), "funnel");
|
||||
optgroup = page->new_optgroup(L("Extruders"));
|
||||
|
Loading…
Reference in New Issue
Block a user