Browse Source

signed distances in tutorial

Former-commit-id: b6c92a81ade2809674a39c08b1e31c32bb27473a
Alec Jacobson 10 years ago
parent
commit
bb6a441f98

+ 11 - 0
tutorial/704_SignedDistance/CMakeLists.txt

@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 2.6)
+project(704_SignedDistance)
+
+include("../CMakeLists.shared")
+
+set(SOURCES
+${PROJECT_SOURCE_DIR}/main.cpp
+)
+
+add_executable(${PROJECT_NAME}_bin ${SOURCES} ${SHARED_SOURCES})
+target_link_libraries(${PROJECT_NAME}_bin ${SHARED_LIBRARIES})

+ 157 - 0
tutorial/704_SignedDistance/main.cpp

@@ -0,0 +1,157 @@
+#include <igl/cat.h>
+#include <igl/edge_lengths.h>
+#include <igl/parula.h>
+#include <igl/per_edge_normals.h>
+#include <igl/per_face_normals.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/point_mesh_squared_distance.h>
+#include <igl/readMESH.h>
+#include <igl/signed_distance.h>
+#include <igl/slice_mask.h>
+#include <igl/slice_tets.h>
+#include <igl/upsample.h>
+#include <igl/viewer/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi T,F;
+igl::AABB<Eigen::MatrixXd,3> tree;
+Eigen::MatrixXd FN,VN,EN;
+Eigen::MatrixXi E;
+Eigen::VectorXi EMAP;
+double max_distance = 1;
+
+double slice_z = 0.5;
+bool overlay = false;
+
+void update_visualization(igl::Viewer & viewer)
+{
+  using namespace Eigen;
+  using namespace std;
+  Eigen::Vector4d plane(
+    0,0,1,-((1-slice_z)*V.col(2).minCoeff()+slice_z*V.col(2).maxCoeff()));
+  MatrixXd V_vis;
+  MatrixXi F_vis;
+  // Extract triangle mesh slice through volume mesh and subdivide nasty
+  // triangles
+  {
+    VectorXi J;
+    SparseMatrix<double> bary;
+    igl::slice_tets(V,T,plane,V_vis,F_vis,J,bary);
+    while(true)
+    {
+      MatrixXd l;
+      igl::edge_lengths(V_vis,F_vis,l);
+      l /= (V_vis.colwise().maxCoeff() - V_vis.colwise().minCoeff()).norm();
+      const double max_l = 0.03;
+      if(l.maxCoeff()<max_l)
+      {
+        break;
+      }
+      Array<bool,Dynamic,1> bad = l.array().rowwise().maxCoeff() > max_l;
+      MatrixXi F_vis_bad, F_vis_good;
+      igl::slice_mask(F_vis,bad,1,F_vis_bad);
+      igl::slice_mask(F_vis,(bad!=true).eval(),1,F_vis_good);
+      igl::upsample(V_vis,F_vis_bad);
+      F_vis = igl::cat(1,F_vis_bad,F_vis_good);
+    }
+  }
+
+  // Compute signed distance
+  VectorXd S_vis;
+  {
+    VectorXi I;
+    MatrixXd N,C;
+    // Bunny is a watertight mesh so use pseudonormal for signing
+    signed_distance_pseudonormal(V_vis,V,F,tree,FN,VN,EN,EMAP,S_vis,I,C,N);
+  }
+  // push to [0,1] range
+  S_vis.array() = 0.5*(S_vis.array()/max_distance)+0.5;
+  MatrixXd C_vis;
+  // color without normalizing
+  igl::parula(S_vis,false,C_vis);
+
+
+  const auto & append_mesh = [&C_vis,&F_vis,&V_vis](
+    const Eigen::MatrixXd & V,
+    const Eigen::MatrixXi & F,
+    const RowVector3d & color)
+  {
+    F_vis.conservativeResize(F_vis.rows()+F.rows(),3);
+    F_vis.bottomRows(F.rows()) = F.array()+V_vis.rows();
+    V_vis.conservativeResize(V_vis.rows()+V.rows(),3);
+    V_vis.bottomRows(V.rows()) = V;
+    C_vis.conservativeResize(C_vis.rows()+V.rows(),3);
+    C_vis.bottomRows(V.rows()).rowwise() = color;
+  };
+  if(overlay)
+  {
+    append_mesh(V,F,RowVector3d(0.8,0.8,0.8));
+  }
+  viewer.data.clear();
+  viewer.data.set_mesh(V_vis,F_vis);
+  viewer.data.set_colors(C_vis);
+  viewer.core.lighting_factor = overlay;
+}
+
+bool key_down(igl::Viewer& viewer, unsigned char key, int mod)
+{
+  switch(key)
+  {
+    default:
+      return false;
+    case ' ':
+      overlay ^= true;
+      break;
+    case '.':
+      slice_z = std::min(slice_z+0.01,0.99);
+      break;
+    case ',':
+      slice_z = std::max(slice_z-0.01,0.01);
+      break;
+  }
+  update_visualization(viewer);
+  return true;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  cout<<"Usage:"<<endl;
+  cout<<"[space]  toggle showing surface."<<endl;
+  cout<<"'.'/','  push back/pull forward slicing plane."<<endl;
+  cout<<endl;
+
+  // Load mesh: (V,T) tet-mesh of convex hull, F contains original surface
+  // triangles
+  igl::readMESH("../shared/bunny.mesh",V,T,F);
+
+
+  // Encapsulated call to point_mesh_squared_distance to determine bounds
+  {
+    VectorXd sqrD;
+    VectorXi I;
+    MatrixXd C;
+    igl::point_mesh_squared_distance(V,V,F,sqrD,I,C);
+    max_distance = sqrt(sqrD.maxCoeff());
+  }
+
+  // Precompute signed distance AABB tree
+  tree.init(V,F);
+  // Precompute vertex,edge and face normals
+  igl::per_face_normals(V,F,FN);
+  igl::per_vertex_normals(
+    V,F,igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE,FN,VN);
+  igl::per_edge_normals(
+    V,F,igl::PER_EDGE_NORMALS_WEIGHTING_TYPE_UNIFORM,FN,EN,E,EMAP);
+
+  // Plot the generated mesh
+  igl::Viewer viewer;
+  update_visualization(viewer);
+  viewer.callback_key_down = &key_down;
+  viewer.core.show_lines = false;
+  viewer.launch();
+}

+ 1 - 0
tutorial/CMakeLists.txt

@@ -80,3 +80,4 @@ endif()
 add_subdirectory("701_Statistics")
 add_subdirectory("702_WindingNumber")
 add_subdirectory("703_Decimation")
+add_subdirectory("704_SignedDistance")

+ 1 - 0
tutorial/images/bunny-signed-distance.gif.REMOVED.git-id

@@ -0,0 +1 @@
+28f9e039292c070f3b8f51dfe2f33e42aeb4ffb4

+ 1 - 0
tutorial/shared/bunny.mesh.REMOVED.git-id

@@ -0,0 +1 @@
+c3402dec900c0f25303fe987adf8f010ac025106

+ 1 - 1
tutorial/tutorial.html.REMOVED.git-id

@@ -1 +1 @@
-9ae5490d9bba6bec788d89d0cc640430b4f08677
+123e5a9852152af5548e1130853e597265d897f5

+ 1 - 1
tutorial/tutorial.md.REMOVED.git-id

@@ -1 +1 @@
-4c8471892755633646d65f3b104fa8a5de3660e4
+628b770ce18ce0b59a429488ecab9c9363b5be76