Browse Source

Selection Plugin and Tutorial Entry (#1654)

* Selection Plugin and Tutorial Entry

* rm __1::

* templates; and dont take F as input

* missing template, fix bug

* better commenting

* windows doesn't understand M_PI

* Use ImGui::SetNextWindowSize

Co-authored-by: Jérémie Dumas <[email protected]>
Alec Jacobson 5 years ago
parent
commit
fbfb2d56ad

+ 1 - 0
cmake/libigl.cmake

@@ -402,6 +402,7 @@ if(LIBIGL_WITH_OPENGL_GLFW_IMGUI)
     if(NOT TARGET imguizmo)
       igl_download_imguizmo()
       add_library(imguizmo ${LIBIGL_EXTERNAL}/imguizmo/ImGuizmo.cpp ${LIBIGL_EXTERNAL}/imguizmo/ImGuizmo.h)
+      target_compile_features(imguizmo PUBLIC cxx_std_11)
       target_link_libraries(imguizmo PUBLIC imgui)
     endif()
     target_link_libraries(igl_opengl_glfw_imgui ${IGL_SCOPE} igl_opengl_glfw imgui imguizmo)

+ 12 - 0
include/igl/WindingNumberAABB.h

@@ -374,4 +374,16 @@ inline typename DerivedV::Scalar
   return igl::winding_number(BV,PBF,p);
 }
 
+// This is a bullshit template because AABB annoyingly needs templates for bad
+// combinations of 3D V with DIM=2 AABB
+//
+// _Define_ as a no-op rather than monkeying around with the proper code above
+namespace igl
+{
+  template <> inline igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::WindingNumberAABB(const Eigen::MatrixBase<Eigen::Matrix<double, -1, 2, 0, -1, 2>> & V, const Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2>> & F){};
+  template <> inline void igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::grow(){};
+  template <> inline void igl::WindingNumberAABB<Eigen::Matrix<double, 1, 3, 1, 1, 3>,Eigen::Matrix<double, -1, 2, 0, -1, 2>,Eigen::Matrix<int, -1, 2, 0, -1, 2>>::init(){};
+
+}
+
 #endif

+ 2 - 0
include/igl/average_onto_faces.cpp

@@ -22,4 +22,6 @@ IGL_INLINE void igl::average_onto_faces(
 
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
+// generated by autoexplicit.sh
+template void igl::average_onto_faces<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
 #endif

+ 2 - 0
include/igl/min_quad_with_fixed.cpp

@@ -584,6 +584,8 @@ IGL_INLINE bool igl::min_quad_with_fixed(
 
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
+// generated by autoexplicit.sh
+template bool igl::min_quad_with_fixed<double, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
 #if EIGEN_VERSION_AT_LEAST(3,3,0)
 #else
 template bool igl::min_quad_with_fixed_solve<double, Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> const>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::min_quad_with_fixed_data<double> const&, Eigen::MatrixBase<Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, Eigen::Matrix<double, -1, 1, 0, -1, 1> const> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);

+ 208 - 0
include/igl/opengl/glfw/imgui/SelectionPlugin.cpp

@@ -0,0 +1,208 @@
+#include "SelectionPlugin.h"
+
+#include <imgui/imgui.h>
+#include <imgui_impl_glfw.h>
+#include <imgui_impl_opengl3.h>
+#include <imgui_fonts_droid_sans.h>
+#include <GLFW/glfw3.h>
+#include "../../../PI.h"
+
+namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
+
+IGL_INLINE void SelectionPlugin::init(igl::opengl::glfw::Viewer *_viewer)
+{
+  ImGuiMenu::init(_viewer);
+  std::cout<<R"(
+igl::opengl::glfw::imgui::SelectionPlugin usage:
+  [drag]  Draw a 2D selection
+  l       Turn on and toggle between lasso and polygonal lasso tool
+  M,m     Turn on and toggle between rectangular and circular marquee tool
+  V,v     Turn off interactive selection
+)";
+}
+IGL_INLINE bool SelectionPlugin::pre_draw()
+{
+  if(!visible){ return false; }
+  ImGuiMenu::pre_draw();
+  return false;
+}
+IGL_INLINE bool SelectionPlugin::post_draw()
+{
+  if(mode == OFF){ return false; }
+  ImGuiIO& io = ImGui::GetIO();
+
+  float width, height;
+  float highdpi = 1.0;
+  {
+    int fwidth, fheight;
+    glfwGetFramebufferSize(viewer->window, &fwidth, &fheight);
+    int iwidth, iheight;
+    glfwGetWindowSize(viewer->window, &iwidth, &iheight);
+    highdpi = float(iwidth)/float(fwidth);
+    // highdpi
+    width = (float)iwidth;
+    height = (float)iheight;
+  }
+
+  ImGui::SetNextWindowPos( ImVec2(0,0) );
+  ImGui::SetNextWindowSize(ImVec2(width,height), ImGuiCond_Always);
+
+  ImGui::Begin("testing", nullptr,
+               ImGuiWindowFlags_NoBackground
+               | ImGuiWindowFlags_NoTitleBar
+               | ImGuiWindowFlags_NoResize
+               | ImGuiWindowFlags_NoMove
+               | ImGuiWindowFlags_NoScrollbar
+               | ImGuiWindowFlags_NoSavedSettings
+               | ImGuiWindowFlags_NoInputs);
+
+  ImDrawList* list = ImGui::GetWindowDrawList();
+  for(int pass = 0;pass<2;pass++)
+  {
+    for(auto & p : L)
+    {
+      list->PathLineTo({ highdpi*p(0),height-highdpi*p(1) });
+    }
+    const bool closed = !(mode==LASSO || mode==POLYGONAL_LASSO) || !(is_down || is_drawing);
+    if(pass == 0)
+    {
+      list->PathStroke(IM_COL32(255, 255, 255, 255), closed, 2);
+    }else
+    {
+      list->PathStroke(IM_COL32(0, 0, 0, 255), closed, 1);
+    }
+  }
+
+  ImGui::End();
+
+  ImGui::Render();
+  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+
+  return false;
+}
+
+IGL_INLINE bool SelectionPlugin::mouse_down(int button, int modifier)
+{
+  if(mode == OFF){ return false;}
+  is_down = true;
+  has_moved_since_down = false;
+  if(!is_drawing)
+  {
+    L.clear();
+    is_drawing = true;
+  }
+  M.row(0) = xy(viewer);
+  M.row(1) = M.row(0);
+  L.emplace_back(M.row(0));
+  return true;
+}
+
+IGL_INLINE bool SelectionPlugin::mouse_up(int button, int modifier)
+{
+  is_down = false;
+  // are we done? Check first and last lasso point (need at least 3 (2 real
+  // points + 1 mouse-mouse point))
+  if(is_drawing &&
+    (mode!=POLYGONAL_LASSO ||(L.size()>=3&&(L[0]-L[L.size()-1]).norm()<=10.0)))
+  {
+    if(callback){ callback();}
+    is_drawing = false;
+  }
+  return false;
+}
+
+IGL_INLINE bool SelectionPlugin::mouse_move(int mouse_x, int mouse_y)
+{
+  if(!is_drawing){ return false; }
+  if(!has_moved_since_down)
+  {
+    if(mode == POLYGONAL_LASSO) { L.emplace_back(L[L.size()-1]); }
+    has_moved_since_down = true;
+  }
+  M.row(1) = xy(viewer);
+  switch(mode)
+  {
+    case RECTANGULAR_MARQUEE:
+      rect(M,L);
+      break;
+    case ELLIPTICAL_MARQUEE:
+      circle(M,L);
+      break;
+    case POLYGONAL_LASSO:
+      // Over write last point
+      L[L.size()-1] = xy(viewer);
+      break;
+    case LASSO:
+      L.emplace_back(xy(viewer));
+      break;
+    default: assert(false);
+  }
+  return true;
+}
+
+IGL_INLINE bool SelectionPlugin::key_pressed(unsigned int key, int modifiers)
+{
+  const auto clear = [&]() { M.setZero(); L.clear(); is_drawing = false; is_down = false; };
+  if(OFF_KEY.find(char(key)) != std::string::npos)
+  {
+      mode = OFF;
+      return true;
+  }
+  if(LASSO_KEY.find(char(key)) != std::string::npos)
+  {
+    if(mode == LASSO)
+    {
+      mode = POLYGONAL_LASSO;
+    }else/*if(mode == POLYGONAL_LASSO)*/
+    {
+      mode = LASSO;
+    }
+    clear();
+    return true;
+  }
+  if(MARQUEE_KEY.find(char(key)) != std::string::npos)
+  {
+    if(mode == RECTANGULAR_MARQUEE)
+    {
+      mode = ELLIPTICAL_MARQUEE;
+    }else/*if(mode == ELLIPTICAL_MARQUEE)*/
+    {
+      mode = RECTANGULAR_MARQUEE;
+    }
+    clear();
+    return true;
+  }
+  return false;
+}
+
+IGL_INLINE void SelectionPlugin::circle(const Eigen::Matrix<float,2,2> & M,  std::vector<Eigen::RowVector2f> & L)
+{
+  L.clear();
+  L.reserve(64);
+  const float r = (M.row(1)-M.row(0)).norm();
+  for(float th = 0;th<2.*igl::PI;th+=0.1)
+  {
+    L.emplace_back(M(0,0)+r*cos(th),M(0,1)+r*sin(th));
+  }
+}
+
+IGL_INLINE void SelectionPlugin::rect(const Eigen::Matrix<float,2,2> & M,  std::vector<Eigen::RowVector2f> & L)
+{
+  L.resize(4);
+  L[0] = Eigen::RowVector2f(M(0,0),M(0,1));
+  L[1] = Eigen::RowVector2f(M(1,0),M(0,1));
+  L[2] = Eigen::RowVector2f(M(1,0),M(1,1));
+  L[3] = Eigen::RowVector2f(M(0,0),M(1,1));
+}
+
+IGL_INLINE Eigen::RowVector2f SelectionPlugin::xy(const Viewer * vr)
+{
+  return Eigen::RowVector2f(
+    vr->current_mouse_x,
+    vr->core().viewport(3) - vr->current_mouse_y);
+}
+
+
+
+}}}}

+ 64 - 0
include/igl/opengl/glfw/imgui/SelectionPlugin.h

@@ -0,0 +1,64 @@
+#ifndef IGL_OPENGL_GFLW_IMGUI_IMGUIDRAWLISTPLUGIN_H
+#define IGL_OPENGL_GFLW_IMGUI_IMGUIDRAWLISTPLUGIN_H
+#include <igl/igl_inline.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+#include <imgui/imgui.h>
+#include <imgui/imgui_internal.h>
+#include <imguizmo/ImGuizmo.h>
+#include <Eigen/Dense>
+#include <vector>
+
+namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
+
+class SelectionPlugin: public igl::opengl::glfw::imgui::ImGuiMenu
+{
+public:
+  // callback called when slection is completed (usually on mouse_up)
+  std::function<void(void)> callback;
+  // whether rotating, translating or scaling
+  ImGuizmo::OPERATION operation;
+  // stored transformation
+  Eigen::Matrix4f T;
+  // Initilize with rotate operation on an identity transform (at origin)
+  SelectionPlugin():operation(ImGuizmo::ROTATE),T(Eigen::Matrix4f::Identity()){};
+  IGL_INLINE virtual void init(igl::opengl::glfw::Viewer *_viewer) override;
+  IGL_INLINE virtual bool pre_draw() override;
+  IGL_INLINE virtual bool post_draw() override;
+  IGL_INLINE virtual bool mouse_down(int button, int modifier) override;
+  IGL_INLINE virtual bool mouse_up(int button, int modifier) override;
+  IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) override;
+  IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) override;
+  // helpers
+  IGL_INLINE static void circle(const Eigen::Matrix<float,2,2> & M,  std::vector<Eigen::RowVector2f> & L);
+  IGL_INLINE static void rect(const Eigen::Matrix<float,2,2> & M,  std::vector<Eigen::RowVector2f> & L);
+  IGL_INLINE static Eigen::RowVector2f xy(const Viewer * v);
+  // customizable hotkeys
+  std::string MARQUEE_KEY = "Mm";
+  // leave 'L' for show_lines in viewer
+  std::string LASSO_KEY = "l";
+  std::string OFF_KEY = "Vv";
+  enum Mode
+  {
+    OFF                 = 0,
+    RECTANGULAR_MARQUEE = 1,
+    ELLIPTICAL_MARQUEE  = 2,
+    POLYGONAL_LASSO     = 3,
+    LASSO               = 4,
+    NUM_MODES           = 5
+  } mode = RECTANGULAR_MARQUEE;
+  bool visible = true;
+  bool is_down = false;
+  bool has_moved_since_down = false;
+  bool is_drawing = false;
+  // min and max corners of 2D rectangular marquee
+  Eigen::Matrix<float,2,2> M = Eigen::Matrix<float,2,2>::Zero();
+  // list of points of 2D lasso marquee
+  std::vector<Eigen::RowVector2f> L;
+};
+
+}}}}
+
+#ifndef IGL_STATIC_LIBRARY
+#include "SelectionPlugin.cpp"
+#endif
+#endif

