Improved retract handling on bowden extruders:

Separated deretract speed from a retract speed,
allowed a partial retract before wipe.
This commit is contained in:
bubnikv 2017-05-19 19:24:21 +02:00
parent 8bd3dec331
commit 70db88dd90
20 changed files with 246 additions and 108 deletions

View File

@ -1156,7 +1156,7 @@ sub build {
use_volumetric_e variable_layer_height use_volumetric_e variable_layer_height
single_extruder_multi_material start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode single_extruder_multi_material start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode
nozzle_diameter extruder_offset nozzle_diameter extruder_offset
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length retract_lift retract_speed deretract_speed retract_before_wipe retract_restart_extra retract_before_travel retract_layer_change wipe
retract_length_toolchange retract_restart_extra_toolchange retract_length_toolchange retract_restart_extra_toolchange
printer_notes printer_notes
)); ));
@ -1444,7 +1444,7 @@ sub _extruders_count_changed {
$self->_on_value_change('extruders_count', $extruders_count); $self->_on_value_change('extruders_count', $extruders_count);
} }
sub _extruder_options { qw(nozzle_diameter min_layer_height max_layer_height extruder_offset retract_length retract_lift retract_lift_above retract_lift_below retract_speed retract_restart_extra retract_before_travel wipe sub _extruder_options { qw(nozzle_diameter min_layer_height max_layer_height extruder_offset retract_length retract_lift retract_lift_above retract_lift_below retract_speed deretract_speed retract_before_wipe retract_restart_extra retract_before_travel wipe
retract_layer_change retract_length_toolchange retract_restart_extra_toolchange) } retract_layer_change retract_length_toolchange retract_restart_extra_toolchange) }
sub _build_extruder_pages { sub _build_extruder_pages {
@ -1497,7 +1497,7 @@ sub _build_extruder_pages {
} }
$optgroup->append_single_option_line($_, $extruder_idx) $optgroup->append_single_option_line($_, $extruder_idx)
for qw(retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe); for qw(retract_speed deretract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_before_wipe);
} }
{ {
my $optgroup = $page->new_optgroup('Retraction when tool is disabled (advanced settings for multi-extruder setups)'); my $optgroup = $page->new_optgroup('Retraction when tool is disabled (advanced settings for multi-extruder setups)');
@ -1579,8 +1579,12 @@ sub _update {
# some options only apply when not using firmware retraction # some options only apply when not using firmware retraction
$self->get_field($_, $i)->toggle($retraction && !$config->use_firmware_retraction) $self->get_field($_, $i)->toggle($retraction && !$config->use_firmware_retraction)
for qw(retract_speed retract_restart_extra wipe); for qw(retract_speed deretract_speed retract_before_wipe retract_restart_extra wipe);
if ($config->use_firmware_retraction && $config->get_at('wipe', $i)) {
my $wipe = $config->get_at('wipe', $i);
$self->get_field('retract_before_wipe', $i)->toggle($wipe);
if ($config->use_firmware_retraction && $wipe) {
my $dialog = Wx::MessageDialog->new($self, my $dialog = Wx::MessageDialog->new($self,
"The Wipe option is not available when using the Firmware Retraction mode.\n" "The Wipe option is not available when using the Firmware Retraction mode.\n"
. "\nShall I disable it in order to enable Firmware Retraction?", . "\nShall I disable it in order to enable Firmware Retraction?",

View File

@ -376,6 +376,8 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
opt = new ConfigOptionStrings (); opt = new ConfigOptionStrings ();
} else if (optdef->type == coPercent) { } else if (optdef->type == coPercent) {
opt = new ConfigOptionPercent (); opt = new ConfigOptionPercent ();
} else if (optdef->type == coPercents) {
opt = new ConfigOptionPercents ();
} else if (optdef->type == coFloatOrPercent) { } else if (optdef->type == coFloatOrPercent) {
opt = new ConfigOptionFloatOrPercent (); opt = new ConfigOptionFloatOrPercent ();
} else if (optdef->type == coPoint) { } else if (optdef->type == coPoint) {

View File

@ -57,6 +57,7 @@ class ConfigOptionSingle : public ConfigOption {
class ConfigOptionVectorBase : public ConfigOption { class ConfigOptionVectorBase : public ConfigOption {
public: public:
virtual ~ConfigOptionVectorBase() {}; virtual ~ConfigOptionVectorBase() {};
// Currently used only to initialize the PlaceholderParser.
virtual std::vector<std::string> vserialize() const = 0; virtual std::vector<std::string> vserialize() const = 0;
}; };
@ -257,6 +258,46 @@ class ConfigOptionPercent : public ConfigOptionFloat
}; };
}; };
class ConfigOptionPercents : public ConfigOptionFloats
{
public:
std::string serialize() const {
std::ostringstream ss;
for (const auto &v : this->values) {
if (&v != &this->values.front()) ss << ",";
ss << v << "%";
}
std::string str = ss.str();
return str;
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (const auto v : this->values) {
std::ostringstream ss;
ss << v;
std::string sout = ss.str() + "%";
vv.push_back(sout);
}
return vv;
};
bool deserialize(std::string str) {
this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
std::istringstream iss(item_str);
double value;
// don't try to parse the trailing % since it's optional
iss >> value;
this->values.push_back(value);
}
return true;
};
};
class ConfigOptionFloatOrPercent : public ConfigOptionPercent class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{ {
public: public:
@ -488,6 +529,8 @@ enum ConfigOptionType {
coStrings, coStrings,
// percent value. Currently only used for infill. // percent value. Currently only used for infill.
coPercent, coPercent,
// percents value. Currently used for retract before wipe only.
coPercents,
// a fraction or an absolute value // a fraction or an absolute value
coFloatOrPercent, coFloatOrPercent,
// single 2d point. Currently not used. // single 2d point. Currently not used.

View File

@ -15,20 +15,9 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config)
this->e_per_mm3 = this->extrusion_multiplier() this->e_per_mm3 = this->extrusion_multiplier()
* (4 / ((this->filament_diameter() * this->filament_diameter()) * PI)); * (4 / ((this->filament_diameter() * this->filament_diameter()) * PI));
} }
this->retract_speed_mm_min = this->retract_speed() * 60;
} }
void double Extruder::extrude(double dE)
Extruder::reset()
{
this->E = 0;
this->absolute_E = 0;
this->retracted = 0;
this->restart_extra = 0;
}
double
Extruder::extrude(double dE)
{ {
// in case of relative E distances we always reset to 0 before any output // in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances) if (m_config->use_relative_e_distances)
@ -46,8 +35,7 @@ Extruder::extrude(double dE)
The restart_extra argument sets the extra length to be used for The restart_extra argument sets the extra length to be used for
unretraction. If we're actually performing a retraction, any restart_extra unretraction. If we're actually performing a retraction, any restart_extra
value supplied will overwrite the previous one if any. */ value supplied will overwrite the previous one if any. */
double double Extruder::retract(double length, double restart_extra)
Extruder::retract(double length, double restart_extra)
{ {
// in case of relative E distances we always reset to 0 before any output // in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances) if (m_config->use_relative_e_distances)
@ -65,8 +53,7 @@ Extruder::retract(double length, double restart_extra)
} }
} }
double double Extruder::unretract()
Extruder::unretract()
{ {
double dE = this->retracted + this->restart_extra; double dE = this->retracted + this->restart_extra;
this->extrude(dE); this->extrude(dE);
@ -75,14 +62,12 @@ Extruder::unretract()
return dE; return dE;
} }
double double Extruder::e_per_mm(double mm3_per_mm) const
Extruder::e_per_mm(double mm3_per_mm) const
{ {
return mm3_per_mm * this->e_per_mm3; return mm3_per_mm * this->e_per_mm3;
} }
double double Extruder::extruded_volume() const
Extruder::extruded_volume() const
{ {
if (m_config->use_volumetric_e) { if (m_config->use_volumetric_e) {
// Any current amount of retraction should not affect used filament, since // Any current amount of retraction should not affect used filament, since
@ -93,8 +78,7 @@ Extruder::extruded_volume() const
return this->used_filament() * (this->filament_diameter() * this->filament_diameter()) * PI/4; return this->used_filament() * (this->filament_diameter() * this->filament_diameter()) * PI/4;
} }
double double Extruder::used_filament() const
Extruder::used_filament() const
{ {
if (m_config->use_volumetric_e) { if (m_config->use_volumetric_e) {
return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4); return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4);
@ -105,62 +89,64 @@ Extruder::used_filament() const
return this->absolute_E + this->retracted; return this->absolute_E + this->retracted;
} }
double double Extruder::filament_diameter() const
Extruder::filament_diameter() const
{ {
return m_config->filament_diameter.get_at(this->id); return m_config->filament_diameter.get_at(this->id);
} }
double double Extruder::filament_density() const
Extruder::filament_density() const
{ {
return m_config->filament_density.get_at(this->id); return m_config->filament_density.get_at(this->id);
} }
double double Extruder::filament_cost() const
Extruder::filament_cost() const
{ {
return m_config->filament_cost.get_at(this->id); return m_config->filament_cost.get_at(this->id);
} }
double double Extruder::extrusion_multiplier() const
Extruder::extrusion_multiplier() const
{ {
return m_config->extrusion_multiplier.get_at(this->id); return m_config->extrusion_multiplier.get_at(this->id);
} }
double // Return a "retract_before_wipe" percentage as a factor clamped to <0, 1>
Extruder::retract_length() const double Extruder::retract_before_wipe() const
{
return std::min(1., std::max(0., m_config->retract_before_wipe.get_at(this->id) * 0.01));
}
double Extruder::retract_length() const
{ {
return m_config->retract_length.get_at(this->id); return m_config->retract_length.get_at(this->id);
} }
double double Extruder::retract_lift() const
Extruder::retract_lift() const
{ {
return m_config->retract_lift.get_at(this->id); return m_config->retract_lift.get_at(this->id);
} }
int int Extruder::retract_speed() const
Extruder::retract_speed() const
{ {
return m_config->retract_speed.get_at(this->id); return m_config->retract_speed.get_at(this->id);
} }
double int Extruder::deretract_speed() const
Extruder::retract_restart_extra() const {
int speed = m_config->deretract_speed.get_at(this->id);
return (speed > 0) ? speed : this->retract_speed();
}
double Extruder::retract_restart_extra() const
{ {
return m_config->retract_restart_extra.get_at(this->id); return m_config->retract_restart_extra.get_at(this->id);
} }
double double Extruder::retract_length_toolchange() const
Extruder::retract_length_toolchange() const
{ {
return m_config->retract_length_toolchange.get_at(this->id); return m_config->retract_length_toolchange.get_at(this->id);
} }
double double Extruder::retract_restart_extra_toolchange() const
Extruder::retract_restart_extra_toolchange() const
{ {
return m_config->retract_restart_extra_toolchange.get_at(this->id); return m_config->retract_restart_extra_toolchange.get_at(this->id);
} }

View File

@ -16,12 +16,17 @@ public:
double retracted; double retracted;
double restart_extra; double restart_extra;
double e_per_mm3; double e_per_mm3;
double retract_speed_mm_min;
Extruder(unsigned int id, GCodeConfig *config); Extruder(unsigned int id, GCodeConfig *config);
virtual ~Extruder() {} virtual ~Extruder() {}
void reset(); void reset() {
this->E = 0;
this->absolute_E = 0;
this->retracted = 0;
this->restart_extra = 0;
}
double extrude(double dE); double extrude(double dE);
double retract(double length, double restart_extra); double retract(double length, double restart_extra);
double unretract(); double unretract();
@ -33,9 +38,11 @@ public:
double filament_density() const; double filament_density() const;
double filament_cost() const; double filament_cost() const;
double extrusion_multiplier() const; double extrusion_multiplier() const;
double retract_before_wipe() const;
double retract_length() const; double retract_length() const;
double retract_lift() const; double retract_lift() const;
int retract_speed() const; int retract_speed() const;
int deretract_speed() const;
double retract_restart_extra() const; double retract_restart_extra() const;
double retract_length_toolchange() const; double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const; double retract_restart_extra_toolchange() const;

View File

@ -159,6 +159,11 @@ WipeTowerIntegration::WipeTowerIntegration(const PrintConfig &print_config) : m_
m_impl.reset(wipe_tower); m_impl.reset(wipe_tower);
} }
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
{
return Point(scale_(wipe_tower_pt.x - gcodegen.origin().x), scale_(wipe_tower_pt.y - gcodegen.origin().y));
}
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
{ {
bool over_wipe_tower = false; bool over_wipe_tower = false;
@ -175,6 +180,8 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id,
gcodegen.writer().toolchange(extruder_id); gcodegen.writer().toolchange(extruder_id);
// A phony move to the end position at the wipe tower. // A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, code_and_pos.second));
this->prepare_wipe(gcodegen, code_and_pos.second);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
over_wipe_tower = true; over_wipe_tower = true;
m_brim_done = true; m_brim_done = true;
@ -190,6 +197,8 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id,
gcode += code_and_pos.first; gcode += code_and_pos.first;
// A phony move to the end position at the wipe tower. // A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, code_and_pos.second));
this->prepare_wipe(gcodegen, code_and_pos.second);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
} }
@ -218,13 +227,26 @@ std::string WipeTowerIntegration::travel_to(GCode &gcodegen, const WipeTower::xy
std::string gcode = gcodegen.retract(true); std::string gcode = gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to( gcode += gcodegen.travel_to(
Point(scale_(dest.x - gcodegen.origin().x), scale_(dest.y - gcodegen.origin().y)), wipe_tower_point_to_object_point(gcodegen, dest),
erMixed, erMixed,
"Travel to a Wipe Tower"); "Travel to a Wipe Tower");
gcode += gcodegen.unretract(); gcode += gcodegen.unretract();
return gcode; return gcode;
} }
void WipeTowerIntegration::prepare_wipe(GCode &gcodegen, const WipeTower::xy &current_position)
{
gcodegen.m_wipe.path.points.clear();
// Start the wipe at the current position.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, current_position));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
float l = m_impl->position().x;
float r = l + m_impl->width();
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
WipeTower::xy((std::abs(l - current_position.x) < std::abs(r - current_position.x)) ? r : l,
current_position.y)));
}
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id) #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id)
inline void write(FILE *file, const std::string &what) inline void write(FILE *file, const std::string &what)
@ -577,7 +599,7 @@ bool GCode::do_export(FILE *file, Print &print)
// wher the objects are sorted by their sorted order given by object_indices. // wher the objects are sorted by their sorted order given by object_indices.
auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), ToolOrdering::LayerTools(layer.first)); auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), ToolOrdering::LayerTools(layer.first));
assert(it_layer_tools != tool_ordering.end() && layer.first); assert(it_layer_tools != tool_ordering.end() && layer.first);
if (m_wipe_tower) { if (it_layer_tools->has_wipe_tower && m_wipe_tower) {
bool first_layer = layer.first == layers.begin()->first; bool first_layer = layer.first == layers.begin()->first;
auto it_layer_tools_next = it_layer_tools; auto it_layer_tools_next = it_layer_tools;
++ it_layer_tools_next; ++ it_layer_tools_next;
@ -965,7 +987,7 @@ void GCode::process_layer(
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size()); std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders) for (unsigned int extruder_id : layer_tools.extruders)
{ {
gcode += m_wipe_tower ? gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id); this->set_extruder(extruder_id);
@ -1923,8 +1945,10 @@ GCode::retract(bool toolchange)
return gcode; return gcode;
// wipe (if it's enabled for this extruder and we have a stored wipe path) // wipe (if it's enabled for this extruder and we have a stored wipe path)
if (EXTRUDER_CONFIG(wipe) && m_wipe.has_path()) if (EXTRUDER_CONFIG(wipe) && m_wipe.has_path()) {
gcode += toolchange ? m_writer.retract_for_toolchange(true) : m_writer.retract(true);
gcode += m_wipe.wipe(*this, toolchange); gcode += m_wipe.wipe(*this, toolchange);
}
/* The parent class will decide whether we need to perform an actual retraction /* The parent class will decide whether we need to perform an actual retraction
(the extruder might be already retracted fully or partially). We call these (the extruder might be already retracted fully or partially). We call these

View File

@ -84,6 +84,7 @@ public:
private: private:
std::string travel_to(GCode &codegen, const WipeTower::xy &dest); std::string travel_to(GCode &codegen, const WipeTower::xy &dest);
void prepare_wipe(GCode &gcodegen, const WipeTower::xy &current_position);
std::unique_ptr<WipeTower> m_impl; std::unique_ptr<WipeTower> m_impl;
bool m_brim_done; bool m_brim_done;
}; };

View File

@ -27,6 +27,8 @@ static void collect_extruders(const PrintObject &object, std::vector<LayerTools>
it_layer->extruders.push_back(extruder_support); it_layer->extruders.push_back(extruder_support);
if (has_interface) if (has_interface)
it_layer->extruders.push_back(extruder_interface); it_layer->extruders.push_back(extruder_interface);
if (has_support || has_interface)
it_layer->has_support = true;
} }
// Collect the object extruders. // Collect the object extruders.
for (auto layer : object.layers) { for (auto layer : object.layers) {
@ -38,9 +40,11 @@ static void collect_extruders(const PrintObject &object, std::vector<LayerTools>
if (layerm == nullptr) if (layerm == nullptr)
continue; continue;
const PrintRegion &region = *object.print()->regions[region_id]; const PrintRegion &region = *object.print()->regions[region_id];
if (! layerm->perimeters.entities.empty()) if (! layerm->perimeters.entities.empty()) {
it_layer->extruders.push_back(region.config.perimeter_extruder.value); it_layer->extruders.push_back(region.config.perimeter_extruder.value);
bool has_infill = false; it_layer->has_object = true;
}
bool has_infill = false;
bool has_solid_infill = false; bool has_solid_infill = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) { for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island. // fill represents infill extrusions of a single island.
@ -55,6 +59,8 @@ static void collect_extruders(const PrintObject &object, std::vector<LayerTools>
it_layer->extruders.push_back(region.config.solid_infill_extruder); it_layer->extruders.push_back(region.config.solid_infill_extruder);
if (has_infill) if (has_infill)
it_layer->extruders.push_back(region.config.infill_extruder); it_layer->extruders.push_back(region.config.infill_extruder);
if (has_solid_infill || has_infill)
it_layer->has_object = true;
} }
} }
@ -137,6 +143,10 @@ static void fill_wipe_tower_partitions(std::vector<LayerTools> &layers)
// Propagate the wipe tower partitions down to support the upper partitions by the lower partitions. // Propagate the wipe tower partitions down to support the upper partitions by the lower partitions.
for (int i = int(layers.size()) - 2; i >= 0; -- i) for (int i = int(layers.size()) - 2; i >= 0; -- i)
layers[i].wipe_tower_partitions = std::max(layers[i + 1].wipe_tower_partitions, layers[i].wipe_tower_partitions); layers[i].wipe_tower_partitions = std::max(layers[i + 1].wipe_tower_partitions, layers[i].wipe_tower_partitions);
//FIXME this is a hack to get the ball rolling.
for (LayerTools &lt : layers)
lt.has_wipe_tower = lt.has_object;
} }
// For the use case when each object is printed separately // For the use case when each object is printed separately

View File

@ -11,14 +11,25 @@ namespace ToolOrdering {
struct LayerTools struct LayerTools
{ {
LayerTools(const coordf_t z) : print_z(z), wipe_tower_partitions(0) {} LayerTools(const coordf_t z) :
print_z(z),
has_object(false),
has_support(false),
has_wipe_tower(false),
wipe_tower_partitions(0) {}
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; } bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
coordf_t print_z; coordf_t print_z;
bool has_object;
bool has_support;
// Zero based extruder IDs, ordered to minimize tool switches. // Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
// Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers.
bool has_wipe_tower;
// Number of wipe tower partitions to support the required number of tool switches // Number of wipe tower partitions to support the required number of tool switches
// and to support the wipe tower partitions above this one. // and to support the wipe tower partitions above this one.
size_t wipe_tower_partitions; size_t wipe_tower_partitions;

View File

@ -30,6 +30,9 @@ public:
// Return the wipe tower position. // Return the wipe tower position.
virtual const xy& position() const = 0; virtual const xy& position() const = 0;
// Return the wipe tower width.
virtual float width() const = 0;
// The wipe tower is finished, there should be no more tool changes or wipe tower prints. // The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const = 0; virtual bool finished() const = 0;

View File

@ -348,11 +348,24 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
// Wipe the newly loaded filament until the end of the assigned wipe area. // Wipe the newly loaded filament until the end of the assigned wipe area.
toolchange_Wipe(writer, cleaning_box); toolchange_Wipe(writer, cleaning_box);
// Draw a perimeter around cleaning_box and wipe. // Draw a perimeter around cleaning_box and wipe.
toolchange_Done(writer, cleaning_box); box_coordinates box = cleaning_box;
if (m_current_shape == SHAPE_REVERSED) {
std::swap(box.lu, box.ld);
std::swap(box.ru, box.rd);
}
// Draw a perimeter around cleaning_box.
writer.travel(box.lu, 7000)
.extrude(box.ld, 3200).extrude(box.rd)
.extrude(box.ru).extrude(box.lu);
// Wipe the nozzle.
if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
writer.travel(box.ru, 7200)
.travel(box.lu);
} }
// Reset the extruder current to a normal value. // Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550) writer.set_extruder_trimpot(550)
.feedrate(6000)
.flush_planner_queue() .flush_planner_queue()
.reset_extruder() .reset_extruder()
.append("; CP TOOLCHANGE END\n" .append("; CP TOOLCHANGE END\n"
@ -423,11 +436,15 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose
} }
} }
// Move to the front left corner and wipe along the front edge. // Move to the front left corner.
writer.travel(wipeTower_box.ld, 7000) writer.travel(wipeTower_box.ld, 7000);
.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld) if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n" // Wipe along the front edge.
writer.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld);
writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n"); ";-----------------------------------\n");
// Mark the brim as extruded. // Mark the brim as extruded.
@ -616,26 +633,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
writer.set_extrusion_flow(m_extrusion_flow); writer.set_extrusion_flow(m_extrusion_flow);
} }
// Draw a perimeter around cleaning_box and wipe.
void WipeTowerPrusaMM::toolchange_Done(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box)
{
box_coordinates box = cleaning_box;
if (m_current_shape == SHAPE_REVERSED) {
std::swap(box.lu, box.ld);
std::swap(box.ru, box.rd);
}
// Draw a perimeter around cleaning_box.
writer.travel(box.lu, 7000)
.extrude(box.ld, 3200).extrude(box.rd)
.extrude(box.ru).extrude(box.lu)
// Wipe the nozzle.
.travel(box.ru, 7200)
.travel(box.lu)
.feedrate(6000);
}
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose purpose) std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose purpose)
{ {
// This should only be called if the layer is not finished yet. // This should only be called if the layer is not finished yet.
@ -720,11 +717,16 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor) writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
.extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width)) .extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width)) .extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width)) .extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width));
// Wipe along the front side of the current wiping box.
.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200) if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2)) // Wipe along the front side of the current wiping box.
.append("; CP EMPTY GRID END\n" writer.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
else
writer.feedrate(7200);
writer.append("; CP EMPTY GRID END\n"
";------------------\n\n\n\n\n\n\n"); ";------------------\n\n\n\n\n\n\n");
// Indicate that this wipe tower layer is fully covered. // Indicate that this wipe tower layer is fully covered.

View File

@ -105,6 +105,8 @@ public:
// Return the wipe tower position. // Return the wipe tower position.
virtual const xy& position() const { return m_wipe_tower_pos; } virtual const xy& position() const { return m_wipe_tower_pos; }
// Return the wipe tower width.
virtual float width() const { return m_wipe_tower_width; }
// The wipe tower is finished, there should be no more tool changes or wipe tower prints. // The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const { return m_max_color_changes == 0; } virtual bool finished() const { return m_max_color_changes == 0; }
@ -228,10 +230,6 @@ private:
PrusaMultiMaterial::Writer &writer, PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box); const box_coordinates &cleaning_box);
void toolchange_Done(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box);
void toolchange_Perimeter(); void toolchange_Perimeter();
}; };

View File

@ -384,22 +384,24 @@ GCodeWriter::extrude_to_xyz(const Pointf3 &point, double dE, const std::string &
return gcode.str(); return gcode.str();
} }
std::string std::string GCodeWriter::retract(bool before_wipe)
GCodeWriter::retract()
{ {
double factor = before_wipe ? this->_extruder->retract_before_wipe() : 1.;
assert(factor >= 0. && factor <= 1. + EPSILON);
return this->_retract( return this->_retract(
this->_extruder->retract_length(), factor * this->_extruder->retract_length(),
this->_extruder->retract_restart_extra(), factor * this->_extruder->retract_restart_extra(),
"retract" "retract"
); );
} }
std::string std::string GCodeWriter::retract_for_toolchange(bool before_wipe)
GCodeWriter::retract_for_toolchange()
{ {
double factor = before_wipe ? this->_extruder->retract_before_wipe() : 1.;
assert(factor >= 0. && factor <= 1. + EPSILON);
return this->_retract( return this->_retract(
this->_extruder->retract_length_toolchange(), factor * this->_extruder->retract_length_toolchange(),
this->_extruder->retract_restart_extra_toolchange(), factor * this->_extruder->retract_restart_extra_toolchange(),
"retract for toolchange" "retract for toolchange"
); );
} }
@ -431,7 +433,7 @@ GCodeWriter::_retract(double length, double restart_extra, const std::string &co
gcode << "G10 ; retract\n"; gcode << "G10 ; retract\n";
} else { } else {
gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E) gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E)
<< " F" << this->_extruder->retract_speed_mm_min; << " F" << float(this->_extruder->retract_speed() * 60.);
COMMENT(comment); COMMENT(comment);
gcode << "\n"; gcode << "\n";
} }
@ -462,7 +464,7 @@ GCodeWriter::unretract()
} else { } else {
// use G1 instead of G0 because G0 will blend the restart with the previous travel move // use G1 instead of G0 because G0 will blend the restart with the previous travel move
gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E) gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E)
<< " F" << this->_extruder->retract_speed_mm_min; << " F" << float(this->_extruder->deretract_speed() * 60.);
if (this->config.gcode_comments) gcode << " ; unretract"; if (this->config.gcode_comments) gcode << " ; unretract";
gcode << "\n"; gcode << "\n";
} }

View File

@ -52,8 +52,8 @@ public:
bool will_move_z(double z) const; bool will_move_z(double z) const;
std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string());
std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string());
std::string retract(); std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(); std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract(); std::string unretract();
std::string lift(); std::string lift();
std::string unlift(); std::string unlift();

View File

@ -169,6 +169,7 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|| *opt_key == "perimeter_acceleration" || *opt_key == "perimeter_acceleration"
|| *opt_key == "post_process" || *opt_key == "post_process"
|| *opt_key == "retract_before_travel" || *opt_key == "retract_before_travel"
|| *opt_key == "retract_before_wipe"
|| *opt_key == "retract_layer_change" || *opt_key == "retract_layer_change"
|| *opt_key == "retract_length" || *opt_key == "retract_length"
|| *opt_key == "retract_length_toolchange" || *opt_key == "retract_length_toolchange"
@ -178,6 +179,7 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|| *opt_key == "retract_restart_extra" || *opt_key == "retract_restart_extra"
|| *opt_key == "retract_restart_extra_toolchange" || *opt_key == "retract_restart_extra_toolchange"
|| *opt_key == "retract_speed" || *opt_key == "retract_speed"
|| *opt_key == "deretract_speed"
|| *opt_key == "single_extruder_multi_material" || *opt_key == "single_extruder_multi_material"
|| *opt_key == "slowdown_below_layer_time" || *opt_key == "slowdown_below_layer_time"
|| *opt_key == "spiral_vase" || *opt_key == "spiral_vase"

View File

@ -914,6 +914,17 @@ PrintConfigDef::PrintConfigDef()
opt->values.push_back(2); opt->values.push_back(2);
def->default_value = opt; def->default_value = opt;
} }
def = this->add("retract_before_wipe", coPercents);
def->label = "Retract amount before wipe";
def->tooltip = "With bowden extruders, it may be wise to do some amount of quick retract before doing the wipe movement.";
def->sidetext = "%";
def->cli = "retract-before-wipe=s@";
{
ConfigOptionPercents* opt = new ConfigOptionPercents();
opt->values.push_back(0.f);
def->default_value = opt;
}
def = this->add("retract_layer_change", coBools); def = this->add("retract_layer_change", coBools);
def->label = "Retract on layer change"; def->label = "Retract on layer change";
@ -1007,7 +1018,7 @@ PrintConfigDef::PrintConfigDef()
} }
def = this->add("retract_speed", coFloats); def = this->add("retract_speed", coFloats);
def->label = "Speed"; def->label = "Retraction Speed";
def->full_label = "Retraction Speed"; def->full_label = "Retraction Speed";
def->tooltip = "The speed for retractions (it only applies to the extruder motor)."; def->tooltip = "The speed for retractions (it only applies to the extruder motor).";
def->sidetext = "mm/s"; def->sidetext = "mm/s";
@ -1018,6 +1029,18 @@ PrintConfigDef::PrintConfigDef()
def->default_value = opt; def->default_value = opt;
} }
def = this->add("deretract_speed", coFloats);
def->label = "Deretraction Speed";
def->full_label = "Deretraction Speed";
def->tooltip = "The speed for loading of a filament into extruder after retraction (it only applies to the extruder motor). If left to zero, the retraction speed is used.";
def->sidetext = "mm/s";
def->cli = "retract-speed=f@";
{
ConfigOptionFloats* opt = new ConfigOptionFloats();
opt->values.push_back(0);
def->default_value = opt;
}
def = this->add("seam_position", coEnum); def = this->add("seam_position", coEnum);
def->label = "Seam position"; def->label = "Seam position";
def->category = "Layers and perimeters"; def->category = "Layers and perimeters";

View File

@ -317,6 +317,7 @@ class GCodeConfig : public virtual StaticPrintConfig
{ {
public: public:
ConfigOptionString before_layer_gcode; ConfigOptionString before_layer_gcode;
ConfigOptionFloats deretract_speed;
ConfigOptionString end_gcode; ConfigOptionString end_gcode;
ConfigOptionString extrusion_axis; ConfigOptionString extrusion_axis;
ConfigOptionFloats extrusion_multiplier; ConfigOptionFloats extrusion_multiplier;
@ -333,6 +334,7 @@ public:
ConfigOptionFloat max_volumetric_speed; ConfigOptionFloat max_volumetric_speed;
ConfigOptionFloat max_volumetric_extrusion_rate_slope_positive; ConfigOptionFloat max_volumetric_extrusion_rate_slope_positive;
ConfigOptionFloat max_volumetric_extrusion_rate_slope_negative; ConfigOptionFloat max_volumetric_extrusion_rate_slope_negative;
ConfigOptionPercents retract_before_wipe;
ConfigOptionFloats retract_length; ConfigOptionFloats retract_length;
ConfigOptionFloats retract_length_toolchange; ConfigOptionFloats retract_length_toolchange;
ConfigOptionFloats retract_lift; ConfigOptionFloats retract_lift;
@ -357,6 +359,7 @@ public:
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(before_layer_gcode); OPT_PTR(before_layer_gcode);
OPT_PTR(deretract_speed);
OPT_PTR(end_gcode); OPT_PTR(end_gcode);
OPT_PTR(extrusion_axis); OPT_PTR(extrusion_axis);
OPT_PTR(extrusion_multiplier); OPT_PTR(extrusion_multiplier);
@ -373,6 +376,7 @@ public:
OPT_PTR(max_volumetric_speed); OPT_PTR(max_volumetric_speed);
OPT_PTR(max_volumetric_extrusion_rate_slope_positive); OPT_PTR(max_volumetric_extrusion_rate_slope_positive);
OPT_PTR(max_volumetric_extrusion_rate_slope_negative); OPT_PTR(max_volumetric_extrusion_rate_slope_negative);
OPT_PTR(retract_before_wipe);
OPT_PTR(retract_length); OPT_PTR(retract_length);
OPT_PTR(retract_length_toolchange); OPT_PTR(retract_length_toolchange);
OPT_PTR(retract_lift); OPT_PTR(retract_lift);

View File

@ -95,6 +95,13 @@ ConfigOption_to_SV(const ConfigOption &opt, const ConfigOptionDef &def) {
} else if (def.type == coPercent) { } else if (def.type == coPercent) {
const ConfigOptionPercent* optv = dynamic_cast<const ConfigOptionPercent*>(&opt); const ConfigOptionPercent* optv = dynamic_cast<const ConfigOptionPercent*>(&opt);
return newSVnv(optv->value); return newSVnv(optv->value);
} else if (def.type == coPercents) {
const ConfigOptionPercents* optv = dynamic_cast<const ConfigOptionPercents*>(&opt);
AV* av = newAV();
av_fill(av, optv->values.size()-1);
for (const double &v : optv->values)
av_store(av, &v - &optv->values.front(), newSVnv(v));
return newRV_noinc((SV*)av);
} else if (def.type == coInt) { } else if (def.type == coInt) {
const ConfigOptionInt* optv = dynamic_cast<const ConfigOptionInt*>(&opt); const ConfigOptionInt* optv = dynamic_cast<const ConfigOptionInt*>(&opt);
return newSViv(optv->value); return newSViv(optv->value);
@ -148,7 +155,7 @@ ConfigBase__get_at(ConfigBase* THIS, const t_config_option_key &opt_key, size_t
if (opt == NULL) return &PL_sv_undef; if (opt == NULL) return &PL_sv_undef;
const ConfigOptionDef* def = THIS->def->get(opt_key); const ConfigOptionDef* def = THIS->def->get(opt_key);
if (def->type == coFloats) { if (def->type == coFloats || def->type == coPercents) {
ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt); ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt);
return newSVnv(optv->get_at(i)); return newSVnv(optv->get_at(i));
} else if (def->type == coInts) { } else if (def->type == coInts) {
@ -191,6 +198,17 @@ ConfigBase__set(ConfigBase* THIS, const t_config_option_key &opt_key, SV* value)
values.push_back(SvNV(*elem)); values.push_back(SvNV(*elem));
} }
optv->values = values; optv->values = values;
} else if (def->type == coPercents) {
ConfigOptionPercents* optv = dynamic_cast<ConfigOptionPercents*>(opt);
std::vector<double> values;
AV* av = (AV*)SvRV(value);
const size_t len = av_len(av)+1;
for (size_t i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0);
if (elem == NULL || !looks_like_number(*elem)) return false;
values.push_back(SvNV(*elem));
}
optv->values = values;
} else if (def->type == coInt) { } else if (def->type == coInt) {
if (!looks_like_number(value)) return false; if (!looks_like_number(value)) return false;
ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt); ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt);

View File

@ -106,7 +106,7 @@ print_config_def()
const char* opt_type; const char* opt_type;
if (optdef->type == coFloat || optdef->type == coFloats || optdef->type == coFloatOrPercent) { if (optdef->type == coFloat || optdef->type == coFloats || optdef->type == coFloatOrPercent) {
opt_type = "f"; opt_type = "f";
} else if (optdef->type == coPercent) { } else if (optdef->type == coPercent || optdef->type == coPercents) {
opt_type = "percent"; opt_type = "percent";
} else if (optdef->type == coInt || optdef->type == coInts) { } else if (optdef->type == coInt || optdef->type == coInts) {
opt_type = "i"; opt_type = "i";

View File

@ -38,8 +38,6 @@
%code%{ RETVAL = THIS->restart_extra = val; %}; %code%{ RETVAL = THIS->restart_extra = val; %};
double e_per_mm3() double e_per_mm3()
%code%{ RETVAL = THIS->e_per_mm3; %}; %code%{ RETVAL = THIS->e_per_mm3; %};
double retract_speed_mm_min()
%code%{ RETVAL = THIS->retract_speed_mm_min; %};
double filament_diameter(); double filament_diameter();
double filament_density(); double filament_density();