Initial integration of the Prusa MultiMatrial Wipe Tower.

This commit is contained in:
bubnikv 2017-05-16 13:45:28 +02:00
parent 74346efccb
commit c22b6edeeb
11 changed files with 922 additions and 439 deletions

View file

@ -558,6 +558,7 @@ sub build {
top_infill_extrusion_width support_material_extrusion_width
infill_overlap bridge_flow_ratio
clip_multipart_objects xy_size_compensation threads resolution
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe
));
$self->{config}->set('print_settings_id', '');
@ -720,6 +721,14 @@ sub build {
$optgroup->append_single_option_line('ooze_prevention');
$optgroup->append_single_option_line('standby_temperature_delta');
}
{
my $optgroup = $page->new_optgroup('Wipe tower');
$optgroup->append_single_option_line('wipe_tower');
$optgroup->append_single_option_line('wipe_tower_x');
$optgroup->append_single_option_line('wipe_tower_y');
$optgroup->append_single_option_line('wipe_tower_width');
$optgroup->append_single_option_line('wipe_tower_per_color_wipe');
}
{
my $optgroup = $page->new_optgroup('Advanced');
$optgroup->append_single_option_line('interface_shells');
@ -955,6 +964,10 @@ sub _update {
my $have_ooze_prevention = $config->ooze_prevention;
$self->get_field($_)->toggle($have_ooze_prevention)
for qw(standby_temperature_delta);
my $have_wipe_tower = $config->wipe_tower;
$self->get_field($_)->toggle($have_wipe_tower)
for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe);
}
sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () }
@ -969,7 +982,7 @@ sub build {
my $self = shift;
$self->init_config_options(qw(
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost
filament_colour filament_diameter filament_type filament_soluble filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
fan_always_on cooling
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
@ -984,6 +997,8 @@ sub build {
$optgroup->append_single_option_line('filament_colour', 0);
$optgroup->append_single_option_line('filament_diameter', 0);
$optgroup->append_single_option_line('extrusion_multiplier', 0);
$optgroup->append_single_option_line('filament_type', 0);
$optgroup->append_single_option_line('filament_soluble', 0);
$optgroup->append_single_option_line('filament_density', 0);
$optgroup->append_single_option_line('filament_cost', 0);
}
@ -1137,7 +1152,7 @@ sub build {
octoprint_host octoprint_apikey
use_firmware_retraction
use_volumetric_e variable_layer_height
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
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
retract_length_toolchange retract_restart_extra_toolchange
@ -1198,6 +1213,7 @@ sub build {
min => 1,
);
$optgroup->append_single_option_line($option);
$optgroup->append_single_option_line('single_extruder_multi_material');
}
$optgroup->on_change(sub {
my ($opt_id) = @_;
@ -1538,6 +1554,7 @@ sub _update {
my $have_multiple_extruders = $self->{extruders_count} > 1;
$self->get_field('toolchange_gcode')->toggle($have_multiple_extruders);
$self->get_field('single_extruder_multi_material')->toggle($have_multiple_extruders);
for my $i (0 .. ($self->{extruders_count}-1)) {
my $have_retract_length = $config->get_at('retract_length', $i) > 0;

View file

@ -2,6 +2,7 @@
#include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp"
#include "Geometry.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include <algorithm>
#include <cstdlib>
@ -384,6 +385,8 @@ bool GCode::do_export(FILE *file, Print &print)
// Set first layer extruder.
this->_print_first_layer_extruder_temperatures(file, print, false);
}
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
std::vector<ToolOrdering::LayerTools> tool_ordering = ToolOrdering::tool_ordering(*object);
// Pair the object layers with the support layers by z, extrude them.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
@ -401,7 +404,9 @@ bool GCode::do_export(FILE *file, Print &print)
-- idx_object_layer;
}
}
this->process_layer(file, print, layers_to_print, &copy - object->_shifted_copies.data());
auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), ToolOrdering::LayerTools(layer_to_print.layer()->print_z));
assert(it_layer_tools != tool_ordering.end() && it_layer_tools->print_z == layer_to_print.layer()->print_z);
this->process_layer(file, print, layers_to_print, *it_layer_tools, &copy - object->_shifted_copies.data());
}
write(file, this->filter(m_cooling_buffer->flush(), true));
++ finished_objects;
@ -437,21 +442,63 @@ bool GCode::do_export(FILE *file, Print &print)
}
++ object_order;
}
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
std::vector<ToolOrdering::LayerTools> tool_ordering = ToolOrdering::tool_ordering(print);
// Prusa Multi-Material wipe tower.
if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value &&
! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) {
// Initialize the wipe tower.
auto *wipe_tower = new WipeTowerPrusaMM(
float(print.config.wipe_tower_x.value), float(print.config.wipe_tower_y.value),
float(print.config.wipe_tower_width.value), float(print.config.wipe_tower_per_color_wipe.value));
//wipe_tower->set_retract();
//wipe_tower->set_zhop();
//wipe_tower->set_zhop();
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < 4; ++ i)
wipe_tower->set_extruder(
i,
WipeTowerPrusaMM::parse_material(print.config.filament_type.get_at(i).c_str()),
print.config.temperature.get_at(i),
print.config.first_layer_temperature.get_at(i));
m_wipe_tower.reset(wipe_tower);
}
// Extrude the layers.
for (auto &layer : layers)
for (auto &layer : layers) {
// layer.second is of type std::vector<LayerToPrint>,
// wher the objects are sorted by their sorted order given by object_indices.
this->process_layer(file, print, layer.second);
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);
if (m_wipe_tower) {
bool first_layer = layer.first == layers.begin()->first;
auto it_layer_tools_next = it_layer_tools;
++ it_layer_tools_next;
m_wipe_tower->set_layer(
layer.first,
first_layer ?
print.objects.front()->config.first_layer_height.get_abs_value(print.objects.front()->config.layer_height.value) :
print.objects.front()->config.layer_height.value,
it_layer_tools->wipe_tower_partitions,
first_layer,
it_layer_tools->wipe_tower_partitions == 0 || (it_layer_tools_next == tool_ordering.end() || it_layer_tools_next->wipe_tower_partitions == 0));
}
this->process_layer(file, print, layer.second, *it_layer_tools, size_t(-1));
}
write(file, this->filter(m_cooling_buffer->flush(), true));
}
// write end commands to file
write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully
if (m_wipe_tower) {
// Unload the current filament over the purge tower.
write(file, this->wipe_tower_tool_change(-1));
m_wipe_tower.release();
} else
write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully
write(file, m_writer.set_fan(false));
writeln(file, m_placeholder_parser.process(print.config.end_gcode));
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
write(file, m_writer.postamble());
// get filament stats
print.filament_stats.clear();
print.total_used_filament = 0.;
@ -543,6 +590,7 @@ void GCode::process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
const ToolOrdering::LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx)
@ -562,7 +610,8 @@ void GCode::process_layer(
}
const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer;
coordf_t print_z = layer.print_z;
bool first_layer = print_z < m_config.first_layer_height.get_abs_value(m_config.layer_height.value) + EPSILON;
bool first_layer = layer.id() == 0;
unsigned int first_extruder_id = layer_tools.extruders.empty() ? 0 : layer_tools.extruders.front();
// Initialize config with the 1st object to be printed at this layer.
m_config.apply(layer.object()->config, true);
@ -620,7 +669,7 @@ void GCode::process_layer(
if (! m_brim_done)
// Switch the extruder to the extruder of the perimeters, so the perimeters extruder will be primed
// by the skirt before the brim is extruded with the same extruder.
gcode += this->set_extruder(print.regions.front()->config.perimeter_extruder.value - 1);
gcode += this->set_extruder(layer_tools.extruders.front());
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
@ -643,10 +692,10 @@ void GCode::process_layer(
std::vector<unsigned int> extruder_ids = m_writer.extruder_ids();
// Reorder the extruders, so that the last used extruder is at the front.
for (size_t i = 1; i < extruder_ids.size(); ++ i)
if (extruder_ids[i] == m_writer.extruder()->id) {
if (extruder_ids[i] == first_extruder_id) {
// Move the last extruder to the front.
memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int));
extruder_ids.front() = m_writer.extruder()->id;
extruder_ids.front() = first_extruder_id;
break;
}
size_t n_loops = print.skirt.entities.size();
@ -668,7 +717,7 @@ void GCode::process_layer(
}
} else
// Extrude all skirts with the current extruder.
skirt_loops_per_extruder[m_writer.extruder()->id] = std::pair<size_t, size_t>(0, print.config.skirts.value);
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config.skirts.value);
}
// Group extrusions by an extruder, then by an object, an island and a region.
@ -684,12 +733,12 @@ void GCode::process_layer(
// Don't change extruder if the extruder is set to 0. Use the current extruder instead.
bool single_extruder =
(object.config.support_material_extruder.value == object.config.support_material_interface_extruder.value ||
(object.config.support_material_extruder.value == int(m_writer.extruder()->id) && object.config.support_material_interface_extruder.value == 0) ||
(object.config.support_material_interface_extruder.value == int(m_writer.extruder()->id) && object.config.support_material_extruder.value == 0));
(object.config.support_material_extruder.value == int(first_extruder_id) && object.config.support_material_interface_extruder.value == 0) ||
(object.config.support_material_interface_extruder.value == int(first_extruder_id) && object.config.support_material_extruder.value == 0));
// Assign an extruder to the base.
ObjectByExtruder &obj = object_by_extruder(
by_extruder,
(object.config.support_material_extruder == 0) ? m_writer.extruder()->id : (object.config.support_material_extruder - 1),
(object.config.support_material_extruder == 0) ? first_extruder_id : (object.config.support_material_extruder - 1),
&layer_to_print - layers.data(),
layers.size());
obj.support = &support_layer.support_fills;
@ -697,7 +746,7 @@ void GCode::process_layer(
if (! single_extruder) {
ObjectByExtruder &obj_interface = object_by_extruder(
by_extruder,
(object.config.support_material_interface_extruder == 0) ? m_writer.extruder()->id : (object.config.support_material_interface_extruder - 1),
(object.config.support_material_interface_extruder == 0) ? first_extruder_id : (object.config.support_material_interface_extruder - 1),
&layer_to_print - layers.data(),
layers.size());
obj_interface.support = &support_layer.support_fills;
@ -793,31 +842,14 @@ void GCode::process_layer(
}
} // for objects
// Tweak extruder ordering to save toolchanges.
std::vector<unsigned int> extruders;
extruders.reserve(by_extruder.size());
for (const auto &ex : by_extruder)
extruders.push_back(ex.first);
if (extrude_skirt) {
// Merge with the skirt extruders.
for (const auto &ex : skirt_loops_per_extruder)
extruders.push_back(ex.first);
sort_remove_duplicates(extruders);
}
// Reorder the extruders, so that the last used extruder is at the front.
for (size_t i = 1; i < extruders.size(); ++ i)
if (extruders[i] == m_writer.extruder()->id) {
// Move the last extruder to the front.
memmove(extruders.data() + 1, extruders.data(), i * sizeof(unsigned int));
extruders.front() = m_writer.extruder()->id;
break;
}
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : extruders)
for (unsigned int extruder_id : layer_tools.extruders)
{
gcode += this->set_extruder(extruder_id);
//FIXME here will come the priming tower call.
if (m_wipe_tower && ! m_wipe_tower->finished() && extruder_id == layer_tools.extruders.back())
// Last extruder change on the layer or no extruder change at all.
m_wipe_tower->close_layer();
if (extrude_skirt) {
auto loops_it = skirt_loops_per_extruder.find(extruder_id);
@ -1675,7 +1707,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
this->set_last_pos(path.last_point());
if (m_config.cooling)
m_elapsed_time += path_length / F * 60;
m_elapsed_time += path_length / F * 60.f;
return gcode;
}
@ -1814,21 +1846,39 @@ std::string GCode::set_extruder(unsigned int extruder_id)
gcode += pp.process(m_config.toolchange_gcode.value) + '\n';
}
// if ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature
if (m_ooze_prevention.enable && m_writer.extruder() != NULL)
gcode += m_ooze_prevention.pre_toolchange(*this);
// append the toolchange command
gcode += m_writer.toolchange(extruder_id);
// set the new extruder to the operating temperature
if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this);
if (m_wipe_tower) {
assert(! m_wipe_tower->finished());
if (! m_wipe_tower->finished())
gcode += this->wipe_tower_tool_change(extruder_id);
} else {
// if ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature
if (m_ooze_prevention.enable && m_writer.extruder() != NULL)
gcode += m_ooze_prevention.pre_toolchange(*this);
// append the toolchange command
gcode += m_writer.toolchange(extruder_id);
// set the new extruder to the operating temperature
if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this);
}
return gcode;
}
std::string GCode::wipe_tower_tool_change(int extruder_id)
{
// Move over the wipe tower.
std::string gcode = m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y));
gcode += m_writer.unlift();
// Let the tool change be executed by the wipe tower class.
std::pair<std::string, WipeTower::xy> code_and_pos = m_wipe_tower->tool_change(extruder_id);
// Inform the G-code writer about the changes done behind its back.
gcode += code_and_pos.first;
// A phony move to the end position at the wipe tower.
m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
return gcode;
}
// convert a model-space scaled point into G-code coordinates
Pointf GCode::point_to_gcode(const Point &point) const
{

View file

@ -13,6 +13,8 @@
#include "GCode/CoolingBuffer.hpp"
#include "GCode/PressureEqualizer.hpp"
#include "GCode/SpiralVase.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "EdgeGrid.hpp"
#include <memory>
@ -129,6 +131,7 @@ private:
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
const ToolOrdering::LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@ -172,6 +175,7 @@ private:
std::string retract(bool toolchange = false);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id);
std::string wipe_tower_tool_change(int extruder_id);
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
@ -218,6 +222,7 @@ private:
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
std::unique_ptr<SpiralVase> m_spiral_vase;
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
std::unique_ptr<WipeTower> m_wipe_tower;
// Heights at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done;