+ 1 - 0
include/igl/project.cpp

@@ -51,6 +51,7 @@ IGL_INLINE void igl::project(
 }
 
 #ifdef IGL_STATIC_LIBRARY
+template void igl::project<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 1, 0, 4, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 1, 0, 4, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
 // Explicit template instantiation
 template Eigen::Matrix<double, 3, 1, 0, 3, 1> igl::project<double>(Eigen::Matrix<double, 3, 1, 0, 3, 1> const&, Eigen::Matrix<double, 4, 4, 0, 4, 4> const&, Eigen::Matrix<double, 4, 4, 0, 4, 4> const&, Eigen::Matrix<double, 4, 1, 0, 4, 1> const&);
 template Eigen::Matrix<float, 3, 1, 0, 3, 1> igl::project<float>(Eigen::Matrix<float, 3, 1, 0, 3, 1> const&, Eigen::Matrix<float, 4, 4, 0, 4, 4> const&, Eigen::Matrix<float, 4, 4, 0, 4, 4> const&, Eigen::Matrix<float, 4, 1, 0, 4, 1> const&);

+ 5 - 0
include/igl/project.h

@@ -36,6 +36,11 @@ namespace igl
   // Known issue:
   //   The compiler will not complain if V and P are Vector3d, but the result
   //   will be incorrect.
