Geometry Processing Smoothing

October 21, 2022 · View on GitHub

To get started: Fork this repository then issue

git clone --recursive http://github.com/[username]/geometry-processing-smoothing.git

Installation, Layout, and Compilation

See introduction.

Execution

Once built, you can execute the assignment from inside the build/ by running on a given mesh with given scalar field (in dmat format).

./smoothing [path to mesh.obj] [path to data.dmat]

or to load a mesh with phony noisy data use:

./smoothing [path to mesh.obj] n

or to load a mesh with smooth z-values as data (for mesh smoothing only):

./smoothing [path to mesh.obj]

Background

In this assignment we will explore how to smooth a data signal defined over a curved surface. The data signal may be a scalar field defined on a static surface: for example, noisy temperatures on the surface of an airplane. Smoothing in this context can be understood as data denoising:

The signal could also be the geometric positions of the surface itself. In this context, smoothing acts also affects the underlying geometry of the domain. We can understand this operation as surface fairing:

Flow-based formulation

In both cases, the underlying mathematics of both operations will be very similar. If we think of the signal as undergoing a flow toward a smooth solution over some phony notion of "time", then the governing partial differential equation we will start with sets the change in signal value uu over time proportional to the Laplacian of the signal Δu\Delta u (for now, roughly the second derivative of the signal as we move on the surface):

ut=λΔu,\frac{\partial u}{\partial t} = {\lambda} \Delta u,

where the scalar parameter λ{\lambda} controls the rate of smoothing.

When the signal is the surface geometry, we call this a geometric flow.

There are various ways to motivate this choice of flow for data-/geometry-smoothing. Let us consider one way that will introduce the Laplacian as a form of local averaging.

Given a noisy signal ff, intuitively we can smooth ff by averaging every value with its neighbors' values. In continuous math, we might write that the smoothed value u(x)u(\mathbf{x}) at any point times the volume of a small neighboring ball on our surface xS\mathbf{x} \in \mathbf{S} should be equal to the average value of the ball:

u(x)=1B(x))B(x)f(z),dz,u(\mathbf{x}) = \frac{1}{|B(\mathbf{x}))|} \int _{B(\mathbf{x})} f(\mathbf{z}) \\, d\mathbf{z},

If the ball B(x)B(\mathbf{x}) is small, then we will have to repeat this averaging many times to see a global smoothing effect. Hence, we can write that the current value utu^t flows toward smooth solution by small steps δt{\delta}t in time:

ut+δt(x)=1B(x))B(x)ut(z),dz.u^{t+{\delta}t}(\mathbf{x}) = \frac{1}{|B(\mathbf{x}))|} \int _{B(\mathbf{x})} u^t(\mathbf{z}) \\, d\mathbf{z}.

Dividing both sides by B(x)B(\mathbf{x}); subtracting the current value ut(x)u^t(\mathbf{x}) from both sides, and introducing a flow-speed parameter λ{\lambda} we have a flow equation describing the change in value as an integral of relative values:

ut=λ1B(x))2B(x)(u(z)u(x)),dz.\frac{\partial u}{\partial t} = {\lambda} \frac{1}{|B(\mathbf{x}))|^2} \int _{B(\mathbf{x})} (u(\mathbf{z})-u(\mathbf{x})) \\, d\mathbf{z}.

For harmonic functions, Δu=0\Delta u =0, this integral becomes zero via satisfaction of the mean value theorem. It follows for a non-harmonic Δu0\Delta u \ne 0 this integral is equal to the Laplacian of the uu, so we have arrived at our flow equation:

ut=limB(x)0λ1B(x))2B(x)(u(z)u(x)),dz=λΔu.\frac{\partial u}{\partial t} = \lim_{|B(\mathbf{x})| \rightarrow 0} {\lambda} \frac{1}{|B(\mathbf{x}))|^2} \int _{B(\mathbf{x})} (u(\mathbf{z})-u(\mathbf{x})) \\, d\mathbf{z} = {\lambda} \Delta u.

