Reworked bucket fill so that all neighboring triangles are computed based on information about triangle neighbors.

This commit is contained in:
Lukáš Hejl 2021-07-28 15:03:00 +02:00
parent 6d0e5d3b9c
commit f577350f1f
2 changed files with 139 additions and 95 deletions

View file

@ -228,7 +228,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
}
}
void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out) const
void TriangleSelector::precompute_all_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_propagated_out) const
{
assert(facet_idx < int(m_triangles.size()));
@ -236,7 +236,8 @@ void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_
if (!tr->valid())
return;
neighbors_out[facet_idx] = neighbors_propagated;
neighbors_out[facet_idx] = neighbors;
neighbors_propagated_out[facet_idx] = neighbors_propagated;
if (tr->is_split()) {
assert(this->verify_triangle_neighbors(*tr, neighbors));
@ -247,67 +248,51 @@ void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_
assert(tr->children[i] < int(m_triangles.size()));
// Recursion, deep first search over the children of this triangle.
// All children of this triangle were created by splitting a single source triangle of the original mesh.
this->precompute_all_level_neighbors_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), this->child_neighbors_propagated(*tr, neighbors_propagated, i), neighbors_out);
this->precompute_all_neighbors_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i),
this->child_neighbors_propagated(*tr, neighbors_propagated, i), neighbors_out,
neighbors_propagated_out);
}
}
}
}
std::vector<Vec3i> TriangleSelector::precompute_all_level_neighbors() const
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> TriangleSelector::precompute_all_neighbors() const
{
std::vector<Vec3i> neighbors(m_triangles.size(), Vec3i(-1, -1, -1));
std::vector<Vec3i> neighbors_propagated(m_triangles.size(), Vec3i(-1, -1, -1));
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
neighbors_propagated[facet_idx] = neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
if (m_triangles[facet_idx].is_split())
this->precompute_all_level_neighbors_recursive(facet_idx, neighbors[facet_idx], neighbors[facet_idx], neighbors);
this->precompute_all_neighbors_recursive(facet_idx, neighbors[facet_idx], neighbors_propagated[facet_idx], neighbors, neighbors_propagated);
}
return neighbors;
return std::make_pair(std::move(neighbors), std::move(neighbors_propagated));
}
bool TriangleSelector::are_triangles_touching(const int first_facet_idx, const int second_facet_idx) const
// It appends all triangles that are touching the edge (vertexi, vertexj) of the triangle.
// It doesn't append the triangles that are touching the triangle only by part of the edge that means the triangles are from lower depth.
void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const
{
std::array<Linef3, 3> sides_facet = {Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[0]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[1]].v.cast<double>()),
Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[1]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[2]].v.cast<double>()),
Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[2]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[0]].v.cast<double>())};
if (itriangle == -1)
return;
const Vec3d p0 = m_vertices[m_triangles[second_facet_idx].verts_idxs[0]].v.cast<double>();
const Vec3d p1 = m_vertices[m_triangles[second_facet_idx].verts_idxs[1]].v.cast<double>();
const Vec3d p2 = m_vertices[m_triangles[second_facet_idx].verts_idxs[2]].v.cast<double>();
auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx) -> void {
assert(subtriangle_idx == -1);
if (!m_triangles[subtriangle_idx].is_split())
touching_subtriangles_out.emplace_back(subtriangle_idx);
else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1)
append_touching_subtriangles(subtriangle_idx, vertexi, midpoint, touching_subtriangles_out);
else
append_touching_subtriangles(subtriangle_idx, vertexi, vertexj, touching_subtriangles_out);
};
for (size_t idx = 0; idx < 3; ++idx)
if (line_alg::distance_to_squared(sides_facet[idx], p0) <= EPSILON && (line_alg::distance_to_squared(sides_facet[idx], p1) <= EPSILON || line_alg::distance_to_squared(sides_facet[idx], p2) <= EPSILON))
return true;
else if (line_alg::distance_to_squared(sides_facet[idx], p1) <= EPSILON && line_alg::distance_to_squared(sides_facet[idx], p2) <= EPSILON)
return true;
std::pair<int, int> touching = this->triangle_subtriangles(itriangle, vertexi, vertexj);
if (touching.first != -1)
process_subtriangle(touching.first);
return false;
}
std::vector<int> TriangleSelector::neighboring_triangles(const int first_facet_idx, const int second_facet_idx, EnforcerBlockerType second_facet_state) const
{
assert(first_facet_idx < int(m_triangles.size()));
const Triangle *tr = &m_triangles[first_facet_idx];
if (!tr->valid())
return {};
if (!tr->is_split() && tr->get_state() == second_facet_state && (are_triangles_touching(second_facet_idx, first_facet_idx) || are_triangles_touching(first_facet_idx, second_facet_idx)))
return {first_facet_idx};
std::vector<int> neighbor_facets_out;
int num_of_children = tr->number_of_split_sides() + 1;
if (num_of_children != 1) {
for (int i = 0; i < num_of_children; ++i) {
assert(i < int(tr->children.size()));
assert(tr->children[i] < int(m_triangles.size()));
if (std::vector<int> neighbor_facets = neighboring_triangles(tr->children[i], second_facet_idx, second_facet_state); !neighbor_facets.empty())
Slic3r::append(neighbor_facets_out, std::move(neighbor_facets));
}
}
return neighbor_facets_out;
if (touching.second != -1)
process_subtriangle(touching.second);
}
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate)
@ -326,7 +311,23 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
return;
}
std::vector<Vec3i> all_level_neighbors = this->precompute_all_level_neighbors();
auto get_all_touching_triangles = [this](int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated) -> std::vector<int> {
assert(facet_idx != -1 && facet_idx < m_triangles.size());
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
std::vector<int> touching_triangles;
Vec3i vertices = {m_triangles[facet_idx].verts_idxs[0], m_triangles[facet_idx].verts_idxs[1], m_triangles[facet_idx].verts_idxs[2]};
append_touching_subtriangles(neighbors(0), vertices(1), vertices(0), touching_triangles);
append_touching_subtriangles(neighbors(1), vertices(2), vertices(1), touching_triangles);
append_touching_subtriangles(neighbors(2), vertices(0), vertices(2), touching_triangles);
for (int neighbor_idx : neighbors_propagated)
if (neighbor_idx != -1 && !m_triangles[neighbor_idx].is_split())
touching_triangles.emplace_back(neighbor_idx);
return touching_triangles;
};
auto [neighbors, neighbors_propagated] = this->precompute_all_neighbors();
std::vector<bool> visited(m_triangles.size(), false);
std::queue<int> facet_queue;
@ -338,17 +339,14 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
if (!visited[current_facet]) {
m_triangles[current_facet].select_by_seed_fill();
for (int neighbor_idx : all_level_neighbors[current_facet]) {
if (neighbor_idx < 0 || visited[neighbor_idx])
std::vector<int> touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]);
for(const int tr_idx : touching_triangles) {
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state)
continue;
if (!m_triangles[neighbor_idx].is_split()) {
if (m_triangles[neighbor_idx].get_state() == start_facet_state)
facet_queue.push(neighbor_idx);
} else {
for (int neighbor_facet_idx : neighboring_triangles(neighbor_idx, current_facet, start_facet_state))
facet_queue.push(neighbor_facet_idx);
}
assert(!m_triangles[tr_idx].is_split());
facet_queue.push(tr_idx);
}
}
@ -437,6 +435,40 @@ int TriangleSelector::neighbor_child(int itriangle, int vertexi, int vertexj, Pa
return itriangle == -1 ? -1 : this->neighbor_child(m_triangles[itriangle], vertexi, vertexj, partition);
}
std::pair<int, int> TriangleSelector::triangle_subtriangles(int itriangle, int vertexi, int vertexj) const
{
return itriangle == -1 ? std::make_pair(-1, -1) : this->triangle_subtriangles(m_triangles[itriangle], vertexi, vertexj);
}
std::pair<int, int> TriangleSelector::triangle_subtriangles(const Triangle &tr, int vertexi, int vertexj)
{
if (tr.number_of_split_sides() == 0)
// If this triangle is not split, then there is no subtriangles touching the edge.
return std::make_pair(-1, -1);
// Find the triangle edge.
int edge = tr.verts_idxs[0] == vertexi ? 0 : tr.verts_idxs[1] == vertexi ? 1 : 2;
assert(tr.verts_idxs[edge] == vertexi);
assert(tr.verts_idxs[next_idx_modulo(edge, 3)] == vertexj);
if (tr.number_of_split_sides() == 1) {
return edge == next_idx_modulo(tr.special_side(), 3) ? std::make_pair(tr.children[0], tr.children[1]) :
std::make_pair(tr.children[edge == tr.special_side() ? 0 : 1], -1);
} else if (tr.number_of_split_sides() == 2) {
return edge == next_idx_modulo(tr.special_side(), 3) ? std::make_pair(tr.children[2], -1) :
edge == tr.special_side() ? std::make_pair(tr.children[0], tr.children[1]) :
std::make_pair(tr.children[2], tr.children[0]);
} else {
assert(tr.number_of_split_sides() == 3);
assert(tr.special_side() == 0);
return edge == 0 ? std::make_pair(tr.children[0], tr.children[1]) :
edge == 1 ? std::make_pair(tr.children[1], tr.children[2]) :
std::make_pair(tr.children[2], tr.children[0]);
}
return std::make_pair(-1, -1);
}
// Return existing midpoint of CCW oriented side (vertexi, vertexj).
// If itriangle == -1 or if the side sharing (vertexi, vertexj) is not split, return -1.
int TriangleSelector::triangle_midpoint(const Triangle &tr, int vertexi, int vertexj) const
@ -524,12 +556,8 @@ Vec3i TriangleSelector::child_neighbors(const Triangle &tr, const Vec3i &neighbo
assert(child_idx >= 0 && child_idx <= tr.number_of_split_sides());
int i = tr.special_side();
int j = i + 1;
if (j >= 3)
j = 0;
int k = j + 1;
if (k >= 3)
k = 0;
int j = next_idx_modulo(i, 3);
int k = next_idx_modulo(j, 3);
Vec3i out;
switch (tr.number_of_split_sides()) {
@ -612,23 +640,28 @@ Vec3i TriangleSelector::child_neighbors(const Triangle &tr, const Vec3i &neighbo
Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors, int child_idx) const
{
int i = tr.special_side();
int j = i + 1;
if (j >= 3) j = 0;
int k = j + 1;
if (k >= 3) k = 0;
int j = next_idx_modulo(i, 3);
int k = next_idx_modulo(j, 3);
Vec3i out;
auto replace_if_not_exists = [&out](int index_to_replace, int neighbor) {
if (out(index_to_replace) == -1)
out(index_to_replace) = neighbor;
};
switch (tr.number_of_split_sides()) {
case 1:
switch (child_idx) {
case 0:
out(0) = neighbors(i);
out(1) = neighbors(j);
out(1) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::Second);
replace_if_not_exists(1, neighbors(j));
out(2) = tr.children[1];
break;
default:
assert(child_idx == 1);
out(0) = neighbors(j);
out(0) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::First);
replace_if_not_exists(0, neighbors(j));
out(1) = neighbors(k);
out(2) = tr.children[0];
break;
@ -638,20 +671,24 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec
case 2:
switch (child_idx) {
case 0:
out(0) = neighbors(i);
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::Second);
replace_if_not_exists(0, neighbors(i));
out(1) = tr.children[1];
out(2) = neighbors(k);
out(2) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::First);
replace_if_not_exists(2, neighbors(k));
break;
case 1:
assert(child_idx == 1);
out(0) = neighbors(i);
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::First);
replace_if_not_exists(0, neighbors(i));
out(1) = tr.children[2];
out(2) = tr.children[0];
break;
default:
assert(child_idx == 2);
out(0) = neighbors(j);
out(1) = neighbors(k);
out(1) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::Second);
replace_if_not_exists(1, neighbors(k));
out(2) = tr.children[1];
break;
}
@ -661,18 +698,24 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec
assert(tr.special_side() == 0);
switch (child_idx) {
case 0:
out(0) = neighbors(0);
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::Second);
replace_if_not_exists(0, neighbors(0));
out(1) = tr.children[3];
out(2) = neighbors(2);
out(2) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::First);
replace_if_not_exists(2, neighbors(2));
break;
case 1:
out(0) = neighbors(0);
out(1) = neighbors(1);
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::First);
replace_if_not_exists(0, neighbors(0));
out(1) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::Second);
replace_if_not_exists(1, neighbors(1));
out(2) = tr.children[3];
break;
case 2:
out(0) = neighbors(1);
out(1) = neighbors(2);
out(0) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::First);
replace_if_not_exists(0, neighbors(1));
out(1) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::Second);
replace_if_not_exists(1, neighbors(2));
out(2) = tr.children[3];
break;
default:
@ -886,13 +929,13 @@ void TriangleSelector::undivide_triangle(int facet_idx)
Triangle& tr = m_triangles[facet_idx];
if (tr.is_split()) {
for (int i=0; i<=tr.number_of_split_sides(); ++i) {
for (int i = 0; i <= tr.number_of_split_sides(); ++i) {
int child = tr.children[i];
Triangle &child_tr = m_triangles[child];
assert(child_tr.valid());
undivide_triangle(child);
for (int i = 0; i < 3; ++ i) {
int iv = child_tr.verts_idxs[i];
for (int j = 0; j < 3; ++j) {
int iv = child_tr.verts_idxs[j];
Vertex &v = m_vertices[iv];
assert(v.ref_cnt > 0);
if (-- v.ref_cnt == 0) {
@ -1231,7 +1274,7 @@ void TriangleSelector::get_facets_strict_recursive(
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
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(

View file

@ -22,8 +22,8 @@ public:
POINTER
};
[[nodiscard]] std::vector<Vec3i> precompute_all_level_neighbors() const;
void precompute_all_level_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out) const;
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;
// Set a limit to the edge length, below which the edge will not be split by select_patch().
// Called by select_patch() internally. Made public for debugging purposes, see TriangleSelectorGUI::render_debug().
@ -37,10 +37,6 @@ public:
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx) const;
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx, const Vec3i &neighbors) const;
[[nodiscard]] bool are_triangles_touching(int first_facet_idx, int second_facet_idx) const;
[[nodiscard]] std::vector<int> neighboring_triangles(int first_facet_idx, int second_facet_idx, EnforcerBlockerType second_facet_state) const;
// Select all triangles fully inside the circle, subdivide where needed.
void select_patch(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
@ -60,7 +56,7 @@ public:
bool propagate); // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
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);
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &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;
@ -128,11 +124,11 @@ protected:
bool is_selected_by_seed_fill() const { assert(! is_split()); return m_selected_by_seed_fill; }
// Is this triangle valid or marked to be removed?
bool valid() const throw() { return m_valid; }
bool valid() const noexcept { return m_valid; }
// Get info on how it's split.
bool is_split() const throw() { return number_of_split_sides() != 0; }
int number_of_split_sides() const throw() { return number_of_splits; }
int special_side() const throw() { assert(is_split()); return special_side_idx; }
bool is_split() const noexcept { return number_of_split_sides() != 0; }
int number_of_split_sides() const noexcept { return number_of_splits; }
int special_side() const noexcept { assert(is_split()); return special_side_idx; }
private:
friend TriangleSelector;
@ -205,7 +201,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;
int push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state = EnforcerBlockerType{0});
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
Vec3i child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
@ -221,6 +217,11 @@ private:
int triangle_midpoint(int itriangle, int vertexi, int vertexj) const;
int triangle_midpoint_or_allocate(int itriangle, int vertexi, int vertexj);
static std::pair<int, int> triangle_subtriangles(const Triangle &tr, int vertexi, int vertexj);
std::pair<int, int> triangle_subtriangles(int itriangle, int vertexi, int vertexj) const;
void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const;
#ifndef NDEBUG
bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const;
bool verify_triangle_midpoints(const Triangle& tr) const;
@ -231,7 +232,7 @@ private:
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;
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_vertices_head { -1 };