+  //
+  // Example:
+  //   igl::opengl::glfw::Viewer vr;
+  //   ...
+  //   igl::project(V,vr.core().view,vr.core().proj,vr.core().viewport,P);
   template <typename DerivedV, typename DerivedM, typename DerivedN, typename DerivedO, typename DerivedP>
   IGL_INLINE void project(
     const    Eigen::MatrixBase<DerivedV>&  V,

+ 100 - 0
include/igl/screen_space_selection.cpp

@@ -0,0 +1,100 @@
+#include "screen_space_selection.h"
+
+#include <igl/AABB.h>
+#include <igl/winding_number.h>
+#include <igl/project.h>
+#include <igl/unproject.h>
+#include <igl/Hit.h>
+#include <igl/parallel_for.h>
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedM,
+  typename DerivedN,
+  typename DerivedO,
+  typename Ltype,
+  typename DerivedW,
+  typename Deriveda>
+IGL_INLINE void igl::screen_space_selection(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const igl::AABB<DerivedV, 3> & tree,
+  const Eigen::MatrixBase<DerivedM>& model,
+  const Eigen::MatrixBase<DerivedN>& proj,
+  const Eigen::MatrixBase<DerivedO>& viewport,
+  const std::vector<Eigen::Matrix<Ltype,1,2> > & L,
+  Eigen::PlainObjectBase<DerivedW> & W,
+  Eigen::PlainObjectBase<Deriveda> & and_visible)
+{
+  typedef typename DerivedV::Scalar Scalar;
+  screen_space_selection(V,model,proj,viewport,L,W);
+  const Eigen::RowVector3d origin =
+    (model.inverse().col(3)).head(3).template cast<Scalar>();
+  igl::parallel_for(V.rows(),[&](const int i)
+  {
+    // Skip unselected points
+    if(W(i)<0.5){ return; }
+    igl::Hit hit;
+    tree.intersect_ray(V,F,origin,V.row(i)-origin,hit);
+    and_visible(i) = !(hit.t>1e-5 && hit.t<(1-1e-5));
+  });
+}
+
+template <
+  typename DerivedV,
+  typename DerivedM,
+  typename DerivedN,
+  typename DerivedO,
+  typename Ltype,
+  typename DerivedW>
+IGL_INLINE void igl::screen_space_selection(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedM>& model,
+  const Eigen::MatrixBase<DerivedN>& proj,
+  const Eigen::MatrixBase<DerivedO>& viewport,
+  const std::vector<Eigen::Matrix<Ltype,1,2> > & L,
+  Eigen::PlainObjectBase<DerivedW> & W)
+{
+  typedef typename DerivedV::Scalar Scalar;
+  Eigen::Matrix<Scalar,Eigen::Dynamic,2> P(L.size(),2);
+  Eigen::Matrix<int,Eigen::Dynamic,2> E(L.size(),2);
+  for(int i = 0;i<E.rows();i++)
+  { 
+    P.row(i) = L[i].template cast<Scalar>();
+    E(i,0) = i; 
+    E(i,1) = (i+1)%E.rows(); 
+  }
+  return screen_space_selection(V,model,proj,viewport,P,E,W);
+}
+
+template <
+  typename DerivedV,
+  typename DerivedM,
+  typename DerivedN,
+  typename DerivedO,
+  typename DerivedP,
+  typename DerivedE,
+  typename DerivedW>
+IGL_INLINE void igl::screen_space_selection(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedM>& model,
+  const Eigen::MatrixBase<DerivedN>& proj,
+  const Eigen::MatrixBase<DerivedO>& viewport,
+  const Eigen::MatrixBase<DerivedP> & P,
+  const Eigen::MatrixBase<DerivedE> & E,
+  Eigen::PlainObjectBase<DerivedW> & W)
+{
+  // project all mesh vertices to 2D
+  DerivedV V2;
+  igl::project(V,model,proj,viewport,V2);
+  // In 2D this uses O(N*M) naive algorithm.
+  igl::winding_number(P,E,V2,W);
+  W = W.array().abs().eval();
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+template void igl::screen_space_selection<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 1, 0, 4, 1>, float, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Array<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, igl::AABB<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 3> const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 1, 0, 4, 1> > const&, std::vector<Eigen::Matrix<float, 1, 2, 1, 1, 2>, std::allocator<Eigen::Matrix<float, 1, 2, 1, 1, 2> > > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Array<double, -1, 1, 0, -1, 1> >&);
+template void igl::screen_space_selection<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 4, 0, 4, 4>, Eigen::Matrix<float, 4, 1, 0, 4, 1>, float, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 4, 0, 4, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 4, 1, 0, 4, 1> > const&, std::vector<Eigen::Matrix<float, 1, 2, 1, 1, 2>, std::allocator<Eigen::Matrix<float, 1, 2, 1, 1, 2> > > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+#endif

+ 105 - 0
include/igl/screen_space_selection.h

@@ -0,0 +1,105 @@
+#ifndef IGL_SCREEN_SPACE_SELECTION_H
+#define IGL_SCREEN_SPACE_SELECTION_H
+
+#include "igl/igl_inline.h"
+#include <Eigen/Core>
+#include <vector>
+// Forward declaration
+namespace igl { template <typename DerivedV, int DIM> class AABB; }
+
+namespace igl
+{
+  // Given a mesh, a camera  determine which points are inside of a given 2D
+  // screen space polygon **culling points based on self-occlusion.**
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   F  #F by 3 list of mesh triangle indices into rows of V
+  //   tree  precomputed bounding volume heirarchy
+  //   model  4 by 4 camera model-view matrix
+  //   proj  4 by 4 camera projection matrix (perspective or orthoraphic)
+  //   viewport  4-vector containing camera viewport
+  //   L  #L by 2 list of 2D polygon vertices (in order)
+  // Outputs:
+  //   W  #V by 1 list of winding numbers (|W|>0.5 indicates inside)
+  //   and_visible  #V by 1 list of visibility values (only correct for vertices
+  //     with |W|>0.5)
+  template <
+    typename DerivedV,
+    typename DerivedF,
+    typename DerivedM,
+    typename DerivedN,
+    typename DerivedO,
+    typename Ltype,
+    typename DerivedW,
+    typename Deriveda>
+  IGL_INLINE void screen_space_selection(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    const igl::AABB<DerivedV, 3> & tree,
+    const Eigen::MatrixBase<DerivedM>& model,
+    const Eigen::MatrixBase<DerivedN>& proj,
+    const Eigen::MatrixBase<DerivedO>& viewport,
+    const std::vector<Eigen::Matrix<Ltype,1,2> > & L,
+    Eigen::PlainObjectBase<DerivedW> & W,
+    Eigen::PlainObjectBase<Deriveda> & and_visible);
+  // Given a mesh, a camera  determine which points are inside of a given 2D
+  // screen space polygon
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   model  4 by 4 camera model-view matrix
+  //   proj  4 by 4 camera projection matrix (perspective or orthoraphic)
+  //   viewport  4-vector containing camera viewport
+  //   L  #L by 2 list of 2D polygon vertices (in order)
+  // Outputs:
+  //   W  #V by 1 list of winding numbers (|W|>0.5 indicates inside)
+  template <
+    typename DerivedV,
+    typename DerivedM,
+    typename DerivedN,
+    typename DerivedO,
+    typename Ltype,
+    typename DerivedW>
+  IGL_INLINE void screen_space_selection(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedM>& model,
+    const Eigen::MatrixBase<DerivedN>& proj,
+    const Eigen::MatrixBase<DerivedO>& viewport,
+    const std::vector<Eigen::Matrix<Ltype,1,2> > & L,
+    Eigen::PlainObjectBase<DerivedW> & W);
+  // Given a mesh, a camera  determine which points are inside of a given 2D
+  // screen space polygon
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   model  4 by 4 camera model-view matrix
+  //   proj  4 by 4 camera projection matrix (perspective or orthoraphic)
+  //   viewport  4-vector containing camera viewport
+  //   P  #P by 2 list of screen space polygon vertices
+  //   E  #E by 2 list of screen space edges as indices into rows of P
+  // Outputs:
+  //   W  #V by 1 list of winding numbers (|W|>0.5 indicates inside)
+  template <
+    typename DerivedV,
+    typename DerivedM,
+    typename DerivedN,
+    typename DerivedO,
+    typename DerivedP,
+    typename DerivedE,
+    typename DerivedW>
+  IGL_INLINE void screen_space_selection(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedM>& model,
+    const Eigen::MatrixBase<DerivedN>& proj,
+    const Eigen::MatrixBase<DerivedO>& viewport,
+    const Eigen::MatrixBase<DerivedP> & P,
+    const Eigen::MatrixBase<DerivedE> & E,
+    Eigen::PlainObjectBase<DerivedW> & W);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#include "screen_space_selection.cpp"
+#endif
+  
+#endif

+ 0 - 11
include/igl/signed_angle.cpp

@@ -47,27 +47,16 @@ IGL_INLINE typename DerivedA::Scalar igl::signed_angle(
 
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>, Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>, Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 2, 0, -1, 2> const, 1, 2, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Matrix<double, 1, 2, 1, 1, 2> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 2, 1, 1, 2> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<float, -1, -1, 0, -1, -1> const, 1, -1, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<float, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<float, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<float, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Matrix<float, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>, Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>, Eigen::Matrix<float, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&);
-// generated by autoexplicit.sh
 template Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false>, Eigen::Matrix<double, 1, 2, 1, 1, 2> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1> const, 1, -1, false> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 2, 1, 1, 2> > const&);
 template Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>, Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false>, Eigen::Matrix<float, 1, 2, 1, 1, 2> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 0, -1, 3> const, 1, 3, false> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 2, 1, 1, 2> > const&);
 template Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>::Scalar igl::signed_angle<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true>, Eigen::Matrix<float, 1, 2, 1, 1, 2> >(Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Block<Eigen::Matrix<float, -1, 3, 1, -1, 3> const, 1, 3, true> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, 2, 1, 1, 2> > const&);

