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
}
}
}
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.
if (! recursive_call)
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;
}
@ -934,6 +944,7 @@ void TriangleSelector::split_triangle(int facet_idx)
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]].valid = true;
--m_invalid_triangles;
}
return;
}
@ -941,9 +952,11 @@ void TriangleSelector::split_triangle(int facet_idx)
// If we got here, we are about to actually split the triangle.
const double limit_squared = m_edge_limit_sqr;
stl_triangle_vertex_indices& facet = tr->verts_idxs;
const stl_vertex* pts[3] = { &m_vertices[facet[0]], &m_vertices[facet[1]], &m_vertices[facet[2]]};
double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), (*pts[0]-*pts[2]).squaredNorm(), (*pts[1]-*pts[0]).squaredNorm() };
std::array<int, 3>& facet = tr->verts_idxs;
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() };
std::vector<int> sides_to_split;
int side_to_keep = -1;
@ -970,37 +983,37 @@ void TriangleSelector::split_triangle(int facet_idx)
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);
m_triangles.emplace_back(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[0], verts_idxs[1], verts_idxs[2]);
push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]);
}
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);
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);
m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[4]);
m_triangles.emplace_back(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[0], verts_idxs[1], verts_idxs[4]);
push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]);
push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]);
}
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);
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);
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);
m_triangles.emplace_back(verts_idxs[0], verts_idxs[1], verts_idxs[5]);
m_triangles.emplace_back(verts_idxs[1], verts_idxs[2], verts_idxs[3]);
m_triangles.emplace_back(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[0], verts_idxs[1], verts_idxs[5]);
push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]);
push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]);
push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]);
}
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.;
};
const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]];
const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]];
const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]];
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]].v;
const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v;
const Vec3f& q1 = 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;
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;
}
return inside;
@ -1077,7 +1090,7 @@ bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const
{
Vec3f pts[3];
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;
@ -1109,6 +1122,7 @@ void TriangleSelector::undivide_triangle(int facet_idx)
for (int i=0; i<=tr.number_of_split_sides(); ++i) {
undivide_triangle(tr.children[i]);
m_triangles[tr.children[i]].valid = false;
++m_invalid_triangles;
}
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.
FacetSupportType first_child_type;
FacetSupportType first_child_type = FacetSupportType::NONE;
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
if (m_triangles[tr.children[child_idx]].is_split())
return;
@ -1160,13 +1174,26 @@ void TriangleSelector::garbage_collect()
// First make a map from old to new triangle indices.
int new_idx = m_orig_size_indices;
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) {
if (m_triangles[i].valid) {
new_triangle_indices[i] = new_idx;
++new_idx;
} 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(),
[](const Triangle& tr) { return ! tr.valid; }),
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.
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.
// Children referenced in the cache are dead by now.
tr.forget_history();
}
m_invalid_triangles = 0;
}
TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
{
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)
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_indices = m_triangles.size();
m_invalid_triangles = 0;
m_mesh = &mesh;
}
@ -1222,9 +1264,9 @@ void TriangleSelector::render(ImGuiWrapper* imgui)
::glColor4f(1.f, 0.f, 0.f, 0.2f);
for (int i=0; i<3; ++i)
::glVertex3f(m_vertices[tr.verts_idxs[i]][0],
m_vertices[tr.verts_idxs[i]][1],
m_vertices[tr.verts_idxs[i]][2]);
::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0],
m_vertices[tr.verts_idxs[i]].v[1],
m_vertices[tr.verts_idxs[i]].v[2]);
}
::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
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 invalid triangles: ", m_show_invalid);
int valid_triangles = std::count_if(m_triangles.begin(), m_triangles.end(),
[](const Triangle& tr) { return tr.valid; });
int valid_triangles = m_triangles.size() - m_invalid_triangles;
imgui->text("Valid triangles: " + std::to_string(valid_triangles) +
"/" + 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"))
garbage_collect();
@ -1290,9 +1343,9 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui)
::glColor3f(0.f, 0.f, 1.f);
for (int i=0; i<3; ++i)
::glVertex3f(m_vertices[tr.verts_idxs[i]][0],
m_vertices[tr.verts_idxs[i]][1],
m_vertices[tr.verts_idxs[i]][2]);
::glVertex3f(m_vertices[tr.verts_idxs[i]].v[0],
m_vertices[tr.verts_idxs[i]].v[1],
m_vertices[tr.verts_idxs[i]].v[2]);
}
::glEnd();
::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

View file

@ -33,7 +33,7 @@ public:
// stay valid, a ptr to it is saved and used.
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
int facet_start, // facet that point belongs to
const Vec3f& source, // camera position (mesh coords)
@ -41,14 +41,11 @@ public:
float radius_sqr, // squared radius of the cursor
FacetSupportType new_state); // enforcer or blocker?
void unselect_all();
// Render current selection. Transformation matrices are supposed
// to be already set.
void render(ImGuiWrapper* imgui = nullptr);
// Remove all unnecessary data (such as vertices that are not needed
// because the selection has been made larger.
// Remove all unnecessary data.
void garbage_collect();
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
@ -59,21 +56,24 @@ public:
private:
// Triangle and info about how it's split.
struct Triangle {
class Triangle {
public:
// Use TriangleSelector::push_triangle to create a new triangle.
// It increments/decrements reference counter on vertices.
Triangle(int a, int b, int c)
: verts_idxs{stl_triangle_vertex_indices(a, b, c)},
: verts_idxs{a, b, c},
state{FacetSupportType(0)},
number_of_splits{0},
special_side_idx{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};
// Children triangles (0 = no child)
// Children triangles.
std::array<int, 4> children;
// Set the division type.
@ -101,22 +101,31 @@ private:
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
std::vector<stl_vertex> m_vertices;
std::vector<Vertex> m_vertices;
std::vector<Triangle> m_triangles;
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;
// Number of original vertices and triangles.
int m_orig_size_vertices;
int m_orig_size_indices;
// Limits for stopping the recursion.
float m_max_edge_length;
int m_max_recursion_depth;
// Caches for cursor position, radius and direction.
// Cache for cursor position, radius and direction.
struct Cursor {
Vec3f center;
Vec3f source;
@ -137,6 +146,7 @@ private:
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
bool is_pointer_in_triangle(int facet_idx) const;
bool is_edge_inside_cursor(int facet_idx) const;
void push_triangle(int a, int b, int c);
};