Browse Source

boost extra, connected components in mesh

Former-commit-id: 2fdb94f55ec4fd24d74c3916045348274f3b9638
Alec Jacobson (jalec 11 years ago
parent
commit
7dd2ea26d9

+ 3 - 0
Makefile

@@ -41,6 +41,9 @@ endif
 ifeq ($(IGL_WITH_EMBREE),1)
 	EXTRA_DIRS+=include/igl/embree
 endif
+ifeq ($(IGL_WITH_BOOST),1)
+	EXTRA_DIRS+=include/igl/boost
+endif
 
 .PHONY: examples
 .PHONY: extras

+ 1 - 0
Makefile.conf

@@ -28,6 +28,7 @@ ifeq ($(IGL_USERNAME),ajx)
 	IGL_WITH_MOSEK=1
 	IGL_WITH_PNG=1
 	IGL_WITH_XML=1
+	IGL_WITH_BOOST=1
 	# I don't use llvm
 #AFLAGS = -m64 -march="corei7-avx"
 	# msse4.2 is necessary for me to get embree to compile correctly

+ 8 - 6
examples/flare-eyes/Makefile

@@ -22,12 +22,14 @@ MATLAB_LIB=-L$(MATLAB)/bin/maci64 -lmx -lmat -lmex -lstdc++
 
 CARBON_LIB=-framework Carbon
 
-# Use free glut for mouse scrolling
-FREE_GLUT=/opt/local/
-FREE_GLUT_INC=-I$(FREE_GLUT)/include
-FREE_GLUT_LIB=-L$(FREE_GLUT)/lib -lglut
-GLUT_LIB=$(FREE_GLUT_LIB)
-GLUT_INC=$(FREE_GLUT_INC)
+## Use free glut for mouse scrolling
+#FREE_GLUT=/opt/local/
+#FREE_GLUT_INC=-I$(FREE_GLUT)/include
+#FREE_GLUT_LIB=-L$(FREE_GLUT)/lib -lglut
+#GLUT_LIB=$(FREE_GLUT_LIB)
+#GLUT_INC=$(FREE_GLUT_INC)
+GLUT_LIB=-framework GLUT
+GLUT_INC=
 
 INC=$(LIBIGL_INC) $(ANTTWEAKBAR_INC) $(EIGEN3_INC) $(MATLAB_INC) $(GLUT_INC)
 LIB=$(OPENGL_LIB) $(GLUT_LIB) $(ANTTWEAKBAR_LIB) $(LIBIGL_LIB) $(MATLAB_LIB) $(CARBON_LIB)

+ 11 - 12
examples/flare-eyes/example.cpp

@@ -402,6 +402,7 @@ void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
   using namespace std;
   using namespace Eigen;
   using namespace igl;
+
   bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y);
   switch(glutButton)
   {
@@ -431,25 +432,25 @@ void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
       break;
     }
     // Scroll down
-    case 3:
+    case GLUT_WHEEL_DOWN:
     {
       mouse_wheel(0,-1,mouse_x,mouse_y);
       break;
     }
     // Scroll up
-    case 4:
+    case GLUT_WHEEL_UP:
     {
       mouse_wheel(0,1,mouse_x,mouse_y);
       break;
     }
     // Scroll left
-    case 5:
+    case GLUT_WHEEL_LEFT:
     {
       mouse_wheel(1,-1,mouse_x,mouse_y);
       break;
     }
     // Scroll right