+ 6 - 0
include/igl/unproject_onto_mesh.h

@@ -29,6 +29,12 @@ namespace igl
   //    fid  id of the first face hit
   //    bc  barycentric coordinates of hit
   // Returns true if there's a hit
+  //
+  // Example:
+  //   igl::opengl::glfw::Viewer vr;
+  //   ...
+  //   igl::unproject_onto_mesh(
+  //     pos,vr.core().view,vr.core().proj,vr.core().viewport,V,F,fid,bc);
   template < typename DerivedV, typename DerivedF, typename Derivedbc>
   IGL_INLINE bool unproject_onto_mesh(
     const Eigen::Vector2f& pos,

+ 2 - 0
include/igl/winding_number.cpp

@@ -93,6 +93,8 @@ IGL_INLINE typename DerivedV::Scalar igl::winding_number(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template void igl::winding_number<Eigen::Matrix<double, -1, 2, 0, -1, 2>, Eigen::Matrix<int, -1, 2, 0, -1, 2>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 2, 0, -1, 2> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
 template Eigen::Matrix<double, -1, 3, 1, -1, 3>::Scalar igl::winding_number<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&);
 // generated by autoexplicit.sh
 template Eigen::Matrix<double, -1, 3, 1, -1, 3>::Scalar igl::winding_number<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, 1, 2, 1, 1, 2> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 2, 1, 1, 2> > const&);

+ 5 - 0
tutorial/112_Selection/CMakeLists.txt

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

+ 66 - 0
tutorial/112_Selection/main.cpp

@@ -0,0 +1,66 @@
+#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/SelectionPlugin.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::SelectionPlugin plugin;
+  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);
+  plugin.callback = [&]()
+  {
+    screen_space_selection(V,F,tree,vr.core().view,vr.core().proj,vr.core().viewport,plugin.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.plugins.push_back(&plugin);
+  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();
+}

+ 1 - 0
tutorial/CMakeLists.txt

@@ -63,6 +63,7 @@ if(TUTORIALS_CHAPTER1)
   add_subdirectory("108_MultipleViews")
   if(LIBIGL_WITH_OPENGL_GLFW_IMGUI)
     add_subdirectory("109_ImGuizmo")
+    add_subdirectory("112_Selection")
   endif()
   add_subdirectory("110_MshView")
 endif()