libigl is a simple C++ geometry processing library. We have a wide
functionality including construction of sparse discrete differential geometry
@@ -39,7 +36,7 @@ like MATLAB.
just include igl headers (e.g. #include <igl/cotmatrix.h>) and run. Each
header file contains a single function (e.g. igl/cotmatrix.h contains
igl::cotmatrix()). Most are tailored to operate on a generic triangle mesh
-stored in an n-by–3 matrix of vertex positions V and an m-by–3 matrix of
+stored in an n-by-3 matrix of vertex positions V and an m-by-3 matrix of
triangle indices F.
Optionally the library may also be pre-compiled into a statically
@@ -121,14 +118,14 @@ libigl depends only on the Eigen librar
Libigl compartmentalizes its optional dependences via its directory
organization in the include/ folder. All header files located directly in
the include/igl/ folder have only stl and Eigen as dependencies. For example,
-all of the headers that depend on CGAL are located in include/igl/cgal. For a
-full list of optional dependencies check optional/CMakeLists.txt.
+all of the headers that depend on CGAL are located in include/igl/copyleft/cgal.
+For a full list of optional dependencies check optional/CMakeLists.txt.
GCC and the Optional CGAL Dependency
-
The include/igl/cgal/*.h headers depend on CGAL. It has come to our attention
-that CGAL does not work properly with GCC 4.8. To the best of our knowledge,
-GCC 4.7 and clang will work correctly.
+
The include/igl/copyleft/cgal/*.h headers depend on CGAL. It has come to
+our attention that CGAL does not work properly with GCC 4.8. To the best of
+our knowledge, GCC 4.7 and clang will work correctly.
OpenMP and Windows
@@ -210,7 +207,7 @@ BibTeX entry:
title = {{libigl}: A simple {C++} geometry processing library},
author = {Alec Jacobson and Daniele Panozzo and others},
note = {http://libigl.github.io/libigl/},
- year = {2016},
+ year = {2017},
}
@@ -221,56 +218,59 @@ Eurographics/ACM Symposium on Geometry Processing software award. Here are a
few labs/companies/institutions using libigl:
If you’re using libigl in your projects, quickly drop us a
-note. Tell us who you
+
If you’re using libigl in your projects, quickly drop us a
+note. Tell us who you
are and what you’re using it for. This helps us apply for funding and justify
spending time maintaining this.
@@ -279,11 +279,10 @@ page.
Copyright
-
2017 Alec Jacobson, Daniele Panozzo, Christian Schüller, Olga Diamanti, Qingnan
-Zhou, Sebastian Koch, Amir Vaxman, Nico Pietroni, Stefan Brugger, Kenshi Takayama, Wenzel Jakob, Nikolas De
-Giorgis, Luigi Rocca, Leonardo Sacht, Kevin Walliman, Olga Sorkine-Hornung, and others.
+
2017 Alec Jacobson, Daniele Panozzo, Christian Schüller, Olga Diamanti, Qingnan Zhou, Sebastian Koch, Jeremie Dumas, Amir Vaxman, Nico Pietroni, Stefan Brugger, Kenshi Takayama, Wenzel Jakob, Nikolas De Giorgis, Luigi Rocca, Leonardo Sacht, Kevin Walliman, Olga Sorkine-Hornung, and others.
Please see individual files for appropriate copyright notices.
Libigl is developed most often on Mac OS X, though has current users in Linux
and Windows.
-
Linux/Mac OS X/Cygwin
+
Linux/Mac OS X/Cygwin
Libigl may also be compiled to a static library. This is advantageous when
building a project with libigl, since when used as an header-only library can
@@ -40,7 +37,7 @@ cmake -DCMAKE_BUILD_TYPE=Release ../optional
make
-
Warnings
+
Warnings
You should expect to see a few linker warnings of the form:
@@ -50,7 +47,7 @@ make
These are (admittedly unpopular) functions that have never been used by us
statically so we haven’t explicit instantiations (yet).
-
External
+
External
Finally there are a number of external libraries that we include in
./external/ because they are either difficult to obtain or they have been
@@ -58,7 +55,7 @@ patched for easier use with libigl. Please see the respective readmes in those
directories or build the tutorial using cmake, which will recursively build all
dependencies.
-
Installing Embree 2.0
+
Installing Embree 2.0
To build the embree library and executables on Mac OS X issue:
@@ -74,72 +71,72 @@ make
#sudo make install
-
Extras
+
Extras
-
bbw
+
bbw
This library extra contains functions for computing Bounded Biharmonic Weights, can
be used with and without the mosek extra via the IGL_NO_MOSEK
macro.
-
boolean
+
boolean
This library extra contains functions for computing mesh-mesh booleans,
depending on CGAL and optionally Cork.
-
cgal
+
cgal
This library extra utilizes CGAL’s efficient and exact intersection and
proximity queries.
-
embree
+
embree
This library extra utilizes embree’s efficient ray tracing queries.
-
matlab
+
matlab
This library extra provides support for reading and writing .mat workspace
files, interfacing with Matlab at run time and compiling mex functions.
-
mosek
+
mosek
This library extra utilizes mosek’s efficient interior-point solver for
quadratic programs.
-
png
+
png
This library extra uses libpng and YImage to read and write .png files.
-
tetgen
+
tetgen
This library extra provides a simplified wrapper to the tetgen 3d tetrahedral
meshing library.
-
Triangle
+
Triangle
This library extra provides a simplified wrapper to the triangle 2d triangle
meshing library.
-
viewer
+
viewer
This library extra utilizes glfw and glew to open an opengl context and launch
a simple mesh viewer.
-
xml
+
xml
This library extra utilizes tinyxml2 to read and write serialized classes
containing Eigen matrices and other standard simple data-structures.
Easy incorporation: This can be easily incorporated
- into external projects.
+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 editing are near impossible.
+automatically generated. They’re huge and should not be edited. Thus
+debugging and editing 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
- dependencies 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.
+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
+dependencies 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).
+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:
@@ -373,3 +370,4 @@ return (argc>=2 && igl::read_triangle_mesh(argv[1],V,F)?0:1);
+
diff --git a/python/iglhelpers.py b/python/iglhelpers.py
index bf8d9c1b4..0695bb533 100644
--- a/python/iglhelpers.py
+++ b/python/iglhelpers.py
@@ -11,15 +11,15 @@ import pyigl as igl
def p2e(m):
if isinstance(m, np.ndarray):
- if not m.flags['C_CONTIGUOUS']:
- raise TypeError("p2e only support C-contiguous order")
- if m.dtype.type == np.int32:
- return igl.eigen.MatrixXi(m)
- elif m.dtype.type == np.float64:
- return igl.eigen.MatrixXd(m)
+ if not (m.flags['C_CONTIGUOUS'] or m.flags['F_CONTIGUOUS']):
+ raise TypeError('p2e support either c-order or f-order')
+ if m.dtype.type in [np.int32, np.int64]:
+ return igl.eigen.MatrixXi(m.astype(np.int32))
+ elif m.dtype.type in [np.float64, np.float32]:
+ return igl.eigen.MatrixXd(m.astype(np.float64))
elif m.dtype.type == np.bool:
return igl.eigen.MatrixXb(m)
- raise TypeError("p2e only support dtype float64, int32 and bool")
+ raise TypeError("p2e only support dtype float64/32, int64/32 and bool")
if sparse.issparse(m):
# convert in a dense matrix with triples
coo = m.tocoo()
diff --git a/python/py_doc.cpp b/python/py_doc.cpp
index c5f255baf..7174e51e1 100644
--- a/python/py_doc.cpp
+++ b/python/py_doc.cpp
@@ -598,6 +598,23 @@ const char *__doc_igl_embree_reorient_facets_raycast = R"igl_Qu8mg5v7(// Orient
// Outputs:
// I #F list of whether face has been flipped
// C #F list of patch ID (output of bfs_orient > manifold patches))igl_Qu8mg5v7";
+const char *__doc_igl_exact_geodesic = R"igl_Qu8mg5v7(
+ // Exact geodesic algorithm for triangular mesh with the implementation from https://code.google.com/archive/p/geodesic/,
+ // and the algorithm first described by Mitchell, Mount and Papadimitriou in 1987
+ //
+ // Inputs:
+ // V #V by 3 list of 3D vertex positions
+ // F #F by 3 list of mesh faces
+ // VS #VS by 1 vector specifying indices of source vertices
+ // FS #FS by 1 vector specifying indices of source faces
+ // VT #VT by 1 vector specifying indices of target vertices
+ // FT #FT by 1 vector specifying indices of target faces
+ // Output:
+ // D #VT+#FT by 1 vector of geodesic distances of each target w.r.t. the nearest one in the source set
+ //
+ // Note:
+ // Specifying a face as target/source means its center.
+ //)igl_Qu8mg5v7";
const char *__doc_igl_find_cross_field_singularities = R"igl_Qu8mg5v7(// Inputs:
// V #V by 3 eigen Matrix of mesh vertex 3D positions
// F #F by 3 eigen Matrix of face (quad) indices
@@ -1453,3 +1470,33 @@ const char *__doc_igl_readPLY= R"igl_Qu8mg5v7(// Read a mesh from an ascii ply f
// N double matrix of corner normals #N by 3
// UV #V by 2 texture coordinates
// Returns true on success, false on errors)igl_Qu8mg5v7";
+const char *__doc_igl_seam_edges=R"igl_Qu8mg5v7(// Finds all UV-space boundaries of a mesh.
+ //
+ // Inputs:
+ // V #V by dim list of positions of the input mesh.
+ // TC #TC by 2 list of 2D texture coordinates of the input mesh
+ // F #F by 3 list of triange indices into V representing a
+ // manifold-with-boundary triangle mesh
+ // FTC #F by 3 list of indices into TC for each corner
+ // Outputs:
+ // seams Edges where the forwards and backwards directions have different
+ // texture coordinates, as a #seams-by-4 matrix of indices. Each row is
+ // organized as [ forward_face_index, forward_face_vertex_index,
+ // backwards_face_index, backwards_face_vertex_index ] such that one side
+ // of the seam is the edge:
+ // F[ seams( i, 0 ), seams( i, 1 ) ], F[ seams( i, 0 ), (seams( i, 1 ) + 1) % 3 ]
+ // and the other side is the edge:
+ // F[ seams( i, 2 ), seams( i, 3 ) ], F[ seams( i, 2 ), (seams( i, 3 ) + 1) % 3 ]
+ // boundaries Edges with only one incident triangle, as a #boundaries-by-2
+ // matrix of indices. Each row is organized as
+ // [ face_index, face_vertex_index ]
+ // such that the edge is:
+ // F[ boundaries( i, 0 ), boundaries( i, 1 ) ], F[ boundaries( i, 0 ), (boundaries( i, 1 ) + 1) % 3 ]
+ // foldovers Edges where the two incident triangles fold over each other
+ // in UV-space, as a #foldovers-by-4 matrix of indices.
+ // Each row is organized as [ forward_face_index, forward_face_vertex_index,
+ // backwards_face_index, backwards_face_vertex_index ]
+ // such that one side of the foldover is the edge:
+ // F[ foldovers( i, 0 ), foldovers( i, 1 ) ], F[ foldovers( i, 0 ), (foldovers( i, 1 ) + 1) % 3 ]
+ // and the other side is the edge:
+ // F[ foldovers( i, 2 ), foldovers( i, 3 ) ], F[ foldovers( i, 2 ), (foldovers( i, 3 ) + 1) % 3 ])igl_Qu8mg5v7";
diff --git a/python/py_doc.h b/python/py_doc.h
index 3f7678dc0..2e76267f0 100644
--- a/python/py_doc.h
+++ b/python/py_doc.h
@@ -48,6 +48,7 @@ extern const char *__doc_igl_eigs;
extern const char *__doc_igl_embree_ambient_occlusion;
extern const char *__doc_igl_embree_line_mesh_intersection;
extern const char *__doc_igl_embree_reorient_facets_raycast;
+extern const char *__doc_igl_exact_geodesic;
extern const char *__doc_igl_find_cross_field_singularities;
extern const char *__doc_igl_fit_rotations;
extern const char *__doc_igl_fit_rotations_planar;
@@ -99,6 +100,7 @@ extern const char *__doc_igl_readTGF;
extern const char *__doc_igl_read_triangle_mesh;
extern const char *__doc_igl_remove_duplicate_vertices;
extern const char *__doc_igl_rotate_vectors;
+extern const char *__doc_igl_seam_edges;
extern const char *__doc_igl_setdiff;
extern const char *__doc_igl_signed_distance;
extern const char *__doc_igl_signed_distance_pseudonormal;
@@ -125,4 +127,4 @@ extern const char *__doc_igl_winding_number_2;
extern const char *__doc_igl_writeMESH;
extern const char *__doc_igl_writeOBJ;
extern const char *__doc_igl_writePLY;
-extern const char *__doc_igl_readPLY;
+extern const char *__doc_igl_readPLY;
\ No newline at end of file
diff --git a/python/py_igl.cpp b/python/py_igl.cpp
index 5eaf9a977..5ec827282 100644
--- a/python/py_igl.cpp
+++ b/python/py_igl.cpp
@@ -44,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -102,6 +103,7 @@
#include
#include
#include
+#include
void python_export_igl(py::module &m)
{
@@ -141,6 +143,7 @@ void python_export_igl(py::module &m)
#include "py_igl/py_edge_lengths.cpp"
#include "py_igl/py_edge_topology.cpp"
#include "py_igl/py_eigs.cpp"
+#include "py_igl/py_exact_geodesic.cpp"
#include "py_igl/py_find_cross_field_singularities.cpp"
#include "py_igl/py_fit_rotations.cpp"
#include "py_igl/py_floor.cpp"
@@ -199,4 +202,5 @@ void python_export_igl(py::module &m)
#include "py_igl/py_writeOBJ.cpp"
#include "py_igl/py_writePLY.cpp"
#include "py_igl/py_readPLY.cpp"
+#include "py_igl/py_seam_edges.cpp"
}
diff --git a/python/py_igl/py_exact_geodesic.cpp b/python/py_igl/py_exact_geodesic.cpp
new file mode 100644
index 000000000..b05ac744b
--- /dev/null
+++ b/python/py_igl/py_exact_geodesic.cpp
@@ -0,0 +1,24 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2018 Zhongshi Jiang
+//
+// 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/.
+
+
+m.def("exact_geodesic", []
+(
+ const Eigen::MatrixXd &V,
+ const Eigen::MatrixXi &F,
+ const Eigen::MatrixXi &VS,
+ const Eigen::MatrixXi &FS,
+ const Eigen::MatrixXi &VT,
+ const Eigen::MatrixXi &FT,
+ Eigen::MatrixXd &D
+)
+{
+ return igl::exact_geodesic(V, F, VS,FS,VT,FT, D);
+}, __doc_igl_exact_geodesic,
+py::arg("V"), py::arg("F"), py::arg("VS"), py::arg("FS"), py::arg("VT"), py::arg("FT"), py::arg("D"));
+
diff --git a/python/py_igl/py_seam_edges.cpp b/python/py_igl/py_seam_edges.cpp
new file mode 100755
index 000000000..c3464012b
--- /dev/null
+++ b/python/py_igl/py_seam_edges.cpp
@@ -0,0 +1,21 @@
+m.def("seam_edges", []
+(
+ const Eigen::MatrixXd& V,
+ const Eigen::MatrixXd& TC,
+ const Eigen::MatrixXi& F,
+ const Eigen::MatrixXi& FTC,
+ Eigen::MatrixXi& seams,
+ Eigen::MatrixXi& boundaries,
+ Eigen::MatrixXi& foldovers
+)
+{
+return igl::seam_edges( V, TC, F, FTC, seams, boundaries, foldovers);
+}, __doc_igl_seam_edges,
+py::arg("V"),
+py::arg("TC"),
+py::arg("F"),
+py::arg("FTC"),
+py::arg("seams"),
+py::arg("boundaries"),
+py::arg("foldovers"));
+
diff --git a/style-guidelines.html b/style-guidelines.html
index 316e7588f..e81d7af75 100644
--- a/style-guidelines.html
+++ b/style-guidelines.html
@@ -1,14 +1,11 @@
-
+
libigl
-
-
-
-
+
@@ -18,7 +15,7 @@
style guidelines for developers of the library, but also acts as
best-practices for users.
-
One function, one .h/.cpp pair
+
One function, one .h/.cpp pair
The structure of libigl is very flat and function-based. For every
function/sub-routine, create a single .h and .cpp file. For example, if you have
@@ -61,7 +58,7 @@ namespace igl
}
#ifndef IGL_STATIC_LIBRARY
-# include "example_fun.cpp"
+#include "example_fun.cpp"
#endif
#endif
@@ -185,21 +182,21 @@ Eigen::SparseMatrix<Atype> adjacency_matrix(const ... & F);
Templating with Eigen
Functions taking Eigen dense matrices/arrays as inputs and outputs (but not
-return arguments), should template on top of Eigen::PlainObjectBase. Each
+return arguments), should template on top of Eigen::MatrixBase. Each
parameter should be derived using its own template.
The Derived* template encodes the scalar type (e.g. double, int), the
number of rows and cols at compile time, and the data storage (Row-major vs.
-column-major).
+column-major).
Returning Eigen types is discouraged. In cases where the size and scalar type
are a fixed and matching function of an input Derived* template, then
@@ -219,7 +216,7 @@ output-argument version and call that. So a full implementation looks like:
Classes should be avoided. When naming a class use CamelCase (e.g.
SortableRow.h).
-
Enum naming conversion
+
Enum naming conversion
Enums types should be placed in the appropriate igl:: namespace and should be
named in CamelCase (e.g. igl::SolverStatus) and instances should be named in
@@ -392,3 +389,4 @@ implementation so we’re keeping it as long as possible and profitable.
+
diff --git a/tutorial/206_GeodesicDistance/CMakeLists.txt b/tutorial/206_GeodesicDistance/CMakeLists.txt
new file mode 100644
index 000000000..97a9d6786
--- /dev/null
+++ b/tutorial/206_GeodesicDistance/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(206_GeodesicDistance)
+
+add_executable(${PROJECT_NAME}_bin main.cpp)
+target_link_libraries(${PROJECT_NAME}_bin igl::core igl::opengl igl::opengl_glfw tutorials)
diff --git a/tutorial/206_GeodesicDistance/main.cpp b/tutorial/206_GeodesicDistance/main.cpp
new file mode 100755
index 000000000..ead43f0f5
--- /dev/null
+++ b/tutorial/206_GeodesicDistance/main.cpp
@@ -0,0 +1,73 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "tutorial_shared_path.h"
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+void plotMeshDistance(igl::opengl::glfw::Viewer& viewer, const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, const Eigen::VectorXd& d, const double strip_size )
+{
+ // Rescale the function depending on the strip size
+ Eigen::VectorXd f = (d/strip_size);
+
+ // The function should be 1 on each integer coordinate
+ f = (f*M_PI).array().sin().abs();
+
+ // Compute per-vertex colors
+ Eigen::MatrixXd C;
+ igl::colormap(igl::COLOR_MAP_TYPE_INFERNO,f,false,C);
+
+ // Plot the mesh
+ viewer.data().set_mesh(V, F);
+ viewer.data().set_colors(C);
+}
+
+int main(int argc, char *argv[])
+{
+ using namespace Eigen;
+ using namespace std;
+
+ // Load a mesh in OFF format
+ igl::readOBJ(TUTORIAL_SHARED_PATH "/armadillo.obj", V, F);
+
+
+ igl::opengl::glfw::Viewer viewer;
+ // 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.model,
+ viewer.core.proj, viewer.core.viewport, V, F, fid, bc))
+ {
+ int max;
+ bc.maxCoeff(&max);
+ int vid = F(fid,max);
+ 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;
+ igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+
+ plotMeshDistance(viewer,V,F,d,0.05);
+ }
+ return false;
+ };
+ viewer.data().set_mesh(V,F);
+
+ cout << "Press [space] to smooth." << endl;;
+ cout << "Press [r] to reset." << endl;;
+ return viewer.launch();
+}
diff --git a/tutorial/CMakeLists.txt b/tutorial/CMakeLists.txt
index 3ca47bb50..9c993a873 100644
--- a/tutorial/CMakeLists.txt
+++ b/tutorial/CMakeLists.txt
@@ -64,6 +64,7 @@ if(TUTORIALS_CHAPTER2)
add_subdirectory("203_CurvatureDirections")
add_subdirectory("204_Gradient")
add_subdirectory("205_Laplacian")
+ add_subdirectory("206_GeodesicDistance")
endif()
# Chapter 3
diff --git a/tutorial/images/geodesicdistance.jpg b/tutorial/images/geodesicdistance.jpg
new file mode 100644
index 000000000..eefc4b9c2
Binary files /dev/null and b/tutorial/images/geodesicdistance.jpg differ
diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html
index 1dcb725e3..8f9d830ca 100644
--- a/tutorial/tutorial.html
+++ b/tutorial/tutorial.html
@@ -1,15 +1,12 @@
-
+
libigl Tutorial
-
-
-
-
+
@@ -19,6 +16,7 @@
+
Libigl is an open source C++ library for geometry processing research and
@@ -48,7 +46,7 @@ lecture notes links to a cross-platform example application.
We introduce libigl with a series of self-contained examples. The purpose of
each example is to showcase a feature of libigl while applying to a practical
@@ -166,24 +165,24 @@ concepts of libigl and introduce a simple mesh viewer that allows to
visualize a surface mesh and its attributes. All the tutorial examples are
cross-platform and can be compiled on MacOSX, Linux and Windows.
Before getting into the examples, we summarize the main design principles in
libigl:
No complex data types. We mostly use matrices and vectors. This greatly
- favors code reusability and forces the function authors to expose all the
- parameters used by the algorithm.
+favors code reusability and forces the function authors to expose all the
+parameters used by the algorithm.
Minimal dependencies. We use external libraries only when necessary and
- we wrap them in a small set of functions.
+we wrap them in a small set of functions.
Header-only. It is straight forward to use our library since it is only
- one additional include directory in your project. (if you are worried about
- compilation speed, it is also possible to build the library as a static
- library)
+one additional include directory in your project. (if you are worried about
+compilation speed, it is also possible to build the library as a static
+library)
Function encapsulation. Every function (including its full
- implementation) is contained in a pair of .h/.cpp files with the same name of
- the function.
+implementation) is contained in a pair of .h/.cpp files with the same name of
+the function.
Downloading libigl
@@ -249,7 +248,7 @@ in 32 bit on windows.
We provide a blank project example showing how to use libigl and cmake. Feel free and encouraged to copy or fork this project as a way of starting a new personal project using libigl.
libigl uses the Eigen library to encode vector
and matrices. We suggest that you keep the
@@ -303,7 +302,7 @@ Similarly, a mesh can be written in an OBJ file using:
Example 101 contains a simple mesh
converter from OFF to OBJ format.
Colors and normals can be associated to faces or vertices using the
set_colors function:
@@ -427,7 +426,7 @@ igl::jet(Z,true,C);
vertex) and the second calls a libigl functions that converts a scalar field to colors. The second parameter of jet normalizes the scalar field to lie between 0 and 1 before applying the transfer function.
-(Example 104) igl::jet converts a scalar field to a
color field.
@@ -437,7 +436,7 @@ color field.
types and can be easily reused for many different tasks. Not committing to
heavy data structures types favors simplicity, ease of use and reusability.
-
In addition to plotting the surface, the viewer supports the visualization of points, lines and text labels: these overlays can be very helpful while developing geometric processing algorithms to plot debug information.
@@ -467,13 +466,13 @@ Eigen::Vector3d M = V.colwise().maxCoeff();
-(Example 105) The bounding box of a mesh is shown
using overlays.
-
As of version 1.2 the viewer uses a new menu and completely replaces
AntTweakBar. It is based on the
@@ -528,22 +527,24 @@ viewer.ngui->addVariable<bool>("bool",[&](bool val) {
-
-(Example 106) The UI of the viewer can be easily customized.
+
+(Example 106) The UI of the viewer can be easily
+customized.
-
Libigl’s igl::opengl::glfw::Viewer provides basic support for rendering
multiple meshes.
-
Which mesh is selected is controled via the viewer.selected_data_index
+
Which mesh is selected is controlled via the viewer.selected_data_index
field. By default it his is set to 0, so in the typical case of a single mesh
viewer.data() returns the igl::ViewerData corresponding to the one
and only mesh.
-(Example 107) The igl::opengl::glfw::Viewer
@@ -555,12 +556,15 @@ colors.
This chapter illustrates a few discrete quantities that libigl can compute on a
mesh and the libigl functions that construct popular discrete differential
-geometry operators. It also provides an introduction to basic drawing and coloring routines of our viewer.
+geometry operators. It also provides an introduction to basic drawing and
+coloring routines of our viewer.
Normals
Surface normals are a basic quantity necessary for rendering a surface. There
-are a variety of ways to compute and store normals on a triangle mesh. Example 201 demonstrates how to compute and visualize normals with libigl.
+are a variety of ways to compute and store normals on a triangle mesh. Example
+201 demonstrates how to compute and visualize normals
+with libigl.
Per-face
@@ -613,7 +617,7 @@ implements a simple scheme which computes corner normals as averages of
normals of faces incident on the corresponding vertex which do not deviate by more than a specified dihedral angle (e.g. 20°).
-The Normals example computes per-face (left), per-vertex (middle) and
per-corner (right) normals
@@ -630,8 +634,8 @@ principal curvatures:
not the surface’s embedding.
Intuitively, Gaussian curvature tells how locally spherical or elliptic the
-surface is ( \(k_G>0\) ), how locally saddle-shaped or hyperbolic the surface
-is ( \(k_G<0\) ), or how locally cylindrical or parabolic ( \(k_G=0\) ) the
+surface is ( \(k_G>0\) ), how locally saddle-shaped or hyperbolic the surface
+is ( \(k_G<0\) ), or how locally cylindrical or parabolic ( \(k_G=0\) ) the
surface is.
In the discrete setting, one definition for a “discrete Gaussian curvature”
@@ -640,13 +644,13 @@ on a triangle mesh is via a vertex’s angular deficit:
Just like the continuous analog, our discrete Gaussian curvature reveals
elliptic, hyperbolic and parabolic vertices on the domain, as demonstrated in Example 202.
-The GaussianCurvature example computes discrete Gaussian curvature and
visualizes it in pseudocolor.
@@ -670,7 +674,7 @@ normal:
\(-\Delta \mathbf{x} = H \mathbf{n}.\)
It is easy to compute this on a discrete triangle mesh in libigl using the
-cotangent Laplace-Beltrami operator [1]meyer_2003.
#include <igl/cotmatrix.h>
#include <igl/massmatrix.h>
@@ -687,16 +691,16 @@ H = HN.rowwise().norm(); //up to sign
Combined with the angle defect definition of discrete Gaussian curvature, one
can define principal curvatures and use least squares fitting to find
-directions [1]meyer_2003.
Alternatively, a robust method for determining principal curvatures is via
-quadric fitting [2]panozzo_2010. In the neighborhood around every vertex, a
+quadric fitting (2). In the neighborhood around every vertex, a
best-fit quadric is found and principal curvature values and directions are
analytically computed on this quadric (Example
203).
-The CurvatureDirections example computes principal curvatures via quadric
@@ -726,8 +730,8 @@ linear on incident triangles.
of the hat functions:
This reveals that the gradient is a linear function of the vector of \(f_i\)
values. Because the \(\phi_i\) are linear in each triangle, their gradients are
@@ -738,12 +742,12 @@ as a matrix multiplication taking vertex values to triangle values:
where \(\mathbf{f}\) is \(n\times 1\) and \(\mathbf{G}\) is an \(md\times n\) sparse
matrix. This matrix \(\mathbf{G}\) can be derived geometrically, e.g.
-[ch. 2, 3]jacobson_thesis_2013.
+(ch. 2, 3).
Libigl’s grad function computes \(\mathbf{G}\) for
triangle and tetrahedral meshes (Example 204):
-The Gradient example computes gradients of an input function on a mesh and
visualizes the vector field.
@@ -759,9 +763,9 @@ gradient (or equivalently the Laplacian of a function is the trace of its
Hessian):
The Laplace-Beltrami operator generalizes this to surfaces.
@@ -772,9 +776,9 @@ simultaneously from FEM, DEC and applying divergence theorem to vertex
one-rings. As a linear operator taking vertex values to vertex values, the
Laplacian \(\mathbf{L}\) is a \(n\times n\) matrix with elements:
-
where \(N(i)\) are the vertices adjacent to (neighboring) vertex \(i\), and
@@ -813,16 +817,16 @@ of the Dirichlet energy (sum of squared gradients):
Libigl implements discrete “cotangent” Laplacians for triangles meshes and
tetrahedral meshes, building both with fast geometric rules rather than “by the
book” FEM construction which involves many (small) matrix inversions, cf.
-[4]sharf_2007.
The operator applied to mesh vertex positions amounts to smoothing by flowing
the surface along the mean curvature normal direction (Example 205). Note that this is equivalent to minimizing surface area.
-
+The Laplacian example computes conformalized mean curvature flow using the
-cotangent Laplacian [5]kazhdan_2012.
+cotangent Laplacian (5)[].
So we have that \(\mathbf{L} = \mathbf{G}^T \mathbf{T} \mathbf{G}\). This also
hints that we may consider \(\mathbf{G}^T\) as a discrete divergence operator,
@@ -868,6 +872,34 @@ since the Laplacian is the divergence of the gradient. Naturally, \(n \times md\) sparse matrix which takes vector values stored at triangle faces
to scalar divergence values at vertices.
+
Geodesic
+
+
The discrete geodesic distance between two points is the length of the shortest path between then restricted to the surface. For triangle meshes, such a path is made of a set of segments which can be either edges of the mesh or crossing a triangle.
+
+
Libigl includes a wrapper for the exact geodesic algorithm (6) developed by Danil Kirsanov (https://code.google.com/archive/p/geodesic/), exposing it through an Eigen-based API. The function
+
+
igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+
+
+
computes the closest geodesic distances of each vertex in VT or face in FT, from the source vertices VS or faces FS of the input mesh V,F. The output is writted in the vector d, which lists first the distances for the vertices in VT, and then for the faces in FT. For example, if you want to compute the distance from the vertex with id vid, to all vertices of F you can use:
+
+
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;
+igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+
+
+
+
+Example 206 allows to interactively pick the source vertex and displays the distance using a periodic color pattern.
+
+
+
Chapter 3: Matrices and linear algebra
Libigl relies heavily on the Eigen library for dense and sparse linear algebra
@@ -911,7 +943,7 @@ functionality is provided in libigl using slice_into:
-The example Slice shows how to use igl::slice to change the colors for
triangles on a mesh.
@@ -953,11 +985,11 @@ X(I(i,j),j);. That is, I reveals how X is sorte
Analogous functions are available in libigl for: max, min, and unique.
-
+order ([Example 302](302_Sort/main.cpp))." />
The example Sort shows how to use igl::sortrows to
-pseudocolor triangles according to their barycenters’ sorted
+pseudocolor triangles according to their barycenters' sorted
order (Example 302).
@@ -974,187 +1006,187 @@ functionality as common Matlab functions.
-
Name
-
Description
+
Name
+
Description
-
igl::all
-
Whether all elements are non-zero (true)
+
igl::all
+
Whether all elements are non-zero (true)
-
igl::any
-
Whether any elements are non-zero (true)
+
igl::any
+
Whether any elements are non-zero (true)
-
igl::cat
-
Concatenate two matrices (especially useful for dealing with Eigen sparse matrices)
+
igl::cat
+
Concatenate two matrices (especially useful for dealing with Eigen sparse matrices)
-
igl::ceil
-
Round entries up to nearest integer
+
igl::ceil
+
Round entries up to nearest integer
-
igl::cumsum
-
Cumulative sum of matrix elements
+
igl::cumsum
+
Cumulative sum of matrix elements
-
igl::colon
-
Act like Matlab’s :, similar to Eigen’s LinSpaced
+
igl::colon
+
Act like Matlab’s :, similar to Eigen’s LinSpaced
-
igl::components
-
Connected components of graph (cf. Matlab’s graphconncomp)
+
igl::components
+
Connected components of graph (cf. Matlab’s graphconncomp)
-
igl::count
-
Count non-zeros in rows or columns
+
igl::count
+
Count non-zeros in rows or columns
-
igl::cross
-
Cross product per-row
+
igl::cross
+
Cross product per-row
-
igl::cumsum
-
Cumulative summation
+
igl::cumsum
+
Cumulative summation
-
igl::dot
-
dot product per-row
+
igl::dot
+
dot product per-row
-
igl::eigs
-
Solve sparse eigen value problem
+
igl::eigs
+
Solve sparse eigen value problem
-
igl::find
-
Find subscripts of non-zero entries
+
igl::find
+
Find subscripts of non-zero entries
-
igl::floor
-
Round entries down to nearest integer
+
igl::floor
+
Round entries down to nearest integer
-
igl::histc
-
Counting occurrences for building a histogram
+
igl::histc
+
Counting occurrences for building a histogram
-
igl::hsv_to_rgb
-
Convert HSV colors to RGB (cf. Matlab’s hsv2rgb)
+
igl::hsv_to_rgb
+
Convert HSV colors to RGB (cf. Matlab’s hsv2rgb)
-
igl::intersect
-
Set intersection of matrix elements.
+
igl::intersect
+
Set intersection of matrix elements.
-
igl::isdiag
-
Determine whether matrix is diagonal
+
igl::isdiag
+
Determine whether matrix is diagonal
-
igl::ismember
-
Determine whether elements in A occur in B
+
igl::ismember
+
Determine whether elements in A occur in B
-
igl::jet
-
Quantized colors along the rainbow.
+
igl::jet
+
Quantized colors along the rainbow.
-
igl::max
-
Compute maximum entry per row or column
+
igl::max
+
Compute maximum entry per row or column
-
igl::median
-
Compute the median per column
+
igl::median
+
Compute the median per column
-
igl::min
-
Compute minimum entry per row or column
+
igl::min
+
Compute minimum entry per row or column
-
igl::mod
-
Compute per element modulo
+
igl::mod
+
Compute per element modulo
-
igl::mode
-
Compute the mode per column
+
igl::mode
+
Compute the mode per column
-
igl::null
-
Compute the null space basis of a matrix
+
igl::null
+
Compute the null space basis of a matrix
-
igl::nchoosek
-
Compute all k-size combinations of n-long vector
+
igl::nchoosek
+
Compute all k-size combinations of n-long vector
-
igl::orth
-
Orthogonalization of a basis
+
igl::orth
+
Orthogonalization of a basis
-
igl::parula
-
Generate a quantized colormap from blue to yellow
+
igl::parula
+
Generate a quantized colormap from blue to yellow
-
igl::pinv
-
Compute Moore-Penrose pseudoinverse
+
igl::pinv
+
Compute Moore-Penrose pseudoinverse
-
igl::randperm
-
Generate a random permutation of [0,…,n–1]
+
igl::randperm
+
Generate a random permutation of [0,…,n-1]
-
igl::rgb_to_hsv
-
Convert RGB colors to HSV (cf. Matlab’s rgb2hsv)
+
igl::rgb_to_hsv
+
Convert RGB colors to HSV (cf. Matlab’s rgb2hsv)
-
igl::repmat
-
Repeat a matrix along columns and rows
+
igl::repmat
+
Repeat a matrix along columns and rows
-
igl::round
-
Per-element round to whole number
+
igl::round
+
Per-element round to whole number
-
igl::setdiff
-
Set difference of matrix elements
+
igl::setdiff
+
Set difference of matrix elements
-
igl::setunion
-
Set union of matrix elements
+
igl::setunion
+
Set union of matrix elements
-
igl::setxor
-
Set exclusive “or” of matrix elements
+
igl::setxor
+
Set exclusive “or” of matrix elements
-
igl::slice
-
Slice parts of matrix using index lists: (cf. Matlab’s B = A(I,J))
+
igl::slice
+
Slice parts of matrix using index lists: (cf. Matlab’s B = A(I,J))
-
igl::slice_mask
-
Slice parts of matrix using boolean masks: (cf. Matlab’s B = A(M,N))
+
igl::slice_mask
+
Slice parts of matrix using boolean masks: (cf. Matlab’s B = A(M,N))
-
igl::slice_into
-
Slice left-hand side of matrix assignment using index lists (cf. Matlab’s B(I,J) = A)
+
igl::slice_into
+
Slice left-hand side of matrix assignment using index lists (cf. Matlab’s B(I,J) = A)
-
igl::sort
-
Sort elements or rows of matrix
+
igl::sort
+
Sort elements or rows of matrix
-
igl::speye
-
Identity as sparse matrix
+
igl::speye
+
Identity as sparse matrix
-
igl::sum
-
Sum along columns or rows (of sparse matrix)
+
igl::sum
+
Sum along columns or rows (of sparse matrix)
-
igl::unique
-
Extract unique elements or rows of matrix
+
igl::unique
+
Extract unique elements or rows of matrix
@@ -1184,31 +1216,31 @@ the right-hand side.
vertices come first and then boundary vertices:
Finally we can solve this equation for the unknown values at interior vertices
\(\mathbf{z}_{in}\).
@@ -1223,7 +1255,7 @@ the linear algebra above directly. Then we can slice the solution into
rows of Z corresponding to the interior vertices (Example 303).
-The LaplaceEquation example solves a Laplace equation with Dirichlet
boundary conditions.
@@ -1239,7 +1271,7 @@ energy subject to the same boundary conditions:
The general problem of minimizing some energy over a mesh subject to fixed
value boundary conditions is so wide spread that libigl has a dedicated api for
@@ -1249,26 +1281,28 @@ solving such systems.
\(\mathbf{Q}\) is a (usually sparse) \(n \times n\) positive semi-definite
+
\(\mathbf{Q}\) is a (usually sparse) \(n \times n\) positive semi-definite
matrix of quadratic coefficients (Hessian),
-
\(\mathbf{B}\) is a \(n \times 1\) vector of linear coefficients,
-
\(\mathbf{z}_b\) is a \(|b| \times 1\) portion of
+
\(\mathbf{B}\) is a \(n \times 1\) vector of linear coefficients,
+
\(\mathbf{z}_b\) is a \(|b| \times 1\) portion of
+
\(\mathbf{z}\) corresponding to boundary or fixed vertices,
-
\(\mathbf{z}_{bc}\) is a \(|b| \times 1\) vector of known values corresponding to
+
\(\mathbf{z}_{bc}\) is a \(|b| \times 1\) vector of known values corresponding to
+
\(\mathbf{z}_b\),
-
\(\mathbf{A}_{eq}\) is a (usually sparse) \(m \times n\) matrix of linear
+
\(\mathbf{A}_{eq}\) is a (usually sparse) \(m \times n\) matrix of linear
equality constraint coefficients (one row per constraint), and
-
\(\mathbf{B}_{eq}\) is a \(m \times 1\) vector of linear equality constraint
+
\(\mathbf{B}_{eq}\) is a \(m \times 1\) vector of linear equality constraint
right-hand side values.
@@ -1303,7 +1337,7 @@ with active linear equality constraints. Specifically let’s solve the
bi-Laplace equation or equivalently minimize the Laplace energy:
Differentiating with respect to \(\left( \mathbf{z}^T \lambda^T \right)\) reveals
a linear system and we can solve for \(\mathbf{z}\) and \(\lambda\). The only
@@ -1368,12 +1405,12 @@ different factorization technique (LDLT rather than LLT): libigl’s
the presence of linear equality constraints (Example 304).
-The example LinearEqualityConstraints first solves with just fixed value
-constraints (left: 1 and –1 on the left hand and foot respectively), then
+constraints (left: 1 and -1 on the left hand and foot respectively), then
solves with an additional linear equality constraint (right: points on right
hand and foot constrained to be equal).
@@ -1420,11 +1457,11 @@ igl::active_set(Q,B,b,bc,Aeq,Beq,Aieq,Bieq,lx,ux,as,Z);
-Example 305 uses an active set solver to optimize
-discrete biharmonic kernels [6]rustamov_2011 at multiple scales
+discrete biharmonic kernels (7)[] at multiple scales
.
@@ -1437,7 +1474,7 @@ eigen value problem:
where \(A\) is a sparse symmetric matrix and \(B\) is a sparse positive definite
matrix. Most commonly in geometry processing, we let \(A=L\) the cotangent
-Laplacian and \(B=M\) the per-vertex mass matrix (e.g. [7]vallet_2008).
+Laplacian and \(B=M\) the per-vertex mass matrix (e.g. (8)[]).
Typically applications will make use of the low frequency eigen modes.
Analogous to the Fourier decomposition, a function \(f\) on a surface can be
represented via its spectral decomposition of the eigen modes of the
@@ -1457,7 +1494,7 @@ eigen vector satisfying:
Note that Vallet & Levy [7]vallet_2008 propose solving a symmetrized
+
Note that Vallet & Levy (8)[] propose solving a symmetrized
standard eigen problem \(\mathbf{M}^{-1/2}\mathbf{L}\mathbf{M}^{-1/2} \phi_i
= \lambda_i \phi_i\). Libigl implements a generalized eigen problem solver so
this unnecessary symmetrization can be avoided.
@@ -1465,9 +1502,9 @@ this unnecessary symmetrization can be avoided.
Often the sum above is truncated to the first \(k\) eigen vectors. If the low
frequency modes are chosen, i.e. those corresponding to small \(\lambda_i\)
values, then this truncation effectively regularizes\(\mathbf{f}\) to smooth,
-slowly changing functions over the mesh (e.g. [8]hildebrandt_2011). Modal
+slowly changing functions over the mesh (e.g. (9)[]). Modal
analysis and model subspaces have been used frequently in real-time deformation
-(e.g. [9]barbic_2005).
In Example 306), the first 5 eigen vectors
of the discrete Laplace-Beltrami operator are computed and displayed in
@@ -1484,7 +1521,7 @@ igl::eigs(L,M,5,igl::EIGS_TYPE_SM,U,S);
-(Example 306) Low frequency eigen vectors
@@ -1511,9 +1548,9 @@ partial differential equation.
There are many flavors of these techniques, but a prototypical subset are those
that consider solutions to the bi-Laplace equation, that is a biharmonic
-function [10]botsch_2004. This fourth-order PDE provides sufficient
+function (11)[]. This fourth-order PDE provides sufficient
flexibility in boundary conditions to ensure \(C^1\) continuity at handle
-constraints (in the limit under refinement) [11]jacobson_mixed_2010.
+constraints (in the limit under refinement) (12)[].
Biharmonic surfaces
@@ -1585,7 +1622,7 @@ U = V+D;
-The BiharmonicDeformation example deforms a statue’s head as a biharmonic
@@ -1609,10 +1646,10 @@ terms of the original positions \(\mathbf{x}\) and the
\(\mathbf{x}' = \mathbf{x} - \mathbf{d}\):
In the early work of Sorkine et al., the quantities \(\Delta \mathbf{x}'\) and
-\(\Delta \mathbf{x}\) were dubbed “differential coordinates” [12]sorkine_2004.
+\(\Delta \mathbf{x}\) were dubbed “differential coordinates” (13)[].
Their deformations (without linearized rotations) is thus equivalent to
biharmonic deformation fields.
@@ -1632,7 +1669,7 @@ igl::harmonic(V,F,b,bc,k,Z);
-The PolyharmonicDeformation example deforms a flat domain (left) into a bump as a
solution to various \(k\)-harmonic PDEs.
@@ -1650,7 +1687,7 @@ rotations.
computes its new location as a linear combination of bone transformations:
where \(w_i(\mathbf{x})\) is the scalar weight function of the ith bone evaluated at
\(\mathbf{x}\) and \(\mathbf{T}_i\) is the bone transformation as a \(4 \times 3\)
@@ -1680,15 +1717,15 @@ any handle structure such as a cage, collection of points, selected regions,
etc.).
Bounded biharmonic weights are one such technique that casts weight computation
-as a constrained optimization problem [13]jacobson_2011. The weights enforce
+as a constrained optimization problem (14)[]. The weights enforce
smoothness by minimizing the familiar Laplacian energy:
where \(H_i\) is the ith handle, and constraints which enforce non-negativity,
parition of unity and encourage sparsity:
@@ -1699,7 +1736,7 @@ parition of unity and encourage sparsity:
set solver or by calling out to Mosek.
-The example BoundedBiharmonicWeights computes weights for a tetrahedral
@@ -1717,22 +1754,22 @@ about the \(z\)-axis. Intuitively one might expect to
but instead the blend is a degenerate matrix scaling the \(x\) and \(y\)
coordinates by zero:
-
In practice, this means the shape shrinks and collapses in regions where bone
weights overlap: near joints.
-
Dual quaternion skinning presents a solution [14]kavan_2008. This method
+
Dual quaternion skinning presents a solution (15). This method
represents rigid transformations as a pair of unit quaternions,
\(\hat{\mathbf{q}}\). The linear blend skinning formula is replaced with a
linear blend of dual quaternions:
where \(\hat{\mathbf{q}_i}\) is the dual quaternion representation of the rigid
transformation of bone \(i\). The normalization forces the result of the linear
@@ -1752,8 +1789,8 @@ igl::dqs(V,W,vQ,vT,U);
-The example DualQuaternionSkinning compares linear blend skinning (top) to dual
quaternion skinning (bottom), highlighting LBS’s candy wrapper effect (middle)
@@ -1781,15 +1818,15 @@ They work by comparing the deformation of a mesh
vertex to its rest position rotated to a new coordinate frame which best
matches the deformation. The non-linearity stems from the mutual dependence of
the deformation and the best-fit rotation. These techniques are often labeled
-“as-rigid-as-possible” as they penalize the sum of all local deformations’
+“as-rigid-as-possible” as they penalize the sum of all local deformations'
deviations from rotations.
To arrive at such an energy, let’s consider a simple per-triangle energy:
where \(\mathbf{X}'\) are the mesh’s unknown deformed vertex positions, \(t\) is a
triangle in a list of triangles \(T\), \(a_t\) is the area of triangle \(t\) and
@@ -1808,10 +1845,11 @@ for each triangle \(t\) which are constrained to be ro
rewritten, this time comparing deformed edge vectors to their rotated rest
counterparts:
The separation into the primary vertex position variables \(\mathbf{X}'\) and the
rotations \(\{\mathbf{R}_1,\dots,\mathbf{R}_{|T|}\}\) lead to strategy for
@@ -1825,11 +1863,11 @@ the energy, thus we may safely iterate them until convergence.
The different flavors of “as-rigid-as-possible” depend on the dimension and
codimension of the domain and the edge-sets \(T\). The proposed surface
-manipulation technique by Sorkine and Alexa [15]sorkine_2007, considers \(T\) to
+manipulation technique by Sorkine and Alexa (16)[], considers \(T\) to
be the set of sets of edges emanating from each vertex (spokes). Later, Chao et
al. derived the relationship between “as-rigid-as-possible” mesh energies and
co-rotational elasticity considering 0-codimension elements as edge-sets:
-triangles in 2D and tetrahedra in 3D [16]chao_2010. They also showed how
+triangles in 2D and tetrahedra in 3D (17)[]. They also showed how
Sorkine and Alexa’s edge-sets are not a discretization of a continuous energy,
proposing instead edge-sets for surfaces containing all edges of elements
incident on a vertex (spokes and rims). They show that this amounts to
@@ -1856,10 +1894,10 @@ certain constraints on the positions of vertices in b, we may call:
Libigl’s implementation of as-rigid-as-possible deformation takes advantage of
the highly optimized singular value decomposition code from McAdams et al.
-[17]mcadams_2011 which leverages SSE intrinsics.
+(18)[] which leverages SSE intrinsics.
-The example AsRigidAsPossible deforms a surface as if it were made of an
elastic material
@@ -1875,7 +1913,7 @@ the case of the as-rigid-as-possible optimization, the bottleneck is typically
the large number of polar decompositions necessary to recover best fit
rotations for each edge-set (i.e. for each triangle, tetrahedron, or vertex
cell). Even if this code is optimized, the number of primary degrees of freedom
-is tied to the discretization level, despite the deformations’ low frequency
+is tied to the discretization level, despite the deformations' low frequency
behavior.
This invites two routes toward fast non-linear optimization. First, is it
@@ -1895,7 +1933,7 @@ linear blend skinning in matrix form:
replaced by a linear combination of a small number of degrees of freedom in the
\((3+1)m \times 3\) stack of transposed “handle” transformations. Swapping in
\(\mathbf{M}\mathbf{T}\) for \(\mathbf{X}'\) in the ARAP energies above immediately
-sees performance gains during the global solve step as \(m << n\).
+sees performance gains during the global solve step as \(m << n\).
The complexity of the local step—fitting rotations—is still bound
to the original mesh discretization. However, if the skinning is well behaved,
@@ -1908,7 +1946,7 @@ clustered edge-sets show diminishing returns on the deformation quality so we
may choose a small number of clusters, proportional to the number of skinning
weight functions (rather than the number of discrete mesh vertices).
-
This proposed deformation model [18]jacobson_2012, can simultaneously be seen as a
+
This proposed deformation model (19)[], can simultaneously be seen as a
fast, subspace optimization for ARAP and as an automatic method for finding
the best skinning transformation degrees of freedom.
@@ -1917,11 +1955,11 @@ the skinning transformations associated with handles. To fix a transformation
entirely we simply add the constraint:
where \(\hat{\mathbf{L}}_i^T\) is the fixed \(3 \times 3\) linear part of the
transformation at handle \(i\).
@@ -1971,7 +2009,7 @@ biharmonic distance embedding.
redundant) clustering of the per-triangle edge-sets.
-
@@ -1987,7 +2025,7 @@ rotation edge sets (right of middle), to the very fast subpsace method
propagating full affine transformations at handles (bones, points, regions,
etc.) to the rest of the shape via weights. Another deformation framework,
called “generalized barycentric coordinates”, is a special case of linear blend
-skinning [19]jacobson_skinning_course_2014: transformations are restricted to
+skinning (20)[]: transformations are restricted to
pure translations and weights are required to retain affine precision. This
latter requirement means that we can write the rest-position of any vertex in
the mesh as the weighted combination of the control handle locations:
@@ -2003,19 +2041,19 @@ position of each point of the shape to be the weighted combination of the
There are many different flavors of “generalized barycentric coordinates”
(see table in “Automatic Methods” section,
-[19]jacobson_skinning_course_2014). The vague goal of “generalized barycentric
+(20)[]). The vague goal of “generalized barycentric
coordinates” is to capture as many properties of simplicial barycentric
coordinates (e.g. for triangles in 2D and tetrahedral in 3D) for larger sets of
points or polyhedra. Some generalized barycentric coordinates can be computed
in closed form; others require optimization-based precomputation. Nearly all
flavors require connectivity information describing how the control points form
a external polyhedron around the input shape: a cage. However, a recent
-techinique does not require a cage [20]wang_bc_2015. This method ensures
+techinique does not require a cage (21)[]. This method ensures
affine precision during optimization over weights of a smoothness energy with
affine functions in its kernel:
+\mathbf{W}), \text{subject to: } \mathbf{C} = \mathbf{W}\mathbf{C}\)
subject to interpolation constraints at selected vertices. If \(\mathbf{A}\) has
affine functions in its kernel—that is, if \(\mathbf{A}\mathbf{V} = 0\)—then
@@ -2040,12 +2078,12 @@ the integral-average of \(\mathbf{K}\) applied to a fu
the mesh:
Since the Laplacian \(\mathbf{K}\) is a second-order derivative it measures zero on affine
functions, thus \(\mathbf{A}\) has affine functions in its null space. A short
derivation proves that this implies \(\mathbf{W}\) will be affine precise (see
-[20]wang_bc_2015).
Minimizers of this “squared Laplacian” energy are in some sense discrete
biharmonic functions. Thus they’re dubbed “biharmonic coordinates” (not the
@@ -2060,7 +2098,7 @@ handles):
-
@@ -2070,7 +2108,7 @@ points for a biharmonic coordinates deformation of the blue high-resolution
mesh.
-
Chapter 5: Parametrization
+
Chapter 5: Parametrization
In computer graphics, we denote as surface parametrization a map from the
surface to \(\mathbf{R}^2\). It is usually encoded by a new set of 2D
@@ -2096,9 +2134,9 @@ genus. They initially cut the mesh in multiple patches that can be separately pa
Global seamless parametrization: these are global parametrization algorithm that hides the seams, making the parametrization “continuous”, under specific assumptions that we will discuss later.
Harmonic parametrization [21]eck_2005 is a single patch, fixed boundary parametrization
+
Harmonic parametrization (22)[] is a single patch, fixed boundary parametrization
algorithm that computes the 2D coordinates of the flattened mesh as two
harmonic functions.
@@ -2133,7 +2171,7 @@ functions is designed to be reusable in other parametrization algorithms.
mesh (Example 501).
-(Example 501) Harmonic parametrization. (left)
@@ -2141,9 +2179,9 @@ mesh with texture, (right) UV parametrization with
texture
-
Least squares conformal maps parametrization [22]levy_2002 minimizes the
+
Least squares conformal maps parametrization (23)[] minimizes the
conformal (angular) distortion of the parametrization. Differently from
harmonic parametrization, it does not need to have a fixed boundary.
@@ -2151,7 +2189,7 @@ harmonic parametrization, it does not need to have a fixed boundary.
@@ -2185,15 +2223,15 @@ case we do not need to fix the boundary. To remove the null space of the energy
vertices to two arbitrary positions. The full source code is provided in Example 502.
-(Example 502) LSCM parametrization. (left) mesh
with texture, (right) UV parametrization
-
As-rigid-as-possible parametrization [24]liu_2008 is a powerful single-patch,
+
As-rigid-as-possible parametrization (25)[] is a powerful single-patch,
non-linear algorithm to compute a parametrization that strives to preserve
distances (and thus angles). The idea is very similar to ARAP surface
deformation: each triangle is mapped to the plane trying to preserve its
@@ -2210,7 +2248,7 @@ parametrization. Similarly to LSCM, the boundary is free to deform to minimize
the distortion.
The design of tangent fields is a basic tool used to design guidance fields for
uniform quadrilateral and hexahedral remeshing. Libigl contains an
@@ -2245,7 +2283,7 @@ the triangle mesh (output_field), plus the singularities of the field
The singularities are vertices where the field vanishes (highlighted in red in
-the figure above). igl::nrosy can also generate N-RoSy fields [25]levy_2008,
+the figure above). igl::nrosy can also generate N-RoSy fields (26)[],
which are a generalization of vector fields where in every face the vector is
defined up to a constant rotation of \(2\pi / N\). As can be observed in
the following figure, the singularities of the fields generated with different
@@ -2259,11 +2297,11 @@ N are of different types and they appear in different positions.
We demonstrate how to call and plot N-RoSy fields in Example
504, where the degree of the field can be change
pressing the number keys. igl::nrosy implements the algorithm proposed in
-[26]bommes_2009. N-RoSy fields can also be interpolated with the algorithm
-proposed in [27]knoppel_2013, see Section npolyvectorfields for more details
+(27)[]. N-RoSy fields can also be interpolated with the algorithm
+proposed in (28)[], see Section npolyvectorfields for more details
(igl::n_polyvector).
The previous parametrization methods were focusing on creating parametrizations
of surface patches aimed at texture mapping or baking of other surface
@@ -2271,7 +2309,7 @@ properties such as normals and high-frequency details. Global, seamless
parametrization aims at parametrizing complex shapes with a parametrization
that is aligned with a given set of directions for the purpose of surface
remeshing. In libigl, we provide a reference implementation of the pipeline
-proposed in the mixed integer quadrangulation paper [26]bommes_2009.
+proposed in the mixed integer quadrangulation paper (27)[].
The first step involves the design of a 4-RoSy field (sometimes called cross
field) that describes the alignment of the edges of the desired quadrilateral
@@ -2350,7 +2388,7 @@ input cross field.
We hide the seams by adding integer constraints to the Poisson problem
-that align the isolines on both sides of each seam [26]bommes_2009.
+that align the isolines on both sides of each seam (27).
@@ -2369,12 +2407,12 @@ it contains many overlaps.
libQEx (not included in libigl).
The full pipeline is implemented in Example 505.
-
Anisotropic and non-uniform quad remeshing is important to concentrate the
elements in the regions with more details. It is possible to extend the MIQ
quad meshing framework to generate anisotropic quad meshes using a mesh
-deformation approach [28]panozzo_2014.
The input of the anisotropic remeshing algorithm is a sparse set of constraints
that define the shape and scale of the desired quads. This can be encoded as a
@@ -2430,11 +2468,11 @@ generate the UV parametrization, but other algorithms could be applied: the
only desiderata is that the generated quad mesh should be as isotropic as
possible.
N-RoSy vector fields can be further generalized to represent arbitrary
vector-sets, with arbitrary angles between them and with arbitrary lengths
-[29]diamanti_2014. This generalization is called N-PolyVector field, and
+(30)[]. This generalization is called N-PolyVector field, and
libigl provides the function igl::n_polyvector to design them starting from a
sparse set of constraints (Example 507).
@@ -2447,12 +2485,12 @@ sparse set of constraints (Example 507
-
Globally optimal direction fields [27]knoppel_2013 are a special case of
+
Globally optimal direction fields (28)[] are a special case of
PolyVector fields. If the constraints are taken from an N-RoSy field,
igl::n_polyvector generates a field that is equivalent, after normalization,
to a globally optimal direction field.
Two tangent vectors lying on a face of a triangle mesh are conjugate if
@@ -2461,13 +2499,13 @@ to a globally optimal direction field.
This condition is very important in architectural geometry: The faces of an
infinitely dense quad mesh whose edges are aligned with a conjugate field are
planar. Thus, a quad mesh whose edges follow a conjugate field are easier to
-planarize [30]liu_2011.
Finding a conjugate vector field that satisfies given directional constraints
is a standard problem in architectural geometry, which can be tackled by
deforming a Poly-Vector field to the closest conjugate field.
-
This algorithm [29]diamanti_2014 alternates a global step, which enforces
+
This algorithm (30) alternates a global step, which enforces
smoothness, with a local step, that projects the field on every face to the
closest conjugate field (Example 508).
A quad mesh can be transformed in a planar quad mesh with Shape-Up
-[31]bouaziz_2012, a local/global approach that uses the global step to enforce
+(32), a local/global approach that uses the global step to enforce
surface continuity and the local step to enforce planarity.
Example 509 planarizes a quad mesh until it
@@ -2496,7 +2534,7 @@ igl::palanarize (right). The colors represent the planarity of the
quads.
While mostly applicable for the design of symmetric fields (i.e. fields that
comprise of vector sets with symmetries between them at each point, e.g. N-RoSy
-or frame-fields), the framework presented in [29]diamanti_2014 can be used to
+or frame-fields), the framework presented in (30)[] can be used to
design completely general fields, with possibly no such symmetries. For example,
one can design fields that at each point comprise of an arbitrary number of
vectors, not required to be collinear - as opposed e.g. to the case of the 4
@@ -2542,10 +2580,12 @@ function igl::n_polyvector_general, and is illustrated in the example (
+mesh faces, but is only shown on a subset for clarity.
+" />
Interpolation of a general field with 3 (left) and 9 vectors per point field
from a sparse set of random constraints (in red). The field is defined on all
-mesh faces, but is only shown on a subset for clarity.
+mesh faces, but is only shown on a subset for clarity.
+
The design of these general directional fields (also called vector-set fields)
@@ -2562,14 +2602,14 @@ to the particular nature of the polynomial that applies in that case (two
coefficients are 0).
For a complete categorization of fields used in various applications (including
-these general ones) see Vaxman et al. 2016 [33]vaxman_2016.
+these general ones) see Vaxman et al. 2016 (34).
-
Chapter 6: External libraries
+
Chapter 6: External libraries
An additional positive side effect of using matrices as basic types is that it
is easy to exchange data between libigl and other software and libraries.
Geometry processing applications often require a considerable amount of
computational time and/or manual input. Serializing the state of the application
@@ -2714,7 +2754,7 @@ common to have to do small changes to figures, and being able to serialize the
entire state just before you take screenshots will save you many painful hours
before a submission deadline.
Libigl can be interfaced with Matlab to offload numerically heavy computation
to a Matlab script. The major advantage of this approach is that you will be
@@ -2840,7 +2880,7 @@ L = sparse(LIJV(:,1),LIJV(:,2),LIJV(:,3));
which is easily copied and pasted into Matlab for debugging, etc.
It is also possible to call libigl functions from matlab, compiling them as MEX
functions. This can be used to offload to C++ code the computationally
@@ -2851,7 +2891,7 @@ We plan to provide wrappers for all our functions in the future, if you are
interested in this feature (or if you want to help implementing it) please let
us know.
The generation of high-quality triangle and tetrahedral meshes is a very common
task in geometry processing. We provide wrappers in libigl to
@@ -2876,7 +2916,7 @@ in its interior) is triangulated.
Extreme deformations or parametrizations with high-distortion might flip
elements. This is undesirable in many applications, and it is possible to
avoid it by introducing a non-linear constraints that guarantees that the area
of every element remain positive.
-
Libigl can be used to compute Locally Injective Maps [34]schuller_2013 using a variety of
+
Libigl can be used to compute Locally Injective Maps (35)[] using a variety of
deformation energies. A simple deformation of a 2D grid is computed in Example
608.
@@ -2969,7 +3009,7 @@ editing plus the anti-flipping constraints (right)." />
editing plus the anti-flipping constraints (right).
-
Constructive solid geometry (CSG) is a technique to define a complex surface as
the result of a number of set operations on solid regions of space: union,
@@ -2983,23 +3023,27 @@ function \(a(\mathbf{x})\) “returns true”.
operations are straightforward. For example, the union of solids \(A\) and \(B\)
is simply
-
\(A \cup B = \{\mathbf{x} \left.\right|
- a(\mathbf{x}) \text{ or } b(\mathbf{x})\},\)
+
$A \cup B = {\mathbf{x} \left.\right|
+
+
a(\mathbf{x}) \text{ or } b(\mathbf{x})},$
the intersection is
-
\(A \cap B = \{\mathbf{x} \left.\right|
- a(\mathbf{x}) \text{ and } b(\mathbf{x})\},\)
+
$A \cap B = {\mathbf{x} \left.\right|
+
+
a(\mathbf{x}) \text{ and } b(\mathbf{x})},$
the difference \(A\)minus\(B\) is
-
\(A \setminus B = \{\mathbf{x} \left.\right|
- a(\mathbf{x}) \text{ and _not_ } b(\mathbf{x})\},\)
+
$A \setminus B = {\mathbf{x} \left.\right|
+
+
a(\mathbf{x}) \text{ and not } b(\mathbf{x})},$
and the symmetric difference (XOR) is
-
\(A \triangle B = \{\mathbf{x} \left.\right|
- \text{either } a(\mathbf{x}) \text{ or } b(\mathbf{x}) \text{ but not both }\}.\)
+
$A \triangle B = {\mathbf{x} \left.\right|
+
+
\text{either } a(\mathbf{x}) \text{ or } b(\mathbf{x}) \text{ but not both }}.$
Stringing together many of these operations, one can design quite complex
shapes. A typical CSG library might only keep explicit base-case
@@ -3012,7 +3056,7 @@ compute robustly with boundary representations, but are nonetheless useful.
To compute a boolean operation on a triangle mesh with vertices VA and
triangles FA and another mesh VB and FB, libigl first computes a unified
-“mesh arrangement” (see [35]zhou_2016) with vertices V and triangles F where all triangle-triangle
+“mesh arrangement” (see (36)[]) with vertices V and triangles F where all triangle-triangle
intersections have been “resolved”. That is, edges and vertices are added
exactly at the intersection lines, so the resulting non-manifold mesh (V,F)
has no self-intersections.
@@ -3031,10 +3075,10 @@ intersection) the boundary of the corresponding cells are extracted.
The following figure shows each boolean operation on two meshes.
-The example Boolean conducts
boolean operations on the Cheburashka (red) and Knight (green). From left
@@ -3056,7 +3100,7 @@ together coincident vertices, maintaining original triangle orientations.
cork, which is typically faster, but is not
always robust.
-
Libigl contains various mesh statistics, including face angles, face areas and
the detection of singular vertices, which are vertices with more or less than 6
@@ -3133,7 +3177,7 @@ the angles are to 60 degrees the more stable will the optimization be. In this
case, it is clear that the mesh is of bad quality and it will probably result
in artifacts if used for solving PDEs.
The problem of tetrahedralizing the interior of closed watertight surface mesh
is a difficult, but well-posed problem (see our Tetgen wrappers). But
@@ -3155,7 +3199,7 @@ mesh and which are outside. That is, which should be kept and which should be
removed.
The “Generalized Winding Number” is a robust method for determined
-inside and outside for troublesome meshes [36]jacobson_2013. The generalized
+inside and outside for troublesome meshes (37)[]. The generalized
winding number with respect to (V,F) at some point \(\mathbf{p} \in
\mathcal{R}^3\) is defined as scalar function:
@@ -3174,7 +3218,7 @@ oriented), then \(w(\mathbf{p})\) tends smoothly towar
more inside (V,F), and toward 0 as \(\mathbf{p}\) is more outside.
-Mesh Decimation
+
The study of mesh simplification or decimation is nearly as old as meshes
themselves. Given a high resolution mesh with too many triangles, find a “well
@@ -3198,14 +3242,14 @@ methods are fairly advanced.
One family of mesh decimation methods operates by successively remove elements
from the mesh. In particular, Hoppe advocates for successively remove or rather
-collapsing edges [37]hoppe_1996. The generic form of this technique is to
+collapsing edges (38)[]. The generic form of this technique is to
construct a sequence of n meshes from the initial high-resolution mesh \(M_0\) to
the lowest resolution mesh \(M_n\) by collapsing a single edge:
Hoppe’s original method and subsequent follow-up works propose various ways to
choose the next edge to collapse in this sequence. Using a cost-based paradigm,
@@ -3265,13 +3309,13 @@ drops below m=1000.
One can also scratch deeper inside the decimation loop and call
igl::collapse_edge directly. In order to operate efficiently, this routine
needs more than the usual (V,F) mesh representation. We need E a list of
-edge indices, where E.row(i) --> [s,d]; we need EMAP which maps the
+edge indices, where E.row(i) --> [s,d]; we need EMAP which maps the
“half”-edges of each triangle in F to its corresponding edge in E so that
-E.row(EMAP(f+i*F.rows)) --> [s,d] if the edge across from the ith corner of the
+E.row(EMAP(f+i*F.rows)) --> [s,d] if the edge across from the ith corner of the
fth face is [s,d] (up to orientation); we need EF and EI which keep track
of the faces incident on each edge and across from which corner of those faces
the edges appears, so that EF(e,o) = f and EI(e,o) = i means that the edge
-E.row(e) --> [s,d] appears in the fth face across from its ith corner (for
+E.row(e) --> [s,d] appears in the fth face across from its ith corner (for
o=0 the edge orientations should match, for o=1 the orientations are
opposite).
@@ -3324,7 +3368,7 @@ model.
queue based approach with the simple shortest-edge-midpoint cost/placement
strategy discussed above.
-
In the Generalized Winding Number section, we
examined a robust method for determining whether points lie inside or outside
@@ -3399,15 +3443,15 @@ tree.squared_distance(V,F,P,sqrD,I,C);
Finally, from the closest point or the winding number it’s possible to sign
this distance. In igl::signed_distance we provide two methods for signing:
-the so-called “pseudo-normal test” [38]baerentzen_2005 and the generalized
-winding number [36]jacobson_2013.
+the so-called “pseudo-normal test” (39)[] and the generalized
+winding number (37)[].
The pseudo-normal test (see also igl::pseudonormal_test) assumes the input
mesh is a watertight (closed, non-self-intersecting, manifold) mesh. Then given
a query point \(\mathbf{q}\) and its closest point \(\mathbf{c} \in (V,F)\), it
carefully chooses an outward normal \(\mathbf{n}\) at \(\mathbf{c}\) so that
\(\text{sign}(\mathbf{q}-\mathbf{c})\cdot \mathbf{n}\) reveals whether
-\(\mathbf{q}\) is inside \((V,F)\): –1, or outside: +1. This is a fast \(O(1)\) test
+\(\mathbf{q}\) is inside \((V,F)\): -1, or outside: +1. This is a fast \(O(1)\) test
once \(\mathbf{c}\) is located, but may fail if V,F is not watertight.
Often 3D data is captured as scalar field defined over space \(f(\mathbf{x}) :
\mathcal{R}^3 \rightarrow \mathcal{R}\). Lurking within this field,
@@ -3444,7 +3488,7 @@ iso-surface at value \(v\) is composed of all points <
processing is to extract an iso-surface as a triangle mesh for further
mesh-based processing or visualization. This is referred to as iso-contouring.
“Marching Cubes” (40) is a famous
method for iso-contouring
tri-linear functions \(f\) on a regular lattice (aka grid). The core idea of this
method is to contour the iso-surface passing through each cell (if it does at
@@ -3461,7 +3505,7 @@ input scalar field S sampled at vertex locations GV of
-Facet Orientation
+
Models from the web occasionally arrive unorientated in the sense that
the orderings of each triangles vertices do not consistently agree. Determining
@@ -3495,7 +3539,7 @@ enforce a consistent facet orientation in the output faces FF.
For (closed or nearly closed) surfaces representing the boundary of a solid
object, libigl provides a routine to reorient faces so that the vertex ordering
corresponds to a counter-clockwise ordering of the vertices with a
-right-hand-rule normal pointing outward. This method [40]takayama14 assumes
+right-hand-rule normal pointing outward. This method (41)[] assumes
that most of the universe is
empty.
That is, most points in space are outside of the solid object than inside.
@@ -3510,10 +3554,10 @@ occluded (lighter, i.e., facing more void space).
The boolean vector I reveals which rows of F have been flipped in FF.
-(Example 706) loads a truck model with
inconsistent orientations (back facing triangles shown darker). Orientable
@@ -3522,7 +3566,7 @@ Alternatively, each individual triangle is considered a “patch” (mid
and oriented outward independently.
-
The swept volume \(S\) of a moving solid object \(A\) can be defined as any point in
space such that at one moment in time the point lies inside the solid. In other
@@ -3547,20 +3591,21 @@ error.
volume is by exploiting an alternative definition of the swept volume based on
signed distances:
\min\limits_{t \in [0,1]} d(\mathbf{p},f(t) \partial A) < 0 \right}$
If \(\partial A\) is a triangle mesh, then we can approximate this by 1)
discretizing time at a finite step of steps \([0,\Delta t,2\Delta t, \dots, 1]\)
and by 2) discretizing space with a regular grid and representing the distance
field using trilinear interpolation of grid values. Finally the output mesh,
\(\partial S\) is approximated by contouring using Marching Cubes
-[39]lorensen_1987.
This method is similar to one described by Schroeder et al. in 1994
-[41]schroeder_1994, and the one used in conjunction with boolean operations by
-Garg et al. 2016 [42]garg_2016.
+(42), and the one used in conjunction with boolean operations by
+Garg et al. 2016 (43).
In libigl, if your input solid’s surface is represented by (V,F) then the
output surface mesh will be (SV,SF) after calling:
@@ -3573,7 +3618,7 @@ volume, greater than zero to approximate a positive offset of the swept volume
or less than zero to approximate a negative offset.
-(Example 707) computes
@@ -3581,7 +3626,7 @@ the surface of the swept volume (silver) of the bunny model undergoing a rigid
motion (gold).
-
Picking vertices and faces using the mouse is very common in geometry
processing applications. While this might seem a simple operation, its
@@ -3610,15 +3655,15 @@ Hierarchy constructed
by Embree, and fid and vid are the picked face and vertex, respectively.
-(Example 708) Picking via ray casting. The selected
vertices are colored in red.
-
Vector fields on surfaces are commonly visualized by tracing streamlines. Libigl
+
Vector fields on surfaces are commonly visualized by tracing [streamlines] (https://en.wikipedia.org/wiki/Streamlines,_streaklines,_and_pathlines). Libigl
supports the seeding and tracing of streamlines, for both simple vector fields
and for N-rosy fields. The seeds for the streamlines are initialized using streamlines_init,
and the lines are traced using streamlines_next. Each call to streamlines_next extends
@@ -3626,13 +3671,13 @@ each line by one triangle, allowing interactive rendering of the traced lines, a
in Example 709.
The Scalable Locally Injective Maps [43]rabinovich_2016 algorithm allows to
+
The Scalable Locally Injective Maps (44) algorithm allows to
compute locally injective maps on massive datasets. The algorithm shares many
similarities with ARAP, but uses a reweighting scheme to minimize arbitrary
distortion energies, including those that prevent the introduction of flips.
@@ -3652,7 +3697,7 @@ using the SLIM algorithm in 10 iterations." />
using the SLIM algorithm in 10 iterations.
-
Given a coarse mesh (aka cage) with vertices V and faces F, one can createa
higher-resolution mesh with more vertices and faces by subdividing every
@@ -3676,7 +3721,7 @@ a finer and finer mesh.
The subdivision method of igl::loop is not in plane. The vertices of the
refined mesh are moved to weight combinations of their neighbors: the mesh is
-smoothed as it is refined [44]loop_1987. This and other smooth subdivision
+smoothed as it is refined (45). This and other smooth subdivision
methods can be understood as generalizations of spline curves to surfaces. In
particular the Loop subdivision method will converge to a \(C^1\) surface as we
consider the limit of recursive applications of subdivision. Away from
@@ -3695,14 +3740,14 @@ the carrier surfaces with extreme bias.
+`igl::upsample`, `igl::loop` and
+`igl::false_barycentric_subdivision`." />
The original coarse mesh and three different subdivision methods:
igl::upsample, igl::loop and
igl::false_barycentric_subdivision.
-
A noisy function \(f\) defined on a surface \(\Omega\) can be smoothed using an
energy minimization that balances a smoothing term \(E_S\) with a quadratic
@@ -3733,7 +3778,7 @@ of the function to be perpendicular to the boundary, the Hessian energy gives
an unbiased result.
-
@@ -3743,31 +3788,31 @@ with the Laplacian energy and zero Neumann boundary conditions, and the
result of smoothing with the Hessian energy.
-
Miscellaneous
+
Miscellaneous
Libigl contains a wide variety of geometry processing tools and functions for
dealing with meshes and the linear algebra related to them: far too many to
discuss in this introductory tutorial. We’ve pulled out a couple of the
interesting functions in this chapter to highlight.
-
Outlook for continuing development
+
Outlook for continuing development
Libigl is in active development, and we plan to focus on the following features
in the next months:
A better and more consistent documentation, plus extending this tutorial
- to cover more libigl features.
+to cover more libigl features.
Implement a mixed-integer solver which only uses Eigen to remove the
- dependency on CoMiSo.
+dependency on CoMiSo.
Improve the robustness and performance of the active set QP solver. In
- particular, handle linearly dependent constraints.
We encourage you to contribute to the library and to report problems and bugs.
@@ -3776,262 +3821,310 @@ repository and to open a our github
repository.
-
+
diff --git a/tutorial/tutorial.md b/tutorial/tutorial.md
index 96b0c1c12..41fe9810e 100644
--- a/tutorial/tutorial.md
+++ b/tutorial/tutorial.md
@@ -44,10 +44,11 @@ lecture notes links to a cross-platform example application.
* [202 Gaussian Curvature](#gaussiancurvature)
* [203 Curvature Directions](#curvaturedirections)
* [204 Gradient](#gradient)
- * [204 Laplacian](#laplacian)
+ * [205 Laplacian](#laplacian)
* [Mass matrix](#massmatrix)
* [Alternative construction of
Laplacian](#alternativeconstructionoflaplacian)
+ * [206 Geodesic Distance](#geodesic)
* [Chapter 3: Matrices and Linear Algebra](#chapter3:matricesandlinearalgebra)
* [301 Slice](#slice)
* [302 Sort](#sort)
@@ -790,6 +791,28 @@ since the Laplacian is the divergence of the gradient. Naturally, $\mathbf{G}^T$
a $n \times md$ sparse matrix which takes vector values stored at triangle faces
to scalar divergence values at vertices.
+## Geodesic
+
+The discrete geodesic distance between two points is the length of the shortest path between then restricted to the surface. For triangle meshes, such a path is made of a set of segments which can be either edges of the mesh or crossing a triangle.
+
+Libigl includes a wrapper for the exact geodesic algorithm [#mitchell_1987] developed by Danil Kirsanov (https://code.google.com/archive/p/geodesic/), exposing it through an Eigen-based API. The function
+```cpp
+igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+```
+computes the closest geodesic distances of each vertex in VT or face in FT, from the source vertices VS or faces FS of the input mesh V,F. The output is writted in the vector d, which lists first the distances for the vertices in VT, and then for the faces in FT. For example, if you want to compute the distance from the vertex with id ```vid```, to all vertices of F you can use:
+```cpp
+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;
+igl::exact_geodesic(V,F,VS,FS,VT,FT,d);
+```
+ allows to interactively pick the source vertex and displays the distance using a periodic color pattern.
+](images/geodesicdistance.jpg)
+
# Chapter 3: Matrices and linear algebra
Libigl relies heavily on the Eigen library for dense and sparse linear algebra
routines. Besides geometry processing routines, libigl has linear algebra
@@ -3524,3 +3547,4 @@ pseudonormal](https://www.google.com/search?q=Signed+distance+computation+using+
Solid
Geometry](https://www.google.com/search?q=Mesh+Arrangements+for+Solid+Geometry),
2016
+[#mitchell_1987]: Joseph S. B. Mitchell, David M. Mount, Christos H. Papadimitriou. [The Discrete Geodesic Problem](https://www.google.com/search?q=The+Discrete+Geodesic+Problem), 1987