From 13e2af71e1f5e41395b9c1a4333cb98ee7a8a25e 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/5] 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 52ef00594c..cff0e0665d 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2044,7 +2044,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(); @@ -2055,8 +2055,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(); } @@ -2067,15 +2067,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; @@ -2092,8 +2092,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; @@ -2107,7 +2107,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 c885b78e56..e03d59eb73 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -27,6 +27,7 @@ #include "enum_bitmask.hpp" #include "TextConfiguration.hpp" #include "EmbossShape.hpp" +#include "TriangleSelector.hpp" #include #include @@ -56,7 +57,6 @@ class ModelVolume; class ModelWipeTower; class Print; class SLAPrint; -class TriangleSelector; namespace UndoRedo { class StackImpl; @@ -689,12 +689,12 @@ 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 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; 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. @@ -704,11 +704,11 @@ public: 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(); } + 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. @@ -734,7 +734,7 @@ private: 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 8324b5a4c9..6197363ab4 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1526,8 +1526,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 @@ -1543,7 +1542,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]; @@ -1552,8 +1551,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 @@ -1561,8 +1560,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) @@ -1574,44 +1573,43 @@ 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) -{ +void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs_reset) { if (needs_reset) reset(); // dump any current state // Reserve number of triangles as if each triangle was saved with 4 bits. // With MMU painting this estimate may be somehow low, but better than nothing. - m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4)); + m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.bitstream.size() / 4)); // Number of triangles is twice the number of vertices on a large manifold mesh of genus zero. // Here the triangles count account for both the nodes and leaves, thus the following line may overestimate. m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2)); @@ -1627,13 +1625,13 @@ 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; }; @@ -1706,20 +1704,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 0d6a113943..66f92fd37d 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -184,6 +184,40 @@ public: } }; + 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 Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &neighbors_out, std::vector &neighbors_normal_out) const; @@ -222,7 +256,7 @@ public: 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; @@ -242,10 +276,10 @@ public: // 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); + 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 b425fe459d965eafa67797af84bcb235fe8cd69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 22 Apr 2024 15:38:11 +0200 Subject: [PATCH 2/5] Rename EnforcerBlockerType to TriangleStateType because we no longer use it just as a blocker or an enforcer. --- src/libslic3r/GCode/SeamPainting.cpp | 4 +- src/libslic3r/Model.cpp | 6 +- src/libslic3r/Model.hpp | 38 ++--------- src/libslic3r/MultiMaterialSegmentation.cpp | 4 +- src/libslic3r/Print.cpp | 2 +- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/Support/SupportMaterial.cpp | 4 +- src/libslic3r/Support/TreeSupport.cpp | 4 +- src/libslic3r/TriangleSelector.cpp | 54 +++++++--------- src/libslic3r/TriangleSelector.hpp | 63 ++++++++++++------- src/libslic3r/TriangleSelectorWrapper.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 16 ++--- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 4 +- 16 files changed, 99 insertions(+), 114 deletions(-) diff --git a/src/libslic3r/GCode/SeamPainting.cpp b/src/libslic3r/GCode/SeamPainting.cpp index 1f78eb5098..1f029bc5e8 100644 --- a/src/libslic3r/GCode/SeamPainting.cpp +++ b/src/libslic3r/GCode/SeamPainting.cpp @@ -7,12 +7,12 @@ Painting::Painting(const Transform3d &obj_transform, const ModelVolumePtrs &volu auto model_transformation = obj_transform * mv->get_matrix(); indexed_triangle_set enforcers = mv->seam_facets - .get_facets(*mv, EnforcerBlockerType::ENFORCER); + .get_facets(*mv, TriangleStateType::ENFORCER); its_transform(enforcers, model_transformation); its_merge(this->enforcers, enforcers); indexed_triangle_set blockers = mv->seam_facets - .get_facets(*mv, EnforcerBlockerType::BLOCKER); + .get_facets(*mv, TriangleStateType::BLOCKER); its_transform(blockers, model_transformation); its_merge(this->blockers, blockers); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index cff0e0665d..22f3a4b132 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2021,7 +2021,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin } -indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const +indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, TriangleStateType type) const { TriangleSelector selector(mv.mesh()); // Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize(). @@ -2029,7 +2029,7 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce return selector.get_facets(type); } -indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const +indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, TriangleStateType type) const { TriangleSelector selector(mv.mesh()); // Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize(). @@ -2037,7 +2037,7 @@ indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, return selector.get_facets_strict(type); } -bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType type) const +bool FacetsAnnotation::has_facets(const ModelVolume& mv, TriangleStateType type) const { return TriangleSelector::has_facets(m_data, type); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index e03d59eb73..67fb7ac517 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -654,29 +654,6 @@ private: 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, -}; - enum class ConversionType : int { CONV_TO_INCH, CONV_FROM_INCH, @@ -691,9 +668,9 @@ public: 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; } bool set(const TriangleSelector& selector); - indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; - indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const; - bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const; + indexed_triangle_set get_facets(const ModelVolume& mv, TriangleStateType type) const; + indexed_triangle_set get_facets_strict(const ModelVolume& mv, TriangleStateType type) const; + bool has_facets(const ModelVolume& mv, TriangleStateType type) const; bool empty() const { return m_data.triangles_to_split.empty(); } // Following method clears the config and increases its timestamp, so the deleted @@ -718,9 +695,9 @@ private: // by an existing ID copied from elsewhere. explicit FacetsAnnotation(int) : ObjectWithTimestamp(-1) {} // Copy constructor copies the ID. - explicit FacetsAnnotation(const FacetsAnnotation &rhs) = default; + FacetsAnnotation(const FacetsAnnotation &rhs) = default; // Move constructor copies the ID. - explicit FacetsAnnotation(FacetsAnnotation &&rhs) = default; + FacetsAnnotation(FacetsAnnotation &&rhs) = default; // called by ModelVolume::assign_copy() FacetsAnnotation& operator=(const FacetsAnnotation &rhs) = default; @@ -729,10 +706,7 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; - template void serialize(Archive &ar) - { - ar(cereal::base_class(this), m_data); - } + template void serialize(Archive &ar) { ar(cereal::base_class(this), m_data); } TriangleSelector::TriangleSplittingData m_data; diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 708bdcf5ee..155f26e13d 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -894,7 +894,7 @@ static inline std::vector> mm_segmentation_top_and_botto if (mv->is_model_part()) { const Transform3d volume_trafo = object_trafo * mv->get_matrix(); for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) { - const indexed_triangle_set painted = mv->mm_segmentation_facets.get_facets_strict(*mv, EnforcerBlockerType(extruder_idx)); + const indexed_triangle_set painted = mv->mm_segmentation_facets.get_facets_strict(*mv, TriangleStateType(extruder_idx)); #ifdef MM_SEGMENTATION_DEBUG_TOP_BOTTOM { static int iRun = 0; @@ -1298,7 +1298,7 @@ std::vector> multi_material_segmentation_by_painting(con tbb::parallel_for(tbb::blocked_range(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) { throw_on_cancel_callback(); - const indexed_triangle_set custom_facets = mv->mm_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); + const indexed_triangle_set custom_facets = mv->mm_segmentation_facets.get_facets(*mv, TriangleStateType(extruder_idx)); if (!mv->is_model_part() || custom_facets.indices.empty()) continue; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e7d223cbd0..116fc88d3b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -760,7 +760,7 @@ std::string Print::validate(std::vector* warnings) const if (! object->has_support() && warnings) { for (const ModelVolume* mv : object->model_object()->volumes) { bool has_enforcers = mv->is_support_enforcer() || - (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER)); + (mv->is_model_part() && mv->supported_facets.has_facets(*mv, TriangleStateType::ENFORCER)); if (has_enforcers) { warnings->emplace_back("_SUPPORTS_OFF"); break; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 712bb91c22..1acc6d0b7c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -353,7 +353,7 @@ public: std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } // Helpers to project custom facets on slices - void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; + void project_and_append_custom_facets(bool seam, TriangleStateType type, std::vector& expolys) const; private: // to be called from Print only. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d5ad93de01..ac8d440846 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -3150,7 +3150,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs layers, const inde } void PrintObject::project_and_append_custom_facets( - bool seam, EnforcerBlockerType type, std::vector& out) const + bool seam, TriangleStateType type, std::vector& out) const { for (const ModelVolume* mv : this->model_object()->volumes) if (mv->is_model_part()) { diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 937a4bf5df..25f204fb18 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1106,8 +1106,8 @@ struct SupportAnnotations buildplate_covered(buildplate_covered) { // Append custom supports. - object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers); - object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers); + object.project_and_append_custom_facets(false, TriangleStateType::ENFORCER, enforcers_layers); + object.project_and_append_custom_facets(false, TriangleStateType::BLOCKER, blockers_layers); } std::vector enforcers_layers; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 2f630df7cf..3161c85cb6 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -207,8 +207,8 @@ static std::vector>> group_me const int support_enforce_layers = config.support_material_enforce_layers.value; std::vector enforcers_layers{ print_object.slice_support_enforcers() }; std::vector blockers_layers{ print_object.slice_support_blockers() }; - print_object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers); - print_object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers); + print_object.project_and_append_custom_facets(false, TriangleStateType::ENFORCER, enforcers_layers); + print_object.project_and_append_custom_facets(false, TriangleStateType::BLOCKER, blockers_layers); const int support_threshold = config.support_material_threshold.value; const bool support_threshold_auto = support_threshold == 0; // +1 makes the threshold inclusive diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 6197363ab4..02070dfe70 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -234,7 +234,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c return this->select_unsplit_triangle(hit, facet_idx, neighbors); } -void TriangleSelector::select_patch(int facet_start, std::unique_ptr &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg) +void TriangleSelector::select_patch(int facet_start, std::unique_ptr &&cursor, TriangleStateType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg) { assert(facet_start < m_orig_size_indices); @@ -455,7 +455,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_ return; assert(!m_triangles[start_facet_idx].is_split()); - EnforcerBlockerType start_facet_state = m_triangles[start_facet_idx].get_state(); + TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state(); this->seed_fill_unselect_all_triangles(); if (!propagate) { @@ -511,8 +511,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_ // This is done by an actual recursive call. Returns false if the triangle is // outside the cursor. // Called by select_patch() and by itself. -bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting) -{ +bool TriangleSelector::select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting) { assert(facet_idx < int(m_triangles.size())); if (! m_triangles[facet_idx].valid()) @@ -861,8 +860,7 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec return out; } -bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting) -{ +bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting) { assert(facet_idx < int(m_triangles.size())); Triangle* tr = &m_triangles[facet_idx]; @@ -914,8 +912,7 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &nei return true; } -void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state) -{ +void TriangleSelector::set_facet(int facet_idx, TriangleStateType state) { assert(facet_idx < m_orig_size_indices); undivide_triangle(facet_idx); assert(! m_triangles[facet_idx].is_split()); @@ -934,7 +931,7 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors) Triangle* tr = &m_triangles[facet_idx]; assert(this->verify_triangle_neighbors(*tr, neighbors)); - EnforcerBlockerType old_type = tr->get_state(); + TriangleStateType old_type = tr->get_state(); // If we got here, we are about to actually split the triangle. const double limit_squared = m_edge_limit_sqr; @@ -1117,7 +1114,7 @@ void TriangleSelector::remove_useless_children(int facet_idx) // Return if a child is not leaf or two children differ in type. - EnforcerBlockerType first_child_type = EnforcerBlockerType::NONE; + TriangleStateType first_child_type = TriangleStateType::NONE; for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { if (m_triangles[tr.children[child_idx]].is_split()) return; @@ -1217,8 +1214,7 @@ void TriangleSelector::set_edge_limit(float edge_limit) m_edge_limit_sqr = std::pow(edge_limit, 2.f); } -int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state) -{ +int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const TriangleStateType state) { for (int i : {a, b, c}) { assert(i >= 0 && i < int(m_vertices.size())); ++m_vertices[i].ref_cnt; @@ -1251,8 +1247,7 @@ int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, co // Split a triangle based on Triangle::number_of_split_sides() and Triangle::special_side() // by allocating child triangles and midpoint vertices. // Midpoint vertices are possibly reused by traversing children of neighbor triangles. -void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state) -{ +void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state) { // Reserve space for the new triangles upfront, so that the reference to this triangle will not change. { size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1; @@ -1318,16 +1313,14 @@ void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, Enfo #endif // NDEBUG } -bool TriangleSelector::has_facets(EnforcerBlockerType state) const -{ +bool TriangleSelector::has_facets(TriangleStateType state) const { for (const Triangle& tr : m_triangles) if (tr.valid() && ! tr.is_split() && tr.get_state() == state) return true; return false; } -int TriangleSelector::num_facets(EnforcerBlockerType state) const -{ +int TriangleSelector::num_facets(TriangleStateType state) const { int cnt = 0; for (const Triangle& tr : m_triangles) if (tr.valid() && ! tr.is_split() && tr.get_state() == state) @@ -1335,8 +1328,7 @@ int TriangleSelector::num_facets(EnforcerBlockerType state) const return cnt; } -indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const -{ +indexed_triangle_set TriangleSelector::get_facets(TriangleStateType state) const { indexed_triangle_set out; std::vector vertex_map(m_vertices.size(), -1); for (const Triangle& tr : m_triangles) { @@ -1356,8 +1348,7 @@ indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) con return out; } -indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType state) const -{ +indexed_triangle_set TriangleSelector::get_facets_strict(TriangleStateType state) const { indexed_triangle_set out; size_t num_vertices = 0; @@ -1385,7 +1376,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta void TriangleSelector::get_facets_strict_recursive( const Triangle &tr, const Vec3i &neighbors, - EnforcerBlockerType state, + TriangleStateType state, std::vector &out_triangles) const { if (tr.is_split()) { @@ -1529,8 +1520,8 @@ void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, cons 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 - // leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3) + // leaf triangle: xx = TriangleStateType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0 + // leaf triangle: xx = 0b11, yy = 0b00, zzzz = TriangleStateType (subtracted by 3) // non-leaf: xx = special side, yy = number of split sides // These are bitwise appended and formed into one 64-bit integer. @@ -1590,7 +1581,7 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const { out.data.triangles_to_split.reserve(m_orig_size_indices); for (int i=0; i> 2); + auto state = is_split ? TriangleStateType::NONE : TriangleStateType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2); // Only valid if is_split. int special_side = code >> 2; @@ -1655,7 +1646,7 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs Vec3i neighbors = m_neighbors[triangle_id]; parents.push_back({triangle_id, neighbors, 0, num_of_children}); m_triangles[triangle_id].set_division(num_of_split_sides, special_side); - perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE); + perform_split(triangle_id, neighbors, TriangleStateType::NONE); continue; } else { // root is not split. just set the state and that's it. @@ -1675,7 +1666,7 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs Vec3i neighbors = this->child_neighbors(tr, last.neighbors, child_idx); int this_idx = tr.children[child_idx]; m_triangles[this_idx].set_division(num_of_split_sides, special_side); - perform_split(this_idx, neighbors, EnforcerBlockerType::NONE); + perform_split(this_idx, neighbors, TriangleStateType::NONE); parents.push_back({this_idx, neighbors, 0, num_of_children}); } else { // this triangle belongs to last split one @@ -1704,7 +1695,7 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs } // 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) { +bool TriangleSelector::has_facets(const TriangleSplittingData &data, const TriangleStateType 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; @@ -1761,8 +1752,7 @@ void TriangleSelector::seed_fill_unselect_all_triangles() triangle.unselect_by_seed_fill(); } -void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_state) -{ +void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) { for (Triangle &triangle : m_triangles) if (!triangle.is_split() && triangle.is_selected_by_seed_fill()) triangle.set_state(new_state); diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 66f92fd37d..75fe12383b 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -14,8 +14,29 @@ namespace Slic3r { -enum class EnforcerBlockerType : int8_t; - +enum class TriangleStateType : 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, + Count +}; // Following class holds information about selected triangles. It also has power // to recursively subdivide the triangles and make the selection finer. @@ -206,7 +227,7 @@ public: // 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; TriangleSplittingData() = default; @@ -236,7 +257,7 @@ public: // Select all triangles fully inside the circle, subdivide where needed. void select_patch(int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to std::unique_ptr &&cursor, // Cursor containing information about the point where to start, camera position (mesh coords), matrix to get from mesh to world, and its shape and type. - EnforcerBlockerType new_state, // enforcer or blocker? + TriangleStateType new_state, // enforcer or blocker? const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation bool triangle_splitting, // If triangles will be split base on the cursor or not float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. @@ -255,18 +276,18 @@ public: bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. 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 TriangleSplittingData &data, EnforcerBlockerType test_state); - int num_facets(EnforcerBlockerType state) const; + bool has_facets(TriangleStateType state) const; + static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state); + int num_facets(TriangleStateType state) const; // Get facets at a given state. Don't triangulate T-joints. - indexed_triangle_set get_facets(EnforcerBlockerType state) const; + indexed_triangle_set get_facets(TriangleStateType state) const; // Get facets at a given state. Triangulate T-joints. - indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const; + indexed_triangle_set get_facets_strict(TriangleStateType state) const; // Get edges around the selected area by seed fill. std::vector get_seed_fill_contour() const; // Set facet of the mesh to a given state. Only works for original triangles. - void set_facet(int facet_idx, EnforcerBlockerType state); + void set_facet(int facet_idx, TriangleStateType state); // Clear everything and make the tree empty. void reset(); @@ -284,9 +305,9 @@ public: // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); - // For all triangles selected by seed fill, set new EnforcerBlockerType and remove flag indicating that triangle was selected by seed fill. + // For all triangles selected by seed fill, set new TriangleStateType and remove flag indicating that triangle was selected by seed fill. // The operation may merge split triangles if they are being assigned the same color. - void seed_fill_apply_on_triangles(EnforcerBlockerType new_state); + void seed_fill_apply_on_triangles(TriangleStateType new_state); protected: // Triangle and info about how it's split. @@ -294,7 +315,7 @@ protected: public: // Use TriangleSelector::push_triangle to create a new triangle. // It increments/decrements reference counter on vertices. - Triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType init_state) + Triangle(int a, int b, int c, int source_triangle, const TriangleStateType init_state) : verts_idxs{a, b, c}, source_triangle{source_triangle}, state{init_state} @@ -316,8 +337,8 @@ protected: void set_division(int sides_to_split, int special_side_idx); // Get/set current state. - void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; } - EnforcerBlockerType get_state() const { assert(! is_split()); return state; } + void set_state(TriangleStateType type) { assert(! is_split()); state = type; } + TriangleStateType get_state() const { assert(! is_split()); return state; } // Set if the triangle has been selected or unselected by seed fill. void select_by_seed_fill() { assert(! is_split()); m_selected_by_seed_fill = true; } @@ -341,7 +362,7 @@ protected: // or index of a vertex shared by the two split edges (for number_of_splits == 2). // For number_of_splits == 3, special_side_idx is always zero. char special_side_idx { 0 }; - EnforcerBlockerType state; + TriangleStateType state; bool m_selected_by_seed_fill : 1; // Is this triangle valid or marked to be removed? bool m_valid : 1; @@ -379,14 +400,14 @@ protected: // Private functions: private: - bool select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting); - bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting); + bool select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting); + bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting); void undivide_triangle(int facet_idx); void split_triangle(int facet_idx, const Vec3i &neighbors); void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const; - int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0}); - void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state); + int push_triangle(int a, int b, int c, int source_triangle, TriangleStateType state = TriangleStateType::NONE); + void perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state); Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const; Vec3i child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors_propagated, int child_idx, const Vec3i &child_neighbors) const; // Return child of itriangle at a CCW oriented side (vertexi, vertexj), either first or 2nd part. @@ -415,7 +436,7 @@ private: void get_facets_strict_recursive( const Triangle &tr, const Vec3i &neighbors, - EnforcerBlockerType state, + TriangleStateType state, std::vector &out_triangles) const; void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector &out_triangles) const; diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index 226d1f6b47..517170ed60 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -29,7 +29,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) { std::unique_ptr cursor = std::make_unique( pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); - selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, trafo_no_translate, + selector.select_patch(hit.id, std::move(cursor), TriangleStateType::ENFORCER, trafo_no_translate, true, eps_angle); break; } @@ -42,7 +42,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig if (dist < radius) { std::unique_ptr cursor = std::make_unique( point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); - selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, + selector.select_patch(hit_idx_out, std::move(cursor), TriangleStateType::ENFORCER, trafo_no_translate, true, eps_angle); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 4bbdf6d5d9..4f3b472e21 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -374,7 +374,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) const indexed_triangle_set &its = mv->mesh().its; for (const stl_triangle_vertex_indices &face : its.indices) { if (its_face_normal(its, face).dot(down) > dot_limit) { - m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER); + m_triangle_selectors[mesh_id]->set_facet(idx, block ? TriangleStateType::BLOCKER : TriangleStateType::ENFORCER); m_triangle_selectors.back()->request_update_render_data(); } ++ idx; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 0f343e586c..ad4931058d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -110,8 +110,8 @@ protected: ColorRGBA get_cursor_sphere_left_button_color() const override; ColorRGBA get_cursor_sphere_right_button_color() const override; - EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType(m_first_selected_extruder_idx + 1); } - EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType(m_second_selected_extruder_idx + 1); } + TriangleStateType get_left_button_state_type() const override { return TriangleStateType(m_first_selected_extruder_idx + 1); } + TriangleStateType get_right_button_state_type() const override { return TriangleStateType(m_second_selected_extruder_idx + 1); } void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 29f0b134b6..8d0fad0883 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -503,7 +503,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_triangle_selectors.empty()) return false; - EnforcerBlockerType new_state = EnforcerBlockerType::NONE; + TriangleStateType new_state = TriangleStateType::NONE; if (! shift_down) { if (action == SLAGizmoEventType::Dragging) new_state = m_button_down == Button::Left ? this->get_left_button_state_type() : this->get_right_button_state_type(); @@ -937,16 +937,16 @@ void TriangleSelectorGUI::update_render_data() static const float offset = 0.001f; for (const Triangle &tr : m_triangles) { - if (!tr.valid() || tr.is_split() || (tr.get_state() == EnforcerBlockerType::NONE && !tr.is_selected_by_seed_fill())) + if (!tr.valid() || tr.is_split() || (tr.get_state() == TriangleStateType::NONE && !tr.is_selected_by_seed_fill())) continue; int tr_state = int(tr.get_state()); - GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] : - tr.get_state() == EnforcerBlockerType::ENFORCER ? iva_enforcers_data : - iva_blockers_data; - int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] : - tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : - blc_cnt; + GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] : + tr.get_state() == TriangleStateType::ENFORCER ? iva_enforcers_data : + iva_blockers_data; + int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] : + tr.get_state() == TriangleStateType::ENFORCER ? enf_cnt : + blc_cnt; const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v; const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v; const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index db974205fb..bd185fce0e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -120,8 +120,8 @@ protected: virtual ColorRGBA get_cursor_sphere_left_button_color() const { return { 0.0f, 0.0f, 1.0f, 0.25f }; } virtual ColorRGBA get_cursor_sphere_right_button_color() const { return { 1.0f, 0.0f, 0.0f, 0.25f }; } - virtual EnforcerBlockerType get_left_button_state_type() const { return EnforcerBlockerType::ENFORCER; } - virtual EnforcerBlockerType get_right_button_state_type() const { return EnforcerBlockerType::BLOCKER; } + virtual TriangleStateType get_left_button_state_type() const { return TriangleStateType::ENFORCER; } + virtual TriangleStateType get_right_button_state_type() const { return TriangleStateType::BLOCKER; } float m_cursor_radius = 2.f; static constexpr float CursorRadiusMin = 0.4f; // cannot be zero From e14ba9d43102b566b811febbf7a28920704e9093 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/5] 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 | 17 +++++++++++--- src/libslic3r/TriangleSelector.cpp | 36 ++++++++++++++++++++++++++++++ src/libslic3r/TriangleSelector.hpp | 23 +++++++++++++++++-- 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 22f3a4b132..d6b83c283b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2095,6 +2095,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; @@ -2106,9 +2107,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); } // Test whether the two models contain the same number of ModelObjects with the same set of IDs diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 67fb7ac517..214842d3e5 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -664,9 +664,9 @@ enum class ConversionType : 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, TriangleStateType type) const; indexed_triangle_set get_facets_strict(const ModelVolume& mv, TriangleStateType type) const; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 1ac2c72304..03e1dff23f 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1375,9 +1375,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ if (const auto &volumes = print_object.model_object()->volumes; num_extruders > 1 && std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mm_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(TriangleStateType::Count)> used_facet_states{}; + for (const ModelVolume *volume : volumes) { + const std::vector &volume_used_facet_states = volume->mm_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(TriangleStateType::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 02070dfe70..c8c03fcfbe 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1560,6 +1560,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(TriangleStateType::Count)) + data.used_states[n] = true; + if (n >= 3) { assert(n <= 16); if (n <= 16) { @@ -1694,6 +1697,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 TriangleStateType test_state) { // Depth-first queue of a number of unvisited children. diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 75fe12383b..0e88fe3a7c 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -228,15 +228,31 @@ public: std::vector triangles_to_split; // Bit stream containing splitting information. std::vector bitstream; + // Array indicating which triangle state types are used (encoded inside bitstream). + std::vector used_states { std::vector(static_cast(TriangleStateType::Count), 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(TriangleStateType::Count), 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; @@ -302,6 +318,9 @@ public: // Load serialized data. Assumes that correct mesh is loaded. void deserialize(const TriangleSplittingData &data, bool needs_reset = true); + // 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 4b8fe98443b2d556c13653d3277b11e9b02c40a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 24 Apr 2024 13:15:55 +0200 Subject: [PATCH 4/5] SPE-2186: Add shrinkage compensation into filament settings. There is a limitation that on multi-tool printers, all used filaments must have the same shrinkage compression values. --- src/libslic3r/Geometry.cpp | 15 +++++++++ src/libslic3r/Geometry.hpp | 2 ++ src/libslic3r/Model.cpp | 16 ++++++++++ src/libslic3r/Model.hpp | 3 ++ src/libslic3r/Preset.cpp | 4 ++- src/libslic3r/Print.cpp | 60 +++++++++++++++++++++++++++++++---- src/libslic3r/Print.hpp | 8 ++++- src/libslic3r/PrintApply.cpp | 7 ++-- src/libslic3r/PrintConfig.cpp | 22 +++++++++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/libslic3r/PrintObject.cpp | 20 ++++++------ src/libslic3r/Slicing.cpp | 36 ++++++++++++--------- src/libslic3r/Slicing.hpp | 20 +++++++++--- src/slic3r/GUI/GLCanvas3D.cpp | 19 +++++------ src/slic3r/GUI/GLCanvas3D.hpp | 4 +++ src/slic3r/GUI/Plater.cpp | 4 +++ src/slic3r/GUI/Tab.cpp | 4 +++ 17 files changed, 196 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index fa984dcfbb..810d7ef4b9 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -637,6 +637,21 @@ Transform3d Transformation::get_matrix_no_scaling_factor() const return copy.get_matrix(); } +Transform3d Transformation::get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const { + const Transform3d shrinkage_trafo = Geometry::scale_transform(shrinkage_compensation); + const Vec3d trafo_offset = this->get_offset(); + const Vec3d trafo_offset_xy = Vec3d(trafo_offset.x(), trafo_offset.y(), 0.); + + Transformation copy(*this); + copy.set_offset(Axis::X, 0.); + copy.set_offset(Axis::Y, 0.); + + Transform3d trafo_after_shrinkage = (shrinkage_trafo * copy.get_matrix()); + trafo_after_shrinkage.translation() += trafo_offset_xy; + + return trafo_after_shrinkage; +} + Transformation Transformation::operator * (const Transformation& other) const { return Transformation(get_matrix() * other.get_matrix()); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c5d81bda54..3afe6923da 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -460,6 +460,8 @@ public: Transform3d get_matrix_no_offset() const; Transform3d get_matrix_no_scaling_factor() const; + Transform3d get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const; + void set_matrix(const Transform3d& transform) { m_matrix = transform; } Transformation operator * (const Transformation& other) const; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d6b83c283b..2de86a5283 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1998,6 +1998,22 @@ void ModelVolume::convert_from_meters() this->source.is_converted_from_meters = true; } +std::vector ModelVolume::get_extruders_from_multi_material_painting() const { + if (!this->is_mm_painted()) + return {}; + + assert(static_cast(TriangleStateType::Extruder1) - 1 == 0); + const TriangleSelector::TriangleSplittingData &data = this->mm_segmentation_facets.get_data(); + + std::vector extruders; + for (size_t state_idx = static_cast(TriangleStateType::Extruder1); state_idx < data.used_states.size(); ++state_idx) { + if (data.used_states[state_idx]) + extruders.emplace_back(state_idx - 1); + } + + return extruders; +} + void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(dont_translate ? get_matrix_no_offset() : get_matrix()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 214842d3e5..7ef98db151 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -910,6 +910,9 @@ public: bool is_seam_painted() const { return !this->seam_facets.empty(); } bool is_mm_painted() const { return !this->mm_segmentation_facets.empty(); } + // Returns 0-based indices of extruders painted by multi-material painting gizmo. + std::vector get_extruders_from_multi_material_painting() const; + protected: friend class Print; friend class SLAPrint; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 046334058e..6e34c514d6 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -490,7 +490,9 @@ static std::vector s_Preset_filament_options { "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", "filament_retract_length_toolchange", "filament_retract_restart_extra_toolchange", "filament_travel_ramping_lift", "filament_travel_slope", "filament_travel_max_lift", "filament_travel_lift_before_obstacle", // Profile compatibility - "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" + "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits", + // Shrinkage compensation + "filament_shrinkage_compensation_xy", "filament_shrinkage_compensation_z", }; static std::vector s_Preset_machine_limits_options { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 116fc88d3b..ddf38250af 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -209,7 +209,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n // Spiral Vase forces different kind of slicing than the normal model: // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer. // Therefore toggling the Spiral Vase on / off requires complete reslicing. - || opt_key == "spiral_vase") { + || opt_key == "spiral_vase" + || opt_key == "filament_shrinkage_compensation_xy" + || opt_key == "filament_shrinkage_compensation_z") { osteps.emplace_back(posSlice); } else if ( opt_key == "complete_objects" @@ -525,6 +527,9 @@ std::string Print::validate(std::vector* warnings) const goto DONE; } DONE:; + + if (!this->has_same_shrinkage_compensations()) + warnings->emplace_back("_FILAMENT_SHRINKAGE_DIFFER"); } if (m_objects.empty()) @@ -584,12 +589,19 @@ std::string Print::validate(std::vector* warnings) const //FIXME It is quite expensive to generate object layers just to get the print height! if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx)); ! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) { - return - // Test whether the last slicing plane is below or above the print volume. - 0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON ? - format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) : - format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) + - " " + _u8L("You might want to reduce the size of your model or change current print settings and retry."); + + const double shrinkage_compensation_z = this->shrinkage_compensation().z(); + if (shrinkage_compensation_z != 1. && layers.back() > (this->config().max_print_height / shrinkage_compensation_z + EPSILON)) { + // The object exceeds the maximum build volume height because of shrinkage compensation. + return format(_u8L("While the object %1% itself fits the build volume, it exceeds the maximum build volume height because of material shrinkage compensation."), print_object.model_object()->name); + } else if (0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON) { + // The last slicing plane is below the print volume. + return format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name); + } else { + // The last slicing plane is above the print volume. + return format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) + + " " + _u8L("You might want to reduce the size of your model or change current print settings and retry."); + } } } @@ -1618,6 +1630,40 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(output_filename_format, ".gcode", filename_base, &config); } +// Returns if all used filaments have same shrinkage compensations. +bool Print::has_same_shrinkage_compensations() const { + const std::vector extruders = this->extruders(); + if (extruders.empty()) + return false; + + const double filament_shrinkage_compensation_xy = m_config.filament_shrinkage_compensation_xy.get_at(extruders.front()); + const double filament_shrinkage_compensation_z = m_config.filament_shrinkage_compensation_z.get_at(extruders.front()); + + for (unsigned int extruder : extruders) { + if (filament_shrinkage_compensation_xy != m_config.filament_shrinkage_compensation_xy.get_at(extruder) || + filament_shrinkage_compensation_z != m_config.filament_shrinkage_compensation_z.get_at(extruder)) { + return false; + } + } + + return true; +} + +// Returns scaling for each axis representing shrinkage compensations in each axis. +Vec3d Print::shrinkage_compensation() const +{ + if (!this->has_same_shrinkage_compensations()) + return Vec3d::Ones(); + + const unsigned int first_extruder = this->extruders().front(); + const double xy_compensation_percent = std::clamp(m_config.filament_shrinkage_compensation_xy.get_at(first_extruder), -99., 99.); + const double z_compensation_percent = std::clamp(m_config.filament_shrinkage_compensation_z.get_at(first_extruder), -99., 99.); + const double xy_compensation = 100. / (100. - xy_compensation_percent); + const double z_compensation = 100. / (100. - z_compensation_percent); + + return { xy_compensation, xy_compensation, z_compensation }; +} + const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] ="; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1acc6d0b7c..0da013da73 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -327,7 +327,7 @@ public: // The slicing parameters are dependent on various configuration values // (layer height, first layer height, raft settings, print nozzle diameter etc). const SlicingParameters& slicing_parameters() const { return m_slicing_params; } - static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); + static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation); size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); } const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); } @@ -665,6 +665,12 @@ public: const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; } static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); + // Returns if all used filaments have same shrinkage compensations. + bool has_same_shrinkage_compensations() const; + + // Returns scaling for each axis representing shrinkage compensations in each axis. + Vec3d shrinkage_compensation() const; + protected: // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 03e1dff23f..57c20a6b4d 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -134,13 +134,14 @@ struct PrintObjectTrafoAndInstances }; // Generate a list of trafos and XY offsets for instances of a ModelObject -static std::vector print_objects_from_model_object(const ModelObject &model_object) +static std::vector print_objects_from_model_object(const ModelObject &model_object, const Vec3d &shrinkage_compensation) { std::set trafos; PrintObjectTrafoAndInstances trafo; for (ModelInstance *model_instance : model_object.instances) if (model_instance->is_printable()) { - trafo.trafo = model_instance->get_matrix(); + Geometry::Transformation model_instance_transformation = model_instance->get_transformation(); + trafo.trafo = model_instance_transformation.get_matrix_with_applied_shrinkage_compensation(shrinkage_compensation); auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]); // Reset the XY axes of the transformation. trafo.trafo.data()[12] = 0; @@ -1281,7 +1282,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Walk over all new model objects and check, whether there are matching PrintObjects. for (ModelObject *model_object : m_model.objects) { ModelObjectStatus &model_object_status = const_cast(model_object_status_db.reuse(*model_object)); - model_object_status.print_instances = print_objects_from_model_object(*model_object); + model_object_status.print_instances = print_objects_from_model_object(*model_object, this->shrinkage_compensation()); std::vector old; old.reserve(print_object_status_db.count(*model_object)); for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object)) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 632a48fdcc..b14ff3e11b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1332,6 +1332,28 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionString(L("(Unknown)"))); def->cli = ConfigOptionDef::nocli; + def = this->add("filament_shrinkage_compensation_xy", coPercents); + def->label = L("Shrinkage XY"); + def->tooltip = L("Enter your filament shrinkage percentages for the X and Y axes here to apply scaling of the object to " + "compensate for shrinkage in the X and Y axes. For example, if you measured 99mm instead of 100mm, " + "then you should put here 1%."); + def->sidetext = L("%"); + def->mode = comAdvanced; + def->min = -10.; + def->max = 10.; + def->set_default_value(new ConfigOptionPercents { 0 }); + + def = this->add("filament_shrinkage_compensation_z", coPercents); + def->label = L("Shrinkage Z"); + def->tooltip = L("Enter your filament shrinkage percentages for the Z axis here to apply scaling of the object to " + "compensate for shrinkage in the Z axis. For example, if you measured 99mm instead of 100mm, " + "then you should put here 1%."); + def->sidetext = L("%"); + def->mode = comAdvanced; + def->min = -10.; + def->max = 10.; + def->set_default_value(new ConfigOptionPercents { 0. }); + def = this->add("fill_angle", coFloat); def->label = L("Fill angle"); def->category = L("Infill"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 72ba652e5f..e95f486776 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -745,6 +745,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, filament_multitool_ramming_flow)) ((ConfigOptionFloats, filament_stamping_loading_speed)) ((ConfigOptionFloats, filament_stamping_distance)) + ((ConfigOptionPercents, filament_shrinkage_compensation_xy)) + ((ConfigOptionPercents, filament_shrinkage_compensation_z)) ((ConfigOptionBool, gcode_comments)) ((ConfigOptionEnum, gcode_flavor)) ((ConfigOptionEnum, gcode_label_objects)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ac8d440846..d4b8736e49 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2620,15 +2620,14 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau return config; } -void PrintObject::update_slicing_parameters() -{ - if (!m_slicing_params.valid) - m_slicing_params = SlicingParameters::create_from_config( - this->print()->config(), m_config, this->model_object()->max_z(), this->object_extruders()); +void PrintObject::update_slicing_parameters() { + if (!m_slicing_params.valid) { + m_slicing_params = SlicingParameters::create_from_config(this->print()->config(), m_config, this->model_object()->max_z(), + this->object_extruders(), this->print()->shrinkage_compensation()); + } } -SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) -{ +SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation) { PrintConfig print_config; PrintObjectConfig object_config; PrintRegionConfig default_region_config; @@ -2661,7 +2660,8 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full if (object_max_z <= 0.f) object_max_z = (float)model_object.raw_bounding_box().size().z(); - return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders); + + return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders, object_shrinkage_compensation); } // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) @@ -2682,7 +2682,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c if (layer_height_profile.empty()) { // use the constructor because the assignement is crashing on ASAN OsX layer_height_profile = model_object.layer_height_profile.get(); -// layer_height_profile = model_object.layer_height_profile; // The layer height returned is sampled with high density for the UI layer height painting // and smoothing tool to work. updated = true; @@ -2693,8 +2692,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Must not be of even length. ((layer_height_profile.size() & 1) != 0 || // Last entry must be at the top of the object. - std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max + slicing_parameters.object_print_z_min) > 1e-3)) + std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_uncompensated_max + slicing_parameters.object_print_z_min) > 1e-3)) { layer_height_profile.clear(); + } if (layer_height_profile.empty()) { //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index a2414d6998..091c46dc54 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -63,10 +63,11 @@ coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_c } SlicingParameters SlicingParameters::create_from_config( - const PrintConfig &print_config, - const PrintObjectConfig &object_config, - coordf_t object_height, - const std::vector &object_extruders) + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::vector &object_extruders, + const Vec3d &object_shrinkage_compensation) { assert(! print_config.first_layer_height.percent); coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ? @@ -85,7 +86,9 @@ SlicingParameters SlicingParameters::create_from_config( params.first_print_layer_height = first_layer_height; params.first_object_layer_height = first_layer_height; params.object_print_z_min = 0.; - params.object_print_z_max = object_height; + params.object_print_z_max = object_height * object_shrinkage_compensation.z(); + params.object_print_z_uncompensated_max = object_height; + params.object_shrinkage_compensation_z = object_shrinkage_compensation.z(); params.base_raft_layers = object_config.raft_layers.value; params.soluble_interface = soluble_interface; @@ -152,6 +155,7 @@ SlicingParameters SlicingParameters::create_from_config( coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object; params.object_print_z_min = print_z; params.object_print_z_max += print_z; + params.object_print_z_uncompensated_max += print_z; } params.valid = true; @@ -224,10 +228,10 @@ std::vector layer_height_profile_from_ranges( lh_append(hi, height); } - if (coordf_t z = last_z(); z < slicing_params.object_print_z_height()) { + if (coordf_t z = last_z(); z < slicing_params.object_print_z_uncompensated_height()) { // Insert a step of normal layer height up to the object top. lh_append(z, slicing_params.layer_height); - lh_append(slicing_params.object_print_z_height(), slicing_params.layer_height); + lh_append(slicing_params.object_print_z_uncompensated_height(), slicing_params.layer_height); } return layer_height_profile; @@ -418,12 +422,12 @@ void adjust_layer_height_profile( std::pair z_span_variable = std::pair( slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0., - slicing_params.object_print_z_height()); + slicing_params.object_print_z_uncompensated_height()); if (z < z_span_variable.first || z > z_span_variable.second) return; assert(layer_height_profile.size() >= 2); - assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON); + assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON); // 1) Get the current layer thickness at z. coordf_t current_layer_height = slicing_params.layer_height; @@ -584,7 +588,7 @@ void adjust_layer_height_profile( assert(layer_height_profile.size() > 2); assert(layer_height_profile.size() % 2 == 0); assert(layer_height_profile[0] == 0.); - assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON); + assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON); #ifdef _DEBUG for (size_t i = 2; i < layer_height_profile.size(); i += 2) assert(layer_height_profile[i - 2] <= layer_height_profile[i]); @@ -613,6 +617,7 @@ std::vector generate_object_layers( out.push_back(print_z); } + const coordf_t shrinkage_compensation_z = slicing_params.object_shrinkage_compensation_z; size_t idx_layer_height_profile = 0; // loop until we have at least one layer and the max slice_z reaches the object height coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height; @@ -621,17 +626,18 @@ std::vector generate_object_layers( if (idx_layer_height_profile < layer_height_profile.size()) { size_t next = idx_layer_height_profile + 2; for (;;) { - if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next]) + if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next] * shrinkage_compensation_z) break; idx_layer_height_profile = next; next += 2; } - coordf_t z1 = layer_height_profile[idx_layer_height_profile]; - coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1]; + + const coordf_t z1 = layer_height_profile[idx_layer_height_profile] * shrinkage_compensation_z; + const coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1]; height = h1; if (next < layer_height_profile.size()) { - coordf_t z2 = layer_height_profile[next]; - coordf_t h2 = layer_height_profile[next + 1]; + const coordf_t z2 = layer_height_profile[next] * shrinkage_compensation_z; + const coordf_t h2 = layer_height_profile[next + 1]; height = lerp(h1, h2, (slice_z - z1) / (z2 - z1)); assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON); } diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 7c2b93e21c..9e4b0499cc 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -33,10 +33,11 @@ struct SlicingParameters SlicingParameters() = default; static SlicingParameters create_from_config( - const PrintConfig &print_config, - const PrintObjectConfig &object_config, - coordf_t object_height, - const std::vector &object_extruders); + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::vector &object_extruders, + const Vec3d &object_shrinkage_compensation); // Has any raft layers? bool has_raft() const { return raft_layers() > 0; } @@ -46,8 +47,13 @@ struct SlicingParameters bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; } // Height of the object to be printed. This value does not contain the raft height. + // This value is scaled by shrinkage compensation in the Z-axis. coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; } + // Height of the object to be printed. This value does not contain the raft height. + // This value isn't scaled by shrinkage compensation in the Z-axis. + coordf_t object_print_z_uncompensated_height() const { return object_print_z_uncompensated_max - object_print_z_min; } + bool valid { false }; // Number of raft layers. @@ -99,7 +105,13 @@ struct SlicingParameters coordf_t raft_contact_top_z { 0 }; // In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer. coordf_t object_print_z_min { 0 }; + // This value of maximum print Z is scaled by shrinkage compensation in the Z-axis. coordf_t object_print_z_max { 0 }; + + // This value of maximum print Z isn't scaled by shrinkage compensation. + coordf_t object_print_z_uncompensated_max { 0 }; + // Scaling factor for compensating shrinkage in Z-axis. + coordf_t object_shrinkage_compensation_z { 0 }; }; static_assert(IsTriviallyCopyable::value, "SlicingParameters class is not POD (and it should be - see constructor)."); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5e19f3fe27..65090b6676 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -628,11 +628,10 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) m_layer_height_profile_modified = false; } -void GLCanvas3D::LayersEditing::update_slicing_parameters() -{ - if (m_slicing_parameters == nullptr) { - m_slicing_parameters = new SlicingParameters(); - *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); +void GLCanvas3D::LayersEditing::update_slicing_parameters() { + if (m_slicing_parameters == nullptr) { + m_slicing_parameters = new SlicingParameters(); + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z, m_shrinkage_compensation); } } @@ -1605,9 +1604,11 @@ void GLCanvas3D::set_config(const DynamicPrintConfig* config) m_config = config; m_layers_editing.set_config(config); - if (config) { - PrinterTechnology ptech = current_printer_technology(); + const PrinterTechnology ptech = current_printer_technology(); + if (const Print *print = fff_print(); ptech == ptFFF && print != nullptr) + m_layers_editing.set_shrinkage_compensation(fff_print()->shrinkage_compensation()); + if (config) { auto slot = ArrangeSettingsDb_AppCfg::slotFFF; if (ptech == ptSLA) { @@ -3200,9 +3201,9 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) } } - // If Undo/Redo list is opened, + // If Undo/Redo list is opened, // update them according to the event - if (m_undoredo_toolbar.is_item_pressed("undo") || + if (m_undoredo_toolbar.is_item_pressed("undo") || m_undoredo_toolbar.is_item_pressed("redo")) { m_mouse_wheel = int((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); return; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 09ff631ed5..3975b02b08 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -219,6 +219,8 @@ class GLCanvas3D SlicingParameters *m_slicing_parameters{ nullptr }; std::vector m_layer_height_profile; bool m_layer_height_profile_modified{ false }; + // Shrinkage compensation to apply when we need to use object_max_z with Z compensation. + Vec3d m_shrinkage_compensation{ Vec3d::Ones() }; mutable float m_adaptive_quality{ 0.5f }; mutable HeightProfileSmoothingParams m_smooth_params; @@ -299,6 +301,8 @@ class GLCanvas3D std::pair> get_layers_height_data(); + void set_shrinkage_compensation(const Vec3d &shrinkage_compensation) { m_shrinkage_compensation = shrinkage_compensation; }; + private: bool is_initialized() const; void generate_layer_height_texture(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 62e0c5f6b6..53ae3677a0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1938,6 +1938,10 @@ void Plater::priv::process_validation_warning(const std::vector& wa if (text == "_BED_TEMPS_DIFFER") text = _u8L("Bed temperatures for the used filaments differ significantly."); + if (text == "_FILAMENT_SHRINKAGE_DIFFER") + text = _u8L("Filament shrinkage will not be used because filament shrinkage " + "for the used filaments differs significantly."); + notification_manager->push_notification( NotificationType::ValidateWarning, NotificationManager::NotificationLevel::WarningNotificationLevel, diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fdf0d3a339..017de90fcd 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2237,6 +2237,10 @@ void TabFilament::build() }; optgroup->append_line(line); + optgroup = page->new_optgroup(L("Shrinkage compensation")); + optgroup->append_single_option_line("filament_shrinkage_compensation_xy"); + optgroup->append_single_option_line("filament_shrinkage_compensation_z"); + optgroup = page->new_optgroup(L("Wipe tower parameters")); optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); From 61cd0beaa9fc9d5926b07829e38e14ff1624dec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 14 May 2024 14:58:12 +0200 Subject: [PATCH 5/5] SPE-2186: Allow to show at the same time both warnings about that bed temperature difference and shrinkage compensation weren't used. --- src/slic3r/GUI/NotificationManager.hpp | 4 ++++ src/slic3r/GUI/Plater.cpp | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 05dd363dbc..f3a2aa08fe 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -136,6 +136,10 @@ enum class NotificationType SelectFilamentFromConnect, // Debug notification for connect communication PrusaConnectPrinters, + // Notification that bed temperatures for the used filaments differ significantly. + BedTemperaturesDiffer, + // Notification that shrinkage compensations for the used filaments differ. + ShrinkageCompensationsDiffer, }; class NotificationManager diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 53ae3677a0..b48edca87c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1916,9 +1916,14 @@ void Plater::priv::process_validation_warning(const std::vector& wa if (warnings.empty()) notification_manager->close_notification_of_type(NotificationType::ValidateWarning); - for (std::string text : warnings) { - std::string hypertext = ""; - std::function action_fn = [](wxEvtHandler*){ return false; }; + // Always close warnings BedTemperaturesDiffer and ShrinkageCompensationsDiffer before next processing. + notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer); + notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer); + + for (std::string text : warnings) { + std::string hypertext = ""; + NotificationType notification_type = NotificationType::ValidateWarning; + std::function action_fn = [](wxEvtHandler*){ return false; }; if (text == "_SUPPORTS_OFF") { text = _u8L("An object has custom support enforcers which will not be used " @@ -1934,16 +1939,17 @@ void Plater::priv::process_validation_warning(const std::vector& wa print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto")); return true; }; + } else if (text == "_BED_TEMPS_DIFFER") { + text = _u8L("Bed temperatures for the used filaments differ significantly."); + notification_type = NotificationType::BedTemperaturesDiffer; + } else if (text == "_FILAMENT_SHRINKAGE_DIFFER") { + text = _u8L("Filament shrinkage will not be used because filament shrinkage " + "for the used filaments differs significantly."); + notification_type = NotificationType::ShrinkageCompensationsDiffer; } - if (text == "_BED_TEMPS_DIFFER") - text = _u8L("Bed temperatures for the used filaments differ significantly."); - - if (text == "_FILAMENT_SHRINKAGE_DIFFER") - text = _u8L("Filament shrinkage will not be used because filament shrinkage " - "for the used filaments differs significantly."); notification_manager->push_notification( - NotificationType::ValidateWarning, + notification_type, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("WARNING:") + "\n" + text, hypertext, action_fn );