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();
|
||||
|
||||
// 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) {
|
||||
size_t index = src_start_id/3 + i;
|
||||
assert(index < geometry.custom_supports.size());
|
||||
|
@ -1911,7 +1914,9 @@ namespace Slic3r {
|
|||
if (! geometry.mmu_segmentation[index].empty())
|
||||
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
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
m_data = sel_map;
|
||||
m_data = std::move(sel_map);
|
||||
this->touch();
|
||||
return true;
|
||||
}
|
||||
|
@ -1963,7 +1963,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)
|
|||
|
||||
void FacetsAnnotation::clear()
|
||||
{
|
||||
m_data.clear();
|
||||
m_data.first.clear();
|
||||
m_data.second.clear();
|
||||
this->reset_timestamp();
|
||||
}
|
||||
|
||||
|
@ -1974,15 +1975,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
|||
{
|
||||
std::string out;
|
||||
|
||||
auto triangle_it = m_data.find(triangle_idx);
|
||||
if (triangle_it != m_data.end()) {
|
||||
const std::vector<bool>& code = triangle_it->second;
|
||||
int offset = 0;
|
||||
while (offset < int(code.size())) {
|
||||
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;
|
||||
while (offset < end) {
|
||||
int next_code = 0;
|
||||
for (int i=3; i>=0; --i) {
|
||||
next_code = next_code << 1;
|
||||
next_code |= int(code[offset + i]);
|
||||
next_code |= int(m_data.second[offset + i]);
|
||||
}
|
||||
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)
|
||||
{
|
||||
assert(! str.empty());
|
||||
m_data[triangle_id] = std::vector<bool>(); // zero current state or create new
|
||||
std::vector<bool>& code = m_data[triangle_id];
|
||||
assert(m_data.first.empty() || m_data.first.back().first < triangle_id);
|
||||
m_data.first.emplace_back(triangle_id, int(m_data.second.size()));
|
||||
|
||||
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
||||
const char ch = *it;
|
||||
|
@ -2013,9 +2014,8 @@ 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) {
|
||||
code.insert(code.end(), bool(dec & (1 << i)));
|
||||
}
|
||||
for (int i=0; i<4; ++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.
|
||||
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::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);
|
||||
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();
|
||||
|
||||
// Serialize triangle into string, for serialization into 3MF/AMF.
|
||||
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);
|
||||
// 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:
|
||||
// Constructors to be only called by derived classes.
|
||||
|
@ -544,7 +552,7 @@ private:
|
|||
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.
|
||||
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
|
||||
// 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
|
||||
// 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) {
|
||||
const Triangle& tr = m_triangles[i];
|
||||
|
||||
if (! tr.is_split() && tr.get_state() == EnforcerBlockerType::NONE)
|
||||
continue; // no need to save anything, unsplit and unselected is default
|
||||
|
||||
std::vector<bool> data; // complete encoding of this mesh triangle
|
||||
int stored_triangles = 0; // how many have been already encoded
|
||||
// Store index of the first bit assigned to ith triangle.
|
||||
out.first.emplace_back(i, int(out.second.size()));
|
||||
|
||||
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];
|
||||
|
||||
// Always save number of split sides. It is zero for unsplit triangles.
|
||||
int split_sides = tr.number_of_split_sides();
|
||||
assert(split_sides >= 0 && split_sides <= 3);
|
||||
|
||||
//data |= (split_sides << (stored_triangles * 4));
|
||||
data.push_back(split_sides & 0b01);
|
||||
data.push_back(split_sides & 0b10);
|
||||
out.second.push_back(split_sides & 0b01);
|
||||
out.second.push_back(split_sides & 0b10);
|
||||
|
||||
if (tr.is_split()) {
|
||||
// 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.
|
||||
assert(split_sides > 0);
|
||||
assert(tr.special_side() >= 0 && tr.special_side() <= 3);
|
||||
data.push_back(tr.special_side() & 0b01);
|
||||
data.push_back(tr.special_side() & 0b10);
|
||||
++stored_triangles;
|
||||
out.second.push_back(tr.special_side() & 0b01);
|
||||
out.second.push_back(tr.special_side() & 0b10);
|
||||
// Now save all children.
|
||||
for (int child_idx=0; child_idx<=split_sides; ++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.
|
||||
assert(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) {
|
||||
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 {
|
||||
data.push_back(int(tr.get_state()) & 0b01);
|
||||
data.push_back(int(tr.get_state()) & 0b10);
|
||||
out.second.push_back(int(tr.get_state()) & 0b01);
|
||||
out.second.push_back(int(tr.get_state()) & 0b10);
|
||||
}
|
||||
++stored_triangles;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
for (const auto& [triangle_id, code] : data) {
|
||||
for (const auto [triangle_id, first_bit] : data.first) {
|
||||
assert(triangle_id < int(m_triangles.size()));
|
||||
assert(! code.empty());
|
||||
assert(first_bit < data.second.size());
|
||||
int processed_nibbles = 0;
|
||||
struct ProcessingInfo {
|
||||
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) {
|
||||
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;
|
||||
|
|
|
@ -54,12 +54,12 @@ public:
|
|||
// Remove all unnecessary data.
|
||||
void garbage_collect();
|
||||
|
||||
// Store the division trees in compact form (a long stream of
|
||||
// bits for each triangle of the original mesh).
|
||||
std::map<int, std::vector<bool>> serialize() const;
|
||||
// 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<std::pair<int, int>>, std::vector<bool>> serialize() const;
|
||||
|
||||
// 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.
|
||||
void seed_fill_unselect_all_triangles();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue