mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-18 04:08:02 -06:00
Porting of Pressure Equalizer feature from Prusa Slicer 2.6.x (#2161)
* Overhang perimeter handling
Updated code to handle overhang perimeters as an overhang and not as a bridge.
* Preparing to add curled extrusions identification
* Porting curling calculations from Prusa Slier 2.6.1
* Prototype 1 - slowdown extended to detect curled edges and further reduce speed
First prototype of the code submitted.
* Working prototype - 2
Code is now finally working - external perimeters are slowed down as needed when there is likelyhood of curling up.
ToDo:
1. Reslicing the model causes the algorithm not to run - need to find where this fails to trigger the call for this.
2. Slowdown of internal perimeters not working yet.
* Updated to use overhang wall speed instead of bridging speed for this algorithm
* Fixed bug in speed calculation and tweaked parameters for high speed printer
Fixed bug in speed calculation and tweaked parameters for high speed printer
* Attempting to fix "set started" not being set
* Parameter tweak after print tests
* Fixed estimation not running when model is re-sliced.
* Removing debug printf statements and fixed threading flag.
* Fixed threading
* Parameter tweaks following print tests
* Made this as an option in the GUI
* Reintroduced handling of bridges as per original design
* UI line toggling when option makes sense to be visible.
* Fixed bug in field visibility & made it default to off
* Code optimisation
* Initial commit of code from Prusa Slicer 2.6.1
* Ported ExtrusionRole from Prusa Slicer 2.6.1
* fix compile errors
* Update GCode.hpp
* code changes to invoke pressure equalizer
* attempting to trigger pressure equalizer
(Not compiling)
* Update Fill.cpp
* Update Fill.cpp
* Pressure equaliser layer result update
* Further commits
* Merged PR https://github.com/prusa3d/PrusaSlicer/pull/9622
* First complete working version
* Update PressureEqualizer.cpp
* Implemented parameter in GUI
* Toggle fields according to compatibility
* Updated UI toggles between extrusion rate slope and arc fitting.
* Updated tooltip
* Introduced parameter smoothing segment length
This parameter influences the number of division a line will undergo in response to the requirement to adhere to the extrusion rate flow adjustment.
* Internal parameter and tool tip tweaking
* Parameter and tool tip tweaking
* Updated parameters and tooltip following testing.
* Sync PressureEq with latest PrusaSlicer
* Revert "Sync PressureEq with latest PrusaSlicer"
This reverts commit 131fb94c6b
.
---------
Co-authored-by: MGunlogson <MGunlogson@users.noreply.github.com>
Co-authored-by: Vojtech Bubnik <bubnikv@gmail.com>
This commit is contained in:
parent
78a8bad6f4
commit
cf846195cc
14 changed files with 824 additions and 431 deletions
|
@ -62,6 +62,8 @@ set(lisbslic3r_sources
|
||||||
ExtrusionEntity.hpp
|
ExtrusionEntity.hpp
|
||||||
ExtrusionEntityCollection.cpp
|
ExtrusionEntityCollection.cpp
|
||||||
ExtrusionEntityCollection.hpp
|
ExtrusionEntityCollection.hpp
|
||||||
|
ExtrusionRole.cpp
|
||||||
|
ExtrusionRole.hpp
|
||||||
ExtrusionSimulator.cpp
|
ExtrusionSimulator.cpp
|
||||||
ExtrusionSimulator.hpp
|
ExtrusionSimulator.hpp
|
||||||
FileParserError.hpp
|
FileParserError.hpp
|
||||||
|
@ -128,8 +130,8 @@ set(lisbslic3r_sources
|
||||||
GCode/FanMover.hpp
|
GCode/FanMover.hpp
|
||||||
GCode/PostProcessor.cpp
|
GCode/PostProcessor.cpp
|
||||||
GCode/PostProcessor.hpp
|
GCode/PostProcessor.hpp
|
||||||
# GCode/PressureEqualizer.cpp
|
GCode/PressureEqualizer.cpp
|
||||||
# GCode/PressureEqualizer.hpp
|
GCode/PressureEqualizer.hpp
|
||||||
GCode/PrintExtents.cpp
|
GCode/PrintExtents.cpp
|
||||||
GCode/PrintExtents.hpp
|
GCode/PrintExtents.hpp
|
||||||
GCode/RetractWhenCrossingPerimeters.cpp
|
GCode/RetractWhenCrossingPerimeters.cpp
|
||||||
|
|
94
src/libslic3r/ExtrusionRole.cpp
Normal file
94
src/libslic3r/ExtrusionRole.cpp
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2023 Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
|
#include "ExtrusionRole.hpp"
|
||||||
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole.
|
||||||
|
// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer,
|
||||||
|
GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role)
|
||||||
|
{
|
||||||
|
if (role == erNone) return GCodeExtrusionRole::None;
|
||||||
|
if (role == erOverhangPerimeter) return GCodeExtrusionRole::OverhangPerimeter;
|
||||||
|
if (role == erExternalPerimeter) return GCodeExtrusionRole::ExternalPerimeter;
|
||||||
|
if (role == erPerimeter) return GCodeExtrusionRole::Perimeter;
|
||||||
|
if (role == erInternalInfill) return GCodeExtrusionRole::InternalInfill;
|
||||||
|
if (role == erSolidInfill) return GCodeExtrusionRole::SolidInfill;
|
||||||
|
if (role == erTopSolidInfill) return GCodeExtrusionRole::TopSolidInfill;
|
||||||
|
if (role == erIroning) return GCodeExtrusionRole::Ironing;
|
||||||
|
if (role == erBridgeInfill) return GCodeExtrusionRole::BridgeInfill;
|
||||||
|
if (role == erGapFill) return GCodeExtrusionRole::GapFill;
|
||||||
|
if (role == erSkirt) return GCodeExtrusionRole::Skirt;
|
||||||
|
if (role == erSupportMaterial) return GCodeExtrusionRole::SupportMaterial;
|
||||||
|
if (role == erSupportMaterialInterface) return GCodeExtrusionRole::SupportMaterialInterface;
|
||||||
|
if (role == erWipeTower) return GCodeExtrusionRole::WipeTower;
|
||||||
|
assert(false);
|
||||||
|
return GCodeExtrusionRole::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role)
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case GCodeExtrusionRole::None : return L("Unknown");
|
||||||
|
case GCodeExtrusionRole::Perimeter : return L("Perimeter");
|
||||||
|
case GCodeExtrusionRole::ExternalPerimeter : return L("External perimeter");
|
||||||
|
case GCodeExtrusionRole::OverhangPerimeter : return L("Overhang perimeter");
|
||||||
|
case GCodeExtrusionRole::InternalInfill : return L("Internal infill");
|
||||||
|
case GCodeExtrusionRole::SolidInfill : return L("Solid infill");
|
||||||
|
case GCodeExtrusionRole::TopSolidInfill : return L("Top solid infill");
|
||||||
|
case GCodeExtrusionRole::Ironing : return L("Ironing");
|
||||||
|
case GCodeExtrusionRole::BridgeInfill : return L("Bridge infill");
|
||||||
|
case GCodeExtrusionRole::GapFill : return L("Gap fill");
|
||||||
|
case GCodeExtrusionRole::Skirt : return L("Skirt/Brim");
|
||||||
|
case GCodeExtrusionRole::SupportMaterial : return L("Support material");
|
||||||
|
case GCodeExtrusionRole::SupportMaterialInterface : return L("Support material interface");
|
||||||
|
case GCodeExtrusionRole::WipeTower : return L("Wipe tower");
|
||||||
|
case GCodeExtrusionRole::Custom : return L("Custom");
|
||||||
|
default : assert(false);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role)
|
||||||
|
{
|
||||||
|
if (role == L("Perimeter"))
|
||||||
|
return GCodeExtrusionRole::Perimeter;
|
||||||
|
else if (role == L("External perimeter"))
|
||||||
|
return GCodeExtrusionRole::ExternalPerimeter;
|
||||||
|
else if (role == L("Overhang perimeter"))
|
||||||
|
return GCodeExtrusionRole::OverhangPerimeter;
|
||||||
|
else if (role == L("Internal infill"))
|
||||||
|
return GCodeExtrusionRole::InternalInfill;
|
||||||
|
else if (role == L("Solid infill"))
|
||||||
|
return GCodeExtrusionRole::SolidInfill;
|
||||||
|
else if (role == L("Top solid infill"))
|
||||||
|
return GCodeExtrusionRole::TopSolidInfill;
|
||||||
|
else if (role == L("Ironing"))
|
||||||
|
return GCodeExtrusionRole::Ironing;
|
||||||
|
else if (role == L("Bridge infill"))
|
||||||
|
return GCodeExtrusionRole::BridgeInfill;
|
||||||
|
else if (role == L("Gap fill"))
|
||||||
|
return GCodeExtrusionRole::GapFill;
|
||||||
|
else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier
|
||||||
|
return GCodeExtrusionRole::Skirt;
|
||||||
|
else if (role == L("Support material"))
|
||||||
|
return GCodeExtrusionRole::SupportMaterial;
|
||||||
|
else if (role == L("Support material interface"))
|
||||||
|
return GCodeExtrusionRole::SupportMaterialInterface;
|
||||||
|
else if (role == L("Wipe tower"))
|
||||||
|
return GCodeExtrusionRole::WipeTower;
|
||||||
|
else if (role == L("Custom"))
|
||||||
|
return GCodeExtrusionRole::Custom;
|
||||||
|
else
|
||||||
|
return GCodeExtrusionRole::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
src/libslic3r/ExtrusionRole.hpp
Normal file
83
src/libslic3r/ExtrusionRole.hpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
///|/ Copyright (c) 2023 Robert Schiele @schiele
|
||||||
|
///|/ Copyright (c) Prusa Research 2023 Vojtěch Bubník @bubnikv
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
|
#ifndef slic3r_ExtrusionRole_hpp_
|
||||||
|
#define slic3r_ExtrusionRole_hpp_
|
||||||
|
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
|
#include "ExtrusionEntity.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
enum class ExtrusionRoleModifier : uint16_t {
|
||||||
|
// 1) Extrusion types
|
||||||
|
// Perimeter (external, inner, ...)
|
||||||
|
Perimeter,
|
||||||
|
// Infill (top / bottom / solid inner / sparse inner / bridging inner ...)
|
||||||
|
Infill,
|
||||||
|
// Variable width extrusion
|
||||||
|
Thin,
|
||||||
|
// Support material extrusion
|
||||||
|
Support,
|
||||||
|
Skirt,
|
||||||
|
Wipe,
|
||||||
|
// 2) Extrusion modifiers
|
||||||
|
External,
|
||||||
|
Solid,
|
||||||
|
Ironing,
|
||||||
|
Bridge,
|
||||||
|
// 3) Special types
|
||||||
|
// Indicator that the extrusion role was mixed from multiple differing extrusion roles,
|
||||||
|
// for example from Support and SupportInterface.
|
||||||
|
Mixed,
|
||||||
|
// Stopper, there should be maximum 16 modifiers defined for uint16_t bit mask.
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
// There should be maximum 16 modifiers defined for uint16_t bit mask.
|
||||||
|
static_assert(int(ExtrusionRoleModifier::Count) <= 16, "ExtrusionRoleModifier: there must be maximum 16 modifiers defined to fit a 16 bit bitmask");
|
||||||
|
|
||||||
|
using ExtrusionRoleModifiers = enum_bitmask<ExtrusionRoleModifier>;
|
||||||
|
ENABLE_ENUM_BITMASK_OPERATORS(ExtrusionRoleModifier);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Be careful when editing this list as many parts of the code depend
|
||||||
|
// on the values of these ordinars, for example
|
||||||
|
// GCodeViewer::Extrusion_Role_Colors
|
||||||
|
enum class GCodeExtrusionRole : uint8_t {
|
||||||
|
None,
|
||||||
|
Perimeter,
|
||||||
|
ExternalPerimeter,
|
||||||
|
OverhangPerimeter,
|
||||||
|
InternalInfill,
|
||||||
|
SolidInfill,
|
||||||
|
TopSolidInfill,
|
||||||
|
Ironing,
|
||||||
|
BridgeInfill,
|
||||||
|
GapFill,
|
||||||
|
Skirt,
|
||||||
|
SupportMaterial,
|
||||||
|
SupportMaterialInterface,
|
||||||
|
WipeTower,
|
||||||
|
// Custom (user defined) G-code block, for example start / end G-code.
|
||||||
|
Custom,
|
||||||
|
// Stopper to count number of enums.
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole.
|
||||||
|
// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer,
|
||||||
|
GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role);
|
||||||
|
|
||||||
|
std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role);
|
||||||
|
GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // slic3r_ExtrusionRole_hpp_
|
|
@ -1873,14 +1873,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||||
|
|
||||||
if (print.config().spiral_mode.value)
|
if (print.config().spiral_mode.value)
|
||||||
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 ||
|
if (print.config().max_volumetric_extrusion_rate_slope.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;
|
||||||
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
|
} else
|
||||||
#else /* HAS_PRESSURE_EQUALIZER */
|
m_enable_extrusion_role_markers = false;
|
||||||
m_enable_extrusion_role_markers = false;
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
|
|
||||||
file.write_format("; HEADER_BLOCK_START\n");
|
file.write_format("; HEADER_BLOCK_START\n");
|
||||||
// Write information on the generator.
|
// Write information on the generator.
|
||||||
|
@ -2388,10 +2387,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||||
file.write("M1003 S0\n");
|
file.write("M1003 S0\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
if (m_pressure_equalizer)
|
|
||||||
file.write(m_pressure_equalizer->process("", true));
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
++ finished_objects;
|
++ finished_objects;
|
||||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||||
// Reset it when starting another object from 1st layer.
|
// Reset it when starting another object from 1st layer.
|
||||||
|
@ -2459,10 +2454,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||||
file.write("M1003 S0\n");
|
file.write("M1003 S0\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
if (m_pressure_equalizer)
|
|
||||||
file.write(m_pressure_equalizer->process("", true));
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
if (m_wipe_tower)
|
if (m_wipe_tower)
|
||||||
// Purge the extruder, pull out the active filament.
|
// Purge the extruder, pull out the active filament.
|
||||||
file.write(m_wipe_tower->finalize(*this));
|
file.write(m_wipe_tower->finalize(*this));
|
||||||
|
@ -2595,11 +2586,18 @@ void GCode::process_layers(
|
||||||
{
|
{
|
||||||
// The pipeline is variable: The vase mode filter is optional.
|
// The pipeline is variable: The vase mode filter is optional.
|
||||||
size_t layer_to_print_idx = 0;
|
size_t layer_to_print_idx = 0;
|
||||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
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) -> GCode::LayerResult {
|
[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 (layer_to_print_idx >= layers_to_print.size()) {
|
||||||
fc.stop();
|
if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) {
|
||||||
return {};
|
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 {
|
} else {
|
||||||
const std::pair<coordf_t, std::vector<LayerToPrint>>& layer = layers_to_print[layer_to_print_idx++];
|
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);
|
const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||||
|
@ -2612,13 +2610,23 @@ 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));
|
return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const auto spiral_mode = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
|
||||||
[&spiral_mode = *this->m_spiral_vase.get()](GCode::LayerResult in) -> GCode::LayerResult {
|
const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[&spiral_mode = *this->m_spiral_vase.get()](LayerResult in) -> LayerResult {
|
||||||
|
if (in.nop_layer_result)
|
||||||
|
return in;
|
||||||
|
|
||||||
spiral_mode.enable(in.spiral_vase_enable);
|
spiral_mode.enable(in.spiral_vase_enable);
|
||||||
return { spiral_mode.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
return { spiral_mode.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,
|
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
[pressure_equalizer = this->m_pressure_equalizer.get()](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.get()](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);
|
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||||
});
|
});
|
||||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
@ -2627,6 +2635,7 @@ void GCode::process_layers(
|
||||||
|
|
||||||
const auto fan_mover = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
const auto fan_mover = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string {
|
[&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string {
|
||||||
|
|
||||||
CNumericLocalesSetter locales_setter;
|
CNumericLocalesSetter locales_setter;
|
||||||
|
|
||||||
if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) {
|
if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) {
|
||||||
|
@ -2645,10 +2654,14 @@ void GCode::process_layers(
|
||||||
});
|
});
|
||||||
|
|
||||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||||
if (m_spiral_vase)
|
if (m_spiral_vase && m_pressure_equalizer)
|
||||||
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & fan_mover & output);
|
tbb::parallel_pipeline(12, generator & spiral_mode & pressure_equalizer & cooling & fan_mover & output);
|
||||||
|
else if (m_spiral_vase)
|
||||||
|
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & fan_mover & output);
|
||||||
|
else if (m_pressure_equalizer)
|
||||||
|
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & fan_mover & output);
|
||||||
else
|
else
|
||||||
tbb::parallel_pipeline(12, generator & cooling & fan_mover & output);
|
tbb::parallel_pipeline(12, generator & cooling & fan_mover & output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
||||||
|
@ -2665,8 +2678,8 @@ void GCode::process_layers(
|
||||||
{
|
{
|
||||||
// The pipeline is variable: The vase mode filter is optional.
|
// The pipeline is variable: The vase mode filter is optional.
|
||||||
size_t layer_to_print_idx = 0;
|
size_t layer_to_print_idx = 0;
|
||||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
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, prime_extruder](tbb::flow_control& fc) -> GCode::LayerResult {
|
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx, prime_extruder](tbb::flow_control& fc) -> LayerResult {
|
||||||
if (layer_to_print_idx == layers_to_print.size()) {
|
if (layer_to_print_idx == layers_to_print.size()) {
|
||||||
fc.stop();
|
fc.stop();
|
||||||
return {};
|
return {};
|
||||||
|
@ -2679,13 +2692,13 @@ void GCode::process_layers(
|
||||||
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, prime_extruder);
|
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, prime_extruder);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const auto spiral_mode = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[&spiral_mode = *this->m_spiral_vase.get()](GCode::LayerResult in)->GCode::LayerResult {
|
[&spiral_mode = *this->m_spiral_vase.get()](LayerResult in)->LayerResult {
|
||||||
spiral_mode.enable(in.spiral_vase_enable);
|
spiral_mode.enable(in.spiral_vase_enable);
|
||||||
return { spiral_mode.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
return { spiral_mode.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,
|
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
|
[&cooling_buffer = *this->m_cooling_buffer.get()](LayerResult in)->std::string {
|
||||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||||
});
|
});
|
||||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
@ -3216,7 +3229,7 @@ inline std::string get_instance_name(const PrintObject *object, const PrintInsta
|
||||||
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
|
// 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
|
// For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
|
||||||
// and performing the extruder specific extrusions together.
|
// and performing the extruder specific extrusions together.
|
||||||
GCode::LayerResult GCode::process_layer(
|
LayerResult GCode::process_layer(
|
||||||
const Print &print,
|
const Print &print,
|
||||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
|
@ -3255,7 +3268,7 @@ GCode::LayerResult GCode::process_layer(
|
||||||
else if (support_layer != nullptr)
|
else if (support_layer != nullptr)
|
||||||
layer_ptr = support_layer;
|
layer_ptr = support_layer;
|
||||||
const Layer& layer = *layer_ptr;
|
const Layer& layer = *layer_ptr;
|
||||||
GCode::LayerResult result { {}, layer.id(), false, last_layer };
|
LayerResult result { {}, layer.id(), false, last_layer };
|
||||||
if (layer_tools.extruders.empty())
|
if (layer_tools.extruders.empty())
|
||||||
// Nothing to extrude.
|
// Nothing to extrude.
|
||||||
return result;
|
return result;
|
||||||
|
@ -3878,14 +3891,6 @@ 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).
|
// 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);
|
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);
|
file.write(gcode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,13 @@
|
||||||
#include "libslic3r/ObjectID.hpp"
|
#include "libslic3r/ObjectID.hpp"
|
||||||
#include "GCode/ExtrusionProcessor.hpp"
|
#include "GCode/ExtrusionProcessor.hpp"
|
||||||
|
|
||||||
|
#include "GCode/PressureEqualizer.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
#include "GCode/PressureEqualizer.hpp"
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
// Forward declarations.
|
// Forward declarations.
|
||||||
|
@ -138,6 +136,20 @@ public:
|
||||||
static const std::vector<std::string>& get() { return Colors; }
|
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 {
|
class GCode {
|
||||||
public:
|
public:
|
||||||
GCode() :
|
GCode() :
|
||||||
|
@ -285,14 +297,6 @@ private:
|
||||||
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
|
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);
|
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(
|
LayerResult process_layer(
|
||||||
const Print &print,
|
const Print &print,
|
||||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||||
|
@ -511,9 +515,9 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||||
std::unique_ptr<SpiralVase> m_spiral_vase;
|
std::unique_ptr<SpiralVase> m_spiral_vase;
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
|
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
|
||||||
|
|
||||||
// Heights (print_z) at which the skirt has already been extruded.
|
// Heights (print_z) at which the skirt has already been extruded.
|
||||||
|
@ -572,6 +576,7 @@ private:
|
||||||
|
|
||||||
friend class Wipe;
|
friend class Wipe;
|
||||||
friend class WipeTowerIntegration;
|
friend class WipeTowerIntegration;
|
||||||
|
friend class PressureEqualizer;
|
||||||
friend class Print;
|
friend class Print;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,139 +1,218 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena
|
||||||
|
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <float.h>
|
#include <cfloat>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
#include "../LocalesUtils.hpp"
|
#include "../LocalesUtils.hpp"
|
||||||
|
#include "../GCode.hpp"
|
||||||
|
|
||||||
#include "PressureEqualizer.hpp"
|
#include "PressureEqualizer.hpp"
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include "GCodeWriter.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
|
static const std::string EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:";
|
||||||
m_config(config)
|
static const std::string EXTRUDE_END_TAG = ";_EXTRUDE_END";
|
||||||
{
|
static const std::string EXTRUDE_SET_SPEED_TAG = ";_EXTRUDE_SET_SPEED";
|
||||||
reset();
|
static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER";
|
||||||
}
|
|
||||||
|
|
||||||
PressureEqualizer::~PressureEqualizer()
|
// For how many GCode lines back will adjust a flow rate from the latest line.
|
||||||
{
|
// Bigger values affect the GCode export speed a lot, and smaller values could
|
||||||
}
|
// affect how distant will be propagated a flow rate adjustment.
|
||||||
|
static constexpr int max_look_back_limit = 128;
|
||||||
|
|
||||||
void PressureEqualizer::reset()
|
// Max non-extruding XY distance (travel move) in mm between two continous extrusions where we pretend
|
||||||
{
|
// its all one continous extruded line. Above this distance we assume extruder pressure hits 0
|
||||||
circular_buffer_pos = 0;
|
// This exists because often there's tiny travel moves between stuff like infill
|
||||||
circular_buffer_size = 100;
|
// lines where some extruder pressure will remain (so we should equalize between these small travels)
|
||||||
circular_buffer_items = 0;
|
static constexpr long max_ignored_gap_between_extruding_segments = 3;
|
||||||
circular_buffer.assign(circular_buffer_size, GCodeLine());
|
|
||||||
|
|
||||||
|
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value)
|
||||||
|
{
|
||||||
// Preallocate some data, so that output_buffer.data() will return an empty string.
|
// Preallocate some data, so that output_buffer.data() will return an empty string.
|
||||||
output_buffer.assign(32, 0);
|
output_buffer.assign(32, 0);
|
||||||
output_buffer_length = 0;
|
output_buffer_length = 0;
|
||||||
|
output_buffer_prev_length = 0;
|
||||||
|
|
||||||
m_current_extruder = 0;
|
m_current_extruder = 0;
|
||||||
// Zero the position of the XYZE axes + the current feed
|
// Zero the position of the XYZE axes + the current feed
|
||||||
memset(m_current_pos, 0, sizeof(float) * 5);
|
memset(m_current_pos, 0, sizeof(float) * 5);
|
||||||
m_current_extrusion_role = erNone;
|
m_current_extrusion_role = GCodeExtrusionRole::None;
|
||||||
// Expect the first command to fill the nozzle (deretract).
|
// Expect the first command to fill the nozzle (deretract).
|
||||||
m_retracted = true;
|
m_retracted = true;
|
||||||
|
|
||||||
|
m_max_segment_length = 2.f;
|
||||||
|
|
||||||
// Calculate filamet crossections for the multiple extruders.
|
// Calculate filamet crossections for the multiple extruders.
|
||||||
m_filament_crossections.clear();
|
m_filament_crossections.clear();
|
||||||
for (size_t i = 0; i < m_config->filament_diameter.values.size(); ++ i) {
|
for (double r : config.filament_diameter.values) {
|
||||||
double r = m_config->filament_diameter.values[i];
|
double a = 0.25f * M_PI * r * r;
|
||||||
double a = 0.25f*M_PI*r*r;
|
|
||||||
m_filament_crossections.push_back(float(a));
|
m_filament_crossections.push_back(float(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_max_segment_length = 20.f;
|
|
||||||
// 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 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
|
// 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
|
// 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;
|
if(config.max_volumetric_extrusion_rate_slope.value > 0){
|
||||||
m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f :
|
m_max_volumetric_extrusion_rate_slope_positive = float(config.max_volumetric_extrusion_rate_slope.value) * 60.f * 60.f;
|
||||||
m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
|
m_max_volumetric_extrusion_rate_slope_negative = float(config.max_volumetric_extrusion_rate_slope.value) * 60.f * 60.f;
|
||||||
|
m_max_segment_length = float(config.max_volumetric_extrusion_rate_slope_segment_length.value);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't regulate the pressure in infill.
|
for (ExtrusionRateSlope &extrusion_rate_slope : m_max_volumetric_extrusion_rate_slopes) {
|
||||||
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].negative = 0;
|
extrusion_rate_slope.negative = m_max_volumetric_extrusion_rate_slope_negative;
|
||||||
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].positive = 0;
|
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
|
||||||
// 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 before and after ironing.
|
||||||
|
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::Ironing}) {
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0;
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
|
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||||
m_stat.reset();
|
m_stat.reset();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
line_idx = 0;
|
line_idx = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* PressureEqualizer::process(const char *szGCode, bool flush)
|
void PressureEqualizer::process_layer(const std::string &gcode)
|
||||||
{
|
{
|
||||||
// Reset length of the output_buffer.
|
if (!gcode.empty()) {
|
||||||
output_buffer_length = 0;
|
const char *gcode_begin = gcode.c_str();
|
||||||
|
while (*gcode_begin != 0) {
|
||||||
if (szGCode != 0) {
|
|
||||||
const char *p = szGCode;
|
|
||||||
while (*p != 0) {
|
|
||||||
// Find end of the line.
|
// Find end of the line.
|
||||||
const char *endl = p;
|
const char *gcode_end = gcode_begin;
|
||||||
// Slic3r always generates end of lines in a Unix style.
|
// Slic3r always generates end of lines in a Unix style.
|
||||||
for (; *endl != 0 && *endl != '\n'; ++ endl) ;
|
for (; *gcode_end != 0 && *gcode_end != '\n'; ++gcode_end);
|
||||||
if (circular_buffer_items == circular_buffer_size)
|
|
||||||
// Buffer is full. Push out the oldest line.
|
m_gcode_lines.emplace_back();
|
||||||
output_gcode_line(circular_buffer[circular_buffer_pos]);
|
if (!this->process_line(gcode_begin, gcode_end, m_gcode_lines.back())) {
|
||||||
else
|
// The line has to be forgotten. It contains comment marks, which shall be filtered out of the target g-code.
|
||||||
++ circular_buffer_items;
|
m_gcode_lines.pop_back();
|
||||||
// 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;
|
gcode_begin = gcode_end;
|
||||||
if (*p == '\n')
|
if (*gcode_begin == '\n')
|
||||||
++ p;
|
++gcode_begin;
|
||||||
|
}
|
||||||
|
assert(!this->opened_extrude_set_speed_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point, we have an entire layer of gcode lines loaded into m_gcode_lines
|
||||||
|
// now we will split the mix of travels and extrudes into segments of continous extrusion and process those
|
||||||
|
// We skip over large travels, and pretend small ones are part of a continous extrusion segment
|
||||||
|
long idx_end_current_extrusion = 0;
|
||||||
|
while (idx_end_current_extrusion < m_gcode_lines.size()) {
|
||||||
|
// find beginning of next extrusion segment from current pos
|
||||||
|
const long idx_begin_current_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
|
||||||
|
[](GCodeLine line) { return line.extruding(); }) - m_gcode_lines.begin();
|
||||||
|
// (extrusion begin idx = extrusion end idx) here because we start with extrusion length of zero
|
||||||
|
idx_end_current_extrusion = idx_begin_current_extrusion;
|
||||||
|
|
||||||
|
// inner loop extends the extrusion segment over small travel moves
|
||||||
|
while (idx_end_current_extrusion < m_gcode_lines.size()) {
|
||||||
|
// find end of the current extrusion segment
|
||||||
|
const auto just_after_end_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
|
||||||
|
[](GCodeLine line) { return !line.extruding(); });
|
||||||
|
idx_end_current_extrusion = std::max<long>(0,(just_after_end_extrusion - m_gcode_lines.begin()) - 1);
|
||||||
|
const long idx_begin_segment_continuation = advance_segment_beyond_small_gap(idx_end_current_extrusion);
|
||||||
|
if (idx_begin_segment_continuation > idx_end_current_extrusion) {
|
||||||
|
// extend the continous line over the small gap
|
||||||
|
idx_end_current_extrusion = idx_begin_segment_continuation;
|
||||||
|
continue; // keep going, loop again to find new end of extrusion segment
|
||||||
|
} else {
|
||||||
|
// gap to next extrude is too big, stop looking forward. We've found end of this segment
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now run the pressure equalizer across the segment like a streamroller
|
||||||
|
// it operates on a sliding window that moves forward across gcode line by line
|
||||||
|
for (int i = idx_begin_current_extrusion; i < idx_end_current_extrusion; ++i) {
|
||||||
|
// feed pressure equalizer past lines, going back to max_look_back_limit (or start of segment)
|
||||||
|
const auto start_idx = std::max<long>(idx_begin_current_extrusion, i - max_look_back_limit);
|
||||||
|
adjust_volumetric_rate(start_idx, i);
|
||||||
|
}
|
||||||
|
// current extrusion is all done processing so advance beyond it for next loop
|
||||||
|
idx_end_current_extrusion++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long PressureEqualizer::advance_segment_beyond_small_gap(const long idx_orig)
|
||||||
|
{
|
||||||
|
// this should only be run on the last extruding line before a gap
|
||||||
|
assert(m_gcode_lines[idx_cur_pos].extruding());
|
||||||
|
double distance_traveled = 0.0;
|
||||||
|
// start at beginning of gap, advance till extrusion found or gap too big
|
||||||
|
for (auto idx_cur_pos = idx_orig + 1; idx_cur_pos < m_gcode_lines.size(); idx_cur_pos++) {
|
||||||
|
// started extruding again! return segment extension
|
||||||
|
if (m_gcode_lines[idx_cur_pos].extruding()) {
|
||||||
|
return idx_cur_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
distance_traveled += m_gcode_lines[idx_cur_pos].dist_xy();
|
||||||
|
// gap too big, dont extend segment
|
||||||
|
if (distance_traveled > max_ignored_gap_between_extruding_segments) {
|
||||||
|
return idx_orig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// looped until end of layer and couldn't extend extrusion
|
||||||
|
return idx_orig;
|
||||||
|
}
|
||||||
|
|
||||||
if (flush) {
|
LayerResult PressureEqualizer::process_layer(LayerResult &&input)
|
||||||
// Flush the remaining valid lines of the circular buffer.
|
{
|
||||||
for (size_t idx = circular_buffer_idx_head(); circular_buffer_items > 0; -- circular_buffer_items) {
|
const bool is_first_layer = m_layer_results.empty();
|
||||||
output_gcode_line(circular_buffer[idx]);
|
const size_t next_layer_first_idx = m_gcode_lines.size();
|
||||||
if (++ idx == circular_buffer_size)
|
|
||||||
idx = 0;
|
|
||||||
}
|
|
||||||
// Reset the index pointer.
|
|
||||||
assert(circular_buffer_items == 0);
|
|
||||||
circular_buffer_pos = 0;
|
|
||||||
|
|
||||||
#if 1
|
if (!input.nop_layer_result) {
|
||||||
printf("Statistics: \n");
|
this->process_layer(input.gcode);
|
||||||
printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min);
|
input.gcode.clear(); // GCode is already processed, so it isn't needed to store it.
|
||||||
printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max);
|
m_layer_results.emplace(new LayerResult(input));
|
||||||
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();
|
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;
|
||||||
|
output_buffer_prev_length = 0;
|
||||||
|
for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx)
|
||||||
|
output_gcode_line(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?
|
// Is a white space?
|
||||||
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
|
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
|
||||||
// Is it an end of line? Consider a comment to be an end of line as well.
|
// Is it an end of line? Consider a comment to be an end of line as well.
|
||||||
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; };
|
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; }
|
||||||
// Is it a white space or end of line?
|
// Is it a white space or end of line?
|
||||||
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); };
|
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); }
|
||||||
|
|
||||||
// Eat whitespaces.
|
// Eat whitespaces.
|
||||||
static void eatws(const char *&line)
|
static void eatws(const char *&line)
|
||||||
|
@ -146,35 +225,45 @@ static void eatws(const char *&line)
|
||||||
// If succeeded, the line pointer is advanced.
|
// If succeeded, the line pointer is advanced.
|
||||||
static inline int parse_int(const char *&line)
|
static inline int parse_int(const char *&line)
|
||||||
{
|
{
|
||||||
char *endptr = NULL;
|
char *endptr = nullptr;
|
||||||
long result = strtol(line, &endptr, 10);
|
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");
|
throw Slic3r::InvalidArgument("PressureEqualizer: Error parsing an int");
|
||||||
line = endptr;
|
line = endptr;
|
||||||
return int(result);
|
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.
|
// Parse an int starting at the current position of a line.
|
||||||
// If succeeded, the line pointer is advanced.
|
// 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;
|
size_t endptr = 0;
|
||||||
float result = string_to_double_decimal_point(line, &endptr);
|
auto result = string_to_float_decimal_point(line, line_length, &endptr);
|
||||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
if (endptr == 0 || !is_ws_or_eol(*(line + endptr)))
|
||||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
||||||
line = endptr;
|
line = line + endptr;
|
||||||
return result;
|
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:";
|
const size_t len = line_end - line;
|
||||||
|
if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG.length()) == 0) {
|
||||||
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
|
line += EXTRUSION_ROLE_TAG.length();
|
||||||
line += strlen(EXTRUSION_ROLE_TAG);
|
|
||||||
int role = atoi(line);
|
int role = atoi(line);
|
||||||
m_current_extrusion_role = ExtrusionRole(role);
|
m_current_extrusion_role = GCodeExtrusionRole(role);
|
||||||
++ line_idx;
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
|
++line_idx;
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,21 +288,43 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
|
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
|
||||||
buf.extrusion_role = m_current_extrusion_role;
|
buf.extrusion_role = m_current_extrusion_role;
|
||||||
|
|
||||||
|
std::string str_line(line, line_end);
|
||||||
|
const bool found_extrude_set_speed_tag = boost::contains(str_line, EXTRUDE_SET_SPEED_TAG);
|
||||||
|
const bool found_extrude_end_tag = boost::contains(str_line, EXTRUDE_END_TAG);
|
||||||
|
assert(!found_extrude_set_speed_tag || !found_extrude_end_tag);
|
||||||
|
|
||||||
|
if (found_extrude_set_speed_tag)
|
||||||
|
this->opened_extrude_set_speed_block = true;
|
||||||
|
else if (found_extrude_end_tag)
|
||||||
|
this->opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
// Parse the G-code line, store the result into the buf.
|
// Parse the G-code line, store the result into the buf.
|
||||||
switch (toupper(*line ++)) {
|
switch (toupper(*line ++)) {
|
||||||
case 'G': {
|
case 'G': {
|
||||||
int gcode = parse_int(line);
|
int gcode = -1;
|
||||||
|
try {
|
||||||
|
gcode = parse_int(line);
|
||||||
|
} catch (Slic3r::InvalidArgument &) {
|
||||||
|
// Ignore invalid GCodes.
|
||||||
|
eatws(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(gcode != -1);
|
||||||
eatws(line);
|
eatws(line);
|
||||||
switch (gcode) {
|
switch (gcode) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
// G0, G1: A FFF 3D printer does not make a difference between the two.
|
// G0, G1: A FFF 3D printer does not make a difference between the two.
|
||||||
|
buf.adjustable_flow = this->opened_extrude_set_speed_block;
|
||||||
|
buf.extrude_set_speed_tag = found_extrude_set_speed_tag;
|
||||||
|
buf.extrude_end_tag = found_extrude_end_tag;
|
||||||
float new_pos[5];
|
float new_pos[5];
|
||||||
memcpy(new_pos, m_current_pos, sizeof(float)*5);
|
memcpy(new_pos, m_current_pos, sizeof(float)*5);
|
||||||
bool changed[5] = { false, false, false, false, false };
|
bool changed[5] = { false, false, false, false, false };
|
||||||
while (!is_eol(*line)) {
|
while (!is_eol(*line)) {
|
||||||
char axis = toupper(*line++);
|
const char axis = toupper(*line++);
|
||||||
int i = -1;
|
int i = -1;
|
||||||
switch (axis) {
|
switch (axis) {
|
||||||
case 'X':
|
case 'X':
|
||||||
|
@ -228,16 +339,16 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
i = 4;
|
i = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
break;
|
||||||
|
}
|
||||||
|
if (i != -1) {
|
||||||
|
buf.pos_provided[i] = true;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
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] += m_current_pos[i];
|
|
||||||
changed[i] = new_pos[i] != m_current_pos[i];
|
|
||||||
eatws(line);
|
|
||||||
}
|
}
|
||||||
if (changed[3]) {
|
if (changed[3]) {
|
||||||
// Extrusion, retract or unretract.
|
// Extrusion, retract or unretract.
|
||||||
|
@ -263,15 +374,17 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
buf.volumetric_extrusion_rate = rate;
|
buf.volumetric_extrusion_rate = rate;
|
||||||
buf.volumetric_extrusion_rate_start = rate;
|
buf.volumetric_extrusion_rate_start = rate;
|
||||||
buf.volumetric_extrusion_rate_end = rate;
|
buf.volumetric_extrusion_rate_end = rate;
|
||||||
|
|
||||||
|
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||||
m_stat.update(rate, sqrt(len2));
|
m_stat.update(rate, sqrt(len2));
|
||||||
|
#endif
|
||||||
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
if (rate < 40.f) {
|
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",
|
printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n",
|
||||||
rate,
|
rate, int(line_idx), sqrt(len2), sqrt((diff[3] * diff[3]) / len2), m_current_pos[0], m_current_pos[1], m_current_pos[2],
|
||||||
int(line_idx),
|
new_pos[0], new_pos[1], new_pos[2]);
|
||||||
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]) {
|
} else if (changed[0] || changed[1] || changed[2]) {
|
||||||
// Moving without extrusion.
|
// Moving without extrusion.
|
||||||
|
@ -285,26 +398,22 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
// G92 : Set Position
|
// G92 : Set Position
|
||||||
// Set a logical coordinate position to a new value without actually moving the machine motors.
|
// Set a logical coordinate position to a new value without actually moving the machine motors.
|
||||||
// Which axes to set?
|
// Which axes to set?
|
||||||
bool set = false;
|
|
||||||
while (!is_eol(*line)) {
|
while (!is_eol(*line)) {
|
||||||
char axis = toupper(*line++);
|
const char axis = toupper(*line++);
|
||||||
switch (axis) {
|
switch (axis) {
|
||||||
case 'X':
|
case 'X':
|
||||||
case 'Y':
|
case 'Y':
|
||||||
case 'Z':
|
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;
|
break;
|
||||||
case 'E':
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
|
break;
|
||||||
}
|
}
|
||||||
eatws(line);
|
eatws(line);
|
||||||
}
|
}
|
||||||
assert(set);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 10:
|
case 10:
|
||||||
|
@ -326,20 +435,24 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'M': {
|
case 'M': {
|
||||||
int mcode = parse_int(line);
|
|
||||||
eatws(line);
|
eatws(line);
|
||||||
switch (mcode) {
|
// Ignore the rest of the M-codes.
|
||||||
default:
|
|
||||||
// Ignore the rest of the M-codes.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'T':
|
case 'T':
|
||||||
{
|
{
|
||||||
// Activate an extruder head.
|
// Activate an extruder head.
|
||||||
int new_extruder = parse_int(line);
|
int new_extruder = -1;
|
||||||
if (new_extruder != m_current_extruder) {
|
try {
|
||||||
|
new_extruder = parse_int(line);
|
||||||
|
} catch (Slic3r::InvalidArgument &) {
|
||||||
|
// Ignore invalid GCodes starting with T.
|
||||||
|
eatws(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(new_extruder != -1);
|
||||||
|
|
||||||
|
if (new_extruder != int(m_current_extruder)) {
|
||||||
m_current_extruder = new_extruder;
|
m_current_extruder = new_extruder;
|
||||||
m_retracted = true;
|
m_retracted = true;
|
||||||
buf.type = GCODELINETYPE_TOOL_CHANGE;
|
buf.type = GCODELINETYPE_TOOL_CHANGE;
|
||||||
|
@ -352,15 +465,16 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
||||||
|
|
||||||
buf.extruder_id = m_current_extruder;
|
buf.extruder_id = m_current_extruder;
|
||||||
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
|
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
|
||||||
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
adjust_volumetric_rate();
|
++line_idx;
|
||||||
++ line_idx;
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
void PressureEqualizer::output_gcode_line(const size_t line_idx)
|
||||||
{
|
{
|
||||||
if (! line.modified) {
|
GCodeLine &line = m_gcode_lines[line_idx];
|
||||||
|
if (!line.modified) {
|
||||||
push_to_output(line.raw.data(), line.raw_length, true);
|
push_to_output(line.raw.data(), line.raw_length, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -370,15 +484,12 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
const char *comment = line.raw.data();
|
const char *comment = line.raw.data();
|
||||||
while (*comment != ';' && *comment != 0) ++comment;
|
while (*comment != ';' && *comment != 0) ++comment;
|
||||||
if (*comment != ';')
|
if (*comment != ';')
|
||||||
comment = NULL;
|
comment = nullptr;
|
||||||
|
|
||||||
// Emit the line with lowered extrusion rates.
|
// Emit the line with lowered extrusion rates.
|
||||||
float l2 = line.dist_xyz2();
|
float l = line.dist_xyz();
|
||||||
float l = sqrt(l2);
|
if (auto nSegments = size_t(ceil(l / m_max_segment_length)); nSegments == 1) { // Just update this segment.
|
||||||
size_t nSegments = size_t(ceil(l / m_max_segment_length));
|
push_line_to_output(line_idx, line.feedrate() * line.volumetric_correction_avg(), comment);
|
||||||
if (nSegments == 1) {
|
|
||||||
// Just update this segment.
|
|
||||||
push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment);
|
|
||||||
} else {
|
} else {
|
||||||
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
||||||
// Update the initial and final feed rate values.
|
// Update the initial and final feed rate values.
|
||||||
|
@ -386,8 +497,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;
|
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]);
|
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
|
||||||
// Limiting volumetric extrusion rate slope for this segment.
|
// Limiting volumetric extrusion rate slope for this segment.
|
||||||
float max_volumetric_extrusion_rate_slope = accelerating ?
|
float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive :
|
||||||
line.max_volumetric_extrusion_rate_slope_positive : line.max_volumetric_extrusion_rate_slope_negative;
|
line.max_volumetric_extrusion_rate_slope_negative;
|
||||||
// Total time for the segment, corrected for the possibly lowered volumetric feed rate,
|
// Total time for the segment, corrected for the possibly lowered volumetric feed rate,
|
||||||
// if accelerating / decelerating over the complete segment.
|
// if accelerating / decelerating over the complete segment.
|
||||||
float t_total = line.dist_xyz() / feed_avg;
|
float t_total = line.dist_xyz() / feed_avg;
|
||||||
|
@ -398,8 +509,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
float l_steady = 0.f;
|
float l_steady = 0.f;
|
||||||
if (t_acc < t_total) {
|
if (t_acc < t_total) {
|
||||||
// One may achieve higher print speeds if part of the segment is not speed limited.
|
// One may achieve higher print speeds if part of the segment is not speed limited.
|
||||||
float l_acc = t_acc * feed_avg;
|
l_acc = t_acc * feed_avg;
|
||||||
float l_steady = l - l_acc;
|
l_steady = l - l_acc;
|
||||||
if (l_steady < 0.5f * m_max_segment_length) {
|
if (l_steady < 0.5f * m_max_segment_length) {
|
||||||
l_acc = l;
|
l_acc = l;
|
||||||
l_steady = 0.f;
|
l_steady = 0.f;
|
||||||
|
@ -407,10 +518,10 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
nSegments = size_t(ceil(l_acc / m_max_segment_length));
|
nSegments = size_t(ceil(l_acc / m_max_segment_length));
|
||||||
}
|
}
|
||||||
float pos_start[5];
|
float pos_start[5];
|
||||||
float pos_end [5];
|
float pos_end[5];
|
||||||
float pos_end2 [4];
|
float pos_end2[4];
|
||||||
memcpy(pos_start, line.pos_start, sizeof(float)*5);
|
memcpy(pos_start, line.pos_start, sizeof(float) * 5);
|
||||||
memcpy(pos_end , line.pos_end , sizeof(float)*5);
|
memcpy(pos_end, line.pos_end, sizeof(float) * 5);
|
||||||
if (l_steady > 0.f) {
|
if (l_steady > 0.f) {
|
||||||
// There will be a steady feed segment emitted.
|
// There will be a steady feed segment emitted.
|
||||||
if (accelerating) {
|
if (accelerating) {
|
||||||
|
@ -428,10 +539,16 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
||||||
line.pos_provided[i] = true;
|
line.pos_provided[i] = true;
|
||||||
}
|
}
|
||||||
push_line_to_output(line, pos_start[4], comment);
|
push_line_to_output(line_idx, 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(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
memcpy(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.
|
// Split the segment into pieces.
|
||||||
|
@ -442,8 +559,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
line.pos_provided[j] = true;
|
line.pos_provided[j] = true;
|
||||||
}
|
}
|
||||||
// Interpolate the feed rate at the center of the segment.
|
// 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);
|
push_line_to_output(line_idx, 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);
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
}
|
}
|
||||||
if (l_steady > 0.f && accelerating) {
|
if (l_steady > 0.f && accelerating) {
|
||||||
|
@ -451,136 +568,161 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
line.pos_end[i] = pos_end2[i];
|
line.pos_end[i] = pos_end2[i];
|
||||||
line.pos_provided[i] = true;
|
line.pos_provided[i] = true;
|
||||||
}
|
}
|
||||||
push_line_to_output(line, pos_end[4], comment);
|
push_line_to_output(line_idx, 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_idx, pos_end[4], comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::adjust_volumetric_rate()
|
void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const size_t last_line_idx)
|
||||||
{
|
{
|
||||||
if (circular_buffer_items < 2)
|
// don't bother adjusting volumetric rate if there's no gcode to adjust
|
||||||
|
if (last_line_idx-fist_line_idx < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
size_t line_idx = last_line_idx;
|
||||||
const size_t idx_head = circular_buffer_idx_head();
|
if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding())
|
||||||
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())
|
|
||||||
// Nothing to do, the last move is not extruding.
|
// Nothing to do, the last move is not extruding.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float feedrate_per_extrusion_role[numExtrusionRoles];
|
std::array<float, size_t(GCodeExtrusionRole::Count)> feedrate_per_extrusion_role{};
|
||||||
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
||||||
feedrate_per_extrusion_role[i] = FLT_MAX;
|
feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||||
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_start;
|
|
||||||
|
|
||||||
bool modified = true;
|
while (line_idx != fist_line_idx) {
|
||||||
while (modified && idx != idx_head) {
|
size_t idx_prev = line_idx - 1;
|
||||||
size_t idx_prev = circular_buffer_idx_prev(idx);
|
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
|
||||||
for (; ! circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)) ;
|
if (!m_gcode_lines[idx_prev].extruding())
|
||||||
if (! circular_buffer[idx_prev].extruding())
|
break;
|
||||||
break;
|
// Don't decelerate before ironing.
|
||||||
|
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) { line_idx = idx_prev;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Volumetric extrusion rate at the start of the succeding segment.
|
// 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?
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
idx = idx_prev;
|
line_idx = idx_prev;
|
||||||
GCodeLine &line = circular_buffer[idx];
|
GCodeLine &line = m_gcode_lines[line_idx];
|
||||||
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
|
||||||
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
|
for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) {
|
||||||
if (rate_slope == 0)
|
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
|
||||||
// The negative rate is unlimited.
|
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
|
||||||
continue;
|
continue; // The negative rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
|
||||||
|
|
||||||
float rate_end = feedrate_per_extrusion_role[iRole];
|
float rate_end = feedrate_per_extrusion_role[iRole];
|
||||||
if (iRole == line.extrusion_role && rate_succ < rate_end)
|
if (iRole == size_t(line.extrusion_role) && rate_succ < rate_end)
|
||||||
// Limit by the succeeding volumetric flow rate.
|
// Limit by the succeeding volumetric flow rate.
|
||||||
rate_end = rate_succ;
|
rate_end = rate_succ;
|
||||||
if (line.volumetric_extrusion_rate_end > rate_end) {
|
|
||||||
line.volumetric_extrusion_rate_end = rate_end;
|
// don't alter the flow rate for these extrusion types
|
||||||
line.modified = true;
|
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
|
||||||
} else if (iRole == line.extrusion_role) {
|
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 == size_t(line.extrusion_role)) {
|
||||||
rate_end = line.volumetric_extrusion_rate_end;
|
rate_end = line.volumetric_extrusion_rate_end;
|
||||||
} else if (rate_end == FLT_MAX) {
|
|
||||||
// The rate for ExtrusionRole iRole is unlimited.
|
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
// 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 (line.adjustable_flow) {
|
||||||
if (rate_start < line.volumetric_extrusion_rate_start) {
|
float rate_start = rate_end + rate_slope * line.time_corrected();
|
||||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
if (rate_start < line.volumetric_extrusion_rate_start) {
|
||||||
// of ExtrusionType iRole, which will be extruded in the future.
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
line.volumetric_extrusion_rate_start = rate_start;
|
// of ExtrusionType iRole, which will be extruded in the future.
|
||||||
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
line.volumetric_extrusion_rate_start = rate_start;
|
||||||
line.modified = true;
|
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
||||||
// modified = true;
|
line.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;
|
||||||
|
// Don't store feed rate for ironing
|
||||||
|
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
|
||||||
|
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.
|
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
||||||
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
feedrate_per_extrusion_role[size_t(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
|
||||||
feedrate_per_extrusion_role[i] = FLT_MAX;
|
|
||||||
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end;
|
|
||||||
|
|
||||||
assert(circular_buffer[idx].extruding());
|
assert(m_gcode_lines[line_idx].extruding());
|
||||||
while (idx != idx_tail) {
|
while (line_idx != last_line_idx) {
|
||||||
size_t idx_next = circular_buffer_idx_next(idx);
|
size_t idx_next = line_idx + 1;
|
||||||
for (; ! circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)) ;
|
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
|
||||||
if (! circular_buffer[idx_next].extruding())
|
if (!m_gcode_lines[idx_next].extruding())
|
||||||
break;
|
break;
|
||||||
float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end;
|
// Don't accelerate after ironing.
|
||||||
|
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
|
||||||
|
line_idx = idx_next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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?
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
idx = idx_next;
|
line_idx = idx_next;
|
||||||
GCodeLine &line = circular_buffer[idx];
|
GCodeLine &line = m_gcode_lines[line_idx];
|
||||||
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
|
||||||
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
|
for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) {
|
||||||
if (rate_slope == 0)
|
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
|
||||||
// The positive rate is unlimited.
|
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
|
||||||
continue;
|
continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
|
||||||
|
|
||||||
float rate_start = feedrate_per_extrusion_role[iRole];
|
float rate_start = feedrate_per_extrusion_role[iRole];
|
||||||
if (iRole == line.extrusion_role && rate_prec < rate_start)
|
// don't alter the flow rate for these extrusion types
|
||||||
|
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
|
||||||
|
rate_start = line.volumetric_extrusion_rate_start;
|
||||||
|
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
|
||||||
rate_start = rate_prec;
|
rate_start = rate_prec;
|
||||||
if (line.volumetric_extrusion_rate_start > rate_start) {
|
if (line.volumetric_extrusion_rate_start > rate_start) {
|
||||||
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;
|
line.modified = true;
|
||||||
} else if (iRole == line.extrusion_role) {
|
} else if (iRole == size_t(line.extrusion_role)) {
|
||||||
rate_start = line.volumetric_extrusion_rate_start;
|
rate_start = line.volumetric_extrusion_rate_start;
|
||||||
} else if (rate_start == FLT_MAX) {
|
|
||||||
// The rate for ExtrusionRole iRole is unlimited.
|
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
// 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();
|
|
||||||
if (rate_end < line.volumetric_extrusion_rate_end) {
|
if (line.adjustable_flow) {
|
||||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
float rate_end = rate_start + rate_slope * line.time_corrected();
|
||||||
// of ExtrusionType iRole, which was extruded before.
|
if (rate_end < line.volumetric_extrusion_rate_end) {
|
||||||
line.volumetric_extrusion_rate_end = rate_end;
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
// of ExtrusionType iRole, which was extruded before.
|
||||||
line.modified = true;
|
line.volumetric_extrusion_rate_end = rate_end;
|
||||||
|
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;
|
||||||
|
// Don't store feed rate for ironing
|
||||||
|
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
|
||||||
|
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];
|
return this->push_to_output(formatter.string(), false);
|
||||||
int len = sprintf(buf,
|
|
||||||
(axis == 'E') ? " %c%.3f" : " %c%.5f",
|
|
||||||
axis, value);
|
|
||||||
push_to_output(buf, len, add_eol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// New length of the output buffer content.
|
||||||
size_t len_new = output_buffer_length + len + 1;
|
size_t len_new = output_buffer_length + len + 1;
|
||||||
if (add_eol)
|
if (add_eol)
|
||||||
++ len_new;
|
++len_new;
|
||||||
|
|
||||||
// Resize the output buffer to a power of 2 higher than the required memory.
|
// Resize the output buffer to a power of 2 higher than the required memory.
|
||||||
if (output_buffer.size() < len_new) {
|
if (output_buffer.size() < len_new) {
|
||||||
|
@ -600,24 +742,63 @@ void PressureEqualizer::push_to_output(const char *text, const size_t len, bool
|
||||||
// Copy the text to the output.
|
// Copy the text to the output.
|
||||||
if (len != 0) {
|
if (len != 0) {
|
||||||
memcpy(output_buffer.data() + output_buffer_length, text, len);
|
memcpy(output_buffer.data() + output_buffer_length, text, len);
|
||||||
|
this->output_buffer_prev_length = this->output_buffer_length;
|
||||||
output_buffer_length += len;
|
output_buffer_length += len;
|
||||||
}
|
}
|
||||||
if (add_eol)
|
if (add_eol)
|
||||||
output_buffer[output_buffer_length ++] = '\n';
|
output_buffer[output_buffer_length++] = '\n';
|
||||||
output_buffer[output_buffer_length] = 0;
|
output_buffer[output_buffer_length] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
|
inline bool is_just_line_with_extrude_set_speed_tag(const std::string &line)
|
||||||
{
|
{
|
||||||
push_to_output("G1", 2, false);
|
if (line.empty() && !boost::starts_with(line, "G1 ") && !boost::ends_with(line, EXTRUDE_SET_SPEED_TAG))
|
||||||
for (char i = 0; i < 3; ++ i)
|
return false;
|
||||||
if (line.pos_provided[i])
|
|
||||||
push_axis_to_output('X'+i, line.pos_end[i]);
|
const char *p_line = line.data() + 3;
|
||||||
push_axis_to_output('E', m_config->use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]);
|
const char *const line_end = line.data() + line.length() - 1;
|
||||||
// if (line.pos_provided[4] || fabs(line.feedrate() - new_feedrate) > 1e-5)
|
while (!is_eol(*p_line)) {
|
||||||
push_axis_to_output('F', new_feedrate);
|
if (toupper(*p_line++) == 'F')
|
||||||
// output comment and EOL
|
break;
|
||||||
push_to_output(comment, (comment == NULL) ? 0 : strlen(comment), true);
|
else
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
parse_float(p_line, line_end - p_line);
|
||||||
|
eatws(p_line);
|
||||||
|
p_line += EXTRUDE_SET_SPEED_TAG.length();
|
||||||
|
return p_line <= line_end && is_eol(*p_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PressureEqualizer::push_line_to_output(const size_t line_idx, const float new_feedrate, const char *comment)
|
||||||
|
{
|
||||||
|
const GCodeLine &line = m_gcode_lines[line_idx];
|
||||||
|
if (line_idx > 0 && output_buffer_length > 0) {
|
||||||
|
const std::string prev_line_str = std::string(output_buffer.begin() + int(this->output_buffer_prev_length),
|
||||||
|
output_buffer.begin() + int(this->output_buffer_length) + 1);
|
||||||
|
if (is_just_line_with_extrude_set_speed_tag(prev_line_str))
|
||||||
|
this->output_buffer_length = this->output_buffer_prev_length; // Remove the last line because it only sets the speed for an empty block of g-code lines, so it is useless.
|
||||||
|
else
|
||||||
|
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
|
||||||
|
} else
|
||||||
|
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 == GCodeExtrusionRole::ExternalPerimeter)
|
||||||
|
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
|
} // namespace Slic3r
|
||||||
|
|
|
@ -1,41 +1,59 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
|
||||||
|
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
#ifndef slic3r_GCode_PressureEqualizer_hpp_
|
#ifndef slic3r_GCode_PressureEqualizer_hpp_
|
||||||
#define slic3r_GCode_PressureEqualizer_hpp_
|
#define slic3r_GCode_PressureEqualizer_hpp_
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
#include "../ExtrusionEntity.hpp"
|
#include "../ExtrusionRole.hpp"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
namespace Slic3r {
|
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
|
// 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.
|
// between these paths to limit fast changes in the volumetric extrusion speed.
|
||||||
class PressureEqualizer
|
class PressureEqualizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PressureEqualizer(const Slic3r::GCodeConfig *config);
|
PressureEqualizer() = delete;
|
||||||
~PressureEqualizer();
|
explicit PressureEqualizer(const Slic3r::GCodeConfig &config);
|
||||||
|
~PressureEqualizer() = default;
|
||||||
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; }
|
|
||||||
|
|
||||||
|
// 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:
|
private:
|
||||||
|
|
||||||
|
void process_layer(const std::string &gcode);
|
||||||
|
|
||||||
|
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||||
struct Statistics
|
struct Statistics
|
||||||
{
|
{
|
||||||
void reset() {
|
void reset()
|
||||||
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
|
{
|
||||||
|
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
|
||||||
volumetric_extrusion_rate_max = 0.f;
|
volumetric_extrusion_rate_max = 0.f;
|
||||||
volumetric_extrusion_rate_avg = 0.f;
|
volumetric_extrusion_rate_avg = 0.f;
|
||||||
extrusion_length = 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_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;
|
volumetric_extrusion_rate_avg += volumetric_extrusion_rate * length;
|
||||||
extrusion_length += length;
|
extrusion_length += length;
|
||||||
}
|
}
|
||||||
float volumetric_extrusion_rate_min;
|
float volumetric_extrusion_rate_min;
|
||||||
float volumetric_extrusion_rate_max;
|
float volumetric_extrusion_rate_max;
|
||||||
|
@ -44,9 +62,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Statistics m_stat;
|
struct Statistics m_stat;
|
||||||
|
#endif
|
||||||
// Keeps the reference, does not own the config.
|
|
||||||
const Slic3r::GCodeConfig *m_config;
|
|
||||||
|
|
||||||
// Private configuration values
|
// Private configuration values
|
||||||
// How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2
|
// How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2
|
||||||
|
@ -54,12 +70,9 @@ private:
|
||||||
float positive;
|
float positive;
|
||||||
float negative;
|
float negative;
|
||||||
};
|
};
|
||||||
enum { numExtrusionRoles = erSupportMaterialInterface + 1 };
|
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[size_t(GCodeExtrusionRole::Count)];
|
||||||
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[numExtrusionRoles];
|
|
||||||
float m_max_volumetric_extrusion_rate_slope_positive;
|
float m_max_volumetric_extrusion_rate_slope_positive;
|
||||||
float m_max_volumetric_extrusion_rate_slope_negative;
|
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.
|
|
||||||
float m_max_segment_length;
|
|
||||||
|
|
||||||
// Configuration extracted from config.
|
// Configuration extracted from config.
|
||||||
// Area of the crossestion of each filament. Necessary to calculate the volumetric flow rate.
|
// Area of the crossestion of each filament. Necessary to calculate the volumetric flow rate.
|
||||||
|
@ -69,11 +82,19 @@ private:
|
||||||
// X,Y,Z,E,F
|
// X,Y,Z,E,F
|
||||||
float m_current_pos[5];
|
float m_current_pos[5];
|
||||||
size_t m_current_extruder;
|
size_t m_current_extruder;
|
||||||
ExtrusionRole m_current_extrusion_role;
|
GCodeExtrusionRole m_current_extrusion_role;
|
||||||
bool m_retracted;
|
bool m_retracted;
|
||||||
|
bool m_use_relative_e_distances;
|
||||||
|
|
||||||
enum GCodeLineType
|
// Maximum segment length to split a long segment if the initial and the final flow rate differ.
|
||||||
{
|
// Smaller value means a smoother transition between two different flow rates.
|
||||||
|
float m_max_segment_length;
|
||||||
|
|
||||||
|
// Indicate if extrude set speed block was opened using the tag ";_EXTRUDE_SET_SPEED"
|
||||||
|
// or not (not opened, or it was closed using the tag ";_EXTRUDE_END").
|
||||||
|
bool opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
|
enum GCodeLineType {
|
||||||
GCODELINETYPE_INVALID,
|
GCODELINETYPE_INVALID,
|
||||||
GCODELINETYPE_NOOP,
|
GCODELINETYPE_NOOP,
|
||||||
GCODELINETYPE_OTHER,
|
GCODELINETYPE_OTHER,
|
||||||
|
@ -128,18 +149,16 @@ private:
|
||||||
// or maybe the line needs to be split into multiple lines.
|
// or maybe the line needs to be split into multiple lines.
|
||||||
bool modified;
|
bool modified;
|
||||||
|
|
||||||
// float timeStart;
|
|
||||||
// float timeEnd;
|
|
||||||
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
||||||
float pos_start[5];
|
float pos_start[5];
|
||||||
float pos_end[5];
|
float pos_end[5];
|
||||||
// Was the axis found on the G-code line? X,Y,Z,F
|
// Was the axis found on the G-code line? X,Y,Z,E,F
|
||||||
bool pos_provided[5];
|
bool pos_provided[5];
|
||||||
|
|
||||||
// Index of the active extruder.
|
// Index of the active extruder.
|
||||||
size_t extruder_id;
|
size_t extruder_id;
|
||||||
// Extrusion role of this segment.
|
// Extrusion role of this segment.
|
||||||
ExtrusionRole extrusion_role;
|
GCodeExtrusionRole extrusion_role;
|
||||||
|
|
||||||
// Current volumetric extrusion rate.
|
// Current volumetric extrusion rate.
|
||||||
float volumetric_extrusion_rate;
|
float volumetric_extrusion_rate;
|
||||||
|
@ -152,59 +171,42 @@ private:
|
||||||
// If set to zero, the slope is unlimited.
|
// If set to zero, the slope is unlimited.
|
||||||
float max_volumetric_extrusion_rate_slope_positive;
|
float max_volumetric_extrusion_rate_slope_positive;
|
||||||
float max_volumetric_extrusion_rate_slope_negative;
|
float max_volumetric_extrusion_rate_slope_negative;
|
||||||
};
|
|
||||||
|
|
||||||
// Circular buffer of GCode lines. The circular buffer size will be limited to circular_buffer_size.
|
bool adjustable_flow = false;
|
||||||
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).
|
bool extrude_set_speed_tag = false;
|
||||||
size_t circular_buffer_pos;
|
bool extrude_end_tag = false;
|
||||||
// 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.
|
// Output buffer will only grow. It will not be reallocated over and over.
|
||||||
std::vector<char> output_buffer;
|
std::vector<char> output_buffer;
|
||||||
size_t output_buffer_length;
|
size_t output_buffer_length;
|
||||||
|
size_t output_buffer_prev_length;
|
||||||
|
|
||||||
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
// For debugging purposes. Index of the G-code line processed.
|
// For debugging purposes. Index of the G-code line processed.
|
||||||
size_t line_idx;
|
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);
|
long advance_segment_beyond_small_gap(long idx_cur_pos);
|
||||||
|
void output_gcode_line(size_t line_idx);
|
||||||
|
|
||||||
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||||
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
||||||
void adjust_volumetric_rate();
|
void adjust_volumetric_rate(size_t first_line_idx, size_t last_line_idx);
|
||||||
|
|
||||||
// Push the text to the end of the output_buffer.
|
// 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);
|
inline void push_to_output(GCodeG1Formatter &formatter);
|
||||||
// Push an axis assignment to the end of the output buffer.
|
inline void push_to_output(const std::string &text, bool add_eol);
|
||||||
void push_axis_to_output(const char axis, const float value, bool add_eol = false);
|
inline void push_to_output(const char *text, size_t len, bool add_eol = true);
|
||||||
// Push a G-code line to the output,
|
// Push a G-code line to the output.
|
||||||
void push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment);
|
void push_line_to_output(size_t line_idx, float new_feedrate, const char *comment);
|
||||||
|
|
||||||
size_t circular_buffer_idx_head() const {
|
public:
|
||||||
size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items;
|
std::queue<LayerResult*> m_layer_results;
|
||||||
if (idx >= circular_buffer_size)
|
|
||||||
idx -= circular_buffer_size;
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t circular_buffer_idx_tail() const { return circular_buffer_pos; }
|
std::vector<GCodeLine> m_gcode_lines;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -725,9 +725,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||||
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing",
|
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing",
|
||||||
"max_travel_detour_distance",
|
"max_travel_detour_distance",
|
||||||
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance",
|
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance",
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
"max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length",
|
||||||
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
|
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
|
||||||
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
|
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
|
||||||
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
|
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
|
||||||
|
|
|
@ -150,10 +150,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||||
"fan_max_speed",
|
"fan_max_speed",
|
||||||
"printable_height",
|
"printable_height",
|
||||||
"slow_down_min_speed",
|
"slow_down_min_speed",
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
"max_volumetric_extrusion_rate_slope",
|
||||||
"max_volumetric_extrusion_rate_slope_positive",
|
"max_volumetric_extrusion_rate_slope_segment_length",
|
||||||
"max_volumetric_extrusion_rate_slope_negative",
|
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
"reduce_infill_retraction",
|
"reduce_infill_retraction",
|
||||||
"filename_format",
|
"filename_format",
|
||||||
"retraction_minimum_travel",
|
"retraction_minimum_travel",
|
||||||
|
|
|
@ -2470,29 +2470,37 @@ def = this->add("filament_loading_speed", coFloats);
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||||
|
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
def = this->add("max_volumetric_extrusion_rate_slope", coFloat);
|
||||||
//def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat);
|
def->label = L("Extrusion rate smoothing");
|
||||||
//def->label = L("Max volumetric slope positive");
|
def->tooltip = L("This parameter smooths out sudden extrusion rate changes that happen when "
|
||||||
//def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. "
|
"the printer transitions from printing a high flow (high speed/larger width) "
|
||||||
// "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate "
|
"extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.\n\n"
|
||||||
// "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) "
|
"It defines the maximum rate by which the extruded volumetric flow in mm3/sec can change over time. "
|
||||||
// "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds.");
|
"Higher values mean higher extrusion rate changes are allowed, resulting in faster speed transitions.\n\n"
|
||||||
//def->sidetext = L("mm³/s²");
|
"A value of 0 disables the feature. \n\n"
|
||||||
//def->min = 0;
|
"For a high speed, high flow direct drive printer (like the Bambu lab or Voron) this value is usually not needed. "
|
||||||
//def->mode = comAdvanced;
|
"However it can provide some marginal benefit in certain cases where feature speeds vary greatly. For example, "
|
||||||
//def->set_default_value(new ConfigOptionFloat(0));
|
"when there are aggressive slowdowns due to overhangs. In these cases a high value of around 300-350mm3/s2 is "
|
||||||
|
"recommended as this allows for just enough smoothing to assist pressure advance achieve a smoother flow transition.\n\n"
|
||||||
//def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat);
|
"For slower printers without pressure advance, the value should be set much lower. A value of 10-15mm3/s2 is a "
|
||||||
//def->label = L("Max volumetric slope negative");
|
"good starting point for direct drive extruders and 5-10mm3/s2 for Bowden style. \n\n"
|
||||||
//def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. "
|
"This feature is known as Pressure Equalizer in Prusa slicer.\n\n"
|
||||||
// "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate "
|
"Note: this parameter disables arc fitting.");
|
||||||
// "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) "
|
def->sidetext = L("mm³/s²");
|
||||||
// "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds.");
|
def->min = 0;
|
||||||
//def->sidetext = L("mm³/s²");
|
def->mode = comAdvanced;
|
||||||
//def->min = 0;
|
def->set_default_value(new ConfigOptionFloat(0));
|
||||||
//def->mode = comAdvanced;
|
|
||||||
//def->set_default_value(new ConfigOptionFloat(0));
|
def = this->add("max_volumetric_extrusion_rate_slope_segment_length", coInt);
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
def->label = L("Smoothing segment length");
|
||||||
|
def->tooltip = L("A lower value results in smoother extrusion rate transitions. However, this results in a significantly larger gcode file "
|
||||||
|
"and more instructions for the printer to process. \n\n"
|
||||||
|
"Default value of 3 works well for most cases. If your printer is stuttering, increase this value to reduce the number of adjustments made\n\n"
|
||||||
|
"Allowed values: 1-5");
|
||||||
|
def->min = 1;
|
||||||
|
def->max = 5;
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionInt(3));
|
||||||
|
|
||||||
def = this->add("fan_min_speed", coInts);
|
def = this->add("fan_min_speed", coInts);
|
||||||
def->label = L("Fan speed");
|
def->label = L("Fan speed");
|
||||||
|
@ -4962,9 +4970,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||||
"acceleration", "scale", "rotate", "duplicate", "duplicate_grid",
|
"acceleration", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||||
"bed_size",
|
"bed_size",
|
||||||
"print_center", "g0", "wipe_tower_per_color_wipe"
|
"print_center", "g0", "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 */
|
|
||||||
// BBS
|
// BBS
|
||||||
, "support_sharp_tails","support_remove_small_overhangs", "support_with_sheath",
|
, "support_sharp_tails","support_remove_small_overhangs", "support_with_sheath",
|
||||||
"tree_support_collision_resolution", "tree_support_with_infill",
|
"tree_support_collision_resolution", "tree_support_with_infill",
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
#include <boost/preprocessor/tuple/elem.hpp>
|
#include <boost/preprocessor/tuple/elem.hpp>
|
||||||
#include <boost/preprocessor/tuple/to_seq.hpp>
|
#include <boost/preprocessor/tuple/to_seq.hpp>
|
||||||
|
|
||||||
// #define HAS_PRESSURE_EQUALIZER
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum GCodeFlavor : unsigned char {
|
enum GCodeFlavor : unsigned char {
|
||||||
|
@ -883,10 +881,10 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||||
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
||||||
|
|
||||||
((ConfigOptionString, layer_change_gcode))
|
((ConfigOptionString, layer_change_gcode))
|
||||||
//#ifdef HAS_PRESSURE_EQUALIZER
|
|
||||||
// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive))
|
((ConfigOptionFloat, max_volumetric_extrusion_rate_slope))
|
||||||
// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative))
|
((ConfigOptionInt, max_volumetric_extrusion_rate_slope_segment_length))
|
||||||
//#endif
|
|
||||||
((ConfigOptionPercents, retract_before_wipe))
|
((ConfigOptionPercents, retract_before_wipe))
|
||||||
((ConfigOptionFloats, retraction_length))
|
((ConfigOptionFloats, retraction_length))
|
||||||
((ConfigOptionFloats, retract_length_toolchange))
|
((ConfigOptionFloats, retract_length_toolchange))
|
||||||
|
|
|
@ -31,14 +31,25 @@ public:
|
||||||
constexpr enum_bitmask(option_type o) : m_bits(mask_value(o)) {}
|
constexpr enum_bitmask(option_type o) : m_bits(mask_value(o)) {}
|
||||||
|
|
||||||
// Set the bit corresponding to the given option.
|
// Set the bit corresponding to the given option.
|
||||||
constexpr enum_bitmask operator|(option_type t) { return enum_bitmask(m_bits | mask_value(t)); }
|
constexpr enum_bitmask operator|(option_type t) const { return enum_bitmask(m_bits | mask_value(t)); }
|
||||||
|
|
||||||
// Combine with another enum_bitmask of the same type.
|
// Combine with another enum_bitmask of the same type.
|
||||||
constexpr enum_bitmask operator|(enum_bitmask<option_type> t) { return enum_bitmask(m_bits | t.m_bits); }
|
constexpr enum_bitmask operator|(enum_bitmask<option_type> t) const { return enum_bitmask(m_bits | t.m_bits); }
|
||||||
|
|
||||||
|
// Set the bit corresponding to the given option.
|
||||||
|
constexpr void operator|=(option_type t) { m_bits = enum_bitmask(m_bits | mask_value(t)); }
|
||||||
|
|
||||||
|
// Combine with another enum_bitmask of the same type.
|
||||||
|
constexpr void operator|=(enum_bitmask<option_type> t) { m_bits = enum_bitmask(m_bits | t.m_bits); }
|
||||||
|
|
||||||
// Get the value of the bit corresponding to the given option.
|
// Get the value of the bit corresponding to the given option.
|
||||||
constexpr bool operator&(option_type t) { return m_bits & mask_value(t); }
|
constexpr bool operator&(option_type t) const { return m_bits & mask_value(t); }
|
||||||
constexpr bool has(option_type t) { return m_bits & mask_value(t); }
|
constexpr bool has(option_type t) const { return m_bits & mask_value(t); }
|
||||||
|
|
||||||
|
constexpr bool operator==(const enum_bitmask r) const { return m_bits == r.m_bits; }
|
||||||
|
constexpr bool operator!=(const enum_bitmask r) const { return m_bits != r.m_bits; }
|
||||||
|
// For sorting by the enum values.
|
||||||
|
constexpr bool lower(const enum_bitmask r) const { return m_bits < r.m_bits; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
underlying_type m_bits = 0;
|
underlying_type m_bits = 0;
|
||||||
|
|
|
@ -475,6 +475,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||||
apply(config, &new_conf);
|
apply(config, &new_conf);
|
||||||
is_msg_dlg_already_exist = false;
|
is_msg_dlg_already_exist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase *, ModelConfig *> const &configs)
|
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase *, ModelConfig *> const &configs)
|
||||||
|
@ -508,6 +509,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||||
//SoftFever
|
//SoftFever
|
||||||
auto gcflavor = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
|
auto gcflavor = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
|
||||||
|
|
||||||
|
bool have_volumetric_extrusion_rate_slope = config->option<ConfigOptionFloat>("max_volumetric_extrusion_rate_slope")->value > 0;
|
||||||
|
int have_volumetric_extrusion_rate_slope_segment_length = config->option<ConfigOptionInt>("max_volumetric_extrusion_rate_slope_segment_length")->value;
|
||||||
|
toggle_field("enable_arc_fitting", !have_volumetric_extrusion_rate_slope);
|
||||||
|
toggle_line("max_volumetric_extrusion_rate_slope_segment_length", have_volumetric_extrusion_rate_slope);
|
||||||
|
if(have_volumetric_extrusion_rate_slope) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false));
|
||||||
|
if(have_volumetric_extrusion_rate_slope_segment_length==0) {
|
||||||
|
DynamicPrintConfig new_conf = *config;
|
||||||
|
new_conf.set_key_value("max_volumetric_extrusion_rate_slope_segment_length", new ConfigOptionInt(1));
|
||||||
|
apply(config, &new_conf);
|
||||||
|
}
|
||||||
|
|
||||||
bool have_perimeters = config->opt_int("wall_loops") > 0;
|
bool have_perimeters = config->opt_int("wall_loops") > 0;
|
||||||
for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall",
|
for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall",
|
||||||
"seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width",
|
"seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width",
|
||||||
|
|
|
@ -1995,11 +1995,10 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("top_surface_jerk");
|
optgroup->append_single_option_line("top_surface_jerk");
|
||||||
optgroup->append_single_option_line("initial_layer_jerk");
|
optgroup->append_single_option_line("initial_layer_jerk");
|
||||||
optgroup->append_single_option_line("travel_jerk");
|
optgroup->append_single_option_line("travel_jerk");
|
||||||
|
|
||||||
#ifdef HAS_PRESSURE_EQUALIZER
|
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced", 15);
|
||||||
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive");
|
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope");
|
||||||
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative");
|
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_segment_length");
|
||||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
|
||||||
|
|
||||||
page = add_options_page(L("Support"), "support");
|
page = add_options_page(L("Support"), "support");
|
||||||
optgroup = page->new_optgroup(L("Support"), L"param_support");
|
optgroup = page->new_optgroup(L("Support"), L"param_support");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue