You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
5.6 KiB
208 lines
5.6 KiB
#include <igl/fast_winding_number.h>
|
|
#include <igl/read_triangle_mesh.h>
|
|
#include <igl/slice_mask.h>
|
|
#include <Eigen/Geometry>
|
|
#include <igl/octree.h>
|
|
#include <igl/barycenter.h>
|
|
#include <igl/knn.h>
|
|
#include <igl/random_points_on_mesh.h>
|
|
#include <igl/bounding_box_diagonal.h>
|
|
#include <igl/per_face_normals.h>
|
|
#include <igl/copyleft/cgal/point_areas.h>
|
|
#include <igl/opengl/glfw/Viewer.h>
|
|
#include <igl/get_seconds.h>
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const auto time = [](std::function<void(void)> func)->double
|
|
{
|
|
const double t_before = igl::get_seconds();
|
|
func();
|
|
const double t_after = igl::get_seconds();
|
|
return t_after-t_before;
|
|
};
|
|
|
|
Eigen::MatrixXd V;
|
|
Eigen::MatrixXi F;
|
|
igl::read_triangle_mesh(argc>1?argv[1]:TUTORIAL_SHARED_PATH "/bunny.off",V,F);
|
|
// Sample mesh for point cloud
|
|
Eigen::MatrixXd P,N;
|
|
{
|
|
Eigen::VectorXi I;
|
|
Eigen::MatrixXd B;
|
|
igl::random_points_on_mesh(10000,V,F,B,I,P);
|
|
Eigen::MatrixXd FN;
|
|
igl::per_face_normals(V,F,FN);
|
|
N.resize(P.rows(),3);
|
|
for(int p = 0;p<I.rows();p++)
|
|
{
|
|
N.row(p) = FN.row(I(p));
|
|
}
|
|
}
|
|
// Build octree
|
|
std::vector<std::vector<int > > O_PI;
|
|
Eigen::MatrixXi O_CH;
|
|
Eigen::MatrixXd O_CN;
|
|
Eigen::VectorXd O_W;
|
|
igl::octree(P,O_PI,O_CH,O_CN,O_W);
|
|
Eigen::VectorXd A;
|
|
{
|
|
Eigen::MatrixXi I;
|
|
igl::knn(P,20,O_PI,O_CH,O_CN,O_W,I);
|
|
// CGAL is only used to help get point areas
|
|
igl::copyleft::cgal::point_areas(P,I,N,A);
|
|
}
|
|
|
|
if(argc<=1)
|
|
{
|
|
// corrupt mesh
|
|
Eigen::MatrixXd BC;
|
|
igl::barycenter(V,F,BC);
|
|
Eigen::MatrixXd OV = V;
|
|
V.resize(F.rows()*3,3);
|
|
for(int f = 0;f<F.rows();f++)
|
|
{
|
|
for(int c = 0;c<3;c++)
|
|
{
|
|
int v = f+c*F.rows();
|
|
// random rotation about barycenter
|
|
Eigen::AngleAxisd R(
|
|
0.5*static_cast <double> (rand()) / static_cast <double> (RAND_MAX),
|
|
Eigen::Vector3d::Random(3,1));
|
|
V.row(v) = (OV.row(F(f,c))-BC.row(f))*R.matrix()+BC.row(f);
|
|
F(f,c) = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate a list of random query points in the bounding box
|
|
Eigen::MatrixXd Q = Eigen::MatrixXd::Random(1000000,3);
|
|
const Eigen::RowVector3d Vmin = V.colwise().minCoeff();
|
|
const Eigen::RowVector3d Vmax = V.colwise().maxCoeff();
|
|
const Eigen::RowVector3d Vdiag = Vmax-Vmin;
|
|
for(int q = 0;q<Q.rows();q++)
|
|
{
|
|
Q.row(q) = (Q.row(q).array()*0.5+0.5)*Vdiag.array() + Vmin.array();
|
|
}
|
|
|
|
// Positions of points inside of point cloud P
|
|
Eigen::MatrixXd QiP;
|
|
{
|
|
Eigen::MatrixXd O_CM;
|
|
Eigen::VectorXd O_R;
|
|
Eigen::MatrixXd O_EC;
|
|
printf(" point cloud precomputation (% 8ld points): %g secs\n",
|
|
P.rows(),
|
|
time([&](){igl::fast_winding_number(P,N,A,O_PI,O_CH,2,O_CM,O_R,O_EC);}));
|
|
Eigen::VectorXd WiP;
|
|
printf(" point cloud evaluation (% 8ld queries): %g secs\n",
|
|
Q.rows(),
|
|
time([&](){igl::fast_winding_number(P,N,A,O_PI,O_CH,O_CM,O_R,O_EC,Q,2,WiP);}));
|
|
igl::slice_mask(Q,(WiP.array()>0.5).eval(),1,QiP);
|
|
}
|
|
|
|
// Positions of points inside of triangle soup (V,F)
|
|
Eigen::MatrixXd QiV;
|
|
{
|
|
igl::FastWindingNumberBVH fwn_bvh;
|
|
printf("triangle soup precomputation (% 8ld triangles): %g secs\n",
|
|
F.rows(),
|
|
time([&](){igl::fast_winding_number(V.cast<float>().eval(),F,2,fwn_bvh);}));
|
|
Eigen::VectorXf WiV;
|
|
printf(" triangle soup evaluation (% 8ld queries): %g secs\n",
|
|
Q.rows(),
|
|
time([&](){igl::fast_winding_number(fwn_bvh,2,Q.cast<float>().eval(),WiV);}));
|
|
igl::slice_mask(Q,WiV.array()>0.5,1,QiV);
|
|
}
|
|
|
|
|
|
// Visualization
|
|
igl::opengl::glfw::Viewer viewer;
|
|
// For dislpaying normals as little line segments
|
|
Eigen::MatrixXd PN(2*P.rows(),3);
|
|
Eigen::MatrixXi E(P.rows(),2);
|
|
const double bbd = igl::bounding_box_diagonal(V);
|
|
for(int p = 0;p<P.rows();p++)
|
|
{
|
|
E(p,0) = 2*p;
|
|
E(p,1) = 2*p+1;
|
|
PN.row(E(p,0)) = P.row(p);
|
|
PN.row(E(p,1)) = P.row(p)+bbd*0.01*N.row(p);
|
|
}
|
|
|
|
bool show_P = false;
|
|
int show_Q = 0;
|
|
|
|
int query_data = 0;
|
|
viewer.data_list[query_data].set_mesh(V,F);
|
|
viewer.data_list[query_data].clear();
|
|
viewer.data_list[query_data].point_size = 2;
|
|
viewer.append_mesh();
|
|
int object_data = 1;
|
|
viewer.data_list[object_data].set_mesh(V,F);
|
|
viewer.data_list[object_data].point_size = 5;
|
|
|
|
const auto update = [&]()
|
|
{
|
|
viewer.data_list[query_data].clear();
|
|
switch(show_Q)
|
|
{
|
|
case 1:
|
|
// show all Q
|
|
viewer.data_list[query_data].set_points(Q,Eigen::RowVector3d(0.996078,0.760784,0.760784));
|
|
break;
|
|
case 2:
|
|
// show all Q inside
|
|
if(show_P)
|
|
{
|
|
viewer.data_list[query_data].set_points(QiP,Eigen::RowVector3d(0.564706,0.847059,0.768627));
|
|
}else
|
|
{
|
|
viewer.data_list[query_data].set_points(QiV,Eigen::RowVector3d(0.564706,0.847059,0.768627));
|
|
}
|
|
break;
|
|
}
|
|
|
|
viewer.data_list[object_data].clear();
|
|
if(show_P)
|
|
{
|
|
viewer.data_list[object_data].set_points(P,Eigen::RowVector3d(1,1,1));
|
|
viewer.data_list[object_data].set_edges(PN,E,Eigen::RowVector3d(0.8,0.8,0.8));
|
|
}else
|
|
{
|
|
viewer.data_list[object_data].set_mesh(V,F);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
viewer.callback_key_pressed =
|
|
[&](igl::opengl::glfw::Viewer &, unsigned int key, int mod)
|
|
{
|
|
switch(key)
|
|
{
|
|
default:
|
|
return false;
|
|
case '1':
|
|
show_P = !show_P;
|
|
break;
|
|
case '2':
|
|
show_Q = (show_Q+1) % 3;
|
|
break;
|
|
}
|
|
update();
|
|
return true;
|
|
};
|
|
|
|
std::cout<<R"(
|
|
FastWindingNumber
|
|
1 Toggle point cloud and triangle soup
|
|
2 Toggle hiding query points, showing query points, showing inside queries
|
|
)";
|
|
|
|
update();
|
|
viewer.launch();
|
|
|
|
}
|
|
|