Implemented raft support for the wipe tower
https://github.com/prusa3d/Slic3r/issues/324 Implemented a correct layer height preview for the wipe tower layers, if the wipe tower layer height is not constant due to the application of raft.
This commit is contained in:
parent
f9f0940297
commit
89dcd3e8b1
7 changed files with 118 additions and 36 deletions
|
@ -9,6 +9,9 @@ namespace Slic3r {
|
||||||
// (print.config.complete_objects is true).
|
// (print.config.complete_objects is true).
|
||||||
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder)
|
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder)
|
||||||
{
|
{
|
||||||
|
if (object.layers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
// Initialize the print layers for just a single object.
|
// Initialize the print layers for just a single object.
|
||||||
{
|
{
|
||||||
std::vector<coordf_t> zs;
|
std::vector<coordf_t> zs;
|
||||||
|
@ -26,7 +29,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
||||||
// Reorder the extruders to minimize tool switches.
|
// Reorder the extruders to minimize tool switches.
|
||||||
this->reorder_extruders(first_extruder);
|
this->reorder_extruders(first_extruder);
|
||||||
|
|
||||||
this->fill_wipe_tower_partitions();
|
this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the use case when all objects are printed at once.
|
// For the use case when all objects are printed at once.
|
||||||
|
@ -34,6 +37,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
||||||
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder)
|
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder)
|
||||||
{
|
{
|
||||||
// Initialize the print layers for all objects and all layers.
|
// Initialize the print layers for all objects and all layers.
|
||||||
|
coordf_t object_bottom_z = 0.;
|
||||||
{
|
{
|
||||||
std::vector<coordf_t> zs;
|
std::vector<coordf_t> zs;
|
||||||
for (auto object : print.objects) {
|
for (auto object : print.objects) {
|
||||||
|
@ -42,6 +46,8 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder)
|
||||||
zs.emplace_back(layer->print_z);
|
zs.emplace_back(layer->print_z);
|
||||||
for (auto layer : object->support_layers)
|
for (auto layer : object->support_layers)
|
||||||
zs.emplace_back(layer->print_z);
|
zs.emplace_back(layer->print_z);
|
||||||
|
if (! object->layers.empty())
|
||||||
|
object_bottom_z = object->layers.front()->print_z - object->layers.front()->height;
|
||||||
}
|
}
|
||||||
this->initialize_layers(zs);
|
this->initialize_layers(zs);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +59,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder)
|
||||||
// Reorder the extruders to minimize tool switches.
|
// Reorder the extruders to minimize tool switches.
|
||||||
this->reorder_extruders(first_extruder);
|
this->reorder_extruders(first_extruder);
|
||||||
|
|
||||||
this->fill_wipe_tower_partitions();
|
this->fill_wipe_tower_partitions(print.config, object_bottom_z);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int ToolOrdering::first_extruder() const
|
unsigned int ToolOrdering::first_extruder() const
|
||||||
|
@ -221,7 +227,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolOrdering::fill_wipe_tower_partitions()
|
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
|
||||||
{
|
{
|
||||||
if (m_layer_tools.empty())
|
if (m_layer_tools.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -244,7 +250,50 @@ void ToolOrdering::fill_wipe_tower_partitions()
|
||||||
|
|
||||||
//FIXME this is a hack to get the ball rolling.
|
//FIXME this is a hack to get the ball rolling.
|
||||||
for (LayerTools < : m_layer_tools)
|
for (LayerTools < : m_layer_tools)
|
||||||
lt.has_wipe_tower = lt.has_object && lt.wipe_tower_partitions > 0;
|
lt.has_wipe_tower = (lt.has_object && lt.wipe_tower_partitions > 0) || lt.print_z < object_bottom_z + EPSILON;
|
||||||
|
|
||||||
|
// Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
|
||||||
|
double max_layer_height = FLT_MAX;
|
||||||
|
for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) {
|
||||||
|
double mlh = config.max_layer_height.values[i];
|
||||||
|
if (mlh == 0.)
|
||||||
|
mlh = 0.75 * config.nozzle_diameter.values[i];
|
||||||
|
max_layer_height = std::min(max_layer_height, mlh);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) {
|
||||||
|
const LayerTools < = m_layer_tools[i];
|
||||||
|
const LayerTools <_next = m_layer_tools[i + 1];
|
||||||
|
if (lt.print_z < object_bottom_z + EPSILON && lt_next.print_z >= object_bottom_z + EPSILON) {
|
||||||
|
// lt is the last raft layer. Find the 1st object layer.
|
||||||
|
size_t j = i + 1;
|
||||||
|
for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_wipe_tower; ++ j);
|
||||||
|
if (j < m_layer_tools.size()) {
|
||||||
|
const LayerTools <_object = m_layer_tools[j];
|
||||||
|
coordf_t gap = lt_object.print_z - lt.print_z;
|
||||||
|
assert(gap > 0.f);
|
||||||
|
if (gap > max_layer_height + EPSILON) {
|
||||||
|
// Insert one additional wipe tower layer between lh.print_z and lt_object.print_z.
|
||||||
|
LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
|
||||||
|
// Find the 1st layer above lt_new.
|
||||||
|
for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z; ++ j);
|
||||||
|
LayerTools <_extra = (m_layer_tools[j].print_z == lt_new.print_z) ?
|
||||||
|
m_layer_tools[j] :
|
||||||
|
*m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
|
||||||
|
lt_extra.has_wipe_tower = true;
|
||||||
|
lt_extra.wipe_tower_partitions = lt_object.wipe_tower_partitions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the wipe_tower_layer_height values.
|
||||||
|
coordf_t wipe_tower_print_z_last = 0.;
|
||||||
|
for (LayerTools < : m_layer_tools)
|
||||||
|
if (lt.has_wipe_tower) {
|
||||||
|
lt.wipe_tower_layer_height = lt.print_z - wipe_tower_print_z_last;
|
||||||
|
wipe_tower_print_z_last = lt.print_z;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -20,7 +20,8 @@ public:
|
||||||
has_object(false),
|
has_object(false),
|
||||||
has_support(false),
|
has_support(false),
|
||||||
has_wipe_tower(false),
|
has_wipe_tower(false),
|
||||||
wipe_tower_partitions(0) {}
|
wipe_tower_partitions(0),
|
||||||
|
wipe_tower_layer_height(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; }
|
||||||
|
@ -37,6 +38,7 @@ public:
|
||||||
// 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;
|
||||||
|
coordf_t wipe_tower_layer_height;
|
||||||
};
|
};
|
||||||
|
|
||||||
ToolOrdering() {}
|
ToolOrdering() {}
|
||||||
|
@ -71,7 +73,7 @@ private:
|
||||||
void initialize_layers(std::vector<coordf_t> &zs);
|
void initialize_layers(std::vector<coordf_t> &zs);
|
||||||
void collect_extruders(const PrintObject &object);
|
void collect_extruders(const PrintObject &object);
|
||||||
void reorder_extruders(unsigned int last_extruder_id);
|
void reorder_extruders(unsigned int last_extruder_id);
|
||||||
void fill_wipe_tower_partitions();
|
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
|
||||||
|
|
||||||
std::vector<LayerTools> m_layer_tools;
|
std::vector<LayerTools> m_layer_tools;
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,6 +75,7 @@ public:
|
||||||
{
|
{
|
||||||
// Print heigh of this tool change.
|
// Print heigh of this tool change.
|
||||||
float print_z;
|
float print_z;
|
||||||
|
float layer_height;
|
||||||
// G-code section to be directly included into the output G-code.
|
// G-code section to be directly included into the output G-code.
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
// For path preview.
|
// For path preview.
|
||||||
|
|
|
@ -471,12 +471,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(int tool, bool last_in
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
result.gcode = writer.gcode();
|
result.layer_height = this->m_layer_height;
|
||||||
|
result.gcode = writer.gcode();
|
||||||
result.elapsed_time = writer.elapsed_time();
|
result.elapsed_time = writer.elapsed_time();
|
||||||
result.extrusions = writer.extrusions();
|
result.extrusions = writer.extrusions();
|
||||||
result.start_pos = writer.start_pos();
|
result.start_pos = writer.start_pos();
|
||||||
result.end_pos = writer.pos();
|
result.end_pos = writer.pos();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,12 +555,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
result.gcode = writer.gcode();
|
result.layer_height = this->m_layer_height;
|
||||||
|
result.gcode = writer.gcode();
|
||||||
result.elapsed_time = writer.elapsed_time();
|
result.elapsed_time = writer.elapsed_time();
|
||||||
result.extrusions = writer.extrusions();
|
result.extrusions = writer.extrusions();
|
||||||
result.start_pos = writer.start_pos();
|
result.start_pos = writer.start_pos();
|
||||||
result.end_pos = writer.pos();
|
result.end_pos = writer.pos();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,12 +850,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
result.gcode = writer.gcode();
|
result.layer_height = this->m_layer_height;
|
||||||
|
result.gcode = writer.gcode();
|
||||||
result.elapsed_time = writer.elapsed_time();
|
result.elapsed_time = writer.elapsed_time();
|
||||||
result.extrusions = writer.extrusions();
|
result.extrusions = writer.extrusions();
|
||||||
result.start_pos = writer.start_pos();
|
result.start_pos = writer.start_pos();
|
||||||
result.end_pos = writer.pos();
|
result.end_pos = writer.pos();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -565,7 +565,7 @@ std::string Print::validate() const
|
||||||
return "The Spiral Vase option can only be used when printing single material objects.";
|
return "The Spiral Vase option can only be used when printing single material objects.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->config.wipe_tower) {
|
if (this->config.wipe_tower && ! this->objects.empty()) {
|
||||||
for (auto dmr : this->config.nozzle_diameter.values)
|
for (auto dmr : this->config.nozzle_diameter.values)
|
||||||
if (std::abs(dmr - 0.4) > EPSILON)
|
if (std::abs(dmr - 0.4) > EPSILON)
|
||||||
return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
|
return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
|
||||||
|
@ -573,16 +573,22 @@ std::string Print::validate() const
|
||||||
return "The Wipe Tower is currently only supported for the RepRap (Marlin / Sprinter) G-code flavor.";
|
return "The Wipe Tower is currently only supported for the RepRap (Marlin / Sprinter) G-code flavor.";
|
||||||
if (! this->config.use_relative_e_distances)
|
if (! this->config.use_relative_e_distances)
|
||||||
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
||||||
|
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
|
||||||
for (PrintObject *object : this->objects) {
|
for (PrintObject *object : this->objects) {
|
||||||
if (std::abs(object->config.first_layer_height - this->objects.front()->config.first_layer_height) > EPSILON ||
|
SlicingParameters slicing_params = object->slicing_parameters();
|
||||||
std::abs(object->config.layer_height - this->objects.front()->config.layer_height ) > EPSILON)
|
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||||
|
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||||
return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths";
|
return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths";
|
||||||
|
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||||
|
return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers";
|
||||||
|
if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance)
|
||||||
|
return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance";
|
||||||
|
if (! equal_layering(slicing_params, slicing_params0))
|
||||||
|
return "The Wipe Tower is only supported for multiple objects if they are sliced equally.";
|
||||||
object->update_layer_height_profile();
|
object->update_layer_height_profile();
|
||||||
if (object->layer_height_profile.size() != 8 ||
|
for (size_t i = 1; i < object->layer_height_profile.size(); i += 2)
|
||||||
std::abs(object->layer_height_profile[1] - object->config.first_layer_height) > EPSILON ||
|
if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
|
||||||
std::abs(object->layer_height_profile[3] - object->config.first_layer_height) > EPSILON ||
|
std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)
|
||||||
std::abs(object->layer_height_profile[5] - object->config.layer_height) > EPSILON ||
|
|
||||||
std::abs(object->layer_height_profile[7] - object->config.layer_height) > EPSILON)
|
|
||||||
return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";
|
return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -959,9 +965,7 @@ void Print::_make_wipe_tower()
|
||||||
bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
|
bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
|
||||||
wipe_tower.set_layer(
|
wipe_tower.set_layer(
|
||||||
float(layer_tools.print_z),
|
float(layer_tools.print_z),
|
||||||
float(first_layer ?
|
float(layer_tools.wipe_tower_layer_height),
|
||||||
this->objects.front()->config.first_layer_height.get_abs_value(this->objects.front()->config.layer_height.value) :
|
|
||||||
this->objects.front()->config.layer_height.value),
|
|
||||||
layer_tools.wipe_tower_partitions,
|
layer_tools.wipe_tower_partitions,
|
||||||
first_layer,
|
first_layer,
|
||||||
last_layer);
|
last_layer);
|
||||||
|
|
|
@ -91,6 +91,32 @@ struct SlicingParameters
|
||||||
coordf_t object_print_z_max;
|
coordf_t object_print_z_max;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
|
||||||
|
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
|
||||||
|
{
|
||||||
|
return sp1.base_raft_layers == sp2.base_raft_layers &&
|
||||||
|
sp1.interface_raft_layers == sp2.interface_raft_layers &&
|
||||||
|
sp1.base_raft_layer_height == sp2.base_raft_layer_height &&
|
||||||
|
sp1.interface_raft_layer_height == sp2.interface_raft_layer_height &&
|
||||||
|
sp1.contact_raft_layer_height == sp2.contact_raft_layer_height &&
|
||||||
|
sp1.contact_raft_layer_height_bridging == sp2.contact_raft_layer_height_bridging &&
|
||||||
|
sp1.layer_height == sp2.layer_height &&
|
||||||
|
sp1.min_layer_height == sp2.min_layer_height &&
|
||||||
|
sp1.max_layer_height == sp2.max_layer_height &&
|
||||||
|
sp1.max_suport_layer_height == sp2.max_suport_layer_height &&
|
||||||
|
sp1.first_print_layer_height == sp2.first_print_layer_height &&
|
||||||
|
sp1.first_object_layer_height == sp2.first_object_layer_height &&
|
||||||
|
sp1.first_object_layer_bridging == sp2.first_object_layer_bridging &&
|
||||||
|
sp1.soluble_interface == sp2.soluble_interface &&
|
||||||
|
sp1.gap_raft_object == sp2.gap_raft_object &&
|
||||||
|
sp1.gap_object_support == sp2.gap_object_support &&
|
||||||
|
sp1.gap_support_object == sp2.gap_support_object &&
|
||||||
|
sp1.raft_base_top_z == sp2.raft_base_top_z &&
|
||||||
|
sp1.raft_interface_top_z == sp2.raft_interface_top_z &&
|
||||||
|
sp1.raft_contact_top_z == sp2.raft_contact_top_z &&
|
||||||
|
sp1.object_print_z_min == sp2.object_print_z_min;
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
|
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
|
||||||
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
|
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
|
||||||
|
|
||||||
|
|
|
@ -1016,9 +1016,6 @@ void _3DScene::_load_wipe_tower_toolpaths(
|
||||||
}
|
}
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.print->m_wipe_tower_tool_changes[idx_layer];
|
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.print->m_wipe_tower_tool_changes[idx_layer];
|
||||||
coordf_t layer_height = (idx_layer == 0) ?
|
|
||||||
ctxt.print->objects.front()->config.first_layer_height.get_abs_value(ctxt.print->objects.front()->config.layer_height.value) :
|
|
||||||
ctxt.print->objects.front()->config.layer_height.value;
|
|
||||||
for (size_t i = 0; i < vols.size(); ++ i) {
|
for (size_t i = 0; i < vols.size(); ++ i) {
|
||||||
GLVolume &vol = *vols[i];
|
GLVolume &vol = *vols[i];
|
||||||
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
|
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
|
||||||
|
@ -1045,7 +1042,7 @@ void _3DScene::_load_wipe_tower_toolpaths(
|
||||||
std::vector<double> heights;
|
std::vector<double> heights;
|
||||||
lines.reserve(n_lines);
|
lines.reserve(n_lines);
|
||||||
widths.reserve(n_lines);
|
widths.reserve(n_lines);
|
||||||
heights.assign(n_lines, layer_height);
|
heights.assign(n_lines, extrusions.layer_height);
|
||||||
for (; i < j; ++ i) {
|
for (; i < j; ++ i) {
|
||||||
const WipeTower::Extrusion &e = extrusions.extrusions[i];
|
const WipeTower::Extrusion &e = extrusions.extrusions[i];
|
||||||
assert(e.width > 0.f);
|
assert(e.width > 0.f);
|
||||||
|
|
Loading…
Add table
Reference in a new issue