diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 23c15f089..81ea94a29 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,5 +1,6 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) -add_subdirectory(meshboolean) -add_subdirectory(opencsg) +# add_subdirectory(meshboolean) +add_subdirectory(its_neighbor_index) +# add_subdirectory(opencsg) #add_subdirectory(aabb-evaluation) \ No newline at end of file diff --git a/sandboxes/its_neighbor_index/CMakeLists.txt b/sandboxes/its_neighbor_index/CMakeLists.txt new file mode 100644 index 000000000..e28939948 --- /dev/null +++ b/sandboxes/its_neighbor_index/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(its_neighbor_index main.cpp ItsNeighborIndex.cpp ItsNeighborIndex.hpp) + +target_link_libraries(its_neighbor_index libslic3r admesh) + +if (WIN32) + prusaslicer_copy_dlls(its_neighbor_index) +endif() diff --git a/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp b/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp new file mode 100644 index 000000000..72a6b6f05 --- /dev/null +++ b/sandboxes/its_neighbor_index/ItsNeighborIndex.cpp @@ -0,0 +1,580 @@ +#include +#include +#include +#include + +#include "ItsNeighborIndex.hpp" + +#include "tbb/parallel_sort.h" + +namespace Slic3r { + +FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its) +{ + // Just to be clear what type of object are we referencing + using FaceID = size_t; + using VertexID = uint64_t; + using EdgeID = uint64_t; + + constexpr auto UNASSIGNED = std::numeric_limits::max(); + + struct Edge // Will contain IDs of the two facets touching this edge + { + FaceID first, second; + Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} + void assign(FaceID fid) + { + first == UNASSIGNED ? first = fid : second = fid; + } + }; + + // All vertex IDs will fit into this number of bits. (Used for hashing) + 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; + + // Edge id is constructed by concatenating two vertex ids, starting with + // the lowest in MSB + auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) { + if (a > b) std::swap(a, b); + return (a << max_vertex_id_bits) + b; + }; + + // Go through all edges of all facets and mark the facets touching each edge + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + edge_index[e1].assign(face_id); + edge_index[e2].assign(face_id); + edge_index[e3].assign(face_id); + } + + FaceNeighborIndex index(its.indices.size()); + + // Now collect the neighbors for each facet into the final index + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + const Edge &neighs1 = edge_index[e1]; + const Edge &neighs2 = edge_index[e2]; + const Edge &neighs3 = edge_index[e3]; + + std::array &neighs = index[face_id]; + neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; + neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; + neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; + } + + return index; +} + +std::vector its_create_neighbors_index_2(const indexed_triangle_set &its) +{ + std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); + + // Create a mapping from triangle edge into face. + struct EdgeToFace { + // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high. + int vertex_low; + // Index of the 2nd vertex of the triangle edge. + int vertex_high; + // Index of a triangular face. + int face; + // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high). + int face_edge; + bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } + bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } + }; + std::vector edges_map; + edges_map.assign(its.indices.size() * 3, EdgeToFace()); + for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) + for (int i = 0; i < 3; ++ i) { + EdgeToFace &e2f = edges_map[facet_idx * 3 + i]; + e2f.vertex_low = its.indices[facet_idx][i]; + e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; + e2f.face = facet_idx; + // 1 based indexing, to be always strictly positive. + e2f.face_edge = i + 1; + if (e2f.vertex_low > e2f.vertex_high) { + // Sort the vertices + std::swap(e2f.vertex_low, e2f.vertex_high); + // and make the face_edge negative to indicate a flipped edge. + e2f.face_edge = - e2f.face_edge; + } + } + + std::sort(edges_map.begin(), edges_map.end()); + + // Assign a unique common edge id to touching triangle edges. + int num_edges = 0; + 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; + break; + } + } + // Assign an edge index to the 1st face. + // out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges; + 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; + } + ++ num_edges; + } + + return out; +} + +std::vector its_create_neighbors_index_3(const indexed_triangle_set &its) +{ + std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); + + // Create a mapping from triangle edge into face. + struct EdgeToFace { + // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high. + int vertex_low; + // Index of the 2nd vertex of the triangle edge. + int vertex_high; + // Index of a triangular face. + int face; + // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high). + int face_edge; + bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } + bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } + }; + std::vector edges_map; + edges_map.assign(its.indices.size() * 3, EdgeToFace()); + for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) + for (int i = 0; i < 3; ++ i) { + EdgeToFace &e2f = edges_map[facet_idx * 3 + i]; + e2f.vertex_low = its.indices[facet_idx][i]; + e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; + e2f.face = facet_idx; + // 1 based indexing, to be always strictly positive. + e2f.face_edge = i + 1; + if (e2f.vertex_low > e2f.vertex_high) { + // Sort the vertices + std::swap(e2f.vertex_low, e2f.vertex_high); + // and make the face_edge negative to indicate a flipped edge. + e2f.face_edge = - e2f.face_edge; + } + } + + tbb::parallel_sort(edges_map.begin(), edges_map.end()); + + // Assign a unique common edge id to touching triangle edges. + int num_edges = 0; + 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; + break; + } + } + // Assign an edge index to the 1st face. + // out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges; + 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; + } + ++ num_edges; + } + + return out; +} + +FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its) +{ + // Just to be clear what type of object are we referencing + using FaceID = size_t; + using VertexID = uint64_t; + using EdgeID = uint64_t; + + constexpr auto UNASSIGNED = std::numeric_limits::max(); + + struct Edge // Will contain IDs of the two facets touching this edge + { + FaceID first, second; + Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} + void assign(FaceID fid) + { + first == UNASSIGNED ? first = fid : second = fid; + } + }; + + Benchmark bm; + bm.start(); + + // All vertex IDs will fit into this number of bits. (Used for hashing) + // const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); + // assert(max_vertex_id_bits <= 32); + + const uint64_t Vn = its.vertices.size(); + // const uint64_t Fn = 3 * its.indices.size(); + // double MaxQ = double(Vn) * (Vn + 1) / Fn; + // const uint64_t Nq = MaxQ < 0 ? 0 : std::ceil(std::log2(MaxQ)); + // const uint64_t Nr = std::ceil(std::log2(std::min(Vn * (Vn + 1), Fn))); + // const uint64_t Nfn = std::ceil(std::log2(Fn)); + + //// const uint64_t max_edge_ids = (uint64_t(1) << (Nq + Nr)); + // const uint64_t max_edge_ids = MaxQ * Fn + (std::min(Vn * (Vn + 1), Fn)); //(uint64_t(1) << Nfn); + const uint64_t Fn = 3 * its.indices.size(); + std::vector< Edge > edge_index(3 * Fn); + + // Edge id is constructed by concatenating two vertex ids, starting with + // the lowest in MSB + auto hash = [Vn, Fn /*, Nr*/] (VertexID a, VertexID b) { + if (a > b) std::swap(a, b); + + uint64_t C = Vn * a + b; + uint64_t Q = C / Fn, R = C % Fn; + + return Q * Fn + R; + }; + + // Go through all edges of all facets and mark the facets touching each edge + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + edge_index[e1].assign(face_id); + edge_index[e2].assign(face_id); + edge_index[e3].assign(face_id); + } + + FaceNeighborIndex index(its.indices.size()); + + // Now collect the neighbors for each facet into the final index + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + const Edge &neighs1 = edge_index[e1]; + const Edge &neighs2 = edge_index[e2]; + const Edge &neighs3 = edge_index[e3]; + + std::array &neighs = index[face_id]; + neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; + neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; + neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; + } + + bm.stop(); + + std::cout << "Creating neighbor index took: " << bm.getElapsedSec() << " seconds." << std::endl; + + 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; + if (vertex_index == triangle_indices[2]) return 2; + return -1; +} + +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); +} + +static std::vector> create_vertex_faces_index( + const std::vector& indices, size_t count_vertices) +{ + if (count_vertices == 0) return {}; + std::vector> index; + size_t res = indices.size() / count_vertices; + index.assign(count_vertices, reserve_vector(res)); + for (size_t fi = 0; fi < indices.size(); ++fi) { + auto &face = indices[fi]; + index[face(0)].emplace_back(fi); + index[face(1)].emplace_back(fi); + index[face(2)].emplace_back(fi); + } + return index; +} + +std::vector its_create_neighbors_index_5(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); + 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 std::vector &faces = vertex_triangles[edge_indices[0]]; + for (const size_t &face : faces) { + 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; +} + +std::vector> its_create_neighbors_index_6(const indexed_triangle_set &its) +{ + constexpr auto UNASSIGNED_EDGE = std::numeric_limits::max(); + constexpr auto UNASSIGNED_FACE = std::numeric_limits::max(); + struct Edge + { + uint64_t id = UNASSIGNED_EDGE; + size_t face_id = UNASSIGNED_FACE; + bool operator < (const Edge &e) const { return id < e.id; } + }; + + const size_t facenum = its.indices.size(); + + // All vertex IDs will fit into this number of bits. (Used for hashing) + const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); + assert(max_vertex_id_bits <= 32); + + // Edge id is constructed by concatenating two vertex ids, starting with + // the lowest in MSB + auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) { + if (a > b) std::swap(a, b); + return (a << max_vertex_id_bits) + b; + }; + + std::vector edge_map(3 * facenum); + + // Go through all edges of all facets and mark the facets touching each edge + for (size_t face_id = 0; face_id < facenum; ++face_id) { + const Vec3i &face = its.indices[face_id]; + + edge_map[face_id * 3] = {hash(face(0), face(1)), face_id}; + edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id}; + edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id}; + } + + std::sort(edge_map.begin(), edge_map.end()); + + std::vector> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE}); + + auto add_neighbor = [](std::array &slot, size_t face_id) { + if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; } + if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; } + if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; } + }; + + for (auto it = edge_map.begin(); it != edge_map.end();) { + size_t face_id = it->face_id; + uint64_t edge_id = it->id; + + while (++it != edge_map.end() && (it->id == edge_id)) { + size_t other_face_id = it->face_id; + add_neighbor(out[other_face_id], face_id); + add_neighbor(out[face_id], other_face_id); + } + } + + return out; +} + + +std::vector> its_create_neighbors_index_7(const indexed_triangle_set &its) +{ + constexpr auto UNASSIGNED_EDGE = std::numeric_limits::max(); + constexpr auto UNASSIGNED_FACE = std::numeric_limits::max(); + struct Edge + { + uint64_t id = UNASSIGNED_EDGE; + size_t face_id = UNASSIGNED_FACE; + bool operator < (const Edge &e) const { return id < e.id; } + }; + + const size_t facenum = its.indices.size(); + + // All vertex IDs will fit into this number of bits. (Used for hashing) + const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); + assert(max_vertex_id_bits <= 32); + + // Edge id is constructed by concatenating two vertex ids, starting with + // the lowest in MSB + auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) { + if (a > b) std::swap(a, b); + return (a << max_vertex_id_bits) + b; + }; + + std::vector edge_map(3 * facenum); + + // Go through all edges of all facets and mark the facets touching each edge + for (size_t face_id = 0; face_id < facenum; ++face_id) { + const Vec3i &face = its.indices[face_id]; + + edge_map[face_id * 3] = {hash(face(0), face(1)), face_id}; + edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id}; + edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id}; + } + + tbb::parallel_sort(edge_map.begin(), edge_map.end()); + + std::vector> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE}); + + auto add_neighbor = [](std::array &slot, size_t face_id) { + if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; } + if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; } + if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; } + }; + + for (auto it = edge_map.begin(); it != edge_map.end();) { + size_t face_id = it->face_id; + uint64_t edge_id = it->id; + + while (++it != edge_map.end() && (it->id == edge_id)) { + size_t other_face_id = it->face_id; + add_neighbor(out[other_face_id], face_id); + add_neighbor(out[face_id], other_face_id); + } + } + + return out; +} + +FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its) +{ + // Just to be clear what type of object are we referencing + using FaceID = size_t; + using VertexID = uint64_t; + using EdgeID = uint64_t; + + constexpr auto UNASSIGNED = std::numeric_limits::max(); + + struct Edge // Will contain IDs of the two facets touching this edge + { + FaceID first, second; + Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} + void assign(FaceID fid) + { + first == UNASSIGNED ? first = fid : second = fid; + } + }; + + // All vertex IDs will fit into this number of bits. (Used for hashing) + const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); + assert(max_vertex_id_bits <= 32); + + std::map< EdgeID, Edge > edge_index; + + // Edge id is constructed by concatenating two vertex ids, starting with + // the lowest in MSB + auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) { + if (a > b) std::swap(a, b); + return (a << max_vertex_id_bits) + b; + }; + + // Go through all edges of all facets and mark the facets touching each edge + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + edge_index[e1].assign(face_id); + edge_index[e2].assign(face_id); + edge_index[e3].assign(face_id); + } + + FaceNeighborIndex index(its.indices.size()); + + // Now collect the neighbors for each facet into the final index + for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { + const Vec3i &face = its.indices[face_id]; + + EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), + e3 = hash(face(2), face(0)); + + const Edge &neighs1 = edge_index[e1]; + const Edge &neighs2 = edge_index[e2]; + const Edge &neighs3 = edge_index[e3]; + + std::array &neighs = index[face_id]; + neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; + neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; + neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; + } + + return index; +} + +} // namespace Slic3r diff --git a/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp b/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp new file mode 100644 index 000000000..df377f76d --- /dev/null +++ b/sandboxes/its_neighbor_index/ItsNeighborIndex.hpp @@ -0,0 +1,14 @@ +#include +#include "libslic3r/MeshSplitImpl.hpp" + +namespace Slic3r { +FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its); +std::vector its_create_neighbors_index_2(const indexed_triangle_set &its); +std::vector its_create_neighbors_index_3(const indexed_triangle_set &its); +FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its); +//FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its); +std::vector its_create_neighbors_index_5(const indexed_triangle_set &its); +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); +} diff --git a/sandboxes/its_neighbor_index/main.cpp b/sandboxes/its_neighbor_index/main.cpp new file mode 100644 index 000000000..0e89da831 --- /dev/null +++ b/sandboxes/its_neighbor_index/main.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#include "ItsNeighborIndex.hpp" + +#include "libnest2d/tools/benchmark.h" +#include "libnest2d/utils/metaloop.hpp" + +namespace Slic3r { + +struct MeasureResult +{ + double t_index_create = 0; + double t_split = 0; + double memory = 0; + + double full_time() const { return t_index_create + t_split; } +}; + +template +static MeasureResult measure_index(const indexed_triangle_set &its, IndexCreatorFn fn) +{ + Benchmark b; + + b.start(); + ItsNeighborsWrapper itsn{its, fn(its)}; + b.stop(); + + MeasureResult r; + r.t_index_create = b.getElapsedSec(); + + b.start(); + its_split(itsn); + b.stop(); + + r.t_split = b.getElapsedSec(); + + return r; +} + +static TriangleMesh two_spheres(double detail) +{ + TriangleMesh sphere1 = make_sphere(10., 2 * PI / detail), 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(); + + return sphere1; +} + +static const std::map ToMeasure = { + {"simple", make_cube(10., 10., 10.) }, + {"two_spheres", two_spheres(200.)}, + {"two_spheres_detail", two_spheres(360.)}, + {"two_spheres_high_detail", two_spheres(3600.)}, +}; + +static const auto IndexFunctions = std::make_tuple( + std::make_pair("tamas's unordered_map based", its_create_neighbors_index_1), + std::make_pair("vojta std::sort based", its_create_neighbors_index_2), + std::make_pair("vojta tbb::parallel_sort based", its_create_neighbors_index_3), + std::make_pair("filip's vertex->face based", its_create_neighbors_index_5), + std::make_pair("tamas's std::sort based", its_create_neighbors_index_6), + std::make_pair("tamas's tbb::parallel_sort based", its_create_neighbors_index_7), + std::make_pair("tamas's map based", its_create_neighbors_index_8) +); + +static constexpr size_t IndexFuncNum = std::tuple_size_v; + +} // namespace Slic3r + +int main(const int argc, const char * argv[]) +{ + using namespace Slic3r; + + std::map > results; + std::array funcnames; + + for (auto &m : ToMeasure) { + auto &name = m.first; + auto &mesh = m.second; + libnest2d::opt::metaloop::apply([&mesh, &name, &results, &funcnames](int N, auto &e) { + MeasureResult r = measure_index(mesh.its, e.second); + funcnames[N] = e.first; + results[name][N] = r; + }, IndexFunctions); + } + + + std::string outfilename = "out.csv"; + std::fstream outfile; + if (argc > 1) { + outfilename = argv[1]; + outfile.open(outfilename, std::fstream::out); + std::cout << outfilename << " will be used" << std::endl; + } + + std::ostream &out = outfile.is_open() ? outfile : std::cout; + + out << "model;" ; + for (const std::string &funcname : funcnames) { + out << funcname << ";"; + } + + out << std::endl; + + for (auto &[name, result] : results) { + out << name << ";"; + for (auto &r : result) + out << r.full_time() << ";"; + + out << std::endl; + } + + return 0; +} diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ed7a3c220..2b5f3f932 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -205,6 +205,7 @@ add_library(libslic3r STATIC TriangleMesh.hpp TriangleMeshSlicer.cpp TriangleMeshSlicer.hpp + MeshSplitImpl.hpp TriangulateWall.hpp TriangulateWall.cpp utils.cpp diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp new file mode 100644 index 000000000..c5662095f --- /dev/null +++ b/src/libslic3r/MeshSplitImpl.hpp @@ -0,0 +1,163 @@ +#ifndef MESHSPLITIMPL_HPP +#define MESHSPLITIMPL_HPP + +#include "TriangleMesh.hpp" +#include "libnest2d/tools/benchmark.h" + +namespace Slic3r { + +namespace meshsplit_detail { + +template 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_ { + using Index = std::vector; + 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 its_create_neighbors_index(its); + } +}; + +// Visit all unvisited neighboring facets that are reachable from the first unvisited facet, +// and return them. +template +std::vector its_find_unvisited_neighbors( + const indexed_triangle_set &its, + const NeighborIndex & neighbor_index, + std::vector & visited) +{ + using stack_el = size_t; + + auto facestack = reserve_vector(its.indices.size()); + auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); }; + auto pop = [&facestack] () -> stack_el { + stack_el ret = facestack.back(); + facestack.pop_back(); + return ret; + }; + + // find the next unvisited facet and push the index + auto facet = std::find(visited.begin(), visited.end(), false); + std::vector ret; + + if (facet != visited.end()) { + ret.reserve(its.indices.size()); + auto idx = size_t(facet - visited.begin()); + push(idx); + ret.emplace_back(idx); + visited[idx] = true; + } + + while (!facestack.empty()) { + 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)]) { + visited[size_t(neighbor_idx)] = true; + push(stack_el(neighbor_idx)); + ret.emplace_back(size_t(neighbor_idx)); + } + } + } + + return ret; +} + +} // namespace meshsplit_detail + +template struct ItsNeighborsWrapper +{ + using Index = IndexT; + const indexed_triangle_set *its; + IndexT index; + + ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx) + : its{&m}, index{std::move(idx)} + {} + + const auto& get_its() const noexcept { return *its; } + const auto& get_index() const noexcept { return index; } +}; + +// Splits a mesh into multiple meshes when possible. +template +void its_split(const Its &m, OutputIt out_it) +{ + using namespace meshsplit_detail; + + const indexed_triangle_set &its = ItsWithNeighborsIndex_::get_its(m); + + std::vector visited(its.indices.size(), false); + + const size_t UNASSIGNED = its.vertices.size(); + std::vector vidx_conv(its.vertices.size()); + + const auto& neighbor_index = ItsWithNeighborsIndex_::get_index(m); + + for (;;) { + std::vector facets = + its_find_unvisited_neighbors(its, neighbor_index, visited); + + if (facets.empty()) + break; + + std::fill(vidx_conv.begin(), vidx_conv.end(), UNASSIGNED); + + // Create a new mesh for the part that was just split off. + indexed_triangle_set mesh; + + // 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] == UNASSIGNED) { + vidx_conv[vi] = mesh.vertices.size(); + mesh.vertices.emplace_back(its.vertices[size_t(vi)]); + } + + new_face(v) = vidx_conv[vi]; + } + + mesh.indices.emplace_back(new_face); + } + + out_it = std::move(mesh); + } +} + +template +std::vector its_split(const Its &its) +{ + auto ret = reserve_vector(3); + its_split(its, std::back_inserter(ret)); + + return ret; +} + +template bool its_is_splittable(const Its &m) +{ + using namespace meshsplit_detail; + const indexed_triangle_set &its = ItsWithNeighborsIndex_::get_its(m); + const auto& neighbor_index = ItsWithNeighborsIndex_::get_index(m); + + std::vector visited(its.indices.size(), false); + its_find_unvisited_neighbors(its, neighbor_index, visited); + + // Try finding an unvisited facet. If there are none, the mesh is not splittable. + auto it = std::find(visited.begin(), visited.end(), false); + + return it != visited.end(); +} + +} // namespace Slic3r + +#endif // MESHSPLITIMPL_HPP diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 248d4129f..c69fa27e1 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -21,6 +21,12 @@ class MultiPoint; class Point; using Vector = Point; +// Base template for eigen derived vectors +template +using Mat = Eigen::Matrix; + +template using Vec = Mat; + // Eigen types, to replace the Slic3r's own types in the future. // Vector types with a fixed point coordinate base type. using Vec2crd = Eigen::Matrix; @@ -488,4 +494,18 @@ namespace cereal { template void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); } } +namespace Eigen { +template +T* begin(Slic3r::Mat &mat) { return mat.data(); } + +template +T* end(Slic3r::Mat &mat) { return mat.data() + N * M; } + +template +const T* begin(const Slic3r::Mat &mat) { return mat.data(); } + +template +const T* end(const Slic3r::Mat &mat) { return mat.data() + N * M; } +} // namespace Eigen + #endif diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 6f82830ea..49242db7e 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1,8 +1,10 @@ #include "Exception.hpp" #include "TriangleMesh.hpp" #include "TriangleMeshSlicer.hpp" +#include "MeshSplitImpl.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include "Point.hpp" #include #include @@ -685,51 +687,24 @@ std::vector> create_vertex_faces_index(const indexed_triangl return index; } -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; -} +// Create a mapping from triangle edge into face. +struct EdgeToFace { + // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high. + int vertex_low; + // Index of the 2nd vertex of the triangle edge. + int vertex_high; + // Index of a triangular face. + int face; + // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high). + int face_edge; + bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } + bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } +}; -// Map from a face edge to a unique edge identifier or -1 if no neighbor exists. -// Two neighbor faces share a unique edge identifier even if they are flipped. template -static inline std::vector create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) +static std::vector create_edge_map( + const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) { - std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); - - // Create a mapping from triangle edge into face. - struct EdgeToFace { - // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high. - int vertex_low; - // Index of the 2nd vertex of the triangle edge. - int vertex_high; - // Index of a triangular face. - int face; - // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high). - int face_edge; - bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } - bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } - }; std::vector edges_map; edges_map.assign(its.indices.size() * 3, EdgeToFace()); for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) @@ -750,6 +725,18 @@ static inline std::vector create_face_neighbors_index_impl(const indexed_ throw_on_cancel(); std::sort(edges_map.begin(), edges_map.end()); + return edges_map; +} + +// Map from a face edge to a unique edge identifier or -1 if no neighbor exists. +// Two neighbor faces share a unique edge identifier even if they are flipped. +template +static inline std::vector create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) +{ + std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); + + std::vector edges_map = create_edge_map(its, throw_on_cancel); + // Assign a unique common edge id to touching triangle edges. int num_edges = 0; for (size_t i = 0; i < edges_map.size(); ++ i) { @@ -1183,132 +1170,59 @@ float its_volume(const indexed_triangle_set &its) return volume; } -std::vector its_find_unvisited_neighbors( - const indexed_triangle_set &its, - const FaceNeighborIndex & neighbor_index, - std::vector & visited) +std::vector its_split(const indexed_triangle_set &its) { - using stack_el = size_t; + return its_split<>(its); +} - auto facestack = reserve_vector(its.indices.size()); - auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); }; - auto pop = [&facestack] () -> stack_el { - stack_el ret = facestack.back(); - facestack.pop_back(); - return ret; - }; +bool its_is_splittable(const indexed_triangle_set &its) +{ + return its_is_splittable<>(its); +} - // find the next unvisited facet and push the index - auto facet = std::find(visited.begin(), visited.end(), false); - std::vector ret; +std::vector its_create_neighbors_index(const indexed_triangle_set &its) +{ + std::vector out(its.indices.size(), Vec3i(-1, -1, -1)); - if (facet != visited.end()) { - ret.reserve(its.indices.size()); - auto idx = size_t(facet - visited.begin()); - push(idx); - ret.emplace_back(idx); - visited[idx] = true; - } + std::vector edges_map = create_edge_map(its, []{}); - while (!facestack.empty()) { - size_t facet_idx = pop(); - const auto &neighbors = neighbor_index[facet_idx]; - for (size_t neighbor_idx : neighbors) { - if (!visited[neighbor_idx]) { - visited[neighbor_idx] = true; - push(neighbor_idx); - ret.emplace_back(neighbor_idx); + // 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; + 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 ret; -} - -bool its_is_splittable(const indexed_triangle_set &its, - const FaceNeighborIndex & neighbor_index) -{ - std::vector visited(its.indices.size(), false); - its_find_unvisited_neighbors(its, neighbor_index, visited); - - // Try finding an unvisited facet. If there are none, the mesh is not splittable. - auto it = std::find(visited.begin(), visited.end(), false); - return it != visited.end(); -} - -std::vector its_split( - const indexed_triangle_set &its, const FaceNeighborIndex &neighbor_index) -{ - auto ret = reserve_vector(3); - its_split(its, std::back_inserter(ret), neighbor_index); - - return ret; -} - -FaceNeighborIndex its_create_neighbors_index(const indexed_triangle_set &its) -{ - // Just to be clear what type of object are we referencing - using FaceID = size_t; - using VertexID = uint64_t; - using EdgeID = uint64_t; - - constexpr auto UNASSIGNED = std::numeric_limits::max(); - - struct Edge // Will contain IDs of the two facets touching this edge - { - FaceID first, second; - Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} - void assign(FaceID fid) - { - first == UNASSIGNED ? first = fid : second = fid; - } - }; - - // All vertex IDs will fit into this number of bits. (Used for hashing) - 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; - - // Edge id is constructed by concatenating two vertex ids, starting with - // the lowest in MSB - auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) { - if (a > b) std::swap(a, b); - return (a << max_vertex_id_bits) + b; - }; - - // Go through all edges of all facets and mark the facets touching each edge - for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { - const Vec3i &face = its.indices[face_id]; - - EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), - e3 = hash(face(2), face(0)); - - edge_index[e1].assign(face_id); - edge_index[e2].assign(face_id); - edge_index[e3].assign(face_id); - } - - FaceNeighborIndex index(its.indices.size()); - - // Now collect the neighbors for each facet into the final index - for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { - const Vec3i &face = its.indices[face_id]; - - EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), - e3 = hash(face(2), face(0)); - - const Edge &neighs1 = edge_index[e1]; - const Edge &neighs2 = edge_index[e2]; - const Edge &neighs3 = edge_index[e3]; - - std::array &neighs = index[face_id]; - neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; - neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; - neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; - } - - return index; + return out; } } // namespace Slic3r diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 3439eda4e..bad528202 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -139,70 +139,11 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true using FaceNeighborIndex = std::vector< std::array >; // Create index that gives neighbor faces for each face. Ignores face orientations. -FaceNeighborIndex its_create_neighbors_index(const indexed_triangle_set &its); +std::vector its_create_neighbors_index(const indexed_triangle_set &its); -// Visit all unvisited neighboring facets that are reachable from the first unvisited facet, -// and return them. -std::vector its_find_unvisited_neighbors( - const indexed_triangle_set &its, - const FaceNeighborIndex & neighbor_index, - std::vector & visited); +std::vector its_split(const indexed_triangle_set &its); -// Splits a mesh into multiple meshes when possible. -template -void its_split(const indexed_triangle_set & its, - OutputIt out_it, - const FaceNeighborIndex &neighbor_index_ = {}) -{ - const auto &neighbor_index = neighbor_index_.empty() ? - its_create_neighbors_index(its) : - neighbor_index_; - - std::vector visited(its.indices.size(), false); - - const size_t UNASSIGNED = its.vertices.size(); - std::vector vidx_conv(its.vertices.size()); - - for (;;) { - std::vector facets = - its_find_unvisited_neighbors(its, neighbor_index, visited); - - if (facets.empty()) - break; - - std::fill(vidx_conv.begin(), vidx_conv.end(), UNASSIGNED); - - // Create a new mesh for the part that was just split off. - indexed_triangle_set mesh; - - // 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] == UNASSIGNED) { - vidx_conv[vi] = mesh.vertices.size(); - mesh.vertices.emplace_back(its.vertices[size_t(vi)]); - } - - new_face(v) = vidx_conv[vi]; - } - - mesh.indices.emplace_back(new_face); - } - - out_it = std::move(mesh); - } -} - -std::vector its_split( - const indexed_triangle_set &its, - const FaceNeighborIndex & neighbor_index = {}); - -bool its_is_splittable(const indexed_triangle_set &its, - const FaceNeighborIndex & neighbor_index = {}); +bool its_is_splittable(const indexed_triangle_set &its); // Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors. void its_shrink_to_fit(indexed_triangle_set &its); diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index ae493169a..6c5902fc7 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -4,15 +4,12 @@ #include "libslic3r/TriangleMesh.hpp" -//#include "libnest2d/tools/benchmark.h" - TEST_CASE("Split empty mesh", "[its_split][its]") { using namespace Slic3r; indexed_triangle_set its; - std::vector res; - its_split(its, std::back_inserter(res)); + std::vector res = its_split(its); REQUIRE(res.empty()); } @@ -22,8 +19,7 @@ TEST_CASE("Split simple mesh consisting of one part", "[its_split][its]") { TriangleMesh cube = make_cube(10., 10., 10.); - std::vector res; - its_split(cube.its, std::back_inserter(res)); + std::vector res = its_split(cube.its); REQUIRE(res.size() == 1); REQUIRE(res.front().indices.size() == cube.its.indices.size()); @@ -41,14 +37,7 @@ TEST_CASE("Split two merged spheres", "[its_split][its]") { sphere1.merge(sphere2); sphere1.require_shared_vertices(); -// Benchmark bench; - -// bench.start(); - auto index = its_create_neighbors_index(sphere1.its); - std::vector parts = its_split(sphere1.its, index); -// bench.stop(); - -// std::cout << "split took " << bench.getElapsedSec() << " seconds." << std::endl; + std::vector parts = its_split(sphere1.its); REQUIRE(parts.size() == 2); @@ -60,32 +49,3 @@ TEST_CASE("Split two merged spheres", "[its_split][its]") { #endif } -//TEST_CASE("Split two merged spheres TriangleMesh", "[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(); - -// Benchmark bench; - -// bench.start(); -// TriangleMeshPtrs parts = sphere1.split(); -// for (auto &part : parts) part->require_shared_vertices(); -// bench.stop(); - -// std::cout << "split took " << bench.getElapsedSec() << " seconds." << std::endl; - -// REQUIRE(parts.size() == 2); - -////#ifndef NDEBUG -//// size_t part_idx = 0; -//// for (auto &part : parts) { -//// its_write_obj(part->its, (std::string("part_its") + std::to_string(part_idx++) + ".obj").c_str()); -//// } -////#endif -//}