igl logo

Using the libigl library

The libigl library is a collection of useful/reusable/sharable C++ functions with very few external dependencies. The library may be used as a "headers only" library, a statically linked library, or as a compressed .h/.cpp pair.

This duality between statically compiled and header-only is illustrated in the example examples/example_fun. When built, this example compiles two binaries, one using the example_fun routine of the igl library, either as a headers-only library or linking against the igl static library.

Headers (.h) only library

All classes and functions in the libigl library are written in a way in which the entire library may be compiled just-in-time, effectively behaiving as if it were a "headers only" library (like e.g. Eigen). This is achieved by careful organization of each pair of .h and .cpp files. To take advantage of this one must only include the path to libigl directory in one's project's include path and define the preprocessor macro IGL_HEADER_ONLY.

Defining IGL_HEADER_ONLY may be done at the project level, prescribing that all included igl headers be treated as code that should be inlined. Consequently all templated functions will be derived at compile time if need be.

One may also define IGL_HEADER_ONLY not only on a per-file basis, but a per-include basis. For example it may be useful for a project to use the static library for most functionality from IGL, but then include a certain IGL function as an inlined function. This may be achieved by surrounding the relevant include with a define and undefine of the IGL_HEADER_ONLY macro. Here's an example of the little widget:


...
#include <igl/some_other_igl_function.h>
#ifndef IGL_HEADER_ONLY
#  define IGL_HEADER_ONLY
#  define IGL_HEADER_ONLY_WAS_NOT_DEFINED
#endif
#include <igl/igl_function_to_inline.h>
#ifdef IGL_HEADER_ONLY_WAS_NOT_DEFINED
#  undef IGL_HEADER_ONLY
#endif
#include <igl/yet_another_igl_function.h>
...
      
example examples/XXX also highlights this feature
This practice is not recommended outside of debugging purposes.

Benefits of headers-only library

  • Easy templates: When using the libigl library as a headers-only library no special care need be taken when using templated functions.

Drawbacks of headers-only library

  • Inlining not guaranteed: Most compilers do not guarantee that functions will get inlined even if explicitly told to do so. Though we have not yet encountered this problem, it is always a risk.
  • Long compile, large binary:As a headers-only library we depend on the compiler to properly inline each function call. This means compile time is high and binary size can be quite large.

Statically linked library

Explicit specialization of templated functions

Special care must be taken by the developers of each function and class in the libigl library that uses C++ templates. If this function is intended to be compiled into the statically linked libigl library then function is only compiled for each explicitly specialized declaration. These should be added at the bottom of the corresponding .cpp file surrounded by a #ifndef IGL_HEADER_ONLY.

Of course, a developer may not know ahead of time which specializations should be explicitly included in the igl static lib. One way to find out is to add one explicit specialization for each call in one's own project. This only ever needs to be done once for each template.

The process is somewhat mechanical using a linker with reasonable error output.

Supposed for example we have compiled the igl static lib, including the cat.h and cat.cpp functions, without any explicit instanciation. Say using the makefile in the libigl directory:


cd $LIBIGL
make
        

Now if we try to compile a project and link against it we may get an error like:


Undefined symbols for architecture x86_64:
  "Eigen::Matrix<int, -1, -1, 0, -1, -1> igl::cat<Eigen::Matrix<int, -1, -1, 0, -1, -1> >(int, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&)", referenced from:
      uniform_sample(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, int, double, Eigen::Matrix<double, -1, -1, 0, -1, -1>&)in Skinning.o
  "Eigen::SparseMatrix<double, 0, int> igl::cat<Eigen::SparseMatrix<double, 0, int> >(int, Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int> const&)", referenced from:
      covariance_scatter_matrix(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, ArapEnergy, Eigen::SparseMatrix<double, 0, int>&)in arap_dof.o
      arap_rhs(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, ArapEnergy, Eigen::SparseMatrix<double, 0, int>&)in arap_dof.o
        

This looks like a mess, but luckily we don't really need to read it all. Just copy the first highlighted part in quotes, then append it to the list of explicit templat specializations at the end of cat.cpp after the word template and followed by a semi-colon. Like this:


...
#ifndef IGL_HEADER_ONLY
  // Explicit template specialization
  template Eigen::Matrix<int, -1, -1, 0, -1, -1> igl::cat<Eigen::Matrix<int, -1, -1, 0, -1, -1> >(int, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, Eigen::Matrix<int, -1, -1, 0, -1, -1> const&);
#endif
        

Then you must recompile the IGL static library.


cd $LIBIGL
make
        

And try to compile your project again, potentially repeating this process until no more symbols are undefined.

It may be useful to check that you code compiles with no errors first using the headers-only version to be sure that all errors are from missing template specializations.

If you're using make then the following command will reveal each missing symbol on its own line:


make 2>&1 | grep "referenced from" | sed -e "s/, referenced from.*//"
        

Alternatively you can use the autoexplicit.sh function which (for well organized .h/.cpp pairs in libigl) automatically create explicit instanciations from your compiler's error messages. Repeat this process until convergence:


cd /to/your/project
make 2>$LIBIGL/make.err
cd $LIBIGL
cat make.err | ./autoexplicit.sh
make clean 
make
        

