Clean up and fix TriangleMesh::split and relatives

This commit is contained in:
Sijmen Schoon 2019-03-28 12:34:41 +01:00 committed by bubnikv
parent 62539bc35b
commit 19dc89bfab
3 changed files with 67 additions and 105 deletions

View File

@ -1467,9 +1467,9 @@ int ModelVolume::extruder_id() const
bool ModelVolume::is_splittable() 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) 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; return m_is_splittable == 1;
} }

View File

@ -338,113 +338,78 @@ void TriangleMesh::rotate(double angle, Point* center)
this->translate(c(0), c(1), 0); 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 std::vector<bool> visited;
if (!this->repaired) find_unvisited_neighbors(visited);
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0) // Try finding an unvisited facet. If there are none, the mesh is not splittable.
return false; auto it = std::find(visited.begin(), visited.end(), false);
return it != visited.end();
std::vector<int> facet_queue(this->stl.stats.number_of_facets, 0);
std::vector<char> 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) * Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
if (! facet_visited[facet_idx]) * and return them.
return true; *
return false; * @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.
size_t TriangleMesh::number_of_patches() const */
std::deque<uint32_t> TriangleMesh::find_unvisited_neighbors(std::vector<bool> &facet_visited) const
{ {
// we need neighbors // If the visited list is empty, populate it with false for every facet.
if (!this->repaired) if (facet_visited.empty()) {
throw std::runtime_error("split() requires repair()"); facet_visited = std::vector<bool>(this->stl.stats.number_of_facets, false);
if (this->stl.stats.number_of_facets == 0)
return false;
std::vector<int> facet_queue(this->stl.stats.number_of_facets, 0);
std::vector<char> 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];
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;
}
}
} }
return num_bodies; // Find the first unvisited facet.
}
TriangleMeshPtrs TriangleMesh::split() const
{
TriangleMeshPtrs meshes;
std::vector<unsigned char> facet_visited(this->stl.stats.number_of_facets, false);
// we need neighbors
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
// loop while we have remaining facets
for (;;) {
// get the first facet
std::queue<int> facet_queue; std::queue<int> facet_queue;
std::deque<int> facets; auto facet = std::find(facet_visited.begin(), facet_visited.end(), false);
for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) { if (facet != facet_visited.end())
if (! facet_visited[facet_idx]) { facet_queue.push(facet - facet_visited.begin());
// if facet was not seen put it into queue and start searching
facet_queue.push(facet_idx);
break;
}
}
if (facet_queue.empty())
break;
// Traverse all reachable neighbors and mark them as visited.
std::deque<uint32_t> facets;
while (!facet_queue.empty()) { while (!facet_queue.empty()) {
int facet_idx = facet_queue.front(); int facet_idx = facet_queue.front();
facet_queue.pop(); facet_queue.pop();
if (! facet_visited[facet_idx]) {
facets.emplace_back(facet_idx); if (facet_idx != -1 && !facet_visited[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; facet_visited[facet_idx] = true;
facets.emplace_back(facet_idx);
for (int facet : this->stl.neighbors_start[facet_idx].neighbor)
facet_queue.push(facet);
} }
} }
return facets;
}
/**
* Splits a mesh into multiple meshes when possible.
*
* @return A TriangleMeshPtrs with the newly created meshes.
*/
TriangleMeshPtrs TriangleMesh::split() const
{
// 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.
std::vector<bool> facet_visited;
TriangleMeshPtrs meshes;
for (;;) {
std::deque<uint32_t> facets = find_unvisited_neighbors(facet_visited);
if (facets.empty())
break;
// Create a new mesh for the part that was just split off.
TriangleMesh* mesh = new TriangleMesh; TriangleMesh* mesh = new TriangleMesh;
meshes.emplace_back(mesh); meshes.emplace_back(mesh);
mesh->stl.stats.type = inmemory; mesh->stl.stats.type = inmemory;
@ -453,8 +418,9 @@ TriangleMeshPtrs TriangleMesh::split() const
stl_clear_error(&mesh->stl); stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl); stl_allocate(&mesh->stl);
// Assign the facets to the new mesh.
bool first = true; bool first = true;
for (std::deque<int>::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]; mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
} }

View File

@ -68,12 +68,8 @@ public:
size_t facets_count() const { return this->stl.stats.number_of_facets; } size_t facets_count() const { return this->stl.stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; } bool empty() const { return this->facets_count() == 0; }
// Returns true, if there are two and more connected patches in the mesh. bool is_splittable() const;
// Returns false, if one or zero connected patch is in the mesh. std::deque<uint32_t> find_unvisited_neighbors(std::vector<bool> &facet_visited) const;
bool has_multiple_patches() const;
// Count disconnected triangle patches.
size_t number_of_patches() const;
stl_file stl; stl_file stl;
bool repaired; bool repaired;