diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f9db9fea0b..ffb80c5732 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -61,7 +61,7 @@ Model& Model::assign_copy(Model &&rhs) this->objects = std::move(rhs.objects); for (ModelObject *model_object : this->objects) model_object->set_model(this); - rhs.objects.clear(); + rhs.objects.clear(); return *this; } @@ -651,7 +651,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) for (ModelInstance *model_instance : this->instances) model_instance->set_model_object(this); - return *this; + return *this; } void ModelObject::assign_new_unique_ids_recursive() @@ -970,8 +970,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) } } } - std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); - pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); + std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); + pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); Polygon hull; int n = (int)pts.size(); @@ -1291,11 +1291,11 @@ void ModelObject::split(ModelObjectPtrs* new_objects) // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? ModelObject* new_object = m_model->add_object(); - new_object->name = this->name; - new_object->config = this->config; - new_object->instances.reserve(this->instances.size()); - for (const ModelInstance *model_instance : this->instances) - new_object->add_instance(*model_instance); + new_object->name = this->name; + new_object->config = this->config; + new_object->instances.reserve(this->instances.size()); + for (const ModelInstance *model_instance : this->instances) + new_object->add_instance(*model_instance); ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); #if !ENABLE_VOLUMES_CENTERING_FIXES new_vol->center_geometry(); @@ -1467,9 +1467,9 @@ int ModelVolume::extruder_id() const bool ModelVolume::is_splittable() const { - // the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once + // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once if (m_is_splittable == -1) - m_is_splittable = (int)mesh.has_multiple_patches(); + m_is_splittable = (int)mesh.is_splittable(); return m_is_splittable == 1; } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index eaa11b7381..467c44a8ab 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -338,113 +338,78 @@ void TriangleMesh::rotate(double angle, Point* center) this->translate(c(0), c(1), 0); } -bool TriangleMesh::has_multiple_patches() const +/** + * Calculates whether or not the mesh is splittable. + */ +bool TriangleMesh::is_splittable() const { - // we need neighbors - if (!this->repaired) - throw std::runtime_error("split() requires repair()"); - - if (this->stl.stats.number_of_facets == 0) - return false; + std::vector visited; + find_unvisited_neighbors(visited); - std::vector facet_queue(this->stl.stats.number_of_facets, 0); - std::vector facet_visited(this->stl.stats.number_of_facets, false); - int facet_queue_cnt = 1; - facet_queue[0] = 0; - facet_visited[0] = true; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - - // If any of the face was not visited at the first time, return "multiple bodies". - for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) - if (! facet_visited[facet_idx]) - return true; - return false; + // 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(); } -size_t TriangleMesh::number_of_patches() const +/** + * Visit all unvisited neighboring facets that are reachable from the first unvisited facet, + * and return them. + * + * @param facet_visited A reference to a vector of booleans. Contains whether or not a + * facet with the same index has been visited. + * @return A deque with all newly visited facets. + */ +std::deque TriangleMesh::find_unvisited_neighbors(std::vector &facet_visited) const { - // we need neighbors - if (!this->repaired) - throw std::runtime_error("split() requires repair()"); - - if (this->stl.stats.number_of_facets == 0) - return false; + // If the visited list is empty, populate it with false for every facet. + if (facet_visited.empty()) { + facet_visited = std::vector(this->stl.stats.number_of_facets, false); + } - std::vector facet_queue(this->stl.stats.number_of_facets, 0); - std::vector facet_visited(this->stl.stats.number_of_facets, false); - int facet_queue_cnt = 0; - size_t num_bodies = 0; - for (;;) { - // Find a seeding triangle for a new body. - int facet_idx = 0; - for (; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) - if (! facet_visited[facet_idx]) { - // A seed triangle was found. - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - break; - } - if (facet_idx == this->stl.stats.number_of_facets) - // No seed found. - break; - ++ num_bodies; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; + // Find the first unvisited facet. + std::queue facet_queue; + auto facet = std::find(facet_visited.begin(), facet_visited.end(), false); + if (facet != facet_visited.end()) + facet_queue.push(facet - facet_visited.begin()); + + // Traverse all reachable neighbors and mark them as visited. + std::deque facets; + while (!facet_queue.empty()) { + int facet_idx = facet_queue.front(); + facet_queue.pop(); + + if (facet_idx != -1 && !facet_visited[facet_idx]) { facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } + + facets.emplace_back(facet_idx); + for (int facet : this->stl.neighbors_start[facet_idx].neighbor) + facet_queue.push(facet); } } - return num_bodies; + return facets; } +/** + * Splits a mesh into multiple meshes when possible. + * + * @return A TriangleMeshPtrs with the newly created meshes. + */ TriangleMeshPtrs TriangleMesh::split() const { - TriangleMeshPtrs meshes; - std::vector facet_visited(this->stl.stats.number_of_facets, false); - - // we need neighbors + // Make sure we're not operating on a broken mesh. if (!this->repaired) throw std::runtime_error("split() requires repair()"); - // loop while we have remaining facets + // Loop while we have remaining facets. + std::vector facet_visited; + TriangleMeshPtrs meshes; for (;;) { - // get the first facet - std::queue facet_queue; - std::deque facets; - for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) { - if (! facet_visited[facet_idx]) { - // if facet was not seen put it into queue and start searching - facet_queue.push(facet_idx); - break; - } - } - if (facet_queue.empty()) + std::deque facets = find_unvisited_neighbors(facet_visited); + if (facets.empty()) break; - while (! facet_queue.empty()) { - int facet_idx = facet_queue.front(); - facet_queue.pop(); - if (! facet_visited[facet_idx]) { - facets.emplace_back(facet_idx); - for (int j = 0; j < 3; ++ j) - facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]); - facet_visited[facet_idx] = true; - } - } - + // Create a new mesh for the part that was just split off. TriangleMesh* mesh = new TriangleMesh; meshes.emplace_back(mesh); mesh->stl.stats.type = inmemory; @@ -453,8 +418,9 @@ TriangleMeshPtrs TriangleMesh::split() const stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); + // Assign the facets to the new mesh. bool first = true; - for (std::deque::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) { + for (auto facet = facets.begin(); facet != facets.end(); ++ facet) { mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index a4387e5c1e..b204a9a3ec 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -68,12 +68,8 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } - // Returns true, if there are two and more connected patches in the mesh. - // Returns false, if one or zero connected patch is in the mesh. - bool has_multiple_patches() const; - - // Count disconnected triangle patches. - size_t number_of_patches() const; + bool is_splittable() const; + std::deque find_unvisited_neighbors(std::vector &facet_visited) const; stl_file stl; bool repaired;