TriangleSelector: Vertices are reference-counted and garbage collected

Garbage collection is triggered automatically when more than half of all triangles are invalid
This commit is contained in:
Lukas Matena 2020-06-24 14:47:53 +02:00
parent b9321856f3
commit da6acd73e2
2 changed files with 120 additions and 57 deletions

View file

@ -908,10 +908,20 @@ bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, boo
} }
} }
} }
// In case that all children are leafs and have the same state now,
// they may be removed and substituted by the parent triangle. if (! recursive_call) {
if (! recursive_call) // In case that all children are leafs and have the same state now,
// they may be removed and substituted by the parent triangle.
remove_useless_children(facet_idx); remove_useless_children(facet_idx);
// Make sure that we did not lose track of invalid triangles.
assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(),
[](const Triangle& tr) { return ! tr.valid; }));
// Do garbage collection maybe?
if (2*m_invalid_triangles > int(m_triangles.size()))
garbage_collect();
}
return true; return true;
} }
@ -934,6 +944,7 @@ void TriangleSelector::split_triangle(int facet_idx)
for (int i=0; i<=tr->number_of_split_sides(); ++i) { for (int i=0; i<=tr->number_of_split_sides(); ++i) {
m_triangles[tr->children[i]].set_state(old_type); m_triangles[tr->children[i]].set_state(old_type);
m_triangles[tr->children[i]].valid = true; m_triangles[tr->children[i]].valid = true;
--m_invalid_triangles;
} }
return; return;
} }
@ -941,9 +952,11 @@ void TriangleSelector::split_triangle(int facet_idx)
// If we got here, we are about to actually split the triangle. // If we got here, we are about to actually split the triangle.
const double limit_squared = m_edge_limit_sqr; const double limit_squared = m_edge_limit_sqr;
stl_triangle_vertex_indices& facet = tr->verts_idxs; std::array<int, 3>& facet = tr->verts_idxs;
const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]}; const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v};
double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() }; double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(),
(*pts[0]-*pts[2]).squaredNorm(),
(*pts[1]-*pts[0]).squaredNorm() };
std::vector<int> sides_to_split; std::vector<int> sides_to_split;
int side_to_keep = -1; int side_to_keep = -1;
@ -970,37 +983,37 @@ void TriangleSelector::split_triangle(int facet_idx)
if (sides_to_split.size() == 1) { if (sides_to_split.size() == 1) {
m_vertices.emplace_back((m_vertices[verts_idxs[1]] + m_vertices[verts_idxs[2]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1);
m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[2]); push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]);
m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[0]); push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]);
} }
if (sides_to_split.size() == 2) { if (sides_to_split.size() == 2) {
m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1);
m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[3]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1);
m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[4]); push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]);
m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[4]); push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]);
m_triangles.emplace_back(verts_idxs[2], verts_idxs[3], verts_idxs[4]); push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]);
} }
if (sides_to_split.size() == 3) { if (sides_to_split.size() == 3) {
m_vertices.emplace_back((m_vertices[verts_idxs[0]] + m_vertices[verts_idxs[1]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1);
m_vertices.emplace_back((m_vertices[verts_idxs[2]] + m_vertices[verts_idxs[3]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1);
m_vertices.emplace_back((m_vertices[verts_idxs[4]] + m_vertices[verts_idxs[0]])/2.); m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.);
verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1);
m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[5]); push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]);
m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[3]); push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]);
m_triangles.emplace_back(verts_idxs[3], verts_idxs[4], verts_idxs[5]); push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]);
m_triangles.emplace_back(verts_idxs[1], verts_idxs[3], verts_idxs[5]); push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]);
} }
tr = &m_triangles[facet_idx]; // may have been invalidated tr = &m_triangles[facet_idx]; // may have been invalidated
@ -1035,9 +1048,9 @@ bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
return ((b-a).cross(c-a)).dot(d-a) > 0.; return ((b-a).cross(c-a)).dot(d-a) > 0.;
}; };
const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]]; const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v;
const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]]; const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v;
const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]]; const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v;
const Vec3f& q1 = m_cursor.center + m_cursor.dir; const Vec3f& q1 = m_cursor.center + m_cursor.dir;
const Vec3f q2 = m_cursor.center - m_cursor.dir; const Vec3f q2 = m_cursor.center - m_cursor.dir;
@ -1065,7 +1078,7 @@ int TriangleSelector::vertices_inside(int facet_idx) const
{ {
int inside = 0; int inside = 0;
for (size_t i=0; i<3; ++i) { for (size_t i=0; i<3; ++i) {
if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]])) if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v))
++inside; ++inside;
} }
return inside; return inside;
@ -1077,7 +1090,7 @@ bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const
{ {
Vec3f pts[3]; Vec3f pts[3];
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]]; pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v;
const Vec3f& p = m_cursor.center; const Vec3f& p = m_cursor.center;
@ -1109,6 +1122,7 @@ void TriangleSelector::undivide_triangle(int facet_idx)
for (int i=0; i<=tr.number_of_split_sides(); ++i) { for (int i=0; i<=tr.number_of_split_sides(); ++i) {
undivide_triangle(tr.children[i]); undivide_triangle(tr.children[i]);
m_triangles[tr.children[i]].valid = false; m_triangles[tr.children[i]].valid = false;
++m_invalid_triangles;
} }
tr.set_division(0); // not split tr.set_division(0); // not split
} }
@ -1138,7 +1152,7 @@ void TriangleSelector::remove_useless_children(int facet_idx)
// Return if a child is not leaf or two children differ in type. // Return if a child is not leaf or two children differ in type.
FacetSupportType first_child_type; FacetSupportType first_child_type = FacetSupportType::NONE;
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
if (m_triangles[tr.children[child_idx]].is_split()) if (m_triangles[tr.children[child_idx]].is_split())
return; return;
@ -1160,13 +1174,26 @@ void TriangleSelector::garbage_collect()
// First make a map from old to new triangle indices. // First make a map from old to new triangle indices.
int new_idx = m_orig_size_indices; int new_idx = m_orig_size_indices;
std::vector<int> new_triangle_indices(m_triangles.size(), -1); std::vector<int> new_triangle_indices(m_triangles.size(), -1);
std::vector<bool> invalid_vertices(m_vertices.size(), false);
for (int i = m_orig_size_indices; i<int(m_triangles.size()); ++i) { for (int i = m_orig_size_indices; i<int(m_triangles.size()); ++i) {
if (m_triangles[i].valid) { if (m_triangles[i].valid) {
new_triangle_indices[i] = new_idx; new_triangle_indices[i] = new_idx;
++new_idx; ++new_idx;
} else { } else {
// FIXME: Decrement reference counter for the vertices. // Decrement reference counter for the vertices.
for (int j=0; j<3; ++j)
--m_vertices[m_triangles[i].verts_idxs[j]].ref_cnt;
}
}
// Now we know which vertices are not referenced anymore. Make a map
// from old idxs to new ones, like we did for triangles.
new_idx = m_orig_size_vertices;
std::vector<int> new_vertices_indices(m_vertices.size(), -1);
for (int i=m_orig_size_vertices; i<int(m_vertices.size()); ++i) {
assert(m_vertices[i].ref_cnt >= 0);
if (m_vertices[i].ref_cnt != 0) {
new_vertices_indices[i] = new_idx;
++new_idx;
} }
} }
@ -1174,6 +1201,9 @@ void TriangleSelector::garbage_collect()
m_triangles.erase(std::remove_if(m_triangles.begin()+m_orig_size_indices, m_triangles.end(), m_triangles.erase(std::remove_if(m_triangles.begin()+m_orig_size_indices, m_triangles.end(),
[](const Triangle& tr) { return ! tr.valid; }), [](const Triangle& tr) { return ! tr.valid; }),
m_triangles.end()); m_triangles.end());
m_vertices.erase(std::remove_if(m_vertices.begin()+m_orig_size_vertices, m_vertices.end(),
[](const Vertex& vert) { return vert.ref_cnt == 0; }),
m_vertices.end());
// Now go through all remaining triangles and update changed indices. // Now go through all remaining triangles and update changed indices.
for (Triangle& tr : m_triangles) { for (Triangle& tr : m_triangles) {
@ -1187,20 +1217,32 @@ void TriangleSelector::garbage_collect()
} }
} }
// Update indices into m_vertices. The original vertices are never
// touched and need not be reindexed.
for (int& idx : tr.verts_idxs) {
if (idx >= m_orig_size_vertices) {
assert(new_vertices_indices[idx] != -1);
idx = new_vertices_indices[idx];
}
}
// If this triangle was split before, forget it. // If this triangle was split before, forget it.
// Children referenced in the cache are dead by now. // Children referenced in the cache are dead by now.
tr.forget_history(); tr.forget_history();
} }
m_invalid_triangles = 0;
} }
TriangleSelector::TriangleSelector(const TriangleMesh& mesh) TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
{ {
for (const stl_vertex& vert : mesh.its.vertices) for (const stl_vertex& vert : mesh.its.vertices)
m_vertices.push_back(vert); m_vertices.emplace_back(vert);
for (const stl_triangle_vertex_indices& ind : mesh.its.indices) for (const stl_triangle_vertex_indices& ind : mesh.its.indices)
m_triangles.emplace_back(Triangle(ind[0], ind[1], ind[2])); push_triangle(ind[0], ind[1], ind[2]);
m_orig_size_vertices = m_vertices.size(); m_orig_size_vertices = m_vertices.size();
m_orig_size_indices = m_triangles.size(); m_orig_size_indices = m_triangles.size();
m_invalid_triangles = 0;
m_mesh = &mesh; m_mesh = &mesh;
} }
@ -1222,9 +1264,9 @@ void TriangleSelector::render(ImGuiWrapper* imgui)
::glColor4f(1.f, 0.f, 0.f, 0.2f); ::glColor4f(1.f, 0.f, 0.f, 0.2f);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
::glVertex3f(m_vertices[tr.verts_idxs[i]][0], ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0],
m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]].v[1],
m_vertices[tr.verts_idxs[i]][2]); m_vertices[tr.verts_idxs[i]].v[2]);
} }
::glEnd(); ::glEnd();
@ -1250,6 +1292,18 @@ void TriangleSelector::set_edge_limit(float edge_limit)
} }
} }
void TriangleSelector::push_triangle(int a, int b, int c)
{
for (int i : {a, b, c}) {
assert(i >= 0 && i < int(m_vertices.size()));
++m_vertices[i].ref_cnt;
}
m_triangles.emplace_back(a, b, c);
}
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
void TriangleSelector::render_debug(ImGuiWrapper* imgui) void TriangleSelector::render_debug(ImGuiWrapper* imgui)
{ {
@ -1262,11 +1316,10 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui)
imgui->checkbox("Show split triangles: ", m_show_triangles); imgui->checkbox("Show split triangles: ", m_show_triangles);
imgui->checkbox("Show invalid triangles: ", m_show_invalid); imgui->checkbox("Show invalid triangles: ", m_show_invalid);
int valid_triangles = std::count_if(m_triangles.begin(), m_triangles.end(), int valid_triangles = m_triangles.size() - m_invalid_triangles;
[](const Triangle& tr) { return tr.valid; });
imgui->text("Valid triangles: " + std::to_string(valid_triangles) + imgui->text("Valid triangles: " + std::to_string(valid_triangles) +
"/" + std::to_string(m_triangles.size())); "/" + std::to_string(m_triangles.size()));
imgui->text("Number of vertices: " + std::to_string(m_vertices.size())); imgui->text("Vertices: " + std::to_string(m_vertices.size()));
if (imgui->button("Force garbage collection")) if (imgui->button("Force garbage collection"))
garbage_collect(); garbage_collect();
@ -1290,9 +1343,9 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui)
::glColor3f(0.f, 0.f, 1.f); ::glColor3f(0.f, 0.f, 1.f);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
::glVertex3f(m_vertices[tr.verts_idxs[i]][0], ::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0],
m_vertices[tr.verts_idxs[i]][1], m_vertices[tr.verts_idxs[i]].v[1],
m_vertices[tr.verts_idxs[i]][2]); m_vertices[tr.verts_idxs[i]].v[2]);
} }
::glEnd(); ::glEnd();
::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

