Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/misc fixes of toolchanger #6396

Merged
merged 4 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions src/libslic3r/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3293,7 +3293,7 @@ bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType typ

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) {
m_data = std::move(sel_map);
this->touch();
Expand All @@ -3304,8 +3304,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)

void FacetsAnnotation::reset()
{
m_data.first.clear();
m_data.second.clear();
m_data.triangles_to_split.clear();
m_data.bitstream.clear();
this->touch();
}

Expand All @@ -3316,15 +3316,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
{
std::string out;

auto triangle_it = std::lower_bound(m_data.first.begin(), m_data.first.end(), triangle_idx, [](const std::pair<int, int> &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;

Expand All @@ -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)
{
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()));

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;
Expand All @@ -3355,14 +3356,16 @@ 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)
m_data.second.insert(m_data.second.end(), bool(dec & (1 << i)));
for (int i = 0; i < 4; ++i)
m_data.bitstream.insert(m_data.bitstream.end(), bool(dec & (1 << i)));
}

m_data.update_used_states(bitstream_start_idx);
}

bool FacetsAnnotation::equals(const FacetsAnnotation &other) const
{
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);
}

Expand Down
40 changes: 8 additions & 32 deletions src/libslic3r/Model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "enum_bitmask.hpp"
#include "TextConfiguration.hpp"
#include "EmbossShape.hpp"
#include "TriangleSelector.hpp"

//BBS: add bbs 3mf
#include "Format/bbs_3mf.hpp"
Expand Down Expand Up @@ -704,31 +705,6 @@ class ModelObject final : public ObjectBase
void update_min_max_z();
};

enum class EnforcerBlockerType : int8_t {
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits.
NONE = 0,
ENFORCER = 1,
BLOCKER = 2,
// Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
Extruder1 = ENFORCER,
Extruder2 = BLOCKER,
Extruder3,
Extruder4,
Extruder5,
Extruder6,
Extruder7,
Extruder8,
Extruder9,
Extruder10,
Extruder11,
Extruder12,
Extruder13,
Extruder14,
Extruder15,
Extruder16,
ExtruderMax = Extruder16
};

enum class ConversionType : int {
CONV_TO_INCH,
CONV_FROM_INCH,
Expand All @@ -745,17 +721,17 @@ enum class En3mfType : int {
class FacetsAnnotation final : public ObjectWithTimestamp {
public:
// Assign the content if the timestamp differs, don't assign an ObjectID.
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; }
void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; }
bool set(const TriangleSelector& selector);
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
// BBS
void get_facets(const ModelVolume& mv, std::vector<indexed_triangle_set>& facets_per_type) const;
void set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type);
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
bool empty() const { return m_data.first.empty(); }
bool empty() const { return m_data.triangles_to_split.empty(); }

// Following method clears the config and increases its timestamp, so the deleted
// state is considered changed from perspective of the undo/redo stack.
Expand All @@ -765,11 +741,11 @@ class FacetsAnnotation final : public ObjectWithTimestamp {
std::string get_triangle_as_string(int i) const;

// Before deserialization, reserve space for n_triangles.
void reserve(int n_triangles) { m_data.first.reserve(n_triangles); }
void reserve(int n_triangles) { m_data.triangles_to_split.reserve(n_triangles); }
// Deserialize triangles one by one, with strictly increasing triangle_id.
void set_triangle_from_string(int triangle_id, const std::string& str);
// After deserializing the last triangle, shrink data to fit.
void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); }
void shrink_to_fit() { m_data.triangles_to_split.shrink_to_fit(); m_data.bitstream.shrink_to_fit(); }
bool equals(const FacetsAnnotation &other) const;

private:
Expand All @@ -796,7 +772,7 @@ class FacetsAnnotation final : public ObjectWithTimestamp {
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.
friend class ModelVolume;
Expand Down
19 changes: 15 additions & 4 deletions src/libslic3r/PrintApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1498,11 +1498,22 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
std::vector<unsigned int> painting_extruders;
if (const auto &volumes = print_object.model_object()->volumes;
num_extruders > 1 &&
num_extruders > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
painting_extruders.assign(num_extruders , 0);
std::iota(painting_extruders.begin(), painting_extruders.end(), 1);

std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax)> used_facet_states{};
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) {
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
Expand Down
88 changes: 61 additions & 27 deletions src/libslic3r/TriangleSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
// 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
Expand All @@ -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).
struct Serializer {
const TriangleSelector* triangle_selector;
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> data;
TriangleSplittingData data;

void serialize(int facet_idx) {
const Triangle& tr = triangle_selector->m_triangles[facet_idx];
Expand All @@ -1648,71 +1647,74 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> 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
// of one split) or kept (in case of two splits). The value will
// 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)
this->serialize(tr.children[child_idx]);
} else {
// In case this is leaf, we better save information about its state.
int n = int(tr.get_state());
if (n < static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
data.used_states[n] = true;

if (n >= 3) {
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<m_orig_size_indices; ++i)
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.
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.serialize(i);
}

// May be stored onto Undo / Redo stack, thus conserve memory.
out.data.first.shrink_to_fit();
out.data.second.shrink_to_fit();
out.data.triangles_to_split.shrink_to_fit();
out.data.bitstream.shrink_to_fit();
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)
reset(); // dump any current state
for (auto [triangle_id, ibit] : data.first) {
for (auto [triangle_id, ibit] : data.triangles_to_split) {
if (triangle_id >= int(m_triangles.size())) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "array bound:error:triangle_id >= int(m_triangles.size())";
return;
}
}
// 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));
Expand All @@ -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.
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(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;
};

Expand Down Expand Up @@ -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.
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.
// Kept outside of the loop to avoid re-allocating inside the loop.
std::vector<int> parents_children;
parents_children.reserve(64);

for (const std::pair<int, int> &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
Expand Down
Loading