Add new (winner) neighbor index based on measurements
This commit is contained in:
parent
b14b000c73
commit
c4507842a0
11 changed files with 327 additions and 125 deletions
|
@ -322,6 +322,27 @@ FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its)
|
|||
return index;
|
||||
}
|
||||
|
||||
// Create an index of faces belonging to each vertex. The returned vector can
|
||||
// be indexed with vertex indices and contains a list of face indices for each
|
||||
// vertex.
|
||||
static std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its)
|
||||
{
|
||||
std::vector<std::vector<size_t>> index;
|
||||
|
||||
if (! its.vertices.empty()) {
|
||||
size_t res = its.indices.size() / its.vertices.size();
|
||||
index.assign(its.vertices.size(), reserve_vector<size_t>(res));
|
||||
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
|
||||
auto &face = its.indices[fi];
|
||||
index[face(0)].emplace_back(fi);
|
||||
index[face(1)].emplace_back(fi);
|
||||
index[face(2)].emplace_back(fi);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
|
||||
if (vertex_index == triangle_indices[0]) return 0;
|
||||
if (vertex_index == triangle_indices[1]) return 1;
|
||||
|
@ -329,7 +350,7 @@ static int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indic
|
|||
return -1;
|
||||
}
|
||||
|
||||
Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
|
||||
static Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
|
||||
{
|
||||
int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
|
||||
coord_t vi0 = triangle_indices[edge_index];
|
||||
|
@ -577,4 +598,44 @@ FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its)
|
|||
return index;
|
||||
}
|
||||
|
||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its)
|
||||
{
|
||||
const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
|
||||
size_t vertices_size = its.vertices.size();
|
||||
|
||||
if (indices.empty() || vertices_size == 0) return {};
|
||||
// std::vector<std::vector<size_t>> vertex_triangles = create_vertex_faces_index(indices, vertices_size);
|
||||
auto vertex_triangles = VertexFaceIndex{its};
|
||||
coord_t no_value = -1;
|
||||
std::vector<Vec3crd> neighbors(indices.size(), Vec3crd(no_value, no_value, no_value));
|
||||
for (const stl_triangle_vertex_indices& triangle_indices : indices) {
|
||||
coord_t index = &triangle_indices - &indices.front();
|
||||
Vec3crd& neighbor = neighbors[index];
|
||||
for (int edge_index = 0; edge_index < 3; ++edge_index) {
|
||||
// check if done
|
||||
coord_t& neighbor_edge = neighbor[edge_index];
|
||||
if (neighbor_edge != no_value) continue;
|
||||
Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices);
|
||||
// IMPROVE: use same vector for 2 sides of triangle
|
||||
const auto &faces_range = vertex_triangles[edge_indices[0]];
|
||||
for (const size_t &face : faces_range) {
|
||||
if (face <= index) continue;
|
||||
const stl_triangle_vertex_indices &face_indices = indices[face];
|
||||
int vertex_index = get_vertex_index(edge_indices[1], face_indices);
|
||||
// NOT Contain second vertex?
|
||||
if (vertex_index < 0) continue;
|
||||
// Has NOT oposit direction?
|
||||
if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
|
||||
neighbor_edge = face;
|
||||
neighbors[face][vertex_index] = index;
|
||||
break;
|
||||
}
|
||||
// must be paired
|
||||
assert(neighbor_edge != no_value);
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -11,4 +11,5 @@ std::vector<Vec3crd> its_create_neighbors_index_5(const indexed_triangle_set &it
|
|||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_6(const indexed_triangle_set &its);
|
||||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_7(const indexed_triangle_set &its);
|
||||
FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its);
|
||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its);
|
||||
}
|
||||
|
|
|
@ -33,9 +33,12 @@ static MeasureResult measure_index(const indexed_triangle_set &its, IndexCreator
|
|||
r.t_index_create += b.getElapsedSec();
|
||||
|
||||
b.start();
|
||||
its_split(itsn);
|
||||
auto res = its_split(itsn);
|
||||
b.stop();
|
||||
|
||||
if (res.size() != 2 || res[0].indices.size() != res[1].indices.size() )
|
||||
std::cerr << "Something is wrong, split result invalid" << std::endl;
|
||||
|
||||
r.t_split += b.getElapsedSec();
|
||||
}
|
||||
|
||||
|
@ -49,20 +52,30 @@ static indexed_triangle_set two_spheres(double detail)
|
|||
{
|
||||
auto sphere1 = its_make_sphere(10., 2 * PI / detail), sphere2 = sphere1;
|
||||
|
||||
its_transform(sphere1, Transform3f{}.translate(Vec3f{-5.f, 0.f, 0.f}));
|
||||
its_transform(sphere2, Transform3f{}.translate(Vec3f{5.f, 0.f, 0.f}));
|
||||
its_transform(sphere1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f}));
|
||||
its_transform(sphere2, identity3f().translate(Vec3f{5.f, 0.f, 0.f}));
|
||||
|
||||
its_merge(sphere1, sphere2);
|
||||
|
||||
return sphere1;
|
||||
}
|
||||
|
||||
static const std::map<std::string, indexed_triangle_set> ToMeasure = {
|
||||
{"simple", its_make_cube(10., 10., 10.) }, // this has 12 faces, 8 vertices
|
||||
constexpr double sq2 = std::sqrt(2.);
|
||||
|
||||
static const std::pair<const std::string, indexed_triangle_set> ToMeasure[] = {
|
||||
{"two_spheres_1x", two_spheres(60.)},
|
||||
{"two_spheres_2x", two_spheres(120.)},
|
||||
{"two_spheres_4x", two_spheres(240.)},
|
||||
{"two_spheres_8x", two_spheres(480.)},
|
||||
{"two_spheres_16x", two_spheres(2 * 480.)},
|
||||
{"two_spheres_32x", two_spheres(2 * 2 * 480.)},
|
||||
|
||||
// {"two_spheres_1x", two_spheres(60.)},
|
||||
// {"two_spheres_2x", two_spheres(sq2 * 60.)},
|
||||
// {"two_spheres_4x", two_spheres(2 * 60.)},
|
||||
// {"two_spheres_8x", two_spheres(sq2 * 2. * 60.)},
|
||||
// {"two_spheres_16x", two_spheres(4. * 60.)},
|
||||
// {"two_spheres_32x", two_spheres(sq2 * 4. * 60.)},
|
||||
};
|
||||
|
||||
static const auto IndexFunctions = std::make_tuple(
|
||||
|
@ -70,25 +83,53 @@ static const auto IndexFunctions = std::make_tuple(
|
|||
std::make_pair("vojta std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_2); }),
|
||||
std::make_pair("vojta tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_3); }),
|
||||
std::make_pair("filip's vertex->face based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_5); }),
|
||||
std::make_pair("vojta's vertex->face", [](const auto &its) { return measure_index(its, its_create_neighbors_index_9); }),
|
||||
std::make_pair("tamas's std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_6); }),
|
||||
std::make_pair("tamas's tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_7); }),
|
||||
std::make_pair("tamas's map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_8); }),
|
||||
std::make_pair("tamas's map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_8); })/*,
|
||||
std::make_pair("TriangleMesh split", [](const auto &its) {
|
||||
TriangleMesh m{its};
|
||||
|
||||
MeasureResult ret;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
TriangleMesh m{its};
|
||||
Benchmark b;
|
||||
b.start();
|
||||
m.repair();
|
||||
m.split();
|
||||
auto res = m.split();
|
||||
b.stop();
|
||||
|
||||
if (res.size() != 2 || res[0]->size() != res[1]->size())
|
||||
std::cerr << "Something is wrong, split result invalid" << std::endl;
|
||||
|
||||
ret.t_split += b.getElapsedSec();
|
||||
}
|
||||
ret.t_split /= 10;
|
||||
|
||||
return ret;
|
||||
})
|
||||
})*/
|
||||
|
||||
// std::make_pair("Vojta's vertex->face index", [](const auto &its){
|
||||
// Benchmark b;
|
||||
// b.start();
|
||||
// auto index = create_vertex_faces_index(its);
|
||||
// b.stop();
|
||||
|
||||
// if (index.size() != its.vertices.size())
|
||||
// std::cerr << "Something went wrong!";
|
||||
|
||||
// return MeasureResult{b.getElapsedSec(), 0., 0.};
|
||||
// }),
|
||||
// std::make_pair("Tamas's vertex->face index", [](const auto &its){
|
||||
// Benchmark b;
|
||||
// b.start();
|
||||
// VertexFaceIndex index{its};
|
||||
// b.stop();
|
||||
|
||||
// if (index.size() < its.vertices.size())
|
||||
// std::cerr << "Something went wrong!";
|
||||
|
||||
// return MeasureResult{b.getElapsedSec(), 0., 0.};
|
||||
// })
|
||||
);
|
||||
|
||||
static constexpr size_t IndexFuncNum = std::tuple_size_v<decltype (IndexFunctions)>;
|
||||
|
@ -99,16 +140,18 @@ int main(const int argc, const char * argv[])
|
|||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
std::map<std::string, std::array<MeasureResult, IndexFuncNum> > results;
|
||||
std::array<MeasureResult, IndexFuncNum> results[std::size(ToMeasure)];
|
||||
std::array<std::string, IndexFuncNum> funcnames;
|
||||
|
||||
for (auto &m : ToMeasure) {
|
||||
for (size_t i = 0; i < std::size(ToMeasure); ++i) {
|
||||
auto &m = ToMeasure[i];
|
||||
auto &name = m.first;
|
||||
auto &mesh = m.second;
|
||||
libnest2d::opt::metaloop::apply([&mesh, &name, &results, &funcnames](int N, auto &e) {
|
||||
std::cout << "Mesh " << name << " has " << mesh.indices.size() << " faces and " << mesh.vertices.size() << " vertices." << std::endl;
|
||||
libnest2d::opt::metaloop::apply([&mesh, i, &results, &funcnames](int N, auto &e) {
|
||||
MeasureResult r = e.second(mesh);
|
||||
funcnames[N] = e.first;
|
||||
results[name][N] = r;
|
||||
results[i][N] = r;
|
||||
}, IndexFunctions);
|
||||
}
|
||||
|
||||
|
@ -129,10 +172,12 @@ int main(const int argc, const char * argv[])
|
|||
|
||||
out << std::endl;
|
||||
|
||||
for (auto &[name, result] : results) {
|
||||
for (size_t i = 0; i < std::size(ToMeasure); ++i) {
|
||||
const auto &result_row = results[i];
|
||||
const std::string &name = ToMeasure[i].first;
|
||||
out << name << ";";
|
||||
for (auto &r : result)
|
||||
out << r.full_time() << ";";
|
||||
for (auto &r : result_row)
|
||||
out << r.t_index_create << ";";
|
||||
|
||||
out << std::endl;
|
||||
}
|
||||
|
|
|
@ -74,29 +74,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/// A very simple range concept implementation with iterator-like objects.
|
||||
template<class It> class Range
|
||||
{
|
||||
It from, to;
|
||||
|
||||
public:
|
||||
// The class is ready for range based for loops.
|
||||
It begin() const { return from; }
|
||||
It end() const { return to; }
|
||||
|
||||
// The iterator type can be obtained this way.
|
||||
using Type = It;
|
||||
|
||||
Range() = default;
|
||||
Range(It &&b, It &&e)
|
||||
: from(std::forward<It>(b)), to(std::forward<It>(e))
|
||||
{}
|
||||
|
||||
// Some useful container-like methods...
|
||||
inline size_t size() const { return end() - begin(); }
|
||||
inline bool empty() const { return size() == 0; }
|
||||
};
|
||||
|
||||
template<class C> bool all_of(const C &container)
|
||||
{
|
||||
return std::all_of(container.begin(),
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "libnest2d/tools/benchmark.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace meshsplit_detail {
|
||||
|
||||
template<class Its, class Enable = void> struct ItsWithNeighborsIndex_ {
|
||||
|
@ -58,7 +57,7 @@ std::vector<size_t> its_find_unvisited_neighbors(
|
|||
size_t facet_idx = pop();
|
||||
const auto &neighbors = neighbor_index[facet_idx];
|
||||
for (auto neighbor_idx : neighbors) {
|
||||
if (neighbor_idx >= 0 && !visited[size_t(neighbor_idx)]) {
|
||||
if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) {
|
||||
visited[size_t(neighbor_idx)] = true;
|
||||
push(stack_el(neighbor_idx));
|
||||
ret.emplace_back(size_t(neighbor_idx));
|
||||
|
@ -111,6 +110,8 @@ void its_split(const Its &m, OutputIt out_it)
|
|||
|
||||
// Create a new mesh for the part that was just split off.
|
||||
indexed_triangle_set mesh;
|
||||
mesh.indices.reserve(facets.size());
|
||||
mesh.vertices.reserve(facets.size() * 3);
|
||||
|
||||
// Assign the facets to the new mesh.
|
||||
for (size_t face_id : facets) {
|
||||
|
|
|
@ -60,11 +60,19 @@ using Matrix3d = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>;
|
|||
using Matrix4f = Eigen::Matrix<float, 4, 4, Eigen::DontAlign>;
|
||||
using Matrix4d = Eigen::Matrix<double, 4, 4, Eigen::DontAlign>;
|
||||
|
||||
template<int N, class T>
|
||||
using Transform = Eigen::Transform<float, N, Eigen::Affine, Eigen::DontAlign>;
|
||||
|
||||
using Transform2f = Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
|
||||
// I don't know why Eigen::Transform::Identity() return a const object...
|
||||
template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::Identity(); }
|
||||
inline const auto &identity3f = identity<3, float>;
|
||||
inline const auto &identity3d = identity<3, double>;
|
||||
|
||||
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y()); }
|
||||
|
||||
template<int Options>
|
||||
|
@ -494,6 +502,7 @@ namespace cereal {
|
|||
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
}
|
||||
|
||||
// To be able to use Vec<> and Mat<> in range based for loops:
|
||||
namespace Eigen {
|
||||
template<class T, int N, int M>
|
||||
T* begin(Slic3r::Mat<N, M, T> &mat) { return mat.data(); }
|
||||
|
|
|
@ -198,14 +198,14 @@ static std::vector<bool> create_exclude_mask(
|
|||
|
||||
std::vector<bool> exclude_mask(its.indices.size(), false);
|
||||
|
||||
std::vector< std::vector<size_t> > neighbor_index =
|
||||
create_vertex_faces_index(its);
|
||||
VertexFaceIndex neighbor_index{its};
|
||||
|
||||
auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const std::vector<size_t> &neighbors = neighbor_index[face(i)];
|
||||
for (size_t fi_n : neighbors) exclude_mask[fi_n] = true;
|
||||
const auto &neighbors_range = neighbor_index[face(i)];
|
||||
for (size_t fi_n : neighbors_range)
|
||||
exclude_mask[fi_n] = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -669,24 +669,6 @@ void TriangleMesh::restore_optional()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its)
|
||||
{
|
||||
std::vector<std::vector<size_t>> index;
|
||||
|
||||
if (! its.vertices.empty()) {
|
||||
size_t res = its.indices.size() / its.vertices.size();
|
||||
index.assign(its.vertices.size(), reserve_vector<size_t>(res));
|
||||
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
|
||||
auto &face = its.indices[fi];
|
||||
index[face(0)].emplace_back(fi);
|
||||
index[face(1)].emplace_back(fi);
|
||||
index[face(2)].emplace_back(fi);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
// Create a mapping from triangle edge into face.
|
||||
struct EdgeToFace {
|
||||
// Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
|
||||
|
@ -1197,49 +1179,81 @@ bool its_is_splittable(const indexed_triangle_set &its)
|
|||
return its_is_splittable<>(its);
|
||||
}
|
||||
|
||||
void VertexFaceIndex::create(const indexed_triangle_set &its)
|
||||
{
|
||||
m_vertex_to_face_start.assign(its.vertices.size() + 1, 0);
|
||||
// 1) Calculate vertex incidence by scatter.
|
||||
for (auto &face : its.indices) {
|
||||
++ m_vertex_to_face_start[face(0) + 1];
|
||||
++ m_vertex_to_face_start[face(1) + 1];
|
||||
++ m_vertex_to_face_start[face(2) + 1];
|
||||
}
|
||||
// 2) Prefix sum to calculate offsets to m_vertex_faces_all.
|
||||
for (size_t i = 2; i < m_vertex_to_face_start.size(); ++ i)
|
||||
m_vertex_to_face_start[i] += m_vertex_to_face_start[i - 1];
|
||||
// 3) Scatter indices of faces incident to a vertex into m_vertex_faces_all.
|
||||
m_vertex_faces_all.assign(m_vertex_to_face_start.back(), 0);
|
||||
for (size_t face_idx = 0; face_idx < its.indices.size(); ++ face_idx) {
|
||||
auto &face = its.indices[face_idx];
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
m_vertex_faces_all[m_vertex_to_face_start[face(i)] ++] = face_idx;
|
||||
}
|
||||
// 4) The previous loop modified m_vertex_to_face_start. Revert the change.
|
||||
for (auto i = int(m_vertex_to_face_start.size()) - 1; i > 0; -- i)
|
||||
m_vertex_to_face_start[i] = m_vertex_to_face_start[i - 1];
|
||||
m_vertex_to_face_start.front() = 0;
|
||||
}
|
||||
|
||||
static int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
|
||||
if (int(vertex_index) == triangle_indices[0]) return 0;
|
||||
if (int(vertex_index) == triangle_indices[1]) return 1;
|
||||
if (int(vertex_index) == triangle_indices[2]) return 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
|
||||
{
|
||||
int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
|
||||
coord_t vi0 = triangle_indices[edge_index];
|
||||
coord_t vi1 = triangle_indices[next_edge_index];
|
||||
return Vec2crd(vi0, vi1);
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its)
|
||||
{
|
||||
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
|
||||
const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
|
||||
size_t vertices_size = its.vertices.size();
|
||||
|
||||
std::vector<EdgeToFace> edges_map = create_edge_map(its, []{});
|
||||
|
||||
// Assign a unique common edge id to touching triangle edges.
|
||||
for (size_t i = 0; i < edges_map.size(); ++ i) {
|
||||
EdgeToFace &edge_i = edges_map[i];
|
||||
if (edge_i.face == -1)
|
||||
// This edge has been connected to some neighbor already.
|
||||
continue;
|
||||
// Unconnected edge. Find its neighbor with the correct orientation.
|
||||
size_t j;
|
||||
bool found = false;
|
||||
for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
|
||||
if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) {
|
||||
// Faces touching with opposite oriented edges and none of the edges is connected yet.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (! found) {
|
||||
//FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
|
||||
// admesh can assign the same edge ID to more than two facets (which is
|
||||
// still topologically correct), so we have to search for a duplicate of
|
||||
// this edge too in case it was already seen in this orientation
|
||||
for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
|
||||
if (edges_map[j].face != -1) {
|
||||
// Faces touching with equally oriented edges and none of the edges is connected yet.
|
||||
found = true;
|
||||
if (indices.empty() || vertices_size == 0) return {};
|
||||
auto vertex_triangles = VertexFaceIndex{its};
|
||||
coord_t 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) {
|
||||
coord_t index = &triangle_indices - &indices.front();
|
||||
Vec3i& neighbor = neighbors[index];
|
||||
for (int edge_index = 0; edge_index < 3; ++edge_index) {
|
||||
// check if done
|
||||
coord_t& neighbor_edge = neighbor[edge_index];
|
||||
if (neighbor_edge != no_value) continue;
|
||||
Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices);
|
||||
// IMPROVE: use same vector for 2 sides of triangle
|
||||
const auto &faces_range = vertex_triangles[edge_indices[0]];
|
||||
for (const size_t &face : faces_range) {
|
||||
if (int(face) <= index) continue;
|
||||
const stl_triangle_vertex_indices &face_indices = indices[face];
|
||||
int vertex_index = get_vertex_index(edge_indices[1], face_indices);
|
||||
// NOT Contain second vertex?
|
||||
if (vertex_index < 0) continue;
|
||||
// Has NOT oposit direction?
|
||||
if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
|
||||
neighbor_edge = face;
|
||||
neighbors[face][vertex_index] = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
EdgeToFace &edge_j = edges_map[j];
|
||||
out[edge_i.face](std::abs(edge_i.face_edge) - 1) = edge_j.face;
|
||||
out[edge_j.face](std::abs(edge_j.face_edge) - 1) = edge_i.face;
|
||||
// Mark the edge as connected.
|
||||
edge_j.face = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -88,10 +88,30 @@ private:
|
|||
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
|
||||
};
|
||||
|
||||
// Create an index of faces belonging to each vertex. The returned vector can
|
||||
// be indexed with vertex indices and contains a list of face indices for each
|
||||
// vertex.
|
||||
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its);
|
||||
// Index of face indices incident with a vertex index.
|
||||
struct VertexFaceIndex
|
||||
{
|
||||
public:
|
||||
using iterator = std::vector<size_t>::const_iterator;
|
||||
|
||||
VertexFaceIndex(const indexed_triangle_set &its) { this->create(its); }
|
||||
VertexFaceIndex() {}
|
||||
|
||||
void create(const indexed_triangle_set &its);
|
||||
void clear() { m_vertex_to_face_start.clear(); m_vertex_faces_all.clear(); }
|
||||
|
||||
// Iterators of face indices incident with the input vertex_id.
|
||||
iterator begin(size_t vertex_id) const throw() { return m_vertex_faces_all.begin() + m_vertex_to_face_start[vertex_id]; }
|
||||
iterator end (size_t vertex_id) const throw() { return m_vertex_faces_all.begin() + m_vertex_to_face_start[vertex_id + 1]; }
|
||||
// Vertex incidence.
|
||||
size_t count(size_t vertex_id) const throw() { return m_vertex_to_face_start[vertex_id + 1] - m_vertex_to_face_start[vertex_id]; }
|
||||
|
||||
const Range<iterator> operator[](size_t vertex_id) const { return {begin(vertex_id), end(vertex_id)}; }
|
||||
|
||||
private:
|
||||
std::vector<size_t> m_vertex_to_face_start;
|
||||
std::vector<size_t> m_vertex_faces_all;
|
||||
};
|
||||
|
||||
// Index of face indices incident with a vertex index.
|
||||
struct VertexFaceIndex
|
||||
|
|
|
@ -306,6 +306,29 @@ IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
|
|||
template<class T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
// A very simple range concept implementation with iterator-like objects.
|
||||
// This should be replaced by std::ranges::subrange (C++20)
|
||||
template<class It> class Range
|
||||
{
|
||||
It from, to;
|
||||
public:
|
||||
|
||||
// The class is ready for range based for loops.
|
||||
It begin() const { return from; }
|
||||
It end() const { return to; }
|
||||
|
||||
// The iterator type can be obtained this way.
|
||||
using iterator = It;
|
||||
using value_type = typename std::iterator_traits<It>::value_type;
|
||||
|
||||
Range() = default;
|
||||
Range(It b, It e) : from(std::move(b)), to(std::move(e)) {}
|
||||
|
||||
// Some useful container-like methods...
|
||||
inline size_t size() const { return end() - begin(); }
|
||||
inline bool empty() const { return size() == 0; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,35 +17,86 @@ TEST_CASE("Split empty mesh", "[its_split][its]") {
|
|||
TEST_CASE("Split simple mesh consisting of one part", "[its_split][its]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
TriangleMesh cube = make_cube(10., 10., 10.);
|
||||
auto cube = its_make_cube(10., 10., 10.);
|
||||
|
||||
std::vector<indexed_triangle_set> res = its_split(cube.its);
|
||||
std::vector<indexed_triangle_set> res = its_split(cube);
|
||||
|
||||
REQUIRE(res.size() == 1);
|
||||
REQUIRE(res.front().indices.size() == cube.its.indices.size());
|
||||
REQUIRE(res.front().vertices.size() == cube.its.vertices.size());
|
||||
REQUIRE(res.front().indices.size() == cube.indices.size());
|
||||
REQUIRE(res.front().vertices.size() == cube.vertices.size());
|
||||
}
|
||||
|
||||
TEST_CASE("Split two merged spheres", "[its_split][its]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
TriangleMesh sphere1 = make_sphere(10., 2 * PI / 200.), sphere2 = sphere1;
|
||||
|
||||
sphere1.translate(-5.f, 0.f, 0.f);
|
||||
sphere2.translate( 5.f, 0.f, 0.f);
|
||||
|
||||
sphere1.merge(sphere2);
|
||||
sphere1.require_shared_vertices();
|
||||
|
||||
std::vector<indexed_triangle_set> parts = its_split(sphere1.its);
|
||||
|
||||
REQUIRE(parts.size() == 2);
|
||||
|
||||
void debug_write_obj(const std::vector<indexed_triangle_set> &res, const std::string &name)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
size_t part_idx = 0;
|
||||
for (auto &part_its : parts) {
|
||||
its_write_obj(part_its, (std::string("part_its") + std::to_string(part_idx++) + ".obj").c_str());
|
||||
for (auto &part_its : res) {
|
||||
its_write_obj(part_its, (name + std::to_string(part_idx++) + ".obj").c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Split two non-watertight mesh", "[its_split][its]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
auto cube1 = its_make_cube(10., 10., 10.);
|
||||
cube1.indices.pop_back();
|
||||
auto cube2 = cube1;
|
||||
|
||||
its_transform(cube1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f}));
|
||||
its_transform(cube2, identity3f().translate(Vec3f{5.f, 0.f, 0.f}));
|
||||
|
||||
its_merge(cube1, cube2);
|
||||
|
||||
std::vector<indexed_triangle_set> res = its_split(cube1);
|
||||
|
||||
REQUIRE(res.size() == 2);
|
||||
REQUIRE(res[0].indices.size() == res[1].indices.size());
|
||||
REQUIRE(res[0].indices.size() == cube2.indices.size());
|
||||
REQUIRE(res[0].vertices.size() == res[1].vertices.size());
|
||||
REQUIRE(res[0].vertices.size() == cube2.vertices.size());
|
||||
|
||||
debug_write_obj(res, "parts_non_watertight");
|
||||
}
|
||||
|
||||
TEST_CASE("Split non-manifold mesh", "[its_split][its]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
auto cube = its_make_cube(10., 10., 10.), cube_low = cube;
|
||||
|
||||
its_transform(cube_low, identity3f().translate(Vec3f{10.f, 10.f, 10.f}));
|
||||
its_merge(cube, cube_low);
|
||||
its_merge_vertices(cube);
|
||||
|
||||
std::vector<indexed_triangle_set> res = its_split(cube);
|
||||
|
||||
REQUIRE(res.size() == 2);
|
||||
REQUIRE(res[0].indices.size() == res[1].indices.size());
|
||||
REQUIRE(res[0].indices.size() == cube_low.indices.size());
|
||||
REQUIRE(res[0].vertices.size() == res[1].vertices.size());
|
||||
REQUIRE(res[0].vertices.size() == cube_low.vertices.size());
|
||||
|
||||
debug_write_obj(res, "cubes_non_manifold");
|
||||
}
|
||||
|
||||
TEST_CASE("Split two watertight meshes", "[its_split][its]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
auto sphere1 = its_make_sphere(10., 2 * PI / 200.), sphere2 = sphere1;
|
||||
|
||||
its_transform(sphere1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f}));
|
||||
its_transform(sphere2, identity3f().translate(Vec3f{5.f, 0.f, 0.f}));
|
||||
|
||||
its_merge(sphere1, sphere2);
|
||||
|
||||
std::vector<indexed_triangle_set> res = its_split(sphere1);
|
||||
|
||||
REQUIRE(res.size() == 2);
|
||||
REQUIRE(res[0].indices.size() == res[1].indices.size());
|
||||
REQUIRE(res[0].indices.size() == sphere2.indices.size());
|
||||
REQUIRE(res[0].vertices.size() == res[1].vertices.size());
|
||||
REQUIRE(res[0].vertices.size() == sphere2.vertices.size());
|
||||
|
||||
debug_write_obj(res, "parts_watertight");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue