Browse Source

Merge pull request #1510 from libigl/exploded-view

exploded view of tetrahedral meshes
Alec Jacobson 5 years ago
parent
commit
ce9b23e8b9

+ 70 - 0
include/igl/exploded_view.cpp

@@ -0,0 +1,70 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2020 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "exploded_view.h"
+#include "barycenter.h"
+#include "volume.h"
+
+template <
+  typename DerivedV,
+  typename DerivedT,
+  typename DerivedEV,
+  typename DerivedEF,
+  typename DerivedI,
+  typename DerivedJ>
+IGL_INLINE void igl::exploded_view(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedT> & T,
+  const typename DerivedV::Scalar s,
+  const typename DerivedV::Scalar t,
+  Eigen::PlainObjectBase<DerivedEV> & EV,
+  Eigen::PlainObjectBase<DerivedEF> & EF,
+  Eigen::PlainObjectBase<DerivedI> & I,
+  Eigen::PlainObjectBase<DerivedJ> & J)
+{
+  assert(T.cols() == 4 && "T should be a tet mesh");
+  EV.resize(4*T.rows(),3);
+  EF.resize(4*T.rows(),3);
+  I.resize(EV.rows());
+  J.resize(EF.rows());
+  Eigen::MatrixXd BC;
+  igl::barycenter(V,T,BC);
+  Eigen::VectorXd vol;
+  igl::volume(V,T,vol);
+  const Eigen::RowVectorXd c = vol.transpose()*BC/vol.array().sum();
+  for(int i = 0;i<T.rows();i++)
+  {
+    // scale the barycenters outward
+    const auto tbc = ((BC.row(i)-c)*t+c).eval();
+    for(int j = 0;j<4;j++)
+    {
+      // vector to barycenter
+      const auto v = V.row(T(i,j))-BC.row(i);
+      // scale vector and add to scaled barycenter
+      EV.row(i*4+j) = v*s+tbc;
+      I(i*4+j) = T(i,j);
+      J(i*4+j) = i;
+      if(j%2)
+      {
+        EF(i*4+j,0) = i*4+((j+3)%4);
+        EF(i*4+j,1) = i*4+((j+2)%4);
+        EF(i*4+j,2) = i*4+((j+1)%4);
+      }else
+      {
+        EF(i*4+j,0) = i*4+((j+1)%4);
+        EF(i*4+j,1) = i*4+((j+2)%4);
+        EF(i*4+j,2) = i*4+((j+3)%4);
+      }
+    }
+  }
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+// generated by autoexplicit.sh
+template void igl::exploded_view<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar, Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+#endif

+ 50 - 0
include/igl/exploded_view.h

@@ -0,0 +1,50 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2020 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_EXPLODED_VIEW_H
+#define IGL_EXPLODED_VIEW_H
+#include "igl_inline.h"
+#include <Eigen/Core>
+namespace igl
+{
+  // Given a tet-mesh, create a trivial surface mesh (4 triangles per tet) with
+  // each tet scaled individually and translated outward from the mesh's
+  // centroid, creating an exploded-view visualization.
+  //
+  // Inputs:
+  //   V  #V by 3 list of tet mesh vertex positions
+  //   T  #T by 4 list of tet mesh indices into rows of V
+  //   s  amount to scale each tet indvidually, typically (0,1]
+  //   t  amount to scale away from mesh's centroid, typically >=1
+  // Outputs:
+  //   EV  #T*4 by 3 list of output mesh vertex positions
+  //   EF  #T*4 by 3 list of output triangle indices into rows of EV
+  //   I  #EV list of indices into V revealing birth parent
+  //   J  #EF list of indices into F revealing birth parent
+  template <
+    typename DerivedV,
+    typename DerivedT,
+    typename DerivedEV,
+    typename DerivedEF,
+    typename DerivedI,
+    typename DerivedJ>
+  IGL_INLINE void exploded_view(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedT> & T,
+    const typename DerivedV::Scalar s,
+    const typename DerivedV::Scalar t,
+    Eigen::PlainObjectBase<DerivedEV> & EV,
+    Eigen::PlainObjectBase<DerivedEF> & EF,
+    Eigen::PlainObjectBase<DerivedI> & I,
+    Eigen::PlainObjectBase<DerivedJ> & J);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "exploded_view.cpp"
+#endif
+
+#endif

+ 24 - 20
include/igl/opengl/ViewerData.cpp

@@ -189,31 +189,35 @@ IGL_INLINE void igl::opengl::ViewerData::set_colors(const Eigen::MatrixXd &C)
     F_material_ambient = ambient(F_material_diffuse);
     F_material_specular = specular(F_material_diffuse);
   }
-  else if (C.rows() == V.rows())
+  else if(C.rows() == V.rows() || C.rows() == F.rows())
   {
-    set_face_based(false);
-    for (unsigned i=0;i<V_material_diffuse.rows();++i)
+    // face based colors? 
+    if((C.rows()==F.rows()) && (C.rows() != V.rows() || face_based))
     {
-      if (C.cols() == 3)
-        V_material_diffuse.row(i) << C.row(i), 1;
-      else if (C.cols() == 4)
-        V_material_diffuse.row(i) << C.row(i);
+      set_face_based(true);
+      for (unsigned i=0;i<F_material_diffuse.rows();++i)
+      {
+        if (C.cols() == 3)
+          F_material_diffuse.row(i) << C.row(i), 1;
+        else if (C.cols() == 4)
+          F_material_diffuse.row(i) << C.row(i);
+      }
+      F_material_ambient = ambient(F_material_diffuse);
+      F_material_specular = specular(F_material_diffuse);
     }
-    V_material_ambient = ambient(V_material_diffuse);
-    V_material_specular = specular(V_material_diffuse);
-  }
-  else if (C.rows() == F.rows())
-  {
-    set_face_based(true);
-    for (unsigned i=0;i<F_material_diffuse.rows();++i)
+    else/*(C.rows() == V.rows())*/
     {
-      if (C.cols() == 3)
-        F_material_diffuse.row(i) << C.row(i), 1;
-      else if (C.cols() == 4)
-        F_material_diffuse.row(i) << C.row(i);
+      set_face_based(false);
+      for (unsigned i=0;i<V_material_diffuse.rows();++i)
+      {
+        if (C.cols() == 3)
+          V_material_diffuse.row(i) << C.row(i), 1;
+        else if (C.cols() == 4)
+          V_material_diffuse.row(i) << C.row(i);
+      }
+      V_material_ambient = ambient(V_material_diffuse);
+      V_material_specular = specular(V_material_diffuse);
     }
-    F_material_ambient = ambient(F_material_diffuse);
-    F_material_specular = specular(F_material_diffuse);
   }
   else
     cerr << "ERROR (set_colors): Please provide a single color, or a color per face or per vertex."<<endl;

+ 5 - 0
tutorial/719_ExplodedView/CMakeLists.txt

@@ -0,0 +1,5 @@
+get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+project(${PROJECT_NAME})
+
+add_executable(${PROJECT_NAME}_bin main.cpp)
+target_link_libraries(${PROJECT_NAME}_bin igl::core igl::opengl igl::opengl_glfw tutorials)

+ 99 - 0
tutorial/719_ExplodedView/main.cpp

@@ -0,0 +1,99 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2020 Alec Jacobson <[email protected]>
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/readMESH.h>
+#include <igl/barycenter.h>
+#include <igl/volume.h>
+#include <igl/exploded_view.h>
+#include <igl/slice.h>
+#include <igl/colormap.h>
+
+
+int main(int argc, char *argv[])
+{
+
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi T,F;
+  igl::readMESH(argc>1?argv[1]: TUTORIAL_SHARED_PATH "/octopus-low.mesh",V,T,F);
+  // Some per-tet data
+  Eigen::VectorXd D;
+  {
+    Eigen::MatrixXd BC;
+    igl::barycenter(V,T,BC);
+    Eigen::VectorXd vol;
+    igl::volume(V,T,vol);
+    const Eigen::RowVectorXd c = vol.transpose()*BC/vol.array().sum();
+    D = (BC.rowwise()-c).rowwise().norm();
+  }
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+
+  double t = 1;
+  double s = 1;
+  const auto update = [&]()
+  {
+    Eigen::MatrixXd EV;
+    Eigen::MatrixXi EF;
+    Eigen::VectorXi I,J;
+    igl::exploded_view(V,T,s,t,EV,EF,I,J);
+    Eigen::VectorXd DJ;
+    igl::slice(D,J,1,DJ);
+    static bool first = true;
+    if(first)
+    {
+      viewer.data().clear();
+      viewer.data().set_mesh(EV,EF);
+      first = false;
+    }else
+    {
+      viewer.data().set_vertices(EV);
+    }
+    viewer.data().set_face_based(true);
+    Eigen::MatrixXd C;
+    igl::colormap(igl::COLOR_MAP_TYPE_VIRIDIS,DJ,true,C);
+    viewer.data().set_colors(C);
+  };
+  int mod = 0;
+  float prev_y;
+  viewer.callback_mouse_move = [&](igl::opengl::glfw::Viewer &, int x, int y)->bool
+  {
+    if((mod & IGL_MOD_SHIFT)||(mod & IGL_MOD_ALT))
+    {
+      if(mod & IGL_MOD_SHIFT)
+      {
+        t = std::min(std::max(t+0.001*(y-prev_y),1.),2.);
+      }
+      if(mod & IGL_MOD_ALT)
+      {
+        s = std::min(std::max(s+0.001*(y-prev_y),0.),1.);
+      }
+      prev_y = y;
+      update();
+      return true;
+    }
+    return false;
+  };
+  // get modifier
+  viewer.callback_key_down = 
+    [&](igl::opengl::glfw::Viewer &, unsigned char key, int _mod)->bool
+  {
+    prev_y = viewer.current_mouse_y;
+    mod = _mod;
+    return false;
+  };
+  viewer.callback_key_up = 
+    [&](igl::opengl::glfw::Viewer &, unsigned char key, int _mod)->bool
+  {
+    mod = _mod;
+    return false;
+  };
+  update();
+  viewer.launch();
+}

+ 1 - 0
tutorial/CMakeLists.txt

@@ -159,4 +159,5 @@ if(TUTORIALS_CHAPTER7)
     add_subdirectory("717_FastWindingNumber")
   endif()
   add_subdirectory("718_IterativeClosestPoint")
+  add_subdirectory("719_ExplodedView")
 endif()