From f1445ff0477795e9baf3792348ff27d79ee2308c Mon Sep 17 00:00:00 2001 From: "xun.zhang" Date: Wed, 1 Jan 2025 09:19:53 +0800 Subject: [PATCH] ENH: enhance extruder unprintable area detection 1. Detect unprintable area for extruder when slicing 2. Always do filament map again if object pos changed jira:STUDIO-9473 Signed-off-by: xun.zhang Change-Id: Ic01b8be8e3b08ba6b34efb2d3c451c9e985a03e8 --- src/libslic3r/GCode/ToolOrdering.cpp | 80 +------------------------ src/libslic3r/GCode/ToolOrdering.hpp | 2 - src/libslic3r/Print.cpp | 62 ++++++++++++++++--- src/libslic3r/Print.hpp | 32 ++++++++-- src/libslic3r/PrintApply.cpp | 9 ++- src/libslic3r/PrintObject.cpp | 89 +++++++++++++++++++++++++++- 6 files changed, 180 insertions(+), 94 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index c42e11d41..7ce9a966d 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -38,82 +38,6 @@ static std::setget_filament_by_type(const std::vector& used_f } -/** - * @brief Determines the unprintable filaments for each extruder based on its physical attributes - * - * Currently, the criteria for determining unprintable filament include the following: - * 1. TPU filaments can only be placed in the master extruder and must be grouped alone. - * 2. We only support at most 1 tpu filament. - * 3. An extruder can only accommodate filament with a hardness requirement lower than that of its nozzle. - * If extruder num is 1, just return an empty vector. - * - * @param used_filaments Totally used filaments when slicing - * @param config Config that stores releted params - * @return A vector of sets representing unprintable filaments for each extruder - */ -std::vector> ToolOrdering::get_physical_unprintables(const std::vector& used_filaments, const PrintConfig* config) -{ - // master saved in config is 1 based,so we should transfer to 0 based here - int master_extruder_id = config->master_extruder_id.value - 1; - auto tpu_filaments = get_filament_by_type(used_filaments, config, "TPU"); - if (tpu_filaments.size() > 1) { - throw Slic3r::RuntimeError(std::string("Only supports up to one TPU filament.")); - } - - int extruder_num = config->nozzle_diameter.size(); - // consider tpu, only place tpu in extruder with ams - std::vector>physical_unprintables(extruder_num); - if (extruder_num < 2) - return physical_unprintables; - - int extruder_without_tpu = 1 - master_extruder_id; - for (auto& f : tpu_filaments) - physical_unprintables[extruder_without_tpu].insert(f); - - // consider nozzle hrc, nozzle hrc should larger than filament hrc - for (size_t eid = 0; eid < physical_unprintables.size(); ++eid) { - auto nozzle_type = config->nozzle_type.get_at(eid); - int nozzle_hrc = Print::get_hrc_by_nozzle_type(NozzleType(nozzle_type)); - for (auto& f : used_filaments) { - int filament_hrc = config->required_nozzle_HRC.get_at(f); - if(filament_hrc>nozzle_hrc){ - physical_unprintables[eid].insert(f); - } - } - } - - return physical_unprintables; -} - -/** - * @brief Determines the unprintable filaments for each extruder based on its printable area. - * - * The returned array will always have the same size as the number of extruders. - * If extruder num is 1, just return an empty vector. - * If an extruder has no unprintable filaments, an empty set will also be returned - * - * @param unprintable_arrs An array of unprintable filaments for each extruder - * @param config Containing extruder nums or any other info requested - * @return A vector of sets representing unprintable filaments for each extruder - */ -std::vector> ToolOrdering::get_geometrical_unprintables(const std::vector>& unprintable_arrs, const PrintConfig* config) -{ - auto arrs_idx_switched = unprintable_arrs; - int extruder_nums = config->nozzle_diameter.size(); - std::vector> unprintables(extruder_nums); - if(extruder_nums < 2) - return unprintables; - - for (auto& arr : arrs_idx_switched) - for (auto& item : arr) - item -= 1; - - for (size_t idx = 0; idx < arrs_idx_switched.size(); ++idx) - unprintables[idx] = std::set(arrs_idx_switched[idx].begin(), arrs_idx_switched[idx].end()); - - return unprintables; -} - // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise. bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const { @@ -1147,8 +1071,8 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first std::vector used_filaments = collect_sorted_used_filaments(layer_filaments); - std::vector>geometric_unprintables = get_geometrical_unprintables(m_print->get_unprintable_filament_ids(), print_config); - std::vector>physical_unprintables = get_physical_unprintables(used_filaments, print_config); + std::vector>geometric_unprintables = m_print->get_geometric_unprintable_filaments(); + std::vector>physical_unprintables = m_print->get_physical_unprintable_filaments(used_filaments); filament_maps = m_print->get_filament_maps(); map_mode = m_print->get_filament_map_mode(); diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index df3268ca8..bc39d0dad 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -242,8 +242,6 @@ public: */ static std::vector get_recommended_filament_maps(const std::vector>& layer_filaments, const Print* print,const FilamentMapMode mode, const std::vector>& physical_unprintables, const std::vector>& geometric_unprintables); - static std::vector> get_physical_unprintables(const std::vector& layer_filaments, const PrintConfig* config); - static std::vector> get_geometrical_unprintables(const std::vector>& unprintable_arrs, const PrintConfig* config); static bool check_tpu_group(const std::vector&used_filaments,const std::vector& filament_maps,const PrintConfig* config); // should be called after doing reorder diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a0269a3bc..74d46f4d8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1820,7 +1820,22 @@ void Print::process(std::unordered_map* slice_time, bool } } + + if (this->set_started(psWipeTower)) { + { + std::vector> geometric_unprintables(m_config.nozzle_diameter.size()); + for (PrintObject* obj : m_objects) { + std::vector> obj_geometric_unprintables = obj->detect_extruder_geometric_unprintables(); + for (size_t idx = 0; idx < obj_geometric_unprintables.size(); ++idx) { + if (idx < geometric_unprintables.size()) { + geometric_unprintables[idx].insert(obj_geometric_unprintables[idx].begin(), obj_geometric_unprintables[idx].end()); + } + } + } + this->set_geometric_unprintable_filaments(geometric_unprintables); + } + m_wipe_tower_data.clear(); m_tool_ordering.clear(); if (this->has_wipe_tower()) { @@ -1875,9 +1890,8 @@ void Print::process(std::unordered_map* slice_time, bool } auto used_filaments = collect_sorted_used_filaments(all_filaments); - auto physical_unprintables = ToolOrdering::get_physical_unprintables(used_filaments, &m_config); - auto geometric_unprintables = ToolOrdering::get_geometrical_unprintables(get_unprintable_filament_ids(), &m_config); - + auto physical_unprintables = this->get_physical_unprintable_filaments(used_filaments); + auto geometric_unprintables = this->get_geometric_unprintable_filaments(); std::vectorfilament_maps = this->get_filament_maps(); auto map_mode = get_filament_map_mode(); // get recommended filament map @@ -2397,10 +2411,6 @@ void Print::update_filament_maps_to_config(std::vector f_maps) m_has_auto_filament_map_result = true; } -const std::vector>& Print::get_unprintable_filament_ids() const -{ - return m_config.unprintable_filament_map.values; -} std::vector Print::get_filament_maps() const { @@ -2412,6 +2422,44 @@ FilamentMapMode Print::get_filament_map_mode() const return m_config.filament_map_mode; } +std::vector> Print::get_physical_unprintable_filaments(const std::vector& used_filaments) const +{ + // master saved in config is 1 based,so we should transfer to 0 based here + int master_extruder_id = m_config.master_extruder_id.value - 1; + std::set tpu_filaments; + for (auto f : used_filaments) { + if (m_config.filament_type.get_at(f) == "TPU") + tpu_filaments.insert(f); + } + if (tpu_filaments.size() > 1) { + throw Slic3r::RuntimeError(std::string("Only supports up to one TPU filament.")); + } + + int extruder_num = m_config.nozzle_diameter.size(); + // consider tpu, only place tpu in extruder with ams + std::vector>physical_unprintables(extruder_num); + if (extruder_num < 2) + return physical_unprintables; + + int extruder_without_tpu = 1 - master_extruder_id; + for (auto& f : tpu_filaments) + physical_unprintables[extruder_without_tpu].insert(f); + + // consider nozzle hrc, nozzle hrc should larger than filament hrc + for (size_t eid = 0; eid < physical_unprintables.size(); ++eid) { + auto nozzle_type = m_config.nozzle_type.get_at(eid); + int nozzle_hrc = get_hrc_by_nozzle_type(NozzleType(nozzle_type)); + for (auto& f : used_filaments) { + int filament_hrc = m_config.required_nozzle_HRC.get_at(f); + if(filament_hrc>nozzle_hrc){ + physical_unprintables[eid].insert(f); + } + } + } + return physical_unprintables; +} + + std::vector Print::get_extruder_printable_height() const { return m_config.extruder_printable_height.values; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 67ac6a6f4..af10a3dda 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -436,7 +436,7 @@ public: //BBS BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name); void get_certain_layers(float start, float end, std::vector &out, std::vector &boundingbox_objects); - std::vector get_instances_shift_without_plate_offset(); + std::vector get_instances_shift_without_plate_offset() const; PrintObject* get_shared_object() const { return m_shared_object; } void set_shared_object(PrintObject *object); void clear_shared_object(); @@ -485,6 +485,17 @@ private: void generate_support_material(); void simplify_extrusion_path(); + /** + * @brief Determines the unprintable filaments for each extruder based on its printable area. + * + * The returned array will always have the same size as the number of extruders. + * If extruder num is 1, just return an empty vector. + * If an extruder has no unprintable filaments, an empty set will also be returned + * + * @return A vector of sets representing unprintable filaments for each extruder + */ + std::vector> detect_extruder_geometric_unprintables() const; + void slice_volumes(); //BBS ExPolygons _shrink_contour_holes(double contour_delta, double hole_delta, const ExPolygons& polys) const; @@ -831,8 +842,21 @@ public: const std::vector>& get_extruder_filament_info() const { return m_extruder_filament_info; } void set_extruder_filament_info(const std::vector>& filament_info) { m_extruder_filament_info = filament_info; } - // 1 based ids - const std::vector> &get_unprintable_filament_ids() const; + void set_geometric_unprintable_filaments(const std::vector> &unprintables_filament_ids) { m_geometric_unprintable_filaments = unprintables_filament_ids; } + std::vector> get_geometric_unprintable_filaments() const { return m_geometric_unprintable_filaments;} + + /** + * @brief Determines the unprintable filaments for each extruder based on its physical attributes + * + * Currently, the criteria for determining unprintable filament include the following: + * 1. TPU filaments can only be placed in the master extruder and must be grouped alone. + * 2. We only support at most 1 tpu filament. + * 3. An extruder can only accommodate filament with a hardness requirement lower than that of its nozzle. + * + * @param used_filaments Totally used filaments when slicing + * @return A vector of sets representing unprintable filaments for each extruder.Return an empty vecto if extruder num is 1 + */ + std::vector> get_physical_unprintable_filaments(const std::vector& used_filaments) const; std::vector get_extruder_printable_height() const; std::vector get_extruder_printable_polygons() const; @@ -958,7 +982,7 @@ private: FakeWipeTower m_fake_wipe_tower; bool m_has_auto_filament_map_result{false}; - std::vector> m_unprintable_filament_ids; + std::vector> m_geometric_unprintable_filaments; // OrcaSlicer: calibration Calib_Params m_calib_params; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 8184a3ffe..1d628c86e 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1521,8 +1521,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } else { // The PrintObject already exists and the copies differ. PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances)); - if (status != PrintBase::APPLY_STATUS_UNCHANGED) - update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); + if (status != PrintBase::APPLY_STATUS_UNCHANGED) { + size_t extruder_num = new_full_config.option("nozzle_diameter")->size(); + if (extruder_num > 1) { + update_apply_status(this->invalidate_steps({ psWipeTower,psSkirtBrim, psGCodeExport })); + } + update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); + } print_objects_new.emplace_back((*it_old)->print_object); const_cast(*it_old)->status = PrintObjectStatus::Reused; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 48fad2ed1..85abee9c8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -28,6 +28,7 @@ #include #include +#include #include @@ -190,6 +191,92 @@ void PrintObject::merge_layer_node(const size_t layer_id, int &max_merged_id, st } } +std::vector> PrintObject::detect_extruder_geometric_unprintables() const +{ + int extruder_size = m_print->config().nozzle_diameter.size(); + if(extruder_size == 1) + return std::vector>(1, std::set()); + + std::vector> tbb_geometric_unprintables(extruder_size); + std::vector unprintable_area_in_obj_coord = m_print->get_extruder_unprintable_polygons(); + std::vector unprintable_area_bbox; + + for (auto& polys : unprintable_area_in_obj_coord) { + for (auto& poly : polys) { + poly.translate(-m_instances.front().shift_without_plate_offset()); + } + unprintable_area_bbox.emplace_back(get_extents(polys)); + } + + tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), + [this, &tbb_geometric_unprintables, &unprintable_area_in_obj_coord, &unprintable_area_bbox](const tbb::blocked_range& range) { + for (int j = range.begin(); j < range.end(); ++j) { + auto layer = m_layers[j]; + for (auto layerm : layer->regions()) { + const auto& region = layerm->region(); + int wall_filament = region.config().wall_filament; + int solid_infill_filament = region.config().solid_infill_filament; + int sparse_infill_filament = region.config().sparse_infill_filament; + std::optional fill_expolys; + BoundingBox fill_bbox; + std::optional wall_expolys; + BoundingBox wall_bbox; + + for (size_t idx = 0; idx < unprintable_area_in_obj_coord.size(); ++idx) { + bool do_infill_filament_detect = (solid_infill_filament > 0 && tbb_geometric_unprintables[idx].count(solid_infill_filament - 1) == 0) || + (sparse_infill_filament > 0 && tbb_geometric_unprintables[idx].count(sparse_infill_filament-1) == 0); + + bool infill_unprintable = !layerm->fills.entities.empty() && + ((solid_infill_filament > 0 && tbb_geometric_unprintables[idx].count(solid_infill_filament - 1) > 0) || + (sparse_infill_filament > 0 && tbb_geometric_unprintables[idx].count(sparse_infill_filament - 1) > 0)); + + if (!layerm->fills.entities.empty() && do_infill_filament_detect) { + if (!fill_expolys) { + fill_expolys = layerm->fill_expolygons; + fill_bbox = get_extents(*fill_expolys); + } + if (fill_bbox.overlap(unprintable_area_bbox[idx]) && + !intersection(*fill_expolys, unprintable_area_in_obj_coord[idx]).empty()) { + if (solid_infill_filament > 0) + tbb_geometric_unprintables[idx].insert(solid_infill_filament - 1); + if (sparse_infill_filament > 0) + tbb_geometric_unprintables[idx].insert(sparse_infill_filament - 1); + infill_unprintable = true; + } + } + + bool do_wall_filament_detect = wall_filament > 0 && tbb_geometric_unprintables[idx].count(wall_filament - 1) == 0; + if (!layerm->perimeters.entities.empty() && do_wall_filament_detect) { + if (infill_unprintable) { + tbb_geometric_unprintables[idx].insert(wall_filament - 1); + continue; + } + + if (!wall_expolys) { + if (!fill_expolys) { + fill_expolys = layerm->fill_expolygons; + fill_bbox = get_extents(*fill_expolys); + } + wall_expolys = diff_ex(layerm->raw_slices, *fill_expolys); + wall_bbox = get_extents(*wall_expolys); + } + + if (wall_bbox.overlap(unprintable_area_bbox[idx]) && + !intersection(*wall_expolys, unprintable_area_in_obj_coord[idx]).empty()) { + tbb_geometric_unprintables[idx].insert(wall_filament - 1); + } + } + } + } + } + }); + + std::vector> ret(tbb_geometric_unprintables.size()); + for (size_t idx = 0; idx < ret.size(); ++idx) + ret[idx] = std::set(tbb_geometric_unprintables[idx].begin(), tbb_geometric_unprintables[idx].end()); + return ret; +} + // 1) Merges typed region slices into stInternal type. // 2) Increases an "extra perimeters" counter at region slices where needed. // 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). @@ -3055,7 +3142,7 @@ void PrintObject::get_certain_layers(float start, float end, std::vector PrintObject::get_instances_shift_without_plate_offset() +std::vector PrintObject::get_instances_shift_without_plate_offset() const { std::vector out; out.reserve(m_instances.size());