Feature/misc fixes of toolchanger (#6396)

* Use more readable data types for storing triangle splitting information.

* fix build errors

* 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.

* Fixed an bug that filament list was not updated properly

---------

Co-authored-by: Lukáš Hejl <hejl.lukas@gmail.com>
This commit is contained in:
SoftFever 2024-08-09 21:11:17 +08:00 committed by GitHub
parent a4cfc14a7e
commit b8a9c22404
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 198 additions and 88 deletions

View file

@ -3293,7 +3293,7 @@ bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType typ
bool FacetsAnnotation::set(const TriangleSelector& selector) bool FacetsAnnotation::set(const TriangleSelector& selector)
{ {
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> sel_map = selector.serialize(); TriangleSelector::TriangleSplittingData sel_map = selector.serialize();
if (sel_map != m_data) { if (sel_map != m_data) {
m_data = std::move(sel_map); m_data = std::move(sel_map);
this->touch(); this->touch();
@ -3304,8 +3304,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)
void FacetsAnnotation::reset() void FacetsAnnotation::reset()
{ {
m_data.first.clear(); m_data.triangles_to_split.clear();
m_data.second.clear(); m_data.bitstream.clear();
this->touch(); this->touch();
} }
@ -3316,15 +3316,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
{ {
std::string out; std::string out;
auto triangle_it = std::lower_bound(m_data.first.begin(), m_data.first.end(), triangle_idx, [](const std::pair<int, int> &l, const int r) { return l.first < r; }); 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.first.end() && triangle_it->first == triangle_idx) { if (triangle_it != m_data.triangles_to_split.end() && triangle_it->triangle_idx == triangle_idx) {
int offset = triangle_it->second; int offset = triangle_it->bitstream_start_idx;
int end = ++ triangle_it == m_data.first.end() ? int(m_data.second.size()) : triangle_it->second; int end = ++ triangle_it == m_data.triangles_to_split.end() ? int(m_data.bitstream.size()) : triangle_it->bitstream_start_idx;
while (offset < end) { while (offset < end) {
int next_code = 0; int next_code = 0;
for (int i=3; i>=0; --i) { for (int i=3; i>=0; --i) {
next_code = next_code << 1; next_code = next_code << 1;
next_code |= int(m_data.second[offset + i]); next_code |= int(m_data.bitstream[offset + i]);
} }
offset += 4; offset += 4;
@ -3341,9 +3341,10 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str) void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
{ {
assert(! str.empty()); assert(! str.empty());
assert(m_data.first.empty() || m_data.first.back().first < triangle_id); assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id);
m_data.first.emplace_back(triangle_id, int(m_data.second.size())); 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) { for (auto it = str.crbegin(); it != str.crend(); ++it) {
const char ch = *it; const char ch = *it;
int dec = 0; int dec = 0;
@ -3355,14 +3356,16 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
assert(false); assert(false);
// Convert to binary and append into code. // Convert to binary and append into code.
for (int i=0; i<4; ++i) 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)));
} }
m_data.update_used_states(bitstream_start_idx);
} }
bool FacetsAnnotation::equals(const FacetsAnnotation &other) const bool FacetsAnnotation::equals(const FacetsAnnotation &other) const
{ {
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data = other.get_data(); const auto& data = other.get_data();
return (m_data == data); return (m_data == data);
} }

View file

@ -16,6 +16,7 @@
#include "enum_bitmask.hpp" #include "enum_bitmask.hpp"
#include "TextConfiguration.hpp" #include "TextConfiguration.hpp"
#include "EmbossShape.hpp" #include "EmbossShape.hpp"
#include "TriangleSelector.hpp"
//BBS: add bbs 3mf //BBS: add bbs 3mf
#include "Format/bbs_3mf.hpp" #include "Format/bbs_3mf.hpp"
@ -704,31 +705,6 @@ private:
void update_min_max_z(); 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 { enum class ConversionType : int {
CONV_TO_INCH, CONV_TO_INCH,
CONV_FROM_INCH, CONV_FROM_INCH,
@ -745,9 +721,9 @@ enum class En3mfType : int {
class FacetsAnnotation final : public ObjectWithTimestamp { class FacetsAnnotation final : public ObjectWithTimestamp {
public: public:
// Assign the content if the timestamp differs, don't assign an ObjectID. // 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(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); } } 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<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; } const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; }
bool set(const TriangleSelector& selector); bool set(const TriangleSelector& selector);
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
// BBS // BBS
@ -755,7 +731,7 @@ public:
void set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type); void set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type);
indexed_triangle_set get_facets_strict(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 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 // Following method clears the config and increases its timestamp, so the deleted
// state is considered changed from perspective of the undo/redo stack. // state is considered changed from perspective of the undo/redo stack.
@ -765,11 +741,11 @@ public:
std::string get_triangle_as_string(int i) const; std::string get_triangle_as_string(int i) const;
// Before deserialization, reserve space for n_triangles. // 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. // Deserialize triangles one by one, with strictly increasing triangle_id.
void set_triangle_from_string(int triangle_id, const std::string& str); void set_triangle_from_string(int triangle_id, const std::string& str);
// After deserializing the last triangle, shrink data to fit. // 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(); }
bool equals(const FacetsAnnotation &other) const; bool equals(const FacetsAnnotation &other) const;
private: private:
@ -796,7 +772,7 @@ private:
ar(cereal::base_class<ObjectWithTimestamp>(this), m_data); ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
} }
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> m_data; TriangleSelector::TriangleSplittingData m_data;
// To access set_new_unique_id() when copy / pasting a ModelVolume. // To access set_new_unique_id() when copy / pasting a ModelVolume.
friend class ModelVolume; friend class ModelVolume;

View file

@ -1498,11 +1498,22 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
} }
std::vector<unsigned int> painting_extruders; std::vector<unsigned int> painting_extruders;
if (const auto &volumes = print_object.model_object()->volumes; 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()) { 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::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax)> used_facet_states{};
std::iota(painting_extruders.begin(), painting_extruders.end(), 1); for (const ModelVolume *volume : volumes) {
const std::vector<bool> &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<size_t>(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) { 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. // Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.

View file

@ -1622,8 +1622,7 @@ void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, cons
} }
} }
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
{
// Each original triangle of the mesh is assigned a number encoding its state // 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): // 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 = 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<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
// (std::function calls using a pointer, while this implementation calls directly). // (std::function calls using a pointer, while this implementation calls directly).
struct Serializer { struct Serializer {
const TriangleSelector* triangle_selector; const TriangleSelector* triangle_selector;
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> data; TriangleSplittingData data;
void serialize(int facet_idx) { void serialize(int facet_idx) {
const Triangle& tr = triangle_selector->m_triangles[facet_idx]; const Triangle& tr = triangle_selector->m_triangles[facet_idx];
@ -1648,8 +1647,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
int split_sides = tr.number_of_split_sides(); int split_sides = tr.number_of_split_sides();
assert(split_sides >= 0 && split_sides <= 3); assert(split_sides >= 0 && split_sides <= 3);
data.second.push_back(split_sides & 0b01); data.bitstream.push_back(split_sides & 0b01);
data.second.push_back(split_sides & 0b10); data.bitstream.push_back(split_sides & 0b10);
if (split_sides) { if (split_sides) {
// If this triangle is split, save which side is split (in case // If this triangle is split, save which side is split (in case
@ -1657,8 +1656,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
// be ignored for 3-side split. // be ignored for 3-side split.
assert(tr.is_split() && split_sides > 0); assert(tr.is_split() && split_sides > 0);
assert(tr.special_side() >= 0 && tr.special_side() <= 3); assert(tr.special_side() >= 0 && tr.special_side() <= 3);
data.second.push_back(tr.special_side() & 0b01); data.bitstream.push_back(tr.special_side() & 0b01);
data.second.push_back(tr.special_side() & 0b10); data.bitstream.push_back(tr.special_side() & 0b10);
// Now save all children. // Now save all children.
// Serialized in reverse order for compatibility with PrusaSlicer 2.3.1. // Serialized in reverse order for compatibility with PrusaSlicer 2.3.1.
for (int child_idx = split_sides; child_idx >= 0; -- child_idx) for (int child_idx = split_sides; child_idx >= 0; -- child_idx)
@ -1666,45 +1665,48 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
} else { } else {
// In case this is leaf, we better save information about its state. // In case this is leaf, we better save information about its state.
int n = int(tr.get_state()); int n = int(tr.get_state());
if (n < static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
data.used_states[n] = true;
if (n >= 3) { if (n >= 3) {
assert(n <= 16); assert(n <= 16);
if (n <= 16) { if (n <= 16) {
// Store "11" plus 4 bits of (n-3). // 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; n -= 3;
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) 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 { } else {
// Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams. // Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams.
// Store 2 bits of n. // Store 2 bits of n.
data.second.push_back(n & 0b01); data.bitstream.push_back(n & 0b01);
data.second.push_back(n & 0b10); data.bitstream.push_back(n & 0b10);
} }
} }
} }
} out { this }; } 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<m_orig_size_indices; ++i) for (int i=0; i<m_orig_size_indices; ++i)
if (const Triangle& tr = m_triangles[i]; tr.is_split() || tr.get_state() != EnforcerBlockerType::NONE) { if (const Triangle& tr = m_triangles[i]; tr.is_split() || tr.get_state() != EnforcerBlockerType::NONE) {
// Store index of the first bit assigned to ith triangle. // Store index of the first bit assigned to ith triangle.
out.data.first.emplace_back(i, int(out.data.second.size())); out.data.triangles_to_split.emplace_back(i, int(out.data.bitstream.size()));
// out the triangle bits. // out the triangle bits.
out.serialize(i); out.serialize(i);
} }
// May be stored onto Undo / Redo stack, thus conserve memory. // May be stored onto Undo / Redo stack, thus conserve memory.
out.data.first.shrink_to_fit(); out.data.triangles_to_split.shrink_to_fit();
out.data.second.shrink_to_fit(); out.data.bitstream.shrink_to_fit();
return out.data; return out.data;
} }
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, bool needs_reset, EnforcerBlockerType max_ebt) void TriangleSelector::deserialize(const TriangleSplittingData& data, bool needs_reset, EnforcerBlockerType max_ebt)
{ {
if (needs_reset) if (needs_reset)
reset(); // dump any current state 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())) { if (triangle_id >= int(m_triangles.size())) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "array bound:error:triangle_id >= int(m_triangles.size())"; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "array bound:error:triangle_id >= int(m_triangles.size())";
return; return;
@ -1712,7 +1714,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
} }
// Reserve number of triangles as if each triangle was saved with 4 bits. // 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. // 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. // 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. // 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)); m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
@ -1728,13 +1730,13 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
// kept outside of the loop to avoid re-allocating inside the loop. // kept outside of the loop to avoid re-allocating inside the loop.
std::vector<ProcessingInfo> parents; std::vector<ProcessingInfo> 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(triangle_id < int(m_triangles.size()));
assert(ibit < int(data.second.size())); assert(ibit < int(data.bitstream.size()));
auto next_nibble = [&data, &ibit = ibit]() { auto next_nibble = [&data, &ibit = ibit]() {
int n = 0; int n = 0;
for (int i = 0; i < 4; ++ i) for (int i = 0; i < 4; ++ i)
n |= data.second[ibit ++] << i; n |= data.bitstream[ibit ++] << i;
return n; return n;
}; };
@ -1811,21 +1813,53 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
} }
} }
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. // Lightweight variant of deserialization, which only tests whether a face of test_state exists.
bool TriangleSelector::has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &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. // Depth-first queue of a number of unvisited children.
// Kept outside of the loop to avoid re-allocating inside the loop. // Kept outside of the loop to avoid re-allocating inside the loop.
std::vector<int> parents_children; std::vector<int> parents_children;
parents_children.reserve(64); parents_children.reserve(64);
for (const std::pair<int, int> &triangle_id_and_ibit : data.first) { for (const TriangleBitStreamMapping &triangle_id_and_ibit : data.triangles_to_split) {
int ibit = triangle_id_and_ibit.second; int ibit = triangle_id_and_ibit.bitstream_start_idx;
assert(ibit < int(data.second.size())); assert(ibit < int(data.bitstream.size()));
auto next_nibble = [&data, &ibit = ibit]() { auto next_nibble = [&data, &ibit = ibit]() {
int n = 0; int n = 0;
for (int i = 0; i < 4; ++ i) for (int i = 0; i < 4; ++ i)
n |= data.second[ibit ++] << i; n |= data.bitstream[ibit ++] << i;
return n; return n;
}; };
// < 0 -> negative of a number of children // < 0 -> negative of a number of children

View file

@ -7,10 +7,34 @@
#include <cfloat> #include <cfloat>
#include "Point.hpp" #include "Point.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r { 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 // Following class holds information about selected triangles. It also has power
// to recursively subdivide the triangles and make the selection finer. // to recursively subdivide the triangles and make the selection finer.
class TriangleSelector class TriangleSelector
@ -208,6 +232,56 @@ 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<class Archive> void serialize(Archive &ar) { ar(triangle_idx, bitstream_start_idx); }
};
struct TriangleSplittingData {
// Vector of triangles and its indexes to the bitstream.
std::vector<TriangleBitStreamMapping> triangles_to_split;
// Bit stream containing splitting information.
std::vector<bool> bitstream;
// Array indicating which triangle state types are used (encoded inside bitstream).
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(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
&& 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<size_t>(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<class Archive> void serialize(Archive &ar) { ar(triangles_to_split, bitstream, used_states); }
};
std::pair<std::vector<Vec3i32>, std::vector<Vec3i32>> precompute_all_neighbors() const; std::pair<std::vector<Vec3i32>, std::vector<Vec3i32>> precompute_all_neighbors() const;
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i32 &neighbors, const Vec3i32 &neighbors_propagated, std::vector<Vec3i32> &neighbors_out, std::vector<Vec3i32> &neighbors_normal_out) const; void precompute_all_neighbors_recursive(int facet_idx, const Vec3i32 &neighbors, const Vec3i32 &neighbors_propagated, std::vector<Vec3i32> &neighbors_out, std::vector<Vec3i32> &neighbors_normal_out) const;
@ -247,7 +321,7 @@ public:
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle 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; bool has_facets(EnforcerBlockerType state) const;
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state); static bool has_facets(const TriangleSplittingData &data, EnforcerBlockerType test_state);
int num_facets(EnforcerBlockerType state) const; int num_facets(EnforcerBlockerType state) const;
// Get facets at a given state. Don't triangulate T-joints. // Get facets at a given state. Don't triangulate T-joints.
indexed_triangle_set get_facets(EnforcerBlockerType state) const; indexed_triangle_set get_facets(EnforcerBlockerType state) const;
@ -270,10 +344,15 @@ public:
// Store the division trees in compact form (a long stream of bits for each triangle of the original mesh). // 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). // First vector contains pairs of (triangle index, first bit in the second vector).
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const; TriangleSplittingData serialize() const;
// Load serialized data. Assumes that correct mesh is loaded. // Load serialized data. Assumes that correct mesh is loaded.
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax); void deserialize(const TriangleSplittingData& data,
bool needs_reset = true,
EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax);
// Extract all used facet states from the given TriangleSplittingData.
static std::vector<EnforcerBlockerType> extract_used_facet_states(const TriangleSplittingData &data);
// For all triangles, remove the flag indicating that the triangle was selected by seed fill. // For all triangles, remove the flag indicating that the triangle was selected by seed fill.
void seed_fill_unselect_all_triangles(); void seed_fill_unselect_all_triangles();

