#include "dedge.hpp" #include "config.hpp" #include #include #include #include #include #include "compare-key.hpp" #ifdef WITH_TBB #include "tbb/tbb.h" #endif namespace qflow { inline int dedge_prev(int e, int deg) { return (e % deg == 0u) ? e + (deg - 1) : e - 1; } inline bool atomicCompareAndExchange(volatile int* v, uint32_t newValue, int oldValue) { #if defined(_WIN32) return _InterlockedCompareExchange(reinterpret_cast(v), (long)newValue, (long)oldValue) == (long)oldValue; #else return __sync_bool_compare_and_swap(v, oldValue, newValue); #endif } const int INVALID = -1; #undef max #undef min bool compute_direct_graph(MatrixXd& V, MatrixXi& F, VectorXi& V2E, VectorXi& E2E, VectorXi& boundary, VectorXi& nonManifold) { V2E.resize(V.cols()); V2E.setConstant(INVALID); uint32_t deg = F.rows(); std::vector> tmp(F.size()); #ifdef WITH_TBB tbb::parallel_for( tbb::blocked_range(0u, (uint32_t)F.cols(), GRAIN_SIZE), [&](const tbb::blocked_range& range) { for (uint32_t f = range.begin(); f != range.end(); ++f) { for (uint32_t i = 0; i < deg; ++i) { uint32_t idx_cur = F(i, f), idx_next = F((i + 1) % deg, f), edge_id = deg * f + i; if (idx_cur >= V.cols() || idx_next >= V.cols()) throw std::runtime_error( "Mesh data contains an out-of-bounds vertex reference!"); if (idx_cur == idx_next) continue; tmp[edge_id] = std::make_pair(idx_next, INVALID); if (!atomicCompareAndExchange(&V2E[idx_cur], edge_id, INVALID)) { uint32_t idx = V2E[idx_cur]; while (!atomicCompareAndExchange((int*)&tmp[idx].second, edge_id, INVALID)) idx = tmp[idx].second; } } } }); #else for (int f = 0; f < F.cols(); ++f) { for (unsigned int i = 0; i < deg; ++i) { unsigned int idx_cur = F(i, f), idx_next = F((i + 1) % deg, f), edge_id = deg * f + i; if (idx_cur >= V.cols() || idx_next >= V.cols()) throw std::runtime_error("Mesh data contains an out-of-bounds vertex reference!"); if (idx_cur == idx_next) continue; tmp[edge_id] = std::make_pair(idx_next, -1); if (V2E[idx_cur] == -1) V2E[idx_cur] = edge_id; else { unsigned int idx = V2E[idx_cur]; while (tmp[idx].second != -1) { idx = tmp[idx].second; } tmp[idx].second = edge_id; } } } #endif nonManifold.resize(V.cols()); nonManifold.setConstant(false); E2E.resize(F.cols() * deg); E2E.setConstant(INVALID); #ifdef WITH_OMP #pragma omp parallel for #endif for (int f = 0; f < F.cols(); ++f) { for (uint32_t i = 0; i < deg; ++i) { uint32_t idx_cur = F(i, f), idx_next = F((i + 1) % deg, f), edge_id_cur = deg * f + i; if (idx_cur == idx_next) continue; uint32_t it = V2E[idx_next], edge_id_opp = INVALID; while (it != INVALID) { if (tmp[it].first == idx_cur) { if (edge_id_opp == INVALID) { edge_id_opp = it; } else { nonManifold[idx_cur] = true; nonManifold[idx_next] = true; edge_id_opp = INVALID; break; } } it = tmp[it].second; } if (edge_id_opp != INVALID && edge_id_cur < edge_id_opp) { E2E[edge_id_cur] = edge_id_opp; E2E[edge_id_opp] = edge_id_cur; } } } std::atomic nonManifoldCounter(0), boundaryCounter(0), isolatedCounter(0); boundary.resize(V.cols()); boundary.setConstant(false); /* Detect boundary regions of the mesh and adjust vertex->edge pointers*/ #ifdef WITH_OMP #pragma omp parallel for #endif for (int i = 0; i < V.cols(); ++i) { uint32_t edge = V2E[i]; if (edge == INVALID) { isolatedCounter++; continue; } if (nonManifold[i]) { nonManifoldCounter++; V2E[i] = INVALID; continue; } /* Walk backwards to the first boundary edge (if any) */ uint32_t start = edge, v2e = INVALID; do { v2e = std::min(v2e, edge); uint32_t prevEdge = E2E[dedge_prev(edge, deg)]; if (prevEdge == INVALID) { /* Reached boundary -- update the vertex->edge link */ v2e = edge; boundary[i] = true; boundaryCounter++; break; } edge = prevEdge; } while (edge != start); V2E[i] = v2e; } #ifdef LOG_OUTPUT printf("counter triangle %d %d\n", (int)boundaryCounter, (int)nonManifoldCounter); #endif return true; std::vector> vert_to_edges(V2E.size()); for (int i = 0; i < F.cols(); ++i) { for (int j = 0; j < 3; ++j) { int v = F(j, i); vert_to_edges[v].push_back(i * 3 + j); } } std::vector colors(F.cols() * 3, -1); bool update = false; int num_v = V.cols(); std::map new_vertices; for (int i = 0; i < vert_to_edges.size(); ++i) { int num_color = 0; for (int j = 0; j < vert_to_edges[i].size(); ++j) { int deid0 = vert_to_edges[i][j]; if (colors[deid0] == -1) { int deid = deid0; do { colors[deid] = num_color; if (num_color != 0) F(deid % 3, deid / 3) = num_v; deid = deid / 3 * 3 + (deid + 2) % 3; deid = E2E[deid]; } while (deid != deid0); num_color += 1; if (num_color > 1) { update = true; new_vertices[num_v] = i; num_v += 1; } } } } if (update) { V.conservativeResize(3, num_v); for (auto& p : new_vertices) { V.col(p.first) = V.col(p.second); } return false; } return true; } void compute_direct_graph_quad(std::vector& V, std::vector& F, std::vector& V2E, std::vector& E2E, VectorXi& boundary, VectorXi& nonManifold) { V2E.clear(); E2E.clear(); boundary = VectorXi(); nonManifold = VectorXi(); V2E.resize(V.size(), INVALID); uint32_t deg = 4; std::vector> tmp(F.size() * deg); #ifdef WITH_TBB tbb::parallel_for( tbb::blocked_range(0u, (uint32_t)F.size(), GRAIN_SIZE), [&](const tbb::blocked_range& range) { for (uint32_t f = range.begin(); f != range.end(); ++f) { for (uint32_t i = 0; i < deg; ++i) { uint32_t idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg], edge_id = deg * f + i; if (idx_cur >= V.size() || idx_next >= V.size()) throw std::runtime_error( "Mesh data contains an out-of-bounds vertex reference!"); if (idx_cur == idx_next) continue; tmp[edge_id] = std::make_pair(idx_next, INVALID); if (!atomicCompareAndExchange(&V2E[idx_cur], edge_id, INVALID)) { uint32_t idx = V2E[idx_cur]; while (!atomicCompareAndExchange((int*)&tmp[idx].second, edge_id, INVALID)) idx = tmp[idx].second; } } } }); #else for (int f = 0; f < F.size(); ++f) { for (unsigned int i = 0; i < deg; ++i) { unsigned int idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg], edge_id = deg * f + i; if (idx_cur >= V.size() || idx_next >= V.size()) throw std::runtime_error("Mesh data contains an out-of-bounds vertex reference!"); if (idx_cur == idx_next) continue; tmp[edge_id] = std::make_pair(idx_next, -1); if (V2E[idx_cur] == -1) { V2E[idx_cur] = edge_id; } else { unsigned int idx = V2E[idx_cur]; while (tmp[idx].second != -1) { idx = tmp[idx].second; } tmp[idx].second = edge_id; } } } #endif nonManifold.resize(V.size()); nonManifold.setConstant(false); E2E.resize(F.size() * deg, INVALID); #ifdef WITH_OMP #pragma omp parallel for #endif for (int f = 0; f < F.size(); ++f) { for (uint32_t i = 0; i < deg; ++i) { uint32_t idx_cur = F[f][i], idx_next = F[f][(i + 1) % deg], edge_id_cur = deg * f + i; if (idx_cur == idx_next) continue; uint32_t it = V2E[idx_next], edge_id_opp = INVALID; while (it != INVALID) { if (tmp[it].first == idx_cur) { if (edge_id_opp == INVALID) { edge_id_opp = it; } else { nonManifold[idx_cur] = true; nonManifold[idx_next] = true; edge_id_opp = INVALID; break; } } it = tmp[it].second; } if (edge_id_opp != INVALID && edge_id_cur < edge_id_opp) { E2E[edge_id_cur] = edge_id_opp; E2E[edge_id_opp] = edge_id_cur; } } } std::atomic nonManifoldCounter(0), boundaryCounter(0), isolatedCounter(0); boundary.resize(V.size()); boundary.setConstant(false); /* Detect boundary regions of the mesh and adjust vertex->edge pointers*/ #ifdef WITH_OMP #pragma omp parallel for #endif for (int i = 0; i < V.size(); ++i) { uint32_t edge = V2E[i]; if (edge == INVALID) { isolatedCounter++; continue; } if (nonManifold[i]) { nonManifoldCounter++; V2E[i] = INVALID; continue; } /* Walk backwards to the first boundary edge (if any) */ uint32_t start = edge, v2e = INVALID; do { v2e = std::min(v2e, edge); uint32_t prevEdge = E2E[dedge_prev(edge, deg)]; if (prevEdge == INVALID) { /* Reached boundary -- update the vertex->edge link */ v2e = edge; boundary[i] = true; boundaryCounter++; break; } edge = prevEdge; } while (edge != start); V2E[i] = v2e; } #ifdef LOG_OUTPUT printf("counter %d %d\n", (int)boundaryCounter, (int)nonManifoldCounter); #endif } void remove_nonmanifold(std::vector& F, std::vector& V) { typedef std::pair Edge; int degree = 4; std::map>> irregular; std::vector> E(V.size()); std::vector> VF(V.size()); auto kill_face_single = [&](uint32_t f) { if (F[f][0] == INVALID) return; for (int i = 0; i < degree; ++i) E[F[f][i]].erase(F[f][(i + 1) % degree]); F[f].setConstant(INVALID); }; auto kill_face = [&](uint32_t f) { if (degree == 4 && F[f][2] == F[f][3]) { auto it = irregular.find(F[f][2]); if (it != irregular.end()) { for (auto& item : it->second) { kill_face_single(item.second.second); } } } kill_face_single(f); }; uint32_t nm_edge = 0, nm_vert = 0; for (uint32_t f = 0; f < (uint32_t)F.size(); ++f) { if (F[f][0] == INVALID) continue; if (degree == 4 && F[f][2] == F[f][3]) { /* Special handling of irregular faces */ irregular[F[f][2]][F[f][0]] = std::make_pair(F[f][1], f); continue; } bool nonmanifold = false; for (uint32_t e = 0; e < degree; ++e) { uint32_t v0 = F[f][e], v1 = F[f][(e + 1) % degree], v2 = F[f][(e + 2) % degree]; if (E[v0].find(v1) != E[v0].end() || (degree == 4 && E[v0].find(v2) != E[v0].end())) nonmanifold = true; } if (nonmanifold) { nm_edge++; F[f].setConstant(INVALID); continue; } for (uint32_t e = 0; e < degree; ++e) { uint32_t v0 = F[f][e], v1 = F[f][(e + 1) % degree], v2 = F[f][(e + 2) % degree]; E[v0].insert(v1); if (degree == 4) E[v0].insert(v2); VF[v0].insert(f); } } std::vector edges; for (auto item : irregular) { bool nonmanifold = false; auto face = item.second; edges.clear(); uint32_t cur = face.begin()->first, stop = cur; while (true) { uint32_t pred = cur; cur = face[cur].first; uint32_t next = face[cur].first, it = 0; while (true) { ++it; if (next == pred) break; if (E[cur].find(next) != E[cur].end() && it == 1) nonmanifold = true; edges.push_back(Edge(cur, next)); next = face[next].first; } if (cur == stop) break; } if (nonmanifold) { nm_edge++; for (auto& i : item.second) F[i.second.second].setConstant(INVALID); continue; } else { for (auto e : edges) { E[e.first].insert(e.second); for (auto e2 : face) VF[e.first].insert(e2.second.second); } } } /* Check vertices */ std::set v_marked, v_unmarked, f_adjacent; std::function dfs = [&](uint32_t i) { v_marked.insert(i); v_unmarked.erase(i); for (uint32_t f : VF[i]) { if (f_adjacent.find(f) == f_adjacent.end()) /* if not part of adjacent face */ continue; for (uint32_t j = 0; j < degree; ++j) { uint32_t k = F[f][j]; if (v_unmarked.find(k) == v_unmarked.end() || /* if not unmarked OR */ v_marked.find(k) != v_marked.end()) /* if already marked */ continue; dfs(k); } } }; for (uint32_t i = 0; i < (uint32_t)V.size(); ++i) { v_marked.clear(); v_unmarked.clear(); f_adjacent.clear(); for (uint32_t f : VF[i]) { if (F[f][0] == INVALID) continue; for (uint32_t k = 0; k < degree; ++k) v_unmarked.insert(F[f][k]); f_adjacent.insert(f); } if (v_unmarked.empty()) continue; v_marked.insert(i); v_unmarked.erase(i); dfs(*v_unmarked.begin()); if (v_unmarked.size() > 0) { nm_vert++; for (uint32_t f : f_adjacent) kill_face(f); } } if (nm_vert > 0 || nm_edge > 0) { std::cout << "Non-manifold elements: vertices=" << nm_vert << ", edges=" << nm_edge << std::endl; } uint32_t nFaces = 0, nFacesOrig = F.size(); for (uint32_t f = 0; f < (uint32_t)F.size(); ++f) { if (F[f][0] == INVALID) continue; if (nFaces != f) { F[nFaces] = F[f]; } ++nFaces; } if (nFacesOrig != nFaces) { F.resize(nFaces); std::cout << "Faces reduced from " << nFacesOrig << " -> " << nFaces << std::endl; } } } // namespace qflow