Benefits of static library

  • Faster compile time: Because the libigl library is already compiled, only the new code in ones project must be compiled and then linked to IGL. This means compile times are generally faster.
  • Debug or optimized: The IGL static library may be compiled in debug mode or optimized release mode regardless of whether one's project is being optimized or debugged.

Drawbacks of static library

  • Hard to use templates: Special care (by the developers of the library) needs to be taken when exposing templated functions.

Compressed .h/.cpp pair

Calling the script:

scripts/compress.sh igl.h igl.cpp

will create a single header igl.h and a single cpp file igl.cpp.

Alternatively, you can also compress everything into a single header file (analagous to IGL_HEADER_ONLY):

scripts/compress.sh igl.h

Benefits of compressed .h/.cpp pair

  • Easy incorporation: This can be easily incorporated into external projects.

Drawbacks of compressed .h/.cpp pair

  • Hard to debug/edit: The compressed files are automatically generated. They're huge and should not be edited. Thus debugging and editting are near impossible.
  • Compounded dependencies: An immediate disadvantage of this seems to be that even to use a single function (e.g. cotmatrix), compiling and linking against igl.cpp will require linking to all of libigl's dependencies (OpenGL, GLUT, AntTweakBar, BLAS). However, because all depencies other than Eigen should be encapsulated between #ifndef guards (e.g. #ifndef IGL_NO_OPENGL, it is possible to ignore certain functions that have such dependencies.
  • Long compile: Compiling igl.cpp takes a long time and isn't easily parallelized (no make -j12 equivalent).

Here's a tiny test example using igl.h and igl.cpp. Save the following in test.cpp:


#include <igl.h>
#include <Eigen/Core>

int main(int argc, char * argv[])
{
  Eigen::MatrixXd V;
  Eigen::MatrixXi F;
  return (argc>=2 && igl::read(argv[1],V,F)?0:1);
}
      

Then compile igl.cpp with:


g++ -o igl.o -c igl.cpp -I/opt/local/include/eigen3 -DIGL_NO_OPENGL -DIGL_NO_ANTTWEAKBAR
      

Notice that we're using -DIGL_NO_OPENGL -DIGL_NO_ANTTWEAKBAR to disable any libigl dependencies on OpenGL and AntTweakBar.

Now compile test.cpp with:


g++ -g -I/opt/local/include/eigen3/ -I/usr/local/igl/libigl/ -L/usr/local/igl/libigl/ -ligl -DIGL_NO_OPENGL -DIGL_NO_ANTTWEAKBAR -o test
      

Try running it with:


./test path/to/mesh.obj
      

Dependencies

By design the libigl library has very few external dependencies.

Mandatory dependencies

Eigen3 and the Standard Template Library (STL) are the only truly mandatory dependencies. Without them libigl will not compile or work properly.

OpenGL is an assumed dependency, but is in fact also optional. All OpenGL-dependent functions may be disabled by defining the IGL_NO_OPENGL preprocessor macro.

Likewise, AntTweakBar is an assumed dependency, similarly diabled by defining the IGL_NO_ANTTWEAKBAR preprocessor macro.

Each IGL_NO_XXX should just be replaced by a libiglXXX.a extra

Optional dependencies

Certain extra functions and classes included the libigl library have external dependencies by construction (e.g. the matlab_interface routines are only useful when matlab is present anyway). These are never compiled by default into the static igl library, rather they are compiled as "extras" (e.g. libiglmatlab.a contains the MATLAB-dependent functions).

Currently these include:

ExtraDependency
libiglmatlab.aMATLAB
libiglmosek.aMosek
libigltetgen.aTetGen
libiglxml.aTinyXml2
libiglpng.aLibPNG
libiglembree.aEmbree

Some of our examples (libigl/examples) also depend on GLUT.

Converting matlab code to C++ using IGL and Eigen

Eigen's matrix API often makes it fairly to translate to and from matlab code (Eigen provides a translation table). We have implemented a few additional matlab-esque functions to make the translation even easier. Our own translation table shows a list of common matlab functions and their igl-eigen equivalents.

Including OpenGL

Just include the convenience header, which takes care of system dependent paths, glew, etc.:


#include "OpenGL_convenience.h"
      

A standard include for the OpenGL headers should be placed in the .cpp file if possible. To ensure compilability on Mac OS X, Windows and Linux, use:


#if __APPLE__
#  include <OpenGL/gl.h>
#elif defined(_WIN32)
#    define NOMINMAX
#    include <Windows.h>
#    undef NOMINMAX
#    include <GL/glew.h>
#    include <GL/gl.h>
#else
#  define GL_GLEXT_PROTOTYPES
#  include <GL/gl.h>
#  include <GL/glext.h>
#endif
      

Including headers to other igl functions

Source files in the main igl directory should include other libigl headers using the name of the file in quotation marks:


#include "other_function.h"
      

Source files in your project and in libigl extras should include libigl headers using the igl directory and name file in angle brackets:


#include <igl/some_function.h>
      

libigl headers of extras can then be included using:


#include <igl/extra/some_extra_function.h>
      

See also: style guidlines, auto-documentation, file formats