From c4507842a05b6274cc519675d5a73db6086556c8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Jun 2021 15:45:11 +0200 Subject: [PATCH] Add new (winner) neighbor index based on measurements --- .../its_neighbor_index/ItsNeighborIndex.cpp | 65 +++++++++- .../its_neighbor_index/ItsNeighborIndex.hpp | 1 + sandboxes/its_neighbor_index/main.cpp | 77 ++++++++--- src/libslic3r/MTUtils.hpp | 23 ---- src/libslic3r/MeshSplitImpl.hpp | 5 +- src/libslic3r/Point.hpp | 9 ++ src/libslic3r/SLAPrintSteps.cpp | 8 +- src/libslic3r/TriangleMesh.cpp | 120 ++++++++++-------- src/libslic3r/TriangleMesh.hpp | 28 +++- src/libslic3r/libslic3r.h | 23 ++++ tests/libslic3r/test_indexed_triangle_set.cpp | 93 +++++++++++--- 11 files changed, 327 insertions(+), 125 deletions(-) diff --git a/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp b/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp index 72a6b6f05e..d8f7e9d841 100644 --- a/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp +++ b/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp @@ -32,7 +32,7 @@ FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its) const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); assert(max_vertex_id_bits <= 32); - std::unordered_map< EdgeID, Edge > edge_index; + std::unordered_map< EdgeID, Edge> edge_index; // Edge id is constructed by concatenating two vertex ids, starting with // the lowest in MSB @@ -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> create_vertex_faces_index(const indexed_triangle_set &its) +{ + std::vector> index; + + if (! its.vertices.empty()) { + size_t res = its.indices.size() / its.vertices.size(); + index.assign(its.vertices.size(), reserve_vector(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 its_create_neighbors_index_9(const indexed_triangle_set &its) +{ + const std::vector &indices = its.indices; + size_t vertices_size = its.vertices.size(); + + if (indices.empty() || vertices_size == 0) return {}; + // std::vector> vertex_triangles = create_vertex_faces_index(indices, vertices_size); + auto vertex_triangles = VertexFaceIndex{its}; + coord_t no_value = -1; + std::vector 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 diff --git a/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp b/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp index df377f76dc..acfc14da84 100644 --- a/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp +++ b/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp @@ -11,4 +11,5 @@ std::vector its_create_neighbors_index_5(const indexed_triangle_set &it std::vector> its_create_neighbors_index_6(const indexed_triangle_set &its); std::vector> its_create_neighbors_index_7(const indexed_triangle_set &its); FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its); +std::vector its_create_neighbors_index_9(const indexed_triangle_set &its); } diff --git a/sandboxes/its_neighbor_index/main.cpp b/sandboxes/its_neighbor_index/main.cpp index 2ae6f47cd0..69dcbb2401 100644 --- a/sandboxes/its_neighbor_index/main.cpp +++ b/sandboxes/its_neighbor_index/main.cpp @@ -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 ToMeasure = { - {"simple", its_make_cube(10., 10., 10.) }, // this has 12 faces, 8 vertices +constexpr double sq2 = std::sqrt(2.); + +static const std::pair 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; @@ -99,16 +140,18 @@ int main(const int argc, const char * argv[]) { using namespace Slic3r; - std::map > results; + std::array results[std::size(ToMeasure)]; std::array 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; } diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 9e77aa90af..e60918fab6 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -74,29 +74,6 @@ public: } }; -/// A very simple range concept implementation with iterator-like objects. -template 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(b)), to(std::forward(e)) - {} - - // Some useful container-like methods... - inline size_t size() const { return end() - begin(); } - inline bool empty() const { return size() == 0; } -}; - template bool all_of(const C &container) { return std::all_of(container.begin(), diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp index c5662095fa..1adbf09a3b 100644 --- a/src/libslic3r/MeshSplitImpl.hpp +++ b/src/libslic3r/MeshSplitImpl.hpp @@ -5,7 +5,6 @@ #include "libnest2d/tools/benchmark.h" namespace Slic3r { - namespace meshsplit_detail { template struct ItsWithNeighborsIndex_ { @@ -58,7 +57,7 @@ std::vector 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) { diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index c69fa27e1f..20726270d8 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -60,11 +60,19 @@ using Matrix3d = Eigen::Matrix; using Matrix4f = Eigen::Matrix; using Matrix4d = Eigen::Matrix; +template +using Transform = Eigen::Transform; + using Transform2f = Eigen::Transform; using Transform2d = Eigen::Transform; using Transform3f = Eigen::Transform; using Transform3d = Eigen::Transform; +// I don't know why Eigen::Transform::Identity() return a const object... +template Transform identity() { return Transform::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 @@ -494,6 +502,7 @@ namespace cereal { template 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 T* begin(Slic3r::Mat &mat) { return mat.data(); } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index a640d5eacc..b72221fb3e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -198,14 +198,14 @@ static std::vector create_exclude_mask( std::vector exclude_mask(its.indices.size(), false); - std::vector< std::vector > 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 &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; } }; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 64d8a8e4b7..b81ab03736 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -669,24 +669,6 @@ void TriangleMesh::restore_optional() } } -std::vector> create_vertex_faces_index(const indexed_triangle_set &its) -{ - std::vector> index; - - if (! its.vertices.empty()) { - size_t res = its.indices.size() / its.vertices.size(); - index.assign(its.vertices.size(), reserve_vector(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 its_create_neighbors_index(const indexed_triangle_set &its) { - std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); + const std::vector &indices = its.indices; + size_t vertices_size = its.vertices.size(); - std::vector 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; + if (indices.empty() || vertices_size == 0) return {}; + auto vertex_triangles = VertexFaceIndex{its}; + coord_t no_value = -1; + std::vector 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) { - //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; - 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 diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index d02f498e9e..feacff2c2a 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -88,10 +88,30 @@ private: std::deque find_unvisited_neighbors(std::vector &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> 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::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 operator[](size_t vertex_id) const { return {begin(vertex_id), end(vertex_id)}; } + +private: + std::vector m_vertex_to_face_start; + std::vector m_vertex_faces_all; +}; // Index of face indices incident with a vertex index. struct VertexFaceIndex diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index d2c1d0fc18..8d8d15df9d 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -306,6 +306,29 @@ IntegerOnly> reserve_vector(I capacity) template using remove_cvref_t = std::remove_cv_t>; +// A very simple range concept implementation with iterator-like objects. +// This should be replaced by std::ranges::subrange (C++20) +template 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::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 diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 6c5902fc74..4c11283451 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -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 res = its_split(cube.its); + std::vector 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 parts = its_split(sphere1.its); - - REQUIRE(parts.size() == 2); - +void debug_write_obj(const std::vector &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 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 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 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"); +} +