diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 881466b39..9f566b405 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -187,6 +187,8 @@ add_library(libslic3r STATIC Utils.hpp Time.cpp Time.hpp + TriangleSelector.cpp + TriangleSelector.hpp MTUtils.hpp VoronoiOffset.cpp VoronoiOffset.hpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0719cac8c..b6bae489b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2,6 +2,7 @@ #include "ModelArrange.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" +#include "TriangleSelector.hpp" #include "Format/AMF.hpp" #include "Format/OBJ.hpp" @@ -1833,25 +1834,21 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const std::vector FacetsAnnotation::get_facets(FacetSupportType type) const { std::vector out; - for (auto& [facet_idx, this_type] : m_data) + /*for (auto& [facet_idx, this_type] : m_data) if (this_type == type) out.push_back(facet_idx); - return out; + */return out; } -void FacetsAnnotation::set_facet(int idx, FacetSupportType type) +void FacetsAnnotation::set(const TriangleSelector& selector) { - bool changed = true; - - if (type == FacetSupportType::NONE) - changed = m_data.erase(idx) != 0; - else - m_data[idx] = type; - - if (changed) + std::map> sel_map = selector.serialize(); + if (sel_map != m_data) { + m_data = sel_map; update_timestamp(); + } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index be298ae4b..de20e0fdc 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -39,6 +39,7 @@ class ModelVolume; class ModelWipeTower; class Print; class SLAPrint; +class TriangleSelector; namespace UndoRedo { class StackImpl; @@ -404,8 +405,9 @@ class FacetsAnnotation { public: using ClockType = std::chrono::steady_clock; + const std::map>& get_data() const { return m_data; } + void set(const TriangleSelector& selector); std::vector get_facets(FacetSupportType type) const; - void set_facet(int idx, FacetSupportType type); void clear(); ClockType::time_point get_timestamp() const { return timestamp; } @@ -419,7 +421,7 @@ public: } private: - std::map m_data; + std::map> m_data; ClockType::time_point timestamp; void update_timestamp() { diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp new file mode 100644 index 000000000..340f5a29a --- /dev/null +++ b/src/libslic3r/TriangleSelector.cpp @@ -0,0 +1,654 @@ +#include "TriangleSelector.hpp" +#include "Model.hpp" + + +namespace Slic3r { + + + +// 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 >=-1 && sides_to_split <= 3); + assert(special_side_idx >=-1 && special_side_idx < 3); + + // If splitting one or two sides, second argument must be provided. + assert(sides_to_split != 1 || special_side_idx != -1); + assert(sides_to_split != 2 || special_side_idx != -1); + + if (sides_to_split != -1) { + this->number_of_splits = sides_to_split; + if (sides_to_split != 0) { + assert(old_number_of_splits == 0); + this->special_side_idx = special_side_idx; + this->old_number_of_splits = sides_to_split; + } + } + else { + assert(old_number_of_splits != 0); + this->number_of_splits = old_number_of_splits; + // indices of children should still be there. + } +} + + + +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); + assert(is_approx(dir.norm(), 1.f)); + + // Save current cursor center, squared radius and camera direction, + // so we don't have to pass it around. + m_cursor = {hit, source, dir, radius_sqr}; + + // Now start with the facet the pointer points to and check all adjacent facets. + std::vector facets_to_check{facet_start}; + std::vector visited(m_orig_size_indices, false); // keep track of facets we already processed + int facet_idx = 0; // index into facets_to_check + while (facet_idx < int(facets_to_check.size())) { + int facet = facets_to_check[facet_idx]; + if (! visited[facet]) { + if (select_triangle(facet, new_state)) { + // 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; + ++facet_idx; + } +} + + + +// 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. +bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) +{ + assert(facet_idx < int(m_triangles.size())); + + Triangle* tr = &m_triangles[facet_idx]; + if (! tr->valid) + return false; + + 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; + } + + split_triangle(facet_idx); + tr = &m_triangles[facet_idx]; // might have been invalidated + + + int num_of_children = tr->number_of_split_sides() + 1; + if (num_of_children != 1) { + for (int i=0; ichildren.size())); + assert(tr->children[i] < int(m_triangles.size())); + + select_triangle(tr->children[i], type, true); + tr = &m_triangles[facet_idx]; // might have been invalidated + } + } + } + + if (! recursive_call) { + // In case that all children are leafs and have the same state now, + // they may be removed and substituted by the parent triangle. + remove_useless_children(facet_idx); + + // Make sure that we did not lose track of invalid triangles. + assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(), + [](const Triangle& tr) { return ! tr.valid; })); + + // Do garbage collection maybe? + if (2*m_invalid_triangles > int(m_triangles.size())) + garbage_collect(); + } + return true; +} + + +void TriangleSelector::split_triangle(int facet_idx) +{ + if (m_triangles[facet_idx].is_split()) { + // The triangle is divided already. + return; + } + + Triangle* tr = &m_triangles[facet_idx]; + + FacetSupportType old_type = tr->get_state(); + + if (tr->was_split_before() != 0) { + // This triangle is not split at the moment, but was at one point + // in history. We can just restore it and resurrect its children. + tr->set_division(-1); + for (int i=0; i<=tr->number_of_split_sides(); ++i) { + m_triangles[tr->children[i]].set_state(old_type); + m_triangles[tr->children[i]].valid = true; + --m_invalid_triangles; + } + return; + } + + // If we got here, we are about to actually split the triangle. + const double limit_squared = m_edge_limit_sqr; + + std::array& facet = tr->verts_idxs; + const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; + double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), + (*pts[0]-*pts[2]).squaredNorm(), + (*pts[1]-*pts[0]).squaredNorm() }; + + std::vector 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); + 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(sides_to_split.size(), + sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); + + perform_split(facet_idx, old_type); +} + + +// Calculate distance of a point from a line. +bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const +{ + Vec3f diff = m_cursor.center - point; + return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; +} + + +// 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]].v; + const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; + const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; + const Vec3f& q1 = m_cursor.center + m_cursor.dir; + const Vec3f q2 = m_cursor.center - m_cursor.dir; + + 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 +{ + assert(facet < m_orig_size_indices); + // The normal is cached in mesh->stl, use it. + return (m_mesh->stl.facet_start[facet].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 (is_point_inside_cursor(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 +{ + Vec3f pts[3]; + for (int i=0; i<3; ++i) + pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; + + 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) { + undivide_triangle(tr.children[i]); + m_triangles[tr.children[i]].valid = false; + ++m_invalid_triangles; + } + tr.set_division(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. + FacetSupportType first_child_type = FacetSupportType::NONE; + for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + if (m_triangles[tr.children[child_idx]].is_split()) + return; + 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 new_triangle_indices(m_triangles.size(), -1); + for (int i = m_orig_size_indices; i new_vertices_indices(m_vertices.size(), -1); + for (int i=m_orig_size_vertices; i= 0); + if (m_vertices[i].ref_cnt != 0) { + new_vertices_indices[i] = new_idx; + ++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]; + } + } + + // If this triangle was split before, forget it. + // Children referenced in the cache are dead by now. + tr.forget_history(); + } + + m_invalid_triangles = 0; +} + +TriangleSelector::TriangleSelector(const TriangleMesh& mesh) + : m_mesh{&mesh} +{ + reset(); +} + + +void TriangleSelector::reset() +{ + if (! m_orig_size_indices != 0) // unless this is run from constructor + garbage_collect(); + m_vertices.clear(); + m_triangles.clear(); + for (const stl_vertex& vert : m_mesh->its.vertices) + m_vertices.emplace_back(vert); + for (const stl_triangle_vertex_indices& ind : m_mesh->its.indices) + push_triangle(ind[0], ind[1], ind[2]); + m_orig_size_vertices = m_vertices.size(); + m_orig_size_indices = m_triangles.size(); + m_invalid_triangles = 0; +} + + + + + +void TriangleSelector::set_edge_limit(float edge_limit) +{ + float new_limit_sqr = std::pow(edge_limit, 2.f); + + if (new_limit_sqr != m_edge_limit_sqr) { + m_edge_limit_sqr = new_limit_sqr; + + // The way how triangles split may be different now, forget + // all cached splits. + garbage_collect(); + } +} + + + +void TriangleSelector::push_triangle(int a, int b, int c) +{ + for (int i : {a, b, c}) { + assert(i >= 0 && i < int(m_vertices.size())); + ++m_vertices[i].ref_cnt; + } + m_triangles.emplace_back(a, b, c); +} + + +void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) +{ + Triangle* tr = &m_triangles[facet_idx]; + + assert(tr->is_split()); + + // Read info about how to split this triangle. + int sides_to_split = tr->number_of_split_sides(); + + // indices of triangle vertices + std::vector verts_idxs; + int idx = tr->special_side(); + for (int j=0; j<3; ++j) { + verts_idxs.push_back(tr->verts_idxs[idx++]); + if (idx == 3) + idx = 0; + } + + if (sides_to_split == 1) { + m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); + } + + if (sides_to_split == 2) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); + push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); + } + + if (sides_to_split == 3) { + m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); + m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); + verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); + + push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); + push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); + push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); + } + + tr = &m_triangles[facet_idx]; // may have been invalidated + + // And save the children. All children should start in the same state as the triangle we just split. + assert(sides_to_split <= 3); + for (int i=0; i<=sides_to_split; ++i) { + tr->children[i] = m_triangles.size()-1-i; + m_triangles[tr->children[i]].set_state(old_state); + } +} + + +std::map> 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): + // leaf triangle: xx = FacetSupportType, yy = 0 + // 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. + + std::map> out; + for (int i=0; i data; // complete encoding of this mesh triangle + int stored_triangles = 0; // how many have been already encoded + + std::function serialize_recursive; + serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) { + const Triangle& tr = 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 |= (split_sides << (stored_triangles * 4)); + data.push_back(split_sides & 0b01); + data.push_back(split_sides & 0b10); + + if (tr.is_split()) { + // 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(split_sides > 0); + assert(tr.special_side() >= 0 && tr.special_side() <= 3); + data.push_back(tr.special_side() & 0b01); + data.push_back(tr.special_side() & 0b10); + ++stored_triangles; + // Now save all children. + for (int child_idx=0; child_idx<=split_sides; ++child_idx) + serialize_recursive(tr.children[child_idx]); + } else { + // In case this is leaf, we better save information about its state. + assert(int(tr.get_state()) <= 3); + data.push_back(int(tr.get_state()) & 0b01); + data.push_back(int(tr.get_state()) & 0b10); + ++stored_triangles; + } + }; + + serialize_recursive(i); + out[i] = data; + } + + return out; +} + +void TriangleSelector::deserialize(const std::map> data) +{ + reset(); // dump any current state + for (const auto& [triangle_id, code] : data) { + assert(triangle_id < int(m_triangles.size())); + int processed_triangles = 0; + struct ProcessingInfo { + int facet_id = 0; + int processed_children = 0; + int total_children = 0; + }; + + // Vector to store all parents that have offsprings. + std::vector parents; + + while (true) { + // Read next triangle info. + int next_code = 0; + for (int i=3; i>=0; --i) { + next_code = next_code << 1; + next_code |= int(code[4 * processed_triangles + i]); + } + ++processed_triangles; + + int num_of_split_sides = (next_code & 0b11); + int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; + bool is_split = num_of_children != 0; + FacetSupportType state = FacetSupportType(next_code >> 2); + int special_side = (next_code >> 2); + + // Take care of the first iteration separately, so handling of the others is simpler. + if (parents.empty()) { + if (! is_split) { + // root is not split. just set the state and that's it. + m_triangles[triangle_id].set_state(state); + break; + } else { + // root is split, add it into list of parents and split it. + // then go to the next. + parents.push_back({triangle_id, 0, num_of_children}); + m_triangles[triangle_id].set_division(num_of_children-1, special_side); + perform_split(triangle_id, FacetSupportType::NONE); + continue; + } + } + + // 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 (is_split) { + // split the triangle and save it as parent of the next ones. + const ProcessingInfo& last = parents.back(); + int this_idx = m_triangles[last.facet_id].children[last.processed_children]; + m_triangles[this_idx].set_division(num_of_children-1, special_side); + perform_split(this_idx, FacetSupportType::NONE); + parents.push_back({this_idx, 0, num_of_children}); + } else { + // this triangle belongs to last split one + m_triangles[m_triangles[parents.back().facet_id].children[parents.back().processed_children]].set_state(state); + ++parents.back().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; + } + + } +} + + + + +} // namespace Slic3r diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp new file mode 100644 index 000000000..943029548 --- /dev/null +++ b/src/libslic3r/TriangleSelector.hpp @@ -0,0 +1,149 @@ +#ifndef libslic3r_TriangleSelector_hpp_ +#define libslic3r_TriangleSelector_hpp_ + +#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + + +#include "Point.hpp" +#include "TriangleMesh.hpp" + +namespace Slic3r { + +enum class FacetSupportType : int8_t; + + + +// Following class holds information about selected triangles. It also has power +// to recursively subdivide the triangles and make the selection finer. +class TriangleSelector { +public: + void set_edge_limit(float edge_limit); + + // 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 fully inside the circle, subdivide where needed. + void select_patch(const Vec3f& hit, // point where to start + int facet_start, // facet that point belongs to + const Vec3f& source, // camera position (mesh coords) + const Vec3f& dir, // direction of the ray (mesh coords) + float radius_sqr, // squared radius of the cursor + FacetSupportType new_state); // enforcer or blocker? + + + // Clear everything and make the tree empty. + void reset(); + + // Remove all unnecessary data. + void garbage_collect(); + + // Store the division trees in compact form (a long stream of + // bits for each triangle of the original mesh). + std::map> serialize() const; + + // Load serialized data. Assumes that correct mesh is loaded. + void deserialize(const std::map> data); + + +protected: + // Triangle and info about how it's split. + class Triangle { + public: + // Use TriangleSelector::push_triangle to create a new triangle. + // It increments/decrements reference counter on vertices. + Triangle(int a, int b, int c) + : verts_idxs{a, b, c}, + state{FacetSupportType(0)}, + number_of_splits{0}, + special_side_idx{0}, + old_number_of_splits{0} + {} + // Indices into m_vertices. + std::array verts_idxs; + + // Is this triangle valid or marked to be removed? + bool valid{true}; + + // Children triangles. + std::array children; + + // Set the division type. + void set_division(int sides_to_split, int special_side_idx = -1); + + // Get/set current state. + void set_state(FacetSupportType type) { assert(! is_split()); state = type; } + FacetSupportType get_state() const { assert(! is_split()); return state; } + + // Get info on how it's split. + bool is_split() const { return number_of_split_sides() != 0; } + int number_of_split_sides() const { return number_of_splits; } + int special_side() const { assert(is_split()); return special_side_idx; } + bool was_split_before() const { return old_number_of_splits != 0; } + void forget_history() { old_number_of_splits = 0; } + + private: + int number_of_splits; + int special_side_idx; + FacetSupportType state; + + // How many children were spawned during last split? + // Is not reset on remerging the triangle. + int old_number_of_splits; + }; + + struct Vertex { + explicit Vertex(const stl_vertex& vert) + : v{vert}, + ref_cnt{0} + {} + stl_vertex v; + int ref_cnt; + }; + + // Lists of vertices and triangles, both original and new + std::vector m_vertices; + std::vector m_triangles; + const TriangleMesh* m_mesh; + + // Number of invalid triangles (to trigger garbage collection). + int m_invalid_triangles; + + // Limiting length of triangle side (squared). + float m_edge_limit_sqr = 1.f; + + // Number of original vertices and triangles. + int m_orig_size_vertices; + int m_orig_size_indices; + + // Cache for cursor position, radius and direction. + struct Cursor { + Vec3f center; + Vec3f source; + Vec3f dir; + float radius_sqr; + }; + + Cursor m_cursor; + + // Private functions: + bool select_triangle(int facet_idx, FacetSupportType type, + bool recursive_call = false); + bool is_point_inside_cursor(const Vec3f& point) const; + int vertices_inside(int facet_idx) const; + bool faces_camera(int facet) const; + void undivide_triangle(int facet_idx); + void split_triangle(int facet_idx); + void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. + bool is_pointer_in_triangle(int facet_idx) const; + bool is_edge_inside_cursor(int facet_idx) const; + void push_triangle(int a, int b, int c); + void perform_split(int facet_idx, FacetSupportType old_state); +}; + + + + +} // namespace Slic3r + +#endif // libslic3r_TriangleSelector_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 01427ec09..ca7695907 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -89,15 +89,12 @@ void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const S void GLGizmoFdmSupports::on_render() const { - //const Selection& selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - //render_triangles(selection); - - if (m_triangle_selector && ! m_setting_angle) - m_triangle_selector->render(m_imgui); + render_triangles(selection); m_c->object_clipper()->render_cut(); render_cursor_circle(); @@ -148,14 +145,9 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(trafo_matrix.data())); - // Now render both enforcers and blockers. - //for (int i=0; i<2; ++i) { - // glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); - // for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i]) { - if (m_iva.has_VBOs()) - m_iva.render(); - // } - //} + if (! m_setting_angle) + m_triangle_selectors[mesh_id]->render(m_imgui); + glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); @@ -212,16 +204,14 @@ void GLGizmoFdmSupports::render_cursor_circle() const void GLGizmoFdmSupports::update_model_object() const { - return; - /*ModelObject* mo = m_c->selection_info()->model_object(); + ModelObject* mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume* mv : mo->volumes) { ++idx; if (! mv->is_model_part()) continue; - for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); - }*/ + mv->m_supported_facets.set(*m_triangle_selectors[idx].get()); + } } @@ -230,21 +220,7 @@ void GLGizmoFdmSupports::update_from_model_object() wxBusyCursor wait; const ModelObject* mo = m_c->selection_info()->model_object(); - /*size_t num_of_volumes = 0; - for (const ModelVolume* mv : mo->volumes) - if (mv->is_model_part()) - ++num_of_volumes; - m_selected_facets.resize(num_of_volumes);*/ - - m_triangle_selector = std::make_unique(mo->volumes.front()->mesh()); - - /*m_ivas.clear(); - m_ivas.resize(num_of_volumes); - for (size_t i=0; ivolumes) { @@ -256,17 +232,9 @@ void GLGizmoFdmSupports::update_from_model_object() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh* mesh = &mv->mesh(); - m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE); - - // Load current state from ModelVolume. - for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { - const std::vector& list = mv->m_supported_facets.get_facets(type); - for (int i : list) - m_selected_facets[volume_id][i] = type; - } - update_vertex_buffers(mesh, volume_id, FacetSupportType::ENFORCER); - update_vertex_buffers(mesh, volume_id, FacetSupportType::BLOCKER); - }*/ + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->m_supported_facets.get_data()); + } } @@ -321,7 +289,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous || action == SLAGizmoEventType::RightDown || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { - if (! m_triangle_selector) + if (m_triangle_selectors.empty()) return false; FacetSupportType new_state = FacetSupportType::NONE; @@ -426,23 +394,20 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } } - // FIXME: just for now, only process first mesh - if (mesh_id != 0) - return false; - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; // Calculate how far can a point be from the line (in mesh coords). // FIXME: The scaling of the mesh can be non-uniform. const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = pow(m_cursor_radius/avg_scaling , 2.f); + const float limit = std::pow(m_cursor_radius/avg_scaling , 2.f); // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); Vec3f dir = (closest_hit - camera_pos).normalized(); - m_triangle_selector->select_patch(closest_hit, closest_facet, camera_pos, + assert(mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, dir, limit, new_state); return true; @@ -468,7 +433,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } -void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, +/*void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, int mesh_id, FacetSupportType type, const std::vector* new_facets) @@ -506,7 +471,7 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, if (pushed) m_iva.finalize_geometry(true); - /*} else { + } else { // we are only appending - let's make new vertex array and let the old ones live ivas.push_back(GLIndexedVertexArray()); for (size_t facet_idx : *new_facets) @@ -516,9 +481,9 @@ void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh, ivas.back().finalize_geometry(true); else ivas.pop_back(); - }*/ + } -} +}*/ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwrite, bool block) @@ -761,8 +726,8 @@ void GLGizmoFdmSupports::on_set_state() } activate_internal_undo_redo_stack(false); m_old_mo_id = -1; - m_iva.release_geometry(); - m_selected_facets.clear(); + //m_iva.release_geometry(); + m_triangle_selectors.clear(); } m_old_state = m_state; } @@ -800,422 +765,14 @@ void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive&) const } -// sides_to_split==-1 : just restore previous split -void TriangleSelector::Triangle::set_division(int sides_to_split, int special_side_idx) +void TriangleSelectorGUI::render(ImGuiWrapper* imgui) { - assert(sides_to_split >=-1 && sides_to_split <= 3); - assert(special_side_idx >=-1 && special_side_idx < 3); - - // If splitting one or two sides, second argument must be provided. - assert(sides_to_split != 1 || special_side_idx != -1); - assert(sides_to_split != 2 || special_side_idx != -1); - - if (sides_to_split != -1) { - this->number_of_splits = sides_to_split; - if (sides_to_split != 0) { - assert(old_number_of_splits == 0); - this->special_side_idx = special_side_idx; - this->old_number_of_splits = sides_to_split; - } - } - else { - assert(old_number_of_splits != 0); - this->number_of_splits = old_number_of_splits; - // indices of children should still be there. - } -} - - - -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); - assert(is_approx(dir.norm(), 1.f)); - - // Save current cursor center, squared radius and camera direction, - // so we don't have to pass it around. - m_cursor = {hit, source, dir, radius_sqr}; - - // Now start with the facet the pointer points to and check all adjacent facets. - std::vector facets_to_check{facet_start}; - std::vector visited(m_orig_size_indices, false); // keep track of facets we already processed - int facet_idx = 0; // index into facets_to_check - while (facet_idx < int(facets_to_check.size())) { - int facet = facets_to_check[facet_idx]; - if (! visited[facet]) { - if (select_triangle(facet, new_state)) { - // 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; - ++facet_idx; - } -} - - - -// 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. -bool TriangleSelector::select_triangle(int facet_idx, FacetSupportType type, bool recursive_call) -{ - assert(facet_idx < int(m_triangles.size())); - - Triangle* tr = &m_triangles[facet_idx]; - if (! tr->valid) - return false; - - 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; - } - - split_triangle(facet_idx); - tr = &m_triangles[facet_idx]; // might have been invalidated - - - int num_of_children = tr->number_of_split_sides() + 1; - if (num_of_children != 1) { - for (int i=0; ichildren.size())); - assert(tr->children[i] < int(m_triangles.size())); - - select_triangle(tr->children[i], type, true); - tr = &m_triangles[facet_idx]; // might have been invalidated - } - } - } - - if (! recursive_call) { - // In case that all children are leafs and have the same state now, - // they may be removed and substituted by the parent triangle. - remove_useless_children(facet_idx); - - // Make sure that we did not lose track of invalid triangles. - assert(m_invalid_triangles == std::count_if(m_triangles.begin(), m_triangles.end(), - [](const Triangle& tr) { return ! tr.valid; })); - - // Do garbage collection maybe? - if (2*m_invalid_triangles > int(m_triangles.size())) - garbage_collect(); - } - return true; -} - - -void TriangleSelector::split_triangle(int facet_idx) -{ - if (m_triangles[facet_idx].is_split()) { - // The triangle is divided already. - return; - } - - Triangle* tr = &m_triangles[facet_idx]; - - FacetSupportType old_type = tr->get_state(); - - if (tr->was_split_before() != 0) { - // This triangle is not split at the moment, but was at one point - // in history. We can just restore it and resurrect its children. - tr->set_division(-1); - for (int i=0; i<=tr->number_of_split_sides(); ++i) { - m_triangles[tr->children[i]].set_state(old_type); - m_triangles[tr->children[i]].valid = true; - --m_invalid_triangles; - } - return; - } - - // If we got here, we are about to actually split the triangle. - const double limit_squared = m_edge_limit_sqr; - - std::array& facet = tr->verts_idxs; - const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; - double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), - (*pts[0]-*pts[2]).squaredNorm(), - (*pts[1]-*pts[0]).squaredNorm() }; - - std::vector 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); - 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(sides_to_split.size(), - sides_to_split.size() == 2 ? side_to_keep : sides_to_split[0]); - - perform_split(facet_idx, old_type); -} - - -// Calculate distance of a point from a line. -bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const -{ - Vec3f diff = m_cursor.center - point; - return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; -} - - -// 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]].v; - const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; - const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; - const Vec3f& q1 = m_cursor.center + m_cursor.dir; - const Vec3f q2 = m_cursor.center - m_cursor.dir; - - 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 -{ - assert(facet < m_orig_size_indices); - // The normal is cached in mesh->stl, use it. - return (m_mesh->stl.facet_start[facet].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 (is_point_inside_cursor(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 -{ - Vec3f pts[3]; - for (int i=0; i<3; ++i) - pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; - - 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) { - undivide_triangle(tr.children[i]); - m_triangles[tr.children[i]].valid = false; - ++m_invalid_triangles; - } - tr.set_division(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. - FacetSupportType first_child_type = FacetSupportType::NONE; - for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { - if (m_triangles[tr.children[child_idx]].is_split()) - return; - 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 new_triangle_indices(m_triangles.size(), -1); - for (int i = m_orig_size_indices; i new_vertices_indices(m_vertices.size(), -1); - for (int i=m_orig_size_vertices; i= 0); - if (m_vertices[i].ref_cnt != 0) { - new_vertices_indices[i] = new_idx; - ++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]; - } - } - - // If this triangle was split before, forget it. - // Children referenced in the cache are dead by now. - tr.forget_history(); - } - - m_invalid_triangles = 0; -} - -TriangleSelector::TriangleSelector(const TriangleMesh& mesh) - : m_mesh{&mesh} -{ - reset(); - -} - - -void TriangleSelector::reset() -{ - if (! m_orig_size_indices != 0) // unless this is run from constructor - garbage_collect(); - m_vertices.clear(); - m_triangles.clear(); - for (const stl_vertex& vert : m_mesh->its.vertices) - m_vertices.emplace_back(vert); - for (const stl_triangle_vertex_indices& ind : m_mesh->its.indices) - push_triangle(ind[0], ind[1], ind[2]); - m_orig_size_vertices = m_vertices.size(); - m_orig_size_indices = m_triangles.size(); - m_invalid_triangles = 0; -} - -void TriangleSelector::render(ImGuiWrapper* imgui) -{ - Vec3d offset = wxGetApp().model().objects.front()->instances.front()->get_transformation().get_offset(); - ::glTranslatef(offset.x(), offset.y(), offset.z()); - ::glScalef(1.005f, 1.005f, 1.005f); - - int enf_cnt = 0; int blc_cnt = 0; + m_iva_enforcers.release_geometry(); + m_iva_blockers.release_geometry(); + for (const Triangle& tr : m_triangles) { if (! tr.valid || tr.is_split() || tr.get_state() == FacetSupportType::NONE) continue; @@ -1245,13 +802,13 @@ void TriangleSelector::render(ImGuiWrapper* imgui) ::glColor4f(0.f, 0.f, 1.f, 0.2f); m_iva_enforcers.render(); } - m_iva_enforcers.release_geometry(); + if (m_iva_blockers.has_VBOs()) { ::glColor4f(1.f, 0.f, 0.f, 0.2f); m_iva_blockers.render(); } - m_iva_blockers.release_geometry(); + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) @@ -1262,242 +819,9 @@ void TriangleSelector::render(ImGuiWrapper* imgui) } -void TriangleSelector::set_edge_limit(float edge_limit) -{ - float new_limit_sqr = std::pow(edge_limit, 2.f); - - if (new_limit_sqr != m_edge_limit_sqr) { - m_edge_limit_sqr = new_limit_sqr; - - // The way how triangles split may be different now, forget - // all cached splits. - garbage_collect(); - } -} - - - -void TriangleSelector::push_triangle(int a, int b, int c) -{ - for (int i : {a, b, c}) { - assert(i >= 0 && i < int(m_vertices.size())); - ++m_vertices[i].ref_cnt; - } - m_triangles.emplace_back(a, b, c); -} - - -void TriangleSelector::perform_split(int facet_idx, FacetSupportType old_state) -{ - Triangle* tr = &m_triangles[facet_idx]; - - assert(tr->is_split()); - - // Read info about how to split this triangle. - int sides_to_split = tr->number_of_split_sides(); - - // indices of triangle vertices - std::vector verts_idxs; - int idx = tr->special_side(); - for (int j=0; j<3; ++j) { - verts_idxs.push_back(tr->verts_idxs[idx++]); - if (idx == 3) - idx = 0; - } - - if (sides_to_split == 1) { - m_vertices.emplace_back((m_vertices[verts_idxs[1]].v + m_vertices[verts_idxs[2]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+2, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0]); - } - - if (sides_to_split == 2) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+4, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4]); - push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4]); - } - - if (sides_to_split == 3) { - m_vertices.emplace_back((m_vertices[verts_idxs[0]].v + m_vertices[verts_idxs[1]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+1, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[2]].v + m_vertices[verts_idxs[3]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+3, m_vertices.size() - 1); - m_vertices.emplace_back((m_vertices[verts_idxs[4]].v + m_vertices[verts_idxs[0]].v)/2.); - verts_idxs.insert(verts_idxs.begin()+5, m_vertices.size() - 1); - - push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3]); - push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5]); - push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5]); - } - - tr = &m_triangles[facet_idx]; // may have been invalidated - - // And save the children. All children should start in the same state as the triangle we just split. - assert(sides_to_split <= 3); - for (int i=0; i<=sides_to_split; ++i) { - tr->children[i] = m_triangles.size()-1-i; - m_triangles[tr->children[i]].set_state(old_state); - } -} - - -std::map> 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): - // leaf triangle: xx = FacetSupportType, yy = 0 - // 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. - - std::map> out; - for (int i=0; i data; // complete encoding of this mesh triangle - int stored_triangles = 0; // how many have been already encoded - - std::function serialize_recursive; - serialize_recursive = [this, &serialize_recursive, &stored_triangles, &data](int facet_idx) { - const Triangle& tr = 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 |= (split_sides << (stored_triangles * 4)); - data.push_back(split_sides & 0b01); - data.push_back(split_sides & 0b10); - - if (tr.is_split()) { - // 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(split_sides > 0); - assert(tr.special_side() >= 0 && tr.special_side() <= 3); - data.push_back(tr.special_side() & 0b01); - data.push_back(tr.special_side() & 0b10); - ++stored_triangles; - // Now save all children. - for (int child_idx=0; child_idx<=split_sides; ++child_idx) - serialize_recursive(tr.children[child_idx]); - } else { - // In case this is leaf, we better save information about its state. - assert(int(tr.get_state()) <= 3); - data.push_back(int(tr.get_state()) & 0b01); - data.push_back(int(tr.get_state()) & 0b10); - ++stored_triangles; - } - }; - - serialize_recursive(i); - out[i] = data; - } - - return out; -} - -void TriangleSelector::deserialize(const std::map> data) -{ - reset(); // dump any current state - for (const auto& [triangle_id, code] : data) { - assert(triangle_id < int(m_triangles.size())); - int processed_triangles = 0; - struct ProcessingInfo { - int facet_id = 0; - int processed_children = 0; - int total_children = 0; - }; - - // Vector to store all parents that have offsprings. - std::vector parents; - - while (true) { - // Read next triangle info. - int next_code = 0; - for (int i=3; i>=0; --i) { - next_code = next_code << 1; - next_code |= int(code[4 * processed_triangles + i]); - } - ++processed_triangles; - - int num_of_split_sides = (next_code & 0b11); - int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; - bool is_split = num_of_children != 0; - FacetSupportType state = FacetSupportType(next_code >> 2); - int special_side = (next_code >> 2); - - // Take care of the first iteration separately, so handling of the others is simpler. - if (parents.empty()) { - if (! is_split) { - // root is not split. just set the state and that's it. - m_triangles[triangle_id].set_state(state); - break; - } else { - // root is split, add it into list of parents and split it. - // then go to the next. - parents.push_back({triangle_id, 0, num_of_children}); - m_triangles[triangle_id].set_division(num_of_children-1, special_side); - perform_split(triangle_id, FacetSupportType::NONE); - continue; - } - } - - // 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 (is_split) { - // split the triangle and save it as parent of the next ones. - const ProcessingInfo& last = parents.back(); - int this_idx = m_triangles[last.facet_id].children[last.processed_children]; - m_triangles[this_idx].set_division(num_of_children-1, special_side); - perform_split(this_idx, FacetSupportType::NONE); - parents.push_back({this_idx, 0, num_of_children}); - } else { - // this triangle belongs to last split one - m_triangles[m_triangles[parents.back().facet_id].children[parents.back().processed_children]].set_state(state); - ++parents.back().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; - } - - } -} - #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG -void TriangleSelector::render_debug(ImGuiWrapper* imgui) +void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) { imgui->begin(std::string("TriangleSelector dialog (DEV ONLY)"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); @@ -1585,5 +909,7 @@ void TriangleSelector::render_debug(ImGuiWrapper* imgui) } #endif + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 099c9a30c..7020bb7d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -6,11 +6,11 @@ #include "slic3r/GUI/3DScene.hpp" #include "libslic3r/ObjectID.hpp" +#include "libslic3r/TriangleSelector.hpp" #include -#define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG namespace Slic3r { @@ -23,144 +23,26 @@ enum class SLAGizmoEventType : unsigned char; class ClippingPlane; -// Following class holds information about selected triangles. It also has power -// to recursively subdivide the triangles and make the selection finer. -class TriangleSelector { + +class TriangleSelectorGUI : public TriangleSelector { public: - void set_edge_limit(float edge_limit); - - // 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 fully inside the circle, subdivide where needed. - void select_patch(const Vec3f& hit, // point where to start - int facet_start, // facet that point belongs to - const Vec3f& source, // camera position (mesh coords) - const Vec3f& dir, // direction of the ray (mesh coords) - float radius_sqr, // squared radius of the cursor - FacetSupportType new_state); // enforcer or blocker? + explicit TriangleSelectorGUI(const TriangleMesh& mesh) + : TriangleSelector(mesh) {} // Render current selection. Transformation matrices are supposed // to be already set. void render(ImGuiWrapper* imgui = nullptr); - // Clear everything and make the tree empty. - void reset(); - - // Remove all unnecessary data. - void garbage_collect(); - - // Store the division trees in compact form (a long stream of - // bits for each triangle of the original mesh). - std::map> serialize() const; - - // Load serialized data. Assumes that correct mesh is loaded. - void deserialize(const std::map> data); - #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); - bool m_show_triangles{true}; + bool m_show_triangles{false}; bool m_show_invalid{false}; #endif private: - // Triangle and info about how it's split. - class Triangle { - public: - // Use TriangleSelector::push_triangle to create a new triangle. - // It increments/decrements reference counter on vertices. - Triangle(int a, int b, int c) - : verts_idxs{a, b, c}, - state{FacetSupportType(0)}, - number_of_splits{0}, - special_side_idx{0}, - old_number_of_splits{0} - {} - // Indices into m_vertices. - std::array verts_idxs; - - // Is this triangle valid or marked to be removed? - bool valid{true}; - - // Children triangles. - std::array children; - - // Set the division type. - void set_division(int sides_to_split, int special_side_idx = -1); - - // Get/set current state. - void set_state(FacetSupportType type) { assert(! is_split()); state = type; } - FacetSupportType get_state() const { assert(! is_split()); return state; } - - // Get info on how it's split. - bool is_split() const { return number_of_split_sides() != 0; } - int number_of_split_sides() const { return number_of_splits; } - int special_side() const { assert(is_split()); return special_side_idx; } - bool was_split_before() const { return old_number_of_splits != 0; } - void forget_history() { old_number_of_splits = 0; } - - private: - int number_of_splits; - int special_side_idx; - FacetSupportType state; - - // How many children were spawned during last split? - // Is not reset on remerging the triangle. - int old_number_of_splits; - }; - - struct Vertex { - explicit Vertex(const stl_vertex& vert) - : v{vert}, - ref_cnt{0} - {} - stl_vertex v; - int ref_cnt; - }; - - // Lists of vertices and triangles, both original and new - std::vector m_vertices; - std::vector m_triangles; - const TriangleMesh* m_mesh; - GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_blockers; std::array m_varrays; - - // Number of invalid triangles (to trigger garbage collection). - int m_invalid_triangles; - - // Limiting length of triangle side (squared). - float m_edge_limit_sqr = 1.f; - - // Number of original vertices and triangles. - int m_orig_size_vertices; - int m_orig_size_indices; - - // Cache for cursor position, radius and direction. - struct Cursor { - Vec3f center; - Vec3f source; - Vec3f dir; - float radius_sqr; - }; - - Cursor m_cursor; - - // Private functions: - bool select_triangle(int facet_idx, FacetSupportType type, - bool recursive_call = false); - bool is_point_inside_cursor(const Vec3f& point) const; - int vertices_inside(int facet_idx) const; - bool faces_camera(int facet) const; - void undivide_triangle(int facet_idx); - void split_triangle(int facet_idx); - void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. - bool is_pointer_in_triangle(int facet_idx) const; - bool is_edge_inside_cursor(int facet_idx) const; - void push_triangle(int a, int b, int c); - void perform_split(int facet_idx, FacetSupportType old_state); }; @@ -178,17 +60,8 @@ private: static constexpr float CursorRadiusMax = 8.f; static constexpr float CursorRadiusStep = 0.2f; - // For each model-part volume, store a list of statuses of - // individual facets (one of the enum values above). - std::vector> m_selected_facets; - - GLIndexedVertexArray m_iva; - - void update_vertex_buffers(const TriangleMesh* mesh, - int mesh_id, - FacetSupportType type, // enforcers / blockers - const std::vector* new_facets = nullptr); // nullptr -> regenerate all - + // For each model-part volume, store status and division of the triangles. + std::vector> m_triangle_selectors; public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -220,8 +93,6 @@ private: bool m_setting_angle = false; bool m_internal_stack_active = false; bool m_schedule_update = false; - - std::unique_ptr m_triangle_selector; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.