TriangleSelector: Improvements

This commit is contained in:
Lukas Matena 2020-06-19 09:20:58 +02:00
parent d2b2446b07
commit c3db84e382
2 changed files with 176 additions and 148 deletions

View file

@ -436,9 +436,11 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const float limit = pow(m_cursor_radius/avg_scaling , 2.f);
// Calculate direction from camera to the hit (in mesh coords):
Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast<float>() - closest_hit).normalized();
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
Vec3f dir = (closest_hit - camera_pos).normalized();
m_triangle_selector->select_patch(closest_hit, closest_facet, dir, limit, new_state);
m_triangle_selector->select_patch(closest_hit, closest_facet, camera_pos,
dir, limit, new_state);
return true;
}
@ -567,6 +569,12 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (! m_c->selection_info()->model_object())
return;
m_imgui->begin(std::string("TriangleSelector DEBUG"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
static float edge_limit = 1.f;
m_imgui->slider_float("Edge limit (mm): ", &edge_limit, 0.1f, 8.f);
m_triangle_selector->set_edge_limit(edge_limit);
m_imgui->end();
const float approx_height = m_imgui->scaled(18.0f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
@ -798,7 +806,7 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const
void TriangleSelector::DivisionNode::set_division(int sides_to_split, int special_side_idx)
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 >=-1 && special_side_idx < 3);
@ -812,48 +820,49 @@ void TriangleSelector::DivisionNode::set_division(int sides_to_split, int specia
void TriangleSelector::DivisionNode::set_state(FacetSupportType type)
void TriangleSelector::Triangle::set_state(FacetSupportType type)
{
// If this is not a leaf-node, this makes no sense and
// the bits are used for storing index of an edge.
assert(number_of_split_sides() == 0);
assert(! is_split());
division_type = (int8_t(type) << 2);
}
int TriangleSelector::DivisionNode::side_to_keep() const
int TriangleSelector::Triangle::side_to_keep() const
{
assert(number_of_split_sides() == 2);
return (division_type & 0b1100) >> 2;
return division_type >> 2;
}
int TriangleSelector::DivisionNode::side_to_split() const
int TriangleSelector::Triangle::side_to_split() const
{
assert(number_of_split_sides() == 1);
return (division_type & 0b1100) >> 2;
return division_type >> 2;
}
FacetSupportType TriangleSelector::DivisionNode::get_state() const
FacetSupportType TriangleSelector::Triangle::get_state() const
{
assert(number_of_split_sides() == 0); // this must be leaf
return FacetSupportType((division_type & 0b1100) >> 2);
assert(! is_split()); // this must be leaf
return FacetSupportType(division_type >> 2);
}
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& dir,
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
const Vec3f& source, const Vec3f& dir,
float radius_sqr, FacetSupportType new_state)
{
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 = {hit, dir, radius_sqr};
m_cursor = {hit, source, dir, radius_sqr};
// Now start with the facet the pointer points to and check all adjacent facets.
std::vector<int> facets_to_check{facet_start};
@ -862,14 +871,12 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec
while (facet_idx < int(facets_to_check.size())) {
int facet = facets_to_check[facet_idx];
if (! visited[facet]) {
int num_of_inside_vertices = vertices_inside(facet);
// select the facet...
select_triangle(facet, new_state, num_of_inside_vertices, facet == facet_start);
// ...and add neighboring facets to be proccessed later
for (int n=0; n<3; ++n) {
if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n]))
facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]);
if (select_triangle(facet, new_state, facet == facet_start)) {
// add neighboring facets to list to be proccessed later
for (int n=0; n<3; ++n) {
if (faces_camera(m_mesh->stl.neighbors_start[facet].neighbor[n]))
facets_to_check.push_back(m_mesh->stl.neighbors_start[facet].neighbor[n]);
}
}
}
visited[facet] = true;
@ -879,53 +886,71 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec
// Selects either the whole triangle (discarding any children it has), or divides
// 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.
void TriangleSelector::select_triangle(int facet_idx, FacetSupportType type,
int num_of_inside_vertices, bool cursor_inside)
// This is done by an actual recursive call. Returns false if the triangle is
// outside the cursor.
bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool cursor_inside)
{
assert(facet_idx < m_triangles.size());
//cursor_inside=false;
if (num_of_inside_vertices == -1)
num_of_inside_vertices = vertices_inside(facet_idx);
bool out = false;
assert(facet_idx < int(m_triangles.size()));
Triangle& tr = m_triangles[facet_idx];
if (! tr.valid)
return false;
cursor_inside = is_pointer_in_triangle(facet_idx);
int num_of_inside_vertices = vertices_inside(facet_idx);
if (num_of_inside_vertices == 0 && ! cursor_inside)
return; // FIXME: just an edge can be inside
return out; // FIXME: just an edge can be inside
if (num_of_inside_vertices == 3) {
// dump any subdivision and select whole triangle
undivide_triangle(facet_idx);
m_triangles[facet_idx].div_info->set_state(type);
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;
}
split_triangle(facet_idx);
assert(facet_idx < m_triangles.size());
int num_of_children = m_triangles[facet_idx].div_info->number_of_split_sides() + 1;
assert(facet_idx < int(m_triangles.size()));
int num_of_children = tr.number_of_split_sides() + 1;
if (num_of_children != 1) {
for (int i=0; i<num_of_children; ++i) {
select_triangle(m_triangles[facet_idx].div_info->children[i], type, -1, cursor_inside);
}
for (int i=0; i<num_of_children; ++i)
select_triangle(tr.children[i], type, cursor_inside);
}
}
//if (m_triangles[facet_idx].div_info->number_of_split_sides() != 0)
// remove_needless(m_triangles[facet_idx].div_info->children[0]);
// In case that all siblings are leafs and have the same state now,
// they may be removed and substituted by the parent triangle.
//remove_if_needless(facet_idx);
return true;
}
bool TriangleSelector::split_triangle(int facet_idx)
{
if (m_triangles[facet_idx].div_info->number_of_split_sides() != 0) {
if (m_triangles[facet_idx].is_split()) {
// The triangle was divided already.
return 0;
return false;
}
FacetSupportType old_type = m_triangles[facet_idx].div_info->get_state();
Triangle& tr = m_triangles[facet_idx];
const double limit_squared = 4;
FacetSupportType old_type = tr.get_state();
stl_triangle_vertex_indices& facet = m_triangles[facet_idx].verts_idxs;
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() };
@ -938,8 +963,8 @@ bool TriangleSelector::split_triangle(int facet_idx)
side_to_keep = pt_idx;
}
if (sides_to_split.empty()) {
m_triangles[facet_idx].div_info->set_division(0);
return 0;
tr.set_division(0);
return false;
}
// indices of triangle vertices
@ -988,29 +1013,18 @@ bool TriangleSelector::split_triangle(int facet_idx)
// Save how the triangle was split. Second argument makes sense only for one
// or two split sides, otherwise the value is ignored.
m_triangles[facet_idx].div_info->set_division(sides_to_split.size(),
tr.set_division(sides_to_split.size(),
sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]);
// And save the children. All children should start in the same state as the triangle we just split.
assert(! sides_to_split.empty() && int(sides_to_split.size()) <= 3);
for (int i=0; i<=int(sides_to_split.size()); ++i) {
m_triangles[facet_idx].div_info->children[i] = m_triangles.size()-1-i;
m_triangles[m_triangles.size()-1-i].div_info->parent = facet_idx;
m_triangles[m_triangles[facet_idx].div_info->children[i]].div_info->set_state(old_type);
tr.children[i] = m_triangles.size()-1-i;
m_triangles[tr.children[i]].parent = facet_idx;
m_triangles[tr.children[i]].set_state(old_type);
}
#ifndef NDEBUG
int split_sides = m_triangles[facet_idx].div_info->number_of_split_sides();
if (split_sides != 0) {
// check that children are range
for (int i=0; i<=split_sides; ++i)
assert(m_triangles[facet_idx].div_info->children[i] >= 0 && m_triangles[facet_idx].div_info->children[i] < int(m_triangles.size()));
}
#endif
return 1;
return true;
}
@ -1022,6 +1036,29 @@ bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const
}
// Is pointer in a triangle?
bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
{
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.;
};
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& q1 = m_cursor.center + m_cursor.dir;
const Vec3f q2 = m_cursor.center - m_cursor.dir;
if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) {
bool pos = signed_volume_sign(q1,q2,p1,p2);
if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos)
return true;
}
return false;
}
// Determine whether this facet is potentially visible (still can be obscured).
bool TriangleSelector::faces_camera(int facet) const
@ -1056,39 +1093,41 @@ int TriangleSelector::vertices_inside(int facet_idx) const
// Recursively remove all subtriangles.
void TriangleSelector::undivide_triangle(int facet_idx)
{
assert(facet_idx < m_triangles.size());
auto& dn_ptr = m_triangles[facet_idx].div_info;
assert(dn_ptr);
assert(facet_idx < int(m_triangles.size()));
Triangle& tr = m_triangles[facet_idx];
if (dn_ptr->number_of_split_sides() != 0) {
for (int i=0; i<=dn_ptr->number_of_split_sides(); ++i) {
undivide_triangle(dn_ptr->children[i]);
m_triangles[dn_ptr->children[i]].div_info->valid = false;
if (tr.is_split()) {
for (int i=0; i<=tr.number_of_split_sides(); ++i) {
undivide_triangle(tr.children[i]);
m_triangles[tr.children[i]].valid = false;
}
}
dn_ptr->set_division(0); // not split
tr.set_division(0); // not split
}
void TriangleSelector::remove_needless(int child_facet)
void TriangleSelector::remove_if_needless(int child_facet)
{
assert(m_triangles[child_facet].div_info->number_of_split_sides() == 0);
int parent = m_triangles[child_facet].div_info->parent;
if (m_triangles[child_facet].is_split() || ! m_triangles[child_facet].valid)
return;
int parent = m_triangles[child_facet].parent;
if (parent == -1)
return; // root
// Check type of all valid children.
FacetSupportType type = m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state();
for (int i=0; i<=m_triangles[parent].div_info->number_of_split_sides(); ++i)
if (m_triangles[m_triangles[parent].div_info->children[0]].div_info->get_state() != type)
FacetSupportType child_type = m_triangles[child_facet].get_state();
// Check type of all valid children, if they're same, they are needless.
for (int i=0; i<=m_triangles[parent].number_of_split_sides(); ++i)
if (m_triangles[m_triangles[parent].children[i]].is_split()
|| m_triangles[m_triangles[parent].children[i]].get_state() != child_type)
return; // not all children are the same
// All children are the same, let's kill them.
// All children are the same, kill them.
undivide_triangle(parent);
m_triangles[parent].div_info->set_state(type);
m_triangles[parent].set_state(child_type);
// And not try the same for grandparent.
remove_needless(parent);
// And now try the same for parent (which has just become leaf).
remove_if_needless(parent);
}
@ -1114,51 +1153,40 @@ void TriangleSelector::render() const
::glScalef(1.01f, 1.01f, 1.01f);
::glBegin( GL_TRIANGLES);
for (int tr_id=0; tr_id<m_triangles.size(); ++tr_id) {
if (tr_id == m_orig_size_indices)
::glColor3f(1.f, 0.f, 0.f);
for (int tr_id=0; tr_id<int(m_triangles.size()); ++tr_id) {
const Triangle& tr = m_triangles[tr_id];
if (tr.div_info->valid) {
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]);
}
if (! tr.valid)
continue;
if (tr_id == m_orig_size_indices-1)
::glColor3f(1.f, 0.f, 0.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]);
}
::glEnd();
::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
::glBegin( GL_TRIANGLES);
for (int tr_id=0; tr_id<m_triangles.size(); ++tr_id) {
const Triangle& tr = m_triangles[tr_id];
if (! tr.div_info->valid)
for (const Triangle& tr : m_triangles) {
if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE)
continue;
if (tr.div_info->number_of_split_sides() == 0) {
if (tr.div_info->get_state() == FacetSupportType::ENFORCER)
::glColor4f(0.f, 0.f, 1.f, 0.2f);
else if (tr.div_info->get_state() == FacetSupportType::BLOCKER)
::glColor4f(1.f, 0.f, 0.f, 0.2f);
else
continue;
}
if (tr.get_state() == FacetSupportType::ENFORCER)
::glColor4f(0.f, 0.f, 1.f, 0.2f);
else
continue;
::glColor4f(1.f, 0.f, 0.f, 0.2f);
if (tr.div_info->valid) {
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]);
}
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]);
}
::glEnd();
}
TriangleSelector::DivisionNode::DivisionNode()
{
set_division(0);
set_state(FacetSupportType::NONE);
}
} // namespace GUI
} // namespace Slic3r