View file

@ -1354,7 +1354,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
for (size_t i = 0; i < filament_cnt; i++) for (size_t i = 0; i < filament_cnt; i++)
p->combos_filament[i]->update(); p->combos_filament[i]->update();
dynamic_filament_list.update(); update_dynamic_filament_list();
break; break;
} }
@ -1637,7 +1637,7 @@ void Sidebar::on_filaments_change(size_t num_filaments)
Layout(); Layout();
p->m_panel_filament_title->Refresh(); p->m_panel_filament_title->Refresh();
update_ui_from_settings(); update_ui_from_settings();
dynamic_filament_list.update(); update_dynamic_filament_list();
} }
void Sidebar::add_filament() { void Sidebar::add_filament() {
@ -1818,7 +1818,7 @@ void Sidebar::sync_ams_list()
c->update(); c->update();
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]); wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]);
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
dynamic_filament_list.update(); update_dynamic_filament_list();
// Expand filament list // Expand filament list
p->m_panel_filament_content->SetMaxSize({-1, -1}); p->m_panel_filament_content->SetMaxSize({-1, -1});
// BBS:Synchronized consumables information // BBS:Synchronized consumables information
@ -1854,6 +1854,12 @@ void Sidebar::show_SEMM_buttons(bool bshow)
Layout(); Layout();
} }
void Sidebar::update_dynamic_filament_list()
{
dynamic_filament_list.update();
dynamic_filament_list_1_based.update();
}
ObjectList* Sidebar::obj_list() ObjectList* Sidebar::obj_list()
{ {
// BBS // BBS
@ -6535,7 +6541,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); wxGetApp().preset_bundle->set_filament_preset(idx, preset_name);
wxGetApp().plater()->update_project_dirty_from_presets(); wxGetApp().plater()->update_project_dirty_from_presets();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); 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); bool flag_is_change = is_support_filament(idx);
if (flag != flag_is_change) { if (flag != flag_is_change) {
sidebar->auto_calc_flushing_volumes(idx); 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()) { if (update_filament_colors_in_full_config()) {
p->sidebar->obj_list()->update_filament_colors(); p->sidebar->obj_list()->update_filament_colors();
dynamic_filament_list.update(); p->sidebar->update_dynamic_filament_list();
continue; continue;
} }
} }
@ -12851,9 +12857,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
bed_shape_changed = true; bed_shape_changed = true;
update_scheduled = true; update_scheduled = true;
} }
// BBS // Orca: update when *_filament changed
else if (opt_key == "support_interface_filament" || else if (opt_key == "support_interface_filament" || opt_key == "support_filament" || opt_key == "wall_filament" ||
opt_key == "support_filament") { opt_key == "sparse_infill_filament" || opt_key == "solid_infill_filament") {
update_scheduled = true; update_scheduled = true;
} }
} }

View file

@ -150,6 +150,7 @@ public:
void sync_ams_list(); void sync_ams_list();
// Orca // Orca
void show_SEMM_buttons(bool bshow); void show_SEMM_buttons(bool bshow);
void update_dynamic_filament_list();
ObjectList* obj_list(); ObjectList* obj_list();
ObjectSettings* obj_settings(); ObjectSettings* obj_settings();