8a2a9dba2f
TriangleMesh newly only holds indexed_triangle_set and TriangleMeshStats. TriangleMeshStats contains an excerpt of stl_stats. TriangleMeshStats are updated when initializing with indexed_triangle_set. Admesh triangle mesh fixing is newly only used when loading an STL. AMF / 3MF / OBJ file formats are already indexed triangle sets, thus they are no more converted to admesh stl_file format, nor fixed through admesh repair machinery. When importing AMF / 3MF / OBJ files, volume is calculated and if negative, all faces are flipped. Also a bounding box and number of open edges is calculated. Implemented its_number_of_patches(), its_num_open_edges() Optimized its_split(), its_is_splittable() using a visitor pattern. Reworked QHull integration into TriangleMesh: 1) Face normals were not right. 2) Indexed triangle set is newly emitted instead of duplicating vertices for each face. Fixed cut_mesh(): Orient the triangulated faces correctly.
250 lines
9.3 KiB
C++
250 lines
9.3 KiB
C++
#ifndef MESHSPLITIMPL_HPP
|
|
#define MESHSPLITIMPL_HPP
|
|
|
|
#include "TriangleMesh.hpp"
|
|
#include "libnest2d/tools/benchmark.h"
|
|
#include "Execution/ExecutionTBB.hpp"
|
|
|
|
namespace Slic3r {
|
|
|
|
template<class ExPolicy>
|
|
std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its);
|
|
|
|
namespace meshsplit_detail {
|
|
|
|
template<class Its, class Enable = void> struct ItsWithNeighborsIndex_ {
|
|
using Index = typename Its::Index;
|
|
static const indexed_triangle_set &get_its(const Its &m) { return m.get_its();}
|
|
static const Index &get_index(const Its &m) { return m.get_index(); }
|
|
};
|
|
|
|
// Define a default neighbors index for indexed_triangle_set
|
|
template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
|
|
using Index = std::vector<Vec3i>;
|
|
static const indexed_triangle_set &get_its(const indexed_triangle_set &its) noexcept { return its; }
|
|
static Index get_index(const indexed_triangle_set &its) noexcept
|
|
{
|
|
return create_face_neighbors_index(ex_tbb, its);
|
|
}
|
|
};
|
|
|
|
// Discover connected patches of facets one by one.
|
|
template<class NeighborIndex>
|
|
struct NeighborVisitor {
|
|
NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
|
|
its(its), neighbor_index(neighbor_index) {
|
|
m_visited.assign(its.indices.size(), false);
|
|
m_facestack.reserve(its.indices.size());
|
|
}
|
|
NeighborVisitor(const indexed_triangle_set &its, NeighborIndex &&aneighbor_index) :
|
|
its(its), neighbor_index(m_neighbor_index_data), m_neighbor_index_data(std::move(aneighbor_index)) {
|
|
m_visited.assign(its.indices.size(), false);
|
|
m_facestack.reserve(its.indices.size());
|
|
}
|
|
|
|
template<typename Visitor>
|
|
void visit(Visitor visitor)
|
|
{
|
|
// find the next unvisited facet and push the index
|
|
auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
|
|
m_seed = facet - m_visited.begin();
|
|
|
|
if (facet != m_visited.end()) {
|
|
// Skip this element in the next round.
|
|
auto idx = m_seed ++;
|
|
if (! visitor(idx))
|
|
return;
|
|
this->push(idx);
|
|
m_visited[idx] = true;
|
|
while (! m_facestack.empty()) {
|
|
size_t facet_idx = this->pop();
|
|
for (auto neighbor_idx : neighbor_index[facet_idx]) {
|
|
assert(neighbor_idx < int(m_visited.size()));
|
|
if (neighbor_idx >= 0 && !m_visited[neighbor_idx]) {
|
|
if (! visitor(size_t(neighbor_idx)))
|
|
return;
|
|
m_visited[neighbor_idx] = true;
|
|
this->push(stack_el(neighbor_idx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const indexed_triangle_set &its;
|
|
const NeighborIndex &neighbor_index;
|
|
|
|
private:
|
|
// If initialized with &&neighbor_index, take the ownership of the data.
|
|
const NeighborIndex m_neighbor_index_data;
|
|
|
|
std::vector<char> m_visited;
|
|
|
|
using stack_el = size_t;
|
|
std::vector<stack_el> m_facestack;
|
|
void push(const stack_el &s) { m_facestack.emplace_back(s); }
|
|
stack_el pop() { stack_el ret = m_facestack.back(); m_facestack.pop_back(); return ret; }
|
|
|
|
// Last face visited.
|
|
size_t m_seed { 0 };
|
|
};
|
|
|
|
} // namespace meshsplit_detail
|
|
|
|
// Funky wrapper for timinig of its_split() using various neighbor index creating methods, see sandboxes/its_neighbor_index/main.cpp
|
|
template<class IndexT> struct ItsNeighborsWrapper
|
|
{
|
|
using Index = IndexT;
|
|
const indexed_triangle_set &its;
|
|
const IndexT &index_ref;
|
|
const IndexT index;
|
|
|
|
// Keeping a reference to index, the caller is responsible for keeping the index alive.
|
|
ItsNeighborsWrapper(const indexed_triangle_set &its, const IndexT &index) : its{its}, index_ref{index} {}
|
|
// Taking ownership of the index.
|
|
ItsNeighborsWrapper(const indexed_triangle_set &its, IndexT &&aindex) : its{its}, index_ref{index}, index(std::move(aindex)) {}
|
|
|
|
const auto& get_its() const noexcept { return its; }
|
|
const auto& get_index() const noexcept { return index_ref; }
|
|
};
|
|
|
|
// Splits a mesh into multiple meshes when possible.
|
|
template<class Its, class OutputIt>
|
|
void its_split(const Its &m, OutputIt out_it)
|
|
{
|
|
using namespace meshsplit_detail;
|
|
|
|
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
|
|
|
|
struct VertexConv {
|
|
size_t part_id = std::numeric_limits<size_t>::max();
|
|
size_t vertex_image;
|
|
};
|
|
std::vector<VertexConv> vidx_conv(its.vertices.size());
|
|
|
|
meshsplit_detail::NeighborVisitor visitor(its, meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
|
|
|
std::vector<size_t> facets;
|
|
for (size_t part_id = 0;; ++part_id) {
|
|
// Collect all faces of the next patch.
|
|
facets.clear();
|
|
visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
|
|
if (facets.empty())
|
|
break;
|
|
|
|
// Create a new mesh for the part that was just split off.
|
|
indexed_triangle_set mesh;
|
|
mesh.indices.reserve(facets.size());
|
|
mesh.vertices.reserve(std::min(facets.size() * 3, its.vertices.size()));
|
|
|
|
// Assign the facets to the new mesh.
|
|
for (size_t face_id : facets) {
|
|
const auto &face = its.indices[face_id];
|
|
Vec3i new_face;
|
|
for (size_t v = 0; v < 3; ++v) {
|
|
auto vi = face(v);
|
|
|
|
if (vidx_conv[vi].part_id != part_id) {
|
|
vidx_conv[vi] = {part_id, mesh.vertices.size()};
|
|
mesh.vertices.emplace_back(its.vertices[size_t(vi)]);
|
|
}
|
|
|
|
new_face(v) = vidx_conv[vi].vertex_image;
|
|
}
|
|
|
|
mesh.indices.emplace_back(new_face);
|
|
}
|
|
|
|
out_it = std::move(mesh);
|
|
}
|
|
}
|
|
|
|
template<class Its>
|
|
std::vector<indexed_triangle_set> its_split(const Its &its)
|
|
{
|
|
auto ret = reserve_vector<indexed_triangle_set>(3);
|
|
its_split(its, std::back_inserter(ret));
|
|
|
|
return ret;
|
|
}
|
|
|
|
template<class Its>
|
|
bool its_is_splittable(const Its &m)
|
|
{
|
|
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
|
bool has_some = false;
|
|
bool has_some2 = false;
|
|
// Traverse the 1st patch fully.
|
|
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
|
|
if (has_some)
|
|
// Just check whether there is any face of the 2nd patch.
|
|
visitor.visit([&has_some2](size_t idx) { has_some2 = true; return false; });
|
|
return has_some && has_some2;
|
|
}
|
|
|
|
template<class Its>
|
|
size_t its_number_of_patches(const Its &m)
|
|
{
|
|
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
|
size_t num_patches = 0;
|
|
for (;;) {
|
|
bool has_some = false;
|
|
// Traverse the 1st patch fully.
|
|
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
|
|
if (! has_some)
|
|
break;
|
|
++ num_patches;
|
|
}
|
|
return num_patches;
|
|
}
|
|
|
|
template<class ExPolicy>
|
|
std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its)
|
|
{
|
|
const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
|
|
|
|
if (indices.empty()) return {};
|
|
|
|
assert(! its.vertices.empty());
|
|
|
|
auto vertex_triangles = VertexFaceIndex{its};
|
|
static constexpr int no_value = -1;
|
|
std::vector<Vec3i> neighbors(indices.size(),
|
|
Vec3i(no_value, no_value, no_value));
|
|
|
|
//for (const stl_triangle_vertex_indices& triangle_indices : indices) {
|
|
execution::for_each(ex, size_t(0), indices.size(),
|
|
[&neighbors, &indices, &vertex_triangles] (size_t face_idx)
|
|
{
|
|
Vec3i& neighbor = neighbors[face_idx];
|
|
const stl_triangle_vertex_indices & triangle_indices = indices[face_idx];
|
|
for (int edge_index = 0; edge_index < 3; ++edge_index) {
|
|
// check if done
|
|
int& neighbor_edge = neighbor[edge_index];
|
|
if (neighbor_edge != no_value)
|
|
// This edge already has a neighbor assigned.
|
|
continue;
|
|
Vec2i edge_indices = its_triangle_edge(triangle_indices, edge_index);
|
|
// IMPROVE: use same vector for 2 sides of triangle
|
|
for (const size_t other_face : vertex_triangles[edge_indices[0]]) {
|
|
if (other_face <= face_idx) continue;
|
|
const stl_triangle_vertex_indices &face_indices = indices[other_face];
|
|
int vertex_index = its_triangle_vertex_index(face_indices, edge_indices[1]);
|
|
// NOT Contain second vertex?
|
|
if (vertex_index < 0) continue;
|
|
// Has NOT oposite direction?
|
|
if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
|
|
neighbor_edge = other_face;
|
|
neighbors[other_face][vertex_index] = face_idx;
|
|
break;
|
|
}
|
|
}
|
|
}, execution::max_concurrency(ex));
|
|
|
|
return neighbors;
|
|
}
|
|
|
|
} // namespace Slic3r
|
|
|
|
#endif // MESHSPLITIMPL_HPP
|