mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-18 12:17:54 -06:00
Support / seam / MMU painting serialization / deserialization:
Changed the serialization structure std::map<int, std::vector<bool>> to a significantly more compact std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> Such change shall significantly improve efficiency of Undo / Redo stack.
This commit is contained in:
parent
b59ff2c294
commit
95f4690e25
5 changed files with 56 additions and 43 deletions
|
@ -1899,6 +1899,9 @@ namespace Slic3r {
|
||||||
volume->calculate_convex_hull();
|
volume->calculate_convex_hull();
|
||||||
|
|
||||||
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
|
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
|
||||||
|
volume->supported_facets.reserve(triangles_count);
|
||||||
|
volume->seam_facets.reserve(triangles_count);
|
||||||
|
volume->mmu_segmentation_facets.reserve(triangles_count);
|
||||||
for (unsigned i=0; i<triangles_count; ++i) {
|
for (unsigned i=0; i<triangles_count; ++i) {
|
||||||
size_t index = src_start_id/3 + i;
|
size_t index = src_start_id/3 + i;
|
||||||
assert(index < geometry.custom_supports.size());
|
assert(index < geometry.custom_supports.size());
|
||||||
|
@ -1911,7 +1914,9 @@ namespace Slic3r {
|
||||||
if (! geometry.mmu_segmentation[index].empty())
|
if (! geometry.mmu_segmentation[index].empty())
|
||||||
volume->mmu_segmentation_facets.set_triangle_from_string(i, geometry.mmu_segmentation[index]);
|
volume->mmu_segmentation_facets.set_triangle_from_string(i, geometry.mmu_segmentation[index]);
|
||||||
}
|
}
|
||||||
|
volume->supported_facets.shrink_to_fit();
|
||||||
|
volume->seam_facets.shrink_to_fit();
|
||||||
|
volume->mmu_segmentation_facets.shrink_to_fit();
|
||||||
|
|
||||||
// apply the remaining volume's metadata
|
// apply the remaining volume's metadata
|
||||||
for (const Metadata& metadata : volume_data.metadata) {
|
for (const Metadata& metadata : volume_data.metadata) {
|
||||||
|
|
|
@ -1952,9 +1952,9 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce
|
||||||
|
|
||||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
||||||
{
|
{
|
||||||
std::map<int, std::vector<bool>> sel_map = selector.serialize();
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> sel_map = selector.serialize();
|
||||||
if (sel_map != m_data) {
|
if (sel_map != m_data) {
|
||||||
m_data = sel_map;
|
m_data = std::move(sel_map);
|
||||||
this->touch();
|
this->touch();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1963,7 +1963,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)
|
||||||
|
|
||||||
void FacetsAnnotation::clear()
|
void FacetsAnnotation::clear()
|
||||||
{
|
{
|
||||||
m_data.clear();
|
m_data.first.clear();
|
||||||
|
m_data.second.clear();
|
||||||
this->reset_timestamp();
|
this->reset_timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1974,15 +1975,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|
||||||
auto triangle_it = m_data.find(triangle_idx);
|
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.end()) {
|
if (triangle_it != m_data.first.end() && triangle_it->first == triangle_idx) {
|
||||||
const std::vector<bool>& code = triangle_it->second;
|
int offset = triangle_it->second;
|
||||||
int offset = 0;
|
int end = ++ triangle_it == m_data.first.end() ? int(m_data.second.size()) : triangle_it->second;
|
||||||
while (offset < int(code.size())) {
|
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(code[offset + i]);
|
next_code |= int(m_data.second[offset + i]);
|
||||||
}
|
}
|
||||||
offset += 4;
|
offset += 4;
|
||||||
|
|
||||||
|
@ -1999,8 +2000,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)
|
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
|
||||||
{
|
{
|
||||||
assert(! str.empty());
|
assert(! str.empty());
|
||||||
m_data[triangle_id] = std::vector<bool>(); // zero current state or create new
|
assert(m_data.first.empty() || m_data.first.back().first < triangle_id);
|
||||||
std::vector<bool>& code = m_data[triangle_id];
|
m_data.first.emplace_back(triangle_id, int(m_data.second.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;
|
||||||
|
@ -2013,9 +2014,8 @@ 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)
|
||||||
code.insert(code.end(), bool(dec & (1 << i)));
|
m_data.second.insert(m_data.second.end(), bool(dec & (1 << i)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -512,13 +512,21 @@ 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::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
|
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { 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;
|
||||||
bool empty() const { return m_data.empty(); }
|
bool empty() const { return m_data.first.empty(); }
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
// Serialize triangle into string, for serialization into 3MF/AMF.
|
||||||
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.
|
||||||
|
void reserve(int n_triangles) { m_data.first.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);
|
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(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Constructors to be only called by derived classes.
|
// Constructors to be only called by derived classes.
|
||||||
|
@ -544,7 +552,7 @@ private:
|
||||||
ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
|
ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<int, std::vector<bool>> m_data;
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> 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;
|
||||||
|
|
|
@ -593,7 +593,7 @@ indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) con
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> 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):
|
||||||
|
@ -605,27 +605,27 @@ std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
||||||
// The function returns a map from original triangle indices to
|
// The function returns a map from original triangle indices to
|
||||||
// stream of bits encoding state and offsprings.
|
// stream of bits encoding state and offsprings.
|
||||||
|
|
||||||
std::map<int, std::vector<bool>> out;
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> out;
|
||||||
|
out.first.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) {
|
||||||
const Triangle& tr = m_triangles[i];
|
const Triangle& tr = m_triangles[i];
|
||||||
|
|
||||||
if (! tr.is_split() && tr.get_state() == EnforcerBlockerType::NONE)
|
if (! tr.is_split() && tr.get_state() == EnforcerBlockerType::NONE)
|
||||||
continue; // no need to save anything, unsplit and unselected is default
|
continue; // no need to save anything, unsplit and unselected is default
|
||||||
|
|
||||||
std::vector<bool> data; // complete encoding of this mesh triangle
|
// Store index of the first bit assigned to ith triangle.
|
||||||
int stored_triangles = 0; // how many have been already encoded
|
out.first.emplace_back(i, int(out.second.size()));
|
||||||
|
|
||||||
std::function<void(int)> serialize_recursive;
|
std::function<void(int)> serialize_recursive;
|
||||||
serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) {
|
serialize_recursive = [this, &serialize_recursive, &out](int facet_idx) {
|
||||||
const Triangle& tr = m_triangles[facet_idx];
|
const Triangle& tr = m_triangles[facet_idx];
|
||||||
|
|
||||||
// Always save number of split sides. It is zero for unsplit triangles.
|
// Always save number of split sides. It is zero for unsplit triangles.
|
||||||
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 |= (split_sides << (stored_triangles * 4));
|
out.second.push_back(split_sides & 0b01);
|
||||||
data.push_back(split_sides & 0b01);
|
out.second.push_back(split_sides & 0b10);
|
||||||
data.push_back(split_sides & 0b10);
|
|
||||||
|
|
||||||
if (tr.is_split()) {
|
if (tr.is_split()) {
|
||||||
// If this triangle is split, save which side is split (in case
|
// If this triangle is split, save which side is split (in case
|
||||||
|
@ -633,9 +633,8 @@ std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
||||||
// be ignored for 3-side split.
|
// be ignored for 3-side split.
|
||||||
assert(split_sides > 0);
|
assert(split_sides > 0);
|
||||||
assert(tr.special_side() >= 0 && tr.special_side() <= 3);
|
assert(tr.special_side() >= 0 && tr.special_side() <= 3);
|
||||||
data.push_back(tr.special_side() & 0b01);
|
out.second.push_back(tr.special_side() & 0b01);
|
||||||
data.push_back(tr.special_side() & 0b10);
|
out.second.push_back(tr.special_side() & 0b10);
|
||||||
++stored_triangles;
|
|
||||||
// Now save all children.
|
// Now save all children.
|
||||||
for (int child_idx=0; child_idx<=split_sides; ++child_idx)
|
for (int child_idx=0; child_idx<=split_sides; ++child_idx)
|
||||||
serialize_recursive(tr.children[child_idx]);
|
serialize_recursive(tr.children[child_idx]);
|
||||||
|
@ -643,32 +642,33 @@ std::map<int, std::vector<bool>> TriangleSelector::serialize() const
|
||||||
// In case this is leaf, we better save information about its state.
|
// In case this is leaf, we better save information about its state.
|
||||||
assert(int(tr.get_state()) <= 15);
|
assert(int(tr.get_state()) <= 15);
|
||||||
if (3 <= int(tr.get_state()) && int(tr.get_state()) <= 15) {
|
if (3 <= int(tr.get_state()) && int(tr.get_state()) <= 15) {
|
||||||
data.insert(data.end(), {true, true});
|
out.second.insert(out.second.end(), {true, true});
|
||||||
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) {
|
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) {
|
||||||
size_t bit_mask = uint64_t(0b0001) << bit_idx;
|
size_t bit_mask = uint64_t(0b0001) << bit_idx;
|
||||||
data.push_back((int(tr.get_state()) - 3) & bit_mask);
|
out.second.push_back((int(tr.get_state()) - 3) & bit_mask);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data.push_back(int(tr.get_state()) & 0b01);
|
out.second.push_back(int(tr.get_state()) & 0b01);
|
||||||
data.push_back(int(tr.get_state()) & 0b10);
|
out.second.push_back(int(tr.get_state()) & 0b10);
|
||||||
}
|
}
|
||||||
++stored_triangles;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
serialize_recursive(i);
|
serialize_recursive(i);
|
||||||
out[i] = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// May be stored onto Undo / Redo stack, thus conserve memory.
|
||||||
|
out.first.shrink_to_fit();
|
||||||
|
out.second.shrink_to_fit();
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data, const EnforcerBlockerType init_state)
|
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, const EnforcerBlockerType init_state)
|
||||||
{
|
{
|
||||||
reset(init_state); // dump any current state
|
reset(init_state); // dump any current state
|
||||||
for (const auto& [triangle_id, code] : data) {
|
for (const auto [triangle_id, first_bit] : data.first) {
|
||||||
assert(triangle_id < int(m_triangles.size()));
|
assert(triangle_id < int(m_triangles.size()));
|
||||||
assert(! code.empty());
|
assert(first_bit < data.second.size());
|
||||||
int processed_nibbles = 0;
|
int processed_nibbles = 0;
|
||||||
struct ProcessingInfo {
|
struct ProcessingInfo {
|
||||||
int facet_id = 0;
|
int facet_id = 0;
|
||||||
|
@ -689,7 +689,7 @@ void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data,
|
||||||
|
|
||||||
for (int i = 3; i >= 0; --i) {
|
for (int i = 3; i >= 0; --i) {
|
||||||
next_code[nibble_idx] = next_code[nibble_idx] << 1;
|
next_code[nibble_idx] = next_code[nibble_idx] << 1;
|
||||||
next_code[nibble_idx] |= int(code[4 * processed_nibbles + i]);
|
next_code[nibble_idx] |= int(data.second[first_bit + 4 * processed_nibbles + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
++processed_nibbles;
|
++processed_nibbles;
|
||||||
|
|
|
@ -54,12 +54,12 @@ public:
|
||||||
// Remove all unnecessary data.
|
// Remove all unnecessary data.
|
||||||
void garbage_collect();
|
void garbage_collect();
|
||||||
|
|
||||||
// Store the division trees in compact form (a long stream of
|
// Store the division trees in compact form (a long stream of bits for each triangle of the original mesh).
|
||||||
// bits for each triangle of the original mesh).
|
// First vector contains pairs of (triangle index, first bit in the second vector).
|
||||||
std::map<int, std::vector<bool>> serialize() const;
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const;
|
||||||
|
|
||||||
// Load serialized data. Assumes that correct mesh is loaded.
|
// Load serialized data. Assumes that correct mesh is loaded.
|
||||||
void deserialize(const std::map<int, std::vector<bool>> data, const EnforcerBlockerType init_state = EnforcerBlockerType{0});
|
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, const EnforcerBlockerType init_state = EnforcerBlockerType{0});
|
||||||
|
|
||||||
// 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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue