#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 "order_facets_around_edge.h" #include "outer_facet.h" #include "closest_facet.h" #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( 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 nested_Ws = Ws; Eigen::MatrixXi ambient_correction(num_components, 2*num_labels); ambient_correction.setZero(); 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