TriangleSelector:
1) Fixing yesterday's regression in deserialization of older painted 3MFs (order of triangle children is now reversed, thus the serialization / deserialization has to take it into account). 2) WIP extraction into facets to triangulate T-joints.
This commit is contained in:
parent
ccb53f71b6
commit
d0411223be
2 changed files with 218 additions and 18 deletions
|
@ -3,12 +3,22 @@
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifndef _NDEBUG
|
||||||
#define EXPENSIVE_DEBUG_CHECKS
|
#define EXPENSIVE_DEBUG_CHECKS
|
||||||
#endif // DEBUG
|
#endif // _NDEBUG
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
static inline Vec3i root_neighbors(const TriangleMesh &mesh, int triangle_id)
|
||||||
|
{
|
||||||
|
Vec3i neighbors;
|
||||||
|
const stl_neighbors& neighbors_src = mesh.stl.neighbors_start[triangle_id];
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
// Refuse a neighbor with a flipped normal.
|
||||||
|
neighbors(i) = neighbors_src.neighbor[i];
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef _NDEBUG
|
#ifndef _NDEBUG
|
||||||
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
||||||
{
|
{
|
||||||
|
@ -137,7 +147,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f& hit, int facet_st
|
||||||
if (current_facet < m_orig_size_indices)
|
if (current_facet < m_orig_size_indices)
|
||||||
// Propagate over the original triangles.
|
// Propagate over the original triangles.
|
||||||
for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
|
for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
|
||||||
assert(neighbor_idx >= 0);
|
assert(neighbor_idx >= -1);
|
||||||
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
|
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
|
||||||
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
||||||
const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
|
const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
|
||||||
|
@ -163,10 +173,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type,
|
||||||
if (! m_triangles[facet_idx].valid())
|
if (! m_triangles[facet_idx].valid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Vec3i neighbors;
|
Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
||||||
const stl_neighbors &neighbors_src = m_mesh->stl.neighbors_start[facet_idx];
|
|
||||||
for (int i = 0; i < 3; ++ i)
|
|
||||||
neighbors(i) = neighbors_src.neighbor[i];
|
|
||||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||||
|
|
||||||
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
|
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
|
||||||
|
@ -863,6 +870,23 @@ void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, Enfo
|
||||||
#endif // _NDEBUG
|
#endif // _NDEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TriangleSelector::has_facets(EnforcerBlockerType state) const
|
||||||
|
{
|
||||||
|
for (const Triangle& tr : m_triangles)
|
||||||
|
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TriangleSelector::num_facets(EnforcerBlockerType state) const
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
for (const Triangle& tr : m_triangles)
|
||||||
|
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
||||||
|
++ cnt;
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const
|
indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const
|
||||||
{
|
{
|
||||||
indexed_triangle_set out;
|
indexed_triangle_set out;
|
||||||
|
@ -884,7 +908,121 @@ indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) con
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType state) const
|
||||||
|
{
|
||||||
|
indexed_triangle_set out;
|
||||||
|
|
||||||
|
size_t num_vertices = 0;
|
||||||
|
for (const Vertex &v : m_vertices)
|
||||||
|
if (v.ref_cnt > 0)
|
||||||
|
++ num_vertices;
|
||||||
|
out.vertices.reserve(num_vertices);
|
||||||
|
std::vector<int> vertex_map(m_vertices.size(), -1);
|
||||||
|
for (int i = 0; i < m_vertices.size(); ++ i)
|
||||||
|
if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
|
||||||
|
vertex_map[i] = int(out.vertices.size());
|
||||||
|
out.vertices.emplace_back(v.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
|
||||||
|
this->get_facets_strict_recursive(m_triangles[itriangle], root_neighbors(*m_mesh, itriangle), state, out.indices);
|
||||||
|
|
||||||
|
for (auto &triangle : out.indices)
|
||||||
|
for (int i = 0; i < 3; ++ i)
|
||||||
|
triangle(i) = vertex_map[triangle(i)];
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSelector::get_facets_strict_recursive(
|
||||||
|
const Triangle &tr,
|
||||||
|
const Vec3i &neighbors,
|
||||||
|
EnforcerBlockerType state,
|
||||||
|
std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
||||||
|
{
|
||||||
|
if (tr.is_split()) {
|
||||||
|
for (int i = 0; i <= tr.number_of_split_sides(); ++ i)
|
||||||
|
this->get_facets_strict_recursive(
|
||||||
|
m_triangles[tr.children[i]],
|
||||||
|
this->child_neighbors(tr, neighbors, i),
|
||||||
|
state, out_triangles);
|
||||||
|
} else if (tr.get_state() == state)
|
||||||
|
this->get_facets_split_by_tjoints({tr.verts_idxs[0], tr.verts_idxs[1], tr.verts_idxs[2]}, neighbors, out_triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const Vec3i neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
||||||
|
{
|
||||||
|
// Export this triangle, but first collect the T-joint vertices along its edges.
|
||||||
|
Vec3i midpoints(
|
||||||
|
this->triangle_midpoint(neighbors(0), vertices(1), vertices(0)),
|
||||||
|
this->triangle_midpoint(neighbors(1), vertices(2), vertices(1)),
|
||||||
|
this->triangle_midpoint(neighbors(2), vertices(0), vertices(2)));
|
||||||
|
int splits = (midpoints(0) != -1) + (midpoints(1) != -1) + (midpoints(2) != -1);
|
||||||
|
if (splits == 0) {
|
||||||
|
// Just emit this triangle.
|
||||||
|
out_triangles.emplace_back(vertices(0), midpoints(0), midpoints(2));
|
||||||
|
} else if (splits == 1) {
|
||||||
|
// Split to two triangles
|
||||||
|
int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
|
||||||
|
int j = next_idx_modulo(i, 3);
|
||||||
|
int k = next_idx_modulo(j, 3);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ vertices(i), vertices(j), midpoints(j) },
|
||||||
|
{ neighbors(i),
|
||||||
|
this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::Second),
|
||||||
|
-1 },
|
||||||
|
out_triangles);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ midpoints(j), vertices(j), vertices(k) },
|
||||||
|
{ this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::First),
|
||||||
|
neighbors(k),
|
||||||
|
-1 },
|
||||||
|
out_triangles);
|
||||||
|
} else if (splits == 2) {
|
||||||
|
// Split to three triangles.
|
||||||
|
int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
|
||||||
|
int j = next_idx_modulo(i, 3);
|
||||||
|
int k = next_idx_modulo(j, 3);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ vertices(i), midpoints(i), midpoints(k) },
|
||||||
|
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::Second),
|
||||||
|
-1,
|
||||||
|
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::First) },
|
||||||
|
out_triangles);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ midpoints(i), vertices(j), midpoints(k) },
|
||||||
|
{ this->neighbor_child(neighbors(i), vertices(j), vertices(i), Partition::First),
|
||||||
|
-1, -1 },
|
||||||
|
out_triangles);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ vertices(j), vertices(k), midpoints(k) },
|
||||||
|
{ neighbors(j),
|
||||||
|
this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
|
||||||
|
-1 },
|
||||||
|
out_triangles);
|
||||||
|
} else if (splits == 4) {
|
||||||
|
// Split to 4 triangles.
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ vertices(0), midpoints(0), midpoints(2) },
|
||||||
|
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::Second),
|
||||||
|
-1,
|
||||||
|
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::First) },
|
||||||
|
out_triangles);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ midpoints(0), vertices(1), midpoints(1) },
|
||||||
|
{ this->neighbor_child(neighbors(0), vertices(1), vertices(0), Partition::First),
|
||||||
|
this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::Second),
|
||||||
|
-1 },
|
||||||
|
out_triangles);
|
||||||
|
this->get_facets_split_by_tjoints(
|
||||||
|
{ midpoints(1), vertices(2), midpoints(2) },
|
||||||
|
{ this->neighbor_child(neighbors(1), vertices(2), vertices(1), Partition::First),
|
||||||
|
this->neighbor_child(neighbors(2), vertices(0), vertices(2), Partition::Second),
|
||||||
|
-1 },
|
||||||
|
out_triangles);
|
||||||
|
out_triangles.emplace_back(midpoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const
|
||||||
{
|
{
|
||||||
|
@ -924,7 +1062,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
||||||
data.second.push_back(tr.special_side() & 0b01);
|
data.second.push_back(tr.special_side() & 0b01);
|
||||||
data.second.push_back(tr.special_side() & 0b10);
|
data.second.push_back(tr.special_side() & 0b10);
|
||||||
// Now save all children.
|
// Now save all children.
|
||||||
for (int child_idx = 0; child_idx <= split_sides; ++child_idx)
|
// 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]);
|
this->serialize(tr.children[child_idx]);
|
||||||
} 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.
|
||||||
|
@ -1005,10 +1144,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
||||||
if (is_split) {
|
if (is_split) {
|
||||||
// root is split, add it into list of parents and split it.
|
// root is split, add it into list of parents and split it.
|
||||||
// then go to the next.
|
// then go to the next.
|
||||||
Vec3i neighbors;
|
Vec3i neighbors = root_neighbors(*m_mesh, triangle_id);
|
||||||
const stl_neighbors& neighbors_src = m_mesh->stl.neighbors_start[triangle_id];
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
neighbors(i) = neighbors_src.neighbor[i];
|
|
||||||
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
||||||
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
||||||
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
|
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
|
||||||
|
@ -1024,18 +1160,19 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
||||||
assert(! parents.empty());
|
assert(! parents.empty());
|
||||||
assert(parents.back().processed_children < parents.back().total_children);
|
assert(parents.back().processed_children < parents.back().total_children);
|
||||||
|
|
||||||
if (ProcessingInfo& last = parents.back(); is_split) {
|
if (ProcessingInfo& last = parents.back(); is_split) {
|
||||||
// split the triangle and save it as parent of the next ones.
|
// split the triangle and save it as parent of the next ones.
|
||||||
const Triangle &tr = m_triangles[last.facet_id];
|
const Triangle &tr = m_triangles[last.facet_id];
|
||||||
//FIXME calculate neighbor triangles from last.neighbors and last.processed_children.
|
int child_idx = last.total_children - last.processed_children - 1;
|
||||||
Vec3i neighbors = this->child_neighbors(tr, last.neighbors, last.processed_children);
|
Vec3i neighbors = this->child_neighbors(tr, last.neighbors, child_idx);
|
||||||
int this_idx = tr.children[last.processed_children];
|
int this_idx = tr.children[child_idx];
|
||||||
m_triangles[this_idx].set_division(num_of_split_sides, special_side);
|
m_triangles[this_idx].set_division(num_of_split_sides, special_side);
|
||||||
perform_split(this_idx, neighbors, EnforcerBlockerType::NONE);
|
perform_split(this_idx, neighbors, EnforcerBlockerType::NONE);
|
||||||
parents.push_back({this_idx, neighbors, 0, num_of_children});
|
parents.push_back({this_idx, neighbors, 0, num_of_children});
|
||||||
} else {
|
} else {
|
||||||
// this triangle belongs to last split one
|
// this triangle belongs to last split one
|
||||||
m_triangles[m_triangles[last.facet_id].children[last.processed_children]].set_state(state);
|
int child_idx = last.total_children - last.processed_children - 1;
|
||||||
|
m_triangles[m_triangles[last.facet_id].children[child_idx]].set_state(state);
|
||||||
++last.processed_children;
|
++last.processed_children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,6 +1195,57 @@ 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)
|
||||||
|
{
|
||||||
|
// 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 (auto [triangle_id, ibit] : data.first) {
|
||||||
|
assert(ibit < data.second.size());
|
||||||
|
auto next_nibble = [&data, &ibit = ibit]() {
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < 4; ++ i)
|
||||||
|
n |= data.second[ibit ++] << i;
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
// < 0 -> negative of a number of children
|
||||||
|
// >= 0 -> state
|
||||||
|
auto num_children_or_state = [&next_nibble]() -> int {
|
||||||
|
int code = next_nibble();
|
||||||
|
int num_of_split_sides = code & 0b11;
|
||||||
|
return num_of_split_sides == 0 ?
|
||||||
|
((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2) :
|
||||||
|
- num_of_split_sides - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
int state = num_children_or_state();
|
||||||
|
if (state < 0) {
|
||||||
|
// Root is split.
|
||||||
|
parents_children.clear();
|
||||||
|
parents_children.emplace_back(- state);
|
||||||
|
do {
|
||||||
|
if (-- parents_children.back() >= 0) {
|
||||||
|
int state = num_children_or_state();
|
||||||
|
if (state < 0)
|
||||||
|
// Child is split.
|
||||||
|
parents_children.emplace_back(- state);
|
||||||
|
else if (state == int(test_state))
|
||||||
|
// Child is not split and a face of test_state was found.
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
parents_children.pop_back();
|
||||||
|
} while (! parents_children.empty());
|
||||||
|
} else if (state == int(test_state))
|
||||||
|
// Root is not split and a face of test_state was found.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void TriangleSelector::seed_fill_unselect_all_triangles()
|
void TriangleSelector::seed_fill_unselect_all_triangles()
|
||||||
{
|
{
|
||||||
for (Triangle &triangle : m_triangles)
|
for (Triangle &triangle : m_triangles)
|
||||||
|
|
|
@ -43,8 +43,13 @@ public:
|
||||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
float seed_fill_angle); // the maximal angle between two facets to be painted by the same color
|
float seed_fill_angle); // the maximal angle between two facets to be painted by the same color
|
||||||
|
|
||||||
// Get facets currently in the given state.
|
bool has_facets(EnforcerBlockerType state) const;
|
||||||
|
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, const 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;
|
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
||||||
|
// Get facets at a given state. Triangulate T-joints.
|
||||||
|
indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const;
|
||||||
|
|
||||||
// Set facet of the mesh to a given state. Only works for original triangles.
|
// Set facet of the mesh to a given state. Only works for original triangles.
|
||||||
void set_facet(int facet_idx, EnforcerBlockerType state);
|
void set_facet(int facet_idx, EnforcerBlockerType state);
|
||||||
|
@ -204,6 +209,13 @@ private:
|
||||||
bool verify_triangle_midpoints(const Triangle& tr) const;
|
bool verify_triangle_midpoints(const Triangle& tr) const;
|
||||||
#endif // _NDEBUG
|
#endif // _NDEBUG
|
||||||
|
|
||||||
|
void get_facets_strict_recursive(
|
||||||
|
const Triangle &tr,
|
||||||
|
const Vec3i &neighbors,
|
||||||
|
EnforcerBlockerType state,
|
||||||
|
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
|
void get_facets_split_by_tjoints(const Vec3i vertices, const Vec3i neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
|
|
||||||
int m_free_triangles_head { -1 };
|
int m_free_triangles_head { -1 };
|
||||||
int m_free_vertices_head { -1 };
|
int m_free_vertices_head { -1 };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue