From 56bd8566c3640d3cdfec0f5bfe5c04c6060d77fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 19 Apr 2024 22:31:44 +0200 Subject: [PATCH 1/4] Use more readable data types for storing triangle splitting information. --- src/libslic3r/Model.cpp | 22 ++++++------- src/libslic3r/Model.hpp | 12 +++---- src/libslic3r/TriangleSelector.cpp | 51 ++++++++++++++---------------- src/libslic3r/TriangleSelector.hpp | 40 +++++++++++++++++++++-- 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 96136db395e..2e57f392c50 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -3293,7 +3293,7 @@ bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType typ bool FacetsAnnotation::set(const TriangleSelector& selector) { - std::pair>, std::vector> sel_map = selector.serialize(); + TriangleSelector::TriangleSplittingData sel_map = selector.serialize(); if (sel_map != m_data) { m_data = std::move(sel_map); this->touch(); @@ -3304,8 +3304,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector) void FacetsAnnotation::reset() { - m_data.first.clear(); - m_data.second.clear(); + m_data.triangles_to_split.clear(); + m_data.bitstream.clear(); this->touch(); } @@ -3316,15 +3316,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const { std::string out; - auto triangle_it = std::lower_bound(m_data.first.begin(), m_data.first.end(), triangle_idx, [](const std::pair &l, const int r) { return l.first < r; }); - if (triangle_it != m_data.first.end() && triangle_it->first == triangle_idx) { - int offset = triangle_it->second; - int end = ++ triangle_it == m_data.first.end() ? int(m_data.second.size()) : triangle_it->second; + auto triangle_it = std::lower_bound(m_data.triangles_to_split.begin(), m_data.triangles_to_split.end(), triangle_idx, [](const TriangleSelector::TriangleBitStreamMapping &l, const int r) { return l.triangle_idx < r; }); + if (triangle_it != m_data.triangles_to_split.end() && triangle_it->triangle_idx == triangle_idx) { + int offset = triangle_it->bitstream_start_idx; + int end = ++ triangle_it == m_data.triangles_to_split.end() ? int(m_data.bitstream.size()) : triangle_it->bitstream_start_idx; while (offset < end) { int next_code = 0; for (int i=3; i>=0; --i) { next_code = next_code << 1; - next_code |= int(m_data.second[offset + i]); + next_code |= int(m_data.bitstream[offset + i]); } offset += 4; @@ -3341,8 +3341,8 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str) { assert(! str.empty()); - assert(m_data.first.empty() || m_data.first.back().first < triangle_id); - m_data.first.emplace_back(triangle_id, int(m_data.second.size())); + assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id); + m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size())); for (auto it = str.crbegin(); it != str.crend(); ++it) { const char ch = *it; @@ -3356,7 +3356,7 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri // Convert to binary and append into code. for (int i=0; i<4; ++i) - m_data.second.insert(m_data.second.end(), bool(dec & (1 << i))); + m_data.bitstream.insert(m_data.bitstream.end(), bool(dec & (1 << i))); } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 927ab47f9ba..a8a75030aba 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -16,6 +16,7 @@ #include "enum_bitmask.hpp" #include "TextConfiguration.hpp" #include "EmbossShape.hpp" +#include "TriangleSelector.hpp" //BBS: add bbs 3mf #include "Format/bbs_3mf.hpp" @@ -747,7 +748,7 @@ class FacetsAnnotation final : public ObjectWithTimestamp { // Assign the content if the timestamp differs, don't assign an ObjectID. void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } - const std::pair>, std::vector>& get_data() const throw() { return m_data; } + const TriangleSelector::TriangleSplittingData &get_data() const throw() { return m_data; } bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; // BBS @@ -755,7 +756,7 @@ class FacetsAnnotation final : public ObjectWithTimestamp { void set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type); indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const; bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const; - bool empty() const { return m_data.first.empty(); } + bool empty() const { return m_data.triangles_to_split.empty(); } // Following method clears the config and increases its timestamp, so the deleted // state is considered changed from perspective of the undo/redo stack. @@ -765,12 +766,11 @@ class FacetsAnnotation final : public ObjectWithTimestamp { std::string get_triangle_as_string(int i) const; // Before deserialization, reserve space for n_triangles. - void reserve(int n_triangles) { m_data.first.reserve(n_triangles); } + void reserve(int n_triangles) { m_data.triangles_to_split.reserve(n_triangles); } // Deserialize triangles one by one, with strictly increasing triangle_id. void set_triangle_from_string(int triangle_id, const std::string& str); // After deserializing the last triangle, shrink data to fit. - void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); } - bool equals(const FacetsAnnotation &other) const; + void shrink_to_fit() { m_data.triangles_to_split.shrink_to_fit(); m_data.bitstream.shrink_to_fit(); } private: // Constructors to be only called by derived classes. @@ -796,7 +796,7 @@ class FacetsAnnotation final : public ObjectWithTimestamp { ar(cereal::base_class(this), m_data); } - std::pair>, std::vector> m_data; + TriangleSelector::TriangleSplittingData m_data; // To access set_new_unique_id() when copy / pasting a ModelVolume. friend class ModelVolume; diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 329e50f3291..927ca7d56d7 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1622,8 +1622,7 @@ void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, cons } } -std::pair>, std::vector> TriangleSelector::serialize() const -{ +TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state // or how it is split. Each triangle is encoded by 4 bits (xxyy) or 8 bits (zzzzxxyy): // leaf triangle: xx = EnforcerBlockerType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0 @@ -1639,7 +1638,7 @@ std::pair>, std::vector> TriangleSelector: // (std::function calls using a pointer, while this implementation calls directly). struct Serializer { const TriangleSelector* triangle_selector; - std::pair>, std::vector> data; + TriangleSplittingData data; void serialize(int facet_idx) { const Triangle& tr = triangle_selector->m_triangles[facet_idx]; @@ -1648,8 +1647,8 @@ std::pair>, std::vector> TriangleSelector: int split_sides = tr.number_of_split_sides(); assert(split_sides >= 0 && split_sides <= 3); - data.second.push_back(split_sides & 0b01); - data.second.push_back(split_sides & 0b10); + data.bitstream.push_back(split_sides & 0b01); + data.bitstream.push_back(split_sides & 0b10); if (split_sides) { // If this triangle is split, save which side is split (in case @@ -1657,8 +1656,8 @@ std::pair>, std::vector> TriangleSelector: // be ignored for 3-side split. assert(tr.is_split() && split_sides > 0); assert(tr.special_side() >= 0 && tr.special_side() <= 3); - data.second.push_back(tr.special_side() & 0b01); - data.second.push_back(tr.special_side() & 0b10); + data.bitstream.push_back(tr.special_side() & 0b01); + data.bitstream.push_back(tr.special_side() & 0b10); // Now save all children. // Serialized in reverse order for compatibility with PrusaSlicer 2.3.1. for (int child_idx = split_sides; child_idx >= 0; -- child_idx) @@ -1670,38 +1669,37 @@ std::pair>, std::vector> TriangleSelector: assert(n <= 16); if (n <= 16) { // Store "11" plus 4 bits of (n-3). - data.second.insert(data.second.end(), { true, true }); + data.bitstream.insert(data.bitstream.end(), { true, true }); n -= 3; for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) - data.second.push_back(n & (uint64_t(0b0001) << bit_idx)); + data.bitstream.push_back(n & (uint64_t(0b0001) << bit_idx)); } } else { // Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams. // Store 2 bits of n. - data.second.push_back(n & 0b01); - data.second.push_back(n & 0b10); + data.bitstream.push_back(n & 0b01); + data.bitstream.push_back(n & 0b10); } } } } out { this }; - out.data.first.reserve(m_orig_size_indices); + out.data.triangles_to_split.reserve(m_orig_size_indices); for (int i=0; i>, std::vector> &data, bool needs_reset, EnforcerBlockerType max_ebt) -{ +void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs_reset) { if (needs_reset) reset(); // dump any current state for (auto [triangle_id, ibit] : data.first) { @@ -1712,7 +1710,7 @@ void TriangleSelector::deserialize(const std::pair parents; - for (auto [triangle_id, ibit] : data.first) { + for (auto [triangle_id, ibit] : data.triangles_to_split) { assert(triangle_id < int(m_triangles.size())); - assert(ibit < int(data.second.size())); + assert(ibit < int(data.bitstream.size())); auto next_nibble = [&data, &ibit = ibit]() { int n = 0; for (int i = 0; i < 4; ++ i) - n |= data.second[ibit ++] << i; + n |= data.bitstream[ibit ++] << i; return n; }; @@ -1812,20 +1810,19 @@ void TriangleSelector::deserialize(const std::pair>, std::vector> &data, const EnforcerBlockerType test_state) -{ +bool TriangleSelector::has_facets(const TriangleSplittingData &data, const EnforcerBlockerType test_state) { // Depth-first queue of a number of unvisited children. // Kept outside of the loop to avoid re-allocating inside the loop. std::vector parents_children; parents_children.reserve(64); - for (const std::pair &triangle_id_and_ibit : data.first) { - int ibit = triangle_id_and_ibit.second; - assert(ibit < int(data.second.size())); + for (const TriangleBitStreamMapping &triangle_id_and_ibit : data.triangles_to_split) { + int ibit = triangle_id_and_ibit.bitstream_start_idx; + assert(ibit < int(data.bitstream.size())); auto next_nibble = [&data, &ibit = ibit]() { int n = 0; for (int i = 0; i < 4; ++ i) - n |= data.second[ibit ++] << i; + n |= data.bitstream[ibit ++] << i; return n; }; // < 0 -> negative of a number of children diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index b73283c0e1c..1f42dc398f7 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -208,6 +208,40 @@ class TriangleSelector } }; + struct TriangleBitStreamMapping + { + // Index of the triangle to which we assign the bitstream containing splitting information. + int triangle_idx = -1; + // Index of the first bit of the bitstream assigned to this triangle. + int bitstream_start_idx = -1; + + TriangleBitStreamMapping() = default; + explicit TriangleBitStreamMapping(int triangleIdx, int bitstreamStartIdx) : triangle_idx(triangleIdx), bitstream_start_idx(bitstreamStartIdx) {} + + friend bool operator==(const TriangleBitStreamMapping &lhs, const TriangleBitStreamMapping &rhs) { return lhs.triangle_idx == rhs.triangle_idx && lhs.bitstream_start_idx == rhs.bitstream_start_idx; } + friend bool operator!=(const TriangleBitStreamMapping &lhs, const TriangleBitStreamMapping &rhs) { return !(lhs == rhs); } + + private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(triangle_idx, bitstream_start_idx); } + }; + + struct TriangleSplittingData { + // Vector of triangles and its indexes to the bitstream. + std::vector triangles_to_split; + // Bit stream containing splitting information. + std::vector bitstream; + + TriangleSplittingData() = default; + + friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return lhs.triangles_to_split == rhs.triangles_to_split && lhs.bitstream == rhs.bitstream; } + friend bool operator!=(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return !(lhs == rhs); } + + private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(triangles_to_split, bitstream); } + }; + std::pair, std::vector> precompute_all_neighbors() const; void precompute_all_neighbors_recursive(int facet_idx, const Vec3i32 &neighbors, const Vec3i32 &neighbors_propagated, std::vector &neighbors_out, std::vector &neighbors_normal_out) const; @@ -247,7 +281,7 @@ class TriangleSelector bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool has_facets(EnforcerBlockerType state) const; - static bool has_facets(const std::pair>, std::vector> &data, EnforcerBlockerType test_state); + static bool has_facets(const TriangleSplittingData &data, EnforcerBlockerType test_state); int num_facets(EnforcerBlockerType state) const; // Get facets at a given state. Don't triangulate T-joints. indexed_triangle_set get_facets(EnforcerBlockerType state) const; @@ -270,10 +304,10 @@ class TriangleSelector // Store the division trees in compact form (a long stream of bits for each triangle of the original mesh). // First vector contains pairs of (triangle index, first bit in the second vector). - std::pair>, std::vector> serialize() const; + TriangleSplittingData serialize() const; // Load serialized data. Assumes that correct mesh is loaded. - void deserialize(const std::pair>, std::vector>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax); + void deserialize(const TriangleSplittingData &data, bool needs_reset = true); // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); From 68be9411ccd6d8d554f75753b6f9be7d989c5462 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 8 Aug 2024 22:51:32 +0800 Subject: [PATCH 2/4] fix build errors --- src/libslic3r/Model.cpp | 2 +- src/libslic3r/Model.hpp | 26 +------------------------- src/libslic3r/TriangleSelector.cpp | 5 +++-- src/libslic3r/TriangleSelector.hpp | 30 ++++++++++++++++++++++++++++-- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2e57f392c50..752924dd11f 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -3362,7 +3362,7 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri bool FacetsAnnotation::equals(const FacetsAnnotation &other) const { - const std::pair>, std::vector>& data = other.get_data(); + const auto& data = other.get_data(); return (m_data == data); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a8a75030aba..f2fb2369d5f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -705,31 +705,6 @@ class ModelObject final : public ObjectBase void update_min_max_z(); }; -enum class EnforcerBlockerType : int8_t { - // Maximum is 3. The value is serialized in TriangleSelector into 2 bits. - NONE = 0, - ENFORCER = 1, - BLOCKER = 2, - // Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code. - Extruder1 = ENFORCER, - Extruder2 = BLOCKER, - Extruder3, - Extruder4, - Extruder5, - Extruder6, - Extruder7, - Extruder8, - Extruder9, - Extruder10, - Extruder11, - Extruder12, - Extruder13, - Extruder14, - Extruder15, - Extruder16, - ExtruderMax = Extruder16 -}; - enum class ConversionType : int { CONV_TO_INCH, CONV_FROM_INCH, @@ -771,6 +746,7 @@ class FacetsAnnotation final : public ObjectWithTimestamp { void set_triangle_from_string(int triangle_id, const std::string& str); // After deserializing the last triangle, shrink data to fit. void shrink_to_fit() { m_data.triangles_to_split.shrink_to_fit(); m_data.bitstream.shrink_to_fit(); } + bool equals(const FacetsAnnotation &other) const; private: // Constructors to be only called by derived classes. diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 927ca7d56d7..5b31a96e663 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1699,10 +1699,11 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const { return out.data; } -void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs_reset) { +void TriangleSelector::deserialize(const TriangleSplittingData& data, bool needs_reset, EnforcerBlockerType max_ebt) +{ if (needs_reset) reset(); // dump any current state - for (auto [triangle_id, ibit] : data.first) { + for (auto [triangle_id, ibit] : data.triangles_to_split) { if (triangle_id >= int(m_triangles.size())) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "array bound:error:triangle_id >= int(m_triangles.size())"; return; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 1f42dc398f7..5491b5219b2 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -7,10 +7,34 @@ #include #include "Point.hpp" #include "TriangleMesh.hpp" -#include "libslic3r/Model.hpp" namespace Slic3r { +enum class EnforcerBlockerType : int8_t { + // Maximum is 3. The value is serialized in TriangleSelector into 2 bits. + NONE = 0, + ENFORCER = 1, + BLOCKER = 2, + // Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code. + Extruder1 = ENFORCER, + Extruder2 = BLOCKER, + Extruder3, + Extruder4, + Extruder5, + Extruder6, + Extruder7, + Extruder8, + Extruder9, + Extruder10, + Extruder11, + Extruder12, + Extruder13, + Extruder14, + Extruder15, + Extruder16, + ExtruderMax = Extruder16 +}; + // Following class holds information about selected triangles. It also has power // to recursively subdivide the triangles and make the selection finer. class TriangleSelector @@ -307,7 +331,9 @@ class TriangleSelector TriangleSplittingData serialize() const; // Load serialized data. Assumes that correct mesh is loaded. - void deserialize(const TriangleSplittingData &data, bool needs_reset = true); + void deserialize(const TriangleSplittingData& data, + bool needs_reset = true, + EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax); // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); From a3f217b2a2b76f2c4c78ca9f8930bd13f1cc5611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 23 Apr 2024 13:45:34 +0200 Subject: [PATCH 3/4] SPE-2063: Determine correctly which extruders are used when the object is painted by the multi-material painting gizmo. During the serialization of TriangleSelector and also during reading serialized painting data from 3MF, we cache all used states in the painted triangle mesh. Based on this information, we can quickly determine which extruders are used and which don't. --- src/libslic3r/Model.cpp | 5 ++++- src/libslic3r/Model.hpp | 6 ++--- src/libslic3r/PrintApply.cpp | 19 ++++++++++++---- src/libslic3r/TriangleSelector.cpp | 36 ++++++++++++++++++++++++++++++ src/libslic3r/TriangleSelector.hpp | 25 ++++++++++++++++++--- 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 752924dd11f..ad63a21ee61 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -3344,6 +3344,7 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id); m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size())); + const size_t bitstream_start_idx = m_data.bitstream.size(); for (auto it = str.crbegin(); it != str.crend(); ++it) { const char ch = *it; int dec = 0; @@ -3355,9 +3356,11 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri assert(false); // Convert to binary and append into code. - for (int i=0; i<4; ++i) + for (int i = 0; i < 4; ++i) m_data.bitstream.insert(m_data.bitstream.end(), bool(dec & (1 << i))); } + + m_data.update_used_states(bitstream_start_idx); } bool FacetsAnnotation::equals(const FacetsAnnotation &other) const diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f2fb2369d5f..9fc315778fa 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -721,9 +721,9 @@ enum class En3mfType : int { class FacetsAnnotation final : public ObjectWithTimestamp { public: // Assign the content if the timestamp differs, don't assign an ObjectID. - void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } - void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } - const TriangleSelector::TriangleSplittingData &get_data() const throw() { return m_data; } + void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } } + void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } + const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; } bool set(const TriangleSelector& selector); indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; // BBS diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index b6fd173c036..232c86b9ec5 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1498,11 +1498,22 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } std::vector painting_extruders; if (const auto &volumes = print_object.model_object()->volumes; - num_extruders > 1 && + num_extruders > 1 && std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) { - //FIXME be more specific! Don't enumerate extruders that are not used for painting! - painting_extruders.assign(num_extruders , 0); - std::iota(painting_extruders.begin(), painting_extruders.end(), 1); + + std::array(EnforcerBlockerType::ExtruderMax)> used_facet_states{}; + for (const ModelVolume *volume : volumes) { + const std::vector &volume_used_facet_states = volume->mmu_segmentation_facets.get_data().used_states; + + assert(volume_used_facet_states.size() == used_facet_states.size()); + for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx) + used_facet_states[state_idx] |= volume_used_facet_states[state_idx]; + } + + for (size_t state_idx = static_cast(EnforcerBlockerType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) { + if (used_facet_states[state_idx]) + painting_extruders.emplace_back(state_idx); + } } if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) { // Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable. diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 5b31a96e663..e7c59b7119b 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1665,6 +1665,9 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const { } else { // In case this is leaf, we better save information about its state. int n = int(tr.get_state()); + if (n < static_cast(EnforcerBlockerType::ExtruderMax)) + data.used_states[n] = true; + if (n >= 3) { assert(n <= 16); if (n <= 16) { @@ -1810,6 +1813,39 @@ void TriangleSelector::deserialize(const TriangleSplittingData& data, bool needs } } +void TriangleSelector::TriangleSplittingData::update_used_states(const size_t bitstream_start_idx) { + assert(bitstream_start_idx < this->bitstream.size()); + assert(!this->bitstream.empty() && this->bitstream.size() != bitstream_start_idx); + assert((this->bitstream.size() - bitstream_start_idx) % 4 == 0); + + if (this->bitstream.empty() || this->bitstream.size() == bitstream_start_idx) + return; + + size_t nibble_idx = bitstream_start_idx; + + auto read_next_nibble = [&data_bitstream = std::as_const(this->bitstream), &nibble_idx]() -> uint8_t { + assert(nibble_idx + 3 < data_bitstream.size()); + uint8_t code = 0; + for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) + code |= data_bitstream[nibble_idx++] << bit_idx; + return code; + }; + + while (nibble_idx < this->bitstream.size()) { + const uint8_t code = read_next_nibble(); + + if (const bool is_split = (code & 0b11) != 0; is_split) + continue; + + const uint8_t facet_state = (code & 0b1100) == 0b1100 ? read_next_nibble() + 3 : code >> 2; + assert(facet_state < this->used_states.size()); + if (facet_state >= this->used_states.size()) + continue; + + this->used_states[facet_state] = true; + } +} + // Lightweight variant of deserialization, which only tests whether a face of test_state exists. bool TriangleSelector::has_facets(const TriangleSplittingData &data, const EnforcerBlockerType test_state) { // Depth-first queue of a number of unvisited children. diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 5491b5219b2..e22a551baf5 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -254,16 +254,32 @@ class TriangleSelector // Vector of triangles and its indexes to the bitstream. std::vector triangles_to_split; // Bit stream containing splitting information. - std::vector bitstream; + std::vector bitstream; + // Array indicating which triangle state types are used (encoded inside bitstream). + std::vector used_states { std::vector(static_cast(EnforcerBlockerType::ExtruderMax), false) }; TriangleSplittingData() = default; - friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return lhs.triangles_to_split == rhs.triangles_to_split && lhs.bitstream == rhs.bitstream; } + friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { + return lhs.triangles_to_split == rhs.triangles_to_split + && lhs.bitstream == rhs.bitstream + && lhs.used_states == rhs.used_states; + } + friend bool operator!=(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return !(lhs == rhs); } + // Reset all used states before they are recomputed based on the bitstream. + void reset_used_states() { + used_states.resize(static_cast(EnforcerBlockerType::ExtruderMax), false); + std::fill(used_states.begin(), used_states.end(), false); + } + + // Update used states based on the bitstream. It just iterated over the bitstream from the bitstream_start_idx till the end. + void update_used_states(size_t bitstream_start_idx); + private: friend class cereal::access; - template void serialize(Archive &ar) { ar(triangles_to_split, bitstream); } + template void serialize(Archive &ar) { ar(triangles_to_split, bitstream, used_states); } }; std::pair, std::vector> precompute_all_neighbors() const; @@ -335,6 +351,9 @@ class TriangleSelector bool needs_reset = true, EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax); + // Extract all used facet states from the given TriangleSplittingData. + static std::vector extract_used_facet_states(const TriangleSplittingData &data); + // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); From 7571f2e3cd12ade61ae10b64ff89ec076b6df4fc Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 9 Aug 2024 13:13:17 +0800 Subject: [PATCH 4/4] Fixed an bug that filament list was not updated properly --- src/slic3r/GUI/Plater.cpp | 22 ++++++++++++++-------- src/slic3r/GUI/Plater.hpp | 1 + 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 450bf768f24..5ce506951bf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1354,7 +1354,7 @@ void Sidebar::update_presets(Preset::Type preset_type) for (size_t i = 0; i < filament_cnt; i++) p->combos_filament[i]->update(); - dynamic_filament_list.update(); + update_dynamic_filament_list(); break; } @@ -1637,7 +1637,7 @@ void Sidebar::on_filaments_change(size_t num_filaments) Layout(); p->m_panel_filament_title->Refresh(); update_ui_from_settings(); - dynamic_filament_list.update(); + update_dynamic_filament_list(); } void Sidebar::add_filament() { @@ -1818,7 +1818,7 @@ void Sidebar::sync_ams_list() c->update(); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); - dynamic_filament_list.update(); + update_dynamic_filament_list(); // Expand filament list p->m_panel_filament_content->SetMaxSize({-1, -1}); // BBS:Synchronized consumables information @@ -1854,6 +1854,12 @@ void Sidebar::show_SEMM_buttons(bool bshow) Layout(); } +void Sidebar::update_dynamic_filament_list() +{ + dynamic_filament_list.update(); + dynamic_filament_list_1_based.update(); +} + ObjectList* Sidebar::obj_list() { // BBS @@ -6535,7 +6541,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); wxGetApp().plater()->update_project_dirty_from_presets(); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); - dynamic_filament_list.update(); + sidebar->update_dynamic_filament_list(); bool flag_is_change = is_support_filament(idx); if (flag != flag_is_change) { sidebar->auto_calc_flushing_volumes(idx); @@ -12794,7 +12800,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (update_filament_colors_in_full_config()) { p->sidebar->obj_list()->update_filament_colors(); - dynamic_filament_list.update(); + p->sidebar->update_dynamic_filament_list(); continue; } } @@ -12851,9 +12857,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config) bed_shape_changed = true; update_scheduled = true; } - // BBS - else if (opt_key == "support_interface_filament" || - opt_key == "support_filament") { + // Orca: update when *_filament changed + else if (opt_key == "support_interface_filament" || opt_key == "support_filament" || opt_key == "wall_filament" || + opt_key == "sparse_infill_filament" || opt_key == "solid_infill_filament") { update_scheduled = true; } } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5e5c88e7e61..16396631a85 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -150,6 +150,7 @@ class Sidebar : public wxPanel void sync_ams_list(); // Orca void show_SEMM_buttons(bool bshow); + void update_dynamic_filament_list(); ObjectList* obj_list(); ObjectSettings* obj_settings();