#include "point_areas.h" #include "delaunay_triangulation.h" #include "../../colon.h" #include "../../slice.h" #include "../../slice_mask.h" #include "../../parallel_for.h" #include "CGAL/Exact_predicates_inexact_constructions_kernel.h" #include "CGAL/Triangulation_vertex_base_with_info_2.h" #include "CGAL/Triangulation_data_structure_2.h" #include "CGAL/Delaunay_triangulation_2.h" typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; typedef CGAL::Triangulation_data_structure_2 Tds; typedef CGAL::Delaunay_triangulation_2 Delaunay; typedef Kernel::Point_2 Point; namespace igl { namespace copyleft{ namespace cgal{ template IGL_INLINE void point_areas( const Eigen::MatrixBase& P, const Eigen::MatrixBase& I, const Eigen::MatrixBase& N, Eigen::PlainObjectBase & A) { Eigen::MatrixXd T; point_areas(P,I,N,A,T); } template IGL_INLINE void point_areas( const Eigen::MatrixBase& P, const Eigen::MatrixBase& I, const Eigen::MatrixBase& N, Eigen::PlainObjectBase & A, Eigen::PlainObjectBase & T) { typedef typename DerivedP::Scalar real; typedef typename DerivedN::Scalar scalarN; typedef typename DerivedA::Scalar scalarA; typedef Eigen::Matrix RowVec3; typedef Eigen::Matrix RowVec2; typedef Eigen::Matrix MatrixP; typedef Eigen::Matrix MatrixN; typedef Eigen::Matrix VecotorO; typedef Eigen::Matrix MatrixI; const int n = P.rows(); assert(P.cols() == 3 && "P must have exactly 3 columns"); assert(P.rows() == N.rows() && "P and N must have the same number of rows"); assert(P.rows() == I.rows() && "P and I must have the same number of rows"); A.setZero(n,1); T.setZero(n,3); igl::parallel_for(P.rows(),[&](int i) { MatrixI neighbor_index = I.row(i); MatrixP neighbors; igl::slice(P,neighbor_index,1,neighbors); if(N.rows() && neighbors.rows() > 1){ MatrixN neighbor_normals; igl::slice(N,neighbor_index,1,neighbor_normals); Eigen::Matrix poi_normal = neighbor_normals.row(0); Eigen::Matrix dotprod = poi_normal(0)*neighbor_normals.col(0) + poi_normal(1)*neighbor_normals.col(1) + poi_normal(2)*neighbor_normals.col(2); Eigen::Array keep = dotprod.array() > 0; igl::slice_mask(Eigen::MatrixXd(neighbors),keep,1,neighbors); } if(neighbors.rows() <= 2){ A(i) = 0; } else { //subtract the mean from neighbors, then take svd, //the scores will be U*S. This is our pca plane fitting RowVec3 mean = neighbors.colwise().mean(); MatrixP mean_centered = neighbors.rowwise() - mean; Eigen::JacobiSVD svd(mean_centered, Eigen::ComputeThinU | Eigen::ComputeThinV); MatrixP scores = svd.matrixU() * svd.singularValues().asDiagonal(); T.row(i) = svd.matrixV().col(2).transpose(); if(T.row(i).dot(N.row(i)) < 0){ T.row(i) *= -1; } MatrixP plane; igl::slice(scores,igl::colon(0,scores.rows()-1), igl::colon(0,1),plane); std::vector< std::pair > points; //This is where we obtain a delaunay triangulation of the points for(unsigned iter = 0; iter < plane.rows(); iter++){ points.push_back( std::make_pair( Point(plane(iter,0),plane(iter,1)), iter ) ); } Delaunay triangulation; triangulation.insert(points.begin(),points.end()); Eigen::MatrixXi F(triangulation.number_of_faces(),3); int f_row = 0; for(Delaunay::Finite_faces_iterator fit = triangulation.finite_faces_begin(); fit != triangulation.finite_faces_end(); ++fit) { Delaunay::Face_handle face = fit; F.row(f_row) = Eigen::RowVector3i((int)face->vertex(0)->info(), (int)face->vertex(1)->info(), (int)face->vertex(2)->info()); f_row++; } //Here we calculate the voronoi area of the point scalarA area_accumulator = 0; for(int f = 0; f < F.rows(); f++){ int X = -1; for(int face_iter = 0; face_iter < 3; face_iter++){ if(F(f,face_iter) == 0){ X = face_iter; } } if(X >= 0){ //Triangle XYZ with X being the point we want the area of int Y = (X+1)%3; int Z = (X+2)%3; scalarA x = (plane.row(F(f,Y))-plane.row(F(f,Z))).norm(); scalarA y = (plane.row(F(f,X))-plane.row(F(f,Z))).norm(); scalarA z = (plane.row(F(f,Y))-plane.row(F(f,X))).norm(); scalarA cosX = (z*z + y*y - x*x)/(2*y*z); scalarA cosY = (z*z + x*x - y*y)/(2*x*z); scalarA cosZ = (x*x + y*y - z*z)/(2*y*x); Eigen::Matrix barycentric; barycentric << x*cosX, y*cosY, z*cosZ; barycentric /= (barycentric(0)+barycentric(1)+barycentric(2)); //TODO: to make numerically stable, reorder so that x≥y≥z: scalarA full_area = 0.25*std::sqrt( (x+(y+z))*(z-(x-y))*(z+(x-y))*(x+(y-z))); Eigen::Matrix partial_area = barycentric * full_area; if(cosX < 0){ area_accumulator += 0.5*full_area; } else if (cosY < 0 || cosZ < 0){ area_accumulator += 0.25*full_area; } else { area_accumulator += (partial_area(1) + partial_area(2))/2; } } } if(std::isfinite(area_accumulator)){ A(i) = area_accumulator; } else { A(i) = 0; } } },1000); } } } }