1683 lines
70 KiB
C++
1683 lines
70 KiB
C++
#include "TriangleSelector.hpp"
|
|
#include "Model.hpp"
|
|
|
|
#include <boost/container/small_vector.hpp>
|
|
|
|
#ifndef NDEBUG
|
|
#define EXPENSIVE_DEBUG_CHECKS
|
|
#endif // NDEBUG
|
|
|
|
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
|
|
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
|
{
|
|
for (int i = 0; i < 3; ++ i) {
|
|
int v1 = tr.verts_idxs[i];
|
|
int v2 = tr.verts_idxs[next_idx_modulo(i, 3)];
|
|
int vmid = this->triangle_midpoint(tr, v1, v2);
|
|
assert(vmid >= -1);
|
|
if (vmid != -1) {
|
|
Vec3f c1 = 0.5f * (m_vertices[v1].v + m_vertices[v2].v);
|
|
Vec3f c2 = m_vertices[vmid].v;
|
|
float d = (c2 - c1).norm();
|
|
assert(std::abs(d) < EPSILON);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TriangleSelector::verify_triangle_neighbors(const Triangle &tr, const Vec3i &neighbors) const
|
|
{
|
|
assert(neighbors(0) >= -1);
|
|
assert(neighbors(1) >= -1);
|
|
assert(neighbors(2) >= -1);
|
|
assert(verify_triangle_midpoints(tr));
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
if (neighbors(i) != -1) {
|
|
const Triangle &tr2 = m_triangles[neighbors(i)];
|
|
assert(verify_triangle_midpoints(tr2));
|
|
int v1 = tr.verts_idxs[i];
|
|
int v2 = tr.verts_idxs[next_idx_modulo(i, 3)];
|
|
assert(tr2.verts_idxs[0] == v1 || tr2.verts_idxs[1] == v1 || tr2.verts_idxs[2] == v1);
|
|
int j = tr2.verts_idxs[0] == v1 ? 0 : tr2.verts_idxs[1] == v1 ? 1 : 2;
|
|
assert(tr2.verts_idxs[j] == v1);
|
|
assert(tr2.verts_idxs[prev_idx_modulo(j, 3)] == v2);
|
|
}
|
|
return true;
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
// sides_to_split==-1 : just restore previous split
|
|
void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx)
|
|
{
|
|
assert(sides_to_split >= 0 && sides_to_split <= 3);
|
|
assert(special_side_idx >= 0 && special_side_idx < 3);
|
|
assert(sides_to_split == 1 || sides_to_split == 2 || special_side_idx == 0);
|
|
this->number_of_splits = char(sides_to_split);
|
|
this->special_side_idx = char(special_side_idx);
|
|
}
|
|
|
|
inline bool is_point_inside_triangle(const Vec3f &pt, const Vec3f &p1, const Vec3f &p2, const Vec3f &p3)
|
|
{
|
|
// Real-time collision detection, Ericson, Chapter 3.4
|
|
auto barycentric = [&pt, &p1, &p2, &p3]() -> Vec3f {
|
|
std::array<Vec3f, 3> v = {p2 - p1, p3 - p1, pt - p1};
|
|
float d00 = v[0].dot(v[0]);
|
|
float d01 = v[0].dot(v[1]);
|
|
float d11 = v[1].dot(v[1]);
|
|
float d20 = v[2].dot(v[0]);
|
|
float d21 = v[2].dot(v[1]);
|
|
float denom = d00 * d11 - d01 * d01;
|
|
|
|
Vec3f barycentric_cords(1.f, (d11 * d20 - d01 * d21) / denom, (d00 * d21 - d01 * d20) / denom);
|
|
barycentric_cords.x() = barycentric_cords.x() - barycentric_cords.y() - barycentric_cords.z();
|
|
return barycentric_cords;
|
|
};
|
|
|
|
Vec3f barycentric_cords = barycentric();
|
|
return std::all_of(begin(barycentric_cords), end(barycentric_cords), [](float cord) { return 0.f <= cord && cord <= 1.0; });
|
|
}
|
|
|
|
int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx, const Vec3i &neighbors) const
|
|
{
|
|
assert(facet_idx < int(m_triangles.size()));
|
|
const Triangle *tr = &m_triangles[facet_idx];
|
|
if (!tr->valid())
|
|
return -1;
|
|
|
|
if (!tr->is_split()) {
|
|
if (const std::array<int, 3> &t_vert = m_triangles[facet_idx].verts_idxs; is_point_inside_triangle(hit, m_vertices[t_vert[0]].v, m_vertices[t_vert[1]].v, m_vertices[t_vert[2]].v))
|
|
return facet_idx;
|
|
|
|
return -1;
|
|
}
|
|
|
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
|
|
|
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()));
|
|
// 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.
|
|
|
|
const std::array<int, 3> &t_vert = m_triangles[tr->children[i]].verts_idxs;
|
|
if (is_point_inside_triangle(hit, m_vertices[t_vert[0]].v, m_vertices[t_vert[1]].v, m_vertices[t_vert[2]].v))
|
|
return this->select_unsplit_triangle(hit, tr->children[i], this->child_neighbors(*tr, neighbors, i));
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) const
|
|
{
|
|
assert(facet_idx < int(m_triangles.size()));
|
|
if (!m_triangles[facet_idx].valid())
|
|
return -1;
|
|
|
|
Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
|
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
|
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
|
}
|
|
|
|
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|
const Vec3f& source, float radius,
|
|
CursorType cursor_type, EnforcerBlockerType new_state,
|
|
const Transform3d& trafo, bool triangle_splitting)
|
|
{
|
|
assert(facet_start < m_orig_size_indices);
|
|
|
|
// Save current cursor center, squared radius and camera direction, so we don't
|
|
// have to pass it around.
|
|
m_cursor = Cursor(hit, source, radius, cursor_type, trafo);
|
|
|
|
// In case user changed cursor size since last time, update triangle edge limit.
|
|
// It is necessary to compare the internal radius in m_cursor! radius is in
|
|
// world coords and does not change after scaling.
|
|
if (m_old_cursor_radius_sqr != m_cursor.radius_sqr) {
|
|
set_edge_limit(std::sqrt(m_cursor.radius_sqr) / 5.f);
|
|
m_old_cursor_radius_sqr = m_cursor.radius_sqr;
|
|
}
|
|
|
|
// Now start with the facet the pointer points to and check all adjacent facets.
|
|
std::vector<int> facets_to_check;
|
|
facets_to_check.reserve(16);
|
|
facets_to_check.emplace_back(facet_start);
|
|
// Keep track of facets of the original mesh we already processed.
|
|
std::vector<bool> visited(m_orig_size_indices, false);
|
|
// Breadth-first search around the hit point. facets_to_check may grow significantly large.
|
|
// Head of the bread-first facets_to_check FIFO.
|
|
int facet_idx = 0;
|
|
while (facet_idx < int(facets_to_check.size())) {
|
|
int facet = facets_to_check[facet_idx];
|
|
if (! visited[facet]) {
|
|
if (select_triangle(facet, new_state, triangle_splitting)) {
|
|
// add neighboring facets to list to be proccessed later
|
|
for (int neighbor_idx : m_mesh->stl.neighbors_start[facet].neighbor) {
|
|
if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
|
|
facets_to_check.push_back(neighbor_idx);
|
|
}
|
|
}
|
|
}
|
|
visited[facet] = true;
|
|
++facet_idx;
|
|
}
|
|
}
|
|
|
|
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, float seed_fill_angle)
|
|
{
|
|
assert(facet_start < m_orig_size_indices);
|
|
|
|
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill.
|
|
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill())
|
|
return;
|
|
|
|
this->seed_fill_unselect_all_triangles();
|
|
|
|
std::vector<bool> visited(m_triangles.size(), false);
|
|
std::queue<int> facet_queue;
|
|
facet_queue.push(facet_start);
|
|
|
|
const double facet_angle_limit = cos(Geometry::deg2rad(seed_fill_angle)) - EPSILON;
|
|
|
|
// Depth-first traversal of neighbors of the face hit by the ray thrown from the mouse cursor.
|
|
while (!facet_queue.empty()) {
|
|
int current_facet = facet_queue.front();
|
|
facet_queue.pop();
|
|
|
|
if (!visited[current_facet]) {
|
|
if (m_triangles[current_facet].is_split()) {
|
|
for (int split_triangle_idx = 0; split_triangle_idx <= m_triangles[current_facet].number_of_split_sides(); ++split_triangle_idx) {
|
|
assert(split_triangle_idx < int(m_triangles[current_facet].children.size()));
|
|
assert(m_triangles[current_facet].children[split_triangle_idx] < int(m_triangles.size()));
|
|
if (int child = m_triangles[current_facet].children[split_triangle_idx]; !visited[child])
|
|
// Child triangle shares normal with its parent. Select it.
|
|
facet_queue.push(child);
|
|
}
|
|
} else
|
|
m_triangles[current_facet].select_by_seed_fill();
|
|
|
|
if (current_facet < m_orig_size_indices)
|
|
// Propagate over the original triangles.
|
|
for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
|
|
assert(neighbor_idx >= -1);
|
|
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.
|
|
const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
|
|
const Vec3f &n2 = m_mesh->stl.facet_start[m_triangles[current_facet].source_triangle].normal;
|
|
if (std::clamp(n1.dot(n2), 0.f, 1.f) >= facet_angle_limit)
|
|
facet_queue.push(neighbor_idx);
|
|
}
|
|
}
|
|
}
|
|
visited[current_facet] = true;
|
|
}
|
|
}
|
|
|
|
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()));
|
|
|
|
const Triangle *tr = &m_triangles[facet_idx];
|
|
if (!tr->valid())
|
|
return;
|
|
|
|
neighbors_out[facet_idx] = neighbors;
|
|
neighbors_propagated_out[facet_idx] = neighbors_propagated;
|
|
if (tr->is_split()) {
|
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
|
|
|
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()));
|
|
// 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_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::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_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_neighbors_recursive(facet_idx, neighbors[facet_idx], neighbors_propagated[facet_idx], neighbors, neighbors_propagated);
|
|
}
|
|
return std::make_pair(std::move(neighbors), std::move(neighbors_propagated));
|
|
}
|
|
|
|
// 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
|
|
{
|
|
if (itriangle == -1)
|
|
return;
|
|
|
|
auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx, Partition partition) -> 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, partition == Partition::First ? vertexi : midpoint, partition == Partition::First ? midpoint : vertexj, touching_subtriangles_out);
|
|
else
|
|
append_touching_subtriangles(subtriangle_idx, vertexi, vertexj, touching_subtriangles_out);
|
|
};
|
|
|
|
std::pair<int, int> touching = this->triangle_subtriangles(itriangle, vertexi, vertexj);
|
|
if (touching.first != -1)
|
|
process_subtriangle(touching.first, Partition::First);
|
|
|
|
if (touching.second != -1)
|
|
process_subtriangle(touching.second, Partition::Second);
|
|
}
|
|
|
|
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate)
|
|
{
|
|
int start_facet_idx = select_unsplit_triangle(hit, facet_start);
|
|
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill.
|
|
if (start_facet_idx == -1 || m_triangles[start_facet_idx].is_selected_by_seed_fill())
|
|
return;
|
|
|
|
assert(!m_triangles[start_facet_idx].is_split());
|
|
EnforcerBlockerType start_facet_state = m_triangles[start_facet_idx].get_state();
|
|
this->seed_fill_unselect_all_triangles();
|
|
|
|
if (!propagate) {
|
|
m_triangles[start_facet_idx].select_by_seed_fill();
|
|
return;
|
|
}
|
|
|
|
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;
|
|
|
|
facet_queue.push(start_facet_idx);
|
|
while (!facet_queue.empty()) {
|
|
int current_facet = facet_queue.front();
|
|
facet_queue.pop();
|
|
assert(!m_triangles[current_facet].is_split());
|
|
|
|
if (!visited[current_facet]) {
|
|
m_triangles[current_facet].select_by_seed_fill();
|
|
|
|
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;
|
|
|
|
assert(!m_triangles[tr_idx].is_split());
|
|
facet_queue.push(tr_idx);
|
|
}
|
|
}
|
|
|
|
visited[current_facet] = true;
|
|
}
|
|
}
|
|
|
|
// Selects either the whole triangle (discarding any children it had), or divides
|
|
// the triangle recursively, selecting just subtriangles truly inside the circle.
|
|
// This is done by an actual recursive call. Returns false if the triangle is
|
|
// outside the cursor.
|
|
// Called by select_patch() and by itself.
|
|
bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting)
|
|
{
|
|
assert(facet_idx < int(m_triangles.size()));
|
|
|
|
if (! m_triangles[facet_idx].valid())
|
|
return false;
|
|
|
|
Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
|
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
|
|
|
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
|
|
return false;
|
|
|
|
// 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);
|
|
|
|
#ifdef EXPENSIVE_DEBUG_CHECKS
|
|
// 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(); }));
|
|
#endif // EXPENSIVE_DEBUG_CHECKS
|
|
|
|
// Do garbage collection maybe?
|
|
if (2*m_invalid_triangles > int(m_triangles.size()))
|
|
garbage_collect();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return child of itriangle at a CCW oriented side (vertexi, vertexj), either first or 2nd part.
|
|
// If the side sharing (vertexi, vertexj) is not split, return -1.
|
|
int TriangleSelector::neighbor_child(const Triangle &tr, int vertexi, int vertexj, Partition partition) const
|
|
{
|
|
if (tr.number_of_split_sides() == 0)
|
|
// If this triangle is not split, then there is no upper / lower subtriangle sharing the edge.
|
|
return -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);
|
|
|
|
int child_idx;
|
|
if (tr.number_of_split_sides() == 1) {
|
|
if (edge != next_idx_modulo(tr.special_side(), 3))
|
|
// A child may or may not be split at this side.
|
|
return this->neighbor_child(m_triangles[tr.children[edge == tr.special_side() ? 0 : 1]], vertexi, vertexj, partition);
|
|
child_idx = partition == Partition::First ? 0 : 1;
|
|
} else if (tr.number_of_split_sides() == 2) {
|
|
if (edge == next_idx_modulo(tr.special_side(), 3))
|
|
// A child may or may not be split at this side.
|
|
return this->neighbor_child(m_triangles[tr.children[2]], vertexi, vertexj, partition);
|
|
child_idx = edge == tr.special_side() ?
|
|
(partition == Partition::First ? 0 : 1) :
|
|
(partition == Partition::First ? 2 : 0);
|
|
} else {
|
|
assert(tr.number_of_split_sides() == 3);
|
|
assert(tr.special_side() == 0);
|
|
switch(edge) {
|
|
case 0: child_idx = partition == Partition::First ? 0 : 1; break;
|
|
case 1: child_idx = partition == Partition::First ? 1 : 2; break;
|
|
default: assert(edge == 2);
|
|
child_idx = partition == Partition::First ? 2 : 0; break;
|
|
}
|
|
}
|
|
return tr.children[child_idx];
|
|
}
|
|
|
|
// Return child of itriangle at a CCW oriented side (vertexi, vertexj), either first or 2nd part.
|
|
// If itriangle == -1 or if the side sharing (vertexi, vertexj) is not split, return -1.
|
|
int TriangleSelector::neighbor_child(int itriangle, int vertexi, int vertexj, Partition partition) const
|
|
{
|
|
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) : Slic3r::TriangleSelector::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
|
|
{
|
|
if (tr.number_of_split_sides() == 0)
|
|
// If this triangle is not split, then there is no upper / lower subtriangle sharing the edge.
|
|
return -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) ?
|
|
m_triangles[tr.children[0]].verts_idxs[2] :
|
|
this->triangle_midpoint(m_triangles[tr.children[edge == tr.special_side() ? 0 : 1]], vertexi, vertexj);
|
|
} else if (tr.number_of_split_sides() == 2) {
|
|
return edge == next_idx_modulo(tr.special_side(), 3) ?
|
|
this->triangle_midpoint(m_triangles[tr.children[2]], vertexi, vertexj) :
|
|
edge == tr.special_side() ?
|
|
m_triangles[tr.children[0]].verts_idxs[1] :
|
|
m_triangles[tr.children[1]].verts_idxs[2];
|
|
} else {
|
|
assert(tr.number_of_split_sides() == 3);
|
|
assert(tr.special_side() == 0);
|
|
return
|
|
(edge == 0) ? m_triangles[tr.children[0]].verts_idxs[1] :
|
|
(edge == 1) ? m_triangles[tr.children[1]].verts_idxs[2] :
|
|
m_triangles[tr.children[2]].verts_idxs[2];
|
|
}
|
|
}
|
|
|
|
// 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(int itriangle, int vertexi, int vertexj) const
|
|
{
|
|
return itriangle == -1 ? -1 : this->triangle_midpoint(m_triangles[itriangle], vertexi, vertexj);
|
|
}
|
|
|
|
int TriangleSelector::triangle_midpoint_or_allocate(int itriangle, int vertexi, int vertexj)
|
|
{
|
|
int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj);
|
|
if (midpoint == -1) {
|
|
Vec3f c = 0.5f * (m_vertices[vertexi].v + m_vertices[vertexj].v);
|
|
#ifdef EXPENSIVE_DEBUG_CHECKS
|
|
// Verify that the vertex is really a new one.
|
|
auto it = std::find_if(m_vertices.begin(), m_vertices.end(), [c](const Vertex &v) {
|
|
return v.ref_cnt > 0 && (v.v - c).norm() < EPSILON; });
|
|
assert(it == m_vertices.end());
|
|
#endif // EXPENSIVE_DEBUG_CHECKS
|
|
// Allocate a new vertex, possibly reusing the free list.
|
|
if (m_free_vertices_head == -1) {
|
|
// Allocate a new vertex.
|
|
midpoint = int(m_vertices.size());
|
|
m_vertices.emplace_back(c);
|
|
} else {
|
|
// Reuse a vertex from the free list.
|
|
assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
|
|
midpoint = m_free_vertices_head;
|
|
memcpy(&m_free_vertices_head, &m_vertices[midpoint].v[0], sizeof(m_free_vertices_head));
|
|
assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
|
|
m_vertices[midpoint].v = c;
|
|
}
|
|
assert(m_vertices[midpoint].ref_cnt == 0);
|
|
} else {
|
|
#ifndef NDEBUG
|
|
Vec3f c1 = 0.5f * (m_vertices[vertexi].v + m_vertices[vertexj].v);
|
|
Vec3f c2 = m_vertices[midpoint].v;
|
|
float d = (c2 - c1).norm();
|
|
assert(std::abs(d) < EPSILON);
|
|
#endif // NDEBUG
|
|
assert(m_vertices[midpoint].ref_cnt > 0);
|
|
}
|
|
return midpoint;
|
|
}
|
|
|
|
// Return neighbors of ith child of a triangle given neighbors of the triangle.
|
|
// Returns -1 if such a neighbor does not exist at all, or it does not exist
|
|
// at the same depth as the ith child.
|
|
// Using the same splitting strategy as TriangleSelector::split_triangle()
|
|
Vec3i TriangleSelector::child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const
|
|
{
|
|
assert(this->verify_triangle_neighbors(tr, neighbors));
|
|
|
|
assert(child_idx >= 0 && child_idx <= tr.number_of_split_sides());
|
|
int i = tr.special_side();
|
|
int j = next_idx_modulo(i, 3);
|
|
int k = next_idx_modulo(j, 3);
|
|
|
|
Vec3i out;
|
|
switch (tr.number_of_split_sides()) {
|
|
case 1:
|
|
switch (child_idx) {
|
|
case 0:
|
|
out(0) = neighbors(i);
|
|
out(1) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::Second);
|
|
out(2) = tr.children[1];
|
|
break;
|
|
default:
|
|
assert(child_idx == 1);
|
|
out(0) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::First);
|
|
out(1) = neighbors(k);
|
|
out(2) = tr.children[0];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
switch (child_idx) {
|
|
case 0:
|
|
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::Second);
|
|
out(1) = tr.children[1];
|
|
out(2) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::First);
|
|
break;
|
|
case 1:
|
|
assert(child_idx == 1);
|
|
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::First);
|
|
out(1) = tr.children[2];
|
|
out(2) = tr.children[0];
|
|
break;
|
|
default:
|
|
assert(child_idx == 2);
|
|
out(0) = neighbors(j);
|
|
out(1) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::Second);
|
|
out(2) = tr.children[1];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
assert(tr.special_side() == 0);
|
|
switch (child_idx) {
|
|
case 0:
|
|
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::Second);
|
|
out(1) = tr.children[3];
|
|
out(2) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::First);
|
|
break;
|
|
case 1:
|
|
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::First);
|
|
out(1) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::Second);
|
|
out(2) = tr.children[3];
|
|
break;
|
|
case 2:
|
|
out(0) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::First);
|
|
out(1) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::Second);
|
|
out(2) = tr.children[3];
|
|
break;
|
|
default:
|
|
assert(child_idx == 3);
|
|
out(0) = tr.children[1];
|
|
out(1) = tr.children[2];
|
|
out(2) = tr.children[0];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
assert(this->verify_triangle_neighbors(tr, neighbors));
|
|
assert(this->verify_triangle_neighbors(m_triangles[tr.children[child_idx]], out));
|
|
return out;
|
|
}
|
|
|
|
// Return neighbors of the ith child of a triangle given neighbors of the triangle.
|
|
// If such a neighbor doesn't exist, return the neighbor from the previous depth.
|
|
Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors, int child_idx) const
|
|
{
|
|
int i = tr.special_side();
|
|
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) = 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) = 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;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
switch (child_idx) {
|
|
case 0:
|
|
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) = 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) = 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) = 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;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
assert(tr.special_side() == 0);
|
|
switch (child_idx) {
|
|
case 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) = 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) = 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) = 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:
|
|
assert(child_idx == 3);
|
|
out(0) = tr.children[1];
|
|
out(1) = tr.children[2];
|
|
out(2) = tr.children[0];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default: assert(false);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting)
|
|
{
|
|
assert(facet_idx < int(m_triangles.size()));
|
|
|
|
Triangle* tr = &m_triangles[facet_idx];
|
|
if (! tr->valid())
|
|
return false;
|
|
|
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
|
|
|
int num_of_inside_vertices = vertices_inside(facet_idx);
|
|
|
|
if (num_of_inside_vertices == 0
|
|
&& ! is_pointer_in_triangle(facet_idx)
|
|
&& ! is_edge_inside_cursor(facet_idx))
|
|
return false;
|
|
|
|
if (num_of_inside_vertices == 3) {
|
|
// dump any subdivision and select whole triangle
|
|
undivide_triangle(facet_idx);
|
|
tr->set_state(type);
|
|
} else {
|
|
// the triangle is partially inside, let's recursively divide it
|
|
// (if not already) and try selecting its children.
|
|
|
|
if (! tr->is_split() && tr->get_state() == type) {
|
|
// This is leaf triangle that is already of correct type as a whole.
|
|
// No need to split, all children would end up selected anyway.
|
|
return true;
|
|
}
|
|
|
|
if (triangle_splitting)
|
|
split_triangle(facet_idx, neighbors);
|
|
else if (!m_triangles[facet_idx].is_split())
|
|
m_triangles[facet_idx].set_state(type);
|
|
tr = &m_triangles[facet_idx]; // might have been invalidated by split_triangle().
|
|
|
|
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()));
|
|
// 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.
|
|
select_triangle_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), type, triangle_splitting);
|
|
tr = &m_triangles[facet_idx]; // might have been invalidated
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state)
|
|
{
|
|
assert(facet_idx < m_orig_size_indices);
|
|
undivide_triangle(facet_idx);
|
|
assert(! m_triangles[facet_idx].is_split());
|
|
m_triangles[facet_idx].set_state(state);
|
|
}
|
|
|
|
// called by select_patch()->select_triangle()...select_triangle()
|
|
// to decide which sides of the traingle to split and to actually split it calling set_division() and perform_split().
|
|
void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
|
{
|
|
if (m_triangles[facet_idx].is_split()) {
|
|
// The triangle is divided already.
|
|
return;
|
|
}
|
|
|
|
Triangle* tr = &m_triangles[facet_idx];
|
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
|
|
|
EnforcerBlockerType old_type = tr->get_state();
|
|
|
|
// If we got here, we are about to actually split the triangle.
|
|
const double limit_squared = m_edge_limit_sqr;
|
|
|
|
std::array<int, 3>& facet = tr->verts_idxs;
|
|
std::array<const stl_vertex*, 3> pts = { &m_vertices[facet[0]].v,
|
|
&m_vertices[facet[1]].v,
|
|
&m_vertices[facet[2]].v};
|
|
std::array<stl_vertex, 3> pts_transformed; // must stay in scope of pts !!!
|
|
|
|
// In case the object is non-uniformly scaled, transform the
|
|
// points to world coords.
|
|
if (! m_cursor.uniform_scaling) {
|
|
for (size_t i=0; i<pts.size(); ++i) {
|
|
pts_transformed[i] = m_cursor.trafo * (*pts[i]);
|
|
pts[i] = &pts_transformed[i];
|
|
}
|
|
}
|
|
|
|
std::array<double, 3> sides = {(*pts[2] - *pts[1]).squaredNorm(),
|
|
(*pts[0] - *pts[2]).squaredNorm(),
|
|
(*pts[1] - *pts[0]).squaredNorm()};
|
|
|
|
boost::container::small_vector<int, 3> sides_to_split;
|
|
int side_to_keep = -1;
|
|
for (int pt_idx = 0; pt_idx<3; ++pt_idx) {
|
|
if (sides[pt_idx] > limit_squared)
|
|
sides_to_split.push_back(pt_idx);
|
|
else
|
|
side_to_keep = pt_idx;
|
|
}
|
|
if (sides_to_split.empty()) {
|
|
// This shall be unselected.
|
|
tr->set_division(0, 0);
|
|
return;
|
|
}
|
|
|
|
// Save how the triangle will be split. Second argument makes sense only for one
|
|
// or two split sides, otherwise the value is ignored.
|
|
tr->set_division(int(sides_to_split.size()),
|
|
sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]);
|
|
|
|
perform_split(facet_idx, neighbors, old_type);
|
|
}
|
|
|
|
|
|
|
|
// Is pointer in a triangle?
|
|
bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
|
|
{
|
|
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;
|
|
return m_cursor.is_pointer_in_triangle(p1, p2, p3);
|
|
}
|
|
|
|
|
|
|
|
// Determine whether this facet is potentially visible (still can be obscured).
|
|
bool TriangleSelector::faces_camera(int facet) const
|
|
{
|
|
assert(facet < m_orig_size_indices);
|
|
// The normal is cached in mesh->stl, use it.
|
|
Vec3f normal = m_mesh->stl.facet_start[facet].normal;
|
|
|
|
if (! m_cursor.uniform_scaling) {
|
|
// Transform the normal into world coords.
|
|
normal = m_cursor.trafo_normal * normal;
|
|
}
|
|
return (normal.dot(m_cursor.dir) < 0.);
|
|
}
|
|
|
|
|
|
// How many vertices of a triangle are inside the circle?
|
|
int TriangleSelector::vertices_inside(int facet_idx) const
|
|
{
|
|
int inside = 0;
|
|
for (size_t i=0; i<3; ++i) {
|
|
if (m_cursor.is_mesh_point_inside(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v))
|
|
++inside;
|
|
}
|
|
return inside;
|
|
}
|
|
|
|
|
|
// Is edge inside cursor?
|
|
bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const
|
|
{
|
|
std::array<Vec3f, 3> pts;
|
|
for (int i=0; i<3; ++i) {
|
|
pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v;
|
|
if (! m_cursor.uniform_scaling)
|
|
pts[i] = m_cursor.trafo * pts[i];
|
|
}
|
|
|
|
const Vec3f& p = m_cursor.center;
|
|
|
|
for (int side = 0; side < 3; ++side) {
|
|
const Vec3f& a = pts[side];
|
|
const Vec3f& b = pts[side<2 ? side+1 : 0];
|
|
Vec3f s = (b-a).normalized();
|
|
float t = (p-a).dot(s);
|
|
Vec3f vector = a+t*s - p;
|
|
|
|
// vector is 3D vector from center to the intersection. What we want to
|
|
// measure is length of its projection onto plane perpendicular to dir.
|
|
float dist_sqr = vector.squaredNorm() - std::pow(vector.dot(m_cursor.dir), 2.f);
|
|
if (dist_sqr < m_cursor.radius_sqr && t>=0.f && t<=(b-a).norm())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// Recursively remove all subtriangles.
|
|
void TriangleSelector::undivide_triangle(int facet_idx)
|
|
{
|
|
assert(facet_idx < int(m_triangles.size()));
|
|
Triangle& tr = m_triangles[facet_idx];
|
|
|
|
if (tr.is_split()) {
|
|
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 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) {
|
|
// Release this vertex.
|
|
// Chain released vertices into a linked list through ref_cnt.
|
|
assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
|
|
memcpy(&m_vertices[iv].v[0], &m_free_vertices_head, sizeof(m_free_vertices_head));
|
|
m_free_vertices_head = iv;
|
|
assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
|
|
}
|
|
}
|
|
// Chain released triangles into a linked list through children[0].
|
|
assert(child_tr.valid());
|
|
child_tr.m_valid = false;
|
|
assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
|
|
assert(m_free_triangles_head == -1 || ! m_triangles[m_free_triangles_head].valid());
|
|
child_tr.children[0] = m_free_triangles_head;
|
|
m_free_triangles_head = child;
|
|
assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
|
|
++m_invalid_triangles;
|
|
}
|
|
tr.set_division(0, 0); // not split
|
|
}
|
|
}
|
|
|
|
|
|
void TriangleSelector::remove_useless_children(int facet_idx)
|
|
{
|
|
// Check that all children are leafs of the same type. If not, try to
|
|
// make them (recursive call). Remove them if sucessful.
|
|
|
|
assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid());
|
|
Triangle& tr = m_triangles[facet_idx];
|
|
|
|
if (! tr.is_split()) {
|
|
// This is a leaf, there nothing to do. This can happen during the
|
|
// first (non-recursive call). Shouldn't otherwise.
|
|
return;
|
|
}
|
|
|
|
// Call this for all non-leaf children.
|
|
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
|
|
assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid());
|
|
if (m_triangles[tr.children[child_idx]].is_split())
|
|
remove_useless_children(tr.children[child_idx]);
|
|
}
|
|
|
|
|
|
// Return if a child is not leaf or two children differ in type.
|
|
EnforcerBlockerType first_child_type = EnforcerBlockerType::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;
|
|
if (child_idx == 0)
|
|
first_child_type = m_triangles[tr.children[0]].get_state();
|
|
else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type)
|
|
return;
|
|
}
|
|
|
|
// If we got here, the children can be removed.
|
|
undivide_triangle(facet_idx);
|
|
tr.set_state(first_child_type);
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
for (int i = m_orig_size_indices; i<int(m_triangles.size()); ++i)
|
|
if (m_triangles[i].valid())
|
|
new_triangle_indices[i] = new_idx ++;
|
|
|
|
// 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 ++;
|
|
}
|
|
|
|
// We can remove all invalid triangles and vertices that are no longer referenced.
|
|
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) {
|
|
assert(tr.valid());
|
|
|
|
if (tr.is_split()) {
|
|
// There are children. Update their indices.
|
|
for (int j=0; j<=tr.number_of_split_sides(); ++j) {
|
|
assert(new_triangle_indices[tr.children[j]] != -1);
|
|
tr.children[j] = new_triangle_indices[tr.children[j]];
|
|
}
|
|
}
|
|
|
|
// 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];
|
|
}
|
|
}
|
|
}
|
|
|
|
m_invalid_triangles = 0;
|
|
m_free_triangles_head = -1;
|
|
m_free_vertices_head = -1;
|
|
}
|
|
|
|
TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
|
|
: m_mesh{&mesh}
|
|
{
|
|
reset();
|
|
}
|
|
|
|
|
|
void TriangleSelector::reset()
|
|
{
|
|
m_vertices.clear();
|
|
m_triangles.clear();
|
|
m_invalid_triangles = 0;
|
|
m_free_triangles_head = -1;
|
|
m_free_vertices_head = -1;
|
|
m_vertices.reserve(m_mesh->its.vertices.size());
|
|
for (const stl_vertex& vert : m_mesh->its.vertices)
|
|
m_vertices.emplace_back(vert);
|
|
m_triangles.reserve(m_mesh->its.indices.size());
|
|
for (size_t i = 0; i < m_mesh->its.indices.size(); ++i) {
|
|
const stl_triangle_vertex_indices &ind = m_mesh->its.indices[i];
|
|
push_triangle(ind[0], ind[1], ind[2], int(i));
|
|
}
|
|
m_orig_size_vertices = int(m_vertices.size());
|
|
m_orig_size_indices = int(m_triangles.size());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TriangleSelector::set_edge_limit(float edge_limit)
|
|
{
|
|
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
|
|
}
|
|
|
|
|
|
|
|
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state)
|
|
{
|
|
for (int i : {a, b, c}) {
|
|
assert(i >= 0 && i < int(m_vertices.size()));
|
|
++m_vertices[i].ref_cnt;
|
|
}
|
|
int idx;
|
|
if (m_free_triangles_head == -1) {
|
|
// Allocate a new triangle.
|
|
assert(m_invalid_triangles == 0);
|
|
idx = int(m_triangles.size());
|
|
m_triangles.emplace_back(a, b, c, source_triangle, state);
|
|
} else {
|
|
// Reuse triangle from the free list.
|
|
assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
|
|
assert(! m_triangles[m_free_triangles_head].valid());
|
|
assert(m_invalid_triangles > 0);
|
|
idx = m_free_triangles_head;
|
|
m_free_triangles_head = m_triangles[idx].children[0];
|
|
-- m_invalid_triangles;
|
|
assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
|
|
assert(m_free_triangles_head == -1 || ! m_triangles[m_free_triangles_head].valid());
|
|
assert(m_invalid_triangles >= 0);
|
|
assert((m_invalid_triangles == 0) == (m_free_triangles_head == -1));
|
|
m_triangles[idx] = {a, b, c, source_triangle, state};
|
|
}
|
|
assert(m_triangles[idx].valid());
|
|
return idx;
|
|
}
|
|
|
|
// called by deserialize() and select_patch()->select_triangle()->...select_triangle()->split_triangle()
|
|
// Split a triangle based on Triangle::number_of_split_sides() and Triangle::special_side()
|
|
// by allocating child triangles and midpoint vertices.
|
|
// Midpoint vertices are possibly reused by traversing children of neighbor triangles.
|
|
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
|
|
{
|
|
// Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
|
|
{
|
|
size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
|
|
if (m_triangles.capacity() < num_triangles_new)
|
|
m_triangles.reserve(next_highest_power_of_2(num_triangles_new));
|
|
}
|
|
|
|
Triangle &tr = m_triangles[facet_idx];
|
|
assert(tr.is_split());
|
|
|
|
// indices of triangle vertices
|
|
#ifdef NDEBUG
|
|
boost::container::small_vector<int, 6> verts_idxs;
|
|
#else // NDEBUG
|
|
// For easier debugging.
|
|
std::vector<int> verts_idxs;
|
|
verts_idxs.reserve(6);
|
|
#endif // NDEBUG
|
|
for (int j=0, idx = tr.special_side(); j<3; ++j, idx = next_idx_modulo(idx, 3))
|
|
verts_idxs.push_back(tr.verts_idxs[idx]);
|
|
|
|
auto get_alloc_vertex = [this, &neighbors, &verts_idxs](int edge, int i1, int i2) -> int {
|
|
return this->triangle_midpoint_or_allocate(neighbors(edge), verts_idxs[i1], verts_idxs[i2]);
|
|
};
|
|
|
|
int ichild = 0;
|
|
switch (tr.number_of_split_sides()) {
|
|
case 1:
|
|
verts_idxs.insert(verts_idxs.begin()+2, get_alloc_vertex(next_idx_modulo(tr.special_side(), 3), 2, 1));
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2], tr.source_triangle, old_state);
|
|
tr.children[ichild ] = push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0], tr.source_triangle, old_state);
|
|
break;
|
|
|
|
case 2:
|
|
verts_idxs.insert(verts_idxs.begin()+1, get_alloc_vertex(tr.special_side(), 1, 0));
|
|
verts_idxs.insert(verts_idxs.begin()+4, get_alloc_vertex(prev_idx_modulo(tr.special_side(), 3), 0, 3));
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4], tr.source_triangle, old_state);
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4], tr.source_triangle, old_state);
|
|
tr.children[ichild ] = push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4], tr.source_triangle, old_state);
|
|
break;
|
|
|
|
case 3:
|
|
assert(tr.special_side() == 0);
|
|
verts_idxs.insert(verts_idxs.begin()+1, get_alloc_vertex(0, 1, 0));
|
|
verts_idxs.insert(verts_idxs.begin()+3, get_alloc_vertex(1, 3, 2));
|
|
verts_idxs.insert(verts_idxs.begin()+5, get_alloc_vertex(2, 0, 4));
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5], tr.source_triangle, old_state);
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3], tr.source_triangle, old_state);
|
|
tr.children[ichild ++] = push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5], tr.source_triangle, old_state);
|
|
tr.children[ichild ] = push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5], tr.source_triangle, old_state);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
assert(this->verify_triangle_neighbors(tr, neighbors));
|
|
for (int i = 0; i <= tr.number_of_split_sides(); ++i) {
|
|
Vec3i n = this->child_neighbors(tr, neighbors, i);
|
|
assert(this->verify_triangle_neighbors(m_triangles[tr.children[i]], n));
|
|
}
|
|
#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 out;
|
|
std::vector<int> vertex_map(m_vertices.size(), -1);
|
|
for (const Triangle& tr : m_triangles) {
|
|
if (tr.valid() && ! tr.is_split() && tr.get_state() == state) {
|
|
stl_triangle_vertex_indices indices;
|
|
for (int i=0; i<3; ++i) {
|
|
int j = tr.verts_idxs[i];
|
|
if (vertex_map[j] == -1) {
|
|
vertex_map[j] = int(out.vertices.size());
|
|
out.vertices.emplace_back(m_vertices[j].v);
|
|
}
|
|
indices[i] = vertex_map[j];
|
|
}
|
|
out.indices.emplace_back(indices);
|
|
}
|
|
}
|
|
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 (size_t 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);
|
|
switch (splits) {
|
|
case 0:
|
|
// Just emit this triangle.
|
|
out_triangles.emplace_back(vertices(0), vertices(1), vertices(2));
|
|
break;
|
|
case 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(k), vertices(j), Partition::Second),
|
|
-1 },
|
|
out_triangles);
|
|
this->get_facets_split_by_tjoints(
|
|
{ midpoints(j), vertices(k), vertices(i) },
|
|
{ this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::First),
|
|
neighbors(k),
|
|
-1 },
|
|
out_triangles);
|
|
break;
|
|
}
|
|
case 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);
|
|
break;
|
|
}
|
|
default:
|
|
assert(splits == 3);
|
|
// 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);
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> 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
|
|
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3)
|
|
// non-leaf: xx = special side, yy = number of split sides
|
|
// These are bitwise appended and formed into one 64-bit integer.
|
|
|
|
// The function returns a map from original triangle indices to
|
|
// stream of bits encoding state and offsprings.
|
|
|
|
// Using an explicit function object to support recursive call of Serializer::serialize().
|
|
// This is cheaper than the previous implementation using a recursive call of type erased std::function.
|
|
// (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;
|
|
|
|
void serialize(int facet_idx) {
|
|
const Triangle& tr = triangle_selector->m_triangles[facet_idx];
|
|
|
|
// Always save number of split sides. It is zero for unsplit triangles.
|
|
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);
|
|
|
|
if (split_sides) {
|
|
// If this triangle is split, save which side is split (in case
|
|
// of one split) or kept (in case of two splits). The value will
|
|
// 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);
|
|
// 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)
|
|
this->serialize(tr.children[child_idx]);
|
|
} else {
|
|
// In case this is leaf, we better save information about its state.
|
|
int n = int(tr.get_state());
|
|
if (n >= 3) {
|
|
assert(n <= 16);
|
|
if (n <= 16) {
|
|
// Store "11" plus 4 bits of (n-3).
|
|
data.second.insert(data.second.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));
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
} out { this };
|
|
|
|
out.data.first.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 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();
|
|
return out.data;
|
|
}
|
|
|
|
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &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));
|
|
// 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));
|
|
|
|
// Vector to store all parents that have offsprings.
|
|
struct ProcessingInfo {
|
|
int facet_id = 0;
|
|
Vec3i neighbors { -1, -1, -1 };
|
|
int processed_children = 0;
|
|
int total_children = 0;
|
|
};
|
|
// Depth-first queue of a source mesh triangle and its childern.
|
|
// kept outside of the loop to avoid re-allocating inside the loop.
|
|
std::vector<ProcessingInfo> parents;
|
|
|
|
for (auto [triangle_id, ibit] : data.first) {
|
|
assert(triangle_id < int(m_triangles.size()));
|
|
assert(ibit < int(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;
|
|
};
|
|
|
|
parents.clear();
|
|
while (true) {
|
|
// Read next triangle info.
|
|
int code = next_nibble();
|
|
int num_of_split_sides = code & 0b11;
|
|
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
|
bool is_split = num_of_children != 0;
|
|
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
|
auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
|
// Only valid if is_split.
|
|
int special_side = code >> 2;
|
|
|
|
// Take care of the first iteration separately, so handling of the others is simpler.
|
|
if (parents.empty()) {
|
|
if (is_split) {
|
|
// root is split, add it into list of parents and split it.
|
|
// then go to the next.
|
|
Vec3i neighbors = root_neighbors(*m_mesh, triangle_id);
|
|
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
|
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
|
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
|
|
continue;
|
|
} else {
|
|
// root is not split. just set the state and that's it.
|
|
m_triangles[triangle_id].set_state(state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This is not the first iteration. This triangle is a child of last seen parent.
|
|
assert(! parents.empty());
|
|
assert(parents.back().processed_children < parents.back().total_children);
|
|
|
|
if (ProcessingInfo& last = parents.back(); is_split) {
|
|
// split the triangle and save it as parent of the next ones.
|
|
const Triangle &tr = m_triangles[last.facet_id];
|
|
int child_idx = last.total_children - last.processed_children - 1;
|
|
Vec3i neighbors = this->child_neighbors(tr, last.neighbors, child_idx);
|
|
int this_idx = tr.children[child_idx];
|
|
m_triangles[this_idx].set_division(num_of_split_sides, special_side);
|
|
perform_split(this_idx, neighbors, EnforcerBlockerType::NONE);
|
|
parents.push_back({this_idx, neighbors, 0, num_of_children});
|
|
} else {
|
|
// this triangle belongs to last split one
|
|
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;
|
|
}
|
|
|
|
// If all children of the past parent triangle are claimed, move to grandparent.
|
|
while (parents.back().processed_children == parents.back().total_children) {
|
|
parents.pop_back();
|
|
|
|
if (parents.empty())
|
|
break;
|
|
|
|
// And increment the grandparent children counter, because
|
|
// we have just finished that branch and got back here.
|
|
++parents.back().processed_children;
|
|
}
|
|
|
|
// In case we popped back the root, we should be done.
|
|
if (parents.empty())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 (const std::pair<int, int> &triangle_id_and_ibit : data.first) {
|
|
int ibit = triangle_id_and_ibit.second;
|
|
assert(ibit < int(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()
|
|
{
|
|
for (Triangle &triangle : m_triangles)
|
|
if (!triangle.is_split())
|
|
triangle.unselect_by_seed_fill();
|
|
}
|
|
|
|
void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_state)
|
|
{
|
|
for (Triangle &triangle : m_triangles)
|
|
if (!triangle.is_split() && triangle.is_selected_by_seed_fill())
|
|
triangle.set_state(new_state);
|
|
|
|
for (Triangle &triangle : m_triangles)
|
|
if (triangle.is_split() && triangle.valid()) {
|
|
size_t facet_idx = &triangle - &m_triangles.front();
|
|
remove_useless_children(int(facet_idx));
|
|
}
|
|
}
|
|
|
|
TriangleSelector::Cursor::Cursor(
|
|
const Vec3f& center_, const Vec3f& source_, float radius_world,
|
|
CursorType type_, const Transform3d& trafo_)
|
|
: center{center_},
|
|
source{source_},
|
|
type{type_},
|
|
trafo{trafo_.cast<float>()}
|
|
{
|
|
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
|
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
|
radius_sqr = float(std::pow(radius_world / sf(0), 2));
|
|
uniform_scaling = true;
|
|
}
|
|
else {
|
|
// In case that the transformation is non-uniform, all checks whether
|
|
// something is inside the cursor should be done in world coords.
|
|
// First transform center, source and dir in world coords and remember
|
|
// that we did this.
|
|
center = trafo * center;
|
|
source = trafo * source;
|
|
uniform_scaling = false;
|
|
radius_sqr = radius_world * radius_world;
|
|
trafo_normal = trafo.linear().inverse().transpose();
|
|
}
|
|
|
|
// Calculate dir, in whatever coords is appropriate.
|
|
dir = (center - source).normalized();
|
|
}
|
|
|
|
|
|
// Is a point (in mesh coords) inside a cursor?
|
|
bool TriangleSelector::Cursor::is_mesh_point_inside(Vec3f point) const
|
|
{
|
|
if (! uniform_scaling)
|
|
point = trafo * point;
|
|
|
|
Vec3f diff = center - point;
|
|
return (type == CIRCLE ?
|
|
(diff - diff.dot(dir) * dir).squaredNorm() :
|
|
diff.squaredNorm())
|
|
< radius_sqr;
|
|
}
|
|
|
|
|
|
|
|
// p1, p2, p3 are in mesh coords!
|
|
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
|
const Vec3f& p2_,
|
|
const Vec3f& p3_) const
|
|
{
|
|
const Vec3f& q1 = center + dir;
|
|
const Vec3f& q2 = center - dir;
|
|
|
|
auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b,
|
|
const Vec3f& c, const Vec3f& d) -> bool {
|
|
return ((b-a).cross(c-a)).dot(d-a) > 0.;
|
|
};
|
|
|
|
// In case the object is non-uniformly scaled, do the check in world coords.
|
|
const Vec3f& p1 = uniform_scaling ? p1_ : Vec3f(trafo * p1_);
|
|
const Vec3f& p2 = uniform_scaling ? p2_ : Vec3f(trafo * p2_);
|
|
const Vec3f& p3 = uniform_scaling ? p3_ : Vec3f(trafo * p3_);
|
|
|
|
if (signed_volume_sign(q1,p1,p2,p3) == signed_volume_sign(q2,p1,p2,p3))
|
|
return false;
|
|
|
|
bool pos = signed_volume_sign(q1,q2,p1,p2);
|
|
return signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos;
|
|
}
|
|
|
|
} // namespace Slic3r
|