Browse Source

Normalize all the line endings (#2288)

Evan Barentin 2 years ago
parent
commit
2b05343e0d
61 changed files with 5848 additions and 5848 deletions
  1. 6 6
      include/igl/is_dir.cpp
  2. 2 2
      include/igl/is_file.cpp
  3. 19 19
      tutorial/101_FileIO/main.cpp
  4. 16 16
      tutorial/102_DrawMesh/main.cpp
  5. 49 49
      tutorial/103_Events/main.cpp
  6. 27 27
      tutorial/104_Colors/main.cpp
  7. 83 83
      tutorial/105_Overlays/main.cpp
  8. 105 105
      tutorial/106_ViewerMenu/main.cpp
  9. 54 54
      tutorial/107_MultipleMeshes/main.cpp
  10. 59 59
      tutorial/109_ImGuizmo/main.cpp
  11. 111 111
      tutorial/110_MshView/main.cpp
  12. 19 19
      tutorial/111_MatCap/main.cpp
  13. 70 70
      tutorial/112_Selection/main.cpp
  14. 61 61
      tutorial/201_Normals/main.cpp
  15. 30 30
      tutorial/202_GaussianCurvature/main.cpp
  16. 64 64
      tutorial/203_CurvatureDirections/main.cpp
  17. 51 51
      tutorial/204_Gradient/main.cpp
  18. 104 104
      tutorial/205_Laplacian/main.cpp
  19. 73 73
      tutorial/206_GeodesicDistance/main.cpp
  20. 122 122
      tutorial/207_PolygonLaplacian/main.cpp
  21. 44 44
      tutorial/301_Slice/main.cpp
  22. 38 38
      tutorial/302_Sort/main.cpp
  23. 65 65
      tutorial/303_LaplaceEquation/main.cpp
  24. 89 89
      tutorial/304_LinearEqualityConstraints/main.cpp
  25. 93 93
      tutorial/305_QuadraticProgramming/main.cpp
  26. 74 74
      tutorial/306_EigenDecomposition/main.cpp
  27. 125 125
      tutorial/401_BiharmonicDeformation/main.cpp
  28. 110 110
      tutorial/402_PolyharmonicDeformation/main.cpp
  29. 163 163
      tutorial/403_BoundedBiharmonicWeights/main.cpp
  30. 147 147
      tutorial/404_DualQuaternionSkinning/main.cpp
  31. 134 134
      tutorial/405_AsRigidAsPossible/main.cpp
  32. 161 161
      tutorial/406_FastAutomaticSkinningTransformations/main.cpp
  33. 190 190
      tutorial/407_BiharmonicCoordinates/main.cpp
  34. 64 64
      tutorial/501_HarmonicParam/main.cpp
  35. 56 56
      tutorial/502_LSCMParam/main.cpp
  36. 91 91
      tutorial/503_ARAPParam/main.cpp
  37. 148 148
      tutorial/504_NRosyDesign/main.cpp
  38. 325 325
      tutorial/505_MIQ/main.cpp
  39. 255 255
      tutorial/506_FrameField/main.cpp
  40. 114 114
      tutorial/507_Planarization/main.cpp
  41. 113 113
      tutorial/601_Serialization/main.cpp
  42. 70 70
      tutorial/602_Matlab/main.cpp
  43. 48 48
      tutorial/604_Triangle/main.cpp
  44. 79 79
      tutorial/605_Tetgen/main.cpp
  45. 78 78
      tutorial/606_AmbientOcclusion/main.cpp
  46. 95 95
      tutorial/609_Boolean/main.cpp
  47. 57 57
      tutorial/701_Statistics/main.cpp
  48. 146 146
      tutorial/702_WindingNumber/main.cpp
  49. 236 236
      tutorial/704_SignedDistance/main.cpp
  50. 108 108
      tutorial/706_FacetOrientation/main.cpp
  51. 45 45
      tutorial/708_Picking/main.cpp
  52. 126 126
      tutorial/710_SCAF/main.cpp
  53. 152 152
      tutorial/803_ShapeUp/main.cpp
  54. 112 112
      tutorial/806_HeatGeodesics/main.cpp
  55. 208 208
      tutorial/807_FastWindingNumber/main.cpp
  56. 108 108
      tutorial/808_IterativeClosestPoint/main.cpp
  57. 98 98
      tutorial/809_ExplodedView/main.cpp
  58. 108 108
      tutorial/810_BlueNoise/main.cpp
  59. 58 58
      tutorial/903_FastFindSelfIntersections/main.cpp
  60. 98 98
      tutorial/904_FastFindIntersections/main.cpp
  61. 94 94
      tutorial/906_TrimWithSolid/main.cpp

+ 6 - 6
include/igl/is_dir.cpp

@@ -9,12 +9,12 @@
 
 #include <sys/stat.h>
 
-#ifndef S_ISDIR
-#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
-#endif
-
-#ifndef S_ISREG
-#define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
+#ifndef S_ISDIR
+#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
 #endif
 
 IGL_INLINE bool igl::is_dir(const char * filename)

+ 2 - 2
include/igl/is_file.cpp

@@ -9,8 +9,8 @@
 
 #include <sys/stat.h>
 #ifdef _WIN32
-#  ifndef S_ISREG
-#    define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
+#  ifndef S_ISREG
+#    define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
 #  endif
 #endif
 IGL_INLINE bool igl::is_file(const char * filename)

+ 19 - 19
tutorial/101_FileIO/main.cpp

@@ -1,19 +1,19 @@
-#include <igl/readOFF.h>
-#include <igl/writeOBJ.h>
-#include <iostream>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH  "/cube.off", V, F);
-
-  // Print the vertices and faces matrices
-  std::cout << "Vertices: " << std::endl << V << std::endl;
-  std::cout << "Faces:    " << std::endl << F << std::endl;
-
-  // Save the mesh in OBJ format
-  igl::writeOBJ("cube.obj",V,F);
-}
+#include <igl/readOFF.h>
+#include <igl/writeOBJ.h>
+#include <iostream>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH  "/cube.off", V, F);
+
+  // Print the vertices and faces matrices
+  std::cout << "Vertices: " << std::endl << V << std::endl;
+  std::cout << "Faces:    " << std::endl << F << std::endl;
+
+  // Save the mesh in OBJ format
+  igl::writeOBJ("cube.obj",V,F);
+}

+ 16 - 16
tutorial/102_DrawMesh/main.cpp

@@ -1,16 +1,16 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.launch();
+}

+ 49 - 49
tutorial/103_Events/main.cpp

@@ -1,49 +1,49 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-Eigen::MatrixXd V1,V2;
-Eigen::MatrixXi F1,F2;
-
-// This function is called every time a keyboard button is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  std::cout<<"Key: "<<key<<" "<<(unsigned int)key<<std::endl;
-  if (key == '1')
-  {
-    // Clear should be called before drawing the mesh
-    viewer.data().clear();
-    // Draw_mesh creates or updates the vertices and faces of the displayed mesh.
-    // If a mesh is already displayed, draw_mesh returns an error if the given V and
-    // F have size different than the current ones
-    viewer.data().set_mesh(V1, F1);
-    viewer.core().align_camera_center(V1,F1);
-  }
-  else if (key == '2')
-  {
-    viewer.data().clear();
-    viewer.data().set_mesh(V2, F2);
-    viewer.core().align_camera_center(V2,F2);
-  }
-
-  return false;
-}
-
-
-int main(int argc, char *argv[])
-{
-  // Load two meshes
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", V1, F1);
-  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V2, F2);
-  std::cout<<R"(
-1 Switch to bump mesh
-2 Switch to fertility mesh
-    )";
-
-  igl::opengl::glfw::Viewer viewer;
-  // Register a keyboard callback that allows to switch between
-  // the two loaded meshes
-  viewer.callback_key_down = &key_down;
-  viewer.data().set_mesh(V1, F1);
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+Eigen::MatrixXd V1,V2;
+Eigen::MatrixXi F1,F2;
+
+// This function is called every time a keyboard button is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  std::cout<<"Key: "<<key<<" "<<(unsigned int)key<<std::endl;
+  if (key == '1')
+  {
+    // Clear should be called before drawing the mesh
+    viewer.data().clear();
+    // Draw_mesh creates or updates the vertices and faces of the displayed mesh.
+    // If a mesh is already displayed, draw_mesh returns an error if the given V and
+    // F have size different than the current ones
+    viewer.data().set_mesh(V1, F1);
+    viewer.core().align_camera_center(V1,F1);
+  }
+  else if (key == '2')
+  {
+    viewer.data().clear();
+    viewer.data().set_mesh(V2, F2);
+    viewer.core().align_camera_center(V2,F2);
+  }
+
+  return false;
+}
+
+
+int main(int argc, char *argv[])
+{
+  // Load two meshes
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", V1, F1);
+  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V2, F2);
+  std::cout<<R"(
+1 Switch to bump mesh
+2 Switch to fertility mesh
+    )";
+
+  igl::opengl::glfw::Viewer viewer;
+  // Register a keyboard callback that allows to switch between
+  // the two loaded meshes
+  viewer.callback_key_down = &key_down;
+  viewer.data().set_mesh(V1, F1);
+  viewer.launch();
+}

+ 27 - 27
tutorial/104_Colors/main.cpp

@@ -1,27 +1,27 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-Eigen::MatrixXd C;
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/screwdriver.off", V, F);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-
-  // Use the (normalized) vertex positions as colors
-  C =
-    (V.rowwise()            - V.colwise().minCoeff()).array().rowwise()/
-    (V.colwise().maxCoeff() - V.colwise().minCoeff()).array();
-
-  // Add per-vertex colors
-  viewer.data().set_colors(C);
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+Eigen::MatrixXd C;
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/screwdriver.off", V, F);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+
+  // Use the (normalized) vertex positions as colors
+  C =
+    (V.rowwise()            - V.colwise().minCoeff()).array().rowwise()/
+    (V.colwise().maxCoeff() - V.colwise().minCoeff()).array();
+
+  // Add per-vertex colors
+  viewer.data().set_colors(C);
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 83 - 83
tutorial/105_Overlays/main.cpp

@@ -1,83 +1,83 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
-#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
-#include <sstream>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
-
-  // Find the bounding box
-  Eigen::Vector3d m = V.colwise().minCoeff();
-  Eigen::Vector3d M = V.colwise().maxCoeff();
-
-  // Corners of the bounding box
-  Eigen::MatrixXd V_box(8,3);
-  V_box <<
-  m(0), m(1), m(2),
-  M(0), m(1), m(2),
-  M(0), M(1), m(2),
-  m(0), M(1), m(2),
-  m(0), m(1), M(2),
-  M(0), m(1), M(2),
-  M(0), M(1), M(2),
-  m(0), M(1), M(2);
-
-  // Edges of the bounding box
-  Eigen::MatrixXi E_box(12,2);
-  E_box <<
-  0, 1,
-  1, 2,
-  2, 3,
-  3, 0,
-  4, 5,
-  5, 6,
-  6, 7,
-  7, 4,
-  0, 4,
-  1, 5,
-  2, 6,
-  7 ,3;
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-
-  // Plot the corners of the bounding box as points
-  viewer.data().add_points(V_box,Eigen::RowVector3d(1,0,0));
-
-  // Plot the edges of the bounding box
-  for (unsigned i=0;i<E_box.rows(); ++i)
-    viewer.data().add_edges
-    (
-      V_box.row(E_box(i,0)),
-      V_box.row(E_box(i,1)),
-      Eigen::RowVector3d(1,0,0)
-    );
-
-  // Plot labels with the coordinates of bounding box vertices
-  std::stringstream l1;
-  l1 << m(0) << ", " << m(1) << ", " << m(2);
-  viewer.data().add_label(m+Eigen::Vector3d(-0.007, 0, 0),l1.str());
-  std::stringstream l2;
-  l2 << M(0) << ", " << M(1) << ", " << M(2);
-  viewer.data().add_label(M+Eigen::Vector3d(0.007, 0, 0),l2.str());
-  // activate label rendering
-  viewer.data().show_custom_labels = true;
-
-  // Rendering of text labels is handled by ImGui, so we need to enable the ImGui
-  // plugin to show text labels.
-  igl::opengl::glfw::imgui::ImGuiPlugin plugin;
-  viewer.plugins.push_back(&plugin);
-  igl::opengl::glfw::imgui::ImGuiMenu menu;
-  plugin.widgets.push_back(&menu);
-  menu.callback_draw_viewer_window = [](){};
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+#include <sstream>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
+
+  // Find the bounding box
+  Eigen::Vector3d m = V.colwise().minCoeff();
+  Eigen::Vector3d M = V.colwise().maxCoeff();
+
+  // Corners of the bounding box
+  Eigen::MatrixXd V_box(8,3);
+  V_box <<
+  m(0), m(1), m(2),
+  M(0), m(1), m(2),
+  M(0), M(1), m(2),
+  m(0), M(1), m(2),
+  m(0), m(1), M(2),
+  M(0), m(1), M(2),
+  M(0), M(1), M(2),
+  m(0), M(1), M(2);
+
+  // Edges of the bounding box
+  Eigen::MatrixXi E_box(12,2);
+  E_box <<
+  0, 1,
+  1, 2,
+  2, 3,
+  3, 0,
+  4, 5,
+  5, 6,
+  6, 7,
+  7, 4,
+  0, 4,
+  1, 5,
+  2, 6,
+  7 ,3;
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+
+  // Plot the corners of the bounding box as points
+  viewer.data().add_points(V_box,Eigen::RowVector3d(1,0,0));
+
+  // Plot the edges of the bounding box
+  for (unsigned i=0;i<E_box.rows(); ++i)
+    viewer.data().add_edges
+    (
+      V_box.row(E_box(i,0)),
+      V_box.row(E_box(i,1)),
+      Eigen::RowVector3d(1,0,0)
+    );
+
+  // Plot labels with the coordinates of bounding box vertices
+  std::stringstream l1;
+  l1 << m(0) << ", " << m(1) << ", " << m(2);
+  viewer.data().add_label(m+Eigen::Vector3d(-0.007, 0, 0),l1.str());
+  std::stringstream l2;
+  l2 << M(0) << ", " << M(1) << ", " << M(2);
+  viewer.data().add_label(M+Eigen::Vector3d(0.007, 0, 0),l2.str());
+  // activate label rendering
+  viewer.data().show_custom_labels = true;
+
+  // Rendering of text labels is handled by ImGui, so we need to enable the ImGui
+  // plugin to show text labels.
+  igl::opengl::glfw::imgui::ImGuiPlugin plugin;
+  viewer.plugins.push_back(&plugin);
+  igl::opengl::glfw::imgui::ImGuiMenu menu;
+  plugin.widgets.push_back(&menu);
+  menu.callback_draw_viewer_window = [](){};
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 105 - 105
tutorial/106_ViewerMenu/main.cpp

@@ -1,105 +1,105 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
-#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
-#include <igl/opengl/glfw/imgui/ImGuiHelpers.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
-
-  // Init the viewer
-  igl::opengl::glfw::Viewer viewer;
-
-  // Attach a menu plugin
-  igl::opengl::glfw::imgui::ImGuiPlugin plugin;
-  viewer.plugins.push_back(&plugin);
-  igl::opengl::glfw::imgui::ImGuiMenu menu;
-  plugin.widgets.push_back(&menu);
-
-  // Customize the menu
-  double doubleVariable = 0.1f; // Shared between two menus
-
-  // Add content to the default menu window
-  menu.callback_draw_viewer_menu = [&]()
-  {
-    // Draw parent menu content
-    menu.draw_viewer_menu();
-
-    // Add new group
-    if (ImGui::CollapsingHeader("New Group", ImGuiTreeNodeFlags_DefaultOpen))
-    {
-      // Expose variable directly ...
-      ImGui::InputDouble("double", &doubleVariable, 0, 0, "%.4f");
-
-      // ... or using a custom callback
-      static bool boolVariable = true;
-      if (ImGui::Checkbox("bool", &boolVariable))
-      {
-        // do something
-        std::cout << "boolVariable: " << std::boolalpha << boolVariable << std::endl;
-      }
-
-      // Expose an enumeration type
-      enum Orientation { Up=0, Down, Left, Right };
-      static Orientation dir = Up;
-      ImGui::Combo("Direction", (int *)(&dir), "Up\0Down\0Left\0Right\0\0");
-
-      // We can also use a std::vector<std::string> defined dynamically
-      static int num_choices = 3;
-      static std::vector<std::string> choices;
-      static int idx_choice = 0;
-      if (ImGui::InputInt("Num letters", &num_choices))
-      {
-        num_choices = std::max(1, std::min(26, num_choices));
-      }
-      if (num_choices != (int) choices.size())
-      {
-        choices.resize(num_choices);
-        for (int i = 0; i < num_choices; ++i)
-          choices[i] = std::string(1, 'A' + i);
-        if (idx_choice >= num_choices)
-          idx_choice = num_choices - 1;
-      }
-      ImGui::Combo("Letter", &idx_choice, choices);
-
-      // Add a button
-      if (ImGui::Button("Print Hello", ImVec2(-1,0)))
-      {
-        std::cout << "Hello\n";
-      }
-    }
-  };
-
-  // Draw additional windows
-  menu.callback_draw_custom_window = [&]()
-  {
-    // Define next window position + size
-    ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10), ImGuiCond_FirstUseEver);
-    ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiCond_FirstUseEver);
-    ImGui::Begin(
-        "New Window", nullptr,
-        ImGuiWindowFlags_NoSavedSettings
-    );
-
-    // Expose the same variable directly ...
-    ImGui::PushItemWidth(-80);
-    ImGui::DragScalar("double", ImGuiDataType_Double, &doubleVariable, 0.1, 0, 0, "%.4f");
-    ImGui::PopItemWidth();
-
-    static std::string str = "bunny";
-    ImGui::InputText("Name", str);
-
-    ImGui::End();
-  };
-
-  // Plot the mesh
-  viewer.data().set_mesh(V, F);
-  viewer.data().add_label(viewer.data().V.row(0) + viewer.data().V_normals.row(0).normalized()*0.005, "Hello World!");
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+#include <igl/opengl/glfw/imgui/ImGuiHelpers.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
+
+  // Init the viewer
+  igl::opengl::glfw::Viewer viewer;
+
+  // Attach a menu plugin
+  igl::opengl::glfw::imgui::ImGuiPlugin plugin;
+  viewer.plugins.push_back(&plugin);
+  igl::opengl::glfw::imgui::ImGuiMenu menu;
+  plugin.widgets.push_back(&menu);
+
+  // Customize the menu
+  double doubleVariable = 0.1f; // Shared between two menus
+
+  // Add content to the default menu window
+  menu.callback_draw_viewer_menu = [&]()
+  {
+    // Draw parent menu content
+    menu.draw_viewer_menu();
+
+    // Add new group
+    if (ImGui::CollapsingHeader("New Group", ImGuiTreeNodeFlags_DefaultOpen))
+    {
+      // Expose variable directly ...
+      ImGui::InputDouble("double", &doubleVariable, 0, 0, "%.4f");
+
+      // ... or using a custom callback
+      static bool boolVariable = true;
+      if (ImGui::Checkbox("bool", &boolVariable))
+      {
+        // do something
+        std::cout << "boolVariable: " << std::boolalpha << boolVariable << std::endl;
+      }
+
+      // Expose an enumeration type
+      enum Orientation { Up=0, Down, Left, Right };
+      static Orientation dir = Up;
+      ImGui::Combo("Direction", (int *)(&dir), "Up\0Down\0Left\0Right\0\0");
+
+      // We can also use a std::vector<std::string> defined dynamically
+      static int num_choices = 3;
+      static std::vector<std::string> choices;
+      static int idx_choice = 0;
+      if (ImGui::InputInt("Num letters", &num_choices))
+      {
+        num_choices = std::max(1, std::min(26, num_choices));
+      }
+      if (num_choices != (int) choices.size())
+      {
+        choices.resize(num_choices);
+        for (int i = 0; i < num_choices; ++i)
+          choices[i] = std::string(1, 'A' + i);
+        if (idx_choice >= num_choices)
+          idx_choice = num_choices - 1;
+      }
+      ImGui::Combo("Letter", &idx_choice, choices);
+
+      // Add a button
+      if (ImGui::Button("Print Hello", ImVec2(-1,0)))
+      {
+        std::cout << "Hello\n";
+      }
+    }
+  };
+
+  // Draw additional windows
+  menu.callback_draw_custom_window = [&]()
+  {
+    // Define next window position + size
+    ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10), ImGuiCond_FirstUseEver);
+    ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiCond_FirstUseEver);
+    ImGui::Begin(
+        "New Window", nullptr,
+        ImGuiWindowFlags_NoSavedSettings
+    );
+
+    // Expose the same variable directly ...
+    ImGui::PushItemWidth(-80);
+    ImGui::DragScalar("double", ImGuiDataType_Double, &doubleVariable, 0.1, 0, 0, "%.4f");
+    ImGui::PopItemWidth();
+
+    static std::string str = "bunny";
+    ImGui::InputText("Name", str);
+
+    ImGui::End();
+  };
+
+  // Plot the mesh
+  viewer.data().set_mesh(V, F);
+  viewer.data().add_label(viewer.data().V.row(0) + viewer.data().V_normals.row(0).normalized()*0.005, "Hello World!");
+  viewer.launch();
+}

+ 54 - 54
tutorial/107_MultipleMeshes/main.cpp

@@ -1,54 +1,54 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <GLFW/glfw3.h>
-#include <string>
-#include <iostream>
-#include <map>
-
-int main(int argc, char * argv[])
-{
-  igl::opengl::glfw::Viewer viewer;
-  const auto names =
-    {"cube.obj","sphere.obj","xcylinder.obj","ycylinder.obj","zcylinder.obj"};
-  std::map<int, Eigen::RowVector3d> colors;
-  int last_selected = -1;
-  for(const auto & name : names)
-  {
-    viewer.load_mesh_from_file(std::string(TUTORIAL_SHARED_PATH) + "/" + name);
-    colors.emplace(viewer.data().id, 0.5*Eigen::RowVector3d::Random().array() + 0.5);
-  }
-
-  viewer.callback_key_down =
-    [&](igl::opengl::glfw::Viewer &, unsigned int key, int mod)
-  {
-    if(key == GLFW_KEY_BACKSPACE)
-    {
-      int old_id = viewer.data().id;
-      if (viewer.erase_mesh(viewer.selected_data_index))
-      {
-        colors.erase(old_id);
-        last_selected = -1;
-      }
-      return true;
-    }
-    return false;
-  };
-
-  // Refresh selected mesh colors
-  viewer.callback_pre_draw =
-    [&](igl::opengl::glfw::Viewer &)
-  {
-    if (last_selected != viewer.selected_data_index)
-    {
-      for (auto &data : viewer.data_list)
-      {
-        data.set_colors(colors[data.id]);
-      }
-      viewer.data_list[viewer.selected_data_index].set_colors(Eigen::RowVector3d(0.9,0.1,0.1));
-      last_selected = viewer.selected_data_index;
-    }
-    return false;
-  };
-
-  viewer.launch();
-  return EXIT_SUCCESS;
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <GLFW/glfw3.h>
+#include <string>
+#include <iostream>
+#include <map>
+
+int main(int argc, char * argv[])
+{
+  igl::opengl::glfw::Viewer viewer;
+  const auto names =
+    {"cube.obj","sphere.obj","xcylinder.obj","ycylinder.obj","zcylinder.obj"};
+  std::map<int, Eigen::RowVector3d> colors;
+  int last_selected = -1;
+  for(const auto & name : names)
+  {
+    viewer.load_mesh_from_file(std::string(TUTORIAL_SHARED_PATH) + "/" + name);
+    colors.emplace(viewer.data().id, 0.5*Eigen::RowVector3d::Random().array() + 0.5);
+  }
+
+  viewer.callback_key_down =
+    [&](igl::opengl::glfw::Viewer &, unsigned int key, int mod)
+  {
+    if(key == GLFW_KEY_BACKSPACE)
+    {
+      int old_id = viewer.data().id;
+      if (viewer.erase_mesh(viewer.selected_data_index))
+      {
+        colors.erase(old_id);
+        last_selected = -1;
+      }
+      return true;
+    }
+    return false;
+  };
+
+  // Refresh selected mesh colors
+  viewer.callback_pre_draw =
+    [&](igl::opengl::glfw::Viewer &)
+  {
+    if (last_selected != viewer.selected_data_index)
+    {
+      for (auto &data : viewer.data_list)
+      {
+        data.set_colors(colors[data.id]);
+      }
+      viewer.data_list[viewer.selected_data_index].set_colors(Eigen::RowVector3d(0.9,0.1,0.1));
+      last_selected = viewer.selected_data_index;
+    }
+    return false;
+  };
+
+  viewer.launch();
+  return EXIT_SUCCESS;
+}

+ 59 - 59
tutorial/109_ImGuizmo/main.cpp

@@ -1,59 +1,59 @@
-#include <igl/read_triangle_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
-#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
-#include <igl/opengl/glfw/imgui/ImGuizmoWidget.h>
-#include <GLFW/glfw3.h>
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh from file
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  igl::read_triangle_mesh(argc>1?argv[1]: TUTORIAL_SHARED_PATH "/cow.off",V,F);
-  // Set up viewer
-  igl::opengl::glfw::Viewer vr;
-  vr.data().set_mesh(V,F);
- 
-  igl::opengl::glfw::imgui::ImGuiPlugin imgui_plugin;
-  vr.plugins.push_back(&imgui_plugin);
-
-  // Add a 3D gizmo plugin
-  igl::opengl::glfw::imgui::ImGuizmoWidget gizmo;
-  imgui_plugin.widgets.push_back(&gizmo);
-  // Initialize ImGuizmo at mesh centroid
-  gizmo.T.block(0,3,3,1) =
-    0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff()).transpose().cast<float>();
-  // Update can be applied relative to this remembered initial transform
-  const Eigen::Matrix4f T0 = gizmo.T;
-  // Attach callback to apply imguizmo's transform to mesh
-  gizmo.callback = [&](const Eigen::Matrix4f & T)
-  {
-    const Eigen::Matrix4d TT = (T*T0.inverse()).cast<double>().transpose();
-    vr.data().set_vertices(
-      (V.rowwise().homogeneous()*TT).rowwise().hnormalized());
-    vr.data().compute_normals();
-  };
-  // Maya-style keyboard shortcuts for operation
-  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
-  {
-    switch(key)
-    {
-      case ' ': gizmo.visible = !gizmo.visible; return true;
-      case 'W': case 'w': gizmo.operation = ImGuizmo::TRANSLATE; return true;
-      case 'E': case 'e': gizmo.operation = ImGuizmo::ROTATE;    return true;
-      case 'R': case 'r': gizmo.operation = ImGuizmo::SCALE;     return true;
-    }
-    return false;
-  };
-
-  igl::opengl::glfw::imgui::ImGuiMenu menu;
-  imgui_plugin.widgets.push_back(&menu);
-
-  std::cout<<R"(
-W,w  Switch to translate operation
-E,e  Switch to rotate operation
-R,r  Switch to scale operation
-)";
-  vr.launch();
-}
+#include <igl/read_triangle_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+#include <igl/opengl/glfw/imgui/ImGuizmoWidget.h>
+#include <GLFW/glfw3.h>
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh from file
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  igl::read_triangle_mesh(argc>1?argv[1]: TUTORIAL_SHARED_PATH "/cow.off",V,F);
+  // Set up viewer
+  igl::opengl::glfw::Viewer vr;
+  vr.data().set_mesh(V,F);
+ 
+  igl::opengl::glfw::imgui::ImGuiPlugin imgui_plugin;
+  vr.plugins.push_back(&imgui_plugin);
+
+  // Add a 3D gizmo plugin
+  igl::opengl::glfw::imgui::ImGuizmoWidget gizmo;
+  imgui_plugin.widgets.push_back(&gizmo);
+  // Initialize ImGuizmo at mesh centroid
+  gizmo.T.block(0,3,3,1) =
+    0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff()).transpose().cast<float>();
+  // Update can be applied relative to this remembered initial transform
+  const Eigen::Matrix4f T0 = gizmo.T;
+  // Attach callback to apply imguizmo's transform to mesh
+  gizmo.callback = [&](const Eigen::Matrix4f & T)
+  {
+    const Eigen::Matrix4d TT = (T*T0.inverse()).cast<double>().transpose();
+    vr.data().set_vertices(
+      (V.rowwise().homogeneous()*TT).rowwise().hnormalized());
+    vr.data().compute_normals();
+  };
+  // Maya-style keyboard shortcuts for operation
+  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
+  {
+    switch(key)
+    {
+      case ' ': gizmo.visible = !gizmo.visible; return true;
+      case 'W': case 'w': gizmo.operation = ImGuizmo::TRANSLATE; return true;
+      case 'E': case 'e': gizmo.operation = ImGuizmo::ROTATE;    return true;
+      case 'R': case 'r': gizmo.operation = ImGuizmo::SCALE;     return true;
+    }
+    return false;
+  };
+
+  igl::opengl::glfw::imgui::ImGuiMenu menu;
+  imgui_plugin.widgets.push_back(&menu);
+
+  std::cout<<R"(
+W,w  Switch to translate operation
+E,e  Switch to rotate operation
+R,r  Switch to scale operation
+)";
+  vr.launch();
+}

+ 111 - 111
tutorial/110_MshView/main.cpp

@@ -1,111 +1,111 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/barycenter.h>
-#include <igl/colormap.h>
-
-#include <igl/readMSH.h>
-#include <igl/readMESH.h>
-
-
-Eigen::MatrixXd X,B;
-Eigen::MatrixXi Tri;
-Eigen::MatrixXi Tet;
-Eigen::VectorXi TriTag;
-Eigen::VectorXi TetTag;
-
-Eigen::VectorXd D;
-
-std::vector<std::string> XFields;
-std::vector<std::string> EFields;
-
-std::vector<Eigen::MatrixXd> XF;
-std::vector<Eigen::MatrixXd> TriF;
-std::vector<Eigen::MatrixXd> TetF;
-
-
-// This function is called every time a keyboard button is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace std;
-  using namespace Eigen;
-
-  if (key >= '1' && key <= '9')
-  {
-    double t = double((key - '1')+1) / 9.0;
-
-    VectorXd v = B.col(2).array() - B.col(2).minCoeff();
-    v /= v.col(0).maxCoeff();
-
-    vector<int> s;
-
-    for (unsigned i=0; i<v.size();++i)
-      if (v(i) < t && v(i)>(t-0.1)) // select a thick slab
-        s.push_back(i);
-
-    MatrixXd V_temp(s.size()*4,3);
-    MatrixXi F_temp(s.size()*4,3);
-    VectorXd D_temp(s.size()*4);
-
-    for (unsigned i=0; i<s.size();++i)
-    {
-      V_temp.row(i*4+0) = X.row(Tet(s[i],0));
-      V_temp.row(i*4+1) = X.row(Tet(s[i],1));
-      V_temp.row(i*4+2) = X.row(Tet(s[i],2));
-      V_temp.row(i*4+3) = X.row(Tet(s[i],3));
-
-      F_temp.row(i*4+0) << (i*4)+0, (i*4)+1, (i*4)+3;
-      F_temp.row(i*4+1) << (i*4)+0, (i*4)+2, (i*4)+1;
-      F_temp.row(i*4+2) << (i*4)+3, (i*4)+2, (i*4)+0;
-      F_temp.row(i*4+3) << (i*4)+1, (i*4)+2, (i*4)+3;
-
-      D_temp(i*4+0) = D(s[i]);
-      D_temp(i*4+1) = D(s[i]);
-      D_temp(i*4+2) = D(s[i]);
-      D_temp(i*4+3) = D(s[i]);
-    }
-
-    viewer.data().clear();
-    viewer.data().set_mesh(V_temp, F_temp);
-
-    Eigen::MatrixXd C;
-    igl::colormap(igl::COLOR_MAP_TYPE_VIRIDIS, D_temp, true, C);
-    viewer.data().set_face_based(true);
-    viewer.data().set_colors(C);
-
-  }
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  igl::readMSH(argc > 1 ? argv[1] : TUTORIAL_SHARED_PATH "/hand.msh", X, Tri, Tet, TriTag, TetTag, XFields, XF, EFields, TriF, TetF);
-
-  for(auto i:EFields)
-    std::cout<<i<<"\t";
-  std::cout<<std::endl;
-
-  // search for a predefined field name "E"
-  for(int i=0;i<EFields.size();++i)
-  {
-    if(EFields[i]=="E")
-      D = TetF[i].rowwise().norm(); // take a row-wise norm
-  }
-  std::cout<<"D:"<<D.rows()<<"x"<<D.cols()<<std::endl;
-
-  // generate fake data
-  if(D.rows()==0)
-    D = TetTag.cast<double>();
-
-  // Compute barycenters
-  igl::barycenter(X, Tet, B);
-
-  // Plot the generated mesh
-  igl::opengl::glfw::Viewer viewer;
-
-  viewer.callback_key_down = &key_down;
-  key_down(viewer,'5',0);
-  viewer.launch();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/barycenter.h>
+#include <igl/colormap.h>
+
+#include <igl/readMSH.h>
+#include <igl/readMESH.h>
+
+
+Eigen::MatrixXd X,B;
+Eigen::MatrixXi Tri;
+Eigen::MatrixXi Tet;
+Eigen::VectorXi TriTag;
+Eigen::VectorXi TetTag;
+
+Eigen::VectorXd D;
+
+std::vector<std::string> XFields;
+std::vector<std::string> EFields;
+
+std::vector<Eigen::MatrixXd> XF;
+std::vector<Eigen::MatrixXd> TriF;
+std::vector<Eigen::MatrixXd> TetF;
+
+
+// This function is called every time a keyboard button is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace std;
+  using namespace Eigen;
+
+  if (key >= '1' && key <= '9')
+  {
+    double t = double((key - '1')+1) / 9.0;
+
+    VectorXd v = B.col(2).array() - B.col(2).minCoeff();
+    v /= v.col(0).maxCoeff();
+
+    vector<int> s;
+
+    for (unsigned i=0; i<v.size();++i)
+      if (v(i) < t && v(i)>(t-0.1)) // select a thick slab
+        s.push_back(i);
+
+    MatrixXd V_temp(s.size()*4,3);
+    MatrixXi F_temp(s.size()*4,3);
+    VectorXd D_temp(s.size()*4);
+
+    for (unsigned i=0; i<s.size();++i)
+    {
+      V_temp.row(i*4+0) = X.row(Tet(s[i],0));
+      V_temp.row(i*4+1) = X.row(Tet(s[i],1));
+      V_temp.row(i*4+2) = X.row(Tet(s[i],2));
+      V_temp.row(i*4+3) = X.row(Tet(s[i],3));
+
+      F_temp.row(i*4+0) << (i*4)+0, (i*4)+1, (i*4)+3;
+      F_temp.row(i*4+1) << (i*4)+0, (i*4)+2, (i*4)+1;
+      F_temp.row(i*4+2) << (i*4)+3, (i*4)+2, (i*4)+0;
+      F_temp.row(i*4+3) << (i*4)+1, (i*4)+2, (i*4)+3;
+
+      D_temp(i*4+0) = D(s[i]);
+      D_temp(i*4+1) = D(s[i]);
+      D_temp(i*4+2) = D(s[i]);
+      D_temp(i*4+3) = D(s[i]);
+    }
+
+    viewer.data().clear();
+    viewer.data().set_mesh(V_temp, F_temp);
+
+    Eigen::MatrixXd C;
+    igl::colormap(igl::COLOR_MAP_TYPE_VIRIDIS, D_temp, true, C);
+    viewer.data().set_face_based(true);
+    viewer.data().set_colors(C);
+
+  }
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  igl::readMSH(argc > 1 ? argv[1] : TUTORIAL_SHARED_PATH "/hand.msh", X, Tri, Tet, TriTag, TetTag, XFields, XF, EFields, TriF, TetF);
+
+  for(auto i:EFields)
+    std::cout<<i<<"\t";
+  std::cout<<std::endl;
+
+  // search for a predefined field name "E"
+  for(int i=0;i<EFields.size();++i)
+  {
+    if(EFields[i]=="E")
+      D = TetF[i].rowwise().norm(); // take a row-wise norm
+  }
+  std::cout<<"D:"<<D.rows()<<"x"<<D.cols()<<std::endl;
+
+  // generate fake data
+  if(D.rows()==0)
+    D = TetTag.cast<double>();
+
+  // Compute barycenters
+  igl::barycenter(X, Tet, B);
+
+  // Plot the generated mesh
+  igl::opengl::glfw::Viewer viewer;
+
+  viewer.callback_key_down = &key_down;
+  key_down(viewer,'5',0);
+  viewer.launch();
+}

+ 19 - 19
tutorial/111_MatCap/main.cpp

@@ -1,19 +1,19 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/read_triangle_mesh.h>
-#include <igl/stb/read_image.h>
-
-int main(int argc, char *argv[])
-{
-  igl::opengl::glfw::Viewer v;
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  igl::read_triangle_mesh(
-    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
-  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> R,G,B,A;
-  igl::stb::read_image(argc>2?argv[2]: TUTORIAL_SHARED_PATH "/jade.png",R,G,B,A);
-  v.data().set_mesh(V,F);
-  v.data().set_texture(R,G,B,A);
-  v.data().use_matcap = true;
-  v.data().show_lines = false;
-  v.launch();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/read_triangle_mesh.h>
+#include <igl/stb/read_image.h>
+
+int main(int argc, char *argv[])
+{
+  igl::opengl::glfw::Viewer v;
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  igl::read_triangle_mesh(
+    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
+  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> R,G,B,A;
+  igl::stb::read_image(argc>2?argv[2]: TUTORIAL_SHARED_PATH "/jade.png",R,G,B,A);
+  v.data().set_mesh(V,F);
+  v.data().set_texture(R,G,B,A);
+  v.data().use_matcap = true;
+  v.data().show_lines = false;
+  v.launch();
+}

+ 70 - 70
tutorial/112_Selection/main.cpp

@@ -1,70 +1,70 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/read_triangle_mesh.h>
-#include <igl/list_to_matrix.h>
-#include <igl/matlab_format.h>
-#include <igl/AABB.h>
-#include <igl/screen_space_selection.h>
-
-#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
-#include <igl/opengl/glfw/imgui/SelectionWidget.h>
-
-int main(int argc, char *argv[])
-{
-  // Inline mesh of a cube
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  igl::read_triangle_mesh(
-    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer vr;
-  igl::opengl::glfw::imgui::ImGuiPlugin imgui_plugin;
-  vr.plugins.push_back(&imgui_plugin);
-  igl::opengl::glfw::imgui::SelectionWidget widget;
-  imgui_plugin.widgets.push_back(&widget);
-
-  Eigen::VectorXd W = Eigen::VectorXd::Zero(V.rows());
-  Eigen::Array<double,Eigen::Dynamic,1> and_visible = 
-    Eigen::Array<double,Eigen::Dynamic,1>::Zero(V.rows());
-  const Eigen::MatrixXd CM = (Eigen::MatrixXd(2,3)<<
-      0.3,0.3,0.5,                 
-      255.0/255.0,228.0/255.0,58.0/255.0).finished();
-  bool only_visible = false;
-  const auto update = [&]()
-  {
-    const bool was_face_based = vr.data().face_based;
-    Eigen::VectorXd S = W;
-    if(only_visible) { S.array() *= and_visible; }
-    vr.data().set_data(S,0,1,igl::COLOR_MAP_TYPE_PLASMA,2);
-    vr.data().face_based = was_face_based;
-    vr.data().set_colormap(CM);
-  };
-  igl::AABB<Eigen::MatrixXd, 3> tree;
-  tree.init(V,F);
-  widget.callback = [&]()
-  {
-    screen_space_selection(V,F,tree,vr.core().view,vr.core().proj,vr.core().viewport,widget.L,W,and_visible);
-    update();
-  };
-  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
-  {
-    switch(key)
-    {
-      case ' ': only_visible = !only_visible; update(); return true;
-      case 'D': case 'd': W.setZero(); update(); return true;
-    }
-    return false;
-  };
-  std::cout<<R"(
-Usage:
-  [space]  Toggle whether to take visibility into account
-  D,d      Clear selection
-)";
-  vr.data().set_mesh(V,F);
-  vr.data().set_face_based(true);
-  vr.core().background_color.head(3) = CM.row(0).head(3).cast<float>();
-  vr.data().line_color.head(3) = (CM.row(0).head(3)*0.5).cast<float>();
-  vr.data().show_lines = F.rows() < 20000;
-  update();
-  vr.launch();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/read_triangle_mesh.h>
+#include <igl/list_to_matrix.h>
+#include <igl/matlab_format.h>
+#include <igl/AABB.h>
+#include <igl/screen_space_selection.h>
+
+#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
+#include <igl/opengl/glfw/imgui/SelectionWidget.h>
+
+int main(int argc, char *argv[])
+{
+  // Inline mesh of a cube
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  igl::read_triangle_mesh(
+    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer vr;
+  igl::opengl::glfw::imgui::ImGuiPlugin imgui_plugin;
+  vr.plugins.push_back(&imgui_plugin);
+  igl::opengl::glfw::imgui::SelectionWidget widget;
+  imgui_plugin.widgets.push_back(&widget);
+
+  Eigen::VectorXd W = Eigen::VectorXd::Zero(V.rows());
+  Eigen::Array<double,Eigen::Dynamic,1> and_visible = 
+    Eigen::Array<double,Eigen::Dynamic,1>::Zero(V.rows());
+  const Eigen::MatrixXd CM = (Eigen::MatrixXd(2,3)<<
+      0.3,0.3,0.5,                 
+      255.0/255.0,228.0/255.0,58.0/255.0).finished();
+  bool only_visible = false;
+  const auto update = [&]()
+  {
+    const bool was_face_based = vr.data().face_based;
+    Eigen::VectorXd S = W;
+    if(only_visible) { S.array() *= and_visible; }
+    vr.data().set_data(S,0,1,igl::COLOR_MAP_TYPE_PLASMA,2);
+    vr.data().face_based = was_face_based;
+    vr.data().set_colormap(CM);
+  };
+  igl::AABB<Eigen::MatrixXd, 3> tree;
+  tree.init(V,F);
+  widget.callback = [&]()
+  {
+    screen_space_selection(V,F,tree,vr.core().view,vr.core().proj,vr.core().viewport,widget.L,W,and_visible);
+    update();
+  };
+  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
+  {
+    switch(key)
+    {
+      case ' ': only_visible = !only_visible; update(); return true;
+      case 'D': case 'd': W.setZero(); update(); return true;
+    }
+    return false;
+  };
+  std::cout<<R"(
+Usage:
+  [space]  Toggle whether to take visibility into account
+  D,d      Clear selection
+)";
+  vr.data().set_mesh(V,F);
+  vr.data().set_face_based(true);
+  vr.core().background_color.head(3) = CM.row(0).head(3).cast<float>();
+  vr.data().line_color.head(3) = (CM.row(0).head(3)*0.5).cast<float>();
+  vr.data().show_lines = F.rows() < 20000;
+  update();
+  vr.launch();
+}

+ 61 - 61
tutorial/201_Normals/main.cpp

@@ -1,61 +1,61 @@
-#include <igl/read_triangle_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/per_face_normals.h>
-#include <igl/per_corner_normals.h>
-#include <iostream>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-Eigen::MatrixXd N_vertices;
-Eigen::MatrixXd N_faces;
-Eigen::MatrixXd N_corners;
-
-
-// This function is called every time a keyboard button is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  switch(key)
-  {
-    case '1':
-      viewer.data().set_normals(N_faces);
-      return true;
-    case '2':
-      viewer.data().set_normals(N_vertices);
-      return true;
-    case '3':
-      viewer.data().set_normals(N_corners);
-      return true;
-    default: break;
-  }
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::read_triangle_mesh(
-    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/fandisk.off",V,F);
-
-  // Compute per-face normals
-  igl::per_face_normals(V,F,N_faces);
-
-  // Compute per-vertex normals
-  igl::per_vertex_normals(V,F,N_vertices);
-
-  // Compute per-corner normals, |dihedral angle| > 20 degrees --> crease
-  igl::per_corner_normals(V,F,20,N_corners);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.callback_key_down = &key_down;
-  viewer.data().show_lines = false;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_normals(N_faces);
-  std::cout<<
-    "Press '1' for per-face normals."<<std::endl<<
-    "Press '2' for per-vertex normals."<<std::endl<<
-    "Press '3' for per-corner normals."<<std::endl;
-  viewer.launch();
-}
+#include <igl/read_triangle_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/per_face_normals.h>
+#include <igl/per_corner_normals.h>
+#include <iostream>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+Eigen::MatrixXd N_vertices;
+Eigen::MatrixXd N_faces;
+Eigen::MatrixXd N_corners;
+
+
+// This function is called every time a keyboard button is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  switch(key)
+  {
+    case '1':
+      viewer.data().set_normals(N_faces);
+      return true;
+    case '2':
+      viewer.data().set_normals(N_vertices);
+      return true;
+    case '3':
+      viewer.data().set_normals(N_corners);
+      return true;
+    default: break;
+  }
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::read_triangle_mesh(
+    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/fandisk.off",V,F);
+
+  // Compute per-face normals
+  igl::per_face_normals(V,F,N_faces);
+
+  // Compute per-vertex normals
+  igl::per_vertex_normals(V,F,N_vertices);
+
+  // Compute per-corner normals, |dihedral angle| > 20 degrees --> crease
+  igl::per_corner_normals(V,F,20,N_corners);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.callback_key_down = &key_down;
+  viewer.data().show_lines = false;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_normals(N_faces);
+  std::cout<<
+    "Press '1' for per-face normals."<<std::endl<<
+    "Press '2' for per-vertex normals."<<std::endl<<
+    "Press '3' for per-corner normals."<<std::endl;
+  viewer.launch();
+}

+ 30 - 30
tutorial/202_GaussianCurvature/main.cpp

@@ -1,30 +1,30 @@
-#include <igl/gaussian_curvature.h>
-#include <igl/massmatrix.h>
-#include <igl/invert_diag.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off",V,F);
-
-  VectorXd K;
-  // Compute integral of Gaussian curvature
-  igl::gaussian_curvature(V,F,K);
-  // Compute mass matrix
-  SparseMatrix<double> M,Minv;
-  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,M);
-  igl::invert_diag(M,Minv);
-  // Divide by area to get integral average
-  K = (Minv*K).eval();
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_data(K);
-  viewer.launch();
-}
+#include <igl/gaussian_curvature.h>
+#include <igl/massmatrix.h>
+#include <igl/invert_diag.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off",V,F);
+
+  VectorXd K;
+  // Compute integral of Gaussian curvature
+  igl::gaussian_curvature(V,F,K);
+  // Compute mass matrix
+  SparseMatrix<double> M,Minv;
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,M);
+  igl::invert_diag(M,Minv);
+  // Divide by area to get integral average
+  K = (Minv*K).eval();
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_data(K);
+  viewer.launch();
+}

+ 64 - 64
tutorial/203_CurvatureDirections/main.cpp

@@ -1,64 +1,64 @@
-#include <igl/avg_edge_length.h>
-#include <igl/cotmatrix.h>
-#include <igl/invert_diag.h>
-#include <igl/massmatrix.h>
-#include <igl/parula.h>
-#include <igl/per_corner_normals.h>
-#include <igl/per_face_normals.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/principal_curvature.h>
-#include <igl/read_triangle_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  std::string filename = TUTORIAL_SHARED_PATH "/fertility.off";
-  if(argc>1)
-  {
-    filename = argv[1];
-  }
-  // Load a mesh in OFF format
-  igl::read_triangle_mesh(filename, V, F);
-
-  // Alternative discrete mean curvature
-  MatrixXd HN;
-  SparseMatrix<double> L,M,Minv;
-  igl::cotmatrix(V,F,L);
-  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
-  igl::invert_diag(M,Minv);
-  // Laplace-Beltrami of position
-  HN = -Minv*(L*V);
-  // Extract magnitude as mean curvature
-  VectorXd H = HN.rowwise().norm();
-
-  // Compute curvature directions via quadric fitting
-  MatrixXd PD1,PD2;
-  VectorXd PV1,PV2;
-  igl::principal_curvature(V,F,PD1,PD2,PV1,PV2);
-  // mean curvature
-  H = 0.5*(PV1+PV2);
-
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-
-  viewer.data().set_data(H);
-
-  // Average edge length for sizing
-  const double avg = igl::avg_edge_length(V,F);
-
-  // Draw a red segment parallel to the maximal curvature direction
-  const RowVector3d red(0.8,0.2,0.2),blue(0.2,0.2,0.8);
-  viewer.data().add_edges(V + PD1*avg, V - PD1*avg, red);
-
-  // Draw a blue segment parallel to the minimal curvature direction
-  viewer.data().add_edges(V + PD2*avg, V - PD2*avg, blue);
-
-  // Hide wireframe
-  viewer.data().show_lines = false;
-
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/cotmatrix.h>
+#include <igl/invert_diag.h>
+#include <igl/massmatrix.h>
+#include <igl/parula.h>
+#include <igl/per_corner_normals.h>
+#include <igl/per_face_normals.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/principal_curvature.h>
+#include <igl/read_triangle_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  std::string filename = TUTORIAL_SHARED_PATH "/fertility.off";
+  if(argc>1)
+  {
+    filename = argv[1];
+  }
+  // Load a mesh in OFF format
+  igl::read_triangle_mesh(filename, V, F);
+
+  // Alternative discrete mean curvature
+  MatrixXd HN;
+  SparseMatrix<double> L,M,Minv;
+  igl::cotmatrix(V,F,L);
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
+  igl::invert_diag(M,Minv);
+  // Laplace-Beltrami of position
+  HN = -Minv*(L*V);
+  // Extract magnitude as mean curvature
+  VectorXd H = HN.rowwise().norm();
+
+  // Compute curvature directions via quadric fitting
+  MatrixXd PD1,PD2;
+  VectorXd PV1,PV2;
+  igl::principal_curvature(V,F,PD1,PD2,PV1,PV2);
+  // mean curvature
+  H = 0.5*(PV1+PV2);
+
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+
+  viewer.data().set_data(H);
+
+  // Average edge length for sizing
+  const double avg = igl::avg_edge_length(V,F);
+
+  // Draw a red segment parallel to the maximal curvature direction
+  const RowVector3d red(0.8,0.2,0.2),blue(0.2,0.2,0.8);
+  viewer.data().add_edges(V + PD1*avg, V - PD1*avg, red);
+
+  // Draw a blue segment parallel to the minimal curvature direction
+  viewer.data().add_edges(V + PD2*avg, V - PD2*avg, blue);
+
+  // Hide wireframe
+  viewer.data().show_lines = false;
+
+  viewer.launch();
+}

+ 51 - 51
tutorial/204_Gradient/main.cpp

@@ -1,51 +1,51 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/grad.h>
-#include <igl/jet.h>
-#include <igl/readDMAT.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off", V, F);
-
-  // Read scalar function values from a file, U: #V by 1
-  VectorXd U;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/cheburashka-scalar.dmat",U);
-
-  // Compute gradient operator: #F*3 by #V
-  SparseMatrix<double> G;
-  igl::grad(V,F,G);
-
-  // Compute gradient of U
-  MatrixXd GU = Map<const MatrixXd>((G*U).eval().data(),F.rows(),3);
-  // Compute gradient magnitude
-  const VectorXd GU_mag = GU.rowwise().norm();
-
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-
-  viewer.data().set_data(U);
-
-  // Average edge length divided by average gradient (for scaling)
-  const double max_size = igl::avg_edge_length(V,F) / GU_mag.mean();
-  // Draw a black segment in direction of gradient at face barycenters
-  MatrixXd BC;
-  igl::barycenter(V,F,BC);
-  const RowVector3d black(0,0,0);
-  viewer.data().add_edges(BC,BC+max_size*GU, black);
-
-  // Hide wireframe
-  viewer.data().show_lines = false;
-
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/grad.h>
+#include <igl/jet.h>
+#include <igl/readDMAT.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off", V, F);
+
+  // Read scalar function values from a file, U: #V by 1
+  VectorXd U;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/cheburashka-scalar.dmat",U);
+
+  // Compute gradient operator: #F*3 by #V
+  SparseMatrix<double> G;
+  igl::grad(V,F,G);
+
+  // Compute gradient of U
+  MatrixXd GU = Map<const MatrixXd>((G*U).eval().data(),F.rows(),3);
+  // Compute gradient magnitude
+  const VectorXd GU_mag = GU.rowwise().norm();
+
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+
+  viewer.data().set_data(U);
+
+  // Average edge length divided by average gradient (for scaling)
+  const double max_size = igl::avg_edge_length(V,F) / GU_mag.mean();
+  // Draw a black segment in direction of gradient at face barycenters
+  MatrixXd BC;
+  igl::barycenter(V,F,BC);
+  const RowVector3d black(0,0,0);
+  viewer.data().add_edges(BC,BC+max_size*GU, black);
+
+  // Hide wireframe
+  viewer.data().show_lines = false;
+
+  viewer.launch();
+}

+ 104 - 104
tutorial/205_Laplacian/main.cpp

@@ -1,104 +1,104 @@
-#include <igl/barycenter.h>
-#include <igl/cotmatrix.h>
-#include <igl/doublearea.h>
-#include <igl/grad.h>
-#include <igl/jet.h>
-#include <igl/massmatrix.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/readDMAT.h>
-#include <igl/readOFF.h>
-#include <igl/repdiag.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <iostream>
-
-Eigen::MatrixXd V,U;
-Eigen::MatrixXi F;
-Eigen::SparseMatrix<double> L;
-igl::opengl::glfw::Viewer viewer;
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off", V, F);
-
-  // Compute Laplace-Beltrami operator: #V by #V
-  igl::cotmatrix(V,F,L);
-
-  // Alternative construction of same Laplacian
-  SparseMatrix<double> G,K;
-  // Gradient/Divergence
-  igl::grad(V,F,G);
-  // Diagonal per-triangle "mass matrix"
-  VectorXd dblA;
-  igl::doublearea(V,F,dblA);
-  // Place areas along diagonal #dim times
-  const auto & T = 1.*(dblA.replicate(3,1)*0.5).asDiagonal();
-  // Laplacian K built as discrete divergence of gradient or equivalently
-  // discrete Dirichelet energy Hessian
-  K = -G.transpose() * T * G;
-  cout<<"|K-L|: "<<(K-L).norm()<<endl;
-
-  const auto &key_down = [](igl::opengl::glfw::Viewer &viewer,unsigned char key,int mod)->bool
-  {
-    switch(key)
-    {
-      case 'r':
-      case 'R':
-        U = V;
-        break;
-      case ' ':
-      {
-        // Recompute just mass matrix on each step
-        SparseMatrix<double> M;
-        igl::massmatrix(U,F,igl::MASSMATRIX_TYPE_BARYCENTRIC,M);
-        // Solve (M-delta*L) U = M*U
-        const auto & S = (M - 0.001*L);
-        Eigen::SimplicialLLT<Eigen::SparseMatrix<double > > solver(S);
-        assert(solver.info() == Eigen::Success);
-        U = solver.solve(M*U).eval();
-        // Compute centroid and subtract (also important for numerics)
-        VectorXd dblA;
-        igl::doublearea(U,F,dblA);
-        double area = 0.5*dblA.sum();
-        MatrixXd BC;
-        igl::barycenter(U,F,BC);
-        RowVector3d centroid(0,0,0);
-        for(int i = 0;i<BC.rows();i++)
-        {
-          centroid += 0.5*dblA(i)/area*BC.row(i);
-        }
-        U.rowwise() -= centroid;
-        // Normalize to unit surface area (important for numerics)
-        U.array() /= sqrt(area);
-        break;
-      }
-      default:
-        return false;
-    }
-    // Send new positions, update normals, recenter
-    viewer.data().set_vertices(U);
-    viewer.data().compute_normals();
-    viewer.core().align_camera_center(U,F);
-    return true;
-  };
-
-
-  // Use original normals as pseudo-colors
-  MatrixXd N;
-  igl::per_vertex_normals(V,F,N);
-  MatrixXd C = N.rowwise().normalized().array()*0.5+0.5;
-
-  // Initialize smoothing with base mesh
-  U = V;
-  viewer.data().set_mesh(U, F);
-  viewer.data().set_colors(C);
-  viewer.callback_key_down = key_down;
-
-  cout<<"Press [space] to smooth."<<endl;;
-  cout<<"Press [r] to reset."<<endl;;
-  return viewer.launch();
-}
+#include <igl/barycenter.h>
+#include <igl/cotmatrix.h>
+#include <igl/doublearea.h>
+#include <igl/grad.h>
+#include <igl/jet.h>
+#include <igl/massmatrix.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/readDMAT.h>
+#include <igl/readOFF.h>
+#include <igl/repdiag.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <iostream>
+
+Eigen::MatrixXd V,U;
+Eigen::MatrixXi F;
+Eigen::SparseMatrix<double> L;
+igl::opengl::glfw::Viewer viewer;
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off", V, F);
+
+  // Compute Laplace-Beltrami operator: #V by #V
+  igl::cotmatrix(V,F,L);
+
+  // Alternative construction of same Laplacian
+  SparseMatrix<double> G,K;
+  // Gradient/Divergence
+  igl::grad(V,F,G);
+  // Diagonal per-triangle "mass matrix"
+  VectorXd dblA;
+  igl::doublearea(V,F,dblA);
+  // Place areas along diagonal #dim times
+  const auto & T = 1.*(dblA.replicate(3,1)*0.5).asDiagonal();
+  // Laplacian K built as discrete divergence of gradient or equivalently
+  // discrete Dirichelet energy Hessian
+  K = -G.transpose() * T * G;
+  cout<<"|K-L|: "<<(K-L).norm()<<endl;
+
+  const auto &key_down = [](igl::opengl::glfw::Viewer &viewer,unsigned char key,int mod)->bool
+  {
+    switch(key)
+    {
+      case 'r':
+      case 'R':
+        U = V;
+        break;
+      case ' ':
+      {
+        // Recompute just mass matrix on each step
+        SparseMatrix<double> M;
+        igl::massmatrix(U,F,igl::MASSMATRIX_TYPE_BARYCENTRIC,M);
+        // Solve (M-delta*L) U = M*U
+        const auto & S = (M - 0.001*L);
+        Eigen::SimplicialLLT<Eigen::SparseMatrix<double > > solver(S);
+        assert(solver.info() == Eigen::Success);
+        U = solver.solve(M*U).eval();
+        // Compute centroid and subtract (also important for numerics)
+        VectorXd dblA;
+        igl::doublearea(U,F,dblA);
+        double area = 0.5*dblA.sum();
+        MatrixXd BC;
+        igl::barycenter(U,F,BC);
+        RowVector3d centroid(0,0,0);
+        for(int i = 0;i<BC.rows();i++)
+        {
+          centroid += 0.5*dblA(i)/area*BC.row(i);
+        }
+        U.rowwise() -= centroid;
+        // Normalize to unit surface area (important for numerics)
+        U.array() /= sqrt(area);
+        break;
+      }
+      default:
+        return false;
+    }
+    // Send new positions, update normals, recenter
+    viewer.data().set_vertices(U);
+    viewer.data().compute_normals();
+    viewer.core().align_camera_center(U,F);
+    return true;
+  };
+
+
+  // Use original normals as pseudo-colors
+  MatrixXd N;
+  igl::per_vertex_normals(V,F,N);
+  MatrixXd C = N.rowwise().normalized().array()*0.5+0.5;
+
+  // Initialize smoothing with base mesh
+  U = V;
+  viewer.data().set_mesh(U, F);
+  viewer.data().set_colors(C);
+  viewer.callback_key_down = key_down;
+
+  cout<<"Press [space] to smooth."<<endl;;
+  cout<<"Press [r] to reset."<<endl;;
+  return viewer.launch();
+}

+ 73 - 73
tutorial/206_GeodesicDistance/main.cpp

@@ -1,73 +1,73 @@
-#include <igl/readOBJ.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/exact_geodesic.h>
-#include <igl/unproject_onto_mesh.h>
-#include <igl/parula.h>
-#include <igl/isolines_map.h>
-#include <igl/PI.h>
-#include <iostream>
-
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  igl::opengl::glfw::Viewer viewer;
-  // Load a mesh in OFF format
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/armadillo.obj", V, F);
-
-  const auto update_distance = [&](const int vid)
-  {
-    Eigen::VectorXi VS,FS,VT,FT;
-    // The selected vertex is the source
-    VS.resize(1);
-    VS << vid;
-    // All vertices are the targets
-    VT.setLinSpaced(V.rows(),0,V.rows()-1);
-    Eigen::VectorXd d;
-    std::cout<<"Computing geodesic distance to vertex "<<vid<<"..."<<std::endl;
-    igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
-    // Plot the mesh
-    Eigen::MatrixXd CM;
-    igl::parula(Eigen::VectorXd::LinSpaced(21,0,1).eval(),false,CM);
-    igl::isolines_map(Eigen::MatrixXd(CM),CM);
-    viewer.data().set_colormap(CM);
-    viewer.data().set_data(d);
-  };
-
-  // Plot a distance when a vertex is picked
-  viewer.callback_mouse_down =
-  [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
-  {
-    int fid;
-    Eigen::Vector3f bc;
-    // Cast a ray in the view direction starting from the mouse position
-    double x = viewer.current_mouse_x;
-    double y = viewer.core().viewport(3) - viewer.current_mouse_y;
-    if(igl::unproject_onto_mesh(
-      Eigen::Vector2f(x,y),
-      viewer.core().view,
-      viewer.core().proj,
-      viewer.core().viewport,
-      V,
-      F,
-      fid,
-      bc))
-    {
-      int max;
-      bc.maxCoeff(&max);
-      int vid = F(fid,max);
-      update_distance(vid);
-      return true;
-    }
-    return false;
-  };
-  viewer.data().set_mesh(V,F);
-  viewer.data().show_lines = false;
-
-  cout << "Click on mesh to define new source.\n" << std::endl;
-  update_distance(0);
-  return viewer.launch();
-}
+#include <igl/readOBJ.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/exact_geodesic.h>
+#include <igl/unproject_onto_mesh.h>
+#include <igl/parula.h>
+#include <igl/isolines_map.h>
+#include <igl/PI.h>
+#include <iostream>
+
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  igl::opengl::glfw::Viewer viewer;
+  // Load a mesh in OFF format
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/armadillo.obj", V, F);
+
+  const auto update_distance = [&](const int vid)
+  {
+    Eigen::VectorXi VS,FS,VT,FT;
+    // The selected vertex is the source
+    VS.resize(1);
+    VS << vid;
+    // All vertices are the targets
+    VT.setLinSpaced(V.rows(),0,V.rows()-1);
+    Eigen::VectorXd d;
+    std::cout<<"Computing geodesic distance to vertex "<<vid<<"..."<<std::endl;
+    igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+    // Plot the mesh
+    Eigen::MatrixXd CM;
+    igl::parula(Eigen::VectorXd::LinSpaced(21,0,1).eval(),false,CM);
+    igl::isolines_map(Eigen::MatrixXd(CM),CM);
+    viewer.data().set_colormap(CM);
+    viewer.data().set_data(d);
+  };
+
+  // Plot a distance when a vertex is picked
+  viewer.callback_mouse_down =
+  [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
+  {
+    int fid;
+    Eigen::Vector3f bc;
+    // Cast a ray in the view direction starting from the mouse position
+    double x = viewer.current_mouse_x;
+    double y = viewer.core().viewport(3) - viewer.current_mouse_y;
+    if(igl::unproject_onto_mesh(
+      Eigen::Vector2f(x,y),
+      viewer.core().view,
+      viewer.core().proj,
+      viewer.core().viewport,
+      V,
+      F,
+      fid,
+      bc))
+    {
+      int max;
+      bc.maxCoeff(&max);
+      int vid = F(fid,max);
+      update_distance(vid);
+      return true;
+    }
+    return false;
+  };
+  viewer.data().set_mesh(V,F);
+  viewer.data().show_lines = false;
+
+  cout << "Click on mesh to define new source.\n" << std::endl;
+  update_distance(0);
+  return viewer.launch();
+}

+ 122 - 122
tutorial/207_PolygonLaplacian/main.cpp

@@ -1,122 +1,122 @@
-#include <igl/readOBJ.h>
-#include <igl/cotmatrix.h>
-#include <igl/massmatrix.h>
-#include <igl/edges.h>
-#include <igl/find.h>
-#include <igl/min_quad_with_fixed.h>
-#include <igl/polygons_to_triangles.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a mesh in OBJ format
-  Eigen::MatrixXd OV,V;
-  Eigen::VectorXi I,C;
-  igl::readOBJ(
-    argc<=1?TUTORIAL_SHARED_PATH "/cylinder.obj" :argv[1],V,I,C);
-  // center
-  V.rowwise() -= V.colwise().mean();
-  OV = V;
-  // Convert polygon representation to triangles
-  Eigen::MatrixXi F;
-  Eigen::VectorXi J;
-  igl::polygons_to_triangles(I,C,F,J);
-  Eigen::SparseMatrix<double> pL,pM,pP;
-  igl::cotmatrix(V,I,C,pL,pM,pP);
-  Eigen::SparseMatrix<double> tL,tM;
-  igl::cotmatrix(V,F,tL);
-  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,tM);
-  const double bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
-  igl::opengl::glfw::Viewer vr;
-  vr.data_list[0].set_mesh(V,F);
-  vr.append_mesh();
-  vr.selected_data_index = 0;
-  vr.data_list[0].set_face_based(true);
-  Eigen::MatrixXi E;
-  igl::edges(I,C,E);
-  bool show_edges = true;
-  bool use_poly = true;
-  Eigen::MatrixXd pHN;
-  Eigen::MatrixXd tHN;
-  const auto update = [&]()
-  {
-    // why does windows need this Eigen::VectorXd(...) ?
-    pHN = (pL*V).array().colwise() / Eigen::VectorXd(pM.diagonal()).array();
-    tHN = (tL*V).array().colwise() / Eigen::VectorXd(tM.diagonal()).array();
-    pHN *= 1.0/pHN.rowwise().norm().maxCoeff();
-    tHN *= 1.0/tHN.rowwise().norm().maxCoeff();
-    const auto was_face_based  = vr.data_list[0].face_based;
-    Eigen::MatrixXd QV(V.rows()*2,3);
-    QV.topRows(V.rows()) = V;
-    if(use_poly)
-    {
-      printf("using polygon Laplacian\n");
-      QV.bottomRows(V.rows()) = V-pHN;
-    }else
-    {
-      printf("using triangle Laplacian\n");
-      QV.bottomRows(V.rows()) = V-tHN;
-    }
-    Eigen::MatrixXi QE(V.rows(),2);
-    for(int i = 0;i<V.rows();i++){ QE(i,0)=i;QE(i,1)=i+V.rows();}
-    vr.data_list[1].set_edges(QV,QE,Eigen::RowVector3d(1,1,1));
-
-    if(use_poly)
-    {
-      vr.data_list[0].show_lines = false;
-      if(show_edges)
-      {
-        vr.data_list[0].set_edges(V,E,Eigen::RowVector3d(0,0,0));
-      }else
-      {
-        vr.data_list[0].clear_edges();
-      }
-    }else
-    {
-      vr.data_list[0].clear_edges();
-      vr.data_list[0].show_lines = show_edges;
-    }
-    vr.data_list[0].set_face_based(was_face_based);
-  };
-  const double original_area = pM.diagonal().sum();
-  const auto recompute_M = [&]()
-  {
-    Eigen::SparseMatrix<double> _1,_2;
-    igl::cotmatrix(V,I,C,_1,pM,_2);
-    igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,tM);
-    V *= sqrt(original_area / pM.diagonal().sum());
-  };
-  const auto cmcf_step = [&]()
-  {
-    const Eigen::SparseMatrix<double> S =
-      use_poly? ((pM) - 0.05*(pL)): ((tM) - 0.05*(tL));
-    const Eigen::MatrixXd rhs = use_poly? pM*V : tM*V;
-    Eigen::SimplicialLLT<Eigen::SparseMatrix<double > > solver(S);
-    assert(solver.info() == Eigen::Success);
-    V = solver.solve(rhs).eval();
-    // recompute just mass matrices
-    recompute_M();
-    // center
-    V.rowwise() -= V.colwise().mean();
-    vr.data_list[0].set_vertices(V);
-    vr.data_list[0].compute_normals();
-    update();
-  };
-  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
-  {
-    switch(key)
-    {
-      case ' ': cmcf_step(); return true;
-      case 'R': case 'r': V=OV;recompute_M();vr.data_list[0].set_vertices(V);vr.data_list[0].compute_normals(); update();return true;
-      case 'P': case 'p': use_poly=!use_poly; update();return true;
-      case 'L': case 'l': show_edges=!show_edges; update();return true;
-    }
-    return false;
-  };
-  update();
-  vr.launch();
-}
+#include <igl/readOBJ.h>
+#include <igl/cotmatrix.h>
+#include <igl/massmatrix.h>
+#include <igl/edges.h>
+#include <igl/find.h>
+#include <igl/min_quad_with_fixed.h>
+#include <igl/polygons_to_triangles.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a mesh in OBJ format
+  Eigen::MatrixXd OV,V;
+  Eigen::VectorXi I,C;
+  igl::readOBJ(
+    argc<=1?TUTORIAL_SHARED_PATH "/cylinder.obj" :argv[1],V,I,C);
+  // center
+  V.rowwise() -= V.colwise().mean();
+  OV = V;
+  // Convert polygon representation to triangles
+  Eigen::MatrixXi F;
+  Eigen::VectorXi J;
+  igl::polygons_to_triangles(I,C,F,J);
+  Eigen::SparseMatrix<double> pL,pM,pP;
+  igl::cotmatrix(V,I,C,pL,pM,pP);
+  Eigen::SparseMatrix<double> tL,tM;
+  igl::cotmatrix(V,F,tL);
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,tM);
+  const double bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
+  igl::opengl::glfw::Viewer vr;
+  vr.data_list[0].set_mesh(V,F);
+  vr.append_mesh();
+  vr.selected_data_index = 0;
+  vr.data_list[0].set_face_based(true);
+  Eigen::MatrixXi E;
+  igl::edges(I,C,E);
+  bool show_edges = true;
+  bool use_poly = true;
+  Eigen::MatrixXd pHN;
+  Eigen::MatrixXd tHN;
+  const auto update = [&]()
+  {
+    // why does windows need this Eigen::VectorXd(...) ?
+    pHN = (pL*V).array().colwise() / Eigen::VectorXd(pM.diagonal()).array();
+    tHN = (tL*V).array().colwise() / Eigen::VectorXd(tM.diagonal()).array();
+    pHN *= 1.0/pHN.rowwise().norm().maxCoeff();
+    tHN *= 1.0/tHN.rowwise().norm().maxCoeff();
+    const auto was_face_based  = vr.data_list[0].face_based;
+    Eigen::MatrixXd QV(V.rows()*2,3);
+    QV.topRows(V.rows()) = V;
+    if(use_poly)
+    {
+      printf("using polygon Laplacian\n");
+      QV.bottomRows(V.rows()) = V-pHN;
+    }else
+    {
+      printf("using triangle Laplacian\n");
+      QV.bottomRows(V.rows()) = V-tHN;
+    }
+    Eigen::MatrixXi QE(V.rows(),2);
+    for(int i = 0;i<V.rows();i++){ QE(i,0)=i;QE(i,1)=i+V.rows();}
+    vr.data_list[1].set_edges(QV,QE,Eigen::RowVector3d(1,1,1));
+
+    if(use_poly)
+    {
+      vr.data_list[0].show_lines = false;
+      if(show_edges)
+      {
+        vr.data_list[0].set_edges(V,E,Eigen::RowVector3d(0,0,0));
+      }else
+      {
+        vr.data_list[0].clear_edges();
+      }
+    }else
+    {
+      vr.data_list[0].clear_edges();
+      vr.data_list[0].show_lines = show_edges;
+    }
+    vr.data_list[0].set_face_based(was_face_based);
+  };
+  const double original_area = pM.diagonal().sum();
+  const auto recompute_M = [&]()
+  {
+    Eigen::SparseMatrix<double> _1,_2;
+    igl::cotmatrix(V,I,C,_1,pM,_2);
+    igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,tM);
+    V *= sqrt(original_area / pM.diagonal().sum());
+  };
+  const auto cmcf_step = [&]()
+  {
+    const Eigen::SparseMatrix<double> S =
+      use_poly? ((pM) - 0.05*(pL)): ((tM) - 0.05*(tL));
+    const Eigen::MatrixXd rhs = use_poly? pM*V : tM*V;
+    Eigen::SimplicialLLT<Eigen::SparseMatrix<double > > solver(S);
+    assert(solver.info() == Eigen::Success);
+    V = solver.solve(rhs).eval();
+    // recompute just mass matrices
+    recompute_M();
+    // center
+    V.rowwise() -= V.colwise().mean();
+    vr.data_list[0].set_vertices(V);
+    vr.data_list[0].compute_normals();
+    update();
+  };
+  vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
+  {
+    switch(key)
+    {
+      case ' ': cmcf_step(); return true;
+      case 'R': case 'r': V=OV;recompute_M();vr.data_list[0].set_vertices(V);vr.data_list[0].compute_normals(); update();return true;
+      case 'P': case 'p': use_poly=!use_poly; update();return true;
+      case 'L': case 'l': show_edges=!show_edges; update();return true;
+    }
+    return false;
+  };
+  update();
+  vr.launch();
+}

+ 44 - 44
tutorial/301_Slice/main.cpp

@@ -1,44 +1,44 @@
-#include <igl/floor.h>
-#include <igl/readOFF.h>
-#include <igl/find.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
-
-  // 100 random indices into rows of F
-  VectorXi I;
-  igl::floor((0.5*(VectorXd::Random(100,1).array()+1.)*F.rows()).eval(),I);
-
-  // 50 random indices into rows of I
-  VectorXi J;
-  igl::floor((0.5*(VectorXd::Random(50,1).array()+1.)*I.rows()).eval(),J);
-
-  VectorXi K = I(J);
-  // igl::slice(I,J,K); no longer needed
-
-  // default green for all faces
-  MatrixXd C = RowVector3d(0.4,0.8,0.3).replicate(F.rows(),1);
-  // Red for each in K
-  MatrixXd R = RowVector3d(1.0,0.3,0.3).replicate(K.rows(),1);
-  // C(K,:) = R
-  C(K,Eigen::all) = R;
-  // igl::slice_into(R,K,1,C); no longer needed
-
-  Eigen::Array<bool,Eigen::Dynamic,1> W = Eigen::VectorXd::Random(F.rows()).array()>0.5;
-  // Set 1/4 of the colors  to blue
-  MatrixXd B = RowVector3d(0.3,0.3,1.0).replicate(W.count(),1);
-  C(igl::find(W),Eigen::all) = B;
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_colors(C);
-  viewer.launch();
-}
+#include <igl/floor.h>
+#include <igl/readOFF.h>
+#include <igl/find.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
+
+  // 100 random indices into rows of F
+  VectorXi I;
+  igl::floor((0.5*(VectorXd::Random(100,1).array()+1.)*F.rows()).eval(),I);
+
+  // 50 random indices into rows of I
+  VectorXi J;
+  igl::floor((0.5*(VectorXd::Random(50,1).array()+1.)*I.rows()).eval(),J);
+
+  VectorXi K = I(J);
+  // igl::slice(I,J,K); no longer needed
+
+  // default green for all faces
+  MatrixXd C = RowVector3d(0.4,0.8,0.3).replicate(F.rows(),1);
+  // Red for each in K
+  MatrixXd R = RowVector3d(1.0,0.3,0.3).replicate(K.rows(),1);
+  // C(K,:) = R
+  C(K,Eigen::all) = R;
+  // igl::slice_into(R,K,1,C); no longer needed
+
+  Eigen::Array<bool,Eigen::Dynamic,1> W = Eigen::VectorXd::Random(F.rows()).array()>0.5;
+  // Set 1/4 of the colors  to blue
+  MatrixXd B = RowVector3d(0.3,0.3,1.0).replicate(W.count(),1);
+  C(igl::find(W),Eigen::all) = B;
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_colors(C);
+  viewer.launch();
+}

+ 38 - 38
tutorial/302_Sort/main.cpp

@@ -1,38 +1,38 @@
-#include <igl/barycenter.h>
-#include <igl/colon.h>
-#include <igl/jet.h>
-#include <igl/readOFF.h>
-#include <igl/slice_into.h>
-#include <igl/sortrows.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
-
-  // Sort barycenters lexicographically
-  MatrixXd BC,sorted_BC;
-  igl::barycenter(V,F,BC);
-  VectorXi I,J;
-  // sorted_BC = BC(I,:)
-  igl::sortrows(BC,true,sorted_BC,I);
-  // Get sorted "place" from sorted indices
-  J.resize(I.rows());
-  // J(I) = 1:numel(I)
-  igl::slice_into(igl::colon<int>(0,I.size()-1),I,J);
-
-  // Pseudo-color based on sorted place
-  MatrixXd C;
-  igl::jet(J,true,C);
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_colors(C);
-  viewer.launch();
-}
+#include <igl/barycenter.h>
+#include <igl/colon.h>
+#include <igl/jet.h>
+#include <igl/readOFF.h>
+#include <igl/slice_into.h>
+#include <igl/sortrows.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
+
+  // Sort barycenters lexicographically
+  MatrixXd BC,sorted_BC;
+  igl::barycenter(V,F,BC);
+  VectorXi I,J;
+  // sorted_BC = BC(I,:)
+  igl::sortrows(BC,true,sorted_BC,I);
+  // Get sorted "place" from sorted indices
+  J.resize(I.rows());
+  // J(I) = 1:numel(I)
+  igl::slice_into(igl::colon<int>(0,I.size()-1),I,J);
+
+  // Pseudo-color based on sorted place
+  MatrixXd C;
+  igl::jet(J,true,C);
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_colors(C);
+  viewer.launch();
+}

+ 65 - 65
tutorial/303_LaplaceEquation/main.cpp

@@ -1,65 +1,65 @@
-#include <igl/boundary_facets.h>
-#include <igl/colon.h>
-#include <igl/cotmatrix.h>
-#include <igl/jet.h>
-#include <igl/min_quad_with_fixed.h>
-#include <igl/readOFF.h>
-#include <igl/setdiff.h>
-#include <igl/slice.h>
-#include <igl/unique.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <Eigen/Sparse>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off",V,F);
-  // Find boundary edges
-  MatrixXi E;
-  igl::boundary_facets(F,E);
-  // Find boundary vertices
-  VectorXi b,IA,IC;
-  igl::unique(E,b,IA,IC);
-  // List of all vertex indices
-  VectorXi all,in;
-  igl::colon<int>(0,V.rows()-1,all);
-  // List of interior indices
-  igl::setdiff(all,b,in,IA);
-
-  // Construct and slice up Laplacian
-  SparseMatrix<double> L,L_in_in,L_in_b;
-  igl::cotmatrix(V,F,L);
-  igl::slice(L,in,in,L_in_in);
-  igl::slice(L,in,b,L_in_b);
-
-  // Dirichlet boundary conditions from z-coordinate
-  VectorXd Z = V.col(2);
-  VectorXd bc = Z(b);
-
-  // Solve PDE
-  SimplicialLLT<SparseMatrix<double > > solver(-L_in_in);
-  // slice into solution
-  Z(in) = solver.solve(L_in_b*bc);
-
-  // Alternative, short hand
-  igl::min_quad_with_fixed_data<double> mqwf;
-  // Linear term is 0
-  VectorXd B = VectorXd::Zero(V.rows(),1);
-  // Empty constraints
-  VectorXd Beq;
-  SparseMatrix<double> Aeq;
-  // Our cotmatrix is _negative_ definite, so flip sign
-  igl::min_quad_with_fixed_precompute((-L).eval(),b,Aeq,true,mqwf);
-  igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z);
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().show_lines = false;
-  viewer.data().set_data(Z);
-  viewer.launch();
-}
+#include <igl/boundary_facets.h>
+#include <igl/colon.h>
+#include <igl/cotmatrix.h>
+#include <igl/jet.h>
+#include <igl/min_quad_with_fixed.h>
+#include <igl/readOFF.h>
+#include <igl/setdiff.h>
+#include <igl/slice.h>
+#include <igl/unique.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off",V,F);
+  // Find boundary edges
+  MatrixXi E;
+  igl::boundary_facets(F,E);
+  // Find boundary vertices
+  VectorXi b,IA,IC;
+  igl::unique(E,b,IA,IC);
+  // List of all vertex indices
+  VectorXi all,in;
+  igl::colon<int>(0,V.rows()-1,all);
+  // List of interior indices
+  igl::setdiff(all,b,in,IA);
+
+  // Construct and slice up Laplacian
+  SparseMatrix<double> L,L_in_in,L_in_b;
+  igl::cotmatrix(V,F,L);
+  igl::slice(L,in,in,L_in_in);
+  igl::slice(L,in,b,L_in_b);
+
+  // Dirichlet boundary conditions from z-coordinate
+  VectorXd Z = V.col(2);
+  VectorXd bc = Z(b);
+
+  // Solve PDE
+  SimplicialLLT<SparseMatrix<double > > solver(-L_in_in);
+  // slice into solution
+  Z(in) = solver.solve(L_in_b*bc);
+
+  // Alternative, short hand
+  igl::min_quad_with_fixed_data<double> mqwf;
+  // Linear term is 0
+  VectorXd B = VectorXd::Zero(V.rows(),1);
+  // Empty constraints
+  VectorXd Beq;
+  SparseMatrix<double> Aeq;
+  // Our cotmatrix is _negative_ definite, so flip sign
+  igl::min_quad_with_fixed_precompute((-L).eval(),b,Aeq,true,mqwf);
+  igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z);
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().show_lines = false;
+  viewer.data().set_data(Z);
+  viewer.launch();
+}

+ 89 - 89
tutorial/304_LinearEqualityConstraints/main.cpp

@@ -1,89 +1,89 @@
-#include <igl/boundary_facets.h>
-#include <igl/cotmatrix.h>
-#include <igl/invert_diag.h>
-#include <igl/jet.h>
-#include <igl/massmatrix.h>
-#include <igl/min_quad_with_fixed.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <Eigen/Sparse>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",V,F);
-
-  // Two fixed points
-  VectorXi b(2,1);
-  // Left hand, left foot
-  b<<4331,5957;
-  VectorXd bc(2,1);
-  bc<<1,-1;
-
-  // Construct Laplacian and mass matrix
-  SparseMatrix<double> L,M,Minv,Q;
-  igl::cotmatrix(V,F,L);
-  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
-  igl::invert_diag(M,Minv);
-  // Bi-Laplacian
-  Q = L * (Minv * L);
-  // Zero linear term
-  VectorXd B = VectorXd::Zero(V.rows(),1);
-
-  VectorXd Z,Z_const;
-  {
-    // Alternative, short hand
-    igl::min_quad_with_fixed_data<double> mqwf;
-    // Empty constraints
-    VectorXd Beq;
-    SparseMatrix<double> Aeq;
-    igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
-    igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z);
-  }
-
-  {
-    igl::min_quad_with_fixed_data<double> mqwf;
-    // Constraint forcing difference of two points to be 0
-    SparseMatrix<double> Aeq(1,V.rows());
-    // Right hand, right foot
-    Aeq.insert(0,6074) = 1;
-    Aeq.insert(0,6523) = -1;
-    Aeq.makeCompressed();
-    VectorXd Beq(1,1);
-    Beq(0) = 0;
-    igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
-    igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z_const);
-  }
-
-  // Use same color axes
-  const double min_z = std::min(Z.minCoeff(),Z_const.minCoeff());
-  const double max_z = std::max(Z.maxCoeff(),Z_const.maxCoeff());
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().show_lines = false;
-  viewer.data().set_data(Z,min_z,max_z);
-
-  viewer.callback_key_down =
-    [&Z,&Z_const,&min_z,&max_z](igl::opengl::glfw::Viewer& viewer,unsigned char key,int mod)->bool
-    {
-      if(key == ' ')
-      {
-        static bool toggle = true;
-        viewer.data().set_data(toggle?Z_const:Z,min_z,max_z);
-        toggle = !toggle;
-        return true;
-      }else
-      {
-        return false;
-      }
-    };
-  cout<<
-    "Press [space] to toggle between unconstrained and constrained."<<endl;
-  viewer.launch();
-}
+#include <igl/boundary_facets.h>
+#include <igl/cotmatrix.h>
+#include <igl/invert_diag.h>
+#include <igl/jet.h>
+#include <igl/massmatrix.h>
+#include <igl/min_quad_with_fixed.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",V,F);
+
+  // Two fixed points
+  VectorXi b(2,1);
+  // Left hand, left foot
+  b<<4331,5957;
+  VectorXd bc(2,1);
+  bc<<1,-1;
+
+  // Construct Laplacian and mass matrix
+  SparseMatrix<double> L,M,Minv,Q;
+  igl::cotmatrix(V,F,L);
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
+  igl::invert_diag(M,Minv);
+  // Bi-Laplacian
+  Q = L * (Minv * L);
+  // Zero linear term
+  VectorXd B = VectorXd::Zero(V.rows(),1);
+
+  VectorXd Z,Z_const;
+  {
+    // Alternative, short hand
+    igl::min_quad_with_fixed_data<double> mqwf;
+    // Empty constraints
+    VectorXd Beq;
+    SparseMatrix<double> Aeq;
+    igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
+    igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z);
+  }
+
+  {
+    igl::min_quad_with_fixed_data<double> mqwf;
+    // Constraint forcing difference of two points to be 0
+    SparseMatrix<double> Aeq(1,V.rows());
+    // Right hand, right foot
+    Aeq.insert(0,6074) = 1;
+    Aeq.insert(0,6523) = -1;
+    Aeq.makeCompressed();
+    VectorXd Beq(1,1);
+    Beq(0) = 0;
+    igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
+    igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z_const);
+  }
+
+  // Use same color axes
+  const double min_z = std::min(Z.minCoeff(),Z_const.minCoeff());
+  const double max_z = std::max(Z.maxCoeff(),Z_const.maxCoeff());
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().show_lines = false;
+  viewer.data().set_data(Z,min_z,max_z);
+
+  viewer.callback_key_down =
+    [&Z,&Z_const,&min_z,&max_z](igl::opengl::glfw::Viewer& viewer,unsigned char key,int mod)->bool
+    {
+      if(key == ' ')
+      {
+        static bool toggle = true;
+        viewer.data().set_data(toggle?Z_const:Z,min_z,max_z);
+        toggle = !toggle;
+        return true;
+      }else
+      {
+        return false;
+      }
+    };
+  cout<<
+    "Press [space] to toggle between unconstrained and constrained."<<endl;
+  viewer.launch();
+}

+ 93 - 93
tutorial/305_QuadraticProgramming/main.cpp

@@ -1,93 +1,93 @@
-#include <igl/active_set.h>
-#include <igl/boundary_facets.h>
-#include <igl/cotmatrix.h>
-#include <igl/invert_diag.h>
-#include <igl/jet.h>
-#include <igl/massmatrix.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <Eigen/Sparse>
-#include <iostream>
-  
-Eigen::VectorXi b;
-Eigen::VectorXd B,bc,lx,ux,Beq,Bieq,Z;
-Eigen::SparseMatrix<double> Q,Aeq,Aieq;
-
-void solve(igl::opengl::glfw::Viewer &viewer)
-{
-  using namespace std;
-  igl::active_set_params as;
-  as.max_iter = 8;
-  igl::active_set(Q,B,b,bc,Aeq,Beq,Aieq,Bieq,lx,ux,as,Z);
-  viewer.data().set_data(Z);
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mod)
-{
-  switch(key)
-  {
-    case '.':
-      Beq(0) *= 2.0;
-      solve(viewer);
-      return true;
-    case ',':
-      Beq(0) /= 2.0;
-      solve(viewer);
-      return true;
-    case ' ':
-      solve(viewer);
-      return true;
-    default:
-      return false;
-  }
-}
-
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd V;
-  MatrixXi F;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",V,F);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().show_lines = false;
-  viewer.callback_key_down = &key_down;
-
-  // One fixed point
-  b.resize(1,1);
-  // point on belly.
-  b<<2556;
-  bc.resize(1,1);
-  bc<<1;
-
-  // Construct Laplacian and mass matrix
-  SparseMatrix<double> L,M,Minv;
-  igl::cotmatrix(V,F,L);
-  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
-  //M = (M/M.diagonal().maxCoeff()).eval();
-  igl::invert_diag(M,Minv);
-  // Bi-Laplacian
-  Q = L.transpose() * (Minv * L);
-  // Zero linear term
-  B = VectorXd::Zero(V.rows(),1);
-
-  // Lower and upper bound
-  lx = VectorXd::Zero(V.rows(),1);
-  ux = VectorXd::Ones(V.rows(),1);
-
-  // Equality constraint constrain solution to sum to 1
-  Beq.resize(1,1);
-  Beq(0) = 0.08;
-  Aeq = M.diagonal().sparseView().transpose();
-  // (Empty inequality constraints)
-  solve(viewer);
-  cout<<
-    "Press '.' to increase scale and resolve."<<endl<<
-    "Press ',' to decrease scale and resolve."<<endl;
-
-  viewer.launch();
-}
+#include <igl/active_set.h>
+#include <igl/boundary_facets.h>
+#include <igl/cotmatrix.h>
+#include <igl/invert_diag.h>
+#include <igl/jet.h>
+#include <igl/massmatrix.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+  
+Eigen::VectorXi b;
+Eigen::VectorXd B,bc,lx,ux,Beq,Bieq,Z;
+Eigen::SparseMatrix<double> Q,Aeq,Aieq;
+
+void solve(igl::opengl::glfw::Viewer &viewer)
+{
+  using namespace std;
+  igl::active_set_params as;
+  as.max_iter = 8;
+  igl::active_set(Q,B,b,bc,Aeq,Beq,Aieq,Bieq,lx,ux,as,Z);
+  viewer.data().set_data(Z);
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mod)
+{
+  switch(key)
+  {
+    case '.':
+      Beq(0) *= 2.0;
+      solve(viewer);
+      return true;
+    case ',':
+      Beq(0) /= 2.0;
+      solve(viewer);
+      return true;
+    case ' ':
+      solve(viewer);
+      return true;
+    default:
+      return false;
+  }
+}
+
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd V;
+  MatrixXi F;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",V,F);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().show_lines = false;
+  viewer.callback_key_down = &key_down;
+
+  // One fixed point
+  b.resize(1,1);
+  // point on belly.
+  b<<2556;
+  bc.resize(1,1);
+  bc<<1;
+
+  // Construct Laplacian and mass matrix
+  SparseMatrix<double> L,M,Minv;
+  igl::cotmatrix(V,F,L);
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
+  //M = (M/M.diagonal().maxCoeff()).eval();
+  igl::invert_diag(M,Minv);
+  // Bi-Laplacian
+  Q = L.transpose() * (Minv * L);
+  // Zero linear term
+  B = VectorXd::Zero(V.rows(),1);
+
+  // Lower and upper bound
+  lx = VectorXd::Zero(V.rows(),1);
+  ux = VectorXd::Ones(V.rows(),1);
+
+  // Equality constraint constrain solution to sum to 1
+  Beq.resize(1,1);
+  Beq(0) = 0.08;
+  Aeq = M.diagonal().sparseView().transpose();
+  // (Empty inequality constraints)
+  solve(viewer);
+  cout<<
+    "Press '.' to increase scale and resolve."<<endl<<
+    "Press ',' to decrease scale and resolve."<<endl;
+
+  viewer.launch();
+}

+ 74 - 74
tutorial/306_EigenDecomposition/main.cpp

@@ -1,74 +1,74 @@
-#include <igl/eigs.h>
-#include <igl/cotmatrix.h>
-#include <igl/massmatrix.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/parula.h>
-#include <igl/read_triangle_mesh.h>
-#include <Eigen/Sparse>
-#include <iostream>
-#include <queue>
-
-Eigen::MatrixXd V,U;
-Eigen::MatrixXi F;
-int c=0;
-double bbd = 1;
-bool twod = 0;
-int main(int argc, char * argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  using namespace igl;
-  VectorXd D;
-  if(!read_triangle_mesh(
-     argc>1?argv[1]: TUTORIAL_SHARED_PATH "/beetle.off",V,F))
-  {
-    cout<<"failed to load mesh"<<endl;
-  }
-  twod = V.col(2).minCoeff()==V.col(2).maxCoeff();
-  bbd = (V.colwise().maxCoeff()-V.colwise().minCoeff()).norm();
-  SparseMatrix<double> L,M;
-  cotmatrix(V,F,L);
-  L = (-L).eval();
-  massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M);
-  const size_t k = 5;
-  if(!eigs(L,M,k+1,EIGS_TYPE_SM,U,D))
-  {
-    cout<<"failed."<<endl;
-  }
-  // Normalize
-  U = ((U.array()-U.minCoeff())/(U.maxCoeff()-U.minCoeff())).eval();
-
-  igl::opengl::glfw::Viewer viewer;
-  viewer.callback_key_down = [&](igl::opengl::glfw::Viewer & viewer,unsigned char key,int)->bool
-  {
-    switch(key)
-    {
-      default:
-        return false;
-      case ' ':
-      {
-        U = U.rightCols(k).eval();
-        // Rescale eigen vectors for visualization
-        VectorXd Z =
-          bbd*0.5*U.col(c);
-        if(twod)
-        {
-          V.col(2) = Z;
-          viewer.data().set_mesh(V,F);
-          viewer.data().compute_normals();
-        }
-        viewer.data().set_data(U.col(c).eval());
-        c = (c+1)%U.cols();
-        return true;
-      }
-    }
-  };
-  viewer.data().set_mesh(V,F);
-  viewer.callback_key_down(viewer,' ',0);
-  viewer.data().show_lines = false;
-  std::cout<<
-R"(
-  [space] Cycle through eigen modes
-)";
-  viewer.launch();
-}
+#include <igl/eigs.h>
+#include <igl/cotmatrix.h>
+#include <igl/massmatrix.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/parula.h>
+#include <igl/read_triangle_mesh.h>
+#include <Eigen/Sparse>
+#include <iostream>
+#include <queue>
+
+Eigen::MatrixXd V,U;
+Eigen::MatrixXi F;
+int c=0;
+double bbd = 1;
+bool twod = 0;
+int main(int argc, char * argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  using namespace igl;
+  VectorXd D;
+  if(!read_triangle_mesh(
+     argc>1?argv[1]: TUTORIAL_SHARED_PATH "/beetle.off",V,F))
+  {
+    cout<<"failed to load mesh"<<endl;
+  }
+  twod = V.col(2).minCoeff()==V.col(2).maxCoeff();
+  bbd = (V.colwise().maxCoeff()-V.colwise().minCoeff()).norm();
+  SparseMatrix<double> L,M;
+  cotmatrix(V,F,L);
+  L = (-L).eval();
+  massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,M);
+  const size_t k = 5;
+  if(!eigs(L,M,k+1,EIGS_TYPE_SM,U,D))
+  {
+    cout<<"failed."<<endl;
+  }
+  // Normalize
+  U = ((U.array()-U.minCoeff())/(U.maxCoeff()-U.minCoeff())).eval();
+
+  igl::opengl::glfw::Viewer viewer;
+  viewer.callback_key_down = [&](igl::opengl::glfw::Viewer & viewer,unsigned char key,int)->bool
+  {
+    switch(key)
+    {
+      default:
+        return false;
+      case ' ':
+      {
+        U = U.rightCols(k).eval();
+        // Rescale eigen vectors for visualization
+        VectorXd Z =
+          bbd*0.5*U.col(c);
+        if(twod)
+        {
+          V.col(2) = Z;
+          viewer.data().set_mesh(V,F);
+          viewer.data().compute_normals();
+        }
+        viewer.data().set_data(U.col(c).eval());
+        c = (c+1)%U.cols();
+        return true;
+      }
+    }
+  };
+  viewer.data().set_mesh(V,F);
+  viewer.callback_key_down(viewer,' ',0);
+  viewer.data().show_lines = false;
+  std::cout<<
+R"(
+  [space] Cycle through eigen modes
+)";
+  viewer.launch();
+}

+ 125 - 125
tutorial/401_BiharmonicDeformation/main.cpp

@@ -1,125 +1,125 @@
-#include <igl/colon.h>
-#include <igl/harmonic.h>
-#include <igl/readOBJ.h>
-#include <igl/readDMAT.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <algorithm>
-#include <iostream>
-
-double bc_frac = 1.0;
-double bc_dir = -0.03;
-bool deformation_field = false;
-Eigen::MatrixXd V,U,V_bc,U_bc;
-Eigen::VectorXd Z;
-Eigen::MatrixXi F;
-Eigen::VectorXi b;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  // Determine boundary conditions
-  if(viewer.core().is_animating)
-  {
-    bc_frac += bc_dir;
-    bc_dir *= (bc_frac>=1.0 || bc_frac<=0.0?-1.0:1.0);
-  }
-
-  const MatrixXd U_bc_anim = V_bc+bc_frac*(U_bc-V_bc);
-  if(deformation_field)
-  {
-    MatrixXd D;
-    MatrixXd D_bc = U_bc_anim - V_bc;
-    igl::harmonic(V,F,b,D_bc,2,D);
-    U = V+D;
-  }else
-  {
-    igl::harmonic(V,F,b,U_bc_anim,2.,U);
-  }
-  viewer.data().set_vertices(U);
-  viewer.data().compute_normals();
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      return true;
-    case 'D':
-    case 'd':
-      deformation_field = !deformation_field;
-      return true;
-  }
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/decimated-max.obj",V,F);
-  U=V;
-  // S(i) = j: j<0 (vertex i not in handle), j >= 0 (vertex i in handle j)
-  VectorXi S;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/decimated-max-selection.dmat",S);
-  igl::colon<int>(0,V.rows()-1,b);
-  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(),
-   [&S](int i)->bool{return S(i)>=0;})-b.data());
-
-  // Boundary conditions directly on deformed positions
-  U_bc.resize(b.size(),V.cols());
-  V_bc.resize(b.size(),V.cols());
-  for(int bi = 0;bi<b.size();bi++)
-  {
-    V_bc.row(bi) = V.row(b(bi));
-    switch(S(b(bi)))
-    {
-      case 0:
-        // Don't move handle 0
-        U_bc.row(bi) = V.row(b(bi));
-        break;
-      case 1:
-        // move handle 1 down
-        U_bc.row(bi) = V.row(b(bi)) + RowVector3d(0,-50,0);
-        break;
-      case 2:
-      default:
-        // move other handles forward
-        U_bc.row(bi) = V.row(b(bi)) + RowVector3d(0,0,-25);
-        break;
-    }
-  }
-
-  // Pseudo-color based on selection
-  MatrixXd C(F.rows(),3);
-  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
-  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
-  for(int f = 0;f<F.rows();f++)
-  {
-    if( S(F(f,0))>=0 && S(F(f,1))>=0 && S(F(f,2))>=0)
-    {
-      C.row(f) = purple;
-    }else
-    {
-      C.row(f) = gold;
-    }
-  }
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().show_lines = false;
-  viewer.data().set_colors(C);
-  viewer.core().trackball_angle = Eigen::Quaternionf(sqrt(2.0),0,sqrt(2.0),0);
-  viewer.core().trackball_angle.normalize();
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  //viewer.core().is_animating = true;
-  viewer.core().animation_max_fps = 30.;
-  cout<<
-    "Press [space] to toggle deformation."<<endl<<
-    "Press 'd' to toggle between biharmonic surface or displacements."<<endl;
-  viewer.launch();
-}
+#include <igl/colon.h>
+#include <igl/harmonic.h>
+#include <igl/readOBJ.h>
+#include <igl/readDMAT.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <algorithm>
+#include <iostream>
+
+double bc_frac = 1.0;
+double bc_dir = -0.03;
+bool deformation_field = false;
+Eigen::MatrixXd V,U,V_bc,U_bc;
+Eigen::VectorXd Z;
+Eigen::MatrixXi F;
+Eigen::VectorXi b;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  // Determine boundary conditions
+  if(viewer.core().is_animating)
+  {
+    bc_frac += bc_dir;
+    bc_dir *= (bc_frac>=1.0 || bc_frac<=0.0?-1.0:1.0);
+  }
+
+  const MatrixXd U_bc_anim = V_bc+bc_frac*(U_bc-V_bc);
+  if(deformation_field)
+  {
+    MatrixXd D;
+    MatrixXd D_bc = U_bc_anim - V_bc;
+    igl::harmonic(V,F,b,D_bc,2,D);
+    U = V+D;
+  }else
+  {
+    igl::harmonic(V,F,b,U_bc_anim,2.,U);
+  }
+  viewer.data().set_vertices(U);
+  viewer.data().compute_normals();
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      return true;
+    case 'D':
+    case 'd':
+      deformation_field = !deformation_field;
+      return true;
+  }
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/decimated-max.obj",V,F);
+  U=V;
+  // S(i) = j: j<0 (vertex i not in handle), j >= 0 (vertex i in handle j)
+  VectorXi S;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/decimated-max-selection.dmat",S);
+  igl::colon<int>(0,V.rows()-1,b);
+  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(),
+   [&S](int i)->bool{return S(i)>=0;})-b.data());
+
+  // Boundary conditions directly on deformed positions
+  U_bc.resize(b.size(),V.cols());
+  V_bc.resize(b.size(),V.cols());
+  for(int bi = 0;bi<b.size();bi++)
+  {
+    V_bc.row(bi) = V.row(b(bi));
+    switch(S(b(bi)))
+    {
+      case 0:
+        // Don't move handle 0
+        U_bc.row(bi) = V.row(b(bi));
+        break;
+      case 1:
+        // move handle 1 down
+        U_bc.row(bi) = V.row(b(bi)) + RowVector3d(0,-50,0);
+        break;
+      case 2:
+      default:
+        // move other handles forward
+        U_bc.row(bi) = V.row(b(bi)) + RowVector3d(0,0,-25);
+        break;
+    }
+  }
+
+  // Pseudo-color based on selection
+  MatrixXd C(F.rows(),3);
+  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
+  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
+  for(int f = 0;f<F.rows();f++)
+  {
+    if( S(F(f,0))>=0 && S(F(f,1))>=0 && S(F(f,2))>=0)
+    {
+      C.row(f) = purple;
+    }else
+    {
+      C.row(f) = gold;
+    }
+  }
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().show_lines = false;
+  viewer.data().set_colors(C);
+  viewer.core().trackball_angle = Eigen::Quaternionf(sqrt(2.0),0,sqrt(2.0),0);
+  viewer.core().trackball_angle.normalize();
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  //viewer.core().is_animating = true;
+  viewer.core().animation_max_fps = 30.;
+  cout<<
+    "Press [space] to toggle deformation."<<endl<<
+    "Press 'd' to toggle between biharmonic surface or displacements."<<endl;
+  viewer.launch();
+}

+ 110 - 110
tutorial/402_PolyharmonicDeformation/main.cpp

@@ -1,110 +1,110 @@
-#include <igl/colon.h>
-#include <igl/harmonic.h>
-#include <igl/readOBJ.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <algorithm>
-#include <iostream>
-
-double z_max = 1.0;
-double z_dir = -0.03;
-int k = 2;
-bool resolve = true;
-Eigen::MatrixXd V,U;
-Eigen::VectorXd Z;
-Eigen::MatrixXi F;
-Eigen::VectorXi b;
-Eigen::VectorXd bc;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  if(resolve)
-  {
-    igl::harmonic(V,F,b,bc,k,Z);
-    resolve = false;
-  }
-  U.col(2) = z_max*Z;
-  viewer.data().set_vertices(U);
-  viewer.data().compute_normals();
-  if(viewer.core().is_animating)
-  {
-    z_max += z_dir;
-    z_dir *= (z_max>=1.0 || z_max<=0.0?-1.0:1.0);
-  }
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      break;
-    case '.':
-      k++;
-      k = (k>4?4:k);
-      resolve = true;
-      break;
-    case ',':
-      k--;
-      k = (k<1?1:k);
-      resolve = true;
-      break;
-  }
-  return true;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/bump-domain.obj",V,F);
-  U=V;
-  // Find boundary vertices outside annulus
-  typedef Matrix<bool,Dynamic,1> VectorXb;
-  VectorXb is_outer = (V.rowwise().norm().array()-1.0)>-1e-15;
-  VectorXb is_inner = (V.rowwise().norm().array()-0.15)<1e-15;
-  VectorXb in_b = is_outer.array() || is_inner.array();
-  igl::colon<int>(0,V.rows()-1,b);
-  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(),
-   [&in_b](int i)->bool{return in_b(i);})-b.data());
-  bc.resize(b.size(),1);
-  for(int bi = 0;bi<b.size();bi++)
-  {
-    bc(bi) = (is_outer(b(bi))?0.0:1.0);
-  }
-
-
-  // Pseudo-color based on selection
-  MatrixXd C(F.rows(),3);
-  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
-  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
-  for(int f = 0;f<F.rows();f++)
-  {
-    if( in_b(F(f,0)) && in_b(F(f,1)) && in_b(F(f,2)))
-    {
-      C.row(f) = purple;
-    }else
-    {
-      C.row(f) = gold;
-    }
-  }
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().show_lines = false;
-  viewer.data().set_colors(C);
-  viewer.core().trackball_angle = Eigen::Quaternionf(0.81,-0.58,-0.03,-0.03);
-  viewer.core().trackball_angle.normalize();
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  viewer.core().is_animating = true;
-  viewer.core().animation_max_fps = 30.;
-  cout<<
-    "Press [space] to toggle animation."<<endl<<
-    "Press '.' to increase k."<<endl<<
-    "Press ',' to decrease k."<<endl;
-  viewer.launch();
-}
+#include <igl/colon.h>
+#include <igl/harmonic.h>
+#include <igl/readOBJ.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <algorithm>
+#include <iostream>
+
+double z_max = 1.0;
+double z_dir = -0.03;
+int k = 2;
+bool resolve = true;
+Eigen::MatrixXd V,U;
+Eigen::VectorXd Z;
+Eigen::MatrixXi F;
+Eigen::VectorXi b;
+Eigen::VectorXd bc;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  if(resolve)
+  {
+    igl::harmonic(V,F,b,bc,k,Z);
+    resolve = false;
+  }
+  U.col(2) = z_max*Z;
+  viewer.data().set_vertices(U);
+  viewer.data().compute_normals();
+  if(viewer.core().is_animating)
+  {
+    z_max += z_dir;
+    z_dir *= (z_max>=1.0 || z_max<=0.0?-1.0:1.0);
+  }
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      break;
+    case '.':
+      k++;
+      k = (k>4?4:k);
+      resolve = true;
+      break;
+    case ',':
+      k--;
+      k = (k<1?1:k);
+      resolve = true;
+      break;
+  }
+  return true;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/bump-domain.obj",V,F);
+  U=V;
+  // Find boundary vertices outside annulus
+  typedef Matrix<bool,Dynamic,1> VectorXb;
+  VectorXb is_outer = (V.rowwise().norm().array()-1.0)>-1e-15;
+  VectorXb is_inner = (V.rowwise().norm().array()-0.15)<1e-15;
+  VectorXb in_b = is_outer.array() || is_inner.array();
+  igl::colon<int>(0,V.rows()-1,b);
+  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(),
+   [&in_b](int i)->bool{return in_b(i);})-b.data());
+  bc.resize(b.size(),1);
+  for(int bi = 0;bi<b.size();bi++)
+  {
+    bc(bi) = (is_outer(b(bi))?0.0:1.0);
+  }
+
+
+  // Pseudo-color based on selection
+  MatrixXd C(F.rows(),3);
+  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
+  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
+  for(int f = 0;f<F.rows();f++)
+  {
+    if( in_b(F(f,0)) && in_b(F(f,1)) && in_b(F(f,2)))
+    {
+      C.row(f) = purple;
+    }else
+    {
+      C.row(f) = gold;
+    }
+  }
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().show_lines = false;
+  viewer.data().set_colors(C);
+  viewer.core().trackball_angle = Eigen::Quaternionf(0.81,-0.58,-0.03,-0.03);
+  viewer.core().trackball_angle.normalize();
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  viewer.core().is_animating = true;
+  viewer.core().animation_max_fps = 30.;
+  cout<<
+    "Press [space] to toggle animation."<<endl<<
+    "Press '.' to increase k."<<endl<<
+    "Press ',' to decrease k."<<endl;
+  viewer.launch();
+}

+ 163 - 163
tutorial/403_BoundedBiharmonicWeights/main.cpp

@@ -1,163 +1,163 @@
-#include <igl/boundary_conditions.h>
-#include <igl/colon.h>
-#include <igl/column_to_quats.h>
-#include <igl/directed_edge_parents.h>
-#include <igl/forward_kinematics.h>
-#include <igl/jet.h>
-#include <igl/lbs_matrix.h>
-#include <igl/deform_skeleton.h>
-#include <igl/readDMAT.h>
-#include <igl/readMESH.h>
-#include <igl/readTGF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/bbw.h>
-
-#include <Eigen/Geometry>
-#include <Eigen/StdVector>
-#include <vector>
-#include <algorithm>
-#include <iostream>
-
-
-typedef
-  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
-  RotationList;
-
-const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
-int selected = 0;
-Eigen::MatrixXd V,W,U,C,M;
-Eigen::MatrixXi T,F,BE;
-Eigen::VectorXi P;
-RotationList pose;
-double anim_t = 1.0;
-double anim_t_dir = -0.03;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  using namespace std;
-  if(viewer.core().is_animating)
-  {
-    // Interpolate pose and identity
-    RotationList anim_pose(pose.size());
-    for(int e = 0;e<pose.size();e++)
-    {
-      anim_pose[e] = pose[e].slerp(anim_t,Quaterniond::Identity());
-    }
-    // Propagate relative rotations via FK to retrieve absolute transformations
-    RotationList vQ;
-    vector<Vector3d> vT;
-    igl::forward_kinematics(C,BE,P,anim_pose,vQ,vT);
-    const int dim = C.cols();
-    MatrixXd T(BE.rows()*(dim+1),dim);
-    for(int e = 0;e<BE.rows();e++)
-    {
-      Affine3d a = Affine3d::Identity();
-      a.translate(vT[e]);
-      a.rotate(vQ[e]);
-      T.block(e*(dim+1),0,dim+1,dim) =
-        a.matrix().transpose().block(0,0,dim+1,dim);
-    }
-    // Compute deformation via LBS as matrix multiplication
-    U = M*T;
-
-    // Also deform skeleton edges
-    MatrixXd CT;
-    MatrixXi BET;
-    igl::deform_skeleton(C,BE,T,CT,BET);
-
-    viewer.data().set_vertices(U);
-    viewer.data().set_edges(CT,BET,sea_green);
-    viewer.data().compute_normals();
-    anim_t += anim_t_dir;
-    anim_t_dir *= (anim_t>=1.0 || anim_t<=0.0?-1.0:1.0);
-  }
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      break;
-    case '.':
-      selected++;
-      selected = std::min(std::max(selected,0),(int)W.cols()-1);
-      viewer.data().set_data(W.col(selected));
-      break;
-    case ',':
-      selected--;
-      selected = std::min(std::max(selected,0),(int)W.cols()-1);
-      viewer.data().set_data(W.col(selected));
-      break;
-  }
-  return true;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readMESH(TUTORIAL_SHARED_PATH "/hand.mesh",V,T,F);
-  U=V;
-  igl::readTGF(TUTORIAL_SHARED_PATH "/hand.tgf",C,BE);
-  // retrieve parents for forward kinematics
-  igl::directed_edge_parents(BE,P);
-
-  // Read pose as matrix of quaternions per row
-  MatrixXd Q;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/hand-pose.dmat",Q);
-  igl::column_to_quats(Q,pose);
-  assert(pose.size() == BE.rows());
-
-  // List of boundary indices (aka fixed value indices into VV)
-  VectorXi b;
-  // List of boundary conditions of each weight function
-  MatrixXd bc;
-  igl::boundary_conditions(V,T,C,VectorXi(),BE,MatrixXi(),MatrixXi(),b,bc);
-
-  // compute BBW weights matrix
-  igl::BBWData bbw_data;
-  // only a few iterations for sake of demo
-  bbw_data.active_set_params.max_iter = 8;
-  bbw_data.verbosity = 2;
-  if(!igl::bbw(V,T,b,bc,bbw_data,W))
-  {
-    return EXIT_FAILURE;
-  }
-
-  //MatrixXd Vsurf = V.topLeftCorner(F.maxCoeff()+1,V.cols());
-  //MatrixXd Wsurf;
-  //if(!igl::bone_heat(Vsurf,F,C,VectorXi(),BE,MatrixXi(),Wsurf))
-  //{
-  //  return false;
-  //}
-  //W.setConstant(V.rows(),Wsurf.cols(),1);
-  //W.topLeftCorner(Wsurf.rows(),Wsurf.cols()) = Wsurf = Wsurf = Wsurf = Wsurf;
-
-  // Normalize weights to sum to one
-  W  = (W.array().colwise() / W.array().rowwise().sum()).eval();
-  // precompute linear blend skinning matrix
-  igl::lbs_matrix(V,W,M);
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().set_data(W.col(selected));
-  viewer.data().set_edges(C,BE,sea_green);
-  viewer.data().show_lines = false;
-  viewer.data().show_overlay_depth = false;
-  viewer.data().line_width = 1;
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  viewer.core().is_animating = false;
-  viewer.core().animation_max_fps = 30.;
-  cout<<
-    "Press '.' to show next weight function."<<endl<<
-    "Press ',' to show previous weight function."<<endl<<
-    "Press [space] to toggle animation."<<endl;
-  viewer.launch();
-  return EXIT_SUCCESS;
-}
+#include <igl/boundary_conditions.h>
+#include <igl/colon.h>
+#include <igl/column_to_quats.h>
+#include <igl/directed_edge_parents.h>
+#include <igl/forward_kinematics.h>
+#include <igl/jet.h>
+#include <igl/lbs_matrix.h>
+#include <igl/deform_skeleton.h>
+#include <igl/readDMAT.h>
+#include <igl/readMESH.h>
+#include <igl/readTGF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/bbw.h>
+
+#include <Eigen/Geometry>
+#include <Eigen/StdVector>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+
+typedef
+  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
+  RotationList;
+
+const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
+int selected = 0;
+Eigen::MatrixXd V,W,U,C,M;
+Eigen::MatrixXi T,F,BE;
+Eigen::VectorXi P;
+RotationList pose;
+double anim_t = 1.0;
+double anim_t_dir = -0.03;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  using namespace std;
+  if(viewer.core().is_animating)
+  {
+    // Interpolate pose and identity
+    RotationList anim_pose(pose.size());
+    for(int e = 0;e<pose.size();e++)
+    {
+      anim_pose[e] = pose[e].slerp(anim_t,Quaterniond::Identity());
+    }
+    // Propagate relative rotations via FK to retrieve absolute transformations
+    RotationList vQ;
+    vector<Vector3d> vT;
+    igl::forward_kinematics(C,BE,P,anim_pose,vQ,vT);
+    const int dim = C.cols();
+    MatrixXd T(BE.rows()*(dim+1),dim);
+    for(int e = 0;e<BE.rows();e++)
+    {
+      Affine3d a = Affine3d::Identity();
+      a.translate(vT[e]);
+      a.rotate(vQ[e]);
+      T.block(e*(dim+1),0,dim+1,dim) =
+        a.matrix().transpose().block(0,0,dim+1,dim);
+    }
+    // Compute deformation via LBS as matrix multiplication
+    U = M*T;
+
+    // Also deform skeleton edges
+    MatrixXd CT;
+    MatrixXi BET;
+    igl::deform_skeleton(C,BE,T,CT,BET);
+
+    viewer.data().set_vertices(U);
+    viewer.data().set_edges(CT,BET,sea_green);
+    viewer.data().compute_normals();
+    anim_t += anim_t_dir;
+    anim_t_dir *= (anim_t>=1.0 || anim_t<=0.0?-1.0:1.0);
+  }
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      break;
+    case '.':
+      selected++;
+      selected = std::min(std::max(selected,0),(int)W.cols()-1);
+      viewer.data().set_data(W.col(selected));
+      break;
+    case ',':
+      selected--;
+      selected = std::min(std::max(selected,0),(int)W.cols()-1);
+      viewer.data().set_data(W.col(selected));
+      break;
+  }
+  return true;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readMESH(TUTORIAL_SHARED_PATH "/hand.mesh",V,T,F);
+  U=V;
+  igl::readTGF(TUTORIAL_SHARED_PATH "/hand.tgf",C,BE);
+  // retrieve parents for forward kinematics
+  igl::directed_edge_parents(BE,P);
+
+  // Read pose as matrix of quaternions per row
+  MatrixXd Q;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/hand-pose.dmat",Q);
+  igl::column_to_quats(Q,pose);
+  assert(pose.size() == BE.rows());
+
+  // List of boundary indices (aka fixed value indices into VV)
+  VectorXi b;
+  // List of boundary conditions of each weight function
+  MatrixXd bc;
+  igl::boundary_conditions(V,T,C,VectorXi(),BE,MatrixXi(),MatrixXi(),b,bc);
+
+  // compute BBW weights matrix
+  igl::BBWData bbw_data;
+  // only a few iterations for sake of demo
+  bbw_data.active_set_params.max_iter = 8;
+  bbw_data.verbosity = 2;
+  if(!igl::bbw(V,T,b,bc,bbw_data,W))
+  {
+    return EXIT_FAILURE;
+  }
+
+  //MatrixXd Vsurf = V.topLeftCorner(F.maxCoeff()+1,V.cols());
+  //MatrixXd Wsurf;
+  //if(!igl::bone_heat(Vsurf,F,C,VectorXi(),BE,MatrixXi(),Wsurf))
+  //{
+  //  return false;
+  //}
+  //W.setConstant(V.rows(),Wsurf.cols(),1);
+  //W.topLeftCorner(Wsurf.rows(),Wsurf.cols()) = Wsurf = Wsurf = Wsurf = Wsurf;
+
+  // Normalize weights to sum to one
+  W  = (W.array().colwise() / W.array().rowwise().sum()).eval();
+  // precompute linear blend skinning matrix
+  igl::lbs_matrix(V,W,M);
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().set_data(W.col(selected));
+  viewer.data().set_edges(C,BE,sea_green);
+  viewer.data().show_lines = false;
+  viewer.data().show_overlay_depth = false;
+  viewer.data().line_width = 1;
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  viewer.core().is_animating = false;
+  viewer.core().animation_max_fps = 30.;
+  cout<<
+    "Press '.' to show next weight function."<<endl<<
+    "Press ',' to show previous weight function."<<endl<<
+    "Press [space] to toggle animation."<<endl;
+  viewer.launch();
+  return EXIT_SUCCESS;
+}

+ 147 - 147
tutorial/404_DualQuaternionSkinning/main.cpp

@@ -1,147 +1,147 @@
-#include <igl/directed_edge_orientations.h>
-#include <igl/directed_edge_parents.h>
-#include <igl/forward_kinematics.h>
-#include <igl/PI.h>
-#include <igl/lbs_matrix.h>
-#include <igl/deform_skeleton.h>
-#include <igl/dqs.h>
-#include <igl/readDMAT.h>
-#include <igl/readOBJ.h>
-#include <igl/readTGF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <Eigen/Geometry>
-#include <Eigen/StdVector>
-#include <vector>
-#include <algorithm>
-#include <iostream>
-
-
-typedef 
-  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
-  RotationList;
-
-const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
-Eigen::MatrixXd V,W,C,U,M;
-Eigen::MatrixXi F,BE;
-Eigen::VectorXi P;
-std::vector<RotationList > poses;
-double anim_t = 0.0;
-double anim_t_dir = 0.015;
-bool use_dqs = false;
-bool recompute = true;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  using namespace std;
-  if(recompute)
-  {
-    // Find pose interval
-    const int begin = (int)floor(anim_t)%poses.size();
-    const int end = (int)(floor(anim_t)+1)%poses.size();
-    const double t = anim_t - floor(anim_t);
-
-    // Interpolate pose and identity
-    RotationList anim_pose(poses[begin].size());
-    for(int e = 0;e<poses[begin].size();e++)
-    {
-      anim_pose[e] = poses[begin][e].slerp(t,poses[end][e]);
-    }
-    // Propagate relative rotations via FK to retrieve absolute transformations
-    RotationList vQ;
-    vector<Vector3d> vT;
-    igl::forward_kinematics(C,BE,P,anim_pose,vQ,vT);
-    const int dim = C.cols();
-    MatrixXd T(BE.rows()*(dim+1),dim);
-    for(int e = 0;e<BE.rows();e++)
-    {
-      Affine3d a = Affine3d::Identity();
-      a.translate(vT[e]);
-      a.rotate(vQ[e]);
-      T.block(e*(dim+1),0,dim+1,dim) =
-        a.matrix().transpose().block(0,0,dim+1,dim);
-    }
-    // Compute deformation via LBS as matrix multiplication
-    if(use_dqs)
-    {
-      igl::dqs(V,W,vQ,vT,U);
-    }else
-    {
-      U = M*T;
-    }
-
-    // Also deform skeleton edges
-    MatrixXd CT;
-    MatrixXi BET;
-    igl::deform_skeleton(C,BE,T,CT,BET);
-    
-    viewer.data().set_vertices(U);
-    viewer.data().set_edges(CT,BET,sea_green);
-    viewer.data().compute_normals();
-    if(viewer.core().is_animating)
-    {
-      anim_t += anim_t_dir;
-    }
-    else
-    {
-      recompute=false;
-    }
-  }
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  recompute = true;
-  switch(key)
-  {
-    case 'D':
-    case 'd':
-      use_dqs = !use_dqs;
-      return true;
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      return true;
-  }
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/arm.obj",V,F);
-  U=V;
-  igl::readTGF(TUTORIAL_SHARED_PATH "/arm.tgf",C,BE);
-  // retrieve parents for forward kinematics
-  igl::directed_edge_parents(BE,P);
-  RotationList rest_pose;
-  igl::directed_edge_orientations(C,BE,rest_pose);
-  poses.resize(4,RotationList(4,Quaterniond::Identity()));
-  // poses[1] // twist
-  const Quaterniond twist(AngleAxisd(igl::PI,Vector3d(1,0,0)));
-  poses[1][2] = rest_pose[2]*twist*rest_pose[2].conjugate();
-  const Quaterniond bend(AngleAxisd(-igl::PI*0.7,Vector3d(0,0,1)));
-  poses[3][2] = rest_pose[2]*bend*rest_pose[2].conjugate();
-
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/arm-weights.dmat",W);
-  igl::lbs_matrix(V,W,M);
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().set_edges(C,BE,sea_green);
-  viewer.data().show_lines = false;
-  viewer.data().show_overlay_depth = false;
-  viewer.data().line_width = 1;
-  viewer.core().trackball_angle.normalize();
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  viewer.core().is_animating = false;
-  viewer.core().camera_zoom = 2.5;
-  viewer.core().animation_max_fps = 30.;
-  cout<<"Press [d] to toggle between LBS and DQS"<<endl<<
-    "Press [space] to toggle animation"<<endl;
-  viewer.launch();
-}
+#include <igl/directed_edge_orientations.h>
+#include <igl/directed_edge_parents.h>
+#include <igl/forward_kinematics.h>
+#include <igl/PI.h>
+#include <igl/lbs_matrix.h>
+#include <igl/deform_skeleton.h>
+#include <igl/dqs.h>
+#include <igl/readDMAT.h>
+#include <igl/readOBJ.h>
+#include <igl/readTGF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <Eigen/Geometry>
+#include <Eigen/StdVector>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+
+typedef 
+  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
+  RotationList;
+
+const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
+Eigen::MatrixXd V,W,C,U,M;
+Eigen::MatrixXi F,BE;
+Eigen::VectorXi P;
+std::vector<RotationList > poses;
+double anim_t = 0.0;
+double anim_t_dir = 0.015;
+bool use_dqs = false;
+bool recompute = true;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  using namespace std;
+  if(recompute)
+  {
+    // Find pose interval
+    const int begin = (int)floor(anim_t)%poses.size();
+    const int end = (int)(floor(anim_t)+1)%poses.size();
+    const double t = anim_t - floor(anim_t);
+
+    // Interpolate pose and identity
+    RotationList anim_pose(poses[begin].size());
+    for(int e = 0;e<poses[begin].size();e++)
+    {
+      anim_pose[e] = poses[begin][e].slerp(t,poses[end][e]);
+    }
+    // Propagate relative rotations via FK to retrieve absolute transformations
+    RotationList vQ;
+    vector<Vector3d> vT;
+    igl::forward_kinematics(C,BE,P,anim_pose,vQ,vT);
+    const int dim = C.cols();
+    MatrixXd T(BE.rows()*(dim+1),dim);
+    for(int e = 0;e<BE.rows();e++)
+    {
+      Affine3d a = Affine3d::Identity();
+      a.translate(vT[e]);
+      a.rotate(vQ[e]);
+      T.block(e*(dim+1),0,dim+1,dim) =
+        a.matrix().transpose().block(0,0,dim+1,dim);
+    }
+    // Compute deformation via LBS as matrix multiplication
+    if(use_dqs)
+    {
+      igl::dqs(V,W,vQ,vT,U);
+    }else
+    {
+      U = M*T;
+    }
+
+    // Also deform skeleton edges
+    MatrixXd CT;
+    MatrixXi BET;
+    igl::deform_skeleton(C,BE,T,CT,BET);
+    
+    viewer.data().set_vertices(U);
+    viewer.data().set_edges(CT,BET,sea_green);
+    viewer.data().compute_normals();
+    if(viewer.core().is_animating)
+    {
+      anim_t += anim_t_dir;
+    }
+    else
+    {
+      recompute=false;
+    }
+  }
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  recompute = true;
+  switch(key)
+  {
+    case 'D':
+    case 'd':
+      use_dqs = !use_dqs;
+      return true;
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      return true;
+  }
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/arm.obj",V,F);
+  U=V;
+  igl::readTGF(TUTORIAL_SHARED_PATH "/arm.tgf",C,BE);
+  // retrieve parents for forward kinematics
+  igl::directed_edge_parents(BE,P);
+  RotationList rest_pose;
+  igl::directed_edge_orientations(C,BE,rest_pose);
+  poses.resize(4,RotationList(4,Quaterniond::Identity()));
+  // poses[1] // twist
+  const Quaterniond twist(AngleAxisd(igl::PI,Vector3d(1,0,0)));
+  poses[1][2] = rest_pose[2]*twist*rest_pose[2].conjugate();
+  const Quaterniond bend(AngleAxisd(-igl::PI*0.7,Vector3d(0,0,1)));
+  poses[3][2] = rest_pose[2]*bend*rest_pose[2].conjugate();
+
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/arm-weights.dmat",W);
+  igl::lbs_matrix(V,W,M);
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().set_edges(C,BE,sea_green);
+  viewer.data().show_lines = false;
+  viewer.data().show_overlay_depth = false;
+  viewer.data().line_width = 1;
+  viewer.core().trackball_angle.normalize();
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  viewer.core().is_animating = false;
+  viewer.core().camera_zoom = 2.5;
+  viewer.core().animation_max_fps = 30.;
+  cout<<"Press [d] to toggle between LBS and DQS"<<endl<<
+    "Press [space] to toggle animation"<<endl;
+  viewer.launch();
+}

+ 134 - 134
tutorial/405_AsRigidAsPossible/main.cpp

@@ -1,134 +1,134 @@
-#include <igl/colon.h>
-#include <igl/directed_edge_orientations.h>
-#include <igl/directed_edge_parents.h>
-#include <igl/forward_kinematics.h>
-#include <igl/PI.h>
-#include <igl/lbs_matrix.h>
-#include <igl/deform_skeleton.h>
-#include <igl/dqs.h>
-#include <igl/readDMAT.h>
-#include <igl/readOFF.h>
-#include <igl/arap.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <Eigen/Geometry>
-#include <Eigen/StdVector>
-#include <vector>
-#include <algorithm>
-#include <iostream>
-
-
-typedef 
-  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
-  RotationList;
-
-const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
-Eigen::MatrixXd V,U;
-Eigen::MatrixXi F;
-Eigen::VectorXi S,b;
-Eigen::RowVector3d mid;
-double anim_t = 0.0;
-double anim_t_dir = 0.03;
-igl::ARAPData arap_data;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  using namespace std;
-    MatrixXd bc(b.size(),V.cols());
-    for(int i = 0;i<b.size();i++)
-    {
-      bc.row(i) = V.row(b(i));
-      switch(S(b(i)))
-      {
-        case 0:
-        {
-          const double r = mid(0)*0.25;
-          bc(i,0) += r*sin(0.5*anim_t*2.*igl::PI);
-          bc(i,1) -= r+r*cos(igl::PI+0.5*anim_t*2.*igl::PI);
-          break;
-        }
-        case 1:
-        {
-          const double r = mid(1)*0.15;
-          bc(i,1) += r+r*cos(igl::PI+0.15*anim_t*2.*igl::PI);
-          bc(i,2) -= r*sin(0.15*anim_t*2.*igl::PI);
-          break;
-        }
-        case 2:
-        {
-          const double r = mid(1)*0.15;
-          bc(i,2) += r+r*cos(igl::PI+0.35*anim_t*2.*igl::PI);
-          bc(i,0) += r*sin(0.35*anim_t*2.*igl::PI);
-          break;
-        }
-        default:
-          break;
-      }
-    }
-    igl::arap_solve(bc,arap_data,U);
-    viewer.data().set_vertices(U);
-    viewer.data().compute_normals();
-  if(viewer.core().is_animating)
-  {
-    anim_t += anim_t_dir;
-  }
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      return true;
-  }
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
-  U=V;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/decimated-knight-selection.dmat",S);
-
-  // vertices in selection
-  igl::colon<int>(0,V.rows()-1,b);
-  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(), 
-   [](int i)->bool{return S(i)>=0;})-b.data());
-  // Centroid
-  mid = 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff());
-  // Precomputation
-  arap_data.max_iter = 100;
-  igl::arap_precomputation(V,F,V.cols(),b,arap_data);
-
-  // Set color based on selection
-  MatrixXd C(F.rows(),3);
-  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
-  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
-  for(int f = 0;f<F.rows();f++)
-  {
-    if( S(F(f,0))>=0 && S(F(f,1))>=0 && S(F(f,2))>=0)
-    {
-      C.row(f) = purple;
-    }else
-    {
-      C.row(f) = gold;
-    }
-  }
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().set_colors(C);
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  viewer.core().is_animating = false;
-  viewer.core().animation_max_fps = 30.;
-  cout<<
-    "Press [space] to toggle animation"<<endl;
-  viewer.launch();
-}
+#include <igl/colon.h>
+#include <igl/directed_edge_orientations.h>
+#include <igl/directed_edge_parents.h>
+#include <igl/forward_kinematics.h>
+#include <igl/PI.h>
+#include <igl/lbs_matrix.h>
+#include <igl/deform_skeleton.h>
+#include <igl/dqs.h>
+#include <igl/readDMAT.h>
+#include <igl/readOFF.h>
+#include <igl/arap.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <Eigen/Geometry>
+#include <Eigen/StdVector>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+
+typedef 
+  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
+  RotationList;
+
+const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
+Eigen::MatrixXd V,U;
+Eigen::MatrixXi F;
+Eigen::VectorXi S,b;
+Eigen::RowVector3d mid;
+double anim_t = 0.0;
+double anim_t_dir = 0.03;
+igl::ARAPData arap_data;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  using namespace std;
+    MatrixXd bc(b.size(),V.cols());
+    for(int i = 0;i<b.size();i++)
+    {
+      bc.row(i) = V.row(b(i));
+      switch(S(b(i)))
+      {
+        case 0:
+        {
+          const double r = mid(0)*0.25;
+          bc(i,0) += r*sin(0.5*anim_t*2.*igl::PI);
+          bc(i,1) -= r+r*cos(igl::PI+0.5*anim_t*2.*igl::PI);
+          break;
+        }
+        case 1:
+        {
+          const double r = mid(1)*0.15;
+          bc(i,1) += r+r*cos(igl::PI+0.15*anim_t*2.*igl::PI);
+          bc(i,2) -= r*sin(0.15*anim_t*2.*igl::PI);
+          break;
+        }
+        case 2:
+        {
+          const double r = mid(1)*0.15;
+          bc(i,2) += r+r*cos(igl::PI+0.35*anim_t*2.*igl::PI);
+          bc(i,0) += r*sin(0.35*anim_t*2.*igl::PI);
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    igl::arap_solve(bc,arap_data,U);
+    viewer.data().set_vertices(U);
+    viewer.data().compute_normals();
+  if(viewer.core().is_animating)
+  {
+    anim_t += anim_t_dir;
+  }
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      return true;
+  }
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F);
+  U=V;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/decimated-knight-selection.dmat",S);
+
+  // vertices in selection
+  igl::colon<int>(0,V.rows()-1,b);
+  b.conservativeResize(stable_partition( b.data(), b.data()+b.size(), 
+   [](int i)->bool{return S(i)>=0;})-b.data());
+  // Centroid
+  mid = 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff());
+  // Precomputation
+  arap_data.max_iter = 100;
+  igl::arap_precomputation(V,F,V.cols(),b,arap_data);
+
+  // Set color based on selection
+  MatrixXd C(F.rows(),3);
+  RowVector3d purple(80.0/255.0,64.0/255.0,255.0/255.0);
+  RowVector3d gold(255.0/255.0,228.0/255.0,58.0/255.0);
+  for(int f = 0;f<F.rows();f++)
+  {
+    if( S(F(f,0))>=0 && S(F(f,1))>=0 && S(F(f,2))>=0)
+    {
+      C.row(f) = purple;
+    }else
+    {
+      C.row(f) = gold;
+    }
+  }
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().set_colors(C);
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  viewer.core().is_animating = false;
+  viewer.core().animation_max_fps = 30.;
+  cout<<
+    "Press [space] to toggle animation"<<endl;
+  viewer.launch();
+}

+ 161 - 161
tutorial/406_FastAutomaticSkinningTransformations/main.cpp

@@ -1,161 +1,161 @@
-#include "precomputation.h"
-
-#include <igl/readDMAT.h>
-#include <igl/readOBJ.h>
-#include <igl/arap.h>
-#include <igl/arap_dof.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <Eigen/Geometry>
-#include <Eigen/StdVector>
-#include <vector>
-#include <algorithm>
-#include <iostream>
-
-
-typedef 
-  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
-  RotationList;
-
-const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
-Eigen::MatrixXd V,U,M;
-Eigen::MatrixXi F;
-Eigen::VectorXi S,b;
-Eigen::MatrixXd L;
-Eigen::RowVector3d mid;
-double anim_t = 0.0;
-double anim_t_dir = 0.03;
-double bbd = 1.0;
-bool resolve = true;
-igl::ARAPData arap_data,arap_grouped_data;
-igl::ArapDOFData<Eigen::MatrixXd,double> arap_dof_data;
-
-enum ModeType
-{
-  MODE_TYPE_ARAP = 0,
-  MODE_TYPE_ARAP_GROUPED = 1,
-  MODE_TYPE_ARAP_DOF = 2,
-  NUM_MODE_TYPES = 4
-} mode = MODE_TYPE_ARAP;
-
-bool pre_draw(igl::opengl::glfw::Viewer & viewer)
-{
-  using namespace Eigen;
-  using namespace std;
-  if(resolve)
-  {
-    MatrixXd bc(b.size(),V.cols());
-    VectorXd Beq(3*b.size());
-    for(int i = 0;i<b.size();i++)
-    {
-      bc.row(i) = V.row(b(i));
-      switch(i%4)
-      {
-        case 2:
-          bc(i,0) += 0.15*bbd*sin(0.5*anim_t);
-          bc(i,1) += 0.15*bbd*(1.-cos(0.5*anim_t));
-          break;
-        case 1:
-          bc(i,1) += 0.10*bbd*sin(1.*anim_t*(i+1));
-          bc(i,2) += 0.10*bbd*(1.-cos(1.*anim_t*(i+1)));
-          break;
-        case 0:
-          bc(i,0) += 0.20*bbd*sin(2.*anim_t*(i+1));
-          break;
-      }
-      Beq(3*i+0) = bc(i,0);
-      Beq(3*i+1) = bc(i,1);
-      Beq(3*i+2) = bc(i,2);
-    }
-    switch(mode)
-    {
-      default:
-        assert("unknown mode");
-      case MODE_TYPE_ARAP:
-        igl::arap_solve(bc,arap_data,U);
-        break;
-      case MODE_TYPE_ARAP_GROUPED:
-        igl::arap_solve(bc,arap_grouped_data,U);
-        break;
-      case MODE_TYPE_ARAP_DOF:
-      {
-        VectorXd L0 = L;
-        arap_dof_update(arap_dof_data,Beq,L0,30,0,L);
-        const auto & Ucol = M*L;
-        U.col(0) = Ucol.block(0*U.rows(),0,U.rows(),1);
-        U.col(1) = Ucol.block(1*U.rows(),0,U.rows(),1);
-        U.col(2) = Ucol.block(2*U.rows(),0,U.rows(),1);
-        break;
-      }
-    }
-    viewer.data().set_vertices(U);
-    viewer.data().set_points(bc,sea_green);
-    viewer.data().compute_normals();
-    if(viewer.core().is_animating)
-    {
-      anim_t += anim_t_dir;
-    }else
-    {
-      resolve = false;
-    }
-  }
-  return false;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    case '0':
-      anim_t = 0;
-      resolve = true;
-      return true;
-    case '.':
-      mode = (ModeType)(((int)mode+1)%((int)NUM_MODE_TYPES-1));
-      resolve = true;
-      return true;
-    case ',':
-      mode = (ModeType)(((int)mode-1)%((int)NUM_MODE_TYPES-1));
-      resolve = true;
-      return true;
-    case ' ':
-      viewer.core().is_animating = !viewer.core().is_animating;
-      if(viewer.core().is_animating)
-      {
-        resolve = true;
-      }
-      return true;
-  }
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
-  U=V;
-  MatrixXd W;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/armadillo-weights.dmat",W);
-
-  precomputation(V,F,W,M,b,L,arap_data,arap_grouped_data,arap_dof_data);
-
-  // bounding box diagonal
-  bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
-
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(U, F);
-  viewer.data().add_points(V(b,Eigen::all),sea_green);
-  viewer.data().show_lines = false;
-  viewer.callback_pre_draw = &pre_draw;
-  viewer.callback_key_down = &key_down;
-  viewer.core().is_animating = false;
-  viewer.core().animation_max_fps = 30.;
-  cout<<
-    "Press [space] to toggle animation."<<endl<<
-    "Press '0' to reset pose."<<endl<<
-    "Press '.' to switch to next deformation method."<<endl<<
-    "Press ',' to switch to previous deformation method."<<endl;
-  viewer.launch();
-}
+#include "precomputation.h"
+
+#include <igl/readDMAT.h>
+#include <igl/readOBJ.h>
+#include <igl/arap.h>
+#include <igl/arap_dof.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <Eigen/Geometry>
+#include <Eigen/StdVector>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+
+typedef 
+  std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> >
+  RotationList;
+
+const Eigen::RowVector3d sea_green(70./255.,252./255.,167./255.);
+Eigen::MatrixXd V,U,M;
+Eigen::MatrixXi F;
+Eigen::VectorXi S,b;
+Eigen::MatrixXd L;
+Eigen::RowVector3d mid;
+double anim_t = 0.0;
+double anim_t_dir = 0.03;
+double bbd = 1.0;
+bool resolve = true;
+igl::ARAPData arap_data,arap_grouped_data;
+igl::ArapDOFData<Eigen::MatrixXd,double> arap_dof_data;
+
+enum ModeType
+{
+  MODE_TYPE_ARAP = 0,
+  MODE_TYPE_ARAP_GROUPED = 1,
+  MODE_TYPE_ARAP_DOF = 2,
+  NUM_MODE_TYPES = 4
+} mode = MODE_TYPE_ARAP;
+
+bool pre_draw(igl::opengl::glfw::Viewer & viewer)
+{
+  using namespace Eigen;
+  using namespace std;
+  if(resolve)
+  {
+    MatrixXd bc(b.size(),V.cols());
+    VectorXd Beq(3*b.size());
+    for(int i = 0;i<b.size();i++)
+    {
+      bc.row(i) = V.row(b(i));
+      switch(i%4)
+      {
+        case 2:
+          bc(i,0) += 0.15*bbd*sin(0.5*anim_t);
+          bc(i,1) += 0.15*bbd*(1.-cos(0.5*anim_t));
+          break;
+        case 1:
+          bc(i,1) += 0.10*bbd*sin(1.*anim_t*(i+1));
+          bc(i,2) += 0.10*bbd*(1.-cos(1.*anim_t*(i+1)));
+          break;
+        case 0:
+          bc(i,0) += 0.20*bbd*sin(2.*anim_t*(i+1));
+          break;
+      }
+      Beq(3*i+0) = bc(i,0);
+      Beq(3*i+1) = bc(i,1);
+      Beq(3*i+2) = bc(i,2);
+    }
+    switch(mode)
+    {
+      default:
+        assert("unknown mode");
+      case MODE_TYPE_ARAP:
+        igl::arap_solve(bc,arap_data,U);
+        break;
+      case MODE_TYPE_ARAP_GROUPED:
+        igl::arap_solve(bc,arap_grouped_data,U);
+        break;
+      case MODE_TYPE_ARAP_DOF:
+      {
+        VectorXd L0 = L;
+        arap_dof_update(arap_dof_data,Beq,L0,30,0,L);
+        const auto & Ucol = M*L;
+        U.col(0) = Ucol.block(0*U.rows(),0,U.rows(),1);
+        U.col(1) = Ucol.block(1*U.rows(),0,U.rows(),1);
+        U.col(2) = Ucol.block(2*U.rows(),0,U.rows(),1);
+        break;
+      }
+    }
+    viewer.data().set_vertices(U);
+    viewer.data().set_points(bc,sea_green);
+    viewer.data().compute_normals();
+    if(viewer.core().is_animating)
+    {
+      anim_t += anim_t_dir;
+    }else
+    {
+      resolve = false;
+    }
+  }
+  return false;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    case '0':
+      anim_t = 0;
+      resolve = true;
+      return true;
+    case '.':
+      mode = (ModeType)(((int)mode+1)%((int)NUM_MODE_TYPES-1));
+      resolve = true;
+      return true;
+    case ',':
+      mode = (ModeType)(((int)mode-1)%((int)NUM_MODE_TYPES-1));
+      resolve = true;
+      return true;
+    case ' ':
+      viewer.core().is_animating = !viewer.core().is_animating;
+      if(viewer.core().is_animating)
+      {
+        resolve = true;
+      }
+      return true;
+  }
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/armadillo.obj",V,F);
+  U=V;
+  MatrixXd W;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/armadillo-weights.dmat",W);
+
+  precomputation(V,F,W,M,b,L,arap_data,arap_grouped_data,arap_dof_data);
+
+  // bounding box diagonal
+  bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
+
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(U, F);
+  viewer.data().add_points(V(b,Eigen::all),sea_green);
+  viewer.data().show_lines = false;
+  viewer.callback_pre_draw = &pre_draw;
+  viewer.callback_key_down = &key_down;
+  viewer.core().is_animating = false;
+  viewer.core().animation_max_fps = 30.;
+  cout<<
+    "Press [space] to toggle animation."<<endl<<
+    "Press '0' to reset pose."<<endl<<
+    "Press '.' to switch to next deformation method."<<endl<<
+    "Press ',' to switch to previous deformation method."<<endl;
+  viewer.launch();
+}

+ 190 - 190
tutorial/407_BiharmonicCoordinates/main.cpp

@@ -1,190 +1,190 @@
-#include <igl/opengl/gl.h>
-#include <igl/arap.h>
-#include <igl/biharmonic_coordinates.h>
-#include <igl/cat.h>
-#include <igl/cotmatrix.h>
-#include <igl/massmatrix.h>
-#include <igl/matrix_to_list.h>
-#include <igl/parula.h>
-#include <igl/point_mesh_squared_distance.h>
-#include <igl/readDMAT.h>
-#include <igl/readMESH.h>
-#include <igl/remove_unreferenced.h>
-#include <igl/writeDMAT.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <Eigen/Sparse>
-#include <iostream>
-#include <queue>
-
-
-struct Mesh
-{
-  Eigen::MatrixXd V,U;
-  Eigen::MatrixXi T,F;
-} low,high,scene;
-
-Eigen::MatrixXd W;
-igl::ARAPData arap_data;
-
-int main(int argc, char * argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  using namespace igl;
-  
-  // read the mesh, if the code is prepared outside of tutorial, the TUTORIAL_SHARED_PATH
-  // should be the data folder
-  if(!readMESH(TUTORIAL_SHARED_PATH "/octopus-low.mesh",low.V,low.T,low.F))
-  {
-    cout<<"failed to load mesh"<<endl;
-  }
-  if(!readMESH(TUTORIAL_SHARED_PATH "/octopus-high.mesh",high.V,high.T,high.F))
-  {
-    cout<<"failed to load mesh"<<endl;
-  }
-
-  // Precomputation
-  {
-    Eigen::VectorXi b;
-    {
-      // this will create a vector from 0 to V.rows()-1 where the gap is 1
-      Eigen::VectorXi J = Eigen::VectorXi::LinSpaced(high.V.rows(),0,high.V.rows()-1);
-      Eigen::VectorXd sqrD;
-      Eigen::MatrixXd _2;
-      cout<<"Finding closest points..."<<endl;
-      // using J which is N by 1 instead of a matrix that represents faces of N by 3
-      // so that we will find the closest vertices istead of closest point on the face
-      // so far the two meshes are not seperated. So what we are really doing here
-      // is computing handles from low resolution and use that for the high resolution one
-      igl::point_mesh_squared_distance(low.V,high.V,J,sqrD,b,_2);
-      assert(sqrD.minCoeff() < 1e-7 && "low.V should exist in high.V");
-    }
-    // force perfect positioning, rather have popping in low-res than high-res.
-    // The correct/elaborate thing to do is express original low.V in terms of
-    // linear interpolation (or extrapolation) via elements in (high.V,high.F)
-
-    // this is to replace the vertices on low resolution
-    // with the vertices in high resolution. b is the list of vertices
-    // corresponding to the indices in high resolution which has closest
-    // distance to the points in low resolution
-    low.V = high.V(b,Eigen::all);
-
-    // list of points --> list of singleton lists
-    std::vector<std::vector<int> > S;
-    // S will hav size of low.V.rows() and each list inside will have 1 element
-    igl::matrix_to_list(b,S);
-    cout<<"Computing weights for "<<b.size()<<
-      " handles at "<<high.V.rows()<<" vertices..."<<endl;
-    // Technically k should equal 3 for smooth interpolation in 3d, but 2 is
-    // faster and looks OK
-    const int k = 2;
-
-    // using all the points in low resolution as handles for the region
-    // it will be too expansive to use all the points in high reolution as handles
-    // but since low and high resembles the same thing, using points in low reesolution
-    // will give you similar performance
-    igl::biharmonic_coordinates(high.V,high.T,S,k,W);
-    cout<<"Reindexing..."<<endl;
-    // Throw away interior tet-vertices, keep weights and indices of boundary
-    VectorXi I,J;
-    igl::remove_unreferenced(high.V.rows(),high.F,I,J);
-    for_each(high.F.data(),high.F.data()+high.F.size(),[&I](int & a){a=I(a);});
-    for_each(b.data(),b.data()+b.size(),[&I](int & a){a=I(a);});
-    high.V = high.V(J,Eigen::all).eval();
-    W = W(J,Eigen::all).eval();
-  }
-
-  // Resize low res (high res will also be resized by affine precision of W)
-  low.V.rowwise() -= low.V.colwise().mean();
-  low.V /= (low.V.maxCoeff()-low.V.minCoeff());
-  low.V.rowwise() += RowVector3d(0,1,0);
-  low.U = low.V;
-  high.U = high.V;
-
-  arap_data.with_dynamics = true;
-  arap_data.max_iter = 10;
-  arap_data.energy = ARAP_ENERGY_TYPE_DEFAULT;
-  arap_data.h = 0.01;
-  arap_data.ym = 0.001;
-  if(!arap_precomputation(low.V,low.T,3,VectorXi(),arap_data))
-  {
-    cerr<<"arap_precomputation failed."<<endl;
-    return EXIT_FAILURE;
-  }
-  // Constant gravitational force
-  Eigen::SparseMatrix<double> M;
-  igl::massmatrix(low.V,low.T,igl::MASSMATRIX_TYPE_DEFAULT,M);
-  const size_t n = low.V.rows();
-  // f = ma
-  arap_data.f_ext =  M * RowVector3d(0,-9.8,0).replicate(n,1);
-  // Random initial velocities to wiggle things
-  arap_data.vel = MatrixXd::Random(n,3);
-  
-  igl::opengl::glfw::Viewer viewer;
-  // Create one huge mesh containing both meshes
-  igl::cat(1,low.U,high.U,scene.U);
-  // need to remap the indices since we cat the V matrices
-  igl::cat(1,low.F,MatrixXi(high.F.array()+low.V.rows()),scene.F);
-  // Color each mesh
-  viewer.data().set_mesh(scene.U,scene.F);
-  MatrixXd C(scene.F.rows(),3);
-  C<<
-    RowVector3d(0.8,0.5,0.2).replicate(low.F.rows(),1),
-    RowVector3d(0.3,0.4,1.0).replicate(high.F.rows(),1);
-  viewer.data().set_colors(C);
-
-  viewer.callback_key_pressed = 
-    [&](igl::opengl::glfw::Viewer & viewer,unsigned int key,int mods)->bool
-  {
-    switch(key)
-    {
-      default: 
-        return false;
-      case ' ':
-        viewer.core().is_animating = !viewer.core().is_animating;
-        return true;
-      case 'r':
-        low.U = low.V;
-        return true;
-    }
-  };
-  viewer.callback_pre_draw = [&](igl::opengl::glfw::Viewer & viewer)->bool
-  {
-    glEnable(GL_CULL_FACE);
-    if(viewer.core().is_animating)
-    {
-      arap_solve(MatrixXd(0,3),arap_data,low.U);
-      for(int v = 0;v<low.U.rows();v++)
-      {
-        // collide with y=0 plane
-        const int y = 1;
-        if(low.U(v,y) < 0)
-        {
-          low.U(v,y) = -low.U(v,y);
-          // ~ coefficient of restitution
-          const double cr = 1.1;
-          arap_data.vel(v,y) = - arap_data.vel(v,y) / cr;
-        }
-      }
-
-      scene.U.block(0,0,low.U.rows(),low.U.cols()) = low.U;
-      high.U = W * (low.U.rowwise() + RowVector3d(1,0,0));
-      scene.U.block(low.U.rows(),0,high.U.rows(),high.U.cols()) = high.U;
-
-      viewer.data().set_vertices(scene.U);
-      viewer.data().compute_normals();
-    }
-    return false;
-  };
-  viewer.data().show_lines = false;
-  viewer.core().is_animating = true;
-  viewer.core().animation_max_fps = 30.;
-  viewer.data().set_face_based(true);
-  cout<<R"(
-[space] to toggle animation
-'r'     to reset positions 
-      )";
-  viewer.core().rotation_type = 
-    igl::opengl::ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP;
-  viewer.launch();
-}
+#include <igl/opengl/gl.h>
+#include <igl/arap.h>
+#include <igl/biharmonic_coordinates.h>
+#include <igl/cat.h>
+#include <igl/cotmatrix.h>
+#include <igl/massmatrix.h>
+#include <igl/matrix_to_list.h>
+#include <igl/parula.h>
+#include <igl/point_mesh_squared_distance.h>
+#include <igl/readDMAT.h>
+#include <igl/readMESH.h>
+#include <igl/remove_unreferenced.h>
+#include <igl/writeDMAT.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+#include <queue>
+
+
+struct Mesh
+{
+  Eigen::MatrixXd V,U;
+  Eigen::MatrixXi T,F;
+} low,high,scene;
+
+Eigen::MatrixXd W;
+igl::ARAPData arap_data;
+
+int main(int argc, char * argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  using namespace igl;
+  
+  // read the mesh, if the code is prepared outside of tutorial, the TUTORIAL_SHARED_PATH
+  // should be the data folder
+  if(!readMESH(TUTORIAL_SHARED_PATH "/octopus-low.mesh",low.V,low.T,low.F))
+  {
+    cout<<"failed to load mesh"<<endl;
+  }
+  if(!readMESH(TUTORIAL_SHARED_PATH "/octopus-high.mesh",high.V,high.T,high.F))
+  {
+    cout<<"failed to load mesh"<<endl;
+  }
+
+  // Precomputation
+  {
+    Eigen::VectorXi b;
+    {
+      // this will create a vector from 0 to V.rows()-1 where the gap is 1
+      Eigen::VectorXi J = Eigen::VectorXi::LinSpaced(high.V.rows(),0,high.V.rows()-1);
+      Eigen::VectorXd sqrD;
+      Eigen::MatrixXd _2;
+      cout<<"Finding closest points..."<<endl;
+      // using J which is N by 1 instead of a matrix that represents faces of N by 3
+      // so that we will find the closest vertices istead of closest point on the face
+      // so far the two meshes are not seperated. So what we are really doing here
+      // is computing handles from low resolution and use that for the high resolution one
+      igl::point_mesh_squared_distance(low.V,high.V,J,sqrD,b,_2);
+      assert(sqrD.minCoeff() < 1e-7 && "low.V should exist in high.V");
+    }
+    // force perfect positioning, rather have popping in low-res than high-res.
+    // The correct/elaborate thing to do is express original low.V in terms of
+    // linear interpolation (or extrapolation) via elements in (high.V,high.F)
+
+    // this is to replace the vertices on low resolution
+    // with the vertices in high resolution. b is the list of vertices
+    // corresponding to the indices in high resolution which has closest
+    // distance to the points in low resolution
+    low.V = high.V(b,Eigen::all);
+
+    // list of points --> list of singleton lists
+    std::vector<std::vector<int> > S;
+    // S will hav size of low.V.rows() and each list inside will have 1 element
+    igl::matrix_to_list(b,S);
+    cout<<"Computing weights for "<<b.size()<<
+      " handles at "<<high.V.rows()<<" vertices..."<<endl;
+    // Technically k should equal 3 for smooth interpolation in 3d, but 2 is
+    // faster and looks OK
+    const int k = 2;
+
+    // using all the points in low resolution as handles for the region
+    // it will be too expansive to use all the points in high reolution as handles
+    // but since low and high resembles the same thing, using points in low reesolution
+    // will give you similar performance
+    igl::biharmonic_coordinates(high.V,high.T,S,k,W);
+    cout<<"Reindexing..."<<endl;
+    // Throw away interior tet-vertices, keep weights and indices of boundary
+    VectorXi I,J;
+    igl::remove_unreferenced(high.V.rows(),high.F,I,J);
+    for_each(high.F.data(),high.F.data()+high.F.size(),[&I](int & a){a=I(a);});
+    for_each(b.data(),b.data()+b.size(),[&I](int & a){a=I(a);});
+    high.V = high.V(J,Eigen::all).eval();
+    W = W(J,Eigen::all).eval();
+  }
+
+  // Resize low res (high res will also be resized by affine precision of W)
+  low.V.rowwise() -= low.V.colwise().mean();
+  low.V /= (low.V.maxCoeff()-low.V.minCoeff());
+  low.V.rowwise() += RowVector3d(0,1,0);
+  low.U = low.V;
+  high.U = high.V;
+
+  arap_data.with_dynamics = true;
+  arap_data.max_iter = 10;
+  arap_data.energy = ARAP_ENERGY_TYPE_DEFAULT;
+  arap_data.h = 0.01;
+  arap_data.ym = 0.001;
+  if(!arap_precomputation(low.V,low.T,3,VectorXi(),arap_data))
+  {
+    cerr<<"arap_precomputation failed."<<endl;
+    return EXIT_FAILURE;
+  }
+  // Constant gravitational force
+  Eigen::SparseMatrix<double> M;
+  igl::massmatrix(low.V,low.T,igl::MASSMATRIX_TYPE_DEFAULT,M);
+  const size_t n = low.V.rows();
+  // f = ma
+  arap_data.f_ext =  M * RowVector3d(0,-9.8,0).replicate(n,1);
+  // Random initial velocities to wiggle things
+  arap_data.vel = MatrixXd::Random(n,3);
+  
+  igl::opengl::glfw::Viewer viewer;
+  // Create one huge mesh containing both meshes
+  igl::cat(1,low.U,high.U,scene.U);
+  // need to remap the indices since we cat the V matrices
+  igl::cat(1,low.F,MatrixXi(high.F.array()+low.V.rows()),scene.F);
+  // Color each mesh
+  viewer.data().set_mesh(scene.U,scene.F);
+  MatrixXd C(scene.F.rows(),3);
+  C<<
+    RowVector3d(0.8,0.5,0.2).replicate(low.F.rows(),1),
+    RowVector3d(0.3,0.4,1.0).replicate(high.F.rows(),1);
+  viewer.data().set_colors(C);
+
+  viewer.callback_key_pressed = 
+    [&](igl::opengl::glfw::Viewer & viewer,unsigned int key,int mods)->bool
+  {
+    switch(key)
+    {
+      default: 
+        return false;
+      case ' ':
+        viewer.core().is_animating = !viewer.core().is_animating;
+        return true;
+      case 'r':
+        low.U = low.V;
+        return true;
+    }
+  };
+  viewer.callback_pre_draw = [&](igl::opengl::glfw::Viewer & viewer)->bool
+  {
+    glEnable(GL_CULL_FACE);
+    if(viewer.core().is_animating)
+    {
+      arap_solve(MatrixXd(0,3),arap_data,low.U);
+      for(int v = 0;v<low.U.rows();v++)
+      {
+        // collide with y=0 plane
+        const int y = 1;
+        if(low.U(v,y) < 0)
+        {
+          low.U(v,y) = -low.U(v,y);
+          // ~ coefficient of restitution
+          const double cr = 1.1;
+          arap_data.vel(v,y) = - arap_data.vel(v,y) / cr;
+        }
+      }
+
+      scene.U.block(0,0,low.U.rows(),low.U.cols()) = low.U;
+      high.U = W * (low.U.rowwise() + RowVector3d(1,0,0));
+      scene.U.block(low.U.rows(),0,high.U.rows(),high.U.cols()) = high.U;
+
+      viewer.data().set_vertices(scene.U);
+      viewer.data().compute_normals();
+    }
+    return false;
+  };
+  viewer.data().show_lines = false;
+  viewer.core().is_animating = true;
+  viewer.core().animation_max_fps = 30.;
+  viewer.data().set_face_based(true);
+  cout<<R"(
+[space] to toggle animation
+'r'     to reset positions 
+      )";
+  viewer.core().rotation_type = 
+    igl::opengl::ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP;
+  viewer.launch();
+}

+ 64 - 64
tutorial/501_HarmonicParam/main.cpp

@@ -1,64 +1,64 @@
-#include <igl/boundary_loop.h>
-#include <igl/harmonic.h>
-#include <igl/map_vertices_to_circle.h>
-#include <igl/read_triangle_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-
-
-int main(int argc, char *argv[])
-{
-  Eigen::MatrixXd V, V_uv;
-  Eigen::MatrixXi F;
-  // Load a mesh in OFF format
-  igl::read_triangle_mesh(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
-
-  // Find the open boundary
-  Eigen::VectorXi bnd;
-  igl::boundary_loop(F,bnd);
-
-  // Map the boundary to a circle, preserving edge proportions
-  Eigen::MatrixXd bnd_uv;
-  igl::map_vertices_to_circle(V,bnd,bnd_uv);
-
-  // Harmonic parametrization for the internal vertices
-  igl::harmonic(V,F,bnd,bnd_uv,1,V_uv);
-
-  // Scale UV to make the texture more clear
-  V_uv *= 5;
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_uv(V_uv);
-  // Attach callback to allow toggling between 3D and 2D view
-  viewer.callback_key_pressed = 
-    [&V,&V_uv,&F](igl::opengl::glfw::Viewer& viewer, unsigned int key, int /*mod*/)
-  {
-    if(key == '3' || key == '2')
-    {
-      // Plot the 3D mesh or 2D UV coordinates
-      viewer.data().set_vertices(key=='3'?V:V_uv);
-      viewer.data().compute_normals();
-      viewer.core().align_camera_center(key=='3'?V:V_uv,F);
-      // key press was used
-      return true;
-    }
-    // key press not used
-    return false;
-  };
-
-  // Disable wireframe
-  viewer.data().show_lines = false;
-
-  // Draw default checkerboard texture
-  viewer.data().show_texture = true;
-
-  std::cout<<R"(
-3  Show 3D model
-2  Show 2D parametrization
-)";
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/boundary_loop.h>
+#include <igl/harmonic.h>
+#include <igl/map_vertices_to_circle.h>
+#include <igl/read_triangle_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+
+
+int main(int argc, char *argv[])
+{
+  Eigen::MatrixXd V, V_uv;
+  Eigen::MatrixXi F;
+  // Load a mesh in OFF format
+  igl::read_triangle_mesh(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
+
+  // Find the open boundary
+  Eigen::VectorXi bnd;
+  igl::boundary_loop(F,bnd);
+
+  // Map the boundary to a circle, preserving edge proportions
+  Eigen::MatrixXd bnd_uv;
+  igl::map_vertices_to_circle(V,bnd,bnd_uv);
+
+  // Harmonic parametrization for the internal vertices
+  igl::harmonic(V,F,bnd,bnd_uv,1,V_uv);
+
+  // Scale UV to make the texture more clear
+  V_uv *= 5;
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_uv(V_uv);
+  // Attach callback to allow toggling between 3D and 2D view
+  viewer.callback_key_pressed = 
+    [&V,&V_uv,&F](igl::opengl::glfw::Viewer& viewer, unsigned int key, int /*mod*/)
+  {
+    if(key == '3' || key == '2')
+    {
+      // Plot the 3D mesh or 2D UV coordinates
+      viewer.data().set_vertices(key=='3'?V:V_uv);
+      viewer.data().compute_normals();
+      viewer.core().align_camera_center(key=='3'?V:V_uv,F);
+      // key press was used
+      return true;
+    }
+    // key press not used
+    return false;
+  };
+
+  // Disable wireframe
+  viewer.data().show_lines = false;
+
+  // Draw default checkerboard texture
+  viewer.data().show_texture = true;
+
+  std::cout<<R"(
+3  Show 3D model
+2  Show 2D parametrization
+)";
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 56 - 56
tutorial/502_LSCMParam/main.cpp

@@ -1,56 +1,56 @@
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/lscm.h>
-
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-Eigen::MatrixXd V_uv;
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-
-  if (key == '1')
-  {
-    // Plot the 3D mesh
-    viewer.data().set_mesh(V,F);
-    viewer.core().align_camera_center(V,F);
-  }
-  else if (key == '2')
-  {
-    // Plot the mesh in 2D using the UV coordinates as vertex coordinates
-    viewer.data().set_mesh(V_uv,F);
-    viewer.core().align_camera_center(V_uv,F);
-  }
-
-  viewer.data().compute_normals();
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
-
-  // LSCM parametrization
-  igl::lscm(V,F,V_uv);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_uv(V_uv);
-  viewer.callback_key_down = &key_down;
-
-  // Disable wireframe
-  viewer.data().show_lines = false;
-
-  // Draw checkerboard texture
-  viewer.data().show_texture = true;
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/lscm.h>
+
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+Eigen::MatrixXd V_uv;
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+
+  if (key == '1')
+  {
+    // Plot the 3D mesh
+    viewer.data().set_mesh(V,F);
+    viewer.core().align_camera_center(V,F);
+  }
+  else if (key == '2')
+  {
+    // Plot the mesh in 2D using the UV coordinates as vertex coordinates
+    viewer.data().set_mesh(V_uv,F);
+    viewer.core().align_camera_center(V_uv,F);
+  }
+
+  viewer.data().compute_normals();
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
+
+  // LSCM parametrization
+  igl::lscm(V,F,V_uv);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_uv(V_uv);
+  viewer.callback_key_down = &key_down;
+
+  // Disable wireframe
+  viewer.data().show_lines = false;
+
+  // Draw checkerboard texture
+  viewer.data().show_texture = true;
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 91 - 91
tutorial/503_ARAPParam/main.cpp

@@ -1,91 +1,91 @@
-#include <igl/arap.h>
-#include <igl/boundary_loop.h>
-#include <igl/harmonic.h>
-#include <igl/map_vertices_to_circle.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-Eigen::MatrixXd V_uv;
-Eigen::MatrixXd initial_guess;
-
-bool show_uv = false;
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  if (key == '1')
-    show_uv = false;
-  else if (key == '2')
-    show_uv = true;
-
-  if (key == 'q')
-    V_uv = initial_guess;
-
-  if (show_uv)
-  {
-    viewer.data().set_mesh(V_uv,F);
-    viewer.core().align_camera_center(V_uv,F);
-  }
-  else
-  {
-    viewer.data().set_mesh(V,F);
-    viewer.core().align_camera_center(V,F);
-  }
-
-  viewer.data().compute_normals();
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace std;
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
-
-  // Compute the initial solution for ARAP (harmonic parametrization)
-  Eigen::VectorXi bnd;
-  igl::boundary_loop(F,bnd);
-  Eigen::MatrixXd bnd_uv;
-  igl::map_vertices_to_circle(V,bnd,bnd_uv);
-
-  igl::harmonic(V,F,bnd,bnd_uv,1,initial_guess);
-
-  // Add dynamic regularization to avoid to specify boundary conditions
-  igl::ARAPData arap_data;
-  arap_data.with_dynamics = true;
-  Eigen::VectorXi b  = Eigen::VectorXi::Zero(0);
-  Eigen::MatrixXd bc = Eigen::MatrixXd::Zero(0,0);
-
-  // Initialize ARAP
-  arap_data.max_iter = 100;
-  // 2 means that we're going to *solve* in 2d
-  arap_precomputation(V,F,2,b,arap_data);
-
-
-  // Solve arap using the harmonic map as initial guess
-  V_uv = initial_guess;
-
-  arap_solve(bc,arap_data,V_uv);
-
-
-  // Scale UV to make the texture more clear
-  V_uv *= 20;
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_uv(V_uv);
-  viewer.callback_key_down = &key_down;
-
-  // Disable wireframe
-  viewer.data().show_lines = false;
-
-  // Draw checkerboard texture
-  viewer.data().show_texture = true;
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/arap.h>
+#include <igl/boundary_loop.h>
+#include <igl/harmonic.h>
+#include <igl/map_vertices_to_circle.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+Eigen::MatrixXd V_uv;
+Eigen::MatrixXd initial_guess;
+
+bool show_uv = false;
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  if (key == '1')
+    show_uv = false;
+  else if (key == '2')
+    show_uv = true;
+
+  if (key == 'q')
+    V_uv = initial_guess;
+
+  if (show_uv)
+  {
+    viewer.data().set_mesh(V_uv,F);
+    viewer.core().align_camera_center(V_uv,F);
+  }
+  else
+  {
+    viewer.data().set_mesh(V,F);
+    viewer.core().align_camera_center(V,F);
+  }
+
+  viewer.data().compute_normals();
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace std;
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
+
+  // Compute the initial solution for ARAP (harmonic parametrization)
+  Eigen::VectorXi bnd;
+  igl::boundary_loop(F,bnd);
+  Eigen::MatrixXd bnd_uv;
+  igl::map_vertices_to_circle(V,bnd,bnd_uv);
+
+  igl::harmonic(V,F,bnd,bnd_uv,1,initial_guess);
+
+  // Add dynamic regularization to avoid to specify boundary conditions
+  igl::ARAPData arap_data;
+  arap_data.with_dynamics = true;
+  Eigen::VectorXi b  = Eigen::VectorXi::Zero(0);
+  Eigen::MatrixXd bc = Eigen::MatrixXd::Zero(0,0);
+
+  // Initialize ARAP
+  arap_data.max_iter = 100;
+  // 2 means that we're going to *solve* in 2d
+  arap_precomputation(V,F,2,b,arap_data);
+
+
+  // Solve arap using the harmonic map as initial guess
+  V_uv = initial_guess;
+
+  arap_solve(bc,arap_data,V_uv);
+
+
+  // Scale UV to make the texture more clear
+  V_uv *= 20;
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_uv(V_uv);
+  viewer.callback_key_down = &key_down;
+
+  // Disable wireframe
+  viewer.data().show_lines = false;
+
+  // Draw checkerboard texture
+  viewer.data().show_texture = true;
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 148 - 148
tutorial/504_NRosyDesign/main.cpp

@@ -1,148 +1,148 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/local_basis.h>
-#include <igl/readOFF.h>
-#include <igl/copyleft/comiso/nrosy.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/PI.h>
-
-
-// Mesh
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-// Constrained faces id
-Eigen::VectorXi b;
-
-// Cosntrained faces representative vector
-Eigen::MatrixXd bc;
-
-// Degree of the N-RoSy field
-int N = 4;
-
-// Converts a representative vector per face in the full set of vectors that describe
-// an N-RoSy field
-void representative_to_nrosy(
-  const Eigen::MatrixXd& V,
-  const Eigen::MatrixXi& F,
-  const Eigen::MatrixXd& R,
-  const int N,
-  Eigen::MatrixXd& Y)
-{
-  using namespace Eigen;
-  using namespace std;
-  MatrixXd B1, B2, B3;
-
-  igl::local_basis(V,F,B1,B2,B3);
-
-  Y.resize(F.rows()*N,3);
-  for (unsigned i=0;i<F.rows();++i)
-  {
-    double x = R.row(i) * B1.row(i).transpose();
-    double y = R.row(i) * B2.row(i).transpose();
-    double angle = atan2(y,x);
-
-    for (unsigned j=0; j<N;++j)
-    {
-      double anglej = angle + 2*igl::PI*double(j)/double(N);
-      double xj = cos(anglej);
-      double yj = sin(anglej);
-      Y.row(i*N+j) = xj * B1.row(i) + yj * B2.row(i);
-    }
-  }
-}
-
-// Plots the mesh with an N-RoSy field and its singularities on top
-// The constrained faces (b) are colored in red.
-void plot_mesh_nrosy(
-  igl::opengl::glfw::Viewer& viewer,
-  Eigen::MatrixXd& V,
-  Eigen::MatrixXi& F,
-  int N,
-  Eigen::MatrixXd& PD1,
-  Eigen::VectorXd& S,
-  Eigen::VectorXi& b)
-{
-  using namespace Eigen;
-  using namespace std;
-  // Clear the mesh
-  viewer.data().clear();
-  viewer.data().set_mesh(V,F);
-
-  // Expand the representative vectors in the full vector set and plot them as lines
-  double avg = igl::avg_edge_length(V, F);
-  MatrixXd Y;
-  representative_to_nrosy(V, F, PD1, N, Y);
-
-  MatrixXd B;
-  igl::barycenter(V,F,B);
-
-  MatrixXd Be(B.rows()*N,3);
-  for(unsigned i=0; i<B.rows();++i)
-    for(unsigned j=0; j<N; ++j)
-      Be.row(i*N+j) = B.row(i);
-
-  viewer.data().add_edges(Be,Be+Y*(avg/2),RowVector3d(0,0,1));
-
-  // Plot the singularities as colored dots (red for positive, blue for negative)
-  for (unsigned i=0; i<S.size();++i)
-  {
-    if (S(i) < -0.001)
-      viewer.data().add_points(V.row(i),RowVector3d(0,0,1));
-    else if (S(i) > 0.001)
-      viewer.data().add_points(V.row(i),RowVector3d(1,0,0));
-  }
-
-  // Highlight in red the constrained faces
-  MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
-  for (unsigned i=0; i<b.size();++i)
-    C.row(b(i)) << 1, 0, 0;
-  viewer.data().set_colors(C);
-}
-
-  // It allows to change the degree of the field when a number is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace Eigen;
-  using namespace std;
-  if (key >= '1' && key <= '9')
-    N = key - '0';
-
-  MatrixXd R;
-  VectorXd S;
-
-  igl::copyleft::comiso::nrosy(V,F,b,bc,VectorXi(),VectorXd(),MatrixXd(),N,0.5,R,S);
-  plot_mesh_nrosy(viewer,V,F,N,R,S,b);
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace std;
-  using namespace Eigen;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", V, F);
-
-  // Threshold faces with high anisotropy
-  b.resize(1);
-  b << 0;
-  bc.resize(1,3);
-  bc << 1,1,1;
-
-  igl::opengl::glfw::Viewer viewer;
-
-  // Interpolate the field and plot
-  key_down(viewer, '4', 0);
-
-  // Plot the mesh
-  viewer.data().set_mesh(V, F);
-  viewer.callback_key_down = &key_down;
-
-  // Disable wireframe
-  viewer.data().show_lines = false;
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/local_basis.h>
+#include <igl/readOFF.h>
+#include <igl/copyleft/comiso/nrosy.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/PI.h>
+
+
+// Mesh
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+// Constrained faces id
+Eigen::VectorXi b;
+
+// Cosntrained faces representative vector
+Eigen::MatrixXd bc;
+
+// Degree of the N-RoSy field
+int N = 4;
+
+// Converts a representative vector per face in the full set of vectors that describe
+// an N-RoSy field
+void representative_to_nrosy(
+  const Eigen::MatrixXd& V,
+  const Eigen::MatrixXi& F,
+  const Eigen::MatrixXd& R,
+  const int N,
+  Eigen::MatrixXd& Y)
+{
+  using namespace Eigen;
+  using namespace std;
+  MatrixXd B1, B2, B3;
+
+  igl::local_basis(V,F,B1,B2,B3);
+
+  Y.resize(F.rows()*N,3);
+  for (unsigned i=0;i<F.rows();++i)
+  {
+    double x = R.row(i) * B1.row(i).transpose();
+    double y = R.row(i) * B2.row(i).transpose();
+    double angle = atan2(y,x);
+
+    for (unsigned j=0; j<N;++j)
+    {
+      double anglej = angle + 2*igl::PI*double(j)/double(N);
+      double xj = cos(anglej);
+      double yj = sin(anglej);
+      Y.row(i*N+j) = xj * B1.row(i) + yj * B2.row(i);
+    }
+  }
+}
+
+// Plots the mesh with an N-RoSy field and its singularities on top
+// The constrained faces (b) are colored in red.
+void plot_mesh_nrosy(
+  igl::opengl::glfw::Viewer& viewer,
+  Eigen::MatrixXd& V,
+  Eigen::MatrixXi& F,
+  int N,
+  Eigen::MatrixXd& PD1,
+  Eigen::VectorXd& S,
+  Eigen::VectorXi& b)
+{
+  using namespace Eigen;
+  using namespace std;
+  // Clear the mesh
+  viewer.data().clear();
+  viewer.data().set_mesh(V,F);
+
+  // Expand the representative vectors in the full vector set and plot them as lines
+  double avg = igl::avg_edge_length(V, F);
+  MatrixXd Y;
+  representative_to_nrosy(V, F, PD1, N, Y);
+
+  MatrixXd B;
+  igl::barycenter(V,F,B);
+
+  MatrixXd Be(B.rows()*N,3);
+  for(unsigned i=0; i<B.rows();++i)
+    for(unsigned j=0; j<N; ++j)
+      Be.row(i*N+j) = B.row(i);
+
+  viewer.data().add_edges(Be,Be+Y*(avg/2),RowVector3d(0,0,1));
+
+  // Plot the singularities as colored dots (red for positive, blue for negative)
+  for (unsigned i=0; i<S.size();++i)
+  {
+    if (S(i) < -0.001)
+      viewer.data().add_points(V.row(i),RowVector3d(0,0,1));
+    else if (S(i) > 0.001)
+      viewer.data().add_points(V.row(i),RowVector3d(1,0,0));
+  }
+
+  // Highlight in red the constrained faces
+  MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
+  for (unsigned i=0; i<b.size();++i)
+    C.row(b(i)) << 1, 0, 0;
+  viewer.data().set_colors(C);
+}
+
+  // It allows to change the degree of the field when a number is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace Eigen;
+  using namespace std;
+  if (key >= '1' && key <= '9')
+    N = key - '0';
+
+  MatrixXd R;
+  VectorXd S;
+
+  igl::copyleft::comiso::nrosy(V,F,b,bc,VectorXi(),VectorXd(),MatrixXd(),N,0.5,R,S);
+  plot_mesh_nrosy(viewer,V,F,N,R,S,b);
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace std;
+  using namespace Eigen;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", V, F);
+
+  // Threshold faces with high anisotropy
+  b.resize(1);
+  b << 0;
+  bc.resize(1,3);
+  bc << 1,1,1;
+
+  igl::opengl::glfw::Viewer viewer;
+
+  // Interpolate the field and plot
+  key_down(viewer, '4', 0);
+
+  // Plot the mesh
+  viewer.data().set_mesh(V, F);
+  viewer.callback_key_down = &key_down;
+
+  // Disable wireframe
+  viewer.data().show_lines = false;
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 325 - 325
tutorial/505_MIQ/main.cpp

@@ -1,325 +1,325 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/comb_cross_field.h>
-#include <igl/comb_frame_field.h>
-#include <igl/compute_frame_field_bisectors.h>
-#include <igl/cross_field_mismatch.h>
-#include <igl/cut_mesh_from_singularities.h>
-#include <igl/find_cross_field_singularities.h>
-#include <igl/local_basis.h>
-#include <igl/readOFF.h>
-#include <igl/rotate_vectors.h>
-#include <igl/copyleft/comiso/miq.h>
-#include <igl/copyleft/comiso/nrosy.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/PI.h>
-#include <sstream>
-
-#include <igl/serialize.h>
-
-// Input mesh
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-
-// Face barycenters
-Eigen::MatrixXd B;
-
-// Scale for visualizing the fields
-double global_scale;
-bool extend_arrows = false;
-
-// Cross field
-Eigen::MatrixXd X1,X2;
-
-// Bisector field
-Eigen::MatrixXd BIS1, BIS2;
-
-// Combed bisector
-Eigen::MatrixXd BIS1_combed, BIS2_combed;
-
-// Per-corner, integer mismatches
-Eigen::Matrix<int, Eigen::Dynamic, 3> MMatch;
-
-// Field singularities
-Eigen::Matrix<int, Eigen::Dynamic, 1> isSingularity, singularityIndex;
-
-// Per corner seams
-Eigen::Matrix<int, Eigen::Dynamic, 3> Seams;
-
-// Combed field
-Eigen::MatrixXd X1_combed, X2_combed;
-
-
-// Global parametrization (with seams)
-Eigen::MatrixXd UV_seams;
-Eigen::MatrixXi FUV_seams;
-
-// Global parametrization
-Eigen::MatrixXd UV;
-Eigen::MatrixXi FUV;
-
-
-// Create a texture that hides the integer translation in the parametrization
-void line_texture(Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_R,
-                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_G,
-                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_B)
-  {
-    unsigned size = 128;
-    unsigned size2 = size/2;
-    unsigned lineWidth = 3;
-    texture_R.setConstant(size, size, 255);
-    for (unsigned i=0; i<size; ++i)
-      for (unsigned j=size2-lineWidth; j<=size2+lineWidth; ++j)
-        texture_R(i,j) = 0;
-    for (unsigned i=size2-lineWidth; i<=size2+lineWidth; ++i)
-      for (unsigned j=0; j<size; ++j)
-        texture_R(i,j) = 0;
-
-    texture_G = texture_R;
-    texture_B = texture_R;
-  }
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  if (key == 'E')
-  {
-    extend_arrows = !extend_arrows;
-  }
-
-  if (key <'1' || key >'8')
-    return false;
-
-  viewer.data().clear();
-  viewer.data().show_lines = false;
-  viewer.data().show_texture = false;
-
-  if (key == '1')
-  {
-    // Cross field
-    viewer.data().set_mesh(V, F);
-    viewer.data().add_edges(extend_arrows ? B - global_scale*X1 : B, B + global_scale*X1 ,Eigen::RowVector3d(1,0,0));
-    viewer.data().add_edges(extend_arrows ? B - global_scale*X2 : B, B + global_scale*X2 ,Eigen::RowVector3d(0,0,1));
-  }
-
-  if (key == '2')
-  {
-    // Bisector field
-    viewer.data().set_mesh(V, F);
-    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS1 : B, B + global_scale*BIS1 ,Eigen::RowVector3d(1,0,0));
-    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS2 : B, B + global_scale*BIS2 ,Eigen::RowVector3d(0,0,1));
-  }
-
-  if (key == '3')
-  {
-    // Bisector field combed
-    viewer.data().set_mesh(V, F);
-    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS1_combed : B, B + global_scale*BIS1_combed ,Eigen::RowVector3d(1,0,0));
-    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS2_combed : B, B + global_scale*BIS2_combed ,Eigen::RowVector3d(0,0,1));
-  }
-
-  if (key == '4')
-  {
-    // Singularities and cuts
-    viewer.data().set_mesh(V, F);
-
-    // Plot cuts
-    int l_count = Seams.sum();
-    Eigen::MatrixXd P1(l_count,3);
-    Eigen::MatrixXd P2(l_count,3);
-
-    for (unsigned i=0; i<Seams.rows(); ++i)
-    {
-      for (unsigned j=0; j<Seams.cols(); ++j)
-      {
-        if (Seams(i,j) != 0)
-        {
-          P1.row(l_count-1) = V.row(F(i,j));
-          P2.row(l_count-1) = V.row(F(i,(j+1)%3));
-          l_count--;
-        }
-      }
-    }
-
-    viewer.data().add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0));
-
-    // Plot the singularities as colored dots (red for negative, blue for positive)
-    for (unsigned i=0; i<singularityIndex.size();++i)
-    {
-      if (singularityIndex(i) < 2 && singularityIndex(i) > 0)
-        viewer.data().add_points(V.row(i),Eigen::RowVector3d(1,0,0));
-      else if (singularityIndex(i) > 2)
-        viewer.data().add_points(V.row(i),Eigen::RowVector3d(0,1,0));
-    }
-
-  }
-
-  if (key == '5')
-  {
-    // Singularities and cuts, original field
-    // Singularities and cuts
-    viewer.data().set_mesh(V, F);
-    viewer.data().add_edges(extend_arrows ? B - global_scale*X1_combed : B, B + global_scale*X1_combed ,Eigen::RowVector3d(1,0,0));
-    viewer.data().add_edges(extend_arrows ? B - global_scale*X2_combed : B, B + global_scale*X2_combed ,Eigen::RowVector3d(0,0,1));
-
-    // Plot cuts
-    int l_count = Seams.sum();
-    Eigen::MatrixXd P1(l_count,3);
-    Eigen::MatrixXd P2(l_count,3);
-
-    for (unsigned i=0; i<Seams.rows(); ++i)
-    {
-      for (unsigned j=0; j<Seams.cols(); ++j)
-      {
-        if (Seams(i,j) != 0)
-        {
-          P1.row(l_count-1) = V.row(F(i,j));
-          P2.row(l_count-1) = V.row(F(i,(j+1)%3));
-          l_count--;
-        }
-      }
-    }
-
-    viewer.data().add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0));
-
-    // Plot the singularities as colored dots (red for negative, blue for positive)
-    for (unsigned i=0; i<singularityIndex.size();++i)
-    {
-      if (singularityIndex(i) < 2 && singularityIndex(i) > 0)
-        viewer.data().add_points(V.row(i),Eigen::RowVector3d(1,0,0));
-      else if (singularityIndex(i) > 2)
-        viewer.data().add_points(V.row(i),Eigen::RowVector3d(0,1,0));
-    }
-  }
-
-  if (key == '6')
-  {
-    // Global parametrization UV
-    viewer.data().set_mesh(UV, FUV);
-    viewer.data().set_uv(UV);
-    viewer.data().show_lines = true;
-  }
-
-  if (key == '7')
-  {
-    // Global parametrization in 3D
-    viewer.data().set_mesh(V, F);
-    viewer.data().set_uv(UV,FUV);
-    viewer.data().show_texture = true;
-  }
-
-  if (key == '8')
-  {
-    // Global parametrization in 3D with seams
-    viewer.data().set_mesh(V, F);
-    viewer.data().set_uv(UV_seams,FUV_seams);
-    viewer.data().show_texture = true;
-  }
-
-  viewer.data().set_colors(Eigen::RowVector3d(1,1,1));
-
-  // Replace the standard texture with an integer shift invariant texture
-  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R, texture_G, texture_B;
-  line_texture(texture_R, texture_G, texture_B);
-  viewer.data().set_texture(texture_R, texture_B, texture_G);
-
-  viewer.core().align_camera_center(viewer.data().V,viewer.data().F);
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/3holes.off", V, F);
-
-  double gradient_size = 50;
-  double iter = 0;
-  double stiffness = 5.0;
-  bool direct_round = 0;
-
-  // Compute face barycenters
-  igl::barycenter(V, F, B);
-
-  // Compute scale for visualizing fields
-  global_scale =  .5*igl::avg_edge_length(V, F);
-
-    // Contrain one face
-    VectorXi b(1);
-    b << 0;
-    MatrixXd bc(1, 3);
-    bc << 1, 0, 0;
-
-    // Create a smooth 4-RoSy field
-    VectorXd S;
-    igl::copyleft::comiso::nrosy(V, F, b, bc, VectorXi(), VectorXd(), MatrixXd(), 4, 0.5, X1, S);
-
-    // Find the orthogonal vector
-    MatrixXd B1, B2, B3;
-    igl::local_basis(V, F, B1, B2, B3);
-    X2 = igl::rotate_vectors(X1, VectorXd::Constant(1, igl::PI / 2), B1, B2);
-
-    // Always work on the bisectors, it is more general
-    igl::compute_frame_field_bisectors(V, F, X1, X2, BIS1, BIS2);
-
-    // Comb the field, implicitly defining the seams
-    igl::comb_cross_field(V, F, BIS1, BIS2, BIS1_combed, BIS2_combed);
-
-    // Find the integer mismatches
-    igl::cross_field_mismatch(V, F, BIS1_combed, BIS2_combed, true, MMatch);
-
-    // Find the singularities
-    igl::find_cross_field_singularities(V, F, MMatch, isSingularity, singularityIndex);
-
-    // Cut the mesh, duplicating all vertices on the seams
-    igl::cut_mesh_from_singularities(V, F, MMatch, Seams);
-
-    // Comb the frame-field accordingly
-    igl::comb_frame_field(V, F, X1, X2, BIS1_combed, BIS2_combed, X1_combed, X2_combed);
-
-  // Global parametrization
-  igl::copyleft::comiso::miq(V,
-           F,
-           X1_combed,
-           X2_combed,
-           MMatch,
-           isSingularity,
-           Seams,
-           UV,
-           FUV,
-           gradient_size,
-           stiffness,
-           direct_round,
-           iter,
-           5,
-           true);
-
-// Global parametrization (with seams, only for demonstration)
-igl::copyleft::comiso::miq(V,
-         F,
-         X1_combed,
-         X2_combed,
-         MMatch,
-         isSingularity,
-         Seams,
-         UV_seams,
-         FUV_seams,
-         gradient_size,
-         stiffness,
-         direct_round,
-         iter,
-         5,
-         false);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-
-  // Plot the original mesh with a texture parametrization
-  key_down(viewer,'7',0);
-
-  // Launch the viewer
-  viewer.callback_key_down = &key_down;
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/comb_cross_field.h>
+#include <igl/comb_frame_field.h>
+#include <igl/compute_frame_field_bisectors.h>
+#include <igl/cross_field_mismatch.h>
+#include <igl/cut_mesh_from_singularities.h>
+#include <igl/find_cross_field_singularities.h>
+#include <igl/local_basis.h>
+#include <igl/readOFF.h>
+#include <igl/rotate_vectors.h>
+#include <igl/copyleft/comiso/miq.h>
+#include <igl/copyleft/comiso/nrosy.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/PI.h>
+#include <sstream>
+
+#include <igl/serialize.h>
+
+// Input mesh
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+
+// Face barycenters
+Eigen::MatrixXd B;
+
+// Scale for visualizing the fields
+double global_scale;
+bool extend_arrows = false;
+
+// Cross field
+Eigen::MatrixXd X1,X2;
+
+// Bisector field
+Eigen::MatrixXd BIS1, BIS2;
+
+// Combed bisector
+Eigen::MatrixXd BIS1_combed, BIS2_combed;
+
+// Per-corner, integer mismatches
+Eigen::Matrix<int, Eigen::Dynamic, 3> MMatch;
+
+// Field singularities
+Eigen::Matrix<int, Eigen::Dynamic, 1> isSingularity, singularityIndex;
+
+// Per corner seams
+Eigen::Matrix<int, Eigen::Dynamic, 3> Seams;
+
+// Combed field
+Eigen::MatrixXd X1_combed, X2_combed;
+
+
+// Global parametrization (with seams)
+Eigen::MatrixXd UV_seams;
+Eigen::MatrixXi FUV_seams;
+
+// Global parametrization
+Eigen::MatrixXd UV;
+Eigen::MatrixXi FUV;
+
+
+// Create a texture that hides the integer translation in the parametrization
+void line_texture(Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_R,
+                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_G,
+                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_B)
+  {
+    unsigned size = 128;
+    unsigned size2 = size/2;
+    unsigned lineWidth = 3;
+    texture_R.setConstant(size, size, 255);
+    for (unsigned i=0; i<size; ++i)
+      for (unsigned j=size2-lineWidth; j<=size2+lineWidth; ++j)
+        texture_R(i,j) = 0;
+    for (unsigned i=size2-lineWidth; i<=size2+lineWidth; ++i)
+      for (unsigned j=0; j<size; ++j)
+        texture_R(i,j) = 0;
+
+    texture_G = texture_R;
+    texture_B = texture_R;
+  }
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  if (key == 'E')
+  {
+    extend_arrows = !extend_arrows;
+  }
+
+  if (key <'1' || key >'8')
+    return false;
+
+  viewer.data().clear();
+  viewer.data().show_lines = false;
+  viewer.data().show_texture = false;
+
+  if (key == '1')
+  {
+    // Cross field
+    viewer.data().set_mesh(V, F);
+    viewer.data().add_edges(extend_arrows ? B - global_scale*X1 : B, B + global_scale*X1 ,Eigen::RowVector3d(1,0,0));
+    viewer.data().add_edges(extend_arrows ? B - global_scale*X2 : B, B + global_scale*X2 ,Eigen::RowVector3d(0,0,1));
+  }
+
+  if (key == '2')
+  {
+    // Bisector field
+    viewer.data().set_mesh(V, F);
+    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS1 : B, B + global_scale*BIS1 ,Eigen::RowVector3d(1,0,0));
+    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS2 : B, B + global_scale*BIS2 ,Eigen::RowVector3d(0,0,1));
+  }
+
+  if (key == '3')
+  {
+    // Bisector field combed
+    viewer.data().set_mesh(V, F);
+    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS1_combed : B, B + global_scale*BIS1_combed ,Eigen::RowVector3d(1,0,0));
+    viewer.data().add_edges(extend_arrows ? B - global_scale*BIS2_combed : B, B + global_scale*BIS2_combed ,Eigen::RowVector3d(0,0,1));
+  }
+
+  if (key == '4')
+  {
+    // Singularities and cuts
+    viewer.data().set_mesh(V, F);
+
+    // Plot cuts
+    int l_count = Seams.sum();
+    Eigen::MatrixXd P1(l_count,3);
+    Eigen::MatrixXd P2(l_count,3);
+
+    for (unsigned i=0; i<Seams.rows(); ++i)
+    {
+      for (unsigned j=0; j<Seams.cols(); ++j)
+      {
+        if (Seams(i,j) != 0)
+        {
+          P1.row(l_count-1) = V.row(F(i,j));
+          P2.row(l_count-1) = V.row(F(i,(j+1)%3));
+          l_count--;
+        }
+      }
+    }
+
+    viewer.data().add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0));
+
+    // Plot the singularities as colored dots (red for negative, blue for positive)
+    for (unsigned i=0; i<singularityIndex.size();++i)
+    {
+      if (singularityIndex(i) < 2 && singularityIndex(i) > 0)
+        viewer.data().add_points(V.row(i),Eigen::RowVector3d(1,0,0));
+      else if (singularityIndex(i) > 2)
+        viewer.data().add_points(V.row(i),Eigen::RowVector3d(0,1,0));
+    }
+
+  }
+
+  if (key == '5')
+  {
+    // Singularities and cuts, original field
+    // Singularities and cuts
+    viewer.data().set_mesh(V, F);
+    viewer.data().add_edges(extend_arrows ? B - global_scale*X1_combed : B, B + global_scale*X1_combed ,Eigen::RowVector3d(1,0,0));
+    viewer.data().add_edges(extend_arrows ? B - global_scale*X2_combed : B, B + global_scale*X2_combed ,Eigen::RowVector3d(0,0,1));
+
+    // Plot cuts
+    int l_count = Seams.sum();
+    Eigen::MatrixXd P1(l_count,3);
+    Eigen::MatrixXd P2(l_count,3);
+
+    for (unsigned i=0; i<Seams.rows(); ++i)
+    {
+      for (unsigned j=0; j<Seams.cols(); ++j)
+      {
+        if (Seams(i,j) != 0)
+        {
+          P1.row(l_count-1) = V.row(F(i,j));
+          P2.row(l_count-1) = V.row(F(i,(j+1)%3));
+          l_count--;
+        }
+      }
+    }
+
+    viewer.data().add_edges(P1, P2, Eigen::RowVector3d(1, 0, 0));
+
+    // Plot the singularities as colored dots (red for negative, blue for positive)
+    for (unsigned i=0; i<singularityIndex.size();++i)
+    {
+      if (singularityIndex(i) < 2 && singularityIndex(i) > 0)
+        viewer.data().add_points(V.row(i),Eigen::RowVector3d(1,0,0));
+      else if (singularityIndex(i) > 2)
+        viewer.data().add_points(V.row(i),Eigen::RowVector3d(0,1,0));
+    }
+  }
+
+  if (key == '6')
+  {
+    // Global parametrization UV
+    viewer.data().set_mesh(UV, FUV);
+    viewer.data().set_uv(UV);
+    viewer.data().show_lines = true;
+  }
+
+  if (key == '7')
+  {
+    // Global parametrization in 3D
+    viewer.data().set_mesh(V, F);
+    viewer.data().set_uv(UV,FUV);
+    viewer.data().show_texture = true;
+  }
+
+  if (key == '8')
+  {
+    // Global parametrization in 3D with seams
+    viewer.data().set_mesh(V, F);
+    viewer.data().set_uv(UV_seams,FUV_seams);
+    viewer.data().show_texture = true;
+  }
+
+  viewer.data().set_colors(Eigen::RowVector3d(1,1,1));
+
+  // Replace the standard texture with an integer shift invariant texture
+  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R, texture_G, texture_B;
+  line_texture(texture_R, texture_G, texture_B);
+  viewer.data().set_texture(texture_R, texture_B, texture_G);
+
+  viewer.core().align_camera_center(viewer.data().V,viewer.data().F);
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/3holes.off", V, F);
+
+  double gradient_size = 50;
+  double iter = 0;
+  double stiffness = 5.0;
+  bool direct_round = 0;
+
+  // Compute face barycenters
+  igl::barycenter(V, F, B);
+
+  // Compute scale for visualizing fields
+  global_scale =  .5*igl::avg_edge_length(V, F);
+
+    // Contrain one face
+    VectorXi b(1);
+    b << 0;
+    MatrixXd bc(1, 3);
+    bc << 1, 0, 0;
+
+    // Create a smooth 4-RoSy field
+    VectorXd S;
+    igl::copyleft::comiso::nrosy(V, F, b, bc, VectorXi(), VectorXd(), MatrixXd(), 4, 0.5, X1, S);
+
+    // Find the orthogonal vector
+    MatrixXd B1, B2, B3;
+    igl::local_basis(V, F, B1, B2, B3);
+    X2 = igl::rotate_vectors(X1, VectorXd::Constant(1, igl::PI / 2), B1, B2);
+
+    // Always work on the bisectors, it is more general
+    igl::compute_frame_field_bisectors(V, F, X1, X2, BIS1, BIS2);
+
+    // Comb the field, implicitly defining the seams
+    igl::comb_cross_field(V, F, BIS1, BIS2, BIS1_combed, BIS2_combed);
+
+    // Find the integer mismatches
+    igl::cross_field_mismatch(V, F, BIS1_combed, BIS2_combed, true, MMatch);
+
+    // Find the singularities
+    igl::find_cross_field_singularities(V, F, MMatch, isSingularity, singularityIndex);
+
+    // Cut the mesh, duplicating all vertices on the seams
+    igl::cut_mesh_from_singularities(V, F, MMatch, Seams);
+
+    // Comb the frame-field accordingly
+    igl::comb_frame_field(V, F, X1, X2, BIS1_combed, BIS2_combed, X1_combed, X2_combed);
+
+  // Global parametrization
+  igl::copyleft::comiso::miq(V,
+           F,
+           X1_combed,
+           X2_combed,
+           MMatch,
+           isSingularity,
+           Seams,
+           UV,
+           FUV,
+           gradient_size,
+           stiffness,
+           direct_round,
+           iter,
+           5,
+           true);
+
+// Global parametrization (with seams, only for demonstration)
+igl::copyleft::comiso::miq(V,
+         F,
+         X1_combed,
+         X2_combed,
+         MMatch,
+         isSingularity,
+         Seams,
+         UV_seams,
+         FUV_seams,
+         gradient_size,
+         stiffness,
+         direct_round,
+         iter,
+         5,
+         false);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+
+  // Plot the original mesh with a texture parametrization
+  key_down(viewer,'7',0);
+
+  // Launch the viewer
+  viewer.callback_key_down = &key_down;
+  viewer.launch();
+}

+ 255 - 255
tutorial/506_FrameField/main.cpp

@@ -1,255 +1,255 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/frame_field_deformer.h>
-#include <igl/frame_to_cross_field.h>
-#include <igl/jet.h>
-#include <igl/local_basis.h>
-#include <igl/readDMAT.h>
-#include <igl/readOBJ.h>
-#include <igl/rotate_vectors.h>
-#include <igl/copyleft/comiso/nrosy.h>
-#include <igl/copyleft/comiso/miq.h>
-#include <igl/copyleft/comiso/frame_field.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/PI.h>
-
-
-// Input mesh
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-// Face barycenters
-Eigen::MatrixXd B;
-
-// Scale for visualizing the fields
-double global_scale;
-
-// Input frame field constraints
-Eigen::VectorXi b;
-Eigen::MatrixXd bc1;
-Eigen::MatrixXd bc2;
-
-// Interpolated frame field
-Eigen::MatrixXd FF1, FF2;
-
-// Deformed mesh
-Eigen::MatrixXd V_deformed;
-Eigen::MatrixXd B_deformed;
-
-// Frame field on deformed
-Eigen::MatrixXd FF1_deformed;
-Eigen::MatrixXd FF2_deformed;
-
-// Cross field on deformed
-Eigen::MatrixXd X1_deformed;
-Eigen::MatrixXd X2_deformed;
-
-// Global parametrization
-Eigen::MatrixXd V_uv;
-Eigen::MatrixXi F_uv;
-
-// Create a texture that hides the integer translation in the parametrization
-void line_texture(Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_R,
-                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_G,
-                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_B)
-{
-  unsigned size = 128;
-  unsigned size2 = size/2;
-  unsigned lineWidth = 3;
-  texture_R.setConstant(size, size, 255);
-  for (unsigned i=0; i<size; ++i)
-    for (unsigned j=size2-lineWidth; j<=size2+lineWidth; ++j)
-      texture_R(i,j) = 0;
-  for (unsigned i=size2-lineWidth; i<=size2+lineWidth; ++i)
-    for (unsigned j=0; j<size; ++j)
-      texture_R(i,j) = 0;
-
-  texture_G = texture_R;
-  texture_B = texture_R;
-}
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace std;
-  using namespace Eigen;
-
-  if (key <'1' || key >'6')
-    return false;
-
-  viewer.data().clear();
-  viewer.data().show_lines = false;
-  viewer.data().show_texture = false;
-
-  if (key == '1')
-  {
-    // Frame field constraints
-    viewer.data().set_mesh(V, F);
-
-    MatrixXd F1_t = MatrixXd::Zero(FF1.rows(),FF1.cols());
-    MatrixXd F2_t = MatrixXd::Zero(FF2.rows(),FF2.cols());
-    // Highlight in red the constrained faces
-    MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
-    for (unsigned i=0; i<b.size();++i)
-    {
-      C.row(b(i)) << 1, 0, 0;
-      F1_t.row(b(i)) = bc1.row(i);
-      F2_t.row(b(i)) = bc2.row(i);
-    }
-
-    viewer.data().set_colors(C);
-
-    MatrixXd C1,C2;
-    VectorXd K1 = F1_t.rowwise().norm();
-    VectorXd K2 = F2_t.rowwise().norm();
-    igl::jet(K1,true,C1);
-    igl::jet(K2,true,C2);
-
-    viewer.data().add_edges(B - global_scale*F1_t, B + global_scale*F1_t ,C1);
-    viewer.data().add_edges(B - global_scale*F2_t, B + global_scale*F2_t ,C2);
-  }
-
-  if (key == '2')
-  {
-    // Frame field
-    viewer.data().set_mesh(V, F);
-    MatrixXd C1,C2;
-    VectorXd K1 = FF1.rowwise().norm();
-    VectorXd K2 = FF2.rowwise().norm();
-    igl::jet(K1,true,C1);
-    igl::jet(K2,true,C2);
-
-    viewer.data().add_edges(B - global_scale*FF1, B + global_scale*FF1 ,C1);
-    viewer.data().add_edges(B - global_scale*FF2, B + global_scale*FF2 ,C2);
-
-    // Highlight in red the constrained faces
-    MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
-    for (unsigned i=0; i<b.size();++i)
-      C.row(b(i)) << 1, 0, 0;
-    viewer.data().set_colors(C);
-
-  }
-
-  if (key == '3')
-  {
-    // Deformed with frame field
-    viewer.data().set_mesh(V_deformed, F);
-    viewer.data().add_edges(B_deformed - global_scale*FF1_deformed, B_deformed + global_scale*FF1_deformed ,Eigen::RowVector3d(1,0,0));
-    viewer.data().add_edges(B_deformed - global_scale*FF2_deformed, B_deformed + global_scale*FF2_deformed ,Eigen::RowVector3d(0,0,1));
-    viewer.data().set_colors(RowVector3d(1,1,1));
-  }
-
-  if (key == '4')
-  {
-    // Deformed with cross field
-    viewer.data().set_mesh(V_deformed, F);
-    viewer.data().add_edges(B_deformed - global_scale*X1_deformed, B_deformed + global_scale*X1_deformed ,Eigen::RowVector3d(0,0,1));
-    viewer.data().add_edges(B_deformed - global_scale*X2_deformed, B_deformed + global_scale*X2_deformed ,Eigen::RowVector3d(0,0,1));
-    viewer.data().set_colors(RowVector3d(1,1,1));
-  }
-
-  if (key == '5')
-  {
-    // Deformed with quad texture
-    viewer.data().set_mesh(V_deformed, F);
-    viewer.data().set_uv(V_uv,F_uv);
-    viewer.data().set_colors(RowVector3d(1,1,1));
-    viewer.data().show_texture = true;
-  }
-
-  if (key == '6')
-  {
-    // Deformed with quad texture
-    viewer.data().set_mesh(V, F);
-    viewer.data().set_uv(V_uv,F_uv);
-    viewer.data().set_colors(RowVector3d(1,1,1));
-    viewer.data().show_texture = true;
-  }
-
-  // Replace the standard texture with an integer shift invariant texture
-  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R, texture_G, texture_B;
-  line_texture(texture_R, texture_G, texture_B);
-  viewer.data().set_texture(texture_R, texture_B, texture_G);
-  viewer.core().align_camera_center(viewer.data().V,viewer.data().F);
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-
-  // Load a mesh in OBJ format
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/bumpy-cube.obj", V, F);
-
-  // Compute face barycenters
-  igl::barycenter(V, F, B);
-
-  // Compute scale for visualizing fields
-  global_scale =  .2*igl::avg_edge_length(V, F);
-
-  // Load constraints
-  MatrixXd temp;
-  igl::readDMAT(TUTORIAL_SHARED_PATH "/bumpy-cube.dmat",temp);
-
-  b   = temp.block(0,0,temp.rows(),1).cast<int>();
-  bc1 = temp.block(0,1,temp.rows(),3);
-  bc2 = temp.block(0,4,temp.rows(),3);
-
-  // Interpolate the frame field
-  igl::copyleft::comiso::frame_field(V, F, b, bc1, bc2, FF1, FF2);
-
-  // Deform the mesh to transform the frame field in a cross field
-  igl::frame_field_deformer(
-    V,F,FF1,FF2,V_deformed,FF1_deformed,FF2_deformed);
-
-  // Compute face barycenters deformed mesh
-  igl::barycenter(V_deformed, F, B_deformed);
-
-  // Find the closest crossfield to the deformed frame field
-  igl::frame_to_cross_field(V_deformed,F,FF1_deformed,FF2_deformed,X1_deformed);
-
-  // Find a smooth crossfield that interpolates the deformed constraints
-  MatrixXd bc_x(b.size(),3);
-  for (unsigned i=0; i<b.size();++i)
-    bc_x.row(i) = X1_deformed.row(b(i));
-
-  VectorXd S;
-  igl::copyleft::comiso::nrosy(
-             V,
-             F,
-             b,
-             bc_x,
-             VectorXi(),
-             VectorXd(),
-             MatrixXd(),
-             4,
-             0.5,
-             X1_deformed,
-             S);
-
-  // The other representative of the cross field is simply rotated by 90 degrees
-  MatrixXd B1,B2,B3;
-  igl::local_basis(V_deformed,F,B1,B2,B3);
-  X2_deformed =
-    igl::rotate_vectors(X1_deformed, VectorXd::Constant(1,igl::PI/2), B1, B2);
-
-  // Global seamless parametrization
-  igl::copyleft::comiso::miq(V_deformed,
-           F,
-           X1_deformed,
-           X2_deformed,
-           V_uv,
-           F_uv,
-           60.0,
-           5.0,
-           false,
-           2);
-
-  igl::opengl::glfw::Viewer viewer;
-  // Plot the original mesh with a texture parametrization
-  key_down(viewer,'6',0);
-
-  // Launch the viewer
-  viewer.callback_key_down = &key_down;
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/frame_field_deformer.h>
+#include <igl/frame_to_cross_field.h>
+#include <igl/jet.h>
+#include <igl/local_basis.h>
+#include <igl/readDMAT.h>
+#include <igl/readOBJ.h>
+#include <igl/rotate_vectors.h>
+#include <igl/copyleft/comiso/nrosy.h>
+#include <igl/copyleft/comiso/miq.h>
+#include <igl/copyleft/comiso/frame_field.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/PI.h>
+
+
+// Input mesh
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+// Face barycenters
+Eigen::MatrixXd B;
+
+// Scale for visualizing the fields
+double global_scale;
+
+// Input frame field constraints
+Eigen::VectorXi b;
+Eigen::MatrixXd bc1;
+Eigen::MatrixXd bc2;
+
+// Interpolated frame field
+Eigen::MatrixXd FF1, FF2;
+
+// Deformed mesh
+Eigen::MatrixXd V_deformed;
+Eigen::MatrixXd B_deformed;
+
+// Frame field on deformed
+Eigen::MatrixXd FF1_deformed;
+Eigen::MatrixXd FF2_deformed;
+
+// Cross field on deformed
+Eigen::MatrixXd X1_deformed;
+Eigen::MatrixXd X2_deformed;
+
+// Global parametrization
+Eigen::MatrixXd V_uv;
+Eigen::MatrixXi F_uv;
+
+// Create a texture that hides the integer translation in the parametrization
+void line_texture(Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_R,
+                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_G,
+                  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> &texture_B)
+{
+  unsigned size = 128;
+  unsigned size2 = size/2;
+  unsigned lineWidth = 3;
+  texture_R.setConstant(size, size, 255);
+  for (unsigned i=0; i<size; ++i)
+    for (unsigned j=size2-lineWidth; j<=size2+lineWidth; ++j)
+      texture_R(i,j) = 0;
+  for (unsigned i=size2-lineWidth; i<=size2+lineWidth; ++i)
+    for (unsigned j=0; j<size; ++j)
+      texture_R(i,j) = 0;
+
+  texture_G = texture_R;
+  texture_B = texture_R;
+}
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace std;
+  using namespace Eigen;
+
+  if (key <'1' || key >'6')
+    return false;
+
+  viewer.data().clear();
+  viewer.data().show_lines = false;
+  viewer.data().show_texture = false;
+
+  if (key == '1')
+  {
+    // Frame field constraints
+    viewer.data().set_mesh(V, F);
+
+    MatrixXd F1_t = MatrixXd::Zero(FF1.rows(),FF1.cols());
+    MatrixXd F2_t = MatrixXd::Zero(FF2.rows(),FF2.cols());
+    // Highlight in red the constrained faces
+    MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
+    for (unsigned i=0; i<b.size();++i)
+    {
+      C.row(b(i)) << 1, 0, 0;
+      F1_t.row(b(i)) = bc1.row(i);
+      F2_t.row(b(i)) = bc2.row(i);
+    }
+
+    viewer.data().set_colors(C);
+
+    MatrixXd C1,C2;
+    VectorXd K1 = F1_t.rowwise().norm();
+    VectorXd K2 = F2_t.rowwise().norm();
+    igl::jet(K1,true,C1);
+    igl::jet(K2,true,C2);
+
+    viewer.data().add_edges(B - global_scale*F1_t, B + global_scale*F1_t ,C1);
+    viewer.data().add_edges(B - global_scale*F2_t, B + global_scale*F2_t ,C2);
+  }
+
+  if (key == '2')
+  {
+    // Frame field
+    viewer.data().set_mesh(V, F);
+    MatrixXd C1,C2;
+    VectorXd K1 = FF1.rowwise().norm();
+    VectorXd K2 = FF2.rowwise().norm();
+    igl::jet(K1,true,C1);
+    igl::jet(K2,true,C2);
+
+    viewer.data().add_edges(B - global_scale*FF1, B + global_scale*FF1 ,C1);
+    viewer.data().add_edges(B - global_scale*FF2, B + global_scale*FF2 ,C2);
+
+    // Highlight in red the constrained faces
+    MatrixXd C = MatrixXd::Constant(F.rows(),3,1);
+    for (unsigned i=0; i<b.size();++i)
+      C.row(b(i)) << 1, 0, 0;
+    viewer.data().set_colors(C);
+
+  }
+
+  if (key == '3')
+  {
+    // Deformed with frame field
+    viewer.data().set_mesh(V_deformed, F);
+    viewer.data().add_edges(B_deformed - global_scale*FF1_deformed, B_deformed + global_scale*FF1_deformed ,Eigen::RowVector3d(1,0,0));
+    viewer.data().add_edges(B_deformed - global_scale*FF2_deformed, B_deformed + global_scale*FF2_deformed ,Eigen::RowVector3d(0,0,1));
+    viewer.data().set_colors(RowVector3d(1,1,1));
+  }
+
+  if (key == '4')
+  {
+    // Deformed with cross field
+    viewer.data().set_mesh(V_deformed, F);
+    viewer.data().add_edges(B_deformed - global_scale*X1_deformed, B_deformed + global_scale*X1_deformed ,Eigen::RowVector3d(0,0,1));
+    viewer.data().add_edges(B_deformed - global_scale*X2_deformed, B_deformed + global_scale*X2_deformed ,Eigen::RowVector3d(0,0,1));
+    viewer.data().set_colors(RowVector3d(1,1,1));
+  }
+
+  if (key == '5')
+  {
+    // Deformed with quad texture
+    viewer.data().set_mesh(V_deformed, F);
+    viewer.data().set_uv(V_uv,F_uv);
+    viewer.data().set_colors(RowVector3d(1,1,1));
+    viewer.data().show_texture = true;
+  }
+
+  if (key == '6')
+  {
+    // Deformed with quad texture
+    viewer.data().set_mesh(V, F);
+    viewer.data().set_uv(V_uv,F_uv);
+    viewer.data().set_colors(RowVector3d(1,1,1));
+    viewer.data().show_texture = true;
+  }
+
+  // Replace the standard texture with an integer shift invariant texture
+  Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R, texture_G, texture_B;
+  line_texture(texture_R, texture_G, texture_B);
+  viewer.data().set_texture(texture_R, texture_B, texture_G);
+  viewer.core().align_camera_center(viewer.data().V,viewer.data().F);
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+
+  // Load a mesh in OBJ format
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/bumpy-cube.obj", V, F);
+
+  // Compute face barycenters
+  igl::barycenter(V, F, B);
+
+  // Compute scale for visualizing fields
+  global_scale =  .2*igl::avg_edge_length(V, F);
+
+  // Load constraints
+  MatrixXd temp;
+  igl::readDMAT(TUTORIAL_SHARED_PATH "/bumpy-cube.dmat",temp);
+
+  b   = temp.block(0,0,temp.rows(),1).cast<int>();
+  bc1 = temp.block(0,1,temp.rows(),3);
+  bc2 = temp.block(0,4,temp.rows(),3);
+
+  // Interpolate the frame field
+  igl::copyleft::comiso::frame_field(V, F, b, bc1, bc2, FF1, FF2);
+
+  // Deform the mesh to transform the frame field in a cross field
+  igl::frame_field_deformer(
+    V,F,FF1,FF2,V_deformed,FF1_deformed,FF2_deformed);
+
+  // Compute face barycenters deformed mesh
+  igl::barycenter(V_deformed, F, B_deformed);
+
+  // Find the closest crossfield to the deformed frame field
+  igl::frame_to_cross_field(V_deformed,F,FF1_deformed,FF2_deformed,X1_deformed);
+
+  // Find a smooth crossfield that interpolates the deformed constraints
+  MatrixXd bc_x(b.size(),3);
+  for (unsigned i=0; i<b.size();++i)
+    bc_x.row(i) = X1_deformed.row(b(i));
+
+  VectorXd S;
+  igl::copyleft::comiso::nrosy(
+             V,
+             F,
+             b,
+             bc_x,
+             VectorXi(),
+             VectorXd(),
+             MatrixXd(),
+             4,
+             0.5,
+             X1_deformed,
+             S);
+
+  // The other representative of the cross field is simply rotated by 90 degrees
+  MatrixXd B1,B2,B3;
+  igl::local_basis(V_deformed,F,B1,B2,B3);
+  X2_deformed =
+    igl::rotate_vectors(X1_deformed, VectorXd::Constant(1,igl::PI/2), B1, B2);
+
+  // Global seamless parametrization
+  igl::copyleft::comiso::miq(V_deformed,
+           F,
+           X1_deformed,
+           X2_deformed,
+           V_uv,
+           F_uv,
+           60.0,
+           5.0,
+           false,
+           2);
+
+  igl::opengl::glfw::Viewer viewer;
+  // Plot the original mesh with a texture parametrization
+  key_down(viewer,'6',0);
+
+  // Launch the viewer
+  viewer.callback_key_down = &key_down;
+  viewer.launch();
+}

+ 114 - 114
tutorial/507_Planarization/main.cpp

@@ -1,114 +1,114 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/jet.h>
-#include <igl/planarize_quad_mesh.h>
-#include <igl/quad_planarity.h>
-#include <igl/readDMAT.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <vector>
-#include <cstdlib>
-
-
-// Quad mesh generated from conjugate field
-Eigen::MatrixXd VQC;
-Eigen::MatrixXi FQC;
-Eigen::MatrixXi FQCtri;
-Eigen::MatrixXd PQC0, PQC1, PQC2, PQC3;
-
-// Planarized quad mesh
-Eigen::MatrixXd VQCplan;
-Eigen::MatrixXi FQCtriplan;
-Eigen::MatrixXd PQC0plan, PQC1plan, PQC2plan, PQC3plan;
-
-
-// Scale for visualizing the fields
-double global_scale; //TODO: not used
-
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace std;
-  using namespace Eigen;
-
-  // Plot the original quad mesh
-  if (key == '1')
-  {
-    // Draw the triangulated quad mesh
-    viewer.data().set_mesh(VQC, FQCtri);
-
-    // Assign a color to each quad that corresponds to its planarity
-    VectorXd planarity;
-    igl::quad_planarity( VQC, FQC, planarity);
-    MatrixXd Ct;
-    igl::jet(planarity, 0, 0.01, Ct);
-    MatrixXd C(FQCtri.rows(),3);
-    C << Ct, Ct;
-    viewer.data().set_colors(C);
-
-    // Plot a line for each edge of the quad mesh
-    viewer.data().add_edges(PQC0, PQC1, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC1, PQC2, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC2, PQC3, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC3, PQC0, Eigen::RowVector3d(0,0,0));
-  }
-
-  // Plot the planarized quad mesh
-  if (key == '2')
-  {
-    // Draw the triangulated quad mesh
-    viewer.data().set_mesh(VQCplan, FQCtri);
-
-    // Assign a color to each quad that corresponds to its planarity
-    VectorXd planarity;
-    igl::quad_planarity( VQCplan, FQC, planarity);
-    MatrixXd Ct;
-    igl::jet(planarity, 0, 0.01, Ct);
-    MatrixXd C(FQCtri.rows(),3);
-    C << Ct, Ct;
-    viewer.data().set_colors(C);
-
-    // Plot a line for each edge of the quad mesh
-    viewer.data().add_edges(PQC0plan, PQC1plan, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC1plan, PQC2plan, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC2plan, PQC3plan, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC3plan, PQC0plan, Eigen::RowVector3d(0,0,0));
-  }
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a quad mesh generated by a conjugate field
-  igl::readOFF(TUTORIAL_SHARED_PATH "/inspired_mesh_quads_Conjugate.off", VQC, FQC);
-
-  // Convert it in a triangle mesh
-  FQCtri.resize(2*FQC.rows(), 3);
-  FQCtri <<  FQC.col(0),FQC.col(1),FQC.col(2),
-             FQC.col(2),FQC.col(3),FQC.col(0);
-  PQC0 = VQC(FQC.col(0).eval(), Eigen::all);
-  PQC1 = VQC(FQC.col(1).eval(), Eigen::all);
-  PQC2 = VQC(FQC.col(2).eval(), Eigen::all);
-  PQC3 = VQC(FQC.col(3).eval(), Eigen::all);
-
-  // Planarize it
-  igl::planarize_quad_mesh(VQC, FQC, 100, 0.005, VQCplan);
-
-  // Convert the planarized mesh to triangles
-  PQC0plan = VQCplan(FQC.col(0).eval(), Eigen::all);
-  PQC1plan = VQCplan(FQC.col(1).eval(), Eigen::all);
-  PQC2plan = VQCplan(FQC.col(2).eval(), Eigen::all);
-  PQC3plan = VQCplan(FQC.col(3).eval(), Eigen::all);
-
-  // Launch the viewer
-  igl::opengl::glfw::Viewer viewer;
-  key_down(viewer,'2',0);
-  viewer.data().invert_normals = true;
-  viewer.data().show_lines = false;
-  viewer.callback_key_down = &key_down;
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/jet.h>
+#include <igl/planarize_quad_mesh.h>
+#include <igl/quad_planarity.h>
+#include <igl/readDMAT.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <vector>
+#include <cstdlib>
+
+
+// Quad mesh generated from conjugate field
+Eigen::MatrixXd VQC;
+Eigen::MatrixXi FQC;
+Eigen::MatrixXi FQCtri;
+Eigen::MatrixXd PQC0, PQC1, PQC2, PQC3;
+
+// Planarized quad mesh
+Eigen::MatrixXd VQCplan;
+Eigen::MatrixXi FQCtriplan;
+Eigen::MatrixXd PQC0plan, PQC1plan, PQC2plan, PQC3plan;
+
+
+// Scale for visualizing the fields
+double global_scale; //TODO: not used
+
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace std;
+  using namespace Eigen;
+
+  // Plot the original quad mesh
+  if (key == '1')
+  {
+    // Draw the triangulated quad mesh
+    viewer.data().set_mesh(VQC, FQCtri);
+
+    // Assign a color to each quad that corresponds to its planarity
+    VectorXd planarity;
+    igl::quad_planarity( VQC, FQC, planarity);
+    MatrixXd Ct;
+    igl::jet(planarity, 0, 0.01, Ct);
+    MatrixXd C(FQCtri.rows(),3);
+    C << Ct, Ct;
+    viewer.data().set_colors(C);
+
+    // Plot a line for each edge of the quad mesh
+    viewer.data().add_edges(PQC0, PQC1, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC1, PQC2, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC2, PQC3, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC3, PQC0, Eigen::RowVector3d(0,0,0));
+  }
+
+  // Plot the planarized quad mesh
+  if (key == '2')
+  {
+    // Draw the triangulated quad mesh
+    viewer.data().set_mesh(VQCplan, FQCtri);
+
+    // Assign a color to each quad that corresponds to its planarity
+    VectorXd planarity;
+    igl::quad_planarity( VQCplan, FQC, planarity);
+    MatrixXd Ct;
+    igl::jet(planarity, 0, 0.01, Ct);
+    MatrixXd C(FQCtri.rows(),3);
+    C << Ct, Ct;
+    viewer.data().set_colors(C);
+
+    // Plot a line for each edge of the quad mesh
+    viewer.data().add_edges(PQC0plan, PQC1plan, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC1plan, PQC2plan, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC2plan, PQC3plan, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC3plan, PQC0plan, Eigen::RowVector3d(0,0,0));
+  }
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a quad mesh generated by a conjugate field
+  igl::readOFF(TUTORIAL_SHARED_PATH "/inspired_mesh_quads_Conjugate.off", VQC, FQC);
+
+  // Convert it in a triangle mesh
+  FQCtri.resize(2*FQC.rows(), 3);
+  FQCtri <<  FQC.col(0),FQC.col(1),FQC.col(2),
+             FQC.col(2),FQC.col(3),FQC.col(0);
+  PQC0 = VQC(FQC.col(0).eval(), Eigen::all);
+  PQC1 = VQC(FQC.col(1).eval(), Eigen::all);
+  PQC2 = VQC(FQC.col(2).eval(), Eigen::all);
+  PQC3 = VQC(FQC.col(3).eval(), Eigen::all);
+
+  // Planarize it
+  igl::planarize_quad_mesh(VQC, FQC, 100, 0.005, VQCplan);
+
+  // Convert the planarized mesh to triangles
+  PQC0plan = VQCplan(FQC.col(0).eval(), Eigen::all);
+  PQC1plan = VQCplan(FQC.col(1).eval(), Eigen::all);
+  PQC2plan = VQCplan(FQC.col(2).eval(), Eigen::all);
+  PQC3plan = VQCplan(FQC.col(3).eval(), Eigen::all);
+
+  // Launch the viewer
+  igl::opengl::glfw::Viewer viewer;
+  key_down(viewer,'2',0);
+  viewer.data().invert_normals = true;
+  viewer.data().show_lines = false;
+  viewer.callback_key_down = &key_down;
+  viewer.launch();
+}

+ 113 - 113
tutorial/601_Serialization/main.cpp

@@ -1,113 +1,113 @@
-#include <igl/readOFF.h>
-#include <igl/serialize.h>
-#include <igl/xml/serialize_xml.h>
-#include <iostream>
-
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-// derive from igl::Serializable to serialize your own type
-struct State : public igl::Serializable
-{
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  std::vector<int> ids;
-
-  // You have to define this function to
-  // register the fields you want to serialize
-  virtual void InitSerialization()
-  {
-    this->Add(V  , "V");
-    this->Add(F  , "F");
-    this->Add(ids, "ids");
-  }
-};
-
-//// alternatively you can do it like the following to have
-//// a non-intrusive serialization:
-////
-//struct State
-//{
-//  Eigen::MatrixXd V;
-//  Eigen::MatrixXi F;
-//  std::vector<int> ids;
-//};
-//
-//
-//namespace igl
-//{
-//  namespace serialization
-//  {
-//    // the `template <>` is essential
-//    template <> inline void serialize(const State& obj,std::vector<char>& buffer){
-//      ::igl::serialize(obj.V,std::string("V"),buffer);
-//      ::igl::serialize(obj.F,std::string("F"),buffer);
-//      ::igl::serialize(obj.ids,std::string("ids"),buffer);
-//    }
-//    template <> inline void deserialize(State& obj,const std::vector<char>& buffer){
-//      ::igl::deserialize(obj.V,std::string("V"),buffer);
-//      ::igl::deserialize(obj.F,std::string("F"),buffer);
-//      ::igl::deserialize(obj.ids,std::string("ids"),buffer);
-//    }
-//  }
-//}
-//
-////OR:
-//
-//SERIALIZE_TYPE(State,
-// SERIALIZE_MEMBER(V)
-//  SERIALIZE_MEMBER(F)
-//  SERIALIZE_MEMBER_NAME(ids,"ids")
-//)
-
-int main(int argc, char *argv[])
-{
-  std::string binaryFile = "binData";
-  std::string xmlFile = "data.xml";
-
-  bool b = true;
-  unsigned int num = 10;
-  std::vector<float> vec = {0.1f,0.002f,5.3f};
-
-  // use overwrite = true for the first serialization to create or overwrite an existing file
-  igl::serialize(b,"B",binaryFile,true);
-  // append following serialization to existing file
-  igl::serialize(num,"Number",binaryFile);
-  igl::serialize(vec,"VectorName",binaryFile);
-
-  // deserialize back to variables
-  igl::deserialize(b,"B",binaryFile);
-  igl::deserialize(num,"Number",binaryFile);
-  igl::deserialize(vec,"VectorName",binaryFile);
-
-  State stateIn, stateOut;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/2triangles.off",stateIn.V,stateIn.F);
-
-  // Save some integers in a vector
-  stateIn.ids.push_back(6);
-  stateIn.ids.push_back(7);
-
-  // Serialize the state of the application
-  igl::serialize(stateIn,"State",binaryFile,true);
-
-  // Load the state from the same file
-  igl::deserialize(stateOut,"State",binaryFile);
-
-  // Plot the state
-  std::cout << "Vertices: " << std::endl << stateOut.V << std::endl;
-  std::cout << "Faces:    " << std::endl << stateOut.F << std::endl;
-  std::cout << "ids:      " << std::endl
-            << stateOut.ids[0] << " " << stateOut.ids[1] << std::endl;
-
-  // XML serialization
-
-  // binary = false, overwrite = true
-  igl::xml::serialize_xml(vec,"VectorXML",xmlFile,false,true);
-  // binary = true, overwrite = false
-  igl::xml::serialize_xml(vec,"VectorBin",xmlFile,true,false);
-  igl::xml::deserialize_xml(vec,"VectorXML",xmlFile);
-  igl::xml::deserialize_xml(vec,"VectorBin",xmlFile);
-}
+#include <igl/readOFF.h>
+#include <igl/serialize.h>
+#include <igl/xml/serialize_xml.h>
+#include <iostream>
+
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+// derive from igl::Serializable to serialize your own type
+struct State : public igl::Serializable
+{
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  std::vector<int> ids;
+
+  // You have to define this function to
+  // register the fields you want to serialize
+  virtual void InitSerialization()
+  {
+    this->Add(V  , "V");
+    this->Add(F  , "F");
+    this->Add(ids, "ids");
+  }
+};
+
+//// alternatively you can do it like the following to have
+//// a non-intrusive serialization:
+////
+//struct State
+//{
+//  Eigen::MatrixXd V;
+//  Eigen::MatrixXi F;
+//  std::vector<int> ids;
+//};
+//
+//
+//namespace igl
+//{
+//  namespace serialization
+//  {
+//    // the `template <>` is essential
+//    template <> inline void serialize(const State& obj,std::vector<char>& buffer){
+//      ::igl::serialize(obj.V,std::string("V"),buffer);
+//      ::igl::serialize(obj.F,std::string("F"),buffer);
+//      ::igl::serialize(obj.ids,std::string("ids"),buffer);
+//    }
+//    template <> inline void deserialize(State& obj,const std::vector<char>& buffer){
+//      ::igl::deserialize(obj.V,std::string("V"),buffer);
+//      ::igl::deserialize(obj.F,std::string("F"),buffer);
+//      ::igl::deserialize(obj.ids,std::string("ids"),buffer);
+//    }
+//  }
+//}
+//
+////OR:
+//
+//SERIALIZE_TYPE(State,
+// SERIALIZE_MEMBER(V)
+//  SERIALIZE_MEMBER(F)
+//  SERIALIZE_MEMBER_NAME(ids,"ids")
+//)
+
+int main(int argc, char *argv[])
+{
+  std::string binaryFile = "binData";
+  std::string xmlFile = "data.xml";
+
+  bool b = true;
+  unsigned int num = 10;
+  std::vector<float> vec = {0.1f,0.002f,5.3f};
+
+  // use overwrite = true for the first serialization to create or overwrite an existing file
+  igl::serialize(b,"B",binaryFile,true);
+  // append following serialization to existing file
+  igl::serialize(num,"Number",binaryFile);
+  igl::serialize(vec,"VectorName",binaryFile);
+
+  // deserialize back to variables
+  igl::deserialize(b,"B",binaryFile);
+  igl::deserialize(num,"Number",binaryFile);
+  igl::deserialize(vec,"VectorName",binaryFile);
+
+  State stateIn, stateOut;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/2triangles.off",stateIn.V,stateIn.F);
+
+  // Save some integers in a vector
+  stateIn.ids.push_back(6);
+  stateIn.ids.push_back(7);
+
+  // Serialize the state of the application
+  igl::serialize(stateIn,"State",binaryFile,true);
+
+  // Load the state from the same file
+  igl::deserialize(stateOut,"State",binaryFile);
+
+  // Plot the state
+  std::cout << "Vertices: " << std::endl << stateOut.V << std::endl;
+  std::cout << "Faces:    " << std::endl << stateOut.F << std::endl;
+  std::cout << "ids:      " << std::endl
+            << stateOut.ids[0] << " " << stateOut.ids[1] << std::endl;
+
+  // XML serialization
+
+  // binary = false, overwrite = true
+  igl::xml::serialize_xml(vec,"VectorXML",xmlFile,false,true);
+  // binary = true, overwrite = false
+  igl::xml::serialize_xml(vec,"VectorBin",xmlFile,true,false);
+  igl::xml::deserialize_xml(vec,"VectorXML",xmlFile);
+  igl::xml::deserialize_xml(vec,"VectorBin",xmlFile);
+}

+ 70 - 70
tutorial/602_Matlab/main.cpp

@@ -1,70 +1,70 @@
-
-#include <igl/readOFF.h>
-#include <igl/cotmatrix.h>
-#include <igl/matlab/matlabinterface.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-// On mac you may need to issue something like:
-//
-//     PATH=$PATH:/Applications/MATLAB_R2019a.app/bin/ ./tutorial/602_Matlab_bin
-
-// Base mesh
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-// Matlab instance
-Engine* engine;
-
-// Eigenvectors of the laplacian
-Eigen::MatrixXd EV;
-
-// This function is called every time a keyboard button is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  if (key >= '1' && key <= '9')
-  {
-    viewer.data().set_data(EV.col((key - '1') + 1));
-  }
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/3holes.off", V, F);
-
-  // Launch MATLAB
-  igl::matlab::mlinit(&engine);
-
-  // Compute the discrete Laplacian operator
-  Eigen::SparseMatrix<double> L;
-  igl::cotmatrix(V,F,L);
-
-  // Send Laplacian matrix to matlab
-  igl::matlab::mlsetmatrix(&engine,"L",L);
-
-  // Plot the laplacian matrix using matlab spy
-  igl::matlab::mleval(&engine,"spy(L)");
-
-  // Extract the first 10 eigenvectors
-  igl::matlab::mleval(&engine,"[EV,~] = eigs(-L,10,'sm')");
-
-  // Plot the size of EV (only for demonstration purposes)
-  std::cerr << igl::matlab::mleval(&engine,"size(EV)") << std::endl;
-
-  // Retrieve the result
-  igl::matlab::mlgetmatrix(&engine,"EV",EV);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.callback_key_down = &key_down;
-  viewer.data().set_mesh(V, F);
-
-  // Plot the first non-trivial eigenvector
-  viewer.data().set_data(EV.col(1));
-
-  // Launch the viewer
-  viewer.launch();
-}
+
+#include <igl/readOFF.h>
+#include <igl/cotmatrix.h>
+#include <igl/matlab/matlabinterface.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+// On mac you may need to issue something like:
+//
+//     PATH=$PATH:/Applications/MATLAB_R2019a.app/bin/ ./tutorial/602_Matlab_bin
+
+// Base mesh
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+// Matlab instance
+Engine* engine;
+
+// Eigenvectors of the laplacian
+Eigen::MatrixXd EV;
+
+// This function is called every time a keyboard button is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  if (key >= '1' && key <= '9')
+  {
+    viewer.data().set_data(EV.col((key - '1') + 1));
+  }
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/3holes.off", V, F);
+
+  // Launch MATLAB
+  igl::matlab::mlinit(&engine);
+
+  // Compute the discrete Laplacian operator
+  Eigen::SparseMatrix<double> L;
+  igl::cotmatrix(V,F,L);
+
+  // Send Laplacian matrix to matlab
+  igl::matlab::mlsetmatrix(&engine,"L",L);
+
+  // Plot the laplacian matrix using matlab spy
+  igl::matlab::mleval(&engine,"spy(L)");
+
+  // Extract the first 10 eigenvectors
+  igl::matlab::mleval(&engine,"[EV,~] = eigs(-L,10,'sm')");
+
+  // Plot the size of EV (only for demonstration purposes)
+  std::cerr << igl::matlab::mleval(&engine,"size(EV)") << std::endl;
+
+  // Retrieve the result
+  igl::matlab::mlgetmatrix(&engine,"EV",EV);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.callback_key_down = &key_down;
+  viewer.data().set_mesh(V, F);
+
+  // Plot the first non-trivial eigenvector
+  viewer.data().set_data(EV.col(1));
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 48 - 48
tutorial/604_Triangle/main.cpp

@@ -1,48 +1,48 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/triangle/triangulate.h>
-// Input polygon
-Eigen::MatrixXd V;
-Eigen::MatrixXi E;
-Eigen::MatrixXd H;
-
-// Triangulated interior
-Eigen::MatrixXd V2;
-Eigen::MatrixXi F2;
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Create the boundary of a square
-  V.resize(8,2);
-  E.resize(8,2);
-  H.resize(1,2);
-
-  // create two squares, one with edge length of 4,
-  // one with edge length of 2
-  // both centered at origin
-  V << -1,-1, 1,-1, 1,1, -1, 1,
-       -2,-2, 2,-2, 2,2, -2, 2;
-
-  // add the edges of the squares
-  E << 0,1, 1,2, 2,3, 3,0,
-       4,5, 5,6, 6,7, 7,4;
-
-  // specify a point that is inside a closed shape
-  // where we do not want triangulation to happen
-  H << 0,0;
-
-  // Triangulate the interior
-  // a0.005 means that the area of each triangle should
-  // not be greater than 0.005
-  // q means that no angles will be smaller than 20 degrees
-  // for a detailed set of commands please refer to:
-  // https://www.cs.cmu.edu/~quake/triangle.switch.html
-  igl::triangle::triangulate(V,E,H,"a0.005q",V2,F2);
-
-  // Plot the generated mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V2,F2);
-  viewer.launch();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/triangle/triangulate.h>
+// Input polygon
+Eigen::MatrixXd V;
+Eigen::MatrixXi E;
+Eigen::MatrixXd H;
+
+// Triangulated interior
+Eigen::MatrixXd V2;
+Eigen::MatrixXi F2;
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Create the boundary of a square
+  V.resize(8,2);
+  E.resize(8,2);
+  H.resize(1,2);
+
+  // create two squares, one with edge length of 4,
+  // one with edge length of 2
+  // both centered at origin
+  V << -1,-1, 1,-1, 1,1, -1, 1,
+       -2,-2, 2,-2, 2,2, -2, 2;
+
+  // add the edges of the squares
+  E << 0,1, 1,2, 2,3, 3,0,
+       4,5, 5,6, 6,7, 7,4;
+
+  // specify a point that is inside a closed shape
+  // where we do not want triangulation to happen
+  H << 0,0;
+
+  // Triangulate the interior
+  // a0.005 means that the area of each triangle should
+  // not be greater than 0.005
+  // q means that no angles will be smaller than 20 degrees
+  // for a detailed set of commands please refer to:
+  // https://www.cs.cmu.edu/~quake/triangle.switch.html
+  igl::triangle::triangulate(V,E,H,"a0.005q",V2,F2);
+
+  // Plot the generated mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V2,F2);
+  viewer.launch();
+}

+ 79 - 79
tutorial/605_Tetgen/main.cpp

@@ -1,79 +1,79 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/copyleft/tetgen/tetrahedralize.h>
-#include <igl/readOFF.h>
-#include <igl/barycenter.h>
-
-
-// Input polygon
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-Eigen::MatrixXd B;
-
-// Tetrahedralized interior
-Eigen::MatrixXd TV;
-Eigen::MatrixXi TT;
-Eigen::MatrixXi TF;
-
-// This function is called every time a keyboard button is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace std;
-  using namespace Eigen;
-
-  if (key >= '1' && key <= '9')
-  {
-    double t = double((key - '1')+1) / 9.0;
-
-    VectorXd v = B.col(2).array() - B.col(2).minCoeff();
-    v /= v.col(0).maxCoeff();
-
-    vector<int> s;
-
-    for (unsigned i=0; i<v.size();++i)
-      if (v(i) < t)
-        s.push_back(i);
-
-    MatrixXd V_temp(s.size()*4,3);
-    MatrixXi F_temp(s.size()*4,3);
-
-    for (unsigned i=0; i<s.size();++i)
-    {
-      V_temp.row(i*4+0) = TV.row(TT(s[i],0));
-      V_temp.row(i*4+1) = TV.row(TT(s[i],1));
-      V_temp.row(i*4+2) = TV.row(TT(s[i],2));
-      V_temp.row(i*4+3) = TV.row(TT(s[i],3));
-      F_temp.row(i*4+0) << (i*4)+0, (i*4)+1, (i*4)+3;
-      F_temp.row(i*4+1) << (i*4)+0, (i*4)+2, (i*4)+1;
-      F_temp.row(i*4+2) << (i*4)+3, (i*4)+2, (i*4)+0;
-      F_temp.row(i*4+3) << (i*4)+1, (i*4)+2, (i*4)+3;
-    }
-
-    viewer.data().clear();
-    viewer.data().set_mesh(V_temp,F_temp);
-    viewer.data().set_face_based(true);
-  }
-
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a surface mesh
-  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off",V,F);
-
-  // Tetrahedralize the interior
-  igl::copyleft::tetgen::tetrahedralize(V,F,"pq1.414Y", TV,TT,TF);
-
-  // Compute barycenters
-  igl::barycenter(TV,TT,B);
-
-  // Plot the generated mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.callback_key_down = &key_down;
-  key_down(viewer,'5',0);
-  viewer.launch();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/copyleft/tetgen/tetrahedralize.h>
+#include <igl/readOFF.h>
+#include <igl/barycenter.h>
+
+
+// Input polygon
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+Eigen::MatrixXd B;
+
+// Tetrahedralized interior
+Eigen::MatrixXd TV;
+Eigen::MatrixXi TT;
+Eigen::MatrixXi TF;
+
+// This function is called every time a keyboard button is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace std;
+  using namespace Eigen;
+
+  if (key >= '1' && key <= '9')
+  {
+    double t = double((key - '1')+1) / 9.0;
+
+    VectorXd v = B.col(2).array() - B.col(2).minCoeff();
+    v /= v.col(0).maxCoeff();
+
+    vector<int> s;
+
+    for (unsigned i=0; i<v.size();++i)
+      if (v(i) < t)
+        s.push_back(i);
+
+    MatrixXd V_temp(s.size()*4,3);
+    MatrixXi F_temp(s.size()*4,3);
+
+    for (unsigned i=0; i<s.size();++i)
+    {
+      V_temp.row(i*4+0) = TV.row(TT(s[i],0));
+      V_temp.row(i*4+1) = TV.row(TT(s[i],1));
+      V_temp.row(i*4+2) = TV.row(TT(s[i],2));
+      V_temp.row(i*4+3) = TV.row(TT(s[i],3));
+      F_temp.row(i*4+0) << (i*4)+0, (i*4)+1, (i*4)+3;
+      F_temp.row(i*4+1) << (i*4)+0, (i*4)+2, (i*4)+1;
+      F_temp.row(i*4+2) << (i*4)+3, (i*4)+2, (i*4)+0;
+      F_temp.row(i*4+3) << (i*4)+1, (i*4)+2, (i*4)+3;
+    }
+
+    viewer.data().clear();
+    viewer.data().set_mesh(V_temp,F_temp);
+    viewer.data().set_face_based(true);
+  }
+
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a surface mesh
+  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off",V,F);
+
+  // Tetrahedralize the interior
+  igl::copyleft::tetgen::tetrahedralize(V,F,"pq1.414Y", TV,TT,TF);
+
+  // Compute barycenters
+  igl::barycenter(TV,TT,B);
+
+  // Plot the generated mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.callback_key_down = &key_down;
+  key_down(viewer,'5',0);
+  viewer.launch();
+}

+ 78 - 78
tutorial/606_AmbientOcclusion/main.cpp

@@ -1,78 +1,78 @@
-#include <igl/avg_edge_length.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/readOFF.h>
-#include <igl/embree/ambient_occlusion.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-
-// Mesh
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-Eigen::VectorXd AO;
-
-// It allows to change the degree of the field when a number is pressed
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace Eigen;
-  using namespace std;
-  const RowVector3d color(0.9,0.85,0.9);
-  switch(key)
-  {
-    case '1':
-      // Show the mesh without the ambient occlusion factor
-      viewer.data().set_colors(color);
-      break;
-    case '2':
-    {
-      // Show the mesh with the ambient occlusion factor
-      MatrixXd C = color.replicate(V.rows(),1);
-      for (unsigned i=0; i<C.rows();++i)
-        C.row(i) *= AO(i);//std::min<double>(AO(i)+0.2,1);
-      viewer.data().set_colors(C);
-      break;
-    }
-    case '.':
-      viewer.core().lighting_factor += 0.1;
-      break;
-    case ',':
-      viewer.core().lighting_factor -= 0.1;
-      break;
-    default: break;
-  }
-  viewer.core().lighting_factor = 
-    std::min(std::max(viewer.core().lighting_factor,0.f),1.f);
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace std;
-  using namespace Eigen;
-  cout<<
-    "Press 1 to turn off Ambient Occlusion"<<endl<<
-    "Press 2 to turn on Ambient Occlusion"<<endl<<
-    "Press . to turn up lighting"<<endl<<
-    "Press , to turn down lighting"<<endl;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V, F);
-
-  MatrixXd N;
-  igl::per_vertex_normals(V,F,N);
-
-  // Compute ambient occlusion factor using embree
-  igl::embree::ambient_occlusion(V,F,V,N,500,AO);
-  AO = 1.0 - AO.array();
-
-  // Show mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  viewer.callback_key_down = &key_down;
-  key_down(viewer,'2',0);
-  viewer.data().show_lines = false;
-  viewer.core().lighting_factor = 0.0f;
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/readOFF.h>
+#include <igl/embree/ambient_occlusion.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+
+// Mesh
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+Eigen::VectorXd AO;
+
+// It allows to change the degree of the field when a number is pressed
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace Eigen;
+  using namespace std;
+  const RowVector3d color(0.9,0.85,0.9);
+  switch(key)
+  {
+    case '1':
+      // Show the mesh without the ambient occlusion factor
+      viewer.data().set_colors(color);
+      break;
+    case '2':
+    {
+      // Show the mesh with the ambient occlusion factor
+      MatrixXd C = color.replicate(V.rows(),1);
+      for (unsigned i=0; i<C.rows();++i)
+        C.row(i) *= AO(i);//std::min<double>(AO(i)+0.2,1);
+      viewer.data().set_colors(C);
+      break;
+    }
+    case '.':
+      viewer.core().lighting_factor += 0.1;
+      break;
+    case ',':
+      viewer.core().lighting_factor -= 0.1;
+      break;
+    default: break;
+  }
+  viewer.core().lighting_factor = 
+    std::min(std::max(viewer.core().lighting_factor,0.f),1.f);
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace std;
+  using namespace Eigen;
+  cout<<
+    "Press 1 to turn off Ambient Occlusion"<<endl<<
+    "Press 2 to turn on Ambient Occlusion"<<endl<<
+    "Press . to turn up lighting"<<endl<<
+    "Press , to turn down lighting"<<endl;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V, F);
+
+  MatrixXd N;
+  igl::per_vertex_normals(V,F,N);
+
+  // Compute ambient occlusion factor using embree
+  igl::embree::ambient_occlusion(V,F,V,N,500,AO);
+  AO = 1.0 - AO.array();
+
+  // Show mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  viewer.callback_key_down = &key_down;
+  key_down(viewer,'2',0);
+  viewer.data().show_lines = false;
+  viewer.core().lighting_factor = 0.0f;
+  viewer.launch();
+}

+ 95 - 95
tutorial/609_Boolean/main.cpp

@@ -1,95 +1,95 @@
-#include <igl/readOFF.h>
-//#undef IGL_STATIC_LIBRARY
-#include <igl/copyleft/cgal/mesh_boolean.h>
-#include <igl/opengl/glfw/Viewer.h>
-
-#include <Eigen/Core>
-#include <iostream>
-
-
-Eigen::MatrixXd VA,VB,VC;
-Eigen::VectorXi J,I;
-Eigen::MatrixXi FA,FB,FC;
-igl::MeshBooleanType boolean_type(
-  igl::MESH_BOOLEAN_TYPE_UNION);
-
-const char * MESH_BOOLEAN_TYPE_NAMES[] =
-{
-  "Union",
-  "Intersect",
-  "Minus",
-  "XOR",
-  "Resolve",
-};
-
-void update(igl::opengl::glfw::Viewer &viewer)
-{
-  igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,boolean_type,VC,FC,J);
-  Eigen::MatrixXd C(FC.rows(),3);
-  for(size_t f = 0;f<C.rows();f++)
-  {
-    if(J(f)<FA.rows())
-    {
-      C.row(f) = Eigen::RowVector3d(1,0,0);
-    }else
-    {
-      C.row(f) = Eigen::RowVector3d(0,1,0);
-    }
-  }
-  viewer.data().clear();
-  viewer.data().set_mesh(VC,FC);
-  viewer.data().set_colors(C);
-  std::cout<<"A "<<MESH_BOOLEAN_TYPE_NAMES[boolean_type]<<" B."<<std::endl;
-}
-
-bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
-{
-  switch(key)
-  {
-    default:
-      return false;
-    case '.':
-      boolean_type =
-        static_cast<igl::MeshBooleanType>(
-          (boolean_type+1)% igl::NUM_MESH_BOOLEAN_TYPES);
-      break;
-    case ',':
-      boolean_type =
-        static_cast<igl::MeshBooleanType>(
-          (boolean_type+igl::NUM_MESH_BOOLEAN_TYPES-1)%
-          igl::NUM_MESH_BOOLEAN_TYPES);
-      break;
-    case '[':
-      viewer.core().camera_dnear -= 0.1;
-      return true;
-    case ']':
-      viewer.core().camera_dnear += 0.1;
-      return true;
-  }
-  update(viewer);
-  return true;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",VA,FA);
-  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",VB,FB);
-  // Plot the mesh with pseudocolors
-  igl::opengl::glfw::Viewer viewer;
-
-  // Initialize
-  update(viewer);
-
-  viewer.data().show_lines = true;
-  viewer.callback_key_down = &key_down;
-  viewer.core().camera_dnear = 3.9;
-  cout<<
-    "Press '.' to switch to next boolean operation type."<<endl<<
-    "Press ',' to switch to previous boolean operation type."<<endl<<
-    "Press ']' to push near cutting plane away from camera."<<endl<<
-    "Press '[' to pull near cutting plane closer to camera."<<endl<<
-    "Hint: investigate _inside_ the model to see orientation changes."<<endl;
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+//#undef IGL_STATIC_LIBRARY
+#include <igl/copyleft/cgal/mesh_boolean.h>
+#include <igl/opengl/glfw/Viewer.h>
+
+#include <Eigen/Core>
+#include <iostream>
+
+
+Eigen::MatrixXd VA,VB,VC;
+Eigen::VectorXi J,I;
+Eigen::MatrixXi FA,FB,FC;
+igl::MeshBooleanType boolean_type(
+  igl::MESH_BOOLEAN_TYPE_UNION);
+
+const char * MESH_BOOLEAN_TYPE_NAMES[] =
+{
+  "Union",
+  "Intersect",
+  "Minus",
+  "XOR",
+  "Resolve",
+};
+
+void update(igl::opengl::glfw::Viewer &viewer)
+{
+  igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,boolean_type,VC,FC,J);
+  Eigen::MatrixXd C(FC.rows(),3);
+  for(size_t f = 0;f<C.rows();f++)
+  {
+    if(J(f)<FA.rows())
+    {
+      C.row(f) = Eigen::RowVector3d(1,0,0);
+    }else
+    {
+      C.row(f) = Eigen::RowVector3d(0,1,0);
+    }
+  }
+  viewer.data().clear();
+  viewer.data().set_mesh(VC,FC);
+  viewer.data().set_colors(C);
+  std::cout<<"A "<<MESH_BOOLEAN_TYPE_NAMES[boolean_type]<<" B."<<std::endl;
+}
+
+bool key_down(igl::opengl::glfw::Viewer &viewer, unsigned char key, int mods)
+{
+  switch(key)
+  {
+    default:
+      return false;
+    case '.':
+      boolean_type =
+        static_cast<igl::MeshBooleanType>(
+          (boolean_type+1)% igl::NUM_MESH_BOOLEAN_TYPES);
+      break;
+    case ',':
+      boolean_type =
+        static_cast<igl::MeshBooleanType>(
+          (boolean_type+igl::NUM_MESH_BOOLEAN_TYPES-1)%
+          igl::NUM_MESH_BOOLEAN_TYPES);
+      break;
+    case '[':
+      viewer.core().camera_dnear -= 0.1;
+      return true;
+    case ']':
+      viewer.core().camera_dnear += 0.1;
+      return true;
+  }
+  update(viewer);
+  return true;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",VA,FA);
+  igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",VB,FB);
+  // Plot the mesh with pseudocolors
+  igl::opengl::glfw::Viewer viewer;
+
+  // Initialize
+  update(viewer);
+
+  viewer.data().show_lines = true;
+  viewer.callback_key_down = &key_down;
+  viewer.core().camera_dnear = 3.9;
+  cout<<
+    "Press '.' to switch to next boolean operation type."<<endl<<
+    "Press ',' to switch to previous boolean operation type."<<endl<<
+    "Press ']' to push near cutting plane away from camera."<<endl<<
+    "Press '[' to pull near cutting plane closer to camera."<<endl<<
+    "Hint: investigate _inside_ the model to see orientation changes."<<endl;
+  viewer.launch();
+}

+ 57 - 57
tutorial/701_Statistics/main.cpp

@@ -1,57 +1,57 @@
-#include <igl/doublearea.h>
-#include <igl/internal_angles.h>
-#include <igl/is_irregular_vertex.h>
-#include <igl/readOBJ.h>
-#include <igl/PI.h>
-#include <Eigen/Core>
-#include <iostream>
-
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  MatrixXd V;
-  MatrixXi F;
-
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/horse_quad.obj",V,F);
-
-  // Count the number of irregular vertices, the border is ignored
-  vector<bool> irregular = igl::is_irregular_vertex(F);
-
-  int vertex_count = V.rows();
-  int irregular_vertex_count = 
-    std::count(irregular.begin(),irregular.end(),true);
-  double irregular_ratio = double(irregular_vertex_count)/vertex_count;
-
-  printf("Irregular vertices: \n%d/%d (%.2f%%)\n",
-    irregular_vertex_count,vertex_count, irregular_ratio*100);
-
-  // Compute areas, min, max and standard deviation
-  VectorXd area;
-  igl::doublearea(V,F,area);
-  area = area.array() / 2;
-
-  double area_avg   = area.mean();
-  double area_min   = area.minCoeff() / area_avg;
-  double area_max   = area.maxCoeff() / area_avg;
-  double area_sigma = sqrt(((area.array()-area_avg)/area_avg).square().mean());
-
-  printf("Areas (Min/Max)/Avg_Area Sigma: \n%.2f/%.2f (%.2f)\n",
-    area_min,area_max,area_sigma);
-
-  // Compute per face angles, min, max and standard deviation
-  MatrixXd angles;
-  igl::internal_angles(V,F,angles);
-  angles = 360.0 * (angles/(2*igl::PI)); // Convert to degrees
-
-  double angle_avg   = angles.mean();
-  double angle_min   = angles.minCoeff();
-  double angle_max   = angles.maxCoeff();
-  double angle_sigma = sqrt( (angles.array()-angle_avg).square().mean() );
-
-  printf("Angles in degrees (Min/Max) Sigma: \n%.2f/%.2f (%.2f)\n",
-    angle_min,angle_max,angle_sigma);
-
-}
+#include <igl/doublearea.h>
+#include <igl/internal_angles.h>
+#include <igl/is_irregular_vertex.h>
+#include <igl/readOBJ.h>
+#include <igl/PI.h>
+#include <Eigen/Core>
+#include <iostream>
+
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  MatrixXd V;
+  MatrixXi F;
+
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/horse_quad.obj",V,F);
+
+  // Count the number of irregular vertices, the border is ignored
+  vector<bool> irregular = igl::is_irregular_vertex(F);
+
+  int vertex_count = V.rows();
+  int irregular_vertex_count = 
+    std::count(irregular.begin(),irregular.end(),true);
+  double irregular_ratio = double(irregular_vertex_count)/vertex_count;
+
+  printf("Irregular vertices: \n%d/%d (%.2f%%)\n",
+    irregular_vertex_count,vertex_count, irregular_ratio*100);
+
+  // Compute areas, min, max and standard deviation
+  VectorXd area;
+  igl::doublearea(V,F,area);
+  area = area.array() / 2;
+
+  double area_avg   = area.mean();
+  double area_min   = area.minCoeff() / area_avg;
+  double area_max   = area.maxCoeff() / area_avg;
+  double area_sigma = sqrt(((area.array()-area_avg)/area_avg).square().mean());
+
+  printf("Areas (Min/Max)/Avg_Area Sigma: \n%.2f/%.2f (%.2f)\n",
+    area_min,area_max,area_sigma);
+
+  // Compute per face angles, min, max and standard deviation
+  MatrixXd angles;
+  igl::internal_angles(V,F,angles);
+  angles = 360.0 * (angles/(2*igl::PI)); // Convert to degrees
+
+  double angle_avg   = angles.mean();
+  double angle_min   = angles.minCoeff();
+  double angle_max   = angles.maxCoeff();
+  double angle_sigma = sqrt( (angles.array()-angle_avg).square().mean() );
+
+  printf("Angles in degrees (Min/Max) Sigma: \n%.2f/%.2f (%.2f)\n",
+    angle_min,angle_max,angle_sigma);
+
+}

+ 146 - 146
tutorial/702_WindingNumber/main.cpp

@@ -1,146 +1,146 @@
-#include <igl/barycenter.h>
-#include <igl/boundary_facets.h>
-#include <igl/parula.h>
-#include <igl/readMESH.h>
-#include <igl/marching_tets.h>
-#include <igl/winding_number.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <Eigen/Sparse>
-#include <iostream>
-
-
-Eigen::MatrixXd V,BC;
-Eigen::VectorXd W;
-Eigen::MatrixXi T,F,G;
-double slice_z = 0.5;
-enum OverLayType
-{
-  OVERLAY_NONE = 0,
-  OVERLAY_INPUT = 1,
-  OVERLAY_OUTPUT = 2,
-  NUM_OVERLAY = 3,
-} overlay = OVERLAY_NONE;
-
-void update_visualization(igl::opengl::glfw::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;
-  VectorXi J;
-  {
-    SparseMatrix<double> bary;
-    // Value of plane's implicit function at all vertices
-    const VectorXd IV = 
-      (V.col(0)*plane(0) + 
-        V.col(1)*plane(1) + 
-        V.col(2)*plane(2)).array()
-      + plane(3);
-    igl::marching_tets(V,T,IV,V_vis,F_vis,J,bary);
-  }
-  VectorXd W_vis = W(J);
-  MatrixXd C_vis;
-  // color without normalizing
-  igl::parula(W_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()+F.rows(),3);
-    C_vis.bottomRows(F.rows()).rowwise() = color;
-  };
-  switch(overlay)
-  {
-    case OVERLAY_INPUT:
-      append_mesh(V,F,RowVector3d(1.,0.894,0.227));
-      break;
-    case OVERLAY_OUTPUT:
-      append_mesh(V,G,RowVector3d(0.8,0.8,0.8));
-      break;
-    default:
-      break;
-  }
-  viewer.data().clear();
-  viewer.data().set_mesh(V_vis,F_vis);
-  viewer.data().set_colors(C_vis);
-  viewer.data().set_face_based(true);
-}
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int mod)
-{
-  switch(key)
-  {
-    default:
-      return false;
-    case ' ':
-      overlay = (OverLayType)((1+(int)overlay)%NUM_OVERLAY);
-      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 input mesh, output mesh or slice "<<endl;
-  cout<<"         through tet-mesh of convex hull."<<endl;
-  cout<<"'.'/','  push back/pull forward slicing plane."<<endl;
-  cout<<endl;
-
-  // Load mesh: (V,T) tet-mesh of convex hull, F contains facets of input
-  // surface mesh _after_ self-intersection resolution
-  igl::readMESH(TUTORIAL_SHARED_PATH "/big-sigcat.mesh",V,T,F);
-
-  // Compute barycenters of all tets
-  igl::barycenter(V,T,BC);
-
-  // Compute generalized winding number at all barycenters
-  cout<<"Computing winding number over all "<<T.rows()<<" tets..."<<endl;
-  igl::winding_number(V,F,BC,W);
-
-  // Extract interior tets
-  MatrixXi CT((W.array()>0.5).count(),4);
-  {
-    size_t k = 0;
-    for(size_t t = 0;t<T.rows();t++)
-    {
-      if(W(t)>0.5)
-      {
-        CT.row(k) = T.row(t);
-        k++;
-      }
-    }
-  }
-  // find bounary facets of interior tets
-  igl::boundary_facets(CT,G);
-  // boundary_facets seems to be reversed...
-  G = G.rowwise().reverse().eval();
-
-  // normalize
-  W = (W.array() - W.minCoeff())/(W.maxCoeff()-W.minCoeff());
-
-  // Plot the generated mesh
-  igl::opengl::glfw::Viewer viewer;
-  update_visualization(viewer);
-  viewer.callback_key_down = &key_down;
-  viewer.launch();
-}
+#include <igl/barycenter.h>
+#include <igl/boundary_facets.h>
+#include <igl/parula.h>
+#include <igl/readMESH.h>
+#include <igl/marching_tets.h>
+#include <igl/winding_number.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <Eigen/Sparse>
+#include <iostream>
+
+
+Eigen::MatrixXd V,BC;
+Eigen::VectorXd W;
+Eigen::MatrixXi T,F,G;
+double slice_z = 0.5;
+enum OverLayType
+{
+  OVERLAY_NONE = 0,
+  OVERLAY_INPUT = 1,
+  OVERLAY_OUTPUT = 2,
+  NUM_OVERLAY = 3,
+} overlay = OVERLAY_NONE;
+
+void update_visualization(igl::opengl::glfw::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;
+  VectorXi J;
+  {
+    SparseMatrix<double> bary;
+    // Value of plane's implicit function at all vertices
+    const VectorXd IV = 
+      (V.col(0)*plane(0) + 
+        V.col(1)*plane(1) + 
+        V.col(2)*plane(2)).array()
+      + plane(3);
+    igl::marching_tets(V,T,IV,V_vis,F_vis,J,bary);
+  }
+  VectorXd W_vis = W(J);
+  MatrixXd C_vis;
+  // color without normalizing
+  igl::parula(W_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()+F.rows(),3);
+    C_vis.bottomRows(F.rows()).rowwise() = color;
+  };
+  switch(overlay)
+  {
+    case OVERLAY_INPUT:
+      append_mesh(V,F,RowVector3d(1.,0.894,0.227));
+      break;
+    case OVERLAY_OUTPUT:
+      append_mesh(V,G,RowVector3d(0.8,0.8,0.8));
+      break;
+    default:
+      break;
+  }
+  viewer.data().clear();
+  viewer.data().set_mesh(V_vis,F_vis);
+  viewer.data().set_colors(C_vis);
+  viewer.data().set_face_based(true);
+}
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int mod)
+{
+  switch(key)
+  {
+    default:
+      return false;
+    case ' ':
+      overlay = (OverLayType)((1+(int)overlay)%NUM_OVERLAY);
+      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 input mesh, output mesh or slice "<<endl;
+  cout<<"         through tet-mesh of convex hull."<<endl;
+  cout<<"'.'/','  push back/pull forward slicing plane."<<endl;
+  cout<<endl;
+
+  // Load mesh: (V,T) tet-mesh of convex hull, F contains facets of input
+  // surface mesh _after_ self-intersection resolution
+  igl::readMESH(TUTORIAL_SHARED_PATH "/big-sigcat.mesh",V,T,F);
+
+  // Compute barycenters of all tets
+  igl::barycenter(V,T,BC);
+
+  // Compute generalized winding number at all barycenters
+  cout<<"Computing winding number over all "<<T.rows()<<" tets..."<<endl;
+  igl::winding_number(V,F,BC,W);
+
+  // Extract interior tets
+  MatrixXi CT((W.array()>0.5).count(),4);
+  {
+    size_t k = 0;
+    for(size_t t = 0;t<T.rows();t++)
+    {
+      if(W(t)>0.5)
+      {
+        CT.row(k) = T.row(t);
+        k++;
+      }
+    }
+  }
+  // find bounary facets of interior tets
+  igl::boundary_facets(CT,G);
+  // boundary_facets seems to be reversed...
+  G = G.rowwise().reverse().eval();
+
+  // normalize
+  W = (W.array() - W.minCoeff())/(W.maxCoeff()-W.minCoeff());
+
+  // Plot the generated mesh
+  igl::opengl::glfw::Viewer viewer;
+  update_visualization(viewer);
+  viewer.callback_key_down = &key_down;
+  viewer.launch();
+}

+ 236 - 236
tutorial/704_SignedDistance/main.cpp

@@ -1,236 +1,236 @@
-#include <igl/cat.h>
-#include <igl/edge_lengths.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/marching_tets.h>
-#include <igl/upsample.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/writeOBJ.h>
-#include <Eigen/Sparse>
-#include <iostream>
-
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi T,F;
-
-igl::AABB<Eigen::MatrixXd,3> tree;
-igl::FastWindingNumberBVH fwn_bvh;
-
-Eigen::MatrixXd FN,VN,EN;
-Eigen::MatrixXi E;
-Eigen::VectorXi EMAP;
-double max_distance = 1;
-
-double slice_z = 0.5;
-bool overlay = false;
-
-bool useFastWindingNumber = false;
-
-
-const Eigen::MatrixXd CM = 
-  (Eigen::MatrixXd(50,3)<<
-  242,242,242,
-  247,251,253,
-  228,234,238,
-  233,243,249,
-  214,227,234,
-  217,234,244,
-  199,218,230,
-  203,226,240,
-  186,211,226,
-  187,217,236,
-  171,203,222,
-  173,209,232,
-  157,195,218,
-  158,201,228,
-  142,187,214,
-  143,193,223,
-  129,179,210,
-  128,185,219,
-  114,171,206,
-  112,176,215,
-  100,163,202,
-  98,168,211,
-  86,156,198,
-  82,159,207,
-  71,148,194,
-  255,247,223,
-  242,230,204,
-  255,235,206,
-  242,219,189,
-  255,225,191,
-  242,209,175,
-  255,214,176,
-  242,198,159,
-  255,203,160,
-  242,188,145,
-  255,192,145,
-  242,177,129,
-  255,181,128,
-  242,167,115,
-  255,170,113,
-  242,157,101,
-  255,159,97,
-  242,146,85,
-  255,148,82,
-  242,136,71,
-  255,137,65,
-  242,125,55,
-  255,126,50,
-  242,115,41,
-  255,116,36).finished()/255.0;
-
-void update_visualization(igl::opengl::glfw::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;
-    {
-      // Value of plane's implicit function at all vertices
-      const VectorXd IV = 
-        (V.col(0)*plane(0) + 
-         V.col(1)*plane(1) + 
-         V.col(2)*plane(2)).array()
-        + plane(3);
-      igl::marching_tets(V,T,IV,V_vis,F_vis,J,bary);
-      igl::writeOBJ("vis.obj",V_vis,F_vis);
-    }
-    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;
-
-  if (!useFastWindingNumber)
-  {
-    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);
-  } else {
-    signed_distance_fast_winding_number(V_vis, V, F, tree, fwn_bvh, S_vis);
-  }    
-
-  const auto & append_mesh = [&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;
-  };
-  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_colormap(CM);
-  viewer.data().set_data(S_vis);
-  viewer.core().lighting_factor = overlay;
-}
-
-bool key_down(igl::opengl::glfw::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;
-    case '1':
-      useFastWindingNumber = true;
-      break;
-    case '2':
-      useFastWindingNumber = false;
-      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<< "1/2 toggle between fast winding number (1) and pseudonormal (2) signing. \n";
-  cout<<endl;
-
-  // Load mesh: (V,T) tet-mesh of convex hull, F contains original surface
-  // triangles
-  igl::readMESH(TUTORIAL_SHARED_PATH "/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());
-  }
-
-  // Fast winding and Pseudo normal depend on differnt AABB trees... We initialize both here.
-
-  // Pseudonormal setup...
-  // 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);
-
-  // fast winding number setup (just init fwn bvh)
-  igl::fast_winding_number(V, F, 2, fwn_bvh);
-
-  // Plot the generated mesh
-  igl::opengl::glfw::Viewer viewer;
-  update_visualization(viewer);
-  viewer.callback_key_down = &key_down;
-  viewer.data().show_lines = false;
-  viewer.launch();
-}
+#include <igl/cat.h>
+#include <igl/edge_lengths.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/marching_tets.h>
+#include <igl/upsample.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/writeOBJ.h>
+#include <Eigen/Sparse>
+#include <iostream>
+
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi T,F;
+
+igl::AABB<Eigen::MatrixXd,3> tree;
+igl::FastWindingNumberBVH fwn_bvh;
+
+Eigen::MatrixXd FN,VN,EN;
+Eigen::MatrixXi E;
+Eigen::VectorXi EMAP;
+double max_distance = 1;
+
+double slice_z = 0.5;
+bool overlay = false;
+
+bool useFastWindingNumber = false;
+
+
+const Eigen::MatrixXd CM = 
+  (Eigen::MatrixXd(50,3)<<
+  242,242,242,
+  247,251,253,
+  228,234,238,
+  233,243,249,
+  214,227,234,
+  217,234,244,
+  199,218,230,
+  203,226,240,
+  186,211,226,
+  187,217,236,
+  171,203,222,
+  173,209,232,
+  157,195,218,
+  158,201,228,
+  142,187,214,
+  143,193,223,
+  129,179,210,
+  128,185,219,
+  114,171,206,
+  112,176,215,
+  100,163,202,
+  98,168,211,
+  86,156,198,
+  82,159,207,
+  71,148,194,
+  255,247,223,
+  242,230,204,
+  255,235,206,
+  242,219,189,
+  255,225,191,
+  242,209,175,
+  255,214,176,
+  242,198,159,
+  255,203,160,
+  242,188,145,
+  255,192,145,
+  242,177,129,
+  255,181,128,
+  242,167,115,
+  255,170,113,
+  242,157,101,
+  255,159,97,
+  242,146,85,
+  255,148,82,
+  242,136,71,
+  255,137,65,
+  242,125,55,
+  255,126,50,
+  242,115,41,
+  255,116,36).finished()/255.0;
+
+void update_visualization(igl::opengl::glfw::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;
+    {
+      // Value of plane's implicit function at all vertices
+      const VectorXd IV = 
+        (V.col(0)*plane(0) + 
+         V.col(1)*plane(1) + 
+         V.col(2)*plane(2)).array()
+        + plane(3);
+      igl::marching_tets(V,T,IV,V_vis,F_vis,J,bary);
+      igl::writeOBJ("vis.obj",V_vis,F_vis);
+    }
+    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;
+
+  if (!useFastWindingNumber)
+  {
+    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);
+  } else {
+    signed_distance_fast_winding_number(V_vis, V, F, tree, fwn_bvh, S_vis);
+  }    
+
+  const auto & append_mesh = [&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;
+  };
+  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_colormap(CM);
+  viewer.data().set_data(S_vis);
+  viewer.core().lighting_factor = overlay;
+}
+
+bool key_down(igl::opengl::glfw::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;
+    case '1':
+      useFastWindingNumber = true;
+      break;
+    case '2':
+      useFastWindingNumber = false;
+      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<< "1/2 toggle between fast winding number (1) and pseudonormal (2) signing. \n";
+  cout<<endl;
+
+  // Load mesh: (V,T) tet-mesh of convex hull, F contains original surface
+  // triangles
+  igl::readMESH(TUTORIAL_SHARED_PATH "/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());
+  }
+
+  // Fast winding and Pseudo normal depend on differnt AABB trees... We initialize both here.
+
+  // Pseudonormal setup...
+  // 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);
+
+  // fast winding number setup (just init fwn bvh)
+  igl::fast_winding_number(V, F, 2, fwn_bvh);
+
+  // Plot the generated mesh
+  igl::opengl::glfw::Viewer viewer;
+  update_visualization(viewer);
+  viewer.callback_key_down = &key_down;
+  viewer.data().show_lines = false;
+  viewer.launch();
+}

+ 108 - 108
tutorial/706_FacetOrientation/main.cpp

@@ -1,108 +1,108 @@
-#include <igl/read_triangle_mesh.h>
-#include <igl/randperm.h>
-#include <igl/orientable_patches.h>
-#include <igl/hsv_to_rgb.h>
-#include <igl/embree/reorient_facets_raycast.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <vector>
-
-igl::opengl::glfw::Viewer viewer;
-Eigen::MatrixXd V;
-std::vector<Eigen::VectorXi> C(2);
-std::vector<Eigen::MatrixXd> RGBcolors(2);
-Eigen::MatrixXi F;
-std::vector<Eigen::MatrixXi> FF(2);
-bool is_showing_reoriented = false;
-bool facetwise = false;
-
-int main(int argc, char * argv[])
-{
-  using namespace std;
-  cout<<R"(
-Usage:
-
-[space]  Toggle between original and reoriented faces
-F,f      Toggle between patchwise and facetwise reorientation
-S,s      Scramble colors
-)";
-  igl::read_triangle_mesh(TUTORIAL_SHARED_PATH "/truck.obj",V,F);
-
-  const auto & scramble_colors = []()
-  {
-    for(int pass = 0;pass<2;pass++)
-    {
-      Eigen::VectorXi R;
-      igl::randperm(C[pass].maxCoeff()+1,R);
-      C[pass] = R(C[pass]).eval();
-      Eigen::MatrixXd HSV(C[pass].rows(),3);
-      HSV.col(0) = 
-        360.*C[pass].array().cast<double>()/(double)C[pass].maxCoeff();
-      HSV.rightCols(2).setConstant(1.0);
-      igl::hsv_to_rgb(HSV,RGBcolors[pass]);
-    }
-    viewer.data().set_colors(RGBcolors[facetwise]);
-  };
-
-  viewer.callback_key_pressed = 
-    [&scramble_colors]
-    (igl::opengl::glfw::Viewer& /*viewer*/, unsigned int key, int mod)->bool
-  {
-    switch(key)
-    {
-    default:
-      return false;
-    case 'F':
-    case 'f':
-    {
-      facetwise = !facetwise;
-      break;
-    }
-    case 'S':
-    case 's':
-    {
-      scramble_colors();
-      return true;
-    }
-    case ' ':
-    {
-      is_showing_reoriented = !is_showing_reoriented;
-      break;
-    }
-    }
-    viewer.data().clear();
-    viewer.data().set_mesh(V,is_showing_reoriented?FF[facetwise]:F);
-    viewer.data().set_colors(RGBcolors[facetwise]);
-    return true;
-  };
-
-
-  // Compute patches
-  for(int pass = 0;pass<2;pass++)
-  {
-    Eigen::VectorXi I;
-    igl::embree::reorient_facets_raycast(
-      V,F,F.rows()*100,10,pass==1,false,false,I,C[pass]);
-    // apply reorientation
-    FF[pass].conservativeResize(F.rows(),F.cols());
-    for(int i = 0;i<I.rows();i++)
-    {
-      if(I(i))
-      {
-        FF[pass].row(i) = (F.row(i).reverse()).eval();
-      }else
-      {
-        FF[pass].row(i) = F.row(i);
-      }
-    }
-  }
-
-  viewer.data().set_mesh(V,is_showing_reoriented?FF[facetwise]:F);
-  viewer.data().set_face_based(true);
-  scramble_colors();
-  viewer.launch();
-}
-
-
+#include <igl/read_triangle_mesh.h>
+#include <igl/randperm.h>
+#include <igl/orientable_patches.h>
+#include <igl/hsv_to_rgb.h>
+#include <igl/embree/reorient_facets_raycast.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+igl::opengl::glfw::Viewer viewer;
+Eigen::MatrixXd V;
+std::vector<Eigen::VectorXi> C(2);
+std::vector<Eigen::MatrixXd> RGBcolors(2);
+Eigen::MatrixXi F;
+std::vector<Eigen::MatrixXi> FF(2);
+bool is_showing_reoriented = false;
+bool facetwise = false;
+
+int main(int argc, char * argv[])
+{
+  using namespace std;
+  cout<<R"(
+Usage:
+
+[space]  Toggle between original and reoriented faces
+F,f      Toggle between patchwise and facetwise reorientation
+S,s      Scramble colors
+)";
+  igl::read_triangle_mesh(TUTORIAL_SHARED_PATH "/truck.obj",V,F);
+
+  const auto & scramble_colors = []()
+  {
+    for(int pass = 0;pass<2;pass++)
+    {
+      Eigen::VectorXi R;
+      igl::randperm(C[pass].maxCoeff()+1,R);
+      C[pass] = R(C[pass]).eval();
+      Eigen::MatrixXd HSV(C[pass].rows(),3);
+      HSV.col(0) = 
+        360.*C[pass].array().cast<double>()/(double)C[pass].maxCoeff();
+      HSV.rightCols(2).setConstant(1.0);
+      igl::hsv_to_rgb(HSV,RGBcolors[pass]);
+    }
+    viewer.data().set_colors(RGBcolors[facetwise]);
+  };
+
+  viewer.callback_key_pressed = 
+    [&scramble_colors]
+    (igl::opengl::glfw::Viewer& /*viewer*/, unsigned int key, int mod)->bool
+  {
+    switch(key)
+    {
+    default:
+      return false;
+    case 'F':
+    case 'f':
+    {
+      facetwise = !facetwise;
+      break;
+    }
+    case 'S':
+    case 's':
+    {
+      scramble_colors();
+      return true;
+    }
+    case ' ':
+    {
+      is_showing_reoriented = !is_showing_reoriented;
+      break;
+    }
+    }
+    viewer.data().clear();
+    viewer.data().set_mesh(V,is_showing_reoriented?FF[facetwise]:F);
+    viewer.data().set_colors(RGBcolors[facetwise]);
+    return true;
+  };
+
+
+  // Compute patches
+  for(int pass = 0;pass<2;pass++)
+  {
+    Eigen::VectorXi I;
+    igl::embree::reorient_facets_raycast(
+      V,F,F.rows()*100,10,pass==1,false,false,I,C[pass]);
+    // apply reorientation
+    FF[pass].conservativeResize(F.rows(),F.cols());
+    for(int i = 0;i<I.rows();i++)
+    {
+      if(I(i))
+      {
+        FF[pass].row(i) = (F.row(i).reverse()).eval();
+      }else
+      {
+        FF[pass].row(i) = F.row(i);
+      }
+    }
+  }
+
+  viewer.data().set_mesh(V,is_showing_reoriented?FF[facetwise]:F);
+  viewer.data().set_face_based(true);
+  scramble_colors();
+  viewer.launch();
+}
+
+

+ 45 - 45
tutorial/708_Picking/main.cpp

@@ -1,45 +1,45 @@
-#include <igl/readOFF.h>
-#include <igl/unproject_onto_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  // Mesh with per-face color
-  Eigen::MatrixXd V, C;
-  Eigen::MatrixXi F;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V, F);
-
-  // Initialize white
-  C = Eigen::MatrixXd::Constant(F.rows(),3,1);
-  igl::opengl::glfw::Viewer viewer;
-  viewer.callback_mouse_down =
-    [&V,&F,&C](igl::opengl::glfw::Viewer& viewer, int, int)->bool
-  {
-    int fid;
-    Eigen::Vector3f bc;
-    // Cast a ray in the view direction starting from the mouse position
-    double x = viewer.current_mouse_x;
-    double y = viewer.core().viewport(3) - viewer.current_mouse_y;
-    if(igl::unproject_onto_mesh(Eigen::Vector2f(x,y), viewer.core().view,
-      viewer.core().proj, viewer.core().viewport, V, F, fid, bc))
-    {
-      // paint hit red
-      C.row(fid)<<1,0,0;
-      viewer.data().set_colors(C);
-      return true;
-    }
-    return false;
-  };
-  std::cout<<R"(Usage:
-  [click]  Pick face on shape
-
-)";
-  // Show mesh
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_colors(C);
-  viewer.data().show_lines = false;
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/unproject_onto_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  // Mesh with per-face color
+  Eigen::MatrixXd V, C;
+  Eigen::MatrixXi F;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/fertility.off", V, F);
+
+  // Initialize white
+  C = Eigen::MatrixXd::Constant(F.rows(),3,1);
+  igl::opengl::glfw::Viewer viewer;
+  viewer.callback_mouse_down =
+    [&V,&F,&C](igl::opengl::glfw::Viewer& viewer, int, int)->bool
+  {
+    int fid;
+    Eigen::Vector3f bc;
+    // Cast a ray in the view direction starting from the mouse position
+    double x = viewer.current_mouse_x;
+    double y = viewer.core().viewport(3) - viewer.current_mouse_y;
+    if(igl::unproject_onto_mesh(Eigen::Vector2f(x,y), viewer.core().view,
+      viewer.core().proj, viewer.core().viewport, V, F, fid, bc))
+    {
+      // paint hit red
+      C.row(fid)<<1,0,0;
+      viewer.data().set_colors(C);
+      return true;
+    }
+    return false;
+  };
+  std::cout<<R"(Usage:
+  [click]  Pick face on shape
+
+)";
+  // Show mesh
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_colors(C);
+  viewer.data().show_lines = false;
+  viewer.launch();
+}

+ 126 - 126
tutorial/710_SCAF/main.cpp

@@ -1,126 +1,126 @@
-#include <igl/triangle/scaf.h>
-#include <igl/arap.h>
-#include <igl/boundary_loop.h>
-#include <igl/harmonic.h>
-#include <igl/map_vertices_to_circle.h>
-#include <igl/readOBJ.h>
-#include <igl/Timer.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/MappingEnergyType.h>
-#include <igl/doublearea.h>
-#include <igl/PI.h>
-#include <igl/flipped_triangles.h>
-#include <igl/topological_hole_fill.h>
-
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-Eigen::MatrixXd V_uv;
-igl::Timer timer;
-igl::triangle::SCAFData scaf_data;
-
-bool show_uv = false;
-float uv_scale = 0.2f;
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  if (key == '1')
-    show_uv = false;
-  else if (key == '2')
-    show_uv = true;
-
-  if (key == ' ')
-  {
-    timer.start();
-    igl::triangle::scaf_solve(scaf_data, 1);
-    std::cout << "time = " << timer.getElapsedTime() << std::endl;
-  }
-
-  const auto& V_uv = uv_scale * scaf_data.w_uv.topRows(V.rows());
-  if (show_uv)
-  {
-    viewer.data().clear();
-    viewer.data().set_mesh(V_uv,F);
-    viewer.data().set_uv(V_uv);
-    viewer.core().align_camera_center(V_uv,F);
-  }
-  else
-  {
-    viewer.data().set_mesh(V,F);
-    viewer.data().set_uv(V_uv);
-    viewer.core().align_camera_center(V,F);
-  }
-
-  viewer.data().compute_normals();
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace std;
-  // Load a mesh in OFF format
-  igl::readOBJ(TUTORIAL_SHARED_PATH "/camel_b.obj", V, F);
-
-  Eigen::MatrixXd bnd_uv, uv_init;
-
-  Eigen::VectorXd M;
-  igl::doublearea(V, F, M);
-  std::vector<std::vector<int>> all_bnds;
-  igl::boundary_loop(F, all_bnds);
-
-  // Heuristic primary boundary choice: longest
-  auto primary_bnd = std::max_element(all_bnds.begin(), all_bnds.end(), [](const std::vector<int> &a, const std::vector<int> &b) { return a.size()<b.size(); });
-
-  Eigen::VectorXi bnd = Eigen::Map<Eigen::VectorXi>(primary_bnd->data(), primary_bnd->size());
-
-  igl::map_vertices_to_circle(V, bnd, bnd_uv);
-  bnd_uv *= sqrt(M.sum() / (2 * igl::PI));
-  if (all_bnds.size() == 1)
-  {
-    if (bnd.rows() == V.rows()) // case: all vertex on boundary
-    {
-      uv_init.resize(V.rows(), 2);
-      for (int i = 0; i < bnd.rows(); i++)
-        uv_init.row(bnd(i)) = bnd_uv.row(i);
-    }
-    else
-    {
-      igl::harmonic(V, F, bnd, bnd_uv, 1, uv_init);
-      if (igl::flipped_triangles(uv_init, F).size() != 0)
-        igl::harmonic(F, bnd, bnd_uv, 1, uv_init); // fallback uniform laplacian
-    }
-  }
-  else
-  {
-    // if there is a hole, fill it and erase additional vertices.
-    all_bnds.erase(primary_bnd);
-    Eigen::MatrixXi F_filled;
-    igl::topological_hole_fill(F, all_bnds, F_filled);
-    igl::harmonic(F_filled, bnd, bnd_uv ,1, uv_init);
-    uv_init.conservativeResize(V.rows(), 2);
-  }
-
-  Eigen::VectorXi b; Eigen::MatrixXd bc;
-  igl::triangle::scaf_precompute(V, F, uv_init, scaf_data, igl::MappingEnergyType::SYMMETRIC_DIRICHLET, b, bc, 0);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-  const auto& V_uv = uv_scale * scaf_data.w_uv.topRows(V.rows());
-  viewer.data().set_uv(V_uv);
-  viewer.callback_key_down = &key_down;
-
-  // Enable wireframe
-  viewer.data().show_lines = true;
-
-  // Draw checkerboard texture
-  viewer.data().show_texture = true;
-
-
-  std::cerr << "Press space for running an iteration." << std::endl;
-  std::cerr << "Press 1 for Mesh 2 for UV" << std::endl;
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/triangle/scaf.h>
+#include <igl/arap.h>
+#include <igl/boundary_loop.h>
+#include <igl/harmonic.h>
+#include <igl/map_vertices_to_circle.h>
+#include <igl/readOBJ.h>
+#include <igl/Timer.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/MappingEnergyType.h>
+#include <igl/doublearea.h>
+#include <igl/PI.h>
+#include <igl/flipped_triangles.h>
+#include <igl/topological_hole_fill.h>
+
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+Eigen::MatrixXd V_uv;
+igl::Timer timer;
+igl::triangle::SCAFData scaf_data;
+
+bool show_uv = false;
+float uv_scale = 0.2f;
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  if (key == '1')
+    show_uv = false;
+  else if (key == '2')
+    show_uv = true;
+
+  if (key == ' ')
+  {
+    timer.start();
+    igl::triangle::scaf_solve(scaf_data, 1);
+    std::cout << "time = " << timer.getElapsedTime() << std::endl;
+  }
+
+  const auto& V_uv = uv_scale * scaf_data.w_uv.topRows(V.rows());
+  if (show_uv)
+  {
+    viewer.data().clear();
+    viewer.data().set_mesh(V_uv,F);
+    viewer.data().set_uv(V_uv);
+    viewer.core().align_camera_center(V_uv,F);
+  }
+  else
+  {
+    viewer.data().set_mesh(V,F);
+    viewer.data().set_uv(V_uv);
+    viewer.core().align_camera_center(V,F);
+  }
+
+  viewer.data().compute_normals();
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace std;
+  // Load a mesh in OFF format
+  igl::readOBJ(TUTORIAL_SHARED_PATH "/camel_b.obj", V, F);
+
+  Eigen::MatrixXd bnd_uv, uv_init;
+
+  Eigen::VectorXd M;
+  igl::doublearea(V, F, M);
+  std::vector<std::vector<int>> all_bnds;
+  igl::boundary_loop(F, all_bnds);
+
+  // Heuristic primary boundary choice: longest
+  auto primary_bnd = std::max_element(all_bnds.begin(), all_bnds.end(), [](const std::vector<int> &a, const std::vector<int> &b) { return a.size()<b.size(); });
+
+  Eigen::VectorXi bnd = Eigen::Map<Eigen::VectorXi>(primary_bnd->data(), primary_bnd->size());
+
+  igl::map_vertices_to_circle(V, bnd, bnd_uv);
+  bnd_uv *= sqrt(M.sum() / (2 * igl::PI));
+  if (all_bnds.size() == 1)
+  {
+    if (bnd.rows() == V.rows()) // case: all vertex on boundary
+    {
+      uv_init.resize(V.rows(), 2);
+      for (int i = 0; i < bnd.rows(); i++)
+        uv_init.row(bnd(i)) = bnd_uv.row(i);
+    }
+    else
+    {
+      igl::harmonic(V, F, bnd, bnd_uv, 1, uv_init);
+      if (igl::flipped_triangles(uv_init, F).size() != 0)
+        igl::harmonic(F, bnd, bnd_uv, 1, uv_init); // fallback uniform laplacian
+    }
+  }
+  else
+  {
+    // if there is a hole, fill it and erase additional vertices.
+    all_bnds.erase(primary_bnd);
+    Eigen::MatrixXi F_filled;
+    igl::topological_hole_fill(F, all_bnds, F_filled);
+    igl::harmonic(F_filled, bnd, bnd_uv ,1, uv_init);
+    uv_init.conservativeResize(V.rows(), 2);
+  }
+
+  Eigen::VectorXi b; Eigen::MatrixXd bc;
+  igl::triangle::scaf_precompute(V, F, uv_init, scaf_data, igl::MappingEnergyType::SYMMETRIC_DIRICHLET, b, bc, 0);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+  const auto& V_uv = uv_scale * scaf_data.w_uv.topRows(V.rows());
+  viewer.data().set_uv(V_uv);
+  viewer.callback_key_down = &key_down;
+
+  // Enable wireframe
+  viewer.data().show_lines = true;
+
+  // Draw checkerboard texture
+  viewer.data().show_texture = true;
+
+
+  std::cerr << "Press space for running an iteration." << std::endl;
+  std::cerr << "Press 1 for Mesh 2 for UV" << std::endl;
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 152 - 152
tutorial/803_ShapeUp/main.cpp

@@ -1,152 +1,152 @@
-#include <igl/avg_edge_length.h>
-#include <igl/barycenter.h>
-#include <igl/jet.h>
-#include <igl/shapeup.h>
-#include <igl/quad_planarity.h>
-#include <igl/readDMAT.h>
-#include <igl/readOFF.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/PI.h>
-#include <vector>
-#include <cstdlib>
-
-// Quad mesh loaded
-Eigen::MatrixXd VQC;
-Eigen::MatrixXi FQC;
-Eigen::MatrixXi E;
-Eigen::MatrixXi FQCtri;
-Eigen::MatrixXd PQC0, PQC1, PQC2, PQC3;
-// Euclidean-regular quad mesh
-Eigen::MatrixXd VQCregular;
-Eigen::MatrixXi FQCtriregular;
-Eigen::MatrixXd PQC0regular, PQC1regular, PQC2regular, PQC3regular;
-
-igl::ShapeupData su_data;
-
-
-
-// Scale for visualizing the fields
-double global_scale; //TODO: not used
-
-void quadAngleRegularity(const Eigen::MatrixXd& V, const Eigen::MatrixXi& Q, Eigen::VectorXd& angleRegularity)
-{
-  angleRegularity.conservativeResize(Q.rows());
-  angleRegularity.setZero();
-  for (int i=0;i<Q.rows();i++){
-    for (int j=0;j<4;j++){
-      Eigen::RowVectorXd v21=(V.row(Q(i,j))-V.row(Q(i,(j+1)%4))).normalized();
-      Eigen::RowVectorXd v23=(V.row(Q(i,(j+2)%4))-V.row(Q(i,(j+1)%4))).normalized();
-
-      angleRegularity(i)+=(abs(acos(v21.dot(v23))-igl::PI/2.0)/(igl::PI/2.0))/4.0;
-    }
-  }
-}
-
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
-{
-  using namespace std;
-  using namespace Eigen;
-
-  // Plot the original quad mesh
-
-  if (key == '1')
-  {
-    viewer.data().clear();
-    // Draw the triangulated quad mesh
-    viewer.data().set_mesh(VQC, FQCtri);
-
-    // Assign a color to each quad that corresponds to the average deviation of each angle from pi/2
-    VectorXd angleRegularity(FQC.rows());
-    quadAngleRegularity( VQC, FQC, angleRegularity);
-    MatrixXd Ct;
-    igl::jet(angleRegularity, 0.0, 0.05, Ct);
-    MatrixXd C(FQCtri.rows(),3);
-    C << Ct, Ct;
-    viewer.data().set_colors(C);
-
-    // Plot a line for each edge of the quad mesh
-    viewer.data().add_edges(PQC0, PQC1, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC1, PQC2, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC2, PQC3, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC3, PQC0, Eigen::RowVector3d(0,0,0));
-  }
-
-  // Plot the planarized quad mesh
-  if (key == '2')
-  {
-    viewer.data().clear();
-    // Draw the triangulated quad mesh
-    viewer.data().set_mesh(VQCregular, FQCtri);
-
-    // Assign a color to each quad that corresponds to its planarity
-    VectorXd angleRegularity(FQC.rows());
-    quadAngleRegularity( VQCregular, FQC, angleRegularity);
-    MatrixXd Ct;
-    igl::jet(angleRegularity, 0, 0.05, Ct);
-    MatrixXd C(FQCtri.rows(),3);
-    C << Ct, Ct;
-    viewer.data().set_colors(C);
-
-    // Plot a line for each edge of the quad mesh
-    viewer.data().add_edges(PQC0regular, PQC1regular, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC1regular, PQC2regular, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC2regular, PQC3regular, Eigen::RowVector3d(0,0,0));
-    viewer.data().add_edges(PQC3regular, PQC0regular, Eigen::RowVector3d(0,0,0));
-  }
-
-  return false;
-}
-
-int main(int argc, char *argv[])
-{
-  using namespace Eigen;
-  using namespace std;
-
-  // Load a quad mesh
-  igl::readOFF(TUTORIAL_SHARED_PATH "/halftunnel.off", VQC, FQC);
-
-  // Convert it in a triangle mesh
-  FQCtri.resize(2*FQC.rows(), 3);
-  FQCtri <<  FQC.col(0),FQC.col(1),FQC.col(2),
-             FQC.col(2),FQC.col(3),FQC.col(0);
-  PQC0 = VQC(FQC.col(0).eval(), Eigen::all);
-  PQC1 = VQC(FQC.col(1).eval(), Eigen::all);
-  PQC2 = VQC(FQC.col(2).eval(), Eigen::all);
-  PQC3 = VQC(FQC.col(3).eval(), Eigen::all);
-
-  // Create a planar version with ShapeUp
-  //igl::planarize_quad_mesh(VQC, FQC, 100, 0.005, VQCregular);
-
-  E.resize(FQC.size(),2);
-  E.col(0)<<FQC.col(0),FQC.col(1),FQC.col(2),FQC.col(3);
-  E.col(1)<<FQC.col(1),FQC.col(2),FQC.col(3),FQC.col(0);
-
-  VectorXi b(1); b(0)=0;  //setting the first vertex to be the same.
-
-  VectorXd wShape=VectorXd::Constant(FQC.rows(),1.0);
-  VectorXd wSmooth=VectorXd::Constant(E.rows(),1.0);
-  MatrixXd bc(1,3); bc<<VQC.row(0);
-
-  VectorXi array_of_fours=VectorXi::Constant(FQC.rows(),4);
-  igl::shapeup_projection_function localFunction(igl::shapeup_regular_face_projection);
-
-  su_data.maxIterations=200;
-  shapeup_precomputation(VQC, array_of_fours,FQC,E,b,wShape, wSmooth,su_data);
-  shapeup_solve(bc,localFunction, VQC,su_data, false,VQCregular);
-
-
-  // Convert the planarized mesh to triangles
-  PQC0regular = VQCregular(FQC.col(0).eval(), Eigen::all);
-  PQC1regular = VQCregular(FQC.col(1).eval(), Eigen::all);
-  PQC2regular = VQCregular(FQC.col(2).eval(), Eigen::all);
-  PQC3regular = VQCregular(FQC.col(3).eval(), Eigen::all);
-
-  // Launch the viewer
-  igl::opengl::glfw::Viewer viewer;
-  key_down(viewer,'1',0);
-  viewer.data().invert_normals = true;
-  viewer.data().show_lines = false;
-  viewer.callback_key_down = &key_down;
-  viewer.launch();
-}
+#include <igl/avg_edge_length.h>
+#include <igl/barycenter.h>
+#include <igl/jet.h>
+#include <igl/shapeup.h>
+#include <igl/quad_planarity.h>
+#include <igl/readDMAT.h>
+#include <igl/readOFF.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/PI.h>
+#include <vector>
+#include <cstdlib>
+
+// Quad mesh loaded
+Eigen::MatrixXd VQC;
+Eigen::MatrixXi FQC;
+Eigen::MatrixXi E;
+Eigen::MatrixXi FQCtri;
+Eigen::MatrixXd PQC0, PQC1, PQC2, PQC3;
+// Euclidean-regular quad mesh
+Eigen::MatrixXd VQCregular;
+Eigen::MatrixXi FQCtriregular;
+Eigen::MatrixXd PQC0regular, PQC1regular, PQC2regular, PQC3regular;
+
+igl::ShapeupData su_data;
+
+
+
+// Scale for visualizing the fields
+double global_scale; //TODO: not used
+
+void quadAngleRegularity(const Eigen::MatrixXd& V, const Eigen::MatrixXi& Q, Eigen::VectorXd& angleRegularity)
+{
+  angleRegularity.conservativeResize(Q.rows());
+  angleRegularity.setZero();
+  for (int i=0;i<Q.rows();i++){
+    for (int j=0;j<4;j++){
+      Eigen::RowVectorXd v21=(V.row(Q(i,j))-V.row(Q(i,(j+1)%4))).normalized();
+      Eigen::RowVectorXd v23=(V.row(Q(i,(j+2)%4))-V.row(Q(i,(j+1)%4))).normalized();
+
+      angleRegularity(i)+=(abs(acos(v21.dot(v23))-igl::PI/2.0)/(igl::PI/2.0))/4.0;
+    }
+  }
+}
+
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int modifier)
+{
+  using namespace std;
+  using namespace Eigen;
+
+  // Plot the original quad mesh
+
+  if (key == '1')
+  {
+    viewer.data().clear();
+    // Draw the triangulated quad mesh
+    viewer.data().set_mesh(VQC, FQCtri);
+
+    // Assign a color to each quad that corresponds to the average deviation of each angle from pi/2
+    VectorXd angleRegularity(FQC.rows());
+    quadAngleRegularity( VQC, FQC, angleRegularity);
+    MatrixXd Ct;
+    igl::jet(angleRegularity, 0.0, 0.05, Ct);
+    MatrixXd C(FQCtri.rows(),3);
+    C << Ct, Ct;
+    viewer.data().set_colors(C);
+
+    // Plot a line for each edge of the quad mesh
+    viewer.data().add_edges(PQC0, PQC1, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC1, PQC2, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC2, PQC3, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC3, PQC0, Eigen::RowVector3d(0,0,0));
+  }
+
+  // Plot the planarized quad mesh
+  if (key == '2')
+  {
+    viewer.data().clear();
+    // Draw the triangulated quad mesh
+    viewer.data().set_mesh(VQCregular, FQCtri);
+
+    // Assign a color to each quad that corresponds to its planarity
+    VectorXd angleRegularity(FQC.rows());
+    quadAngleRegularity( VQCregular, FQC, angleRegularity);
+    MatrixXd Ct;
+    igl::jet(angleRegularity, 0, 0.05, Ct);
+    MatrixXd C(FQCtri.rows(),3);
+    C << Ct, Ct;
+    viewer.data().set_colors(C);
+
+    // Plot a line for each edge of the quad mesh
+    viewer.data().add_edges(PQC0regular, PQC1regular, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC1regular, PQC2regular, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC2regular, PQC3regular, Eigen::RowVector3d(0,0,0));
+    viewer.data().add_edges(PQC3regular, PQC0regular, Eigen::RowVector3d(0,0,0));
+  }
+
+  return false;
+}
+
+int main(int argc, char *argv[])
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // Load a quad mesh
+  igl::readOFF(TUTORIAL_SHARED_PATH "/halftunnel.off", VQC, FQC);
+
+  // Convert it in a triangle mesh
+  FQCtri.resize(2*FQC.rows(), 3);
+  FQCtri <<  FQC.col(0),FQC.col(1),FQC.col(2),
+             FQC.col(2),FQC.col(3),FQC.col(0);
+  PQC0 = VQC(FQC.col(0).eval(), Eigen::all);
+  PQC1 = VQC(FQC.col(1).eval(), Eigen::all);
+  PQC2 = VQC(FQC.col(2).eval(), Eigen::all);
+  PQC3 = VQC(FQC.col(3).eval(), Eigen::all);
+
+  // Create a planar version with ShapeUp
+  //igl::planarize_quad_mesh(VQC, FQC, 100, 0.005, VQCregular);
+
+  E.resize(FQC.size(),2);
+  E.col(0)<<FQC.col(0),FQC.col(1),FQC.col(2),FQC.col(3);
+  E.col(1)<<FQC.col(1),FQC.col(2),FQC.col(3),FQC.col(0);
+
+  VectorXi b(1); b(0)=0;  //setting the first vertex to be the same.
+
+  VectorXd wShape=VectorXd::Constant(FQC.rows(),1.0);
+  VectorXd wSmooth=VectorXd::Constant(E.rows(),1.0);
+  MatrixXd bc(1,3); bc<<VQC.row(0);
+
+  VectorXi array_of_fours=VectorXi::Constant(FQC.rows(),4);
+  igl::shapeup_projection_function localFunction(igl::shapeup_regular_face_projection);
+
+  su_data.maxIterations=200;
+  shapeup_precomputation(VQC, array_of_fours,FQC,E,b,wShape, wSmooth,su_data);
+  shapeup_solve(bc,localFunction, VQC,su_data, false,VQCregular);
+
+
+  // Convert the planarized mesh to triangles
+  PQC0regular = VQCregular(FQC.col(0).eval(), Eigen::all);
+  PQC1regular = VQCregular(FQC.col(1).eval(), Eigen::all);
+  PQC2regular = VQCregular(FQC.col(2).eval(), Eigen::all);
+  PQC3regular = VQCregular(FQC.col(3).eval(), Eigen::all);
+
+  // Launch the viewer
+  igl::opengl::glfw::Viewer viewer;
+  key_down(viewer,'1',0);
+  viewer.data().invert_normals = true;
+  viewer.data().show_lines = false;
+  viewer.callback_key_down = &key_down;
+  viewer.launch();
+}

+ 112 - 112
tutorial/806_HeatGeodesics/main.cpp

@@ -1,112 +1,112 @@
-#include "isolines_colormap.h"
-#include "update.h"
-#include <igl/read_triangle_mesh.h>
-#include <igl/heat_geodesics.h>
-#include <igl/avg_edge_length.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  Eigen::MatrixXi F;
-  Eigen::MatrixXd V;
-  igl::read_triangle_mesh( argc>1?argv[1]: TUTORIAL_SHARED_PATH "/beetle.off",V,F);
-  double t = std::pow(igl::avg_edge_length(V,F),2);
-
-  // Precomputation
-  igl::HeatGeodesicsData<double> data;
-  const auto precompute = [&]()
-  {
-    if(!igl::heat_geodesics_precompute(V,F,t,data))
-    {
-      std::cerr<<"Error: heat_geodesics_precompute failed."<<std::endl;
-      exit(EXIT_FAILURE);
-    };
-  };
-  precompute();
-
-  igl::opengl::glfw::Viewer viewer;
-  bool down_on_mesh = false;
-  const auto update = [&]()->bool
-  {
-    const double x = viewer.current_mouse_x;
-    const double y = viewer.core().viewport(3) - viewer.current_mouse_y;
-    Eigen::VectorXd D;
-    if(::update(
-      V,F,t,x,y,
-      viewer.core().view,viewer.core().proj,viewer.core().viewport,
-      data,
-      D))
-    {
-      viewer.data().set_data(D);
-      return true;
-    }
-    return false;
-  };
-  viewer.callback_mouse_down =
-    [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
-  {
-    if(update())
-    {
-      down_on_mesh = true;
-      return true;
-    }
-    return false;
-  };
-  viewer.callback_mouse_move =
-    [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
-    {
-      if(down_on_mesh)
-      {
-        update();
-        return true;
-      }
-      return false;
-    };
-  viewer.callback_mouse_up =
-    [&down_on_mesh](igl::opengl::glfw::Viewer& viewer, int, int)->bool
-  {
-    down_on_mesh = false;
-    return false;
-  };
-  std::cout<<R"(Usage:
-  [click]  Click on shape to pick new geodesic distance source
-  ,/.      Decrease/increase t by factor of 10.0
-  D,d      Toggle using intrinsic Delaunay discrete differential operators
-
-)";
-
-  viewer.callback_key_pressed =
-    [&](igl::opengl::glfw::Viewer& /*viewer*/, unsigned int key, int mod)->bool
-  {
-    switch(key)
-    {
-    default:
-      return false;
-    case 'D':
-    case 'd':
-      data.use_intrinsic_delaunay = !data.use_intrinsic_delaunay;
-      std::cout<<(data.use_intrinsic_delaunay?"":"not ")<<
-        "using intrinsic delaunay..."<<std::endl;
-      precompute();
-      update();
-      break;
-    case '.':
-    case ',':
-      t *= (key=='.'?10.0:0.1);
-      precompute();
-      update();
-      std::cout<<"t: "<<t<<std::endl;
-      break;
-    }
-    return true;
-  };
-
-  // Show mesh
-  viewer.data().set_mesh(V, F);
-  viewer.data().set_data(Eigen::VectorXd::Zero(V.rows()));
-  viewer.data().set_colormap(isolines_colormap());
-  viewer.data().show_lines = false;
-  viewer.launch();
-
-}
+#include "isolines_colormap.h"
+#include "update.h"
+#include <igl/read_triangle_mesh.h>
+#include <igl/heat_geodesics.h>
+#include <igl/avg_edge_length.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+  Eigen::MatrixXi F;
+  Eigen::MatrixXd V;
+  igl::read_triangle_mesh( argc>1?argv[1]: TUTORIAL_SHARED_PATH "/beetle.off",V,F);
+  double t = std::pow(igl::avg_edge_length(V,F),2);
+
+  // Precomputation
+  igl::HeatGeodesicsData<double> data;
+  const auto precompute = [&]()
+  {
+    if(!igl::heat_geodesics_precompute(V,F,t,data))
+    {
+      std::cerr<<"Error: heat_geodesics_precompute failed."<<std::endl;
+      exit(EXIT_FAILURE);
+    };
+  };
+  precompute();
+
+  igl::opengl::glfw::Viewer viewer;
+  bool down_on_mesh = false;
+  const auto update = [&]()->bool
+  {
+    const double x = viewer.current_mouse_x;
+    const double y = viewer.core().viewport(3) - viewer.current_mouse_y;
+    Eigen::VectorXd D;
+    if(::update(
+      V,F,t,x,y,
+      viewer.core().view,viewer.core().proj,viewer.core().viewport,
+      data,
+      D))
+    {
+      viewer.data().set_data(D);
+      return true;
+    }
+    return false;
+  };
+  viewer.callback_mouse_down =
+    [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
+  {
+    if(update())
+    {
+      down_on_mesh = true;
+      return true;
+    }
+    return false;
+  };
+  viewer.callback_mouse_move =
+    [&](igl::opengl::glfw::Viewer& viewer, int, int)->bool
+    {
+      if(down_on_mesh)
+      {
+        update();
+        return true;
+      }
+      return false;
+    };
+  viewer.callback_mouse_up =
+    [&down_on_mesh](igl::opengl::glfw::Viewer& viewer, int, int)->bool
+  {
+    down_on_mesh = false;
+    return false;
+  };
+  std::cout<<R"(Usage:
+  [click]  Click on shape to pick new geodesic distance source
+  ,/.      Decrease/increase t by factor of 10.0
+  D,d      Toggle using intrinsic Delaunay discrete differential operators
+
+)";
+
+  viewer.callback_key_pressed =
+    [&](igl::opengl::glfw::Viewer& /*viewer*/, unsigned int key, int mod)->bool
+  {
+    switch(key)
+    {
+    default:
+      return false;
+    case 'D':
+    case 'd':
+      data.use_intrinsic_delaunay = !data.use_intrinsic_delaunay;
+      std::cout<<(data.use_intrinsic_delaunay?"":"not ")<<
+        "using intrinsic delaunay..."<<std::endl;
+      precompute();
+      update();
+      break;
+    case '.':
+    case ',':
+      t *= (key=='.'?10.0:0.1);
+      precompute();
+      update();
+      std::cout<<"t: "<<t<<std::endl;
+      break;
+    }
+    return true;
+  };
+
+  // Show mesh
+  viewer.data().set_mesh(V, F);
+  viewer.data().set_data(Eigen::VectorXd::Zero(V.rows()));
+  viewer.data().set_colormap(isolines_colormap());
+  viewer.data().show_lines = false;
+  viewer.launch();
+
+}

+ 208 - 208
tutorial/807_FastWindingNumber/main.cpp

@@ -1,208 +1,208 @@
-#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();
-
-}
+#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();
+
+}

+ 108 - 108
tutorial/808_IterativeClosestPoint/main.cpp

@@ -1,108 +1,108 @@
-// This file is part of libigl, a simple c++ geometry processing library.
-// 
-// Copyright (C) 2019 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 <Eigen/Core>
-#include <Eigen/Geometry>
-#include <igl/read_triangle_mesh.h>
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/iterative_closest_point.h>
-#include <igl/random_dir.h>
-#include <igl/PI.h>
-#include <igl/AABB.h>
-#include <igl/per_face_normals.h>
-#include <igl/avg_edge_length.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/barycenter.h>
-#include <igl/slice.h>
-#include <igl/slice_mask.h>
-#include <iostream>
-
-int main(int argc, char * argv[])
-{
-  Eigen::MatrixXd OVX,VX,VY;
-  Eigen::MatrixXi FX,FY;
-  igl::read_triangle_mesh( argc>1?argv[1]: TUTORIAL_SHARED_PATH "/decimated-max.obj",VY,FY);
-  const double bbd = (VY.colwise().maxCoeff()-VY.colwise().minCoeff()).norm();
-  FX = FY;
-  {
-    // sprinkle a noise so that we can see z-fighting when the match is perfect.
-    const double h = igl::avg_edge_length(VY,FY);
-    OVX = VY + 1e-2*h*Eigen::MatrixXd::Random(VY.rows(),VY.cols());
-  }
-  
-  VX = OVX;
-
-  igl::AABB<Eigen::MatrixXd,3> Ytree;
-  Ytree.init(VY,FY);
-  Eigen::MatrixXd NY;
-  igl::per_face_normals(VY,FY,NY);
-
-  igl::opengl::glfw::Viewer v;
-  std::cout<<R"(
-  [space]  conduct a single iterative closest point iteration
-  R,r      reset to a random orientation and offset
-)";
-
-  const auto apply_random_rotation = [&]()
-  {
-    const Eigen::Matrix3d R = Eigen::AngleAxisd(
-      2.*igl::PI*(double)rand()/RAND_MAX*0.3, igl::random_dir()).matrix();
-    const Eigen::RowVector3d cen = 
-      0.5*(VY.colwise().maxCoeff()+VY.colwise().minCoeff());
-    VX = ((OVX*R).rowwise()+(cen-cen*R)).eval();
-  };
-  const auto single_iteration = [&]()
-  {
-    ////////////////////////////////////////////////////////////////////////
-    // Perform single iteration of ICP method
-    ////////////////////////////////////////////////////////////////////////
-    Eigen::Matrix3d R;
-    Eigen::RowVector3d t;
-    igl::iterative_closest_point(VX,FX,VY,FY,Ytree,NY,1000,1,R,t);
-    VX = ((VX*R).rowwise()+t).eval();
-    v.data().set_mesh(VX,FX);
-    v.data().compute_normals();
-  };
-  v.callback_pre_draw = [&](igl::opengl::glfw::Viewer &)->bool
-  {
-    if(v.core().is_animating)
-    {
-      single_iteration();
-    }
-    return false;
-  };
-  v.callback_key_pressed = 
-    [&](igl::opengl::glfw::Viewer &,unsigned char key,int)->bool
-  {
-    switch(key)
-    {
-      case ' ':
-      {
-        v.core().is_animating = false;
-        single_iteration();
-        return true;
-      }
-      case 'R':
-      case 'r':
-        // Random rigid transformation
-        apply_random_rotation();
-        v.data().set_mesh(VX,FX);
-        v.data().compute_normals();
-        return true;
-        break;
-    }
-    return false;
-  };
-
-  v.data().set_mesh(VY,FY);
-  v.data().set_colors(Eigen::RowVector3d(1,1,1));
-  v.data().show_lines = false;
-  v.append_mesh();
-  v.data().set_mesh(VX,FX);
-  v.data().show_lines = false;
-  v.launch();
-}
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2019 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 <Eigen/Core>
+#include <Eigen/Geometry>
+#include <igl/read_triangle_mesh.h>
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/iterative_closest_point.h>
+#include <igl/random_dir.h>
+#include <igl/PI.h>
+#include <igl/AABB.h>
+#include <igl/per_face_normals.h>
+#include <igl/avg_edge_length.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/barycenter.h>
+#include <igl/slice.h>
+#include <igl/slice_mask.h>
+#include <iostream>
+
+int main(int argc, char * argv[])
+{
+  Eigen::MatrixXd OVX,VX,VY;
+  Eigen::MatrixXi FX,FY;
+  igl::read_triangle_mesh( argc>1?argv[1]: TUTORIAL_SHARED_PATH "/decimated-max.obj",VY,FY);
+  const double bbd = (VY.colwise().maxCoeff()-VY.colwise().minCoeff()).norm();
+  FX = FY;
+  {
+    // sprinkle a noise so that we can see z-fighting when the match is perfect.
+    const double h = igl::avg_edge_length(VY,FY);
+    OVX = VY + 1e-2*h*Eigen::MatrixXd::Random(VY.rows(),VY.cols());
+  }
+  
+  VX = OVX;
+
+  igl::AABB<Eigen::MatrixXd,3> Ytree;
+  Ytree.init(VY,FY);
+  Eigen::MatrixXd NY;
+  igl::per_face_normals(VY,FY,NY);
+
+  igl::opengl::glfw::Viewer v;
+  std::cout<<R"(
+  [space]  conduct a single iterative closest point iteration
+  R,r      reset to a random orientation and offset
+)";
+
+  const auto apply_random_rotation = [&]()
+  {
+    const Eigen::Matrix3d R = Eigen::AngleAxisd(
+      2.*igl::PI*(double)rand()/RAND_MAX*0.3, igl::random_dir()).matrix();
+    const Eigen::RowVector3d cen = 
+      0.5*(VY.colwise().maxCoeff()+VY.colwise().minCoeff());
+    VX = ((OVX*R).rowwise()+(cen-cen*R)).eval();
+  };
+  const auto single_iteration = [&]()
+  {
+    ////////////////////////////////////////////////////////////////////////
+    // Perform single iteration of ICP method
+    ////////////////////////////////////////////////////////////////////////
+    Eigen::Matrix3d R;
+    Eigen::RowVector3d t;
+    igl::iterative_closest_point(VX,FX,VY,FY,Ytree,NY,1000,1,R,t);
+    VX = ((VX*R).rowwise()+t).eval();
+    v.data().set_mesh(VX,FX);
+    v.data().compute_normals();
+  };
+  v.callback_pre_draw = [&](igl::opengl::glfw::Viewer &)->bool
+  {
+    if(v.core().is_animating)
+    {
+      single_iteration();
+    }
+    return false;
+  };
+  v.callback_key_pressed = 
+    [&](igl::opengl::glfw::Viewer &,unsigned char key,int)->bool
+  {
+    switch(key)
+    {
+      case ' ':
+      {
+        v.core().is_animating = false;
+        single_iteration();
+        return true;
+      }
+      case 'R':
+      case 'r':
+        // Random rigid transformation
+        apply_random_rotation();
+        v.data().set_mesh(VX,FX);
+        v.data().compute_normals();
+        return true;
+        break;
+    }
+    return false;
+  };
+
+  v.data().set_mesh(VY,FY);
+  v.data().set_colors(Eigen::RowVector3d(1,1,1));
+  v.data().show_lines = false;
+  v.append_mesh();
+  v.data().set_mesh(VX,FX);
+  v.data().show_lines = false;
+  v.launch();
+}

+ 98 - 98
tutorial/809_ExplodedView/main.cpp

@@ -1,98 +1,98 @@
-// 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/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 = D(J);
-
-    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();
-}
+// 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/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 = D(J);
+
+    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();
+}

+ 108 - 108
tutorial/810_BlueNoise/main.cpp

@@ -1,108 +1,108 @@
-// 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/read_triangle_mesh.h>
-#include <igl/blue_noise.h>
-#include <igl/per_vertex_normals.h>
-#include <igl/barycentric_interpolation.h>
-#include <igl/blue_noise.h>
-#include <igl/doublearea.h>
-#include <igl/random_points_on_mesh.h>
-#include <igl/PI.h>
-
-
-int main(int argc, char *argv[])
-{
-
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  igl::read_triangle_mesh(
-    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/elephant.obj",V,F);
-  Eigen::MatrixXd N;
-  igl::per_vertex_normals(V,F,N);
-  const double bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
-
-  Eigen::MatrixXd P_blue;
-  Eigen::MatrixXd N_blue;
-  Eigen::VectorXd A_blue;
-  Eigen::MatrixXd C_blue;
-  {
-    const int n_desired = argc>2?atoi(argv[2]):50000;
-    // Heuristic to  determine radius from desired number 
-    const double r = [&V,&F](const int n)
-    {
-      Eigen::VectorXd A;
-      igl::doublearea(V,F,A);
-      return sqrt(((A.sum()*0.5/(n*0.6162910373))/igl::PI));
-    }(n_desired);
-    printf("blue noise radius: %g\n",r);
-    Eigen::MatrixXd B;
-    Eigen::VectorXi I;
-    igl::blue_noise(V,F,r,B,I,P_blue);
-    igl::barycentric_interpolation(N,F,B,I,N_blue);
-    N_blue.rowwise().normalize();
-  }
-  Eigen::MatrixXd P_white;
-  Eigen::MatrixXd N_white;
-  Eigen::VectorXd A_white;
-  Eigen::MatrixXd C_white;
-  {
-    Eigen::MatrixXd B;
-    Eigen::VectorXi I;
-    igl::random_points_on_mesh(P_blue.rows(),V,F,B,I,P_white);
-    igl::barycentric_interpolation(N,F,B,I,N_white);
-    N_white.rowwise().normalize();
-  }
-
-  // Color
-  Eigen::RowVector3d C(1,1,1);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V,F);
-  viewer.data().show_lines = false;
-  viewer.data().show_faces = false;
-  viewer.data().point_size = 4;
-
-  bool use_blue = true;
-  const auto update = [&]()
-  {
-    const Eigen::RowVector3d orange(1.0,0.7,0.2);
-    if(use_blue)
-    {
-      viewer.data().set_points(P_blue,Eigen::RowVector3d(0.3,0.4,1.0));
-      viewer.data().set_edges_from_vector_field(P_blue,bbd*0.01*N_blue,orange);
-    }else
-    {
-      viewer.data().set_points(P_white,Eigen::RowVector3d(1,1,1));
-      viewer.data().set_edges_from_vector_field(P_white,bbd*0.01*N_white,orange);
-    }
-  };
-  update();
-
-  viewer.callback_key_pressed = 
-    [&](igl::opengl::glfw::Viewer &,unsigned char key,int)->bool
-  {
-    switch(key)
-    {
-      case ' ':
-      {
-        use_blue = !use_blue;
-        update();
-        return true;
-      }
-    }
-    return false;
-  };
-  std::cout<<R"(
-[space]  toggle between blue and white noise samplings
-)";
-
-  viewer.launch();
-}
+// 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/read_triangle_mesh.h>
+#include <igl/blue_noise.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/barycentric_interpolation.h>
+#include <igl/blue_noise.h>
+#include <igl/doublearea.h>
+#include <igl/random_points_on_mesh.h>
+#include <igl/PI.h>
+
+
+int main(int argc, char *argv[])
+{
+
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  igl::read_triangle_mesh(
+    argc>1?argv[1]: TUTORIAL_SHARED_PATH "/elephant.obj",V,F);
+  Eigen::MatrixXd N;
+  igl::per_vertex_normals(V,F,N);
+  const double bbd = (V.colwise().maxCoeff()- V.colwise().minCoeff()).norm();
+
+  Eigen::MatrixXd P_blue;
+  Eigen::MatrixXd N_blue;
+  Eigen::VectorXd A_blue;
+  Eigen::MatrixXd C_blue;
+  {
+    const int n_desired = argc>2?atoi(argv[2]):50000;
+    // Heuristic to  determine radius from desired number 
+    const double r = [&V,&F](const int n)
+    {
+      Eigen::VectorXd A;
+      igl::doublearea(V,F,A);
+      return sqrt(((A.sum()*0.5/(n*0.6162910373))/igl::PI));
+    }(n_desired);
+    printf("blue noise radius: %g\n",r);
+    Eigen::MatrixXd B;
+    Eigen::VectorXi I;
+    igl::blue_noise(V,F,r,B,I,P_blue);
+    igl::barycentric_interpolation(N,F,B,I,N_blue);
+    N_blue.rowwise().normalize();
+  }
+  Eigen::MatrixXd P_white;
+  Eigen::MatrixXd N_white;
+  Eigen::VectorXd A_white;
+  Eigen::MatrixXd C_white;
+  {
+    Eigen::MatrixXd B;
+    Eigen::VectorXi I;
+    igl::random_points_on_mesh(P_blue.rows(),V,F,B,I,P_white);
+    igl::barycentric_interpolation(N,F,B,I,N_white);
+    N_white.rowwise().normalize();
+  }
+
+  // Color
+  Eigen::RowVector3d C(1,1,1);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V,F);
+  viewer.data().show_lines = false;
+  viewer.data().show_faces = false;
+  viewer.data().point_size = 4;
+
+  bool use_blue = true;
+  const auto update = [&]()
+  {
+    const Eigen::RowVector3d orange(1.0,0.7,0.2);
+    if(use_blue)
+    {
+      viewer.data().set_points(P_blue,Eigen::RowVector3d(0.3,0.4,1.0));
+      viewer.data().set_edges_from_vector_field(P_blue,bbd*0.01*N_blue,orange);
+    }else
+    {
+      viewer.data().set_points(P_white,Eigen::RowVector3d(1,1,1));
+      viewer.data().set_edges_from_vector_field(P_white,bbd*0.01*N_white,orange);
+    }
+  };
+  update();
+
+  viewer.callback_key_pressed = 
+    [&](igl::opengl::glfw::Viewer &,unsigned char key,int)->bool
+  {
+    switch(key)
+    {
+      case ' ':
+      {
+        use_blue = !use_blue;
+        update();
+        return true;
+      }
+    }
+    return false;
+  };
+  std::cout<<R"(
+[space]  toggle between blue and white noise samplings
+)";
+
+  viewer.launch();
+}

+ 58 - 58
tutorial/903_FastFindSelfIntersections/main.cpp

@@ -1,58 +1,58 @@
-#include <igl/readOFF.h>
-#include <igl/combine.h>
-
-#include <igl/opengl/glfw/Viewer.h>
-//#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
-#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
-
-#include <igl/fast_find_self_intersections.h>
-
-Eigen::MatrixXd V1,V2,V;
-Eigen::MatrixXi F1,F2,F;
-
-int main(int argc, char *argv[])
-{
-
-  // Load two meshes 
-  igl::readOFF(TUTORIAL_SHARED_PATH "/planexy.off", V1, F1);
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off",     V2, F2);
-
-  // Combine into one mesh (will produce self-intersections)
-  igl::combine<Eigen::MatrixXd,Eigen::MatrixXi>({V1,V2},{F1,F2}, V,F);
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V, F);
-
-  Eigen::VectorXi I;
-  Eigen::MatrixXd edges;
-
-  if(igl::fast_find_self_intersections(V,F,I,edges))
-  {
-    std::cout<<"Found "<<I.sum()<<" self intersections"<<std::endl;
-
-    // plot edge vertices
-    //viewer.data().add_points(edges, Eigen::RowVector3d(1,0,0));
-
-    // Plot the edges of the self intersects
-    for (unsigned i=0;i<edges.rows(); i+=2)
-    {
-      viewer.data().add_edges
-      (
-        edges.row(i),
-        edges.row(i+1),
-        Eigen::RowVector3d(1,0,0)
-      );
-    }
-    std::cout<<std::endl;
-  }
-  viewer.data().set_data(I.cast<double>());
-  viewer.data().double_sided=true;
-
-  igl::opengl::glfw::imgui::ImGuiMenu menu;
-  //plugin.widgets.push_back(&menu);
-  menu.callback_draw_viewer_window = [](){};
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/combine.h>
+
+#include <igl/opengl/glfw/Viewer.h>
+//#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+
+#include <igl/fast_find_self_intersections.h>
+
+Eigen::MatrixXd V1,V2,V;
+Eigen::MatrixXi F1,F2,F;
+
+int main(int argc, char *argv[])
+{
+
+  // Load two meshes 
+  igl::readOFF(TUTORIAL_SHARED_PATH "/planexy.off", V1, F1);
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off",     V2, F2);
+
+  // Combine into one mesh (will produce self-intersections)
+  igl::combine<Eigen::MatrixXd,Eigen::MatrixXi>({V1,V2},{F1,F2}, V,F);
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V, F);
+
+  Eigen::VectorXi I;
+  Eigen::MatrixXd edges;
+
+  if(igl::fast_find_self_intersections(V,F,I,edges))
+  {
+    std::cout<<"Found "<<I.sum()<<" self intersections"<<std::endl;
+
+    // plot edge vertices
+    //viewer.data().add_points(edges, Eigen::RowVector3d(1,0,0));
+
+    // Plot the edges of the self intersects
+    for (unsigned i=0;i<edges.rows(); i+=2)
+    {
+      viewer.data().add_edges
+      (
+        edges.row(i),
+        edges.row(i+1),
+        Eigen::RowVector3d(1,0,0)
+      );
+    }
+    std::cout<<std::endl;
+  }
+  viewer.data().set_data(I.cast<double>());
+  viewer.data().double_sided=true;
+
+  igl::opengl::glfw::imgui::ImGuiMenu menu;
+  //plugin.widgets.push_back(&menu);
+  menu.callback_draw_viewer_window = [](){};
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 98 - 98
tutorial/904_FastFindIntersections/main.cpp

@@ -1,98 +1,98 @@
-#include <igl/readOFF.h>
-#include <igl/combine.h>
-
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
-
-#include <igl/fast_find_intersections.h>
-
-Eigen::MatrixXd V1,V2;
-Eigen::MatrixXi F1,F2;
-
-igl::AABB<Eigen::MatrixXd,3> tree;
-
-double min_z,max_z;
-double slice_z;
-
-
-void update_visualization(igl::opengl::glfw::Viewer & viewer)
-{
-  Eigen::MatrixXi I;
-  Eigen::MatrixXd edges;
-
-  //shifted intersection object
-  Eigen::MatrixXd V2_(V2.rows(),V2.cols());
-  V2_<< V2.col(0), V2.col(1), V2.col(2).array()+slice_z;
-
-  igl::fast_find_intersections(tree, V1,F1, V2_,F2, I,edges);
-  Eigen::MatrixXi edges_link=Eigen::MatrixXi::NullaryExpr(edges.rows()/2,2, [](int i,int j) { return i*2+j;});
- 
-  // Plot the edges of the intersects
-  viewer.data().set_edges ( edges, edges_link, Eigen::RowVector3d(1,0,0));
-  
-  // show faces which are intersected
-  Eigen::VectorXd face_data=Eigen::VectorXd::Zero(F1.rows());
-
-  for(int i=0; i<I.rows(); ++i)
-    face_data(I(i,0)) = 1.0;
-  
-  viewer.data().set_data(face_data);
-}
-
-
-bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int mod)
-{
-  switch(key)
-  {
-    default:
-      return false;
-    case '.':
-      slice_z = std::min(slice_z+0.01,max_z);
-      break;
-    case ',':
-      slice_z = std::max(slice_z-0.01,min_z);
-      break;
-  }
-  update_visualization(viewer);
-  return true;
-}
-
-
-int main(int argc, char *argv[])
-{
-  // Load two meshes 
-  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off",     V1, F1);
-  igl::readOFF(TUTORIAL_SHARED_PATH "/planexy.off", V2, F2);
-
-  // initialize AABB tree with first mesh (it doesn't move)
-  tree.init(V1,F1);
-
-  std::cout<<"Usage:"<<std::endl;
-  std::cout<<"'.'/','  push back/pull forward slicing plane."<<std::endl;
-  std::cout<<std::endl;
-
-  min_z=V1.col(2).minCoeff();
-  max_z=V1.col(2).maxCoeff();
-  slice_z=(max_z+min_z)/2;
-
-  //center slicing object
-  V2.col(2).array() -= V2.col(2).array().mean();
-
-  // Plot the mesh
-  igl::opengl::glfw::Viewer viewer;
-  viewer.data().set_mesh(V1, F1);
-  
-
-  update_visualization(viewer);
-
-  igl::opengl::glfw::imgui::ImGuiMenu menu;
-  //plugin.widgets.push_back(&menu);
-  menu.callback_draw_viewer_window = [](){};
-  viewer.callback_key_down = &key_down;
-
-  // show the cow closer
-  viewer.core().camera_zoom = 2.0;
-
-  // Launch the viewer
-  viewer.launch();
-}
+#include <igl/readOFF.h>
+#include <igl/combine.h>
+
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+
+#include <igl/fast_find_intersections.h>
+
+Eigen::MatrixXd V1,V2;
+Eigen::MatrixXi F1,F2;
+
+igl::AABB<Eigen::MatrixXd,3> tree;
+
+double min_z,max_z;
+double slice_z;
+
+
+void update_visualization(igl::opengl::glfw::Viewer & viewer)
+{
+  Eigen::MatrixXi I;
+  Eigen::MatrixXd edges;
+
+  //shifted intersection object
+  Eigen::MatrixXd V2_(V2.rows(),V2.cols());
+  V2_<< V2.col(0), V2.col(1), V2.col(2).array()+slice_z;
+
+  igl::fast_find_intersections(tree, V1,F1, V2_,F2, I,edges);
+  Eigen::MatrixXi edges_link=Eigen::MatrixXi::NullaryExpr(edges.rows()/2,2, [](int i,int j) { return i*2+j;});
+ 
+  // Plot the edges of the intersects
+  viewer.data().set_edges ( edges, edges_link, Eigen::RowVector3d(1,0,0));
+  
+  // show faces which are intersected
+  Eigen::VectorXd face_data=Eigen::VectorXd::Zero(F1.rows());
+
+  for(int i=0; i<I.rows(); ++i)
+    face_data(I(i,0)) = 1.0;
+  
+  viewer.data().set_data(face_data);
+}
+
+
+bool key_down(igl::opengl::glfw::Viewer& viewer, unsigned char key, int mod)
+{
+  switch(key)
+  {
+    default:
+      return false;
+    case '.':
+      slice_z = std::min(slice_z+0.01,max_z);
+      break;
+    case ',':
+      slice_z = std::max(slice_z-0.01,min_z);
+      break;
+  }
+  update_visualization(viewer);
+  return true;
+}
+
+
+int main(int argc, char *argv[])
+{
+  // Load two meshes 
+  igl::readOFF(TUTORIAL_SHARED_PATH "/cow.off",     V1, F1);
+  igl::readOFF(TUTORIAL_SHARED_PATH "/planexy.off", V2, F2);
+
+  // initialize AABB tree with first mesh (it doesn't move)
+  tree.init(V1,F1);
+
+  std::cout<<"Usage:"<<std::endl;
+  std::cout<<"'.'/','  push back/pull forward slicing plane."<<std::endl;
+  std::cout<<std::endl;
+
+  min_z=V1.col(2).minCoeff();
+  max_z=V1.col(2).maxCoeff();
+  slice_z=(max_z+min_z)/2;
+
+  //center slicing object
+  V2.col(2).array() -= V2.col(2).array().mean();
+
+  // Plot the mesh
+  igl::opengl::glfw::Viewer viewer;
+  viewer.data().set_mesh(V1, F1);
+  
+
+  update_visualization(viewer);
+
+  igl::opengl::glfw::imgui::ImGuiMenu menu;
+  //plugin.widgets.push_back(&menu);
+  menu.callback_draw_viewer_window = [](){};
+  viewer.callback_key_down = &key_down;
+
+  // show the cow closer
+  viewer.core().camera_zoom = 2.0;
+
+  // Launch the viewer
+  viewer.launch();
+}

+ 94 - 94
tutorial/906_TrimWithSolid/main.cpp

@@ -1,94 +1,94 @@
-#include <igl/opengl/glfw/Viewer.h>
-#include <igl/read_triangle_mesh.h>
-#include <igl/get_seconds.h>
-#include <igl/sort_triangles.h>
-#include <igl/material_colors.h>
-#include <igl/copyleft/cgal/trim_with_solid.h>
-
-int main(int argc, char *argv[])
-{
-  using namespace igl;
-  IGL_TICTOC_LAMBDA;
-  Eigen::MatrixXd VA, VB;
-  Eigen::MatrixXi FA, FB;
-  // Load a hot mess of a mesh
-  igl::read_triangle_mesh(argc>1?argv[1]:TUTORIAL_SHARED_PATH "/truck.obj", VA, FA);
-  // Load a solid mesh
-  igl::read_triangle_mesh(argc>2?argv[2]:TUTORIAL_SHARED_PATH "/bunny.off", VB, FB);
-  if(argc<2)
-  {
-    // resize bunny
-    VB.rowwise() -= VB.colwise().mean();
-    VB /= (VB.colwise().maxCoeff()-VB.colwise().minCoeff()).maxCoeff();
-    VB *= 1.15;
-    VB.rowwise() += VA.colwise().mean();
-  }
-
-  Eigen::VectorXi J;
-  Eigen::MatrixXd VC;
-  Eigen::MatrixXi FC;
-  Eigen::Array<bool, Eigen::Dynamic, 1> D;
-  using namespace igl::copyleft::cgal;
-  tictoc();
-  // More patches, less intersection handling
-  igl::copyleft::cgal::trim_with_solid(VA, FA, VB, FB, CHECK_EACH_PATCH, VC, FC, D, J);
-  printf("CHECK_EACH_PATCH: %g secs, |FC| = %d, |D| = %d\n", tictoc(),FC.rows(),D.count());
-  // More intersection handling, fewer patches
-  igl::copyleft::cgal::trim_with_solid(VA, FA, VB, FB, RESOLVE_BOTH_AND_RESTORE_THEN_CHECK_EACH_PATCH, VC, FC, D, J);
-  printf("RESOLVE_BOTH_...: %g secs, |FC| = %d, |D| = %d\n", tictoc(),FC.rows(),D.count());
-
-  igl::opengl::glfw::Viewer vr;
-  vr.data().set_mesh(VC, FC);
-  // Turn on double sided lighting
-  vr.data().double_sided = true;
-  vr.data().set_face_based(true);
-  vr.data().set_data(D.cast<double>());
-  Eigen::MatrixXd CM = (Eigen::MatrixXd(2,3)<< 
-      1,1,1,
-      GOLD_DIFFUSE[0],GOLD_DIFFUSE[1],GOLD_DIFFUSE[2]
-      ).finished();
-  vr.data().set_colormap(CM);
-
-  vr.append_mesh();
-  vr.data().set_mesh(VB, FB);
-  vr.data().show_lines = false;
-  // Make a semi-transparent orange matcap
-  {
-    Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> 
-      R(256,256), G(256,256), B(256,256), A(256,256);
-    for(int i = 0;i<R.rows();i++)
-    {
-      for(int j = 0;j<R.cols();j++)
-      {
-        R(i,j) = 255;
-        G(i,j) = 110;
-        B(i,j) = 20;
-        // distance to middle of image
-        const double x = (i+0.5-R.rows()/2.0)/(R.rows()/2.0);
-        const double y = (j+0.5-R.cols()/2.0)/(R.cols()/2.0);
-        const double r = std::min(1.0,sqrt(x*x+y*y));
-        A(i,j) = 255*(1-sqrt(1-r*r));
-      }
-    }
-    vr.data().set_texture(R,G,B,A);
-  }
-  vr.data().use_matcap = true;
-  // On mouse up resort for better transparency
-  vr.callback_mouse_up = 
-    [&](igl::opengl::glfw::Viewer &, int button, int mod)
-  {
-    Eigen::VectorXi _;
-    igl::sort_triangles(
-      VB,Eigen::MatrixXi(FB), vr.core().view, vr.core().proj,FB,_);
-    vr.data_list[1].set_mesh(VB, FB);
-    return false;
-  };
-  // set first mesh as selected
-  vr.selected_data_index = 0;
-
-  vr.launch_init();
-  vr.core().draw(vr.data(),true);
-  vr.callback_mouse_up(vr,0,0);
-  vr.launch_rendering(true);
-  vr.launch_shut();
-}
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/read_triangle_mesh.h>
+#include <igl/get_seconds.h>
+#include <igl/sort_triangles.h>
+#include <igl/material_colors.h>
+#include <igl/copyleft/cgal/trim_with_solid.h>
+
+int main(int argc, char *argv[])
+{
+  using namespace igl;
+  IGL_TICTOC_LAMBDA;
+  Eigen::MatrixXd VA, VB;
+  Eigen::MatrixXi FA, FB;
+  // Load a hot mess of a mesh
+  igl::read_triangle_mesh(argc>1?argv[1]:TUTORIAL_SHARED_PATH "/truck.obj", VA, FA);
+  // Load a solid mesh
+  igl::read_triangle_mesh(argc>2?argv[2]:TUTORIAL_SHARED_PATH "/bunny.off", VB, FB);
+  if(argc<2)
+  {
+    // resize bunny
+    VB.rowwise() -= VB.colwise().mean();
+    VB /= (VB.colwise().maxCoeff()-VB.colwise().minCoeff()).maxCoeff();
+    VB *= 1.15;
+    VB.rowwise() += VA.colwise().mean();
+  }
+
+  Eigen::VectorXi J;
+  Eigen::MatrixXd VC;
+  Eigen::MatrixXi FC;
+  Eigen::Array<bool, Eigen::Dynamic, 1> D;
+  using namespace igl::copyleft::cgal;
+  tictoc();
+  // More patches, less intersection handling
+  igl::copyleft::cgal::trim_with_solid(VA, FA, VB, FB, CHECK_EACH_PATCH, VC, FC, D, J);
+  printf("CHECK_EACH_PATCH: %g secs, |FC| = %d, |D| = %d\n", tictoc(),FC.rows(),D.count());
+  // More intersection handling, fewer patches
+  igl::copyleft::cgal::trim_with_solid(VA, FA, VB, FB, RESOLVE_BOTH_AND_RESTORE_THEN_CHECK_EACH_PATCH, VC, FC, D, J);
+  printf("RESOLVE_BOTH_...: %g secs, |FC| = %d, |D| = %d\n", tictoc(),FC.rows(),D.count());
+
+  igl::opengl::glfw::Viewer vr;
+  vr.data().set_mesh(VC, FC);
+  // Turn on double sided lighting
+  vr.data().double_sided = true;
+  vr.data().set_face_based(true);
+  vr.data().set_data(D.cast<double>());
+  Eigen::MatrixXd CM = (Eigen::MatrixXd(2,3)<< 
+      1,1,1,
+      GOLD_DIFFUSE[0],GOLD_DIFFUSE[1],GOLD_DIFFUSE[2]
+      ).finished();
+  vr.data().set_colormap(CM);
+
+  vr.append_mesh();
+  vr.data().set_mesh(VB, FB);
+  vr.data().show_lines = false;
+  // Make a semi-transparent orange matcap
+  {
+    Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> 
+      R(256,256), G(256,256), B(256,256), A(256,256);
+    for(int i = 0;i<R.rows();i++)
+    {
+      for(int j = 0;j<R.cols();j++)
+      {
+        R(i,j) = 255;
+        G(i,j) = 110;
+        B(i,j) = 20;
+        // distance to middle of image
+        const double x = (i+0.5-R.rows()/2.0)/(R.rows()/2.0);
+        const double y = (j+0.5-R.cols()/2.0)/(R.cols()/2.0);
+        const double r = std::min(1.0,sqrt(x*x+y*y));
+        A(i,j) = 255*(1-sqrt(1-r*r));
+      }
+    }
+    vr.data().set_texture(R,G,B,A);
+  }
+  vr.data().use_matcap = true;
+  // On mouse up resort for better transparency
+  vr.callback_mouse_up = 
+    [&](igl::opengl::glfw::Viewer &, int button, int mod)
+  {
+    Eigen::VectorXi _;
+    igl::sort_triangles(
+      VB,Eigen::MatrixXi(FB), vr.core().view, vr.core().proj,FB,_);
+    vr.data_list[1].set_mesh(VB, FB);
+    return false;
+  };
+  // set first mesh as selected
+  vr.selected_data_index = 0;
+
+  vr.launch_init();
+  vr.core().draw(vr.data(),true);
+  vr.callback_mouse_up(vr,0,0);
+  vr.launch_rendering(true);
+  vr.launch_shut();
+}