|
@@ -21,7 +21,14 @@ of these lecture notes links to a cross-platform example application.
|
|
|
|
|
|
# Table of Contents
|
|
# Table of Contents
|
|
|
|
|
|
-* [Chapter 1: Introduction to libigl]
|
|
|
|
|
|
+* [Chapter 1: Introduction to libigl][100]
|
|
|
|
+ * [Mesh representation][101]
|
|
|
|
+ * [Plotting surfaces][102]
|
|
|
|
+ * [Interaction with keyboard and mouse][103]
|
|
|
|
+ * [Scalar field visualization][104]
|
|
|
|
+ * [Overlays][105]
|
|
|
|
+ * [Picking vertices and faces][106]
|
|
|
|
+ * [libigl design principles][107]
|
|
|
|
|
|
* [Chapter 2: Discrete Geometric Quantities and
|
|
* [Chapter 2: Discrete Geometric Quantities and
|
|
Operators](#chapter2:discretegeometricquantitiesandoperators)
|
|
Operators](#chapter2:discretegeometricquantitiesandoperators)
|
|
@@ -53,6 +60,253 @@ of these lecture notes links to a cross-platform example application.
|
|
* [406 Fast automatic skinning
|
|
* [406 Fast automatic skinning
|
|
transformations](#fastautomaticskinningtransformations)
|
|
transformations](#fastautomaticskinningtransformations)
|
|
|
|
|
|
|
|
+* [Chapter 5: Parametrization][500]
|
|
|
|
+ * [501 Harmonic parametrization][501]
|
|
|
|
+ * [502 Least-Square Conformal Maps][502]
|
|
|
|
+ * [503 As-Rigid-As-Possible][503]
|
|
|
|
+ * [504 N-Rotationally symmetric tangent fields][504]
|
|
|
|
+ * [505 Global, seamless integer-grid parametrization][505]
|
|
|
|
+ * [506 Anisotropic remeshing using frame fields][506]
|
|
|
|
+ * [507 N-PolyVector fields][507]
|
|
|
|
+ * [508 Conjugate vector fields][508]
|
|
|
|
+ * [509 Planarization][509]
|
|
|
|
+
|
|
|
|
+* [Chapter 6: External libraries][600]
|
|
|
|
+ * [601 State serialization][601]
|
|
|
|
+ * [602 Mixing matlab code][602]
|
|
|
|
+ * [603 Calling igl functions from matlab][603]
|
|
|
|
+ * [604 Triangulation of closed polygons][604]
|
|
|
|
+ * [605 Tetrahedralization of closed surfaces][605]
|
|
|
|
+ * [606 Baking ambient occlusion][606]
|
|
|
|
+
|
|
|
|
+* [Chapter 7: Outlook for continuing development][future]
|
|
|
|
+
|
|
|
|
+# Chapter 1 [100]
|
|
|
|
+
|
|
|
|
+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 problem in geometry processing. In this chapter, we will showcase the basic concepts of libigl and introduce a simple mesh viewer that allows to easily visualize surface mesh and its attributes. All the examples are cross-platform and can be compiled on MacOSX, Linux and Windows.
|
|
|
|
+
|
|
|
|
+All dependencies for the compilation of these examples are contained in libigl (external folder), with the exception of Eigen, which should be downloaded and unpacked in the folder containing the libigl root folder.
|
|
|
|
+
|
|
|
|
+All examples depends on glfw, glew and anttweakbar. A copy
|
|
|
|
+of the sourcecode of each library is provided together with libigl
|
|
|
|
+and they can be precompiled using:
|
|
|
|
+
|
|
|
|
+```sh
|
|
|
|
+ sh compile_dependencies_macosx.sh (MACOSX)
|
|
|
|
+ sh compile_dependencies_linux.sh (LINUX)
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Precompiled binaries are provided for Visual Studio 2014 64bit.
|
|
|
|
+
|
|
|
|
+Use the cmake file in the tutorial folder to build all the examples:
|
|
|
|
+
|
|
|
|
+```sh
|
|
|
|
+ cd tutorial
|
|
|
|
+ mkdir build
|
|
|
|
+ cd build
|
|
|
|
+ cmake ../
|
|
|
|
+ make
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+For a few examples in Chapter 5, the [CoMiSo solver](http://www.graphics.rwth-aachen.de/software/comiso) has to be downloaded and compiled separately.
|
|
|
|
+
|
|
|
|
+## Mesh representation [101]
|
|
|
|
+
|
|
|
|
+libIGL uses the [Eigen](http://eigen.tuxfamily.org/) library to encode vector and matrices. We will review in this tutorial many of the basic operations that Eigen supports: If you want to get an idea of what operations are supported you can take a look at the [dense](http://eigen.tuxfamily.org/dox/group__QuickRefPage.html) and [sparse](http://eigen.tuxfamily.org/dox/group__SparseQuickRefPage.html) quick reference guides.
|
|
|
|
+
|
|
|
|
+We encode a triangular mesh as a pair of matrices:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+Eigen::MatrixXd V;
|
|
|
|
+Eigen::MatrixXi F;
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+**V** is a #N by 3 matrix which stores the coordinates of the vertices. Each row stores the coordinate of a vertex, with the x,y,z coordinates in the first, second and third column respectively. The matrix **F** stores the triangle connectivity: each line of **F** denotes a triangle whose 3 vertices are represented as indices pointing to vertex coordinates in **F**.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Note that the order of the vertex indices in F determines the orientation of the triangles and it should be consistent for the entire surface. As we will see later, additional properties of the mesh will be similarly stored as matrices. This simple representation has many advantages:
|
|
|
|
+
|
|
|
|
+* it is memory efficient and cache friendly
|
|
|
|
+* the use of indices instead of pointers greatly simplifies debuggind
|
|
|
|
+* the data can be trivially read/written on disk
|
|
|
|
+
|
|
|
|
+libIGL provides Input/Output functions to read and write common mesh formats.
|
|
|
|
+The reading/writing functions are named read\*.h and write\*.h, respectively.
|
|
|
|
+
|
|
|
|
+Reading a mesh from file requires a single igl function call:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+igl::readOFF("../shared/cube.off", V, F);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The functions read the mesh cube.off and fills the provided matrices V and F.
|
|
|
|
+Similarly, to write a mesh to file (in OBJ format):
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+igl::writeOBJ("cube.obj",V,F);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+See [Example 101](101_FileIO/main.cpp) for the source code of a simple mesh converter from OFF to OBJ format.
|
|
|
|
+
|
|
|
|
+## Plotting surfaces [102]
|
|
|
|
+
|
|
|
|
+libigl contains an OpenGL viewer that can visualize surface and their properties.
|
|
|
|
+
|
|
|
|
+The following code ([Example 102](102_DrawMesh/main.cpp)) is a basic skeleton that will be used over the entire tutorial. It is a standalone application that loads a mesh and visualize it.
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+#include <igl/readOFF.h>
|
|
|
|
+#include <igl/viewer/Viewer.h>
|
|
|
|
+
|
|
|
|
+Eigen::MatrixXd V;
|
|
|
|
+Eigen::MatrixXi F;
|
|
|
|
+
|
|
|
|
+int main(int argc, char *argv[])
|
|
|
|
+{
|
|
|
|
+ // Load a mesh in OFF format
|
|
|
|
+ igl::readOFF("../shared/bunny.off", V, F);
|
|
|
|
+
|
|
|
|
+ // Plot the mesh
|
|
|
|
+ igl::Viewer viewer;
|
|
|
|
+ viewer.set_mesh(V, F);
|
|
|
|
+ viewer.launch();
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The function set_mesh assigns to the viewer the mesh that we want to plot, and the last line creates an opengl context and starts the draw loop. Additional properties can be plotted on the mesh, and it is also possible to extend the viewer with standard OpenGL code. Please see the documentation in [Viewer.h](../include/igl/Viewer/Viewer.h) for more details.
|
|
|
|
+
|
|
|
|
+) loads and draws a mesh.](images/102_DrawMesh.png)
|
|
|
|
+
|
|
|
|
+## Interaction with keyboard and mouse [103]
|
|
|
|
+
|
|
|
|
+Keyboard and mouse events triggers callbacks that can be registered in the viewer. The viewer supports the following callbacks:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+bool (*callback_pre_draw)(Viewer& viewer);
|
|
|
|
+bool (*callback_post_draw)(Viewer& viewer);
|
|
|
|
+bool (*callback_mouse_down)(Viewer& viewer, int button, int modifier);
|
|
|
|
+bool (*callback_mouse_up)(Viewer& viewer, int button, int modifier);
|
|
|
|
+bool (*callback_mouse_move)(Viewer& viewer, int mouse_x, int mouse_y);
|
|
|
|
+bool (*callback_mouse_scroll)(Viewer& viewer, float delta_y);
|
|
|
|
+bool (*callback_key_down)(Viewer& viewer, unsigned char key, int modifiers);
|
|
|
|
+bool (*callback_key_up)(Viewer& viewer, unsigned char key, int modifiers);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+A keyboard callback can be used to visualize multiple meshes or different stages of an algorithm, as demonstrated in [Example 103](103_Events/main.cpp). The keyboard callback changes the visualized mesh depending on the key pressed:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+bool key_down(igl::Viewer& viewer, unsigned char key, int modifier)
|
|
|
|
+{
|
|
|
|
+ if (key == '1')
|
|
|
|
+ {
|
|
|
|
+ viewer.clear_mesh();
|
|
|
|
+ viewer.set_mesh(V1, F1);
|
|
|
|
+ }
|
|
|
|
+ else if (key == '2')
|
|
|
|
+ {
|
|
|
|
+ viewer.clear_mesh();
|
|
|
|
+ viewer.set_mesh(V2, F2);
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+and it is registered in the viewer as follows:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.callback_key_down = &key_down;
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Note that the mesh is cleared before using set_mesh. This has to be called every time the number of vertices or faces of the plotted mesh changes. Every callback returns a boolean value that tells the viewer if the event has been handled by the plugin, or if the viewer should process it normally. This is useful, for example, to disable the default mouse event handling if you want to control the camera directly in your code.
|
|
|
|
+
|
|
|
|
+The viewer can be extended using plugins, which are classes that implements all the viewer's callbacks. See the class Viewer_plugin for more details.
|
|
|
|
+
|
|
|
|
+## Scalar field visualization [104]
|
|
|
|
+
|
|
|
|
+Colors and normals can be associated to both faces or normals using the set_colors function:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.set_colors(C);
|
|
|
|
+```
|
|
|
|
+**C** is a #C by 3 matrix with one RGB color per row, and as many rows as the number of faces **or** the number of vertices. Depending on the size of **C**, the viewer applies the color to faces or vertices.
|
|
|
|
+
|
|
|
|
+Colors are commonly used to visualize scalar functions defined on a surface using a transfer functions, that maps a scalar value between 0 and 1 to a color scale. A simple example of a scalar field defined on a surface is the z coordinate of each point. We can extract this information from our mesh by taking the first column of V (which contains the stacked z coordiantes of all the vertices), and map it to colors using the igl::jet function:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+Eigen::VectorXd x = V.col(2);
|
|
|
|
+igl::jet(x,true,C);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The first row extracts the third column from V and the second calls the 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 color scale.
|
|
|
|
+
|
|
|
|
+) igl::jet converts a scalar field to a color field.](images/104_Colors.png)
|
|
|
|
+
|
|
|
|
+## Overlays [105]
|
|
|
|
+
|
|
|
|
+In addition to the surface, the viewer supports the visualization of points, lines and text label that can be very helful while developing geometric processing algorithms. These additional informations can be drawn using the following functions:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.add_points(P,Eigen::RowVector3d(r,g,b));
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Draws a point of color r,g,b for each row of P at the coordinates specified in each row of P, which is a #P by 3 matrix.
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.add_edges(P1,P2,Eigen::RowVector3d(r,g,b);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Draws a line for each line of P1 and P2, which connects the point in P1 to the point in P2.
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.add_label(p,str);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Draws a label containing the string str at the position p.
|
|
|
|
+
|
|
|
|
+These functions are demonstrate in [Example 105](105_Overlays/main.cpp) where the bounding box of the mesh is plotted using lines and points. The bounding box of a mesh can be found using Eigen:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+Eigen::Vector3d m = V.colwise().minCoeff();
|
|
|
|
+Eigen::Vector3d M = V.colwise().maxCoeff();
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+) The bounding box of a mesh is shown using overlays.](images/105_Overlays.png)
|
|
|
|
+
|
|
|
|
+Using matrices to encode the mesh and its attributes allows to write short and efficient code for many operations, avoiding to write for loops.
|
|
|
|
+
|
|
|
|
+## Picking [106]
|
|
|
|
+
|
|
|
|
+Picking vertices and faces using the mouse is very common in geometry processing applications. While this might seem a simple operation, its implementation is quite involved. libigl contains a function that solves this problem using the [Embree](https://software.intel.com/en-us/articles/embree-photo-realistic-ray-tracing-kernels) raycaster. Its usage is demonstrated in [Example 106](106_Picking/main.cpp):
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+bool hit = igl::unproject_in_mesh(
|
|
|
|
+ Vector2f(x,y),
|
|
|
|
+ F,
|
|
|
|
+ viewer.view * viewer.model,
|
|
|
|
+ viewer.proj,
|
|
|
|
+ viewer.viewport,
|
|
|
|
+ *ei,
|
|
|
|
+ fid,
|
|
|
|
+ vid);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+This function casts a ray from the view plane in the view direction. x,y are the position of the mouse on screen; view,model,proj are the view, model and projection matrix respectively, viewport is the viewport in opengl format; ei contains a [Bounding Volume Hierarchy](http://en.wikipedia.org/wiki/Bounding_volume_hierarchy) constructed by Embree, and fid and vid are the picked face and vertex respectively.
|
|
|
|
+
|
|
|
|
+This function is a good example of the design principles in libigl: the function takes very simple types, mostly matrix or vectors, and can be easily reused for many different tasks.
|
|
|
|
+Not committing to heavy data structures, favors simplicity, ease of use and reusability.
|
|
|
|
+
|
|
|
|
+# libigl design choices [107]
|
|
|
|
+
|
|
|
|
+To conclude the introduction, we summarize the main design principles in libigl:
|
|
|
|
+
|
|
|
|
+* No complex data types. Mostly matrices and vectors. This greatly favors code reusability and forces the 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.
|
|
|
|
+
|
|
|
|
+* Header-only: it is straighforward 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](../build/))
|
|
|
|
+
|
|
|
|
+) Picking via ray casting. The selected vertices are colored in red.](images/106_Picking.png)
|
|
|
|
+
|
|
# Chapter 2: Discrete Geometric Quantities and Operators
|
|
# Chapter 2: Discrete Geometric Quantities and Operators
|
|
This chapter illustrates a few discrete quantities that libigl can compute on a
|
|
This chapter illustrates a few discrete quantities that libigl can compute on a
|
|
mesh. This also provides an introduction to basic drawing and coloring routines
|
|
mesh. This also provides an introduction to basic drawing and coloring routines
|
|
@@ -792,13 +1046,13 @@ deformation.
|
|
### Biharmonic deformation fields
|
|
### Biharmonic deformation fields
|
|
Now we know that one useful property for a deformation technique is "rest pose
|
|
Now we know that one useful property for a deformation technique is "rest pose
|
|
reproduction": applying no deformation to the handles should apply no
|
|
reproduction": applying no deformation to the handles should apply no
|
|
-deformation to the shape.
|
|
|
|
|
|
+deformation to the shape.
|
|
|
|
|
|
To guarantee this by construction we can work with _deformation fields_ (ie.
|
|
To guarantee this by construction we can work with _deformation fields_ (ie.
|
|
displacements)
|
|
displacements)
|
|
-$\mathbf{d}$ rather
|
|
|
|
|
|
+$\mathbf{d}$ rather
|
|
than directly with positions $\mathbf{x}. Then the deformed positions can be
|
|
than directly with positions $\mathbf{x}. Then the deformed positions can be
|
|
-recovered as
|
|
|
|
|
|
+recovered as
|
|
|
|
|
|
$\mathbf{x}' = \mathbf{x}+\mathbf{d}.$
|
|
$\mathbf{x}' = \mathbf{x}+\mathbf{d}.$
|
|
|
|
|
|
@@ -907,7 +1161,7 @@ as a constrained optimization problem [#jacobson_2011][]. The weights enforce
|
|
smoothness by minimizing a smoothness energy: the familiar Laplacian energy:
|
|
smoothness by minimizing a smoothness energy: the familiar Laplacian energy:
|
|
|
|
|
|
$\sum\limits_{i = 0}^m \int_S (\Delta w_i)^2 dA$
|
|
$\sum\limits_{i = 0}^m \int_S (\Delta w_i)^2 dA$
|
|
-
|
|
|
|
|
|
+
|
|
subject to constraints which enforce interpolation of handle constraints:
|
|
subject to constraints which enforce interpolation of handle constraints:
|
|
|
|
|
|
$w_i(\mathbf{x}) = \begin{cases} 1 & \text{ if } \mathbf{x} \in H_i\\ 0 & \text{ otherwise }
|
|
$w_i(\mathbf{x}) = \begin{cases} 1 & \text{ if } \mathbf{x} \in H_i\\ 0 & \text{ otherwise }
|
|
@@ -925,6 +1179,446 @@ set solver or by calling out to Mosek.
|
|
mesh given a skeleton (top) and then animates a linear blend skinning
|
|
mesh given a skeleton (top) and then animates a linear blend skinning
|
|
deformation (bottom).](images/hand-bbw.jpg)
|
|
deformation (bottom).](images/hand-bbw.jpg)
|
|
|
|
|
|
|
|
+
|
|
|
|
+# Chapter 5: Parametrization [500]
|
|
|
|
+
|
|
|
|
+In computer graphics, we denote as parametrization of a surface a map from the surface to \\(\mathbf{R}^2\\). It is usually encoded by a new set of 2D coordinates for each vertex of the mesh and possibly also a new set of faces in one to one correspondence with the faces of the original surface. Note that this definition
|
|
|
|
+is the *inverse* of the classical differential geometry parametrization.
|
|
|
|
+
|
|
|
|
+A parametrization has many applications, ranging from texture mapping to surface remeshing. Many algorithms have been proposed, and they can be broadly characterized in four families:
|
|
|
|
+
|
|
|
|
+1. **Single patch, fixed boundary**: these algorithm can parametrize a disk-like part of the surface given fixed 2D positions for its boundary. These algorithms are efficient and simple, but usually produce high-distortion maps due to the strong cosntraints on the border.
|
|
|
|
+
|
|
|
|
+2. **Single patch, free border:** these algorithms allows the boundary to deform freely, reducing the distortion of the map. Care should be taken to prevent the border to self-intersect.
|
|
|
|
+
|
|
|
|
+3. **Global parametrization**: these algorithms works on meshes with arbitrary genus. They cut the mesh in multiple patches that are then possible to flatten in a 2D domain. Usually the map is discontinuous on the seams.
|
|
|
|
+
|
|
|
|
+4. **Global seamless parametrization**: similar to the global parametrization, but solved with a global solving strategy that hides the seams, making the parametrization "continuous", under specific assumptions that we will discuss later.
|
|
|
|
+
|
|
|
|
+## Harmonic parametrization [501]
|
|
|
|
+
|
|
|
|
+Harmonic parametrization is a single patch, fixed boundary parametrization algorithm that computes the 2D coordinates of the flattened mesh as two Harmonic functions.
|
|
|
|
+
|
|
|
|
+The algorithm is divided in 3 steps:
|
|
|
|
+
|
|
|
|
+* Detection of the boundary vertices
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+Eigen::VectorXi bnd;
|
|
|
|
+igl::boundary_loop(V,F,bnd);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+* Map the boundary vertices to a circle
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+Eigen::MatrixXd bnd_uv;
|
|
|
|
+igl::map_vertices_to_circle(V,bnd,bnd_uv);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+* Computation of harmonic functions for both the u and v coordinate on the plane, using the boundary positions as boundary constraints
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+igl::harmonic(V,F,bnd,bnd_uv,1,V_uv);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+bnd contains the indices of the boundary vertices, bnd_uv their position on the UV plane, and "1" denotes that we want to compute an harmonic function (2 will be for biharmonic, 3 for triharmonic, etc.). Note that each of the three functions is deisgned to be reusable in other parametrization algorithms.
|
|
|
|
+
|
|
|
|
+A UV parametrization can be visualized in the viewer using the method:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+viewer.set_uv(V_uv);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+which uses the UV coordinates to apply a procedural checkerboard texture to the mesh ([Example 501](501_HarmonicParam/main.cpp)).
|
|
|
|
+
|
|
|
|
+) Harmonic parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/501_HarmonicParam.png)
|
|
|
|
+
|
|
|
|
+###References:
|
|
|
|
+
|
|
|
|
+[Multiresolution Analysis of Arbitrary Meshes](http://research.microsoft.com/en-us/um/people/hoppe/mra.pdf),
|
|
|
|
+Matthias Eck, Tony DeRose, Tom Duchamp, Hugues Hoppe, Michael Lounsbery, Werner Stuetzle,
|
|
|
|
+SIGGRAPH 2005
|
|
|
|
+
|
|
|
|
+## Least-Square Conformal Maps [502]
|
|
|
|
+
|
|
|
|
+Least-square conformal maps parametrization minimizes the conformal (angular) distortion of the generated parametrization. If does not need to have a fixed boundary.
|
|
|
|
+
|
|
|
|
+LSCM minimizes the following energy:
|
|
|
|
+
|
|
|
|
+\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \int_X \frac{1}{2}| \nabla \mathbf{u}^{\perp} - \nabla \mathbf{v} |^2 dA \\]
|
|
|
|
+
|
|
|
|
+which can be rewritten in matrix form as:
|
|
|
|
+
|
|
|
|
+\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \frac{1}{2} [\mathbf{u},\mathbf{v}]^t (L_c - 2A) [\mathbf{u},\mathbf{v}] \\]
|
|
|
|
+
|
|
|
|
+where L_c is the cotangent laplacian matrix and A is a matrix such that \\( [\mathbf{u},\mathbf{v}]^t A [\mathbf{u},\mathbf{v}] \\) is equal to the _vector area_ of the mesh.
|
|
|
|
+
|
|
|
|
+Using libigl, this matrix energy can be written using a few lines of codes. The cotangent matrix can be computed using igl::cotmatrix:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+SparseMatrix<double> L;
|
|
|
|
+igl::cotmatrix(V,F,L);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Note that we want to apply the laplacian matrix to the u and v coordinates at the same time, thus we need to extend the laplacian matrix taking the left Kronecker product with a 2x2 identity matrix:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+SparseMatrix<double> L_flat;
|
|
|
|
+repdiag(L,2,L_flat);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The area matrix is computed with igl::vector_area_matrix:
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+SparseMatrix<double> A;
|
|
|
|
+igl::vector_area_matrix(F,A);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The final energy matrix is the sum of these two matrices. Note that in this case we don't need to fix the boundary, we only need to fix two arbitrary vertices to arbitrary positions to remove the null space of the energy and make the minimum unique. The full source code is provided in [Example 502](502_LSCMParam/main.cpp).
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+) LSCM parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/502_LSCMParam.png)
|
|
|
|
+
|
|
|
|
+####References:
|
|
|
|
+
|
|
|
|
+[Least Squares Conformal Maps, for Automatic Texture Atlas Generation,](http://www.cs.jhu.edu/~misha/Fall09/Levy02.pdf)
|
|
|
|
+Bruno Lévy, Sylvain Petitjean, Nicolas Ray, Jérome Maillot,
|
|
|
|
+SIGGRAPH 2002
|
|
|
|
+
|
|
|
|
+[Spectral Conformal Parameterization](http://www.geometry.caltech.edu/pubs/MTAD08.pdf),
|
|
|
|
+Patrick Mullen, Yiying Tong, Pierre Alliez, Mathieu Desbrun,
|
|
|
|
+CGF 2008
|
|
|
|
+
|
|
|
|
+## As-Rigid-As-Possible parametrization [503]
|
|
|
|
+
|
|
|
|
+As-Rigid-As-Possible parametrizationis 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 original shape, up to a rigid 2x2 rotation.
|
|
|
|
+
|
|
|
|
+The algorithm can be implemented reusing the functions discuss in the deformation chapter arap_precomputation and ara_solve. The only difference is that the optimization has to be done in 2D instead of 3D and that a starting point for the non-linear optimization is necessary. While for 3D deformation the original mesh is a perfect starting point, this is not the case for ARAP parametrization since the starting point must be a 2D mesh. In [Example 503](503_ARAPParam/main.cpp), we use Harmonic parametrization as a starting point for the ARAP parametrization: note that similarly to LSCM, the boundary is free to deform to minimize the distortion.
|
|
|
|
+
|
|
|
|
+) As-Rigid-As-Possible parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/503_ARAPParam.png)
|
|
|
|
+
|
|
|
|
+### References
|
|
|
|
+[A Local/Global Approach to Mesh Parameterization](http://cs.harvard.edu/~sjg/papers/arap.pdf)
|
|
|
|
+Ligang Liu, Lei Zhang, Yin Xu, Craig Gotsman, Steven J. Gortler
|
|
|
|
+SGP 2008
|
|
|
|
+
|
|
|
|
+## N-Rotationally symmetric tangent fields [504]
|
|
|
|
+
|
|
|
|
+The design of tangent fields is a basic tool used to design guidance fields for uniform quadrilateral and hexaedral remeshing. libigl contains an implementation of all the state- of-the-art to design algorithms for N-RoSy fields and their generalizations.
|
|
|
|
+
|
|
|
|
+In libigl, tangent unit-length vector fields are piece-wise constant on the faces of a triangle mesh, and described by one or more vectors per-face. The function
|
|
|
|
+
|
|
|
|
+```cpp
|
|
|
|
+igl::nrosy(V,F,b,bc,b_soft,b_soft_weight,bc_soft,N,0.5,
|
|
|
|
+ output_field,output_singularities);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+creates a smooth vector field (N=1) starting from a sparse set of constrained faces, whose indices are listed in b and their constrained value is specified in bc. The functions supports soft_constraints (b_soft,b_soft_weight,bc_soft), and returns the interpolated field for each face of the triangle mesh (output_field) plus the singularities of the field (output_singularities).
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+The singularities are vertices where the field vanishes, and they are highlighted in red. igl::nrosy can generate N-Rotation Symmetric fields, 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 fields generated with different N are in different positions and of a different kind.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+We demonstrate how to call and plot N-RoSy fields in [Example 504](504_NRosyDesign/main.cpp), where the degree of the field can be controlled by pressing the number keys.
|
|
|
|
+
|
|
|
|
+### References
|
|
|
|
+
|
|
|
|
+[N-Symmetry Direction Field Design](http://alice.loria.fr/publications/papers/2008/DGF/NSDFD-TOG.pdf),
|
|
|
|
+Nicolas Ray, Bruno Vallet, Wan Chiu Li, Bruno Lévy
|
|
|
|
+TOG 2008
|
|
|
|
+
|
|
|
|
+[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
|
|
|
|
+David Bommes, Henrik Zimmer, Leif Kobbelt
|
|
|
|
+SIGGRAPH 2009
|
|
|
|
+
|
|
|
|
+#Global, seamless integer grid parametrization
|
|
|
|
+
|
|
|
|
+The previous parametrization methods where focusing on generating parametrization of single patches, mainly aimed at texture mapping and baking of other surface properties like normals 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 remeshing the surface. In libigl, we provide a reference implementation of the pipeline of the [MIQ](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf) paper.
|
|
|
|
+
|
|
|
|
+### Global, seamless integer-grid parametrization [505]
|
|
|
|
+
|
|
|
|
+The first step involves the design of a 4-RoSy field (sometimes called cross field) that describes how the edges of the final quad remeshing should align. The field constraints are usually manually specified or extracted from curvature. In this example, we simply fix one face in a random direction.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+### Combing and cutting
|
|
|
|
+
|
|
|
|
+Given the cross field, we now want to cut the surface so that it becomes homeorphic to a disk. While this can be done directly on the cross-field, we prefer to do this operation on its bisector field (a copy of the field rotated by 45 degrees) since it is more stable and generic.
|
|
|
|
+
|
|
|
|
+We thus rotate the field,
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+and we remove the rotation ambiguity by assigning to each face a u and v direction, computed by diffusing this alignment from a random face.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+You can imagine this process as combing an hairy surface: you'll be able to comb part of it, but at some point you will not be able to comb consistently the full surface ([Hairy ball theorem](http://en.wikipedia.org/wiki/Hairy_ball_theorem)). The discontinuites in the combing defines the cut graph:
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Finally, we rotate the combed field by 45 degrees to undo the initial 45 degrees rotation:
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+This cross field can be seen as the ideal gradient of the parametrization that we want to compute.
|
|
|
|
+
|
|
|
|
+### Poisson parametrization
|
|
|
|
+
|
|
|
|
+The mesh can be then cut along the seams and a parametrization is computed trying to find two scalar functions whose gradient matches the combed cross field. This is a classical Poisson problem, that is solved minimizing the following quadratic energy:
|
|
|
|
+
|
|
|
|
+\\[ E(\mathbf{u},\mathbf{v}) = |\nabla \mathbf{u} - X_u|^2 + |\nabla \mathbf{v} - X_v|^2 \\]
|
|
|
|
+
|
|
|
|
+where \\( X_u \\) and \\( X_u \\) denotes the combed cross field. Solving this problem generates a parametrization whose u and v isolines are aligned with the input cross field.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+We hide the seams by adding a set of integer constraints to the Poisson problem that aligns the isolines on both sides of each seam.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Note that this parametrization can only be used for remeshing purposes, since it contains many overlaps.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+A quad mesh can be extracted from this parametrization using
|
|
|
|
+[libQEx](https://github.com/hcebke/libQEx) (not included in libigl).
|
|
|
|
+
|
|
|
|
+The full pipeline is demonstrated in [Example 505](505_MIQ/main.cpp).
|
|
|
|
+
|
|
|
|
+### References
|
|
|
|
+
|
|
|
|
+[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
|
|
|
|
+David Bommes, Henrik Zimmer, Leif Kobbelt
|
|
|
|
+SIGGRAPH 2009
|
|
|
|
+
|
|
|
|
+## Anisotropic remeshing [506]
|
|
|
|
+
|
|
|
|
+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.
|
|
|
|
+
|
|
|
|
+The input of the remeshing algorithm is now a sparse set of constraints that defines the shape and scale of the desired quad remeshing. This can be encoded as a frame-field, which is a pair of non-orthogonal and non-unit lenght vectors. The frame field can be interpolated by decomposing it in a 4-RoSy field and a unique affine transformation. The two parts can then be interpolated separately, using igl::nrosy for the cross field, and an harmonic interpolant for the affine part.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+After the interpolation, the surface is warped to transform each frame into an orthogonal and unit lenght cross (i.e. removing the scaling and skewness from the frame). This deformation defines a new embedding (and a new metric) for the surface.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+The deformed surface can the be isotropically remeshed using the MIQ algorithm that has been presented in the previous section.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+The UV coordinates of the deformed surface can then be used to transport the parametrization to the original surface, where the isolines will trace a quad mesh whose elements are similar to the shape prescribed in the input frame field.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Our implementation ([Example 506](506_FrameField/main.cpp)) uses MIQ to generate the UV parametrization, but other algorithms could be applied: the only desiderata is that the generated quad mesh will be as isotropic as possible.
|
|
|
|
+
|
|
|
|
+### References
|
|
|
|
+
|
|
|
|
+[Frame Fields: Anisotropic and Non-Orthogonal Cross Fields],
|
|
|
|
+Daniele Panozzo, Enrico Puppo, Marco Tarini, Olga Sorkine-Hornung,
|
|
|
|
+SIGGRAPH, 2014
|
|
|
|
+
|
|
|
|
+## N-PolyVector fields [507]
|
|
|
|
+
|
|
|
|
+* further generalization to arbitrary rosy, same interface
|
|
|
|
+
|
|
|
|
+* globally optimal and keenan optimal field are a subset of them
|
|
|
|
+
|
|
|
|
+## Conjugate vector fields [508]
|
|
|
|
+
|
|
|
|
+* they can be used to encode conjugate field -> planar meshing
|
|
|
|
+
|
|
|
|
+* global/local approach
|
|
|
|
+
|
|
|
|
+## Planarization [509]
|
|
|
|
+
|
|
|
|
+* given a mesh from conjugate directions, enforce planarity with a local/global approach
|
|
|
|
+* useful for architecture
|
|
|
|
+
|
|
|
|
+# Chapter 6: External libraries [600]
|
|
|
|
+
|
|
|
|
+An additional positive side effect of using matrices as basic types is that it is easy to exchange data between libigl and other softwares and libraries.
|
|
|
|
+
|
|
|
|
+## State serialization [601]
|
|
|
|
+
|
|
|
|
+Geometry processing applications often require a considerable amount of computational time and/or manual input. In order to make the development efficient it must be possible to serialize and deserialize the state of the application.
|
|
|
|
+
|
|
|
|
+Having a good serialization framework allows to quickly start debugging just before the crash happens, avoiding to wait for the precomputation to take place every time. It also makes it easier to define unit testing that can be used to find bugs in interactive applications: if the input is slightly different every time the algorithm is executed, it is very difficult to find bugs.
|
|
|
|
+
|
|
|
|
+Unfortunately, serialization is often not considered in geoemtry processing due to the extreme difficulty in serializing pointer-based data structures (like an helf-edge).
|
|
|
|
+
|
|
|
|
+In libigl, serialization is simpler, since the majority of the functions use basic types, and pointers are used in very rare cases (usually to interface with external libraries). libigl provides an extremely easy to use XML serialization framework, that drastically reduces the overhead required to add serialization to your applications.
|
|
|
|
+
|
|
|
|
+Assume that the state of your application is composed of a mesh and set of integer ids:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+class State : public ::igl::XMLSerialization
|
|
|
|
+{
|
|
|
|
+public:
|
|
|
|
+ State() : XMLSerialization("dummy") {}
|
|
|
|
+
|
|
|
|
+ Eigen::MatrixXd V;
|
|
|
|
+ Eigen::MatrixXi F;
|
|
|
|
+ std::vector<int> ids;
|
|
|
|
+
|
|
|
|
+ void InitSerialization()
|
|
|
|
+ {
|
|
|
|
+ xmlSerializer->Add(V , "V");
|
|
|
|
+ xmlSerializer->Add(F , "F");
|
|
|
|
+ xmlSerializer->Add(ids, "ids");
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+A class can be made serializable by inheriting from ::igl::XMLSerialization and trivially implementing the InitSerialization method. Note that you don't have to care the types, Add is able to serialize all basic stl types, all Eigen types and any class inheriting from ::igl::XMLSerialization.
|
|
|
|
+
|
|
|
|
+It is then possible to save the state to an xml file:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+::igl::XMLSerializer serializer_save("601_Serialization");
|
|
|
|
+serializer_save.Add(state,"State");
|
|
|
|
+serializer_save.Save("temp.xml",true);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+This code generates the following xml file (assuming V and F contains a simple mesh with two triangles, and ids contains the numbers 6 and 7):
|
|
|
|
+
|
|
|
|
+``` xml
|
|
|
|
+<:::601_Serialization>
|
|
|
|
+ <State>
|
|
|
|
+ <V rows="4" cols="3" matrix="
|
|
|
|
+0,0,0,
|
|
|
|
+1,0,0,
|
|
|
|
+1,1,1,
|
|
|
|
+2,1,0"/>
|
|
|
|
+ <F rows="2" cols="3" matrix="
|
|
|
|
+0,1,2,
|
|
|
|
+1,3,2"/>
|
|
|
|
+ <ids size="2" vector_int="
|
|
|
|
+6,7"/>
|
|
|
|
+ </State>
|
|
|
|
+</:::601_Serialization>
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The xml file can then be loaded in a similar way:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+State loaded_state;
|
|
|
|
+::igl::XMLSerializer serializer_load("601_Serialization");
|
|
|
|
+serializer_load.Add(loaded_state,"State");
|
|
|
|
+serializer_load.Load("temp.xml");
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+This can also be used as a convenient interface to provide parameters to command line applications, since the xml files can be directly edited with a standard text editor.
|
|
|
|
+
|
|
|
|
+We demonstrate the serialization framework in [Example 601](601_Serialization/main.cpp). We strongly suggest that you make the entire state of your application always serializable: this will save you a lot of troubles when you'll be making figures for a scientific publication. It is very common to have to do small changes to figures during the production of a paper, and being able to serialize the entire state just before you take screenshots will save you many painful hours before a submission deadline.
|
|
|
|
+
|
|
|
|
+## Mixing matlab code [602]
|
|
|
|
+
|
|
|
|
+libigl can be interfaced matlab, to offload some of the numerically heavy computation to a matlab script. This has the major advantage of allowing to develop efficient and complex UI in C++, while keeping the advantage of fast protototyping of matlab. In particular, using an external matlab script in a libigl application allows to change the algorithm in the matlab script without having to recompile the C++ part.
|
|
|
|
+
|
|
|
|
+We demonstrate how to integrate matlab in a libigl application in [Example 602](602_Matlab/main.cpp). The example uses matlab to compute the Eigenfunctions of the discrete Laplacian operator, relying on libigl for mesh IO, visualization and for computing the Laplacian operator.
|
|
|
|
+
|
|
|
|
+libigl can connect to an existing instance of matlab (or launching a new one on Linux/MacOSX) using:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::mlinit(&engine);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+The cotangent laplacian is computed using igl::cotmatrix and uploaded to the matlab workspace:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::cotmatrix(V,F,L);
|
|
|
|
+igl::mlsetmatrix(&engine,"L",L);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+It is now possible to use any matlab function on the data. For example, we can see the sparsity pattern of L using spy:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::mleval(&engine,"spy(L)");
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+You can also do some computation and then return it back to the C++ application
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::mleval(&engine,"[EV,~] = eigs(-L,10,'sm')");
|
|
|
|
+igl::mlgetmatrix(&engine,"EV",EV);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+and then use libigl functions to plot the eigenfunctions.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+## Calling igl functions from matlab [603]
|
|
|
|
+
|
|
|
|
+It is also possible to call libigl functions from matlab, compiling them as MEX functions. This can be very useful to offload to C++ code the computationally intensive parts of a matlab application.
|
|
|
|
+
|
|
|
|
+We provide a wrapper for igl::readOBJ in [Example 603](603_MEX/compileMEX.m). 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.
|
|
|
|
+
|
|
|
|
+## Triangulation of closed polygons [604]
|
|
|
|
+
|
|
|
|
+The generation of high-quality triangle and tetrahedral meshes is a very common task in geometry processing. We provide wrappers in libigl to triangle and tetegen.
|
|
|
|
+
|
|
|
|
+A triangle mesh canb e cerated starting from a set of boundary edges using igl::triangulate.
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::triangulate(V,E,H,V2,F2,"a0.005q");
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+where E is a set of boundary edges, H a set of 2D positions of points contained in holes of the triangulation and (V2,F2) is the generate triangulation. Additional parameters can be given to triangles, to control the quality: "a0.005q" puts a bound on the maximal area of the triangles and a minimal angle of 20 degrees. In Example [Example 604](604_Triangle/main.m), the interior of a square (excluded a smaller square in its interior) is triangulated.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+## Tetrahedralization of closed surfaces [605]
|
|
|
|
+
|
|
|
|
+Similarly, the interior of a closed manifold surface can be tetrahedralized using the function igl::tetrahedralize which wraps the tetgen library ([Example 605](605_Tetgen/main.c)):
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::tetrahedralize(V,F,"pq1.414", TV,TT,TF);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+## Baking ambient occlusion [606]
|
|
|
|
+
|
|
|
|
+[Ambient occlusion](http://en.wikipedia.org/wiki/Ambient_occlusion) is a rendering technique used to calculate the exposure of each point in a surface to ambient lighting. It is usually encoded as a scalar (normalized between 0 and 1) associated with the vertice of a mesh.
|
|
|
|
+
|
|
|
|
+Formally, ambient occlusion is defined as:
|
|
|
|
+
|
|
|
|
+\\[ A_p = \frac{1}{\pi} \int_\omega V_{p,\omega}(n \cdot \omega) d\omega \\]
|
|
|
|
+
|
|
|
|
+where \\( V_{p,\omega} \\) is the visibility function at p, defined to be zero if p is occluded in the direction \\( \omega \\) and one otherwise, and \\( d\omega \\) is the infinitesimal solid angle step of the integration variable \\( \omega \\).
|
|
|
|
+
|
|
|
|
+The integral is usually approximate by casting rays in random directions around each vertex. This approximation can be computed using the function:
|
|
|
|
+
|
|
|
|
+``` cpp
|
|
|
|
+igl::ambient_occlusion(V,F,V_samples,N_samples,500,AO);
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+that given a scene described in V,F, computes the ambient occlusion of the points in V_samples whose associated normals are N_samples. The number of casted rays can be controlled (usually at least 400-500 rays are required to get a smooth result) and the result is return in AO, as a single scalar for each sample.
|
|
|
|
+
|
|
|
|
+Ambient occlusion can be used to darken the surface colors, as shown in [Example 606](606_AmbientOcclusion/main.c)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+# Outlook for continuing development [future]
|
|
|
|
+
|
|
|
|
+* better documentation
|
|
|
|
+* your contributions are welcome, using pull request
|
|
|
|
+* open things to do
|
|
|
|
+ * isotropic remeshing
|
|
|
|
+ * matlab wrappers
|
|
|
|
+ * mixed integer solvers
|
|
|
|
+ * fast spatial indices
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
[#botsch_2004]: Matrio Botsch and Leif Kobbelt. "An Intuitive Framework for
|
|
[#botsch_2004]: Matrio Botsch and Leif Kobbelt. "An Intuitive Framework for
|
|
Real-Time Freeform Modeling," 2004.
|
|
Real-Time Freeform Modeling," 2004.
|
|
[#jacobson_thesis_2013]: Alec Jacobson,
|
|
[#jacobson_thesis_2013]: Alec Jacobson,
|