View file

@ -24,16 +24,18 @@ class ClippingPlane;
// to recursively subdivide the triangles and make the selection finer.
class TriangleSelector {
public:
void set_edge_limit(float edge_limit) { m_edge_limit_sqr = std::pow(edge_limit, 2.f); }
void render() const;
// Create new object on a TriangleMesh. The referenced mesh must
// stay valid, a ptr to it is saved and used.
explicit TriangleSelector(const TriangleMesh& mesh);
// Select all triangles 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& dir, // direction of the ray
float radius_sqr, // squared radius of the cursor
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)
const Vec3f& dir, // direction of the ray (mesh coords)
float radius_sqr, // squared radius of the cursor
FacetSupportType new_state); // enforcer or blocker?
void unselect_all();
@ -43,49 +45,44 @@ public:
void garbage_collect();
private:
// A struct to hold information about how a triangle was divided.
struct DivisionNode {
DivisionNode();
// Index of triangle this describes.
// Triangle and info about how it's split.
struct Triangle {
public:
Triangle(int a, int b, int c)
: verts_idxs{stl_triangle_vertex_indices(a, b, c)},
division_type{0}
{}
stl_triangle_vertex_indices verts_idxs;
// Is this triangle valid or marked to remove?
bool valid{true};
// Index of parent triangle (-1: original)
int parent{-1};
// Bitmask encoding which sides are split.
int8_t division_type;
// bits 0 and 1 : 00 - no division
// 01 - one-edge split
// 10 - two-edge split
// 11 - three-edge split
// bits 2 and 3 : decimal 0, 1 or 2 identifying the special edge (one that
// splits in one-edge split or one that stays in two-edge split).
// Children triangles (0 = no child)
std::array<int, 4> children;
// Set the division type.
void set_division(int sides_to_split, int special_side_idx = -1);
void set_state(FacetSupportType state);
// Helpers that decode the division_type bitmask.
// Get/set current state.
void set_state(FacetSupportType state);
FacetSupportType get_state() const;
// Get info on how it's split.
bool is_split() const { return number_of_split_sides() != 0; }
int number_of_split_sides() const { return division_type & 0b11; }
int side_to_keep() const;
int side_to_split() const;
FacetSupportType get_state() const;
};
// Triangle and pointer to how it's divided (nullptr = not divided).
// The ptr is nullptr for all new triangles, it is only valid for
// the original (undivided) triangles.
struct Triangle {
Triangle(int a, int b, int c)
: verts_idxs{stl_triangle_vertex_indices(a, b, c)},
div_info{std::make_unique<DivisionNode>()}
{}
stl_triangle_vertex_indices verts_idxs;
std::unique_ptr<DivisionNode> div_info;
private:
// Bitmask encoding which sides are split.
int8_t division_type;
// bits 0, 1 : decimal 0, 1, 2 or 3 (how many sides are split)
// bits 2, 3 (non-leaf): decimal 0, 1 or 2 identifying the special edge
// (one that splits in one-edge split or one that stays in two-edge split).
// bits 2, 3 (leaf): FacetSupportType value
};
// Lists of vertices and triangles, both original and new
@ -93,6 +90,8 @@ private:
std::vector<Triangle> m_triangles;
const TriangleMesh* m_mesh;
float m_edge_limit_sqr = 1.f;
// Number of original vertices and triangles.
int m_orig_size_vertices;
int m_orig_size_indices;
@ -104,6 +103,7 @@ private:
// Caches for cursor position, radius and direction.
struct Cursor {
Vec3f center;
Vec3f source;
Vec3f dir;
float radius_sqr;
};
@ -111,8 +111,7 @@ private:
Cursor m_cursor;
// Private functions:
void select_triangle(int facet_idx, FacetSupportType type,
int num_of_inside_vertices = -1,
bool select_triangle(int facet_idx, FacetSupportType type,
bool cursor_inside = false);
bool is_point_inside_cursor(const Vec3f& point) const;
@ -125,7 +124,8 @@ private:
bool split_triangle(int facet_idx);
void remove_needless(int child_facet);
void remove_if_needless(int child_facet);
bool is_pointer_in_triangle(int facet_idx) const;
};