mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 18:55:53 +08:00
Use more readable data types for storing triangle splitting information.
This commit is contained in:
parent
435b4ddf23
commit
13e2af71e1
@ -2044,7 +2044,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();
|
||||
@ -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<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;
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "enum_bitmask.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
#include "EmbossShape.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@ -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<std::pair<int, int>>, std::vector<bool>>& 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<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;
|
||||
|
@ -1526,8 +1526,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
|
||||
@ -1543,7 +1542,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];
|
||||
@ -1552,8 +1551,8 @@ 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
|
||||
@ -1561,8 +1560,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> 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<std::pair<int, int>>, std::vector<bool>> 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<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)
|
||||
{
|
||||
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<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;
|
||||
};
|
||||
|
||||
@ -1706,20 +1704,19 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -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<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;
|
||||
|
||||
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<class Archive> void serialize(Archive &ar) { ar(triangles_to_split, bitstream); }
|
||||
};
|
||||
|
||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &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<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;
|
||||
// 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<std::pair<int, int>>, std::vector<bool>> serialize() const;
|
||||
TriangleSplittingData serialize() const;
|
||||
|
||||
// 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);
|
||||
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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user