View file

@ -0,0 +1,194 @@
#include "ToolOrdering.hpp"
namespace Slic3r {
namespace ToolOrdering {
// Collect extruders reuqired to print layers.
static void collect_extruders(const PrintObject &object, std::vector<LayerTools> &layers)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers) {
auto it_layer = std::find(layers.begin(), layers.end(), LayerTools(support_layer->print_z));
assert(it_layer != layers.end());
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
unsigned int extruder_support = object.config.support_material_extruder.value;
unsigned int extruder_interface = object.config.support_material_interface_extruder.value;
if (has_support && has_interface) {
// If both base and interface supports are to be extruded and one of them will be extruded with a "don't care" extruder,
// print both with the same extruder to minimize extruder switches.
if (extruder_support == 0)
extruder_support = extruder_interface;
else if (extruder_interface == 0)
extruder_interface = extruder_support;
}
if (has_support)
it_layer->extruders.push_back(extruder_support);
if (has_interface)
it_layer->extruders.push_back(extruder_interface);
}
// Collect the object extruders.
for (auto layer : object.layers) {
auto it_layer = std::find(layers.begin(), layers.end(), LayerTools(layer->print_z));
assert(it_layer != layers.end());
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) {
const LayerRegion *layerm = layer->regions[region_id];
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions[region_id];
if (! layerm->perimeters.entities.empty())
it_layer->extruders.push_back(region.config.perimeter_extruder.value);
bool has_infill = false;
bool has_solid_infill = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
ExtrusionRole role = fill->entities.empty() ? erNone : fill->entities.front()->role();
if (is_solid_infill(role))
has_solid_infill = true;
else if (role != erNone)
has_infill = true;
}
if (has_solid_infill)
it_layer->extruders.push_back(region.config.solid_infill_extruder);
if (has_infill)
it_layer->extruders.push_back(region.config.infill_extruder);
}
}
// Sort and remove duplicates
for (LayerTools &lt : layers)
sort_remove_duplicates(lt.extruders);
}
// Reorder extruders to minimize layer changes.
static void reorder_extruders(std::vector<LayerTools> &layers)
{
if (layers.empty())
return;
// Initialize the last_extruder_id with the first non-zero extruder id used for the print.
unsigned int last_extruder_id = 0;
for (size_t i = 0; i < layers.size() && last_extruder_id == 0; ++ i) {
const LayerTools &lt = layers[i];
for (unsigned int extruder_id : lt.extruders)
if (extruder_id > 0) {
last_extruder_id = extruder_id;
break;
}
}
if (last_extruder_id == 0)
last_extruder_id = 1;
for (LayerTools &lt : layers) {
if (lt.extruders.empty())
continue;
if (lt.extruders.size() == 1 && lt.extruders.front() == 0)
lt.extruders.front() = last_extruder_id;
else {
if (lt.extruders.front() == 0)
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
lt.extruders.erase(lt.extruders.begin());
// Reorder the extruders to start with the last one.
for (size_t i = 1; i < lt.extruders.size(); ++ i)
if (lt.extruders[i] == last_extruder_id) {
// Move the last extruder to the front.
memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int));
lt.extruders.front() = last_extruder_id;
break;
}
}
last_extruder_id = lt.extruders.back();
}
// Reindex the extruders, so they are zero based, not 1 based.
for (LayerTools &lt : layers)
for (unsigned int &extruder_id : lt.extruders) {
assert(extruder_id > 0);
-- extruder_id;
}
}
static void fill_wipe_tower_partitions(std::vector<LayerTools> &layers)
{
if (layers.empty())
return;
// Count the minimum number of tool changes per layer.
for (LayerTools &lt : layers)
lt.wipe_tower_partitions = std::max<int>(0, int(layers.front().extruders.size()) - 1);
// In case a distinct set of tools are used between two layers, there will be an additional tool change at the start of a layer.
//FIXME this does not minimize the number of tool changes in worst case.
for (size_t i = 1; i < layers.size(); ++ i)
if (layers[i-1].extruders.back() != layers[i].extruders.front())
++ layers[i].wipe_tower_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)
layers[i].wipe_tower_partitions = std::max(layers[i + 1].wipe_tower_partitions, layers[i].wipe_tower_partitions);
}
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
std::vector<LayerTools> tool_ordering(PrintObject &object)
{
// Initialize the print layers for just a single object.
std::vector<LayerTools> layers;
{
std::vector<coordf_t> zs;
zs.reserve(zs.size() + object.layers.size() + object.support_layers.size());
for (auto layer : object.layers)
zs.emplace_back(layer->print_z);
for (auto layer : object.support_layers)
zs.emplace_back(layer->print_z);
sort_remove_duplicates(zs);
for (coordf_t z : zs)
layers.emplace_back(LayerTools(z));
}
// Collect extruders reuqired to print the layers.
collect_extruders(object, layers);
// Reorder the extruders to minimize tool switches.
reorder_extruders(layers);
fill_wipe_tower_partitions(layers);
return layers;
}
// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
std::vector<LayerTools> tool_ordering(const Print &print)
{
// Initialize the print layers for all objects and all layers.
std::vector<LayerTools> layers;
{
std::vector<coordf_t> zs;
for (auto object : print.objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.emplace_back(layer->print_z);
for (auto layer : object->support_layers)
zs.emplace_back(layer->print_z);
}
sort_remove_duplicates(zs);
for (coordf_t z : zs)
layers.emplace_back(LayerTools(z));
}
// Collect extruders reuqired to print the layers.
for (auto object : print.objects)
collect_extruders(*object, layers);
// Reorder the extruders to minimize tool switches.
reorder_extruders(layers);
fill_wipe_tower_partitions(layers);
return layers;
}
} // namespace ToolOrdering
} // namespace Slic3r

View file

@ -0,0 +1,38 @@
// Ordering of the tools to minimize tool switches.
#ifndef slic3r_ToolOrdering_hpp_
#define slic3r_ToolOrdering_hpp_
#include "libslic3r.h"
#include "Print.hpp"
namespace Slic3r {
namespace ToolOrdering {
struct LayerTools
{
LayerTools(const coordf_t z) : print_z(z), 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; }
coordf_t print_z;
// Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders;
// Number of wipe tower partitions to support the required number of tool switches
// and to support the wipe tower partitions above this one.
size_t wipe_tower_partitions;
};
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
extern std::vector<LayerTools> tool_ordering(PrintObject &object);
// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
extern std::vector<LayerTools> tool_ordering(const Print &print);
} // namespace ToolOrdering
} // namespace SLic3r
#endif /* slic3r_ToolOrdering_hpp_ */

View file

@ -0,0 +1,59 @@
#ifndef slic3r_WipeTower_hpp_
#define slic3r_WipeTower_hpp_
#include <utility>
#include <string>
namespace Slic3r
{
// A pure virtual WipeTower definition.
class WipeTower
{
public:
// Internal point class, to make the wipe tower independent from other slic3r modules.
// This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r.
struct xy
{
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
float x;
float y;
};
WipeTower() {}
virtual ~WipeTower() {}
// Return the wipe tower position.
virtual const xy& position() const = 0;
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const = 0;
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
// Is this the last layer of the wipe tower?
bool is_last_layer) = 0;
// Returns gcode for toolchange and the end position.
// if new_tool == -1, just unload the current filament over the wipe tower.
virtual std::pair<std::string, xy> tool_change(int new_tool) = 0;
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
virtual std::pair<std::string, xy> close_layer() = 0;
};
}; // namespace Slic3r
#endif /* slic3r_WipeTower_hpp_ */

View file

@ -1,4 +1,4 @@
#include "WipeTower.hpp"
#include "WipeTowerPrusaMM.hpp"
#include <assert.h>
#include <math.h>
@ -13,9 +13,11 @@
#define strcasecmp _stricmp
#endif
namespace PrusaSingleExtruderMM
namespace Slic3r
{
namespace PrusaMultiMaterial {
class Writer
{
public:
@ -171,18 +173,18 @@ public:
return *this;
};
Writer& comment_material(WipeTower::material_type material)
Writer& comment_material(WipeTowerPrusaMM::material_type material)
{
m_gcode += "; material : ";
switch (material)
{
case WipeTower::PVA:
case WipeTowerPrusaMM::PVA:
m_gcode += "#8 (PVA)";
break;
case WipeTower::SCAFF:
case WipeTowerPrusaMM::SCAFF:
m_gcode += "#5 (Scaffold)";
break;
case WipeTower::FLEX:
case WipeTowerPrusaMM::FLEX:
m_gcode += "#4 (Flex)";
break;
default:
@ -236,6 +238,8 @@ private:
}
};
} // namespace PrusaMultiMaterial
static inline int randi(int lo, int hi)
{
int n = hi - lo + 1;
@ -244,7 +248,7 @@ static inline int randi(int lo, int hi)
return lo + i;
}
WipeTower::material_type WipeTower::parse_material(const char *name)
WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name)
{
if (strcasecmp(name, "PLA") == 0)
return PLA;
@ -267,116 +271,58 @@ WipeTower::material_type WipeTower::parse_material(const char *name)
return INVALID;
}
std::string WipeTower::FirstLayer(bool sideOnly, float y_offset)
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool)
{
const box_coordinates wipeTower_box(
m_wipe_tower_pos,
m_wipe_tower_width,
m_wipe_area * float(m_color_changes) - perimeterWidth / 2);
// Either it is the last tool unload,
// or there must be a nonzero wipe tower partitions available.
assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
Writer writer;
writer.set_extrusion_flow(extrusion_flow * 1.1f)
// Let the writer know the current Z position as a base for Z-hop.
.set_z(m_z_pos)
.append(
";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
if (m_layer_change_in_layer == size_t(-1))
// First layer, prime the extruder.
return toolchange_Brim(tool);
// Move with Z hop and prime the extruder 10*perimeterWidth left along the vertical edge of the wipe tower.
writer.z_hop(zHop, 7200)
.travel(wipeTower_box.lu - xy(perimeterWidth * 10.f, 0), 6000)
.z_hop(0, 7200)
.extrude_explicit(wipeTower_box.ld - xy(perimeterWidth * 10.f, 0), retract, 2400)
.feedrate(2100);
if (sideOnly) {
float x_offset = 0.f;
for (size_t i = 0; i < 4; ++ i, x_offset += perimeterWidth)
writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset))
.extrude(wipeTower_box.lu + xy(- x_offset, - y_offset));
writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000)
.feedrate(2100);
x_offset = 0.f;
for (size_t i = 0; i < 4; ++ i, x_offset += perimeterWidth)
writer.travel (wipeTower_box.rd + xy(x_offset, y_offset))
.extrude(wipeTower_box.ru + xy(x_offset, - y_offset));
} else {
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
box.ld += xy(- perimeterWidth / 2, 0);
box.lu += xy(- perimeterWidth / 2, perimeterWidth);
box.rd += xy( perimeterWidth / 2, 0);
box.ru += xy( perimeterWidth / 2, perimeterWidth);
for (size_t i = 0; i < 4; ++ i) {
writer.travel(box.ld)
.extrude(box.lu) .extrude(box.ru)
.extrude(box.rd) .extrude(box.ld);
box.expand(perimeterWidth);
}
}
// Move to the front left corner and wipe along the front edge.
writer.travel(wipeTower_box.ld, 7000)
.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld)
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
return writer.gcode();
}
std::pair<std::string, WipeTower::xy> WipeTower::Toolchange(
const int tool,
const material_type current_material,
const material_type new_material,
const int temperature,
const wipe_shape shape,
const int count,
const float spaceAvailable,
const float wipeStartY,
const bool lastInFile,
const bool colorInit)
{
box_coordinates cleaning_box(
m_wipe_tower_pos.x,
m_wipe_tower_pos.y + wipeStartY,
m_wipe_tower_pos.y + m_current_wipe_start_y,
m_wipe_tower_width,
spaceAvailable - perimeterWidth / 2);
m_wipe_area - m_perimeter_width / 2);
Writer writer;
writer.set_extrusion_flow(extrusion_flow)
PrusaMultiMaterial::Writer writer;
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.append(";--------------------\n"
"; CP TOOLCHANGE START\n")
.comment_with_value(" toolchange #", count)
.comment_material(current_material)
.comment_with_value(" toolchange #", m_layer_change_total)
.comment_material(m_current_material)
.append(";--------------------\n")
.speed_override(100)
// Lift for a Z hop.
.z_hop(zHop, 7200)
.z_hop(m_zhop, 7200)
// additional retract on move to tower
.retract(retract/2, 3600)
.travel(((shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.rd) + xy(perimeterWidth, shape * perimeterWidth), 7200)
.retract(m_retract/2, 3600)
.travel(((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.rd) + xy(m_perimeter_width, m_current_shape * m_perimeter_width), 7200)
// Unlift for a Z hop.
.z_hop(0, 7200)
// Additional retract on move to tower.
.deretract(retract/2, 3600)
.deretract(retract, 1500)
.deretract(m_retract/2, 3600)
.deretract(m_retract, 1500)
// Increase extruder current for ramming.
.set_extruder_trimpot(750)
.flush_planner_queue();
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
toolchange_Unload(writer, cleaning_box, current_material, shape, temperature);
toolchange_Unload(writer, cleaning_box, m_current_material, m_current_shape,
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
if (! lastInFile) {
if (tool >= 0) {
// This is not the last change.
// Change the tool, set a speed override for solube and flex materials.
toolchange_Change(writer, tool, current_material, new_material);
toolchange_Load(writer, cleaning_box, current_material, shape, colorInit);
toolchange_Change(writer, tool, m_current_material, m_material[tool]);
toolchange_Load(writer, cleaning_box);
// Wipe the newly loaded filament until the end of the assigned wipe area.
toolchange_Wipe(writer, cleaning_box, current_material, shape);
toolchange_Wipe(writer, cleaning_box, m_current_material);
// Draw a perimeter around cleaning_box and wipe.
toolchange_Done(writer, cleaning_box, current_material, shape);
toolchange_Done(writer, cleaning_box);
}
// Reset the extruder current to a normal value.
@ -387,20 +333,83 @@ std::pair<std::string, WipeTower::xy> WipeTower::Toolchange(
";------------------\n"
"\n\n");
++ m_layer_change_in_layer;
m_current_wipe_start_y += m_wipe_area;
m_current_material = m_material[tool];
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
}
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(size_t tool, bool sideOnly, float y_offset)
{
const box_coordinates wipeTower_box(
m_wipe_tower_pos,
m_wipe_tower_width,
m_wipe_area * float(m_max_color_changes) - m_perimeter_width / 2);
PrusaMultiMaterial::Writer writer;
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
// Let the writer know the current Z position as a base for Z-hop.
.set_z(m_z_pos)
.append(
";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
// Move with Z hop and prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower.
writer.z_hop(m_zhop, 7200)
.travel(wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0), 6000)
.z_hop(0, 7200)
.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400)
.feedrate(2100);
toolchange_Change(writer, tool, m_current_material, m_material[tool]);
if (sideOnly) {
float x_offset = 0.f;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset))
.extrude(wipeTower_box.lu + xy(- x_offset, - y_offset));
writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000)
.feedrate(2100);
x_offset = 0.f;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.rd + xy(x_offset, y_offset))
.extrude(wipeTower_box.ru + xy(x_offset, - y_offset));
} else {
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
box.ld += xy(- m_perimeter_width / 2, 0);
box.lu += xy(- m_perimeter_width / 2, m_perimeter_width);
box.rd += xy( m_perimeter_width / 2, 0);
box.ru += xy( m_perimeter_width / 2, m_perimeter_width);
for (size_t i = 0; i < 4; ++ i) {
writer.travel(box.ld)
.extrude(box.lu) .extrude(box.ru)
.extrude(box.rd) .extrude(box.ld);
box.expand(m_perimeter_width);
}
}
// Move to the front left corner and wipe along the front edge.
writer.travel(wipeTower_box.ld, 7000)
.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld)
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
}
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
void WipeTower::toolchange_Unload(
Writer &writer,
void WipeTowerPrusaMM::toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape,
const int temperature)
{
float xl = cleaning_box.ld.x + (perimeterWidth / 2);
float xr = cleaning_box.rd.x - (perimeterWidth / 2);
float y_step = shape * perimeterWidth;
float xl = cleaning_box.ld.x + (m_perimeter_width / 2);
float xr = cleaning_box.rd.x - (m_perimeter_width / 2);
float y_step = shape * m_perimeter_width;
writer.append("; CP TOOLCHANGE UNLOAD");
@ -409,20 +418,20 @@ void WipeTower::toolchange_Unload(
{
case PVA:
// ramming start end y increment amount feedrate
writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 1.2f, 3, 4000)
.ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.5f, 3, 4500)
.ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 1.5f, 3, 4800)
.ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.5f, 3, 5000);
writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 1.2f, 3, 4000)
.ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 3, 4500)
.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 3, 4800)
.ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 3, 5000);
break;
case SCAFF:
writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 3.f, 3, 4000)
.ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 3.f, 4, 4600)
.ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 3.f, 4.5, 5200);
writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 3.f, 3, 4000)
.ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 3.f, 4, 4600)
.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f, 4.5, 5200);
break;
default:
writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 1.2f, 1.6f, 4000)
.ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.2f, 1.65f, 4600)
.ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 1.2f, 1.74f, 5200);
writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 1.2f, 1.6f, 4000)
.ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.2f, 1.65f, 4600)
.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, 1.74f, 5200);
}
// Pull the filament end into a cooling tube.
@ -462,8 +471,8 @@ void WipeTower::toolchange_Unload(
}
// Change the tool, set a speed override for solube and flex materials.
void WipeTower::toolchange_Change(
Writer &writer,
void WipeTowerPrusaMM::toolchange_Change(
PrusaMultiMaterial::Writer &writer,
const int tool,
material_type /* current_material */,
material_type new_material)
@ -481,15 +490,12 @@ void WipeTower::toolchange_Change(
.flush_planner_queue();
}
void WipeTower::toolchange_Load(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type /* material */,
const wipe_shape shape,
const bool colorInit)
void WipeTowerPrusaMM::toolchange_Load(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box)
{
float xl = cleaning_box.ld.x + perimeterWidth;
float xr = cleaning_box.rd.x - perimeterWidth;
float xl = cleaning_box.ld.x + m_perimeter_width;
float xr = cleaning_box.rd.x - m_perimeter_width;
writer.append("; CP TOOLCHANGE LOAD\n")
// Load the filament while moving left / right,
@ -501,11 +507,12 @@ void WipeTower::toolchange_Load(
// Extrude first five lines (just three lines if colorInit is set).
writer.extrude(xr, writer.y(), 1600);
bool colorInit = false;
size_t pass = colorInit ? 1 : 2;
for (int i = 0; i < pass; ++ i)
writer.travel (xr, writer.y() + shape * perimeterWidth * 0.85f, 2200)
writer.travel (xr, writer.y() + m_current_shape * m_perimeter_width * 0.85f, 2200)
.extrude(xl, writer.y())
.travel (xl, writer.y() + shape * perimeterWidth * 0.85f)
.travel (xl, writer.y() + m_current_shape * m_perimeter_width * 0.85f)
.extrude(xr, writer.y());
// Reset the extruder current to the normal value.
@ -513,52 +520,49 @@ void WipeTower::toolchange_Load(
}
// Wipe the newly loaded filament until the end of the assigned wipe area.
void WipeTower::toolchange_Wipe(
Writer &writer,
void WipeTowerPrusaMM::toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape)
const material_type material)
{
// Increase flow on first layer, slow down print.
writer.set_extrusion_flow(extrusion_flow * (is_first_layer() ? 1.18f : 1.f))
writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
.append("; CP TOOLCHANGE WIPE\n");
float wipe_coeff = is_first_layer() ? 0.5f : 1.f;
float xl = cleaning_box.ld.x + 2.f * perimeterWidth;
float xr = cleaning_box.rd.x - 2.f * perimeterWidth;
float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
float xl = cleaning_box.ld.x + 2.f * m_perimeter_width;
float xr = cleaning_box.rd.x - 2.f * m_perimeter_width;
// Wipe speed will increase up to 4800.
float wipe_speed = 4200;
// Y increment per wipe line.
float dy = shape * perimeterWidth * 0.7f;
float dy = m_current_shape * m_perimeter_width * 0.7f;
for (bool p = true; ; p = ! p) {
writer.feedrate((wipe_speed = std::min(4800.f, wipe_speed + 50.f)) * wipe_coeff);
if (p)
writer.extrude(xl - perimeterWidth/2, writer.y() + dy)
.extrude(xr + perimeterWidth, writer.y());
writer.extrude(xl - m_perimeter_width/2, writer.y() + dy)
.extrude(xr + m_perimeter_width, writer.y());
else
writer.extrude(xl - perimeterWidth, writer.y() + dy)
.extrude(xr + perimeterWidth*2, writer.y());
writer.extrude(xl - m_perimeter_width, writer.y() + dy)
.extrude(xr + m_perimeter_width*2, writer.y());
writer.feedrate((wipe_speed = std::min(4800.f, wipe_speed + 50.f)) * wipe_coeff)
.extrude(xr + perimeterWidth, writer.y() + dy)
.extrude(xl - perimeterWidth, writer.y());
if ((shape == SHAPE_NORMAL) ?
(writer.y() > cleaning_box.lu.y - perimeterWidth) :
(writer.y() < cleaning_box.ld.y + perimeterWidth))
.extrude(xr + m_perimeter_width, writer.y() + dy)
.extrude(xl - m_perimeter_width, writer.y());
if ((m_current_shape == SHAPE_NORMAL) ?
(writer.y() > cleaning_box.lu.y - m_perimeter_width) :
(writer.y() < cleaning_box.ld.y + m_perimeter_width))
// Next wipe line does not fit the cleaning box.
break;
}
// Reset the extrusion flow.
writer.set_extrusion_flow(extrusion_flow);
writer.set_extrusion_flow(m_extrusion_flow);
}
// Draw a perimeter around cleaning_box and wipe.
void WipeTower::toolchange_Done(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type /* material */,
const wipe_shape shape)
void WipeTowerPrusaMM::toolchange_Done(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box)
{
box_coordinates box = cleaning_box;
if (shape == SHAPE_REVERSED) {
if (m_current_shape == SHAPE_REVERSED) {
std::swap(box.lu, box.ld);
std::swap(box.ru, box.rd);
}
@ -572,82 +576,86 @@ void WipeTower::toolchange_Done(
.feedrate(6000);
}
std::string WipeTower::Perimeter(int order, int total, int Layer, bool afterToolchange, int firstLayerOffset)
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::close_layer()
{
Writer writer;
writer.set_extrusion_flow(extrusion_flow)
PrusaMultiMaterial::Writer writer;
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.append(";--------------------\n"
"; CP EMPTY GRID START\n")
.comment_with_value(" layer #", Layer);
.comment_with_value(" layer #", m_layer_change_total ++);
// Slow down on the 1st layer.
float speed_factor = is_first_layer() ? 0.5f : 1.f;
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
box_coordinates _p = _boxForColor(order);
box_coordinates _p = _boxForColor(m_layer_change_in_layer);
{
box_coordinates _to = _boxForColor(total);
box_coordinates _to = _boxForColor(m_max_color_changes);
float firstLayerOffset = 0.f;
_p.ld.y += firstLayerOffset;
_p.rd.y += firstLayerOffset;
_p.lu = _to.lu; _p.ru = _to.ru;
}
if (! afterToolchange)
if (m_layer_change_in_layer == 0)
// There were no tool changes at all in this layer.
// Jump with retract to _p.ld + a random shift in +x.
writer.retract(retract * 1.5f, 3600)
.z_hop(zHop, 7200)
writer.retract(m_retract * 1.5f, 3600)
.z_hop(m_zhop, 7200)
.travel(_p.ld.x + randi(5, 20), _p.ld.y, 7000)
.z_hop(0, 7200)
.extrude_explicit(_p.ld, retract * 1.5f, 3600);
.extrude_explicit(_p.ld, m_retract * 1.5f, 3600);
box_coordinates box = _p;
writer.extrude(box.lu, 2400 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(perimeterWidth / 2, 0));
.extrude(box.ld + xy(m_perimeter_width / 2, 0));
box.expand(- perimeterWidth / 2);
box.expand(- m_perimeter_width / 2);
writer.extrude(box.lu, 3200 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(perimeterWidth / 2, 0))
.extrude(box.ld + xy(perimeterWidth / 2, perimeterWidth / 2));
.extrude(box.ld + xy(m_perimeter_width / 2, 0))
.extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2));
writer.extrude(_p.ld + xy(perimeterWidth * 3, perimeterWidth), 2900 * speed_factor)
.extrude(_p.lu + xy(perimeterWidth * 3, - perimeterWidth))
.extrude(_p.lu + xy(perimeterWidth * 6, - perimeterWidth))
.extrude(_p.ld + xy(perimeterWidth * 6, perimeterWidth));
writer.extrude(_p.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor)
.extrude(_p.lu + xy(m_perimeter_width * 3, - m_perimeter_width))
.extrude(_p.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
.extrude(_p.ld + xy(m_perimeter_width * 6, m_perimeter_width));
if (_p.lu.y - _p.ld.y > 4) {
// Extrude three zig-zags.
writer.feedrate(3200 * speed_factor);
float step = (m_wipe_tower_width - perimeterWidth * 12.f) / 12.f;
float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f;
for (size_t i = 0; i < 3; ++ i) {
writer.extrude(writer.x() + step, _p.ld.y + perimeterWidth * 8);
writer.extrude(writer.x() , _p.lu.y - perimeterWidth * 8);
writer.extrude(writer.x() + step, _p.lu.y - perimeterWidth );
writer.extrude(writer.x() + step, _p.lu.y - perimeterWidth * 8);
writer.extrude(writer.x() , _p.ld.y + perimeterWidth * 8);
writer.extrude(writer.x() + step, _p.ld.y + perimeterWidth );
writer.extrude(writer.x() + step, _p.ld.y + m_perimeter_width * 8);
writer.extrude(writer.x() , _p.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() + step, _p.lu.y - m_perimeter_width );
writer.extrude(writer.x() + step, _p.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() , _p.ld.y + m_perimeter_width * 8);
writer.extrude(writer.x() + step, _p.ld.y + m_perimeter_width );
}
}
writer.extrude(_p.ru + xy(- perimeterWidth * 6, - perimeterWidth), 2900 * speed_factor)
.extrude(_p.ru + xy(- perimeterWidth * 3, - perimeterWidth))
.extrude(_p.rd + xy(- perimeterWidth * 3, perimeterWidth))
.extrude(_p.rd + xy(- perimeterWidth, perimeterWidth))
// Extrude the perimeter.
writer.extrude(_p.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
.extrude(_p.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
.extrude(_p.rd + xy(- m_perimeter_width * 3, m_perimeter_width))
.extrude(_p.rd + xy(- m_perimeter_width, m_perimeter_width))
// Wipe along the front side of the current wiping box.
.travel(_p.ld + xy( perimeterWidth, perimeterWidth / 2), 7200)
.travel(_p.rd + xy(- perimeterWidth, perimeterWidth / 2))
.travel(_p.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
.travel(_p.rd + xy(- m_perimeter_width, m_perimeter_width / 2))
.append("; CP EMPTY GRID END\n"
";------------------\n\n\n\n\n\n\n");
return writer.gcode();
m_current_shape = wipe_shape(- m_current_shape);
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
}
WipeTower::box_coordinates WipeTower::_boxForColor(int order) const
WipeTowerPrusaMM::box_coordinates WipeTowerPrusaMM::_boxForColor(int order) const
{
return box_coordinates(m_wipe_tower_pos.x, m_wipe_tower_pos.y + m_wipe_area * order - perimeterWidth / 2, m_wipe_tower_width, perimeterWidth);
return box_coordinates(m_wipe_tower_pos.x, m_wipe_tower_pos.y + m_wipe_area * order - m_perimeter_width / 2, m_wipe_tower_width, m_perimeter_width);
}
}; // namespace PrusaSingleExtruderMM
}; // namespace Slic3r

View file

@ -0,0 +1,217 @@
#ifndef WipeTowerPrusaMM_hpp_
#define WipeTowerPrusaMM_hpp_
#include <algorithm>
#include <string>
#include <utility>
#include "WipeTower.hpp"
namespace Slic3r
{
namespace PrusaMultiMaterial {
class Writer;
};
class WipeTowerPrusaMM : public WipeTower
{
public:
enum material_type
{
INVALID = -1,
PLA = 0, // E:210C B:55C
ABS = 1, // E:255C B:100C
PET = 2, // E:240C B:90C
HIPS = 3, // E:220C B:100C
FLEX = 4, // E:245C B:80C
SCAFF = 5, // E:215C B:55C
EDGE = 6, // E:240C B:80C
NGEN = 7, // E:230C B:80C
PVA = 8 // E:210C B:80C
};
// Parse material name into material_type.
static material_type parse_material(const char *name);
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTowerPrusaMM(float x, float y, float width, float wipe_area) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_area(wipe_area),
m_z_pos(0.f) {
for (size_t i = 0; i < 4; ++ i) {
// Extruder specific parameters.
m_material[i] = PLA;
m_temperature[i] = 0;
m_first_layer_temperature[i] = 0;
}
}
virtual ~WipeTowerPrusaMM() {}
// _retract - retract value in mm
void set_retract(float retract) { m_retract = retract; }
// _zHop - z hop value in mm
void set_zhop(float zhop) { m_zhop = zhop; }
// Set the extruder properties.
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
{
m_material[idx] = material;
m_temperature[idx] = temp;
m_first_layer_temperature[idx] = first_layer_temp;
}
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
// Is this the last layer of the waste tower?
bool is_last_layer)
{
m_z_pos = print_z;
m_max_color_changes = max_tool_changes;
m_is_first_layer = is_first_layer;
m_is_last_layer = is_last_layer;
// Start counting the color changes from zero.
m_layer_change_in_layer = is_first_layer ? size_t(-1) : 0;
m_current_wipe_start_y = 0.f;
int layer_idx = int(floor(layer_height * 100) + 0.5f);
switch (layer_idx)
{
case 15:
m_extrusion_flow = (float)0.024;
break;
case 20:
default:
m_extrusion_flow = (float)0.029;
break;
}
}
// Return the wipe tower position.
virtual const xy& position() const { return m_wipe_tower_pos; }
// 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; }
// Returns gcode for toolchange
virtual std::pair<std::string, xy> tool_change(int new_tool);
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
virtual std::pair<std::string, xy> close_layer();
private:
WipeTowerPrusaMM();
enum wipe_shape
{
SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
// Left front corner of the wipe tower in mm.
xy m_wipe_tower_pos;
// Width of the wipe tower.
float m_wipe_tower_width;
// Per color Y span.
float m_wipe_area;
// Current Z position.
float m_z_pos = 0.f;
// Maximum number of color changes per layer.
size_t m_max_color_changes = 0;
// Is this the 1st layer of the print? If so, print the brim around the waste tower.
bool m_is_first_layer = false;
// Is this the last layer of this waste tower?
bool m_is_last_layer = false;
// G-code generator parameters.
float m_zhop = 0.5f;
float m_retract = 4.f;
float m_perimeter_width = 0.5f;
float m_extrusion_flow = 0.029f;
// Extruder specific parameters.
material_type m_material[4];
int m_temperature[4];
int m_first_layer_temperature[4];
// State of the wiper tower generator.
// Layer change counter for the output statistics.
unsigned int m_layer_change_total = 0;
// Layer change counter in this layer. Counting up to m_max_color_changes.
unsigned int m_layer_change_in_layer = 0;
wipe_shape m_current_shape = SHAPE_NORMAL;
material_type m_current_material = PLA;
// Current y position at the wipe tower.
float m_current_wipe_start_y = 0.f;
struct box_coordinates
{
box_coordinates(float left, float bottom, float width, float height) :
ld(left , bottom ),
lu(left , bottom + height),
rd(left + width, bottom ),
ru(left + width, bottom + height) {}
box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
void expand(const float offset) {
ld += xy(- offset, - offset);
lu += xy(- offset, offset);
rd += xy( offset, - offset);
ru += xy( offset, offset);
}
xy ld; // left down
xy lu; // left upper
xy ru; // right upper
xy rd; // right lower
};
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
std::pair<std::string, WipeTower::xy> toolchange_Brim(size_t tool, bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape,
const int temperature);
void toolchange_Change(
PrusaMultiMaterial::Writer &writer,
int tool,
material_type current_material,
material_type new_material);
void toolchange_Load(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box);
void toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
const material_type material);
void toolchange_Done(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box);
void toolchange_Perimeter();
box_coordinates _boxForColor(int order) const;
};
}; // namespace Slic3r
#endif /* WipeTowerPrusaMM_hpp_ */

View file

@ -354,6 +354,37 @@ PrintConfigDef::PrintConfigDef()
def->default_value = opt;
}
def = this->add("filament_type", coStrings);
def->label = "Filament type";
def->tooltip = "If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables.";
def->cli = "filament_type=s@";
def->gui_type = "f_enum_open";
def->gui_flags = "show_value";
def->enum_values.push_back("PLA");
def->enum_values.push_back("ABS");
def->enum_values.push_back("PET");
def->enum_values.push_back("HIPS");
def->enum_values.push_back("FLEX");
def->enum_values.push_back("SCAFF");
def->enum_values.push_back("EDGE");
def->enum_values.push_back("NGEN");
def->enum_values.push_back("PVA");
{
ConfigOptionStrings* opt = new ConfigOptionStrings();
opt->values.push_back("PLA");
def->default_value = opt;
}
def = this->add("filament_soluble", coBools);
def->label = "Soluble material";
def->tooltip = "Soluble material is most likely used for a soluble support.";
def->cli = "filament-soluble!";
{
ConfigOptionBools* opt = new ConfigOptionBools();
opt->values.push_back(false);
def->default_value = opt;
}
def = this->add("filament_cost", coFloats);
def->label = "Cost";
def->tooltip = "Enter your filament cost per kg here. This is only for statistical information.";
@ -1168,6 +1199,12 @@ PrintConfigDef::PrintConfigDef()
def->height = 120;
def->default_value = new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n");
def = this->add("single_extruder_multi_material", coBool);
def->label = "Single Extruder Multi Material";
def->tooltip = "The printer multiplexes filaments into a single hot end.";
def->cli = "single-extruder-multi-material!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material", coBool);
def->label = "Generate support material";
def->category = "Support material";
@ -1454,6 +1491,40 @@ PrintConfigDef::PrintConfigDef()
def->default_value = opt;
}
def = this->add("wipe_tower", coBool);
def->label = "Enable";
def->tooltip = "Multi material printers may need to prime or purge extruders on tool changes. Extrude the excess material into the wipe tower.";
def->cli = "wipe-tower!";
def->default_value = new ConfigOptionBool(false);
def = this->add("wipe_tower_x", coFloat);
def->label = "Position X";
def->tooltip = "X coordinate of the left front corner of a wipe tower";
def->sidetext = "mm";
def->cli = "wipe-tower-x=f";
def->default_value = new ConfigOptionFloat(180.);
def = this->add("wipe_tower_y", coFloat);
def->label = "Position Y";
def->tooltip = "Y coordinate of the left front corner of a wipe tower";
def->sidetext = "mm";
def->cli = "wipe-tower-y=f";
def->default_value = new ConfigOptionFloat(140.);
def = this->add("wipe_tower_width", coFloat);
def->label = "Width";
def->tooltip = "Width of a wipe tower";
def->sidetext = "mm";
def->cli = "wipe-tower-width=f";
def->default_value = new ConfigOptionFloat(60.);
def = this->add("wipe_tower_per_color_wipe", coFloat);
def->label = "Per color change depth";
def->tooltip = "Depth of a wipe color per color change. For N colors, there will be maximum (N-1) tool switches performed, therefore the total depth of the wipe tower will be (N-1) times this value.";
def->sidetext = "mm";
def->cli = "wipe-tower-per-color-wipe=f";
def->default_value = new ConfigOptionFloat(15.);
def = this->add("xy_size_compensation", coFloat);
def->label = "XY Size Compensation";
def->category = "Advanced";