View file

@ -33,7 +33,7 @@ public:
// stay valid, a ptr to it is saved and used. // stay valid, a ptr to it is saved and used.
explicit TriangleSelector(const TriangleMesh& mesh); explicit TriangleSelector(const TriangleMesh& mesh);
// Select all triangles inside the circle, subdivide where needed. // Select all triangles fully inside the circle, subdivide where needed.
void select_patch(const Vec3f& hit, // point where to start void select_patch(const Vec3f& hit, // point where to start
int facet_start, // facet that point belongs to int facet_start, // facet that point belongs to
const Vec3f& source, // camera position (mesh coords) const Vec3f& source, // camera position (mesh coords)
@ -41,14 +41,11 @@ public:
float radius_sqr, // squared radius of the cursor float radius_sqr, // squared radius of the cursor
FacetSupportType new_state); // enforcer or blocker? FacetSupportType new_state); // enforcer or blocker?
void unselect_all();
// Render current selection. Transformation matrices are supposed // Render current selection. Transformation matrices are supposed
// to be already set. // to be already set.
void render(ImGuiWrapper* imgui = nullptr); void render(ImGuiWrapper* imgui = nullptr);
// Remove all unnecessary data (such as vertices that are not needed // Remove all unnecessary data.
// because the selection has been made larger.
void garbage_collect(); void garbage_collect();
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
@ -59,21 +56,24 @@ public:
private: private:
// Triangle and info about how it's split. // Triangle and info about how it's split.
struct Triangle { class Triangle {
public: public:
// Use TriangleSelector::push_triangle to create a new triangle.
// It increments/decrements reference counter on vertices.
Triangle(int a, int b, int c) Triangle(int a, int b, int c)
: verts_idxs{stl_triangle_vertex_indices(a, b, c)}, : verts_idxs{a, b, c},
state{FacetSupportType(0)}, state{FacetSupportType(0)},
number_of_splits{0}, number_of_splits{0},
special_side_idx{0}, special_side_idx{0},
old_number_of_splits{0} old_number_of_splits{0}
{} {}
stl_triangle_vertex_indices verts_idxs; // Indices into m_vertices.
std::array<int, 3> verts_idxs;
// Is this triangle valid or marked to remove? // Is this triangle valid or marked to be removed?
bool valid{true}; bool valid{true};
// Children triangles (0 = no child) // Children triangles.
std::array<int, 4> children; std::array<int, 4> children;
// Set the division type. // Set the division type.
@ -101,22 +101,31 @@ private:
int old_number_of_splits; int old_number_of_splits;
}; };
struct Vertex {
explicit Vertex(const stl_vertex& vert)
: v{vert},
ref_cnt{0}
{}
stl_vertex v;
int ref_cnt;
};
// Lists of vertices and triangles, both original and new // Lists of vertices and triangles, both original and new
std::vector<stl_vertex> m_vertices; std::vector<Vertex> m_vertices;
std::vector<Triangle> m_triangles; std::vector<Triangle> m_triangles;
const TriangleMesh* m_mesh; const TriangleMesh* m_mesh;
// Number of invalid triangles (to trigger garbage collection).
int m_invalid_triangles;
// Limiting length of triangle side (squared).
float m_edge_limit_sqr = 1.f; float m_edge_limit_sqr = 1.f;
// Number of original vertices and triangles. // Number of original vertices and triangles.
int m_orig_size_vertices; int m_orig_size_vertices;
int m_orig_size_indices; int m_orig_size_indices;
// Limits for stopping the recursion. // Cache for cursor position, radius and direction.
float m_max_edge_length;
int m_max_recursion_depth;
// Caches for cursor position, radius and direction.
struct Cursor { struct Cursor {
Vec3f center; Vec3f center;
Vec3f source; Vec3f source;
@ -130,13 +139,14 @@ private:
bool select_triangle(int facet_idx, FacetSupportType type, bool select_triangle(int facet_idx, FacetSupportType type,
bool recursive_call = false); bool recursive_call = false);
bool is_point_inside_cursor(const Vec3f& point) const; bool is_point_inside_cursor(const Vec3f& point) const;
int vertices_inside(int facet_idx) const; int vertices_inside(int facet_idx) const;
bool faces_camera(int facet) const; bool faces_camera(int facet) const;
void undivide_triangle(int facet_idx); void undivide_triangle(int facet_idx);
void split_triangle(int facet_idx); void split_triangle(int facet_idx);
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
bool is_pointer_in_triangle(int facet_idx) const; bool is_pointer_in_triangle(int facet_idx) const;
bool is_edge_inside_cursor(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const;
void push_triangle(int a, int b, int c);
}; };