(see, for example, Laplace-Betrami operator case, Laplace operator case.)

Energy-based formulation

Alternatively, we can think of a single smoothing operation as the solution to an energy minimization problem. If ff is our noisy signal over the surface, then we want to find a signal uu such that it simultaneously minimizes its difference with ff and minimizes its variation over the surface:

u\*=argmin_uE_(u)=argmin_u12_S((fu)2_data+λu2_smoothness),dA,u^\* = \mathop{\text{argmin}}\_u E\_(u) = \mathop{\text{argmin}}\_u \frac12 \int \_\mathbf{S} ( \underbrace{(f-u)^{2}}\_\text{data} + \underbrace{{\lambda}|| {\nabla}u|| ^{2}}\_\text{smoothness} )\\, dA,

where again the scalar parameter λ{\lambda} controls the rate of smoothing. This energy-based formulation is equivalent to the flow-based formulation. Minimizing these energies is identical to stepping forward one temporal unit in the flow.

Calculus of variations

In the smooth setting, our energy EE is a function that measures scalar value of a given function u, making it a functional. To understand how to minimize a functional with respect to an unknown function, we will need concepts from the calculus of variations.

We are used to working with minimizing quadratic functions with respect to a discrete set of variables, where the minimum is obtained when the gradient of the energy with respect to the variables is zero.

In our case, the functional E(u)E(u) is quadratic in uu (recall that the gradient operator {\nabla} is a linear operator). The function uu that minimizes E(u)E(u) will be obtained when any small change or variation in uu has no change on the energy values. To create a small change in a function uu we will add another function vv times a infinitesimal scalar ϵ{\epsilon}. If E(u)E(u) is minimized for a function ww and we are given another arbitrary function vv, then let us define a function new function

ϕ(ϵ)=E(w+ϵv)=12S((fw+ϵv)2+λw+ϵv2),dA,{\phi}({\epsilon}) = E(w+{\epsilon}v) = \frac12 \int _\mathbf{S} ((f-w+{\epsilon}v)^{2} + {\lambda} || {\nabla}w + {\epsilon}{\nabla}v|| ^{2}) \\, dA,

where we observe that ϕ{\phi} is quadratic in ϵ{\epsilon}.

Since E(w)E(w) is minimal then ϕ{\phi} is minimized when ϵ{\epsilon} is zero, and if ϕ{\phi} is minimal at ϵ=0{\epsilon}=0, then the derivative of ϕ{\phi} with respect ϵ{\epsilon} must be zero:

0=ϕϵ_ϵ=0,=ϵ12_S((fwϵv)2+λw+ϵv2),dA,_ϵ=0=ϵ12_S(f22wf2ϵfv+w2+2ϵvw+ϵ2v2+λw2+λ2ϵvw+λϵ2v2),dA_ϵ=0=_S(fv+vw+2ϵvw+λvw+λϵv2),dA_ϵ=0=_S(v(wf)+λvw),dA.\begin{align*} 0 & = \left.\frac{\partial {\phi}}{\partial {\epsilon}} \right|\_{{\epsilon} = 0},\\ & = \left.\frac{\partial }{\partial {\epsilon}} \frac12 \int \_\mathbf{S} ( (f-w-{\epsilon}v)^{2} + {\lambda} || {\nabla}w + {\epsilon}{\nabla}v|| ^{2})\\, dA, \right|\_{{\epsilon} = 0} \\ & = \left.\frac{\partial }{\partial {\epsilon}} \frac12 \int \_\mathbf{S} ( f^2 - 2wf - 2{\epsilon}fv +w^{2}+2{\epsilon}vw +{\epsilon}^{2}v^{2} + {\lambda} || {\nabla}w|| ^{2} + {\lambda}2{\epsilon}{\nabla}v\cdot {\nabla}w + {\lambda} {\epsilon}^{2}|| {\nabla}v|| ^{2}) \\, dA \right|\_{{\epsilon} = 0}\\ & = \left.\int \_\mathbf{S} (-fv + vw +2{\epsilon}vw + {\lambda}{\nabla}v\cdot {\nabla}w + {\lambda} {\epsilon}|| {\nabla}v|| ^{2}) \\, dA \right|\_{{\epsilon} = 0}\\ & = \int \_\mathbf{S} (v(w-f) + {\lambda}{\nabla}v\cdot {\nabla}w )\\, dA. \end{align*}

The choice of "test" function vv was arbitrary, so this must hold for any (reasonable) choice of vv:

0=S(v(wf)+λvw),dAv.0 = \int _\mathbf{S} (v(w-f) + {\lambda}{\nabla}v\cdot {\nabla}w) \\, dA \quad \forall v.

It is difficult to claim much about ww from this equation directly because derivatives of vv are still involved. We can move a derivative from vv to a ww by applying Green's first identity:

0=S(v(wf)λvΔw),dA(+boundary term)v,0 = \int _\mathbf{S} (v(w-f) - {\lambda}v\Delta w )\\, dA \quad (+ \text{boundary term} )\quad \forall v,

where we choose to ignore the boundary terms (for now) or equivalently we agree to work on closed surfaces S\mathbf{S}.

Since this equality must hold of any vv let us consider functions that are little "blips" centered at any arbitrary point xS\mathbf{x} \in \mathbf{S}. A function vv that is one at x\mathbf{x} and quickly decays to zero everywhere else. To satisfy the equation above at x\mathbf{x} with this blip vv we must have that:

w(x)f(x)=λΔw(x).w(\mathbf{x})-f(\mathbf{x}) = {\lambda}\Delta w(\mathbf{x}).

The choice of x\mathbf{x} was arbitrary so this must hold everywhere.

Because we invoke variations to arrive at this equation, we call the energy-based formulation a variational formulation.

Implicit smoothing iteration

Now we understand that the flow-based formulation and the variational formulation lead to the same system, let us concretely write out the implicit smoothing step.

Letting u0=fu^0 = f we compute a new smoothed function ut+1u^{t+1} given the current solution utu^t by solving the linear system of equations:

ut(x)=(idλΔ)ut+1(x),xSu^t(\mathbf{x}) = (\text{id}-{\lambda}\Delta )u^{t+1}(\mathbf{x}), \quad \forall \mathbf{x} \in \mathbf{S}

where id\text{id} is the identity operator. In the discrete case, we will need discrete approximations of the id\text{id} and Δ\Delta operators.

Discrete Laplacian

There are many ways to derive a discrete approximation of the Laplacian Δ\Delta operator on a triangle mesh using:

  • finite volume method,
    • "The solution of partial differential equations by means of electrical networks" [MacNeal 1949, pp. 68],
    • "Discrete differential-geometry operators for triangulated 2-manifolds" [Meyer et al. 2002],
    • Polygon mesh processing [Botsch et al. 2010],
  • finite element method,
    • "Variational methods for the solution of problems of equilibrium and vibrations" [Courant 1943],
    • Algorithms and Interfaces for Real-Time Deformation of 2D and 3D Shapes [Jacobson 2013, pp. 9]
  • discrete exterior calculus
    • Discrete Exterior Calculus [Hirani 2003, pp. 69]
    • Discrete Differential Geometry: An Applied Introduction [Crane 2013, pp. 71]
  • gradient of surface area \Rightarrow mean curvature flow
    • "Computing Discrete Minimal Surfaces and Their Conjugates" [Pinkall & Polthier 1993]

All of these techniques will produce the same sparse Laplacian matrix LRn×n\mathbf{L} \in \mathbb{R}^{n\times n} for a mesh with nn vertices.

Finite element derivation of the discrete Laplacian

We want to approximate the Laplacian of a function Δu\Delta u. Let us consider uu to be piecewise-linear represented by scalar values at each vertex, collected in uRn\mathbf{u} \in \mathbb{R}^n.

Any piecewise-linear function can be expressed as a sum of values at mesh vertices times corresponding piecewise-linear basis functions (a.k.a hat functions, φi{\varphi}_i):

u(x)=_i=1nu_iφ_i(x),φ(x)={1if x=v_i,Area(x,v_j,v_k)Area(v_i,v_j,v_k)if xtriangle(i,j,k),0otherwise.\begin{align*} u(\mathbf{x}) &= {\sum}\_{i=1}^n u\_i {\varphi}\_i(\mathbf{x}), \\ {\varphi}(\mathbf{x}) &= \begin{cases} 1 & \text{if $\mathbf{x} = \mathbf{v}\_i$}, \\ \frac{\text{Area($\mathbf{x}$,$\mathbf{v}\_j$,$\mathbf{v}\_k$)}}{\text{Area($\mathbf{v}\_i$,$\mathbf{v}\_j$,$\mathbf{v}\_k$)}} & \text{if $\mathbf{x} \in \text{triangle}(i,j,k)$}, \\ 0 & \text{otherwise}. \end{cases} \end{align*}

By plugging this definition into our smoothness energy above, we have discrete energy that is quadratic in the values at each mesh vertex:

_Su(x)2,dA=_S(_i=1nu_iφ_i(x))2,dA=_S(_i=1nu_iφ_i(x))(_i=1nu_iφ_i(x)),dA=_S_i=1n_j=1nφ_iφ_ju_iu_j,dA=uTLu,where L_ij=_Sφ_iφ_j,dA.\begin{align*} \int \_\mathbf{S} || {\nabla}u(\mathbf{x})|| ^{2} \\, dA &= \int \_\mathbf{S} \||{\nabla}\left({\sum}\_{i=1}^n u\_i {\varphi}\_i(\mathbf{x})\right)\||^2 \\, dA \\ &= \int \_\mathbf{S} \left({\sum}\_{i=1}^n u\_i {\nabla}{\varphi}\_i(\mathbf{x})\right)\cdot \left({\sum}\_{i=1}^n u\_i {\nabla}{\varphi}\_i(\mathbf{x})\right) \\, dA \\ &= \int \_\mathbf{S} {\sum}\_{i=1}^n {\sum}\_{j=1}^n {\nabla}{\varphi}\_i\cdot {\nabla}{\varphi}\_j u\_i u\_j \\, dA \\ &= \mathbf{u}^{\mathsf T} \mathbf{L} \mathbf{u}, \quad \text{where } L\_{ij} = \int \_\mathbf{S} {\nabla}{\varphi}\_i\cdot {\nabla}{\varphi}\_j \\, dA. \end{align*}

By defining φ_i{\varphi}\_i as piecewise-linear hat functions, the values in the system matrix L_ijL\_{ij} are uniquely determined by the geometry of the underlying mesh. These values are famously known as cotangent weights. "Cotangent" because, as we will shortly see, of their trigonometric formulae and "weights" because as a matrix L\mathbf{L} they define a weighted graph Laplacian for the given mesh. Graph Laplacians are employed often in geometry processing, and often in discrete contexts ostensibly disconnected from FEM. The choice or manipulation of Laplacian weights and subsequent use as a discrete Laplace operator has been a point of controversy in geometry processing research (see "Discrete laplace operators: no free lunch" [Wardetzky et al. 2007]).

We first notice that φ_i{\nabla}{\varphi}\_i are constant on each triangle, and only nonzero on triangles incident on node ii. For such a triangle, T_αT\_{\alpha}, this φ_i{\nabla}{\varphi}\_i points perpendicularly from the opposite edge e_ie\_i with inverse magnitude equal to the height hh of the triangle treating that opposite edge as base:

φ_i=1h=e_i2A,||{\nabla}{\varphi}\_i|| = \frac{1}{h} = \frac{||\mathbf{e}\_i||}{2A},

where e_i\mathbf{e}\_i is the edge e_ie\_i as a vector and AA is the area of the triangle.

Now, consider two neighboring nodes ii and jj, connected by some edge eij\mathbf{e}_{ij}. Then φi{\nabla}{\varphi}_i points toward node ii perpendicular to ei\mathbf{e}_i and likewise φj{\nabla} {\varphi}_j points toward node jj perpendicular to ej\mathbf{e}_j. Call the angle formed between these two vectors θ{\theta}. So we may write:

φiφj=φiφjcosθ=ej2Aei2Acosθ.{\nabla} {\varphi}_i \cdot {\nabla} {\varphi}_j = ||{\nabla} {\varphi}_i|| ||{\nabla} {\varphi}_j|| \cos {\theta} = \frac{||\mathbf{e}_j||}{2A}\frac{||\mathbf{e}_i||}{2A} \cos {\theta}.

Now notice that the angle between e_i\mathbf{e}\_i and e_j\mathbf{e}\_j, call it α_ij{\alpha}\_{ij}, is πθ{\pi} - {\theta}, but more importantly that:

cosθ=cos(πθ)=cosα_ij.\cos {\theta} = - \cos \left({\pi} - {\theta}\right) = -\cos {\alpha}\_{ij}.

So, we can rewrite equation the cosine law equation above into:

e_j2Ae_i2Acosα_ij.-\frac{||\mathbf{e}\_j||}{2A}\frac{||\mathbf{e}\_i||}{2A} \cos {\alpha}\_{ij}.

Now, apply the definition of sine for right triangles:

sinα_ij=h_je_i=h_ie_j,\sin {\alpha}\_{ij} = \frac{h\_j}{||\mathbf{e}\_i||} = \frac{h\_i}{||\mathbf{e}\_j||},

where h_ih\_i is the height of the triangle treating e_i\mathbf{e}\_i as base, and likewise for h_jh\_j. Rewriting the equation above, replacing one of the edge norms, e.g. e_i||\mathbf{e}\_i||:

e_j2Ah_jsinα_ij2Acosα_ij.-\frac{||\mathbf{e}\_j||}{2A} \frac{\frac{h\_j}{\sin{\alpha}\_{ij}}}{2A} \cos {\alpha}\_{ij}.

Combine the cosine and sine terms:

e_j2Ah_j2Acotα_ij.-\frac{||\mathbf{e}\_j||}{2A} \frac{h\_j}{2A} \cot {\alpha}\_{ij}.

Finally, since e_jh_j=2A||\mathbf{e}\_j||h\_j=2A, our constant dot product of these gradients in our triangle is:

φ_iφ_j=cotα_ij2A.{\nabla} {\varphi}\_i \cdot {\nabla} {\varphi}\_j = -\frac{\cot {\alpha}\_{ij}}{2A}.

Similarly, inside the other triangle T_βT\_{\beta} incident on nodes ii and jj with angle β_ij{\beta}\_{ij} we have a constant dot product:

φ_iφ_j=cotβ_ij2B,{\nabla} {\varphi}\_i \cdot {\nabla} {\varphi}\_j = -\frac{\cot {\beta}\_{ij}}{2B},

where BB is the area T_βT\_{\beta}.

Recall that φ_i{\varphi}\_i and φ_j{\varphi}\_j are only both nonzero inside these two triangles, T_αT\_{\alpha} and T_βT\_{\beta}. So, since these constants are inside an integral over area we may write:

_Sφ_iφ_j,dA=Aφ_iφ_j_T_α+Bφ_iφ_j_T_β=12(cotα_ij+cotβ_ij).\int\limits\_\mathbf{S} {\nabla} {\varphi}\_i \cdot {\nabla} {\varphi}\_j \\, dA = \left.A{\nabla} {\varphi}\_i \cdot {\nabla} {\varphi}\_j \right|\_{T\_{\alpha}} + \left.B{\nabla} {\varphi}\_i \cdot {\nabla} {\varphi}\_j \right|\_{T\_{\beta}} = -\frac{1}{2} \left( \cot {\alpha}\_{ij} + \cot {\beta}\_{ij} \right).

Mass matrix

Treated as an operator (i.e., when used multiplied against a vector Lu\mathbf{L}\mathbf{u}), the Laplacian matrix L\mathbf{L} computes the local integral of the Laplacian of a function uu. In the energy-based formulation of the smoothing problem this is not an issue. If we used a similar FEM derivation for the data term we would get another sparse matrix MRn×n\mathbf{M} \in \mathbb{R}^{n \times n}:

S(uf)2,dA=Si=1nj=1nφiφj(uifi)(ujfj),dA=(uf)TM(uf),\int _\mathbf{S} (u-f)^{2} \\, dA = \int _\mathbf{S} {\sum}_{i=1}^n {\sum}_{j=1}^n {\varphi}_i\cdot {\varphi}_j (u_i-f_i) (u_j-f_j) \\, dA = (\mathbf{u}-\mathbf{f})^{\mathsf T} \mathbf{M} (\mathbf{u}-\mathbf{f}),

where M\mathbf{M} as an operator computes the local integral of a function's value (i.e., Mu\mathbf{M}\mathbf{u}).

This matrix M\mathbf{M} is often diagonalized or lumped into a diagonal matrix, even in the context of FEM. So often we will simply set:

Mij={13t=1m{Area(t)if triangle t contains vertex i0otherwiseif i=j0otherwise,M_{ij} = \begin{cases} \frac13 {\sum}_{t=1}^m \begin{cases} \text{Area}(t) & \text{if triangle $t$ contains vertex $i$} \\ 0 & \text{otherwise} \end{cases} & \text{if $i=j$}\\ 0 & \text{otherwise}, \end{cases}

for a mesh with mm triangles.

If we start directly with the continuous smoothing iteration equation, then we have a point-wise equality. To fit in our integrated Laplacian L\mathbf{L} we should convert it to a point-wise quantity. From a units perspective, we need to divide by the local area. This would result in a discrete smoothing iteration equation:

ut=(IλM1L)ut+1,\mathbf{u}^t = (\mathbf{I} - {\lambda}\mathbf{M}^{-1} \mathbf{L})\mathbf{u}^{t+1},

where IRn×n\mathbf{I} \in \mathbb{R}^{n\times n} is the identity matrix. This equation is correct but the resulting matrix A:=IλM1L\mathbf{A} := \mathbf{I} - {\lambda}\mathbf{M}^{-1} \mathbf{L} is not symmetric and thus slower to solve against.

Instead, we could take the healthier view of requiring our smoothing iteration equation to hold in a locally integrated sense. In this case, we replace mass matrices on either side:

Mut=(MλL)ut+1.\mathbf{M} \mathbf{u}^t = (\mathbf{M} - {\lambda}\mathbf{L})\mathbf{u}^{t+1}.

Now the system matrix A:=M+λL\mathbf{A} := \mathbf{M} + {\lambda}\mathbf{L} will be symmetric and we can use Cholesky factorization to solve with it.

Laplace Operator is Intrinsic

The discrete Laplacian operator and its accompanying mass matrix are intrinsic operators in the sense that they only depend on lengths. In practical terms, this means we do not need to know where vertices are actually positioned in space (i.e., V\mathbf{V}). Rather we only need to know the relative distances between neighboring vertices (i.e., edge lengths). We do not even need to know which dimension this mesh is living in.

This also means that applying a transformation to a shape that does not change any lengths on the surface (e.g., bending a sheet of paper) will have no affect on the Laplacian.

Data denoising

For the data denoising application, our geometry of the domain is not changing only the scalar function living upon it. We can build our discrete Laplacian L\mathbf{L} and mass matrix M\mathbf{M} and apply the above formula with a chosen λ{\lambda} parameter.

Geometric smoothing

For geometric smoothing, the Laplacian operator (both Δ\Delta in the continuous setting and L,M\mathbf{L},\mathbf{M} in the discrete setting) depend on the geometry of the surface S\mathbf{S}. So if the signal uu is replaced with the positions of points on the surface (say, VRn×3\mathbf{V} \in \mathbb{R}^{n\times 3} in the discrete case), then the smoothing iteration update rule is a non-linear function if we write it as:

Mt+1Vt=(Mt+1λLt+1)Vt+1.\mathbf{M}^{t+1} \mathbf{V}^t = (\mathbf{M}^{t+1} - {\lambda}\mathbf{L}^{t+1})\mathbf{V}^{t+1}.

However, if we assume that small changes in V\mathbf{V} have a negligible effect on L\mathbf{L} and M\mathbf{M} then we can discretize explicitly by computing L\mathbf{L} and M\mathbf{M} before performing the update:

MtVt=(MtλLt)Vt+1.\mathbf{M}^{t} \mathbf{V}^t = (\mathbf{M}^{t} - {\lambda}\mathbf{L}^{t}) \mathbf{V}^{t+1}.

Why did my mesh disappear?

Repeated application of geometric smoothing may cause the mesh to "disappear". Actually the updated vertex values are being set to NaNs due to degenerate numerics. We are rebuilding the discrete Laplacian at every new iteration, regardless of the "quality" of the mesh's triangles. In particular, if a triangle tends to become skinnier and skinnier during smoothing, what will happen to the cotangents of its angles?

In "Can Mean-Curvature Flow Be Made Non-Singular?", Kazhdan et al. derive a new type of geometric flow that is stable (so long as the mesh at time t=0t=0 is reasonable). Their change is remarkably simple: do not update L\mathbf{L}, only update M\mathbf{M}.

Tasks

Learn an alternative derivation of cotangent Laplacian

The "cotangent Laplacian" by far the most important tool in geometry processing. It comes up everywhere. It is important to understand where it comes from and be able to derive it (in one way or another).

The background section above contains a FEM derivation of the discrete "cotangnet Laplacian". For this (unmarked) task, read and understand one of the other derivations listed above.

Hint: The finite-volume method used in [Botsch et al. 2010] is perhaps the most accessible alternative.

White list

  • igl::doublearea
  • igl::edge_lengths

Black list

  • igl::cotmatrix_entries
  • igl::cotmatrix
  • igl::massmatrix
  • Trig functions sin, cos, tan etc. (e.g., from #include <cmath>) See background notes about "intrinisic"-ness

src/cotmatrix.cpp

Construct the "cotangent Laplacian" for a mesh with edge lengths l. Each entry in the output sparse, symmetric matrix L is given by:

L_ij={12cotα_ij+12cotβ_ijif edge ij exists_jiL_ijif i=j0otherwiseL\_{ij} = \begin{cases} \frac12 \cot{{\alpha}\_{ij}} + \frac12 \cot{{\beta}\_{ij}} & \text{if edge $ij$ exists} \\ - {\sum}\_{j\ne i} L\_{ij} & \text{if $i = j$} \\ 0 & \text{otherwise} \end{cases}

Hint: Review the law of sines and law of cosines and Heron's ancient formula to derive a formula for the cotangent of each triangle angle that only uses edge lengths.

src/massmatrix.cpp

Construct the diagonal(ized) mass matrix M for a mesh with given face indices in F and edge lengths l.

src/smooth.cpp

Given a mesh (V,F) and data specified per-vertex (G), smooth this data using a single implicit Laplacian smoothing step.

This data could be a scalar field on the surface and smoothing corresponds to data denoising.

Or the data could be the vector field of the surface's own geometry. This corresponds to geometric smoothing.