View file

@ -42,6 +42,10 @@ enum SeamPosition {
spRandom, spNearest, spAligned, spRear
};
enum FilamentType {
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
};
template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
t_config_enum_values keys_map;
keys_map["reprap"] = gcfRepRap;
@ -91,6 +95,20 @@ template<> inline t_config_enum_values ConfigOptionEnum<SeamPosition>::get_enum_
return keys_map;
}
template<> inline t_config_enum_values ConfigOptionEnum<FilamentType>::get_enum_values() {
t_config_enum_values keys_map;
keys_map["PLA"] = ftPLA;
keys_map["ABS"] = ftABS;
keys_map["PET"] = ftPET;
keys_map["HIPS"] = ftHIPS;
keys_map["FLEX"] = ftFLEX;
keys_map["SCAFF"] = ftSCAFF;
keys_map["EDGE"] = ftEDGE;
keys_map["NGEN"] = ftNGEN;
keys_map["PVA"] = ftPVA;
return keys_map;
}
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
// Does not store the actual values, but defines default values.
class PrintConfigDef : public ConfigDef
@ -297,13 +315,15 @@ class PrintRegionConfig : public virtual StaticPrintConfig
// This object is mapped to Perl as Slic3r::Config::GCode.
class GCodeConfig : public virtual StaticPrintConfig
{
public:
public:
ConfigOptionString before_layer_gcode;
ConfigOptionString end_gcode;
ConfigOptionString extrusion_axis;
ConfigOptionFloats extrusion_multiplier;
ConfigOptionFloats filament_diameter;
ConfigOptionFloats filament_density;
ConfigOptionStrings filament_type;
ConfigOptionBools filament_soluble;
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_max_volumetric_speed;
ConfigOptionBool gcode_comments;
@ -322,6 +342,7 @@ class GCodeConfig : public virtual StaticPrintConfig
ConfigOptionFloats retract_restart_extra_toolchange;
ConfigOptionFloats retract_speed;
ConfigOptionString start_gcode;
ConfigOptionBool single_extruder_multi_material;
ConfigOptionString toolchange_gcode;
ConfigOptionFloat travel_speed;
ConfigOptionBool use_firmware_retraction;
@ -341,6 +362,8 @@ class GCodeConfig : public virtual StaticPrintConfig
OPT_PTR(extrusion_multiplier);
OPT_PTR(filament_diameter);
OPT_PTR(filament_density);
OPT_PTR(filament_type);
OPT_PTR(filament_soluble);
OPT_PTR(filament_cost);
OPT_PTR(filament_max_volumetric_speed);
OPT_PTR(gcode_comments);
@ -358,6 +381,7 @@ class GCodeConfig : public virtual StaticPrintConfig
OPT_PTR(retract_restart_extra);
OPT_PTR(retract_restart_extra_toolchange);
OPT_PTR(retract_speed);
OPT_PTR(single_extruder_multi_material);
OPT_PTR(start_gcode);
OPT_PTR(toolchange_gcode);
OPT_PTR(travel_speed);
@ -435,6 +459,11 @@ class PrintConfig : public GCodeConfig
ConfigOptionInts temperature;
ConfigOptionInt threads;
ConfigOptionBools wipe;
ConfigOptionBool wipe_tower;
ConfigOptionFloat wipe_tower_x;
ConfigOptionFloat wipe_tower_y;
ConfigOptionFloat wipe_tower_width;
ConfigOptionFloat wipe_tower_per_color_wipe;
ConfigOptionFloat z_offset;
PrintConfig(bool initialize = true) : GCodeConfig(false) {
@ -494,6 +523,11 @@ class PrintConfig : public GCodeConfig
OPT_PTR(temperature);
OPT_PTR(threads);
OPT_PTR(wipe);
OPT_PTR(wipe_tower);
OPT_PTR(wipe_tower_x);
OPT_PTR(wipe_tower_y);
OPT_PTR(wipe_tower_width);
OPT_PTR(wipe_tower_per_color_wipe);
OPT_PTR(z_offset);
// look in parent class

View file

@ -1,210 +0,0 @@
#ifndef PrusaSingleExtruderMM_WipeTower_hpp_
#define PrusaSingleExtruderMM_WipeTower_hpp_
#include <algorithm>
#include <string>
#include <utility>
namespace PrusaSingleExtruderMM
{
class Writer;
class WipeTower
{
public:
enum material_type
{
INVALID = -1,
PLA = 0, // E:210C B:55C
ABS = 1, // E:255C B:100C
PET = 2, // E:240C B:90C
HIPS = 3, // E:220C B:100C
FLEX = 4, // E:245C B:80C
SCAFF = 5, // E:215C B:55C
EDGE = 6, // E:240C B:80C
NGEN = 7, // E:230C B:80C
PVA = 8 // E:210C B:80C
};
enum wipe_shape
{
SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
struct xy
{
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
float x;
float y;
};
// Parse material name into material_type.
static material_type parse_material(const char *name);
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
// colors -- maximum colors for object
WipeTower(float x, float y, float width, float wipe_area, int color_changes) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_area(wipe_area),
m_color_changes(color_changes),
m_z_pos(0.f) {}
// colors -- maximum color changes for layer
void setColors(int colors) { m_color_changes = colors; }
// Z height -- mm
void setZ(float z) { m_z_pos = z; }
bool is_first_layer() const { return m_z_pos < 0.205f; }
// _retract - retract value in mm
void setRetract(float _retract) { retract = _retract; }
// _zHop - z hop value in mm
void setZHop(float _zhop) { zHop = _zhop; }
void setExtrusion(int layerHeight)
{
// set extrusion coefficient for layer height
// layerHeight -- mm * 100
switch (layerHeight)
{
case 15:
extrusion_flow = (float)0.024;
break;
case 20:
extrusion_flow = (float)0.029;
break;
default:
break;
}
}
/*
Returns gcode for wipe tower brim
sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
*/
std::string FirstLayer(bool sideOnly = false, float y_offset = 0.f);
// Returns gcode for toolchange
std::pair<std::string, WipeTower::xy> Toolchange(
// extruder # 0 - 3
const int tool,
// filament type currently used to print and loaded in nozzle -- see enum material_type
const material_type current_material,
// filament type that will be loaded in to the nozzle -- see enum material_type
const material_type new_material,
// temperature in Celsius for new filament that will be loaded into the nozzle
const int temperature,
// orientation of purge / wipe shape (NORMAL / REVERSED)
const wipe_shape shape,
// total toolchanges done counter ( comment in header of toolchange only )
const int count,
// space available for toolchange ( purge / load / wipe ) - in mm
const float spaceAvailable,
// experimental, don't use, set to 0
const float wipeStartY,
// for last toolchange in object set to true to unload filament into cooling tube, for all other set to false
const bool lastInFile,
// experimental, set to false
const bool colorInit = false);
/*
Returns gcode to draw empty pattern in place of a toolchange -> in case there are less toolchanges atm then what is required later
order -- total toolchanges done for current layer
total -- total colors in current z layer including empty ones
afterToolchange -- true - ignore some not neccesary moves | false - do whole move from object to wipe tower
firstLayerOffset -- experimental , set to 0
*/
std::string Perimeter(int order, int total, int Layer, bool afterToolchange, int firstLayerOffset = 0);
private:
WipeTower();
// Left front corner of the wipe tower in mm.
xy m_wipe_tower_pos;
// Width of the wipe tower.
float m_wipe_tower_width;
// Per color Y span.
float m_wipe_area;
// Current Z position.
float m_z_pos;
// Maximum number of color changes per layer.
int m_color_changes;
float zHop = 0.5f;
float retract = 4.f;
float perimeterWidth = 0.5f;
float extrusion_flow = 0.029f;
struct box_coordinates
{
box_coordinates(float left, float bottom, float width, float height) :
ld(left , bottom ),
lu(left , bottom + height),
rd(left + width, bottom ),
ru(left + width, bottom + height) {}
box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
void expand(const float offset) {
ld += xy(- offset, - offset);
lu += xy(- offset, offset);
rd += xy( offset, - offset);
ru += xy( offset, offset);
}
xy ld; // left down
xy lu; // left upper
xy ru; // right upper
xy rd; // right lower
};
void toolchange_Unload(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape,
const int temperature);
void toolchange_Change(
Writer &writer,
int tool,
material_type current_material,
material_type new_material);
void toolchange_Load(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape,
const bool colorInit);
void toolchange_Wipe(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape);
void toolchange_Done(
Writer &writer,
const box_coordinates &cleaning_box,
const material_type material,
const wipe_shape shape);
box_coordinates _boxForColor(int order) const;
};
}; // namespace PrusaSingleExtruderMM
#endif /* PrusaSingleExtruderMM_WipeTower_hpp_ */