#include "orient_outward_ao.h" #include "../per_face_normals.h" #include "../doublearea.h" #include "../random_dir.h" #include "EmbreeIntersector.h" #include #include #include #include template < typename DerivedV, typename DerivedF, typename DerivedC, typename DerivedFF, typename DerivedI> IGL_INLINE void igl::orient_outward_ao( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & C, const int min_num_rays_per_component, const int total_num_rays, Eigen::PlainObjectBase & FF, Eigen::PlainObjectBase & I) { using namespace Eigen; using namespace std; assert(C.rows() == F.rows()); assert(F.cols() == 3); assert(V.cols() == 3); EmbreeIntersector ei; ei.init(V.template cast(),F); // number of faces const int m = F.rows(); // number of patches const int num_cc = C.maxCoeff()+1; I.resize(num_cc); if(&FF != &F) { FF = F; } // face normal MatrixXd N; per_face_normals(V,F,N); // face area Matrix A; doublearea(V,F,A); double area_min = numeric_limits::max(); for (int f = 0; f < m; ++f) { area_min = A(f) != 0 && A(f) < area_min ? A(f) : area_min; } double area_total = A.sum(); // determine number of rays per component according to its area VectorXd area_per_component; area_per_component.setZero(num_cc); for (int f = 0; f < m; ++f) { area_per_component(C(f)) += A(f); } VectorXi num_rays_per_component; num_rays_per_component.setZero(num_cc); for (int c = 0; c < num_cc; ++c) { num_rays_per_component(c) = max(min_num_rays_per_component, static_cast(total_num_rays * area_per_component(c) / area_total)); } // generate all the rays cout << "generating rays... "; uniform_real_distribution rdist; mt19937 prng; prng.seed(time(nullptr)); vector ray_face; vector ray_ori; vector ray_dir; ray_face.reserve(total_num_rays); ray_ori .reserve(total_num_rays); ray_dir .reserve(total_num_rays); for (int c = 0; c < num_cc; ++c) { if (area_per_component[c] == 0) { continue; } vector CF; // set of faces per component vector CF_area; for (int f = 0; f < m; ++f) { if (C(f)==c) { CF.push_back(f); CF_area.push_back(static_cast(100 * A(f) / area_min)); } } // discrete distribution for random selection of faces with probability proportional to their areas auto ddist_func = [&] (double i) { return CF_area[static_cast(i)]; }; discrete_distribution ddist(CF.size(), 0, CF.size(), ddist_func); // simple ctor of (Iter, Iter) not provided by the stupid VC11 impl... for (int i = 0; i < num_rays_per_component[c]; ++i) { int f = CF[ddist(prng)]; // select face with probability proportional to face area float t0 = rdist(prng); // random barycentric coordinate float t1 = rdist(prng); float t2 = rdist(prng); float t_sum = t0 + t1 + t2; t0 /= t_sum; t1 /= t_sum; t2 /= t_sum; Vector3f p = t0 * V.row(F(f,0)).template cast().eval() // be careful with the index!!! + t1 * V.row(F(f,1)).template cast().eval() + t2 * V.row(F(f,2)).template cast().eval(); Vector3f n = N.row(f).cast(); // random direction in hemisphere around n (avoid too grazing angle) Vector3f d; while (true) { d = random_dir().cast(); float ndotd = n.dot(d); if (fabsf(ndotd) < 0.1f) { continue; } if (ndotd < 0) { d *= -1.0f; } break; } ray_face.push_back(f); ray_ori .push_back(p); ray_dir .push_back(d); } } // per component voting: first=front, second=back vector> C_vote_distance(num_cc, make_pair(0, 0)); // sum of distance between ray origin and intersection vector> C_vote_infinity(num_cc, make_pair(0, 0)); // number of rays reaching infinity cout << "shooting rays... "; #pragma omp parallel for for (int i = 0; i < (int)ray_face.size(); ++i) { int f = ray_face[i]; Vector3f o = ray_ori [i]; Vector3f d = ray_dir [i]; int c = C(f); // shoot ray toward front & back vector hits_front; vector hits_back; int num_rays_front; int num_rays_back; ei.intersectRay(o, d, hits_front, num_rays_front); ei.intersectRay(o, -d, hits_back , num_rays_back ); if (!hits_front.empty() && hits_front[0].id == f) hits_front.erase(hits_front.begin()); if (!hits_back .empty() && hits_back [0].id == f) hits_back .erase(hits_back .begin()); if (hits_front.empty()) { #pragma omp atomic C_vote_infinity[c].first++; } else { #pragma omp atomic C_vote_distance[c].first += hits_front[0].t; } if (hits_back.empty()) { #pragma omp atomic C_vote_infinity[c].second++; } else { #pragma omp atomic C_vote_distance[c].second += hits_back[0].t; } } for(int c = 0;c IGL_INLINE void igl::orient_outward_ao( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & C, Eigen::PlainObjectBase & FF, Eigen::PlainObjectBase & I) { return orient_outward_ao(V, F, C, 100, F.rows() * 100, FF, I); } #ifndef IGL_HEADER_ONLY // Explicit template specialization template void igl::orient_outward_ao, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); #endif