ENH: Optimize and design the time-lapse wiper tower

Multiplex existing wipe tower and revert old time-lapse wipe tower code

Change-Id: Ia1cba9808647ea900f691b2c5d5887077be12d89
(cherry picked from commit 61f41df572d11b7cc738c34f2624fd123cd9a6df)
This commit is contained in:
zhimin.zeng 2022-08-15 14:41:28 +08:00 committed by Lane.Wei
parent a26c573b1b
commit f745c5fea2
21 changed files with 172 additions and 183 deletions

View file

@ -553,23 +553,30 @@ bool GCode::gcode_label_objects = false;
std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer)
{
std::string gcode;
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
// resulting in a wipe tower with sparse layers.
double wipe_tower_z = -1;
bool ignore_sparse = false;
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
wipe_tower_z = m_last_wipe_tower_print_z;
ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
if (m_tool_change_idx == 0 && !ignore_sparse)
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
}
if (m_enable_timelapse_print && m_is_first_print) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][0], m_tool_changes[m_layer_idx][0].new_tool, wipe_tower_z);
m_tool_change_idx++;
m_is_first_print = false;
}
assert(m_layer_idx >= 0);
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < (int)m_tool_changes.size()) {
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
// resulting in a wipe tower with sparse layers.
double wipe_tower_z = -1;
bool ignore_sparse = false;
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
wipe_tower_z = m_last_wipe_tower_print_z;
ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
if (m_tool_change_idx == 0 && !ignore_sparse)
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
}
if (!ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
@ -2781,6 +2788,9 @@ GCode::LayerResult GCode::process_layer(
}
} // for objects
if (m_wipe_tower)
m_wipe_tower->set_is_first_print(true);
// 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 : layer_tools.extruders)

View file

@ -82,7 +82,9 @@ public:
m_layer_idx(-1),
m_tool_change_idx(0),
m_plate_origin(plate_origin),
m_single_extruder_multi_material(print_config.single_extruder_multi_material)
m_single_extruder_multi_material(print_config.single_extruder_multi_material),
m_enable_timelapse_print(print_config.timelapse_no_toolhead.value),
m_is_first_print(true)
{}
std::string prime(GCode &gcodegen);
@ -91,6 +93,11 @@ public:
std::string finalize(GCode &gcodegen);
std::vector<float> used_filament_length() const;
bool is_first_print() const { return m_is_first_print;}
void set_is_first_print(bool is) { m_is_first_print = is; }
bool enable_timelapse_print() const { return m_enable_timelapse_print; }
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
@ -117,6 +124,8 @@ private:
// BBS
Vec3d m_plate_origin;
bool m_single_extruder_multi_material;
bool m_enable_timelapse_print;
bool m_is_first_print;
};
class ColorPrintColors

View file

@ -544,7 +544,8 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
m_travel_speed(config.travel_speed),
m_current_tool(initial_tool),
//wipe_volumes(flush_matrix)
m_wipe_volume(prime_volume)
m_wipe_volume(prime_volume),
m_enable_timelapse_print(config.timelapse_no_toolhead.value)
{
// Read absolute value of first layer speed, if given as percentage,
// it is taken over following default. Speeds from config are not
@ -1304,7 +1305,10 @@ void WipeTower::plan_tower()
}
}
{
{
if (m_enable_timelapse_print && max_depth < EPSILON)
max_depth = min_wipe_tower_depth;
if (max_depth + EPSILON < min_wipe_tower_depth)
m_extra_spacing = min_wipe_tower_depth / max_depth;
else
@ -1343,9 +1347,13 @@ void WipeTower::plan_tower()
for (auto& layer : m_plan)
layer.depth = 0.f;
float max_depth_for_all = 0;
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
{
float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
if (m_enable_timelapse_print && this_layer_depth < EPSILON)
this_layer_depth = min_wipe_tower_depth;
m_plan[layer_index].depth = this_layer_depth;
if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
@ -1356,7 +1364,16 @@ void WipeTower::plan_tower()
if (m_plan[i].depth - this_layer_depth < 2*m_perimeter_width )
m_plan[i].depth = this_layer_depth;
}
}
if (m_enable_timelapse_print && layer_index == 0)
max_depth_for_all = m_plan[0].depth;
}
if (m_enable_timelapse_print) {
for (int i = int(m_plan.size()) - 1; i >= 0; i--) {
m_plan[i].depth = max_depth_for_all;
}
}
}
void WipeTower::save_on_last_wipe()
@ -1474,17 +1491,25 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
// BBS: consider both soluable and support properties
int idx = first_toolchange_to_nonsoluble_nonsupport (layer.tool_changes);
ToolChangeResult finish_layer_tcr;
ToolChangeResult timelapse_wall;
if (idx == -1) {
// if there is no toolchange switching to non-soluble, finish layer
// will be called at the very beginning. That's the last possibility
// where a nonsoluble tool can be.
finish_layer_tcr = finish_layer();
if (m_enable_timelapse_print) {
timelapse_wall = only_generate_out_wall();
}
finish_layer_tcr = finish_layer(m_enable_timelapse_print ? false : true);
}
for (int i=0; i<int(layer.tool_changes.size()); ++i) {
if (i == 0 && m_enable_timelapse_print) {
timelapse_wall = only_generate_out_wall();
}
if (i == idx) {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool, true));
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool, m_enable_timelapse_print ? false : true));
// finish_layer will be called after this toolchange
finish_layer_tcr = finish_layer(false);
}
@ -1504,8 +1529,57 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
layer_result[idx] = merge_tcr(layer_result[idx], finish_layer_tcr);
}
if (m_enable_timelapse_print) {
layer_result.insert(layer_result.begin(), std::move(timelapse_wall));
}
result.emplace_back(std::move(layer_result));
}
}
WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
{
size_t old_tool = m_current_tool;
m_extrusion_flow = 0.038f; // hard code
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
// BBS: speed up perimeter speed to 90mm/s for non-first layer
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : 5400.f;
float fill_box_y = m_layer_info->toolchanges_depth() + m_perimeter_width;
box_coordinates fill_box(Vec2f(m_perimeter_width, fill_box_y), m_wipe_tower_width - 2 * m_perimeter_width, m_layer_info->depth - fill_box_y);
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON;
// we are in one of the corners, travel to ld along the perimeter:
if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y());
if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y());
// outer perimeter (always):
// BBS
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
wt_box = align_perimeter(wt_box);
writer.rectangle(wt_box, feedrate);
// Now prepare future wipe. box contains rectangle that was extruded last (ccw).
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld)));
writer.add_wipe_point(writer.pos()).add_wipe_point(target);
// Ask our writer about how much material was consumed.
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
if (!m_no_sparse_layers || toolchanges_on_layer)
if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, true, 0.f);
}
} // namespace Slic3r

View file

@ -152,6 +152,8 @@ public:
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<ToolChangeResult>> &result);
WipeTower::ToolChangeResult only_generate_out_wall();
float get_depth() const { return m_wipe_tower_depth; }
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
@ -263,7 +265,7 @@ private:
return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point
}
bool m_enable_timelapse_print = false;
bool m_semm = true; // Are we using a single extruder multimaterial printer?
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.

View file

@ -237,8 +237,7 @@ enum class ModelVolumeType : int {
NEGATIVE_VOLUME,
PARAMETER_MODIFIER,
SUPPORT_BLOCKER,
SUPPORT_ENFORCER,
TIMELAPSE_WIPE_TOWER
SUPPORT_ENFORCER
};
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower, CutToParts };
@ -271,7 +270,6 @@ public:
LayerHeightProfile layer_height_profile;
// Whether or not this object is printable
bool printable;
bool is_timelapse_wipe_tower = false;
// This vector holds position of selected support points for SLA. The data are
// saved in mesh coordinates to allow using them for several instances.

View file

@ -1575,6 +1575,9 @@ void Print::finalize_first_layer_convex_hull()
// Wipe tower support.
bool Print::has_wipe_tower() const
{
if (enable_timelapse_print())
return true;
return
! m_config.spiral_mode.value &&
m_config.enable_prime_tower.value &&
@ -1589,18 +1592,25 @@ const WipeTowerData& Print::wipe_tower_data(size_t filaments_cnt) const
double width = m_config.prime_tower_width;
double layer_height = 0.2; // hard code layer height
double wipe_volume = m_config.prime_volume;
const_cast<Print*>(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width);
if (filaments_cnt == 1 && enable_timelapse_print()) {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume / (layer_height * width);
} else {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width);
}
const_cast<Print*>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
}
return m_wipe_tower_data;
}
bool Print::enable_timelapse_print() const
{
return m_config.timelapse_no_toolhead.value;
}
void Print::_make_wipe_tower()
{
m_wipe_tower_data.clear();
if (! this->has_wipe_tower())
return;
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
@ -1616,7 +1626,18 @@ void Print::_make_wipe_tower()
// BBS: priming logic is removed, so don't consider it in tool ordering
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, false);
if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
// if enable_timelapse_print(), update all layer_tools parameters(has_wipe_tower, wipe_tower_partitions)
if (enable_timelapse_print()) {
std::vector<LayerTools>& layer_tools_array = m_wipe_tower_data.tool_ordering.layer_tools();
for (LayerTools& layer_tools : layer_tools_array) {
layer_tools.has_wipe_tower = true;
if (layer_tools.wipe_tower_partitions == 0) {
layer_tools.wipe_tower_partitions = 1;
}
}
}
if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower.
return;
@ -1705,6 +1726,11 @@ void Print::_make_wipe_tower()
}
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
// if enable timelapse, slice all layer
if (enable_timelapse_print())
continue;
if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break;
}

View file

@ -684,6 +684,8 @@ public:
const WipeTowerData& wipe_tower_data(size_t filaments_cnt = 0) const;
const ToolOrdering& tool_ordering() const { return m_tool_ordering; }
bool enable_timelapse_print() const;
std::string output_filename(const std::string &filename_base = std::string()) const override;
size_t num_print_regions() const throw() { return m_print_regions.size(); }

View file

@ -666,8 +666,7 @@ bool PrintObject::invalidate_state_by_config_options(
//BBS
|| opt_key == "adaptive_layer_height"
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"
|| opt_key == "timelapse_no_toolhead") {
|| opt_key == "raft_contact_distance") {
steps.emplace_back(posSlice);
} else if (
opt_key == "elefant_foot_compensation"