diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 4f7044379..e41d1e3cc 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -205,6 +205,8 @@ add_library(libslic3r STATIC QuadricEdgeCollapse.cpp QuadricEdgeCollapse.hpp Semver.cpp + ShortEdgeCollapse.cpp + ShortEdgeCollapse.hpp ShortestPath.cpp ShortestPath.hpp SLAPrint.cpp diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index b28ea92ba..2a7448d8f 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -17,7 +17,7 @@ #include "libslic3r/Layer.hpp" #include "libslic3r/Geometry/Curves.hpp" -#include "libslic3r/QuadricEdgeCollapse.hpp" +#include "libslic3r/ShortEdgeCollapse.hpp" #include "libslic3r/TriangleSetSampling.hpp" #include "libslic3r/Utils.hpp" @@ -585,7 +585,8 @@ std::pair find_previous_and_next_perimeter_point(const std::vect } // Computes all global model info - transforms object, performs raycasting -void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po, std::function throw_if_canceled) { +void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po, + std::function throw_if_canceled) { BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: gather occlusion meshes: start"; auto obj_transform = po->trafo_centered(); @@ -607,20 +608,19 @@ void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po, st } throw_if_canceled(); - size_t negative_volumes_start_index = triangle_set.indices.size(); - its_merge(triangle_set, negative_volumes_set); - its_transform(triangle_set, obj_transform); - BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: gather occlusion meshes: end"; BOOST_LOG_TRIVIAL(debug) - << "SeamPlacer: decimate: start"; - float target_error = 1.0f; - its_quadric_edge_collapse(triangle_set, 0, &target_error, throw_if_canceled); - BOOST_LOG_TRIVIAL(debug) - << "SeamPlacer: decimate: end"; + << "SeamPlacer: decimate: start"; + its_short_edge_collpase(triangle_set, 25000); + its_short_edge_collpase(negative_volumes_set, 25000); + size_t negative_volumes_start_index = triangle_set.indices.size(); + its_merge(triangle_set, negative_volumes_set); + its_transform(triangle_set, obj_transform); + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: decimate: end"; BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: Compute visibility sample points: start"; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index de7e2f67c..08974bf5f 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -122,9 +122,9 @@ struct PrintObjectSeamData class SeamPlacer { public: // Number of samples generated on the mesh. There are sqr_rays_per_sample_point*sqr_rays_per_sample_point rays casted from each samples - static constexpr size_t raycasting_visibility_samples_count = 25000; + static constexpr size_t raycasting_visibility_samples_count = 30000; //square of number of rays per sample point - static constexpr size_t sqr_rays_per_sample_point = 8; + static constexpr size_t sqr_rays_per_sample_point = 5; // arm length used during angles computation static constexpr float polygon_local_angles_arm_distance = 0.3f; diff --git a/src/libslic3r/ShortEdgeCollapse.cpp b/src/libslic3r/ShortEdgeCollapse.cpp new file mode 100644 index 000000000..049f58b8d --- /dev/null +++ b/src/libslic3r/ShortEdgeCollapse.cpp @@ -0,0 +1,166 @@ +#include "ShortEdgeCollapse.hpp" + +#include +#include +#include +#include + +namespace Slic3r { + +void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) { + std::unordered_map vertices_index_mapping; // this map has two usages: + // in the first step, it contains mapping from original vertex index to final vertex index (which is different from original only for removed vertices) + + std::unordered_set vertices_to_remove; + std::unordered_set faces_to_remove; + std::vector triangles_neighbors; + std::vector face_normals; + std::vector vertex_normals; + std::vector face_indices; + std::random_device rd; + std::mt19937 generator(rd()); + + float decimation_ratio = 1.0f; + float edge_size = 0.2f; + size_t triangle_count = mesh.indices.size(); + + while (triangle_count > target_triangle_count) { + if (decimation_ratio < 0.4) { + edge_size *= 1.5f; + } + if (decimation_ratio < 0.05) { + edge_size *= 1.5f; + } + + float max_edge_len_squared = edge_size * edge_size; + triangles_neighbors = its_face_neighbors_par(mesh); + face_normals = its_face_normals(mesh); + + vertex_normals.resize(mesh.vertices.size()); + face_indices.resize(mesh.indices.size()); + for (size_t face_idx = 0; face_idx < mesh.indices.size(); ++face_idx) { + Vec3i t = mesh.indices[face_idx]; + Vec3f n = face_normals[face_idx]; + vertex_normals[t[0]] = n; + vertex_normals[t[1]] = n; + vertex_normals[t[2]] = n; + + face_indices[face_idx] = face_idx; + } + + std::vector min_vertex_score(mesh.vertices.size(), 1); + for (size_t face_idx = 0; face_idx < mesh.indices.size(); ++face_idx) { + Vec3i t = mesh.indices[face_idx]; + Vec3f n = face_normals[face_idx]; + min_vertex_score[t[0]] = std::min(min_vertex_score[t[0]], n.dot(vertex_normals[t[0]])); + min_vertex_score[t[1]] = std::min(min_vertex_score[t[1]], n.dot(vertex_normals[t[1]])); + min_vertex_score[t[2]] = std::min(min_vertex_score[t[2]], n.dot(vertex_normals[t[2]])); + } + + for (size_t vertex_index = 0; vertex_index < mesh.vertices.size(); ++vertex_index) { + vertices_index_mapping[vertex_index] = vertex_index; + } + + std::shuffle(face_indices.begin(), face_indices.end(), generator); + + for (const size_t &face_idx : face_indices) { + if (faces_to_remove.find(face_idx) != faces_to_remove.end()) { + continue; + } + for (size_t edge_idx = 0; edge_idx < 3; ++edge_idx) { + size_t vertex_index_keep = mesh.indices[face_idx][edge_idx]; + size_t vertex_index_remove = mesh.indices[face_idx][(edge_idx + 1) % 3]; + + if ((mesh.vertices[vertex_index_keep] - mesh.vertices[vertex_index_remove]).squaredNorm() + > max_edge_len_squared) { + continue; + } + + if (min_vertex_score[vertex_index_remove] < min_vertex_score[vertex_index_keep]) { + size_t tmp = vertex_index_keep; + vertex_index_keep = vertex_index_remove; + vertex_index_remove = tmp; + } + + if (min_vertex_score[vertex_index_remove] < -1.0f) { + continue; + } + + if (vertices_to_remove.find(vertex_index_keep) != vertices_to_remove.end() + || vertices_to_remove.find(vertex_index_remove) != vertices_to_remove.end()) { + break; + } + + int neighbor_face_idx = triangles_neighbors[face_idx][edge_idx]; + if (neighbor_face_idx > 0 && faces_to_remove.find(neighbor_face_idx) != faces_to_remove.end()) { + continue; + } + + faces_to_remove.insert(face_idx); + faces_to_remove.insert(neighbor_face_idx); + vertices_to_remove.insert(vertex_index_remove); + vertices_index_mapping[vertex_index_remove] = vertices_index_mapping[vertex_index_keep]; + min_vertex_score[vertex_index_keep] = -2.0; + break; + } + + } + + //flatten the mapping + for (auto &pair : vertices_index_mapping) { + while (vertices_index_mapping[pair.second] != pair.second) { + pair.second = vertices_index_mapping[pair.second]; + } + } + + std::vector new_vertices; + new_vertices.reserve(mesh.vertices.size() - vertices_to_remove.size()); + for (size_t vertex_index = 0; vertex_index < mesh.vertices.size(); ++vertex_index) { + if (vertices_to_remove.find(vertex_index) == vertices_to_remove.end()) { //not removed + new_vertices.push_back(mesh.vertices[vertex_index]); + assert(vertices_index_mapping[vertex_index] == vertex_index); + vertices_index_mapping[vertex_index] = new_vertices.size() - 1; + } + } + + std::vector new_indices; + new_indices.reserve(mesh.indices.size() - faces_to_remove.size()); + for (size_t face_idx = 0; face_idx < mesh.indices.size(); ++face_idx) { + if (faces_to_remove.find(face_idx) != faces_to_remove.end()) { + continue; //skip removed triangles + } + Vec3i new_triangle; + for (int t_vertex_idx = 0; t_vertex_idx < 3; ++t_vertex_idx) { + size_t orig_index = mesh.indices[face_idx][t_vertex_idx]; + if (vertices_to_remove.find(orig_index) == vertices_to_remove.end()) { //this vertex was not removed + new_triangle[t_vertex_idx] = vertices_index_mapping[orig_index]; + } else { // this vertex was removed, so use the vertex mapping points to + new_triangle[t_vertex_idx] = vertices_index_mapping[vertices_index_mapping[orig_index]]; + } + } + if (new_triangle[0] == new_triangle[1] || new_triangle[1] == new_triangle[2] + || new_triangle[2] == new_triangle[0]) { + continue; //skip degenerate + } + new_indices.push_back(new_triangle); + } + + decimation_ratio = float(faces_to_remove.size()) / float(mesh.indices.size()); + +// std::cout << " DECIMATION RATIO: " << decimation_ratio << std::endl; + + mesh.vertices = new_vertices; + mesh.indices = new_indices; + + vertices_index_mapping.clear(); + vertices_to_remove.clear(); + faces_to_remove.clear(); + triangles_neighbors.clear(); + face_normals.clear(); + vertex_normals.clear(); + face_indices.clear(); + triangle_count = mesh.indices.size(); + } +} + +} diff --git a/src/libslic3r/ShortEdgeCollapse.hpp b/src/libslic3r/ShortEdgeCollapse.hpp new file mode 100644 index 000000000..e6f1822c8 --- /dev/null +++ b/src/libslic3r/ShortEdgeCollapse.hpp @@ -0,0 +1,16 @@ +#ifndef SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_ +#define SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_ + +#include "libslic3r/TriangleMesh.hpp" + +namespace Slic3r{ + +// Decimates the model by collapsing short edges. It starts with very small edges and gradually increases the collapsible length, +// until the target triangle count is reached (the algorithm will certainly undershoot the target count, result will have less triangles than target count) +// The algorithm does not check for triangle flipping, disconnections, self intersections or any other degeneration that can appear during mesh processing. +void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count); + +} + + +#endif /* SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_ */