diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 64934612c..52c81f85a 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -474,6 +474,52 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto } } +std::set ToolOrdering::get_tpu_filaments() const +{ + std::vector all_filaments; + for (const auto < : m_layer_tools) { + append(all_filaments, lt.extruders); + sort_remove_duplicates(all_filaments); + } + + std::set tpu_filaments; + for (unsigned int filament_id : all_filaments) { + std::string filament_name = m_print->config().filament_type.get_at(filament_id); + if (filament_name == "TPU") { + tpu_filaments.insert(filament_id); + } + } + + return tpu_filaments; +} + +bool ToolOrdering::check_tpu_group(std::vector filament_maps) const +{ + std::vector all_filaments; + for (const auto < : m_layer_tools) { + append(all_filaments, lt.extruders); + sort_remove_duplicates(all_filaments); + } + + int check_extruder_id = -1; + std::map> extruder_filament_nums; + for (unsigned int filament_id : all_filaments) { + int extruder_id = filament_maps[filament_id]; + extruder_filament_nums[extruder_id].push_back(filament_id); + + std::string filament_name = m_print->config().filament_type.get_at(filament_id); + if (filament_name == "TPU") { + check_extruder_id = filament_maps[filament_id]; + } + + if (check_extruder_id != -1 && (extruder_filament_nums[check_extruder_id].size() > 1)) { + return false; + } + } + + return true; +} + // Reorder extruders to minimize layer changes. void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) { @@ -902,6 +948,18 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first using FlushMatrix = std::vector>; size_t nozzle_nums = print_config->nozzle_diameter.values.size(); + + std::vector> extruder_tpu_status(2, std::set()); + if (nozzle_nums > 1) { + std::set tpu_filaments = get_tpu_filaments(); + if (tpu_filaments.size() > 1) { + throw Slic3r::RuntimeError(std::string("Only supports up to one TPU filament.")); + } + + // todo multi_exturder: Need to determine whether the TPU can be placed on the left or right head according to the print model. + extruder_tpu_status[0] = tpu_filaments; + } + std::vector nozzle_flush_mtx; for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) { std::vector flush_matrix(cast(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, nozzle_nums))); @@ -937,6 +995,15 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first m_print->update_filament_maps_to_config(filament_maps); } std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; }); + + if (!check_tpu_group(filament_maps)) { + if (map_mode == FilamentMapMode::fmmManual) { + throw Slic3r::RuntimeError(std::string("Manual grouping error: TPU can only be placed in a nozzle alone.")); + } + else { + throw Slic3r::RuntimeError(std::string("Auto grouping error: TPU can only be placed in a nozzle alone.")); + } + } } std::vector>filament_sequences; diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 6b6bb8cee..c291b8952 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -238,6 +238,9 @@ private: void initialize_layers(std::vector &zs); void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); void reorder_extruders(unsigned int last_extruder_id); + std::set get_tpu_filaments() const; + bool check_tpu_group(std::vector filament_maps) const; + // BBS void reorder_extruders(std::vector tool_order_layer0); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index e307134b1..d3a7669dd 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -616,7 +616,8 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi //wipe_volumes(flush_matrix) m_wipe_volume(prime_volume), m_enable_timelapse_print(config.timelapse_type.value == TimelapseType::tlSmooth), - m_nozzle_change_length(config.extruder_change_length.get_at(0)) + m_nozzle_change_length(config.extruder_change_length.get_at(0)), + m_is_multi_extruder(config.nozzle_diameter.size() > 1) { // Read absolute value of first layer speed, if given as percentage, // it is taken over following default. Speeds from config are not @@ -831,7 +832,37 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per // BBS //writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road - // ensure travel to initial_positoin + if (m_is_multi_extruder && is_tpu_filament(tool)) { + float dy = 2 * m_perimeter_width; + float nozzle_change_speed = 60.0f * m_filpar[tool].max_e_speed / m_extrusion_flow; + nozzle_change_speed *= 0.25; + + const float &xl = cleaning_box.ld.x(); + const float &xr = cleaning_box.rd.x(); + + Vec2f start_pos = m_nozzle_change_result.start_pos + Vec2f(0, m_perimeter_width); + bool left_to_right = true; + double tpu_travel_length = 5; + double e_flow = extrusion_flow(0.2); + double length = tpu_travel_length / e_flow; + int tpu_line_count = length / (m_wipe_tower_width - 2 * m_perimeter_width) + 1; + + writer.travel(start_pos); + + for (int i = 0; true; ++i) { + if (left_to_right) + writer.travel(xr - m_perimeter_width, writer.y(), nozzle_change_speed); + else + writer.travel(xl + m_perimeter_width, writer.y(), nozzle_change_speed); + + if (i == tpu_line_count - 1) + break; + + writer.travel(writer.x(), writer.y() + dy); + left_to_right = !left_to_right; + } + } + { writer.travel(initial_position - Vec2f(0.5, 0.5)); writer.travel(initial_position); @@ -897,19 +928,22 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int new_filament_id) { - float wipe_depth = 0.f; - float wipe_length = 0.f; - float purge_volume = 0.f; - int nozzle_change_line_count = 0; + float wipe_depth = 0.f; + float wipe_length = 0.f; + float purge_volume = 0.f; + int nozzle_change_line_count = 0; // Finds this toolchange info if (new_filament_id != (unsigned int) (-1)) { for (const auto &b : m_layer_info->tool_changes) if (b.new_tool == new_filament_id) { - wipe_length = b.wipe_length; - wipe_depth = b.required_depth; - purge_volume = b.purge_volume; - nozzle_change_line_count = (b.nozzle_change_depth + WT_EPSILON) / m_perimeter_width; + wipe_length = b.wipe_length; + wipe_depth = b.required_depth; + purge_volume = b.purge_volume; + if (has_tpu_filament()) + nozzle_change_line_count = ((b.nozzle_change_depth + WT_EPSILON) / m_perimeter_width + 1) / 2; + else + nozzle_change_line_count = (b.nozzle_change_depth + WT_EPSILON) / m_perimeter_width; break; } } else { @@ -917,7 +951,7 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int } float nozzle_change_speed = 60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow; - if (m_filpar[m_current_tool].material == "TPU") { + if (is_tpu_filament(m_current_tool)) { nozzle_change_speed *= 0.25; } @@ -932,8 +966,7 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int writer.speed_override_backup(); writer.speed_override(100); - box_coordinates cleaning_box(Vec2f(m_perimeter_width, m_perimeter_width), - m_wipe_tower_width - 2 * m_perimeter_width, + box_coordinates cleaning_box(Vec2f(m_perimeter_width, m_perimeter_width), m_wipe_tower_width - 2 * m_perimeter_width, (new_filament_id != (unsigned int) (-1) ? wipe_depth + m_depth_traversed - m_perimeter_width : m_wipe_tower_depth - m_perimeter_width)); Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); @@ -945,7 +978,9 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int const float &xl = cleaning_box.ld.x(); const float &xr = cleaning_box.rd.x(); - float dy = m_layer_info->extra_spacing * m_perimeter_width; + float dy = m_layer_info->extra_spacing * m_perimeter_width; + if (has_tpu_filament()) + dy = 2 * m_perimeter_width; float start_y = writer.y(); @@ -972,35 +1007,60 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow. - m_depth_traversed += (nozzle_change_line_count) * dy; + m_depth_traversed += (nozzle_change_line_count - 1) *dy + m_perimeter_width; - auto float_to_string_with_precision = [](float value, int precision) { - std::ostringstream out; - out << std::fixed << std::setprecision(precision) << value; - return out.str(); - }; - float wipe_distance = 2; - Vec2f wipe_pos = writer.pos(); - if (m_left_to_right) { - wipe_pos.x() -= wipe_distance; + if (is_tpu_filament(m_current_tool)) + { + bool left_to_right = !m_left_to_right; + double tpu_travel_length = 5; + double e_flow = extrusion_flow(0.2); + double length = tpu_travel_length / e_flow; + int tpu_line_count = length / (m_wipe_tower_width - 2 * m_perimeter_width) + 1; + + writer.travel(writer.x(), writer.y() - m_perimeter_width); + + for (int i = 0; true; ++i) { + if (left_to_right) + writer.travel(xr - m_perimeter_width, writer.y(), nozzle_change_speed); + else + writer.travel(xl + m_perimeter_width, writer.y(), nozzle_change_speed); + + if (i == tpu_line_count - 1) + break; + + writer.travel(writer.x(), writer.y() - dy); + left_to_right = !left_to_right; + } } else { - wipe_pos.x() += wipe_distance; - } - writer.append("; WIPE_START\n"); - writer.extrude_explicit(wipe_pos, -2); - writer.append("; WIPE_END\n"); + auto float_to_string_with_precision = [](float value, int precision) { + std::ostringstream out; + out << std::fixed << std::setprecision(precision) << value; + return out.str(); + }; - std::string lift_gcode = "G2 Z" + float_to_string_with_precision(m_z_pos + 0.4, 3) + " I0.86 J0.86 P1 F10000\n"; - writer.append(lift_gcode); + float wipe_distance = 2; + Vec2f wipe_pos = writer.pos(); + if (m_left_to_right) { + wipe_pos.x() -= wipe_distance; + } else { + wipe_pos.x() += wipe_distance; + } + writer.append("; WIPE_START\n"); + writer.extrude_explicit(wipe_pos, -2); + writer.append("; WIPE_END\n"); + + std::string lift_gcode = "G2 Z" + float_to_string_with_precision(m_z_pos + 0.4, 3) + " I0.86 J0.86 P1 F10000\n"; + writer.append(lift_gcode); + } writer.append("; Nozzle change end\n"); NozzleChangeResult result; - result.start_pos = writer.start_pos_rotated(); - result.end_pos = writer.pos(); - result.gcode = std::move(writer.gcode()); + result.start_pos = initial_position; + result.end_pos = writer.pos(); + result.gcode = std::move(writer.gcode()); return result; } @@ -1565,7 +1625,10 @@ void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned in double e_flow = extrusion_flow(0.2); double length = m_nozzle_change_length / e_flow; int nozzle_change_line_count = length / (m_wipe_tower_width - m_perimeter_width) + 1; - nozzle_change_depth = nozzle_change_line_count * m_perimeter_width; + if (has_tpu_filament()) + nozzle_change_depth = (2 * nozzle_change_line_count - 1) * m_perimeter_width; + else + nozzle_change_depth = nozzle_change_line_count * m_perimeter_width; depth += nozzle_change_depth; } WipeTowerInfo::ToolChange tool_change = WipeTowerInfo::ToolChange(old_tool, new_tool, depth, 0.f, 0.f, wipe_volume, length_to_extrude, purge_volume); @@ -1620,7 +1683,7 @@ 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) + if (max_depth + EPSILON < min_wipe_tower_depth && !has_tpu_filament()) m_extra_spacing = min_wipe_tower_depth / max_depth; else m_extra_spacing = 1.f; @@ -1723,6 +1786,10 @@ void WipeTower::save_on_last_wipe() } } +bool WipeTower::is_tpu_filament(int filament_id) const +{ + return m_filpar[filament_id].material == "TPU"; +} // BBS: consider both soluable and support properties // Return index of first toolchange that switches to non-soluble and non-support extruder diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 07ce844fe..c4c736d7d 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -259,6 +259,9 @@ public: void set_filament_map(const std::vector &filament_map) { m_filament_map = filament_map; } + void set_has_tpu_filament(bool has_tpu) { m_has_tpu_filament = has_tpu; } + bool has_tpu_filament() const { return m_has_tpu_filament; } + struct FilamentParameters { std::string material = "PLA"; bool is_soluble = false; @@ -320,6 +323,8 @@ private: size_t m_cur_layer_id; NozzleChangeResult m_nozzle_change_result; std::vector m_filament_map; + bool m_has_tpu_filament{false}; + bool m_is_multi_extruder{false}; // G-code generator parameters. // BBS: remove useless config @@ -385,6 +390,8 @@ private: // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe void save_on_last_wipe(); + bool is_tpu_filament(int filament_id) const; + // BBS box_coordinates align_perimeter(const box_coordinates& perimeter_box); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0bd4bb2df..914ab2382 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -67,6 +67,17 @@ void Print::clear() m_statistics_by_extruder_count.clear(); } +bool Print::has_tpu_filament() const +{ + for (unsigned int filament_id : m_wipe_tower_data.tool_ordering.all_extruders()) { + std::string filament_name = m_config.filament_type.get_at(filament_id); + if (filament_name == "TPU") { + return true; + } + } + return false; +} + // Called by Print::apply(). // This method only accepts PrintConfig option keys. bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector &opt_keys) @@ -2458,7 +2469,7 @@ void Print::_make_wipe_tower() // BBS: in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume. WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_config.prime_volume, m_wipe_tower_data.tool_ordering.first_extruder(), m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z); - + wipe_tower.set_has_tpu_filament(this->has_tpu_filament()); wipe_tower.set_filament_map(this->get_filament_maps()); // Set the extruder & material properties at the wipe tower object. for (size_t i = 0; i < number_of_extruders; ++ i) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index dd72b635a..b6aee316e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -899,6 +899,7 @@ private: //BBS static StringObjectException check_multi_filament_valid(const Print &print); + bool has_tpu_filament() const; bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector &opt_keys); void _make_skirt();