Merge branch 'master' of github.com:libigl/libigl into alecjacobson
This commit is contained in:
+111
-159
@@ -75,17 +75,12 @@ lecture notes links to a cross-platform example application.
|
||||
* [504 N-Rotationally symmetric tangent fields](#nrotationallysymmetrictangetfields)
|
||||
* [505 Global, seamless integer-grid parametrization](#globalseamlessintegergridparametrization)
|
||||
* [506 Anisotropic remeshing using frame fields](#anisotropicremeshingusingframefields)
|
||||
* [507 N-PolyVector fields](#npolyvectorfields)
|
||||
* [508 Conjugate vector fields](#conjugatevectorfields)
|
||||
* [509 Planarization](#planarization)
|
||||
* [510 Integrable PolyVector Fields](#integrable)
|
||||
* [511 General N-PolyVector Fields](#npolyvectorfields_general)
|
||||
* [507 Planarization](#planarization)
|
||||
* [Chapter 6: External libraries](#chapter6:externallibraries)
|
||||
* [601 State serialization](#stateserialization)
|
||||
* [602 Mixing Matlab code](#mixingmatlabcode)
|
||||
* [Saving a Matlab workspace](#savingamatlabworkspace)
|
||||
* [Dumping Eigen matrices to copy and paste into
|
||||
Matlab](#dumpingeigenmatricestocopyandpasteintomatlab)
|
||||
* [Dumping Eigen matrices to copy and paste into Matlab](#dumpingeigenmatricestocopyandpasteintomatlab)
|
||||
* [603 Calling libigl functions from Matlab](#callinglibiglfunctionsfrommatlab)
|
||||
* [604 Triangulation of closed polygons](#triangulationofclosedpolygons)
|
||||
* [605 Tetrahedralization of closed surfaces](#tetrahedralizationofclosedsurfaces)
|
||||
@@ -107,6 +102,7 @@ lecture notes links to a cross-platform example application.
|
||||
* [710 Scalable Locally Injective Maps](#slim)
|
||||
* [711 Subdivision surfaces](#subdivision)
|
||||
* [712 Data smoothing](#datasmoothing)
|
||||
* [713 ShapeUp projection](#shapeup)
|
||||
* [Chapter 8: Outlook for continuing development](#future)
|
||||
|
||||
# Chapter 1 [chapter1:introductiontolibigl]
|
||||
@@ -291,7 +287,7 @@ The function `set_mesh` copies the mesh into the viewer.
|
||||
Additional properties can be plotted on the mesh (as we will see later),
|
||||
and it is possible to extend the viewer with standard OpenGL code.
|
||||
Please see the documentation in
|
||||
[Viewer.h](../include/igl/Viewer/Viewer.h) for more details.
|
||||
[Viewer.h](../include/igl/opengl/glfw/Viewer.h) for more details.
|
||||
|
||||
) loads and draws a
|
||||
mesh.](images/102_DrawMesh.png)
|
||||
@@ -427,59 +423,87 @@ using overlays.](images/105_Overlays.png)
|
||||
|
||||
## [Viewer Menu](#viewermenu) [viewermenu]
|
||||
|
||||
As of version 1.2 the viewer uses a new menu and completely replaces
|
||||
[AntTweakBar](http://anttweakbar.sourceforge.net/doc/). It is based on the
|
||||
open-source projects [nanovg](https://github.com/memononen/nanovg) and
|
||||
[nanogui](https://github.com/wjakob/nanogui). To extend the default menu of the
|
||||
viewer and to expose more user defined variables you have to define a callback
|
||||
function:
|
||||
|
||||
As of latest version, the viewer uses a new menu and completely replaces
|
||||
[AntTweakBar](http://anttweakbar.sourceforge.net/doc/) and
|
||||
[nanogui](https://github.com/wjakob/nanogui) with [Dear ImGui](https://github.com/ocornut/imgui). To extend the default menu of the
|
||||
viewer and to expose more user defined variables you have to implement a custom interface, as in [Example 106](106_ViewerMenu/main.cpp):
|
||||
```cpp
|
||||
igl::opengl::glfw::Viewer viewer;
|
||||
|
||||
bool boolVariable = true;
|
||||
float floatVariable = 0.1f;
|
||||
enum Orientation { Up=0,Down,Left,Right } dir = Up;
|
||||
|
||||
// Extend viewer menu
|
||||
viewer.callback_init = [&](igl::opengl::glfw::Viewer& viewer)
|
||||
// Add content to the default menu window
|
||||
menu.callback_draw_viewer_menu = [&]()
|
||||
{
|
||||
// Draw parent menu content
|
||||
menu.draw_viewer_menu();
|
||||
|
||||
// Add new group
|
||||
viewer.ngui->addGroup("New Group");
|
||||
if (ImGui::CollapsingHeader("New Group", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
// Expose variable directly ...
|
||||
ImGui::InputFloat("float", &floatVariable, 0, 0, 3);
|
||||
|
||||
// Expose a variable directly ...
|
||||
viewer.ngui->addVariable("float",floatVariable);
|
||||
// ... or using a custom callback
|
||||
static bool boolVariable = true;
|
||||
if (ImGui::Checkbox("bool", &boolVariable))
|
||||
{
|
||||
// do something
|
||||
std::cout << "boolVariable: " << std::boolalpha << boolVariable << std::endl;
|
||||
}
|
||||
|
||||
// Expose an enumaration type
|
||||
viewer.ngui->addVariable<Orientation>("Direction",dir)->setItems({"Up","Down","Left","Right"});
|
||||
// Expose an enumeration type
|
||||
enum Orientation { Up=0, Down, Left, Right };
|
||||
static Orientation dir = Up;
|
||||
ImGui::Combo("Direction", (int *)(&dir), "Up\0Down\0Left\0Right\0\0");
|
||||
|
||||
// Add a button
|
||||
viewer.ngui->addButton("Print Hello",[](){ std::cout << "Hello\n"; });
|
||||
// We can also use a std::vector<std::string> defined dynamically
|
||||
static int num_choices = 3;
|
||||
static std::vector<std::string> choices;
|
||||
static int idx_choice = 0;
|
||||
if (ImGui::InputInt("Num letters", &num_choices))
|
||||
{
|
||||
num_choices = std::max(1, std::min(26, num_choices));
|
||||
}
|
||||
if (num_choices != (int) choices.size())
|
||||
{
|
||||
choices.resize(num_choices);
|
||||
for (int i = 0; i < num_choices; ++i)
|
||||
choices[i] = std::string(1, 'A' + i);
|
||||
if (idx_choice >= num_choices)
|
||||
idx_choice = num_choices - 1;
|
||||
}
|
||||
ImGui::Combo("Letter", &idx_choice, choices);
|
||||
|
||||
// call to generate menu
|
||||
viewer.screen->performLayout();
|
||||
return false;
|
||||
// Add a button
|
||||
if (ImGui::Button("Print Hello", ImVec2(-1,0)))
|
||||
{
|
||||
std::cout << "Hello\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// start viewer
|
||||
viewer.launch();
|
||||
```
|
||||
|
||||
If you need a separate new menu window use:
|
||||
If you need a separate new menu window implement:
|
||||
|
||||
```cpp
|
||||
viewer.ngui->addWindow(Eigen::Vector2i(220,10),"New Window");
|
||||
```
|
||||
// Draw additional windows
|
||||
menu.callback_draw_custom_window = [&]()
|
||||
{
|
||||
// Define next window position + size
|
||||
ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10), ImGuiSetCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiSetCond_FirstUseEver);
|
||||
ImGui::Begin(
|
||||
"New Window", nullptr,
|
||||
ImGuiWindowFlags_NoSavedSettings
|
||||
);
|
||||
|
||||
If you do not want to expose variables directly but rather use the get/set functionality:
|
||||
// Expose the same variable directly ...
|
||||
ImGui::PushItemWidth(-80);
|
||||
ImGui::DragFloat("float", &floatVariable, 0.0, 0.0, 3.0);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
```cpp
|
||||
// ... or using a custom callback
|
||||
viewer.ngui->addVariable<bool>("bool",[&](bool val) {
|
||||
boolVariable = val; // setter
|
||||
},[&]() {
|
||||
return boolVariable; // getter
|
||||
});
|
||||
static std::string str = "bunny";
|
||||
ImGui::InputText("Name", str);
|
||||
|
||||
ImGui::End();
|
||||
};
|
||||
```
|
||||
|
||||
) The UI of the viewer can be easily
|
||||
@@ -587,7 +611,7 @@ where $N(i)$ are the triangles incident on vertex $i$ and $θ_{ij}$ is the angle
|
||||
at vertex $i$ in triangle $j$ [][#meyer_2003].
|
||||
|
||||
Just like the continuous analog, our discrete Gaussian curvature reveals
|
||||
elliptic, hyperbolic and parabolic vertices on the domain, as demonstrated in [Example 202](202GaussianCurvature/main.cpp).
|
||||
elliptic, hyperbolic and parabolic vertices on the domain, as demonstrated in [Example 202](202_GaussianCurvature/main.cpp).
|
||||
|
||||

|
||||
@@ -1986,9 +2010,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](504_NRosyDesign/main.cpp), where the degree of the field can be change
|
||||
pressing the number keys. `igl::nrosy` implements the algorithm proposed in
|
||||
[#bommes_2009][]. N-RoSy fields can also be interpolated with the algorithm
|
||||
proposed in [#knoppel_2013][], see Section [npolyvectorfields] for more details
|
||||
([igl::n_polyvector](../include/igl/n_polyvector.h)).
|
||||
[#bommes_2009][]. N-RoSy fields can also be interpolated with many other algorithms,
|
||||
see the library [libdirectional](https://github.com/avaxman/libdirectional) for
|
||||
a reference implementation of the most popular ones. For a complete categorization
|
||||
of fields used in various applications see Vaxman et al. 2016 [#vaxman_2016].
|
||||
|
||||
|
||||
### [Global, seamless integer-grid parametrization](#globalseamlessintegergridparametrization) [globalseamlessintegergridparametrization]
|
||||
|
||||
@@ -2117,120 +2143,19 @@ 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-PolyVector fields](#npolyvectorfields) [npolyvectorfields]
|
||||
|
||||
N-RoSy vector fields can be further generalized to represent arbitrary
|
||||
vector-sets, with arbitrary angles between them and with arbitrary lengths
|
||||
[#diamanti_2014][]. 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](507_PolyVectorField/main.cpp)).
|
||||
|
||||

|
||||
|
||||
The core idea is to represent the vector set as the roots of a complex
|
||||
polynomial: The polynomial coefficients are then harmonically interpolated
|
||||
leading to polynomials whose roots smoothly vary over the surface.
|
||||
|
||||
Globally optimal direction fields [#knoppel_2013][] 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.
|
||||
|
||||
## [Conjugate vector fields](#conjugatevectorfields) [conjugatevectorfields]
|
||||
|
||||
Two tangent vectors lying on a face of a triangle mesh are conjugate if
|
||||
|
||||
\\[ k_1 (u^T d_1)(v^T d_1) + k_2(u^T d_2)(v^T d_2) = 0. \\]
|
||||
|
||||
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 [#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 [#diamanti_2014] 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](508_ConjugateField/main.cpp)).
|
||||
|
||||

|
||||
|
||||
## [Planarization](#planarization) [planarization]
|
||||
|
||||
A quad mesh can be transformed in a planar quad mesh with Shape-Up
|
||||
[#bouaziz_2012], a local/global approach that uses the global step to enforce
|
||||
surface continuity and the local step to enforce planarity.
|
||||
|
||||
[Example 509](509_Planarization/main.cpp) planarizes a quad mesh until it
|
||||
[Example 507](507_Planarization/main.cpp) planarizes a quad mesh until it
|
||||
satisfies a user-given planarity threshold.
|
||||
|
||||

|
||||
|
||||
## [Integrable PolyVector Fields](#integrable) [integrable]
|
||||
|
||||
Vector-field guided surface parameterization is based on the idea of designing
|
||||
the gradients of the parameterization functions (which are tangent vector fields
|
||||
on the surface) instead of the functions themselves. Thus, vector-set fields
|
||||
(N-Rosy, frame fields, and polyvector fields) that are to be used for
|
||||
parameterization (and subsequent remeshing) need to be integrable: it must be
|
||||
possible to break them down into individual vector fields that are gradients of
|
||||
scalar functions. Fields obtained by most smoothness-based design methods (eg.
|
||||
[#levy_2008][], [#knoppel_2013][], [#diamanti_2014][], [#bommes_2009][],
|
||||
[#panozzo_2014][]) do not have this property. In [#diamanti_2015][], a method
|
||||
for creating integrable polyvector fields was introduced. This method takes as
|
||||
input a given field and improves its integrability by removing the vector field
|
||||
curl, thus turning it into a gradient of a function ([Example
|
||||
510](510_Integrable/main.cpp)).
|
||||
|
||||

|
||||
|
||||
This method retains much of the core principles of the polyvector framework - it
|
||||
expresses the condition for zero discrete curl condition (which typically
|
||||
requires integers for the vector matchings) into a condition involving
|
||||
continuous variables only. This is done using coefficients of appropriately
|
||||
defined polynomials. The parameterizations generated by the resulting fields are
|
||||
exactly aligned to the field directions and contain no inverted triangles.
|
||||
|
||||
## [General N-PolyVector fields](#npolyvectorfields_general) [npolyvectorfields_general]
|
||||
|
||||
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 [#diamanti_2014][] 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
|
||||
pairwise-collinear vectors designed in the example ([Example
|
||||
507](507_PolyVectorField/main.cpp)). This capability is implemented in the
|
||||
function igl::n_polyvector_general, and is illustrated in the example ([Example
|
||||
511](511_PolyVectorFieldGeneral/main.cpp)).
|
||||
|
||||

|
||||
|
||||
The design of these general directional fields (also called vector-set fields)
|
||||
is based on the same polynomial framework and includes the symmetric fields as a
|
||||
special case. Note that in the case that some symmetries do exist in the
|
||||
constraints, the final field is not guaranteed to have these symmetries
|
||||
everywhere else on the mesh. For example, designing a field with 3 vectors per
|
||||
point where, at the constrained faces, two of the vectors are on a line opposite
|
||||
to each other, we are not guaranteed to always have two pairwise-collinear
|
||||
vectors everywhere in the result, as can be seen in the picture. In some cases
|
||||
however (as is the case of the frame field in the previous example [Example
|
||||
507](507_PolyVectorField/main.cpp)) these symmetries are in fact guaranteed due
|
||||
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 [#vaxman_2016].
|
||||
|
||||
# Chapter 6: External libraries [chapter6:externallibraries]
|
||||
|
||||
An additional positive side effect of using matrices as basic types is that it
|
||||
@@ -2748,6 +2673,13 @@ then the final composite.
|
||||
 computes complex CSG Tree operation on 5
|
||||
input meshes.](images/cube-sphere-cylinders-csg.gif)
|
||||
|
||||
# Miscellaneous [chapter7: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.
|
||||
|
||||
## [Mesh Statistics](#meshstatistics) [meshstatistics]
|
||||
|
||||
Libigl contains various mesh statistics, including face angles, face areas and
|
||||
@@ -3343,12 +3275,32 @@ on the beetle mesh, the function with added noise, the result of smoothing
|
||||
with the Laplacian energy and zero Neumann boundary conditions, and the
|
||||
result of smoothing with the Hessian energy.](images/712_beetles.jpg)
|
||||
|
||||
# Miscellaneous [chapter7:miscellaneous]
|
||||
## [ShapeUp Projections](#shapeup) [shapeup]
|
||||
|
||||
Our input is a set of points $P_0$ (not necessarily part of any mesh), and a set of constraints $S=\left\{S_1,S_2,...S_m\right\}$, where each constraint is defined on a different, and sparse, subset of $P_0$. We wish to create a new set of points $P$ that are close to the original set $P_0$ (each point with corresponding indices), while adhering to the constraints. Other objectives, such as smoothness, can be employed. The constraints can be nonlinear, which makes the problem nonconvex, difficult, and without a guaranteed global optimum. A very popular lightweight approach to such problems is a local-global iterative algorithm, comprising these two steps:
|
||||
|
||||
For iteration $k$:
|
||||
1. *Local step*: compute the projections of the set $P_{k-1}$ onto $S$, individually per constraint; that would mean fragmenting each point that appears in multiple constraints. That can be a nonlinear operation, but if the constraints are sparse, it is a a set of many small systems.
|
||||
2. *Global step*: integrate the set $P_k$ to be as close as possible to the projected fragmented set, with auxiliary objective functions possible. That results in a global, but quadratic objective function. Moreover, the resulting linear system has a constant matrix, and therefore can be pre-factored.
|
||||
|
||||
The version we implement in libigl is the general version described by [#bouaziz_2012], and is in two files: ``<igl/shapeup.h>`` and ``<igl/shapeup_local_projections.h>``. A demo implementing regularity constraints (creating a mesh in which each face is as regular as possible) is in [Example 713](713_Shapeup/main.cpp).
|
||||
|
||||
The local step is instantiated by a function of type ``igl::shapeup_projection_function``. The global step is done by two functions: ``igl::shapeup_precomputation()``, which precomputes the matrices required for the global step, and ``igl::shapeup_solve()``, which solves the problem, according to the initial solution $P_0$ and the input local projection function. The data struct ``igl::ShapeUpData`` contains the information necessary to run the algorithm, and can be configured; for instance, the self-explanatory variable ``Maxiterations``.
|
||||
|
||||
The global step minimizes the following energy:
|
||||
|
||||
$$E_{total}=\lambda_{shape}E_{shape}+\lambda_{close}E_{close}+\lambda_{smooth}E_{smooth},$$
|
||||
|
||||
where the $\lambda$ coefficients are encoded in ``igl::ShapeUpData``, and can be updated **prior** to calling ``igl::shapeup_precomputation()``. The $E_{shape}$ component is the integration energy (fitting $P_k$ to the local projections). The $E_{close}$ component is adherence to positional constraints, given by ``b`` and ``bc`` parameters. The $E_{smooth}$ component is an optional objective function, to minimize differences (in the Dirichlet sense) between points, encodes by "edges" in parameter `E`. Both $E_{close}$ and $E_{shape}$ are also weighted by ``wClose`` and ``wShape`` function parameters, respectively.
|
||||
|
||||
) The half-tunnel mesh (left) has been optimized to be almost perfectly regular (right). The color scale is between $[0,0.05]$, measuring the average normalized deviation of the angles of each face from $90^{\circ}$.](images/713_ShapeUp.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 [future]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user