-    case 6:
+    case GLUT_WHEEL_RIGHT:
     {
       mouse_wheel(1,1,mouse_x,mouse_y);
       break;
@@ -564,9 +565,7 @@ void redo()
 void key(unsigned char key, int mouse_x, int mouse_y)
 {
   using namespace std;
-  GetKeys(keyStates);
-  const bool command_down = IS_KEYDOWN(kVK_Command);
-  const bool shift_down = IS_KEYDOWN(kVK_Shift);
+  int mod = glutGetModifiers();
   switch(key)
   {
     // ESC
@@ -577,9 +576,9 @@ void key(unsigned char key, int mouse_x, int mouse_y)
       exit(0);
     case 'z':
     case 'Z':
-      if(command_down)
+      if(mod & GLUT_ACTIVE_COMMAND)
       {
-        if(shift_down)
+        if(mod & GLUT_ACTIVE_SHIFT)
         {
           redo();
         }else
@@ -726,10 +725,10 @@ int main(int argc, char * argv[])
   const float BLUE[3] = {0,0,1};
   //lens_flare_create(RED,GREEN,BLUE,flares);
   flares.resize(4);
-  flares[0] = Flare(-1, 1.0f, 1.*0.1f,  BLUE, 1.0);
+  flares[0] = Flare(-1, 1.0f, 1.*0.1f,  RED, 1.0);
   flares[1] = Flare(-1, 1.0f, 1.*0.15f, GREEN, 1.0);
-  flares[2] = Flare(-1, 1.0f, 1.*0.35f, RED, 1.0);
-  flares[3] = Flare( 2, 1.0f, 1.*0.1f, RED, 0.4);
+  flares[2] = Flare(-1, 1.0f, 1.*0.35f, BLUE, 1.0);
+  flares[3] = Flare( 2, 1.0f, 1.*0.1f, BLUE, 0.4);
 
   glutMainLoop();
 

+ 47 - 0
examples/patches/Makefile

@@ -0,0 +1,47 @@
+
+.PHONY: all
+
+# Shared flags etc.
+include ../../Makefile.conf
+
+all: obj example
+
+.PHONY: example
+
+LIBIGL=../../
+LIBIGL_INC=-I$(LIBIGL)/include
+LIBIGL_LIB=-L$(LIBIGL)/lib -ligl -liglmatlab -liglembree -liglboost
+
+EIGEN3_INC=-I/opt/local/include/eigen3 -I/opt/local/include/eigen3/unsupported
+
+ANTTWEAKBAR_INC=-I$(LIBIGL)/external/AntTweakBar/include
+ANTTWEAKBAR_LIB=-L$(LIBIGL)/external/AntTweakBar/lib -lAntTweakBar -framework AppKit
+
+MATLAB_INC=-I$(MATLAB)/extern/include/
+MATLAB_LIB=-L$(MATLAB)/bin/maci64 -lmx -lmat -lmex -lstdc++
+
+# Use free glut for mouse scrolling
+GLUT_LIB=-framework GLUT
+GLUT_INC=-framework GLUT
+
+INC=$(LIBIGL_INC) $(ANTTWEAKBAR_INC) $(EIGEN3_INC) $(MATLAB_INC) $(GLUT_INC)
+LIB=$(OPENGL_LIB) $(GLUT_LIB) $(ANTTWEAKBAR_LIB) $(LIBIGL_LIB) $(MATLAB_LIB)
+
+CPP_FILES=$(wildcard ./*.cpp)
+OBJ_FILES=$(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o))) 
+
+example: obj $(OBJ_FILES)
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -o example $(OBJ_FILES) $(LIB)
+
+obj:
+	mkdir -p obj
+
+obj/%.o: %.cpp
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC)
+
+obj/%.o: %.cpp %.h
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC)
+
+clean:
+	rm -f example.o
+	rm -f example

+ 658 - 0
examples/patches/example.cpp

@@ -0,0 +1,658 @@
+// Small GLUT application to test different scene rotation paradigms 
+//
+
+#include <igl/readOBJ.h>
+#include <igl/writeOBJ.h>
+#include <igl/writeOFF.h>
+#include <igl/readWRL.h>
+#include <igl/report_gl_error.h>
+#include <igl/triangulate.h>
+#include <igl/readOFF.h>
+#include <igl/readMESH.h>
+#include <igl/draw_mesh.h>
+#include <igl/draw_floor.h>
+#include <igl/pathinfo.h>
+#include <igl/list_to_matrix.h>
+#include <igl/quat_to_mat.h>
+#include <igl/per_face_normals.h>
+#include <igl/material_colors.h>
+#include <igl/trackball.h>
+#include <igl/snap_to_canonical_view_quat.h>
+#include <igl/REDRUM.h>
+#include <igl/Camera.h>
+#include <igl/ReAntTweakBar.h>
+#include <igl/get_seconds.h>
+#include <igl/jet.h>
+#include <igl/boost/manifold_patches.h>
+#include <igl/boost/components.h>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+#include <GLUT/glut.h>
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <iostream>
+
+
+Eigen::MatrixXd V,N,C;
+Eigen::VectorXd Vmid,Vmin,Vmax;
+double bbd = 1.0;
+Eigen::MatrixXi F;
+struct State
+{
+  igl::Camera camera;
+} s;
+
+// See README for descriptions
+enum RotationType
+{
+  ROTATION_TYPE_IGL_TRACKBALL = 0,
+  ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1,
+  NUM_ROTATION_TYPES = 2,
+} rotation_type;
+
+std::stack<State> undo_stack;
+std::stack<State> redo_stack;
+
+bool is_rotating = false;
+int down_x,down_y;
+igl::Camera down_camera;
+
+bool is_animating = false;
+double animation_start_time = 0;
+double ANIMATION_DURATION = 0.5;
+Eigen::Quaterniond animation_from_quat;
+Eigen::Quaterniond animation_to_quat;
+
+int width,height;
+Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
+
+#define REBAR_NAME "temp.rbr"
+igl::ReTwBar rebar;
+
+void push_undo()
+{
+  undo_stack.push(s);
+  // Clear
+  redo_stack = std::stack<State>();
+}
+
+void TW_CALL set_camera_rotation(const void * value, void *clientData)
+{
+  using namespace std;
+  // case current value to double
+  const double * quat = (const double *)(value);
+  std::copy(quat,quat+4,s.camera.rotation);
+}
+
+void TW_CALL set_rotation_type(const void * value, void * clientData)
+{
+  using namespace Eigen;
+  using namespace std;
+  using namespace igl;
+  const RotationType old_rotation_type = rotation_type;
+  rotation_type = *(const RotationType *)(value);
+  if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP && 
+    old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP)
+  {
+    push_undo();
+    copy(s.camera.rotation,s.camera.rotation+4,animation_from_quat.coeffs().data());
+    const Vector3d up = animation_from_quat.matrix() * Vector3d(0,1,0);
+    Vector3d proj_up(0,up(1),up(2));
+    if(proj_up.norm() == 0)
+    {
+      proj_up = Vector3d(0,1,0);
+    }
+    proj_up.normalize();
+    Quaterniond dq;
+    dq = Quaterniond::FromTwoVectors(up,proj_up);
+    animation_to_quat = dq * animation_from_quat;
+    // start animation
+    animation_start_time = get_seconds();
+    is_animating = true;
+  }
+}
+void TW_CALL get_rotation_type(void * value, void *clientData)
+{
+  RotationType * rt = (RotationType *)(value);
+  *rt = rotation_type;
+}
+
+void TW_CALL get_camera_rotation(void * value, void *clientData)
+{
+  using namespace std;
+  // case current value to double
+  double * quat = (double *)(value);
+  std::copy(s.camera.rotation,s.camera.rotation+4,quat);
+}
+
+void reshape(int width, int height)
+{
+  ::width = width;
+  ::height = height;
+  glViewport(0,0,width,height);
+  // Send the new window size to AntTweakBar
+  TwWindowSize(width, height);
+}
+
+void push_scene()
+{
+  using namespace igl;
+  using namespace std;
+  const double angle = s.camera.angle;
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix();
+  glLoadIdentity();
+  double zNear = 1e-2;
+  double zFar = 100;
+  double aspect = ((double)width)/((double)height);
+  // Amount of scaling needed to "fix" perspective z-shift
+  double z_fix = 1.0;
+  // 5 is far enough to see unit "things" well
+  const double camera_z = 2;
+  // Test if should be using true orthographic projection
+  if(angle == 0)
+  {
+    glOrtho(
+      -0.5*camera_z*aspect,
+      0.5*camera_z*aspect,
+      -0.5*camera_z,
+      0.5*camera_z,
+      zNear,
+      zFar);
+  }else
+  {
+    // Make sure aspect is sane
+    aspect = aspect < 0.01 ? 0.01 : aspect;
+    gluPerspective(angle,aspect,zNear,zFar);
+    z_fix = 2.*tan(angle/2./360.*2.*M_PI);
+  }
+
+  glMatrixMode(GL_MODELVIEW);
+  glPushMatrix();
+  glLoadIdentity();
+  gluLookAt(0,0,camera_z,0,0,0,0,1,0);
+  // Adjust scale to correct perspective
+  glScaled(z_fix,z_fix,z_fix);
+  // scale, pan
+  glScaled( s.camera.zoom, s.camera.zoom, s.camera.zoom);
+  double mat[4*4];
+  quat_to_mat(s.camera.rotation,mat);
+  glMultMatrixd(mat);
+}
+
+void push_object()
+{
+  using namespace igl;
+  glPushMatrix();
+  glScaled(2./bbd,2./bbd,2./bbd);
+  glTranslated(-Vmid(0),-Vmid(1),-Vmid(2));
+}
+
+void pop_object()
+{
+  glPopMatrix();
+}
+
+void pop_scene()
+{
+  glMatrixMode(GL_PROJECTION);
+  glPopMatrix();
+  glMatrixMode(GL_MODELVIEW);
+  glPopMatrix();
+}
+
+// Set up double-sided lights
+void lights()
+{
+  using namespace std;
+  using namespace Eigen;
+  glEnable(GL_LIGHTING);
+  //glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+  glEnable(GL_LIGHT0);
+  float WHITE[4] =  {0.7,0.7,0.7,1.};
+  float GREY[4] =  {0.4,0.4,0.4,1.};
+  float BLACK[4] =  {0.,0.,0.,1.};
+  float NEAR_BLACK[4] =  {0.1,0.1,0.1,1.};
+  Vector4f pos = light_pos;
+  glLightfv(GL_LIGHT0,GL_AMBIENT,GREY);
+  glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE);
+  glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK);
+  glLightfv(GL_LIGHT0,GL_POSITION,pos.data());
+  glEnable(GL_LIGHT1);
+  pos(0) *= -1;
+  pos(1) *= -1;
+  pos(2) *= -1;
+  glLightfv(GL_LIGHT1,GL_AMBIENT,BLACK);
+  glLightfv(GL_LIGHT1,GL_DIFFUSE,NEAR_BLACK);
+  glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK);
+  glLightfv(GL_LIGHT1,GL_POSITION,pos.data());
+}
+
+void display()
+{
+  using namespace igl;
+  using namespace std;
+  using namespace Eigen;
+  glClearColor(1,1,1,0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  if(is_animating)
+  {
+    double t = (get_seconds() - animation_start_time)/ANIMATION_DURATION;
+    if(t > 1)
+    {
+      t = 1;
+      is_animating = false;
+    }
+    Quaterniond q;
+    q.coeffs() = 
+      animation_to_quat.coeffs()*t + animation_from_quat.coeffs()*(1.-t);
+    q.normalize();
+    copy(q.coeffs().data(),q.coeffs().data()+4,s.camera.rotation);
+  }
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_NORMALIZE);
+  lights();
+  push_scene();
+  push_object();
+
+  // Set material properties
+  glEnable(GL_COLOR_MATERIAL);
+  glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
+  draw_mesh(V,F,N,C);
+  pop_object();
+
+  // Draw a nice floor
+  glPushMatrix();
+  const double floor_offset =
+    -2./bbd*(V.col(1).maxCoeff()-Vmid(1));
+  glTranslated(0,floor_offset,0);
+  const float GREY[4] = {0.5,0.5,0.6,1.0};
+  const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
+  draw_floor(GREY,DARK_GREY);
+  glPopMatrix();
+
+  pop_scene();
+
+  report_gl_error();
+
+  TwDraw();
+  glutSwapBuffers();
+  glutPostRedisplay();
+}
+
+void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  push_undo();
+  if(wheel == 0)
+  {
+    static double mouse_scroll_y = 0;
+    const double delta_y = 0.125*direction;
+    mouse_scroll_y += delta_y;
+    // absolute scale difference when changing zooms (+1)
+    const double z_diff = 0.01;
+    GLint viewport[4];
+    glGetIntegerv(GL_VIEWPORT,viewport);
+    if(TwMouseMotion(mouse_x, viewport[3] - mouse_y))
+    {
+      TwMouseWheel(mouse_scroll_y);
+    }else
+    {
+      s.camera.zoom *= (1.0+double(direction)*z_diff);
+      const double min_zoom = 0.01;
+      const double max_zoom = 10.0;
+      s.camera.zoom = min(max_zoom,max(min_zoom,s.camera.zoom));
+    }
+  }else
+  {
+    if(!is_rotating)
+    {
+      // Change viewing angle (reshape will take care of adjust zoom)
+      const double a_diff = 1.0;
+      s.camera.angle += double(direction)*a_diff;
+      const double min_angle = 15.0;
+      s.camera.angle = 
+        min(90.0,max(min_angle,s.camera.angle));
+    }
+  }
+}
+
+void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  using namespace Eigen;
+  using namespace igl;
+  bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y);
+  switch(glutButton)
+  {
+    case GLUT_RIGHT_BUTTON:
+    case GLUT_LEFT_BUTTON:
+    {
+      switch(glutState)
+      {
+        case 1:
+          // up
+          glutSetCursor(GLUT_CURSOR_INHERIT);
+          is_rotating = false;
+          break;
+        case 0:
+          if(!tw_using)
+          {
+            push_undo();
+            glutSetCursor(GLUT_CURSOR_CYCLE);
+            // collect information for trackball
+            is_rotating = true;
+            down_camera = s.camera;
+            down_x = mouse_x;
+            down_y = mouse_y;
+          }
+        break;
+      }
+      break;
+      // Scroll down
+      case GLUT_WHEEL_DOWN:
+      {
+        mouse_wheel(0,-1,mouse_x,mouse_y);
+        break;
+      }
+      // Scroll up
+      case GLUT_WHEEL_UP:
+      {
+        mouse_wheel(0,1,mouse_x,mouse_y);
+        break;
+      }
+      // Scroll left
+      case GLUT_WHEEL_LEFT:
+      {
+        mouse_wheel(1,-1,mouse_x,mouse_y);
+        break;
+      }
+      // Scroll right
+      case GLUT_WHEEL_RIGHT:
+      {
+        mouse_wheel(1,1,mouse_x,mouse_y);
+        break;
+      }
+    }
+  }
+}
+
+void mouse_drag(int mouse_x, int mouse_y)
+{
+  using namespace igl;
+  using namespace std;
+  using namespace Eigen;
+
+  if(is_rotating)
+  {
+    glutSetCursor(GLUT_CURSOR_CYCLE);
+    switch(rotation_type)
+    {
+      case ROTATION_TYPE_IGL_TRACKBALL:
+      {
+        // Rotate according to trackball
+        igl::trackball<double>(
+          width,
+          height,
+          2.0,
+          down_camera.rotation,
+          down_x,
+          down_y,
+          mouse_x,
+          mouse_y,
+          s.camera.rotation);
+          break;
+      }
+      case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP:
+      {
+        Quaterniond down_q;
+        copy(down_camera.rotation,down_camera.rotation+4,down_q.coeffs().data());
+        Vector3d axis(0,1,0);
+        const double speed = 2.0;
+        Quaterniond q;
+        q = down_q * 
+          Quaterniond(
+            AngleAxisd(
+              M_PI*((double)(mouse_x-down_x))/(double)width*speed/2.0,
+              axis.normalized()));
+        q.normalize();
+        {
+          Vector3d axis(1,0,0);
+          const double speed = 2.0;
+          if(axis.norm() != 0)
+          {
+            q = 
+              Quaterniond(
+                AngleAxisd(
+                  M_PI*(mouse_y-down_y)/(double)width*speed/2.0,
+                  axis.normalized())) * q;
+            q.normalize();
+          }
+        }
+        copy(q.coeffs().data(),q.coeffs().data()+4,s.camera.rotation);
+        break;
+      }
+      default:
+        break;
+    }
+  }
+}
+
+void init_relative()
+{
+  using namespace Eigen;
+  using namespace igl;
+  per_face_normals(V,F,N);
+  Vmax = V.colwise().maxCoeff();
+  Vmin = V.colwise().minCoeff();
+  Vmid = 0.5*(Vmax + Vmin);
+  bbd = (Vmax-Vmin).norm();
+}
+
+void init_patches()
+{
+  using namespace Eigen;
+  using namespace igl;
+  using namespace std;
+  VectorXi CC;
+  SparseMatrix<int> A;
+  //manifold_patches(F,CC,A);
+  components(F,CC);
+  C.resize(CC.rows(),3);
+  double num_cc = (double)CC.maxCoeff()+1.0;
+  for(int f = 0;f<CC.rows();f++)
+  {
+    jet(
+      (double)CC(f)/num_cc,
+      C(f,0),
+      C(f,1),
+      C(f,2));
+  }
+}
+
+void undo()
+{
+  using namespace std;
+  if(!undo_stack.empty())
+  {
+    redo_stack.push(s);
+    s = undo_stack.top();
+    undo_stack.pop();
+  }
+}
+
+void redo()
+{
+  using namespace std;
+  if(!redo_stack.empty())
+  {
+    undo_stack.push(s);
+    s = redo_stack.top();
+    redo_stack.pop();
+  }
+}
+
+void key(unsigned char key, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  int mod = glutGetModifiers();
+  switch(key)
+  {
+    // ESC
+    case char(27):
+      rebar.save(REBAR_NAME);
+    // ^C
+    case char(3):
+      exit(0);
+    case 'z':
+    case 'Z':
+      if(mod & GLUT_ACTIVE_COMMAND)
+      {
+        if(mod & GLUT_ACTIVE_SHIFT)
+        {
+          redo();
+        }else
+        {
+          undo();
+        }
+        break;
+      }else
+      {
+        push_undo();
+        igl::snap_to_canonical_view_quat<double>(
+          s.camera.rotation,
+          1.0,
+          s.camera.rotation);
+        break;
+      }
+    default:
+      if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y))
+      {
+        cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
+      }
+  }
+  
+}
+
+int main(int argc, char * argv[])
+{
+  using namespace std;
+  using namespace Eigen;
+  using namespace igl;
+  string filename = "../shared/cheburashka.obj";
+  if(argc < 2)
+  {
+    cerr<<"Usage:"<<endl<<"    ./example input.obj"<<endl;
+    cout<<endl<<"Opening default mesh..."<<endl;
+  }else
+  {
+    // Read and prepare mesh
+    filename = argv[1];
+  }
+
+  // print key commands
+  cout<<"[Click] and [drag]  Rotate model using trackball."<<endl;
+  cout<<"[Z,z]               Snap rotation to canonical view."<<endl;
+  cout<<"[⌘ Z]               Undo."<<endl;
+  cout<<"[⇧ ⌘ Z]             Redo."<<endl;
+  cout<<"[^C,ESC]            Exit."<<endl;
+
+  // dirname, basename, extension and filename
+  string d,b,ext,f;
+  pathinfo(filename,d,b,ext,f);
+  // Convert extension to lower case
+  transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+  vector<vector<double > > vV,vN,vTC;
+  vector<vector<int > > vF,vFTC,vFN;
+  if(ext == "obj")
+  {
+    // Convert extension to lower case
+    if(!igl::readOBJ(filename,vV,vTC,vN,vF,vFTC,vFN))
+    {
+      return 1;
+    }
+  }else if(ext == "off")
+  {
+    // Convert extension to lower case
+    if(!igl::readOFF(filename,vV,vF,vN))
+    {
+      return 1;
+    }
+  }else if(ext == "wrl")
+  {
+    // Convert extension to lower case
+    if(!igl::readWRL(filename,vV,vF))
+    {
+      return 1;
+    }
+  //}else
+  //{
+  //  // Convert extension to lower case
+  //  MatrixXi T;
+  //  if(!igl::readMESH(filename,V,T,F))
+  //  {
+  //    return 1;
+  //  }
+  //  //if(F.size() > T.size() || F.size() == 0)
+  //  {
+  //    boundary_faces(T,F);
+  //  }
+  }
+  if(vV.size() > 0)
+  {
+    if(!list_to_matrix(vV,V))
+    {
+      return 1;
+    }
+    triangulate(vF,F);
+  }
+
+  init_patches();
+
+  init_relative();
+
+  // Init glut
+  glutInit(&argc,argv);
+  if( !TwInit(TW_OPENGL, NULL) )
+  {
+    // A fatal error occured
+    fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError());
+    return 1;
+  }
+  // Create a tweak bar
+  rebar.TwNewBar("TweakBar");
+  rebar.TwAddVarCB("camera_rotation", TW_TYPE_QUAT4D, set_camera_rotation,get_camera_rotation, NULL, "open");
+  TwEnumVal RotationTypesEV[NUM_ROTATION_TYPES] = 
+  {
+    {ROTATION_TYPE_IGL_TRACKBALL,"igl trackball"},
+    {ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP,"two axis fixed up"}
+  };
+  TwType RotationTypeTW = 
+    ReTwDefineEnum(
+        "RotationType", 
+        RotationTypesEV, 
+        NUM_ROTATION_TYPES);
+  rebar.TwAddVarCB( "rotation_type", RotationTypeTW,
+    set_rotation_type,get_rotation_type,NULL,"keyIncr=] keyDecr=[");
+  rebar.load(REBAR_NAME);
+
+  // Init antweakbar
+  glutInitDisplayString( "rgba depth double samples>=8 ");
+  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0);
+  glutCreateWindow("patches");
+  glutDisplayFunc(display);
+  glutReshapeFunc(reshape);
+  glutKeyboardFunc(key);
+  glutMouseFunc(mouse);
+  glutMotionFunc(mouse_drag);
+  glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT);
+  glutMainLoop();
+
+  return 0;
+}

+ 67 - 10
examples/scene-rotation/example.cpp

@@ -23,6 +23,7 @@
 #include <igl/REDRUM.h>
 #include <igl/Camera.h>
 #include <igl/ReAntTweakBar.h>
+#include <igl/get_seconds.h>
 
 #include <Eigen/Core>
 #include <Eigen/Geometry>
@@ -47,7 +48,7 @@ struct State
 } s;
 
 // See README for descriptions
-enum ROTATION_TYPE
+enum RotationType
 {
   ROTATION_TYPE_IGL_TRACKBALL = 0,
   ROTATION_TYPE_BELL_TRACKBALL = 1,
@@ -63,17 +64,63 @@ bool is_rotating = false;
 int down_x,down_y;
 igl::Camera down_camera;
 
+bool is_animating = false;
+double animation_start_time = 0;
+double ANIMATION_DURATION = 0.5;
+Eigen::Quaterniond animation_from_quat;
+Eigen::Quaterniond animation_to_quat;
+
 int width,height;
 Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
 
 #define REBAR_NAME "temp.rbr"
 igl::ReTwBar rebar;
 
+void push_undo()
+{
+  undo_stack.push(s);
+  // Clear
+  redo_stack = std::stack<State>();
+}
+
 // No-op setter, does nothing
 void TW_CALL no_op(const void * /*value*/, void * /*clientData*/)
 {
 }
 
+void TW_CALL set_rotation_type(const void * value, void * clientData)
+{
+  using namespace Eigen;
+  using namespace std;
+  using namespace igl;
+  const RotationType old_rotation_type = rotation_type;
+  rotation_type = *(const RotationType *)(value);
+  if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP && 
+    old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP)
+  {
+    push_undo();
+    copy(s.camera.rotation,s.camera.rotation+4,animation_from_quat.coeffs().data());
+    const Vector3d up = animation_from_quat.matrix() * Vector3d(0,1,0);
+    Vector3d proj_up(0,up(1),up(2));
+    if(proj_up.norm() == 0)
+    {
+      proj_up = Vector3d(0,1,0);
+    }
+    proj_up.normalize();
+    Quaterniond dq;
+    dq = Quaterniond::FromTwoVectors(up,proj_up);
+    animation_to_quat = dq * animation_from_quat;
+    // start animation
+    animation_start_time = get_seconds();
+    is_animating = true;
+  }
+}
+void TW_CALL get_rotation_type(void * value, void *clientData)
+{
+  RotationType * rt = (RotationType *)(value);
+  *rt = rotation_type;
+}
+
 void TW_CALL get_camera_rotation(void * value, void *clientData)
 {
   using namespace std;
@@ -82,13 +129,6 @@ void TW_CALL get_camera_rotation(void * value, void *clientData)
   std::copy(s.camera.rotation,s.camera.rotation+4,quat);
 }
 
-void push_undo()
-{
-  undo_stack.push(s);
-  // Clear
-  redo_stack = std::stack<State>();
-}
-
 void reshape(int width, int height)
 {
   ::width = width;
@@ -195,9 +235,25 @@ void display()
 {
   using namespace igl;
   using namespace std;
+  using namespace Eigen;
   glClearColor(1,1,1,0);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
+  if(is_animating)
+  {
+    double t = (get_seconds() - animation_start_time)/ANIMATION_DURATION;
+    if(t > 1)
+    {
+      t = 1;
+      is_animating = false;
+    }
+    Quaterniond q;
+    q.coeffs() = 
+      animation_to_quat.coeffs()*t + animation_from_quat.coeffs()*(1.-t);
+    q.normalize();
+    copy(q.coeffs().data(),q.coeffs().data()+4,s.camera.rotation);
+  }
+
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   lights();
@@ -624,12 +680,13 @@ int main(int argc, char * argv[])
         "RotationType", 
         RotationTypesEV, 
         NUM_ROTATION_TYPES);
-  rebar.TwAddVarRW( "rotation_type", RotationTypeTW, &rotation_type,"keyIncr=] keyDecr=[");
+  rebar.TwAddVarCB( "rotation_type", RotationTypeTW,
+    set_rotation_type,get_rotation_type,NULL,"keyIncr=] keyDecr=[");
   rebar.load(REBAR_NAME);
 
   // Init antweakbar
   glutInitDisplayString( "rgba depth double samples>=8 ");
-  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
+  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0);
   glutCreateWindow("upright");
   glutDisplayFunc(display);
   glutReshapeFunc(reshape);

+ 1 - 0
include/igl/adjacency_matrix.cpp

@@ -42,4 +42,5 @@ IGL_INLINE void igl::adjacency_matrix(
 
 #ifndef IGL_HEADER_ONLY
 // Explicit template specialization
+template void igl::adjacency_matrix<int>(Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::SparseMatrix<int, 0, int>&);
 #endif

+ 43 - 0
include/igl/boost/Makefile

@@ -0,0 +1,43 @@
+include ../../../Makefile.conf
+
+.PHONY: all
+all: libiglboost
+debug: libiglboost
+
+include ../../../Makefile.conf
+all: CFLAGS += -O3 -DNDEBUG
+debug: CFLAGS += -g -Wall 
+
+.PHONY: libiglboost
+libiglboost: obj ../../../lib/libiglboost.a
+
+CPP_FILES=$(wildcard *.cpp)
+OBJ_FILES=$(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o)))
+
+# include igl headers
+INC+=-I../../../include/
+
+# EXPECTS THAT CFLAGS IS ALREADY SET APPROPRIATELY 
+
+# Eigen dependency
+EIGEN3_INC=-I$(DEFAULT_PREFIX)/include/eigen3 -I$(DEFAULT_PREFIX)/include/eigen3/unsupported
+INC+=$(EIGEN3_INC)
+
+# boost dependency
+BOOST=/opt/local/
+BOOST_INC=-I$(BOOST)/include
+INC+=$(BOOST_INC)
+
+obj: 
+	mkdir -p obj
+
+../../../lib/libiglboost.a: $(OBJ_FILES)
+	rm -f $@
+	ar cqs $@ $(OBJ_FILES)
+
+obj/%.o: %.cpp %.h
+	g++ $(AFLAGS) $(OPENMP) $(CFLAGS) -c -o $@ $< $(INC)
+
+clean:
+	rm -f obj/*.o
+	rm -f ../../../lib/libiglboost.a

+ 46 - 0
include/igl/boost/components.cpp

@@ -0,0 +1,46 @@
+#include "components.h"
+#include <igl/adjacency_matrix.h>
+
+#include <boost/graph/adjacency_matrix.hpp>
+#include <boost/graph/connected_components.hpp>
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+template <typename AScalar, typename DerivedC>
+IGL_INLINE void igl::components(
+  const Eigen::SparseMatrix<AScalar> & A,
+  Eigen::PlainObjectBase<DerivedC> & C)
+{
+  assert(A.rows() == A.cols());
+  using namespace Eigen;
+  boost::adjacency_matrix<boost::undirectedS> bA(A.rows());
+  for(int j=0; j<A.outerSize();j++)
+  {
+    // Iterate over inside
+    for(typename SparseMatrix<AScalar>::InnerIterator it (A,j); it; ++it)
+    {
+      if(0 != it.value())
+      {
+        boost::add_edge(it.row(),it.col(),bA);
+      }
+    }
+  }
+  C.resize(A.rows(),1);
+  boost::connected_components(bA,C.data());
+}
+
+template <typename DerivedF, typename DerivedC>
+IGL_INLINE void igl::components(
+  const Eigen::PlainObjectBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedC> & C)
+{
+  Eigen::SparseMatrix<typename DerivedC::Scalar> A;
+  igl::adjacency_matrix(F,A);
+  return components(A,C);
+}
+
+#ifndef IGL_HEADER_ONLY
+// Explicit template specialization
+template void igl::components<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+#endif

+ 36 - 0
include/igl/boost/components.h

@@ -0,0 +1,36 @@
+#ifndef IGL_COMPONENTS_H
+#define IGL_COMPONENTS_H
+#include <igl/igl_inline.h>
+#include <Eigen/Core>
+#include <Eigen/Sparse>
+namespace igl
+{
+  // Compute connected components of a graph represented by an adjacency matrix
+  //
+  // Inputs:
+  //   A  n by n adjacency matrix
+  // Outputs:
+  //   C  n list of component ids
+  //
+  template <typename AScalar, typename DerivedC>
+  IGL_INLINE void components(
+    const Eigen::SparseMatrix<AScalar> & A,
+    Eigen::PlainObjectBase<DerivedC> & C);
+  // Ditto but for mesh faces as input
+  //
+  // Inputs:
+  //   F  n by 3 list of triangle indices
+  // Outputs:
+  //   C  max(F) list of component ids
+  template <typename DerivedF, typename DerivedC>
+  IGL_INLINE void components(
+    const Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedC> & C);
+
+}
+
+#ifdef IGL_HEADER_ONLY
+#  include "components.cpp"
+#endif
+
+#endif

+ 80 - 0
include/igl/boost/manifold_patches.cpp

@@ -0,0 +1,80 @@
+#include "manifold_patches.h"
+#include "components.h"
+#include <igl/sort.h>
+#include <igl/unique.h>
+#include <vector>
+
+template <typename DerivedF, typename DerivedC, typename AScalar>
+void igl::manifold_patches(
+  const Eigen::PlainObjectBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedC> & C,
+  Eigen::SparseMatrix<AScalar> & A)
+{
+  using namespace Eigen;
+  using namespace std;
+
+  // simplex size
+  assert(F.cols() == 3);
+
+  // List of all "half"-edges: 3*#F by 2
+  Matrix<typename DerivedF::Scalar, Dynamic, 2> allE,sortallE,uE;
+  VectorXi IC,_;
+  allE.block(0*F.rows(),0,F.rows(),0) = F.col(1);
+  allE.block(0*F.rows(),0,F.rows(),1) = F.col(2);
+  allE.block(1*F.rows(),0,F.rows(),0) = F.col(2);
+  allE.block(1*F.rows(),0,F.rows(),1) = F.col(0);
+  allE.block(2*F.rows(),0,F.rows(),0) = F.col(0);
+  allE.block(2*F.rows(),0,F.rows(),1) = F.col(1);
+  // Sort each row
+  sort(allE,2,true,sortallE,_);
+  //IC(i) tells us where to find sortallE(i,:) in uE: 
+  // so that sortallE(i,:) = uE(IC(i),:)
+  unique_rows(sortallE,uE,_,IC);
+  // uE2F(e,f) = 1 means face f is adjacent to unique edge e
+  vector<Triplet<AScalar> > uE2Fijv(IC.rows());
+  for(int e = 0;e<IC.rows();e++)
+  {
+    uE2Fijv[e] = Triplet<AScalar>(IC(e),e%F.rows(),1);
+  }
+  SparseMatrix<AScalar> uE2F(uE.rows(),F.rows());
+  uE2F.setFromTriplets(uE2Fijv.begin(),uE2Fijv.end());
+  // kill non-manifold edges
+  for(int j=0; j<uE2F.outerSize();j++)
+  {
+    // Iterate over inside
+    for(typename SparseMatrix<AScalar>::InnerIterator it (uE2F,j); it; ++it)
+    {
+      if(it.value() > 2)
+      {
+        uE2F.coeffRef(it.row(),it.col()) = 0;
+      }
+    }
+  }
+  // Face-face Adjacency matrix
+  SparseMatrix<AScalar> uE2FT;
+  uE2FT = uE2F.transpose().eval();
+  A = uE2FT*uE2F;
+  // All ones
+  for(int j=0; j<A.outerSize();j++)
+  {
+    // Iterate over inside
+    for(typename SparseMatrix<AScalar>::InnerIterator it (A,j); it; ++it)
+    {
+      if(it.value() > 1)
+      {
+        A.coeffRef(it.row(),it.col()) = 1;
+      }
+    }
+  }
+  //% Connected components are patches
+  //%C = components(A); % alternative to graphconncomp from matlab_bgl
+  //[~,C] = graphconncomp(A);
+  // graph connected components using boost
+  components(A,C);
+
+}
+
+#ifndef IGL_HEADER_ONLY
+// Explicit template specialization
+template void igl::manifold_patches<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, int>(Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::SparseMatrix<int, 0, int>&);
+#endif

+ 30 - 0
include/igl/boost/manifold_patches.h

@@ -0,0 +1,30 @@
+#ifndef IGL_MANIFOLD_PATCHES_H
+#define IGL_MANIFOLD_PATCHES_H
+#include <igl/igl_inline.h>
+#include <Eigen/Core>
+#include <Eigen/Sparse>
+namespace igl
+{
+  //  Compute connected components of facets connected by manifold edges.
+  // 
+  //  Known bugs: This will detect a moebius strip as a single patch (manifold,
+  //  non-orientable) and also non-manfiold, yet orientable patches. 
+  // 
+  //  Q: Does this find exactly (manifold || orientable) patches?
+  // 
+  //  Inputs:
+  //    F  #F by simplex-size list of facets
+  //  Outputs:
+  //    C  #F list of component ids
+  //    A  #F by #F adjacency matrix
+  // 
+  template <typename DerivedF, typename DerivedC, typename AScalar>
+  IGL_INLINE void manifold_patches(
+    const Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedC> & C,
+    Eigen::SparseMatrix<AScalar> & A);
+};
+#ifdef IGL_HEADER_ONLY
+#  include "manifold_patches.cpp"
+#endif
+#endif

+ 10 - 10
include/igl/jet.cpp

@@ -63,29 +63,29 @@ void igl::jet(const T x, T & r, T & g, T & b)
   const double gone = 1.0;
   const double bone = 1.0;
 
-  if(x<1/8.)
+  if(x<1./8.)
   {
     r = 0;
     g = 0;
-    b = bone*(0.5+(x)/(1/8.)*0.5);
-  }else if(x<3/8.)
+    b = bone*(0.5+(x)/(1./8.)*0.5);
+  }else if(x<3./8.)
   {
     r = 0;
-    g = gone*(x-1/8.)/(3/8.-1/8.);
+    g = gone*(x-1./8.)/(3./8.-1./8.);
     b = bone; 
-  }else if(x<5/8.)
+  }else if(x<5./8.)
   {
-    r = rone*(x-3/8.)/(5/8.-3/8.);
+    r = rone*(x-3./8.)/(5./8.-3./8.);
     g = gone;
-    b = (bone-(x-3/8.)/(5/8.-3/8.));
-  }else if(x<7/8.)
+    b = (bone-(x-3./8.)/(5./8.-3./8.));
+  }else if(x<7./8.)
   {
     r = rone;
-    g = (gone-(x-5/8.)/(7/8.-5/8.));
+    g = (gone-(x-5./8.)/(7./8.-5./8.));
     b = 0;
   }else
   {
-    r = (bone-(x-7/8.)/(1.-7/8.)*0.5);
+    r = (bone-(x-7./8.)/(1.-7./8.)*0.5);
     g = 0;
     b = 0;
   }

+ 5 - 1
include/igl/lens_flare.h

@@ -13,7 +13,11 @@ namespace igl
     float scale;
     float loc;            /* postion on axis */
     float color[3];
-    Flare(){}
+    Flare():
+      type(-1),
+      scale(0),
+      loc(0)
+    {}
     Flare(int type, float location, float scale, const float color[3], float colorScale) :
       type(type),
       scale(scale),

+ 1 - 0
include/igl/project.cpp

@@ -108,6 +108,7 @@ IGL_INLINE Eigen::PlainObjectBase<Derivedobj> igl::project(
 template int igl::project<Eigen::Matrix<double, 3, 1, 0, 3, 1>, Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> >&);
 template Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > igl::project<Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&);
 template int igl::project<Eigen::Matrix<float, 3, 1, 0, 3, 1>, Eigen::Matrix<float, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> >&);
+template Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> > igl::project<Eigen::Matrix<float, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> > const&);
 #endif
 
 #endif

+ 4 - 4
include/igl/unique.h

@@ -14,8 +14,8 @@ namespace igl
   //   A  #A vector of type T
   // Outputs:
   //   C  #C vector of unique entries in A
-  //   IA  #A index vector so that C = A(IA);
-  //   IC  #C index vector so that A = C(IC);
+  //   IA  #C index vector so that C = A(IA);
+  //   IC  #A index vector so that A = C(IC);
   template <typename T>
   IGL_INLINE void unique(
     const std::vector<T> & A,
@@ -33,8 +33,8 @@ namespace igl
   //   A  m by n matrix whose entries are to unique'd according to rows
   // Outputs:
   //   C  #C vector of unique rows in A
-  //   IA  #A index vector so that C = A(IA,:);
-  //   IC  #C index vector so that A = C(IC,:);
+  //   IA  #C index vector so that C = A(IA,:);
+  //   IC  #A index vector so that A = C(IC,:);
   template <typename DerivedA, typename DerivedIA, typename DerivedIC>
   IGL_INLINE void unique_rows(
     const Eigen::PlainObjectBase<DerivedA>& A,