// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 Qingnan Zhou // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. // #include "propagate_winding_numbers.h" #include "../extract_manifold_patches.h" #include "../extract_non_manifold_edge_curves.h" #include "../facet_components.h" #include "../unique_edge_map.h" #include "../writeOBJ.h" #include "../writePLY.h" #include "order_facets_around_edge.h" #include "outer_facet.h" #include "closest_facet.h" #include "assign_scalar.h" #include "extract_cells.h" #include #include #include #include #include namespace propagate_winding_numbers_helper { template bool winding_number_assignment_is_consistent( const std::vector& orders, const std::vector >& orientations, const Eigen::PlainObjectBase& per_patch_winding_number) { const size_t num_edge_curves = orders.size(); const size_t num_labels = per_patch_winding_number.cols() / 2; for (size_t i=0; i IGL_INLINE bool igl::cgal::propagate_winding_numbers_single_component_patch_wise( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::PlainObjectBase& uE, const std::vector >& uE2E, const Eigen::PlainObjectBase& labels, const Eigen::PlainObjectBase& P, const std::vector >& intersection_curves, Eigen::PlainObjectBase& patch_W) { const size_t num_faces = F.rows(); const size_t num_patches = P.maxCoeff() + 1; assert(labels.size() == num_patches); // Utility functions. auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; auto face_and_corner_index_to_edge_index = [&](size_t fi, size_t ci) { return ci*num_faces + fi; }; auto get_edge_end_points = [&](size_t ei, size_t& s, size_t& d) { const size_t fi = edge_index_to_face_index(ei); const size_t ci = edge_index_to_corner_index(ei); s = F(fi, (ci+1)%3); d = F(fi, (ci+2)%3); }; auto is_positively_orientated = [&](size_t fi, size_t s, size_t d) -> bool{ const Eigen::Vector3i f = F.row(fi); if (f[0] == d && f[1] == s) return true; if (f[1] == d && f[2] == s) return true; if (f[2] == d && f[0] == s) return true; if (f[0] == s && f[1] == d) return false; if (f[1] == s && f[2] == d) return false; if (f[2] == s && f[0] == d) return false; throw std::runtime_error("Edge does not belong to face."); return false; }; auto compute_signed_index = [&](size_t fi, size_t s, size_t d) -> int{ return int(fi+1) * (is_positively_orientated(fi, s, d) ? 1:-1); }; auto compute_unsigned_index = [&](int signed_idx) -> size_t { return abs(signed_idx) - 1; }; // Order patches around each intersection curve. const size_t num_edge_curves = intersection_curves.size(); std::vector orders(num_edge_curves); std::vector > orientations(num_edge_curves); std::vector > patch_curve_adjacency(num_patches); for (size_t i=0; i adj_faces; for (size_t ei : uE2E[uei]) { const size_t fi = edge_index_to_face_index(ei); const size_t signed_fi = compute_signed_index(fi, s, d); adj_faces.push_back(signed_fi); patch_curve_adjacency[P[fi]].push_back(i); } auto& order = orders[i]; igl::cgal::order_facets_around_edge( V, F, s, d, adj_faces, order); assert(order.minCoeff() == 0); assert(order.maxCoeff() == adj_faces.size() - 1); auto& orientation = orientations[i]; orientation.resize(order.size()); std::transform(order.data(), order.data()+order.size(), orientation.begin(), [&](size_t index) { return adj_faces[index] > 0; }); std::transform(order.data(), order.data()+order.size(), order.data(), [&](size_t index) { return P[compute_unsigned_index(adj_faces[index])]; } ); } // Propagate winding number from infinity. // Assuming infinity has winding number 0. const size_t num_labels = labels.maxCoeff() + 1; const int INVALID = std::numeric_limits::max(); patch_W.resize(num_patches, 2*num_labels); patch_W.setConstant(INVALID); size_t outer_facet_idx; bool outer_facet_is_flipped; Eigen::VectorXi face_indices(num_faces); face_indices.setLinSpaced(num_faces, 0, num_faces-1); igl::cgal::outer_facet(V, F, face_indices, outer_facet_idx, outer_facet_is_flipped); size_t outer_patch_idx = P[outer_facet_idx]; size_t outer_patch_label = labels[outer_patch_idx]; patch_W.row(outer_patch_idx).setZero(); if (outer_facet_is_flipped) { patch_W(outer_patch_idx, outer_patch_label*2) = -1; } else { patch_W(outer_patch_idx, outer_patch_label*2+1) = 1; } auto winding_num_assigned = [&](size_t patch_idx) -> bool{ return (patch_W.row(patch_idx).array() != INVALID).all(); }; std::queue Q; Q.push(outer_patch_idx); while (!Q.empty()) { const size_t curr_patch_idx = Q.front(); const size_t curr_patch_label = labels[curr_patch_idx]; Q.pop(); const auto& adj_curves = patch_curve_adjacency[curr_patch_idx]; for (size_t curve_idx : adj_curves) { const auto& order = orders[curve_idx]; const auto& orientation = orientations[curve_idx]; const size_t num_adj_patches = order.size(); assert(num_adj_patches == orientation.size()); size_t curr_i = std::numeric_limits::max(); for (size_t i=0; i IGL_INLINE bool igl::cgal::propagate_winding_numbers_single_component_patch_wise( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::PlainObjectBase& uE, const std::vector >& uE2E, const Eigen::PlainObjectBase& EMAP, const Eigen::PlainObjectBase& labels, const Eigen::PlainObjectBase& P, Eigen::PlainObjectBase& patch_W) { const size_t num_faces = F.rows(); const size_t num_patches = P.maxCoeff() + 1; assert(labels.size() == num_patches); // Utility functions. auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; }; auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; }; auto face_and_corner_index_to_edge_index = [&](size_t fi, size_t ci) { return ci*num_faces + fi; }; auto get_edge_end_points = [&](size_t ei, size_t& s, size_t& d) { const size_t fi = edge_index_to_face_index(ei); const size_t ci = edge_index_to_corner_index(ei); s = F(fi, (ci+1)%3); d = F(fi, (ci+2)%3); }; auto is_positively_orientated = [&](size_t fi, size_t s, size_t d) -> bool{ const Eigen::Vector3i f = F.row(fi); if (f[0] == d && f[1] == s) return true; if (f[1] == d && f[2] == s) return true; if (f[2] == d && f[0] == s) return true; if (f[0] == s && f[1] == d) return false; if (f[1] == s && f[2] == d) return false; if (f[2] == s && f[0] == d) return false; throw std::runtime_error("Edge does not belong to face."); return false; }; auto compute_signed_index = [&](size_t fi, size_t s, size_t d) -> int{ return int(fi+1) * (is_positively_orientated(fi, s, d) ? 1:-1); }; auto compute_unsigned_index = [&](int signed_idx) -> size_t { return abs(signed_idx) - 1; }; // Order patches around each non-manifold edge. const size_t num_edges = uE.rows(); std::vector orders(num_edges); std::vector > orientations(num_edges); std::vector > patch_edge_adjacency(num_patches); for (size_t uei=0; uei adj_faces; for (size_t ei : uE2E[uei]) { const size_t fi = edge_index_to_face_index(ei); const size_t signed_fi = compute_signed_index(fi, s, d); adj_faces.push_back(signed_fi); patch_edge_adjacency[P[fi]].push_back(ei); } auto& order = orders[uei]; igl::cgal::order_facets_around_edge( V, F, s, d, adj_faces, order); assert(order.minCoeff() == 0); assert(order.maxCoeff() == adj_faces.size() - 1); auto& orientation = orientations[uei]; orientation.resize(order.size()); std::transform(order.data(), order.data()+order.size(), orientation.begin(), [&](size_t index) { return adj_faces[index] > 0; }); std::transform(order.data(), order.data()+order.size(), order.data(), [&](size_t index) { return compute_unsigned_index(adj_faces[index]); } ); } // Propagate winding number from infinity. // Assuming infinity has winding number 0. const size_t num_labels = labels.maxCoeff() + 1; const int INVALID = std::numeric_limits::max(); patch_W.resize(num_patches, 2*num_labels); patch_W.setConstant(INVALID); size_t outer_facet_idx; bool outer_facet_is_flipped; Eigen::VectorXi face_indices(num_faces); face_indices.setLinSpaced(num_faces, 0, num_faces-1); igl::cgal::outer_facet(V, F, face_indices, outer_facet_idx, outer_facet_is_flipped); size_t outer_patch_idx = P[outer_facet_idx]; size_t outer_patch_label = labels[outer_patch_idx]; patch_W.row(outer_patch_idx).setZero(); if (outer_facet_is_flipped) { patch_W(outer_patch_idx, outer_patch_label*2) = -1; } else { patch_W(outer_patch_idx, outer_patch_label*2+1) = 1; } auto winding_num_assigned = [&](size_t patch_idx) -> bool{ return (patch_W.row(patch_idx).array() != INVALID).all(); }; std::queue Q; Q.push(outer_patch_idx); while (!Q.empty()) { const size_t curr_patch_idx = Q.front(); const size_t curr_patch_label = labels[curr_patch_idx]; Q.pop(); const auto& adj_edges = patch_edge_adjacency[curr_patch_idx]; for (size_t ei: adj_edges) { const size_t uei = EMAP[ei]; const size_t fid = edge_index_to_face_index(ei); const auto& order = orders[uei]; const auto& orientation = orientations[uei]; const size_t num_adj_patches = order.size(); assert(num_adj_patches == orientation.size()); size_t curr_i = std::numeric_limits::max(); for (size_t i=0; i Vd(V.rows(), V.cols()); for (size_t j=0; j IGL_INLINE bool igl::cgal::propagate_winding_numbers_single_component( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::PlainObjectBase& labels, Eigen::PlainObjectBase& W) { typedef typename DerivedF::Scalar Index; const size_t num_faces = F.rows(); // Extract unique edges. std::vector > uE2E; Eigen::Matrix E, uE; Eigen::Matrix EMAP; igl::unique_edge_map(F, E, uE, EMAP, uE2E); // Extract manifold patches and intersection curves. Eigen::VectorXi P; std::vector > intersection_curves; size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); igl::extract_non_manifold_edge_curves(F, EMAP, uE2E, intersection_curves); assert(P.size() == num_faces); assert(P.maxCoeff() + 1 == num_patches); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i IGL_INLINE bool igl::cgal::propagate_winding_numbers_single_component( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, Eigen::PlainObjectBase& W) { const size_t num_faces = F.rows(); Eigen::VectorXi labels(num_faces); labels.setZero(); return igl::cgal::propagate_winding_numbers_single_component(V, F, labels, W); } template< typename DerivedV, typename DerivedF, typename DerivedL, typename DerivedW> IGL_INLINE void igl::cgal::propagate_winding_numbers( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::PlainObjectBase& labels, Eigen::PlainObjectBase& W) { typedef typename DerivedF::Scalar Index; const size_t num_faces = F.rows(); // Extract unique edges. std::vector > uE2E; Eigen::Matrix E, uE; Eigen::Matrix EMAP; igl::unique_edge_map(F, E, uE, EMAP, uE2E); // Check to make sure there is no boundaries and no non-manifold edges with // odd number of adjacent faces. for (const auto& adj_faces : uE2E) { if (adj_faces.size() % 2 == 1) { std::stringstream err_msg; err_msg << "Input mesh contains odd number of faces " << "sharing a single edge" << std::endl; err_msg << "Indicating the input mesh does not represent a valid volume" << ", and winding number cannot be propagated." << std::endl; throw std::runtime_error(err_msg.str()); } } // Gather connected components. std::vector > > TT,_1; triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1); Eigen::VectorXi counts; Eigen::VectorXi C; igl::facet_components(TT,C,counts); const size_t num_components = counts.size(); std::vector > components(num_components); for (size_t i=0; i comp_faces(num_components); std::vector comp_labels(num_components); for (size_t i=0; i Ws(num_components); for (size_t i=0; i Vd(V.rows(), V.cols()); for (size_t j=0; j nested_Ws = Ws; Eigen::MatrixXi ambient_correction(num_components, 2*num_labels); ambient_correction.setZero(); if (num_components > 1) { for (size_t i=0; i orientation; igl::cgal::closest_facet(V, comp_faces[i], samples, fids, orientation); const auto& comp = components[i]; for (size_t j=0; j IGL_INLINE void igl::cgal::propagate_winding_numbers_beta( const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::PlainObjectBase& labels, Eigen::PlainObjectBase& W) { const size_t num_faces = F.rows(); typedef typename DerivedF::Scalar Index; Eigen::MatrixXi E, uE; Eigen::VectorXi EMAP; std::vector > uE2E; igl::unique_edge_map(F, E, uE, EMAP, uE2E); assert(propagate_winding_numbers_helper::is_orientable(F, uE, uE2E)); Eigen::VectorXi P; const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); DerivedW per_patch_cells; const size_t num_cells = igl::cgal::extract_cells(V, F, P, E, uE, uE2E, EMAP, per_patch_cells); typedef std::tuple CellConnection; std::vector > cell_adjacency(num_cells); for (size_t i=0; i faces; for (size_t i=0; i path; path.push_back(idx); while (parents[path.back()] != path.back()) { path.push_back(parents[path.back()]); } return path; }; for (size_t i=0; i Q; Q.push(i); parents[i] = i; while (!Q.empty()) { size_t curr_idx = Q.front(); Q.pop(); int curr_label = cell_labels[curr_idx]; for (const auto& neighbor : cell_adjacency[curr_idx]) { if (cell_labels[std::get<0>(neighbor)] == 0) { cell_labels[std::get<0>(neighbor)] = curr_label * -1; Q.push(std::get<0>(neighbor)); parents[std::get<0>(neighbor)] = curr_idx; } else { if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) { std::cerr << "Odd cell cycle detected!" << std::endl; auto path = trace_parents(curr_idx); path.reverse(); auto path2 = trace_parents(std::get<0>(neighbor)); path.insert(path.end(), path2.begin(), path2.end()); for (auto cell_id : path) { std::cout << cell_id << " "; std::stringstream filename; filename << "cell_" << cell_id << ".ply"; save_cell(filename.str(), cell_id); } std::cout << std::endl; } assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); } } } } } } size_t outer_facet; bool flipped; Eigen::VectorXi I; I.setLinSpaced(num_faces, 0, num_faces-1); igl::cgal::outer_facet(V, F, I, outer_facet, flipped); const size_t outer_patch = P[outer_facet]; const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i Q; Q.push(infinity_cell); while (!Q.empty()) { size_t curr_cell = Q.front(); Q.pop(); for (const auto& neighbor : cell_adjacency[curr_cell]) { size_t neighbor_cell, patch_idx; bool direction; std::tie(neighbor_cell, direction, patch_idx) = neighbor; if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); for (size_t i=0; i