Jekyll2020-03-14T09:57:32+00:00/feed.xmlR. FalqueThis page is a collection of my personal notes.Half Edge Data Structure2020-03-13T00:00:00+00:002020-03-13T00:00:00+00:00/cpp/cuda/data-structures/2020/03/13/Half-edge-data-structure<p>TL;DR: Definition of a half-edge data structure in cuda.</p> <div class="alert alert-info"> <strong>Info!</strong> Unfinished notes </div> <p>I am a big fan of simple data structures like the ones used in <a href="https://github.com/libigl/libigl">libIGL</a> (a N by 3 matrix of doubles for vertices and a M by 3 matrix of integers for faces). However, these data structures can not be used for efficient circulation through the faces (e.g., finding the neighbouring faces of a face is hard).</p> <p>An alternative is to embrace a more complexe data structure that is more complex to define and use more memory for storing but does provide more flexibility to travel through the mesh. Amongst the popular <a href="https://en.wikipedia.org/wiki/Polygon_mesh#Representations">data structures</a>, halfedges structures are memory efficient but also offer powerful ways to travel on a mesh.</p> <h2 id="links">links</h2> <ul> <li><a href="https://en.wikipedia.org/wiki/Polygon_mesh#Representation">Wiki on data structure</a></li> <li><a href="https://upcoder.com/13/vector-meshes">How to: halfedge data structure</a></li> <li><a href="https://www.graphics.rwth-aachen.de/media/openmesh_static/Documentations/OpenMesh-6.3-Documentation/a00010.html">Open Mesh’s explaination on halfedge data structures</a></li> <li><a href="https://math.stackexchange.com/questions/1541125/total-number-of-edges-in-a-triangle-mesh-with-n-vertices">Euler’s formula</a></li> <li><a href="https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html">Cuda best practices</a></li> </ul> <h2 id="half-edge-data-structures-definition">Half-Edge data structures definition</h2> <p>From the documentation of <a href="https://www.graphics.rwth-aachen.de/media/openmesh_static/Documentations/OpenMesh-6.3-Documentation/a00010.html">OpenMesh</a>.</p> <p><img src="/assets/img/halfedge_structure.png" alt="halfedge structure" title="halfedge structure" /></p> <ul> <li>Each vertex references one outgoing halfedge, i.e. a halfedge that starts at this vertex (1).</li> <li>Each face references one of the halfedges bounding it (2).</li> <li>Each halfedge provides a handle to <ul> <li>the vertex it points to (3),</li> <li>the face it belongs to (4)</li> <li>the next halfedge inside the face (ordered counter-clockwise) (5),</li> <li>the opposite halfedge (6),</li> <li>(optionally: the previous halfedge in the face (7)).</li> </ul> </li> </ul>rFalqueTL;DR: Definition of a half-edge data structure in cuda.Eigen Rows Versus Cols2020-02-03T00:00:00+00:002020-02-03T00:00:00+00:00/cpp/2020/02/03/Eigen-rows-versus-cols<p>TL;DR: The use of a <code class="language-plaintext highlighter-rouge">Eigen::Matrix3Xd</code> or <code class="language-plaintext highlighter-rouge">Eigen::Matrix3Xf</code> should be prefered to store 3D pointclouds in 3D libraries.</p> <p>In C++, Eigen matrices are one of the most common object used to store the raw information of 3D objects. This is the case for both <a href="https://libigl.github.io/tutorial/#mesh-representation">libIGL</a> and <a href="https://github.com/kzampog/cilantro">cilantro</a>. However, this two libraries are using two different coordinates to store the information, e.g., in libIGL, vertices would be stored in a N by 3 matrix and Cilantro would store it into a 3 by N matrix. While this question might seems insignificant, we will show why it matter and which one should you use?</p> <h2 id="it-is-more-logical-given-the-standard-eigen-structures">It is more logical given the standard Eigen structures</h2> <p>While as shown in the <a href="https://libigl.github.io/tutorial/#mesh-representation">libIGL documentation</a>, the use of a N by 3 matrix seems more readable, it creates few problems. However, the natural structure to store a 3D point would be <code class="language-plaintext highlighter-rouge">Eigen::Vector3d</code> which is actually defined as a <code class="language-plaintext highlighter-rouge">Eigen::Matrix &lt;double , 3, 1&gt;</code> in the background. Therefore, it seems natural to stores points using a <code class="language-plaintext highlighter-rouge">Eigen::Matrix &lt;double , 3, Eigen::Dynamic&gt;</code>.</p> <h2 id="it-is-a-natural-fit-for-the-geometry-module">It is a natural fit for the geometry module</h2> <p>An additional tool build into the Eigen library is the <a href="https://eigen.tuxfamily.org/dox/group__TutorialGeometry.html">geometry module</a> which allows to apply transformations to a set of 3D points. For instance applying an affine transformations to a 3 by N matrix would be performed as follow:</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Eigen</span><span class="o">::</span><span class="n">Affine3d</span> <span class="n">transform</span> <span class="n">Eigen</span><span class="o">::</span><span class="n">Matrix3Xd</span> <span class="n">vertices</span><span class="p">,</span> <span class="n">transformed_vertices</span><span class="p">;</span> <span class="n">transformed_vertices</span> <span class="o">=</span> <span class="n">transform</span> <span class="o">*</span> <span class="n">vertices</span><span class="p">;</span> </code></pre></div></div> <p>While using the format defined in libIGL would be writen like:</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Eigen</span><span class="o">::</span><span class="n">Affine3d</span> <span class="n">transform</span> <span class="n">Eigen</span><span class="o">::</span><span class="n">MatrixX3d</span> <span class="n">vertices</span><span class="p">,</span> <span class="n">transformed_vertices</span><span class="p">;</span> <span class="n">transformed_vertices</span> <span class="o">=</span> <span class="p">(</span><span class="n">transform</span> <span class="o">*</span> <span class="n">vertices</span><span class="p">.</span><span class="n">transpose</span><span class="p">()</span> <span class="p">).</span><span class="n">transpose</span><span class="p">();</span> </code></pre></div></div> <h2 id="it-is-safer">It is safer</h2> <p>One problem that might occur in constantly using <code class="language-plaintext highlighter-rouge">.transpose()</code> is that the users might:</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Eigen</span><span class="o">::</span><span class="n">Affine3d</span> <span class="n">transform</span> <span class="n">Eigen</span><span class="o">::</span><span class="n">MatrixXd</span> <span class="n">vertices</span><span class="p">,</span> <span class="n">transformed_vertices</span><span class="p">;</span> <span class="n">vertices</span> <span class="o">=</span> <span class="n">vertices</span><span class="p">.</span><span class="n">transpose</span><span class="p">();</span> <span class="n">transformed_vertices</span> <span class="o">=</span> <span class="n">transform</span> <span class="o">*</span> <span class="n">vertices</span><span class="p">;</span> <span class="n">transformed_vertices</span> <span class="o">=</span> <span class="n">transformed_vertices</span><span class="p">.</span><span class="n">transpose</span><span class="p">();</span> </code></pre></div></div> <p>Which should actually happen through <code class="language-plaintext highlighter-rouge">.transposeInPlace()</code> to avoid an <a href="https://eigen.tuxfamily.org/dox/group__TopicAliasing.html">aliasing problem</a>.</p>rFalqueTL;DR: The use of a Eigen::Matrix3Xd or Eigen::Matrix3Xf should be prefered to store 3D pointclouds in 3D libraries.Point_cloud_skeletonization2019-09-12T00:00:00+00:002019-09-12T00:00:00+00:00/ddg/2019/09/12/point_cloud_skeletonization<p>This are my personal notes on skeletonization methods.</p> <h2 id="point-cloud-contraction">Point cloud contraction</h2> <style> #c { position: absolute; left: 0; width: 100%; height: 100%; } .list-item { display: inline-block; margin: 1em; padding: 1em; box-shadow: 1px 2px 4px 0px rgba(0,0,0,0.25); } .list-item .scene { width: 200px; height: 200px; } .list-item .description { color: #888; font-family: sans-serif; font-size: large; width: 200px; margin-top: 0.5em; } </style> <canvas id="c"></canvas> <div class="view" title="/assets/data/japanese_temple.obj"></div>rFalqueThis are my personal notes on skeletonization methods.Functional Maps2019-07-31T00:00:00+00:002019-07-31T00:00:00+00:00/ddg/2019/07/31/functional-maps<p>Serie of personal notes on functional maps</p> <div class="alert alert-info"> <strong>Info!</strong> Unfinished notes </div> <p>List of related ressources:</p> <ul> <li><a href="http://www.lix.polytechnique.fr/~maks/fmaps_SIG17_course/">SIGGRAPH 2017 course</a></li> <li><a href="http://visionair.ge.imati.cnr.it/ontologies/shapes/viewmodels.jsp">AIM@shape</a> (3D models repository)</li> </ul> <h2 id="spectral-decomposition">Spectral decomposition</h2> <p>``<em>Can you hear the shape of a drum?</em>’’ M. Kac, 1966. No … but …</p> <h3 id="eigenfunction-decomposition">Eigenfunction decomposition</h3> <p>Let us first consider the following shape which is defined as a triangular mesh with $m$ vertices.</p> <p><img src="/assets/img/cow.png" alt="original shape" title="original shape" /></p> <p>The eigenfunctions of the <a href="/ddg/2019/07/25/discrete-differential-geometry.html#laplace-beltrami-operator">Laplace-Beltrami operator</a> are obtained by solving:</p> <script type="math/tex; mode=display">\Delta_s \phi_i = \lambda_i \phi_i,</script> <p>where $\Delta_s$ is the Laplace-Beltrami operator<sup id="fnref:Meyer2003"><a href="#fn:Meyer2003" class="footnote">1</a></sup>, $\phi_i$ is the $i^{th}$ eigenfunction and $\lambda_i$ its associated eigenvalues. In practice, we deal with discrete formulations and the $\Delta_s$ is a square matrix noted $L$ (of size $m \times m$ for the source) and the eigenfunctions are represented as eigenvectors $\Phi$ (e.g., a vector of length $m$). This gives us the following formulation: \begin{equation} L \Phi_i = \lambda_i \Phi_i, \label{eq:eigenfunction_decomposition} \end{equation}</p> <p>Equation \eqref{eq:eigenfunction_decomposition} is then typically solved on a <a href="http://faculty.cse.tamu.edu/davis/suitesparse.html">sparse matrix eigensolver</a> and the eigenvectors are then sorted based on the order of the eigenvalues.</p> <h3 id="what-do-these-eigenfunctions-means">What do these eigenfunctions means?</h3> <p>It is possible to visualize the eigenfunctions projected onto the geometries:</p> <p><img src="/assets/img/eigenfunctions_1.png" alt="eigenfunctions projected on the shape" title="eigenfunctions projected on the shape" /></p> <p>As shown on the figure, after being sorted according to the eigenvalues, the eigenfunctions of the LB operators are ordered from <em>low</em> to <em>high frequencies</em>. This property can be used to apply a low pass filter on the shape. For exemple, we can reconstruct a shape with different number of eigenfunctions<sup id="fnref:Levy2006"><a href="#fn:Levy2006" class="footnote">2</a></sup>: First, we project the geometry onto each eigenfunction such as:</p> <script type="math/tex; mode=display">\alpha^x_i \leftarrow \sum_{j=1}^m x_j \Phi_i(j),~~~~~~ \alpha^y_i \leftarrow \sum_{j=1}^m y_j \Phi_i(j),~~~\text{and}~~~ \alpha^z_i \leftarrow \sum_{j=1}^m z_j \Phi_i(j)</script> <p>And the surface reconstruction for $k$ eigenvalues is obtained by:</p> <script type="math/tex; mode=display">x_j \leftarrow \sum_{i=1}^{k} \alpha_i^x \Phi_i(j),~~~~~~ y_j \leftarrow \sum_{i=1}^{k} \alpha_i^y \Phi_i(j),~~~\text{and}~~~ z_j \leftarrow \sum_{i=1}^{k} \alpha_i^z \Phi_i(j)</script> <p>The final reconstruction with different number of eigenfunction: <img src="/assets/img/eigenfunctions_2.png" alt="eigenfunctions projected on the shape" title="eigenfunctions projected on the shape" /></p> <h3 id="can-we-do-more">Can we do more?</h3> <p>Now given a second shape with $n$ vertices, and its own decomposition formulated similarly such as:</p> <script type="math/tex; mode=display">\beta^x_i \leftarrow \sum_{j=1}^n x_j' \Phi_i'(j),~~~~~~\beta^y_i \leftarrow \sum_{j=1}^n y_j' \Phi_i'(j),~~~\text{and}~~~\beta^z_i \leftarrow \sum_{j=1}^n z_j' \Phi_i'(j)</script> <p>Levy<sup id="fnref:Levy2006:1"><a href="#fn:Levy2006" class="footnote">2</a></sup> mention pose transfert as a potential application ``<em>provided that the $\alpha$’s and the $\beta$’s are expressed in the same langage</em>’’. In this context, the definition of the reconstruction is defined as:</p> <script type="math/tex; mode=display">x_j \leftarrow \sum_{i=1}^{l} \alpha_i^x \Phi_i'(j) + \sum_{i=l+1}^{k} \beta_i^x \Phi_i'(j)\\ y_j \leftarrow \sum_{i=1}^{l} \alpha_i^y \Phi_i'(j) + \sum_{i=l+1}^{k} \beta_i^y \Phi_i'(j)\\ z_j \leftarrow \sum_{i=1}^{l} \alpha_i^z \Phi_i'(j) + \sum_{i=l+1}^{k} \beta_i^z \Phi_i'(j)</script> <p>Let’s give it a try by using the 5 first coefficients ($l = 5$) from the cow and apply to a bull: <img src="/assets/img/eigenfunctions_3.png" alt="pose transfert" title="pose transfert" /></p> <h3 id="why-did-it-fail">Why did it fail?</h3> <p>As Levy says in his paper the $\alpha$’s and the $\beta$’s have to be expressed in the <em>same language</em>. Let’s look at the first eigenfunctions to see how they are distributed: <img src="/assets/img/eigenfunctions_4.png" alt="eigenfunctions on both shapes" title="eigenfunctions on both shapes" /></p> <p>As shown on the figure, some of the eigenfunctions seems to have a good correspondence and some of them are mixted, in other worlds they do not speak the same language. More specifically I hand picked some of the eigenfunctions that have interesing properties in the following figure:</p> <p><img src="/assets/img/eigenfunctions_5.png" alt="eigenfunctions on both shapes" title="eigenfunctions on both shapes" /></p> <p>Both $2^{nd}$, $4^{th}$, $5^{th}$, and $10^{th}$ eigenfunctions are a good match. The eigenfunctions 4 (on the bull) and 3 (on the cow) have a good correspondence but are not ordered similarly. Eigenfunctions 8 (on the bull) and 7 (on the cow) are the opposite of each other; this is a well known phenomenon as the eigenfunction decomposition is prone to sign flipping.</p> <p>As a conclusion, we can consider that the pose transfert did not work because the eigenfunctions do not speak the <em>same language</em>.</p> <h2 id="functional-maps">Functional maps</h2> <h3 id="preliminaries-and-notations">Preliminaries and notations</h3> <p>Let consider the matching between two shapes: the source $M$ and the target $N$.</p> <p>Both the source and the target are defined as a triangular meshes with $m$ and $n$ vertices respectively.</p> <h3 id="feature-descriptors">Feature descriptors</h3> <h4 id="wave-kernel-signature">Wave Kernel Signature</h4> <h4 id="heat-kernel-signature">Heat Kernel Signature</h4> <h4 id="shot">SHOT</h4> <p>See <sup id="fnref:Tombari2010"><a href="#fn:Tombari2010" class="footnote">3</a></sup></p> <h3 id="functional-map-computation">Functional map computation</h3> <h4 id="features-matching">Features matching</h4> <p>The feature matching is obtained by finding the map which maximize the similarity between the descriptors projected on the eigenfunctions. In other words, we seek to minimize: \begin{equation} C = \arg\min_C \vert\vert CA-B \vert\vert^2 \end{equation}</p> <h4 id="regularization-terms">Regularization terms</h4> <p>The map will be <em>isometric</em> if and only if: \begin{equation} C \Delta_M = \Delta_N C \end{equation}</p> <p>The mapping will be <em>locally volume preserving</em> if and only if the map is <em>orthonormal</em> which is defined as: \begin{equation} C^T C = Id \end{equation}</p> <p>The mapping is <em>conformal</em> if and only if: \begin{equation} C^T \Delta_1 C= \Delta_2 \end{equation}</p> <h4 id="final-cost-function">Final cost function</h4> <p>The final functional map that include the feature matching and the regularization term is defined as: \begin{equation} C_{opt} = \arg\min_C \vert\vert CA-B \vert\vert^2 + \vert\vert C \Delta_M - \Delta_N C \vert\vert^2 \end{equation}</p> <h2 id="references">References</h2> <div class="footnotes"> <ol> <li id="fn:Meyer2003"> <p>M. Meyer, M. Desbrun, P. Schroder, and A. H. Barr, <a href="http://multires.caltech.edu/pubs/diffGeoOps.pdf">Discrete Differential-Geometry Operators for Triangulated 2-Manifolds</a>, Springer, 2003. <a href="#fnref:Meyer2003" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:Levy2006"> <p>B Levy, <a href="https://ieeexplore.ieee.org/abstract/document/1631196">Laplace-Beltrami Eigenfunctions Towards an Algorithm That “Understands” Geometry</a>, SMI, 2006 <a href="#fnref:Levy2006" class="reversefootnote">&#8617;</a> <a href="#fnref:Levy2006:1" class="reversefootnote">&#8617;<sup>2</sup></a></p> </li> <li id="fn:Tombari2010"> <p>, F. Tombari, S. Salti, and L. D. Stefano <a href="https://vision.deis.unibo.it/fede/papers/eccv10.pdf">Unique Signatures of Histograms for Local Surface Description</a>, ECCV, 2010. <a href="#fnref:Tombari2010" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>rFalqueSerie of personal notes on functional mapsClosed Mesh Volume Computation2019-07-27T00:00:00+00:002019-07-27T00:00:00+00:00/ddg/2019/07/27/closed-mesh-volume-computation<p>Volume and surface area computation for triangle meshes.</p> <div class="alert alert-warning"> <strong>Warning!</strong> This method works only for <strong>watertight</strong> meshes. </div> <h2 id="volume-computation">Volume computation</h2> <p>Using the notations defined in the following figure: <img src="/assets/img/volumeComputation1.jpg" alt="volume of a tetrahedron" title="volume of a tetrahedron" /> Figure 1: volume of a tetrahedron</p> <p>The surface area of the face $ABC$ can be computed as \begin{equation} Area(ABC) = \frac{ \vert \vec{AB} \times \vec{AC} \vert } {2} \end{equation}</p> <p>Let us now consider the volume of a tetrahedron by adding a point $O$ (e.g., <script type="math/tex">O = \{0,0,0\}</script>). The volume of $OABC$ is then defined as: \begin{equation} Volume(OABC) = \frac{1}{3} . \frac{ \vert \vec{AB} \times \vec{AC} \vert } {2}. \vert \vec{OG}.\vec{N} \vert, \end{equation} with $\vert \vec{OG}.\vec{N} \vert$ the height of the tetrahedron.</p> <p>The final volume is obtained by integrating the volume of the tetrahedron defined for each face. However, not all tetrahedron should be added. This idea is illustrated for a 2D case in the following figure: <img src="/assets/img/volumeComputation2.jpg" alt="one-ring neighbourhood" title="one-ring neighbourhood" /></p> <p>The areas in blue should be added, and the areas in red should be removed during the integration. This is performed by considering the sign of the dot product between $\vec{OG}$ and the normal of the face $\vec{N}$ (in practice, we simply remove the norm used above). The final formula of the volume integration is: \begin{equation} Volume = \sum_{f \in F} \frac{1}{3} . \frac{ \vert \vec{AB} \times \vec{AC} \vert } {2}. (\vec{OG} . \vec{N}) \end{equation}</p> <p>Similarly, the surface area of the mesh can be obtained with: \begin{equation} Area = \sum_{f \in F} \frac{ \vert \vec{AB} \times \vec{AC} \vert } {2} \end{equation}</p> <div class="alert alert-info"> <strong>Info!</strong> Any points O can be used for the computation of the volume. </div> <h2 id="validation">Validation</h2> <p>Example of volume computed on the following dataset</p> <p><img src="/assets/img/volumeValidationCube.jpg" alt="volume validation cube" title="volume validation cube" /> <img src="/assets/img/volumeValidationCylinder.jpg" alt="volume validation cylinder" title="volume validation cylinder" /></p> <table> <thead> <tr> <th>Tables</th> <th style="text-align: center">computed value</th> <th style="text-align: center">real value</th> <th style="text-align: center">relative error</th> </tr> </thead> <tbody> <tr> <td>volume cube</td> <td style="text-align: center">$64.174 m^3$</td> <td style="text-align: center">$64.0 m^3$</td> <td style="text-align: center">$0.0027$</td> </tr> <tr> <td>surface area cube</td> <td style="text-align: center">$90.652 m^2$</td> <td style="text-align: center">$96.0 m^2$</td> <td style="text-align: center">$0.0557$</td> </tr> <tr> <td>volume cylinder</td> <td style="text-align: center">$12.536 m^3$</td> <td style="text-align: center">$12.566 m^3$</td> <td style="text-align: center">$0.0024$</td> </tr> <tr> <td>surface area cylinder</td> <td style="text-align: center">$30.981 m^2$</td> <td style="text-align: center">$31.416 m^2$</td> <td style="text-align: center">$0.0138$</td> </tr> </tbody> </table> <div class="alert alert-info"> <strong>Info!</strong> The error actually comes from the Poisson surface reconstruction. </div> <h2 id="data">Data</h2> <p>The data and the code is available here (in C++ with <a href="http://pointclouds.org/">PCL</a> as a dependency), we start from a point cloud with the surface reconstructed using Poisson surface reconstruction<sup id="fnref:Kazhdan_2006"><a href="#fn:Kazhdan_2006" class="footnote">1</a></sup>.</p> <table> <tbody> <tr> <td><a href="/assets/data/cube.ply" rel="some text"><img src="/assets/img/attached-document.jpg" alt="one-ring neighbourhood" title="cube" /></a></td> <td><a href="/assets/data/cylinder.ply" rel="some text"><img src="/assets/img/attached-document.jpg" alt="one-ring neighbourhood" title="cylinder" /></a></td> <td><a href="/assets/data/volume_code.zip" rel="some text"><img src="/assets/img/attached-document.jpg" alt="one-ring neighbourhood" title="code" /></a></td> </tr> <tr> <td>cube.ply</td> <td>cylinder.ply</td> <td>volume_code.zip</td> </tr> </tbody> </table> <h2 id="references">References</h2> <div class="footnotes"> <ol> <li id="fn:Kazhdan_2006"> <p>M. Kazhdan, M. Bolitho, and H. Hoppe, <a href="http://hhoppe.com/poissonrecon.pdf">Poisson surface reconstruction</a>, In SGP ’06: <em>Proceedings of the fourth Eurographics symposium on Geometry processing</em>. <a href="#fnref:Kazhdan_2006" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>rFalqueVolume and surface area computation for triangle meshes.Discrete Differential Geometry2019-07-25T00:00:00+00:002019-07-25T00:00:00+00:00/ddg/2019/07/25/discrete-differential-geometry<p>Serie of notes on discrete differential-geometry.</p> <div class="alert alert-info"> <strong>Info!</strong> Unfinished notes </div> <h2 id="content">Content</h2> <ul> <li>discrete differential operators</li> <li>definition of the operator with the vertex as a reference</li> <li>implementation with respect to the one-ring neighbourhood</li> </ul> <p>List of ressources:</p> <ul> <li><a href="http://graphics.stanford.edu/courses/cs468-12-spring/schedule/schedule.html">Stanford slides</a></li> <li><a href="http://www.hao-li.com/cs599-ss2015/slides/Lecture04.1.pdf">Hao Li slides</a></li> <li><a href="http://visionair.ge.imati.cnr.it/ontologies/shapes/viewmodels.jsp">AIM@shape</a></li> <li><a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#emphasis">Markdown cheat sheet</a></li> </ul> <h2 id="discrete-differential-geometry-operators">Discrete differential-geometry operators</h2> <h3 id="gradient">Gradient</h3> <p>\begin{equation} \nabla f = \big( \frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}, \frac{\partial f}{\partial z} \big) \notag \end{equation}</p> <script type="math/tex; mode=display">\nabla f(x) = (f_j - f_i) \frac{(x_i-x_k)^\bot}{2A_T} + (f_k - f_i) \frac{(x_j-x_i)^{\bot}} {2A_T}</script> <h3 id="divergent">Divergent</h3> <script type="math/tex; mode=display">\nabla . f = \frac{\partial f_x}{\partial x} + \frac{\partial f_y}{\partial y} + \frac{\partial f_z}{\partial z}</script> <h3 id="curl">Curl</h3> <h3 id="laplace-beltrami-operator">Laplace-Beltrami operator</h3> <p>The Laplace operator is defined in the Euclidean space as: \begin{equation} \Delta f = \nabla \cdot \nabla .f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2} + \frac{\partial^2 f}{\partial z^2}. \end{equation}</p> <p>The <em>Laplace-Beltrami</em> operator, noted <script type="math/tex">\Delta_s</script>, generalizes the Laplacian to surfaces (i.e., 2D manifolds) and is defined with respect to the curvature.</p> <p><strong>Relation with curvature:</strong> Let’s first define the curvatures on continuous surfaces before to formulate it into discrete surfaces, following the notations from Meyer et al.<sup id="fnref:Meyer2003"><a href="#fn:Meyer2003" class="footnote">1</a></sup>.</p> <p>Given a surface $S$, a point $p$, its associated normal vector $\mathbf{n}$, and a unit vector $\mathbf{e}_\theta$; the notion of <em>normal curvature</em> $\kappa^N(\theta)$ is locally defined as the quantification of the curvature in the plane <script type="math/tex">\{p, \mathbf{n}, \mathbf{e}_\theta \}</script>. The two <em>principal curvatures</em> <script type="math/tex">\kappa_1</script> and <script type="math/tex">\kappa_2</script> and the associated orthogonal directions <script type="math/tex">\mathbf{e}_1</script> and <script type="math/tex">\mathbf{e}_2</script> are the extremum values of the normal curvature. The <em>mean curvature</em> <script type="math/tex">\kappa_H</script> is defined as \begin{equation} \kappa_H = \frac{1}{2\pi}\int^{2\pi}_0\kappa^N(\theta)d\theta, \end{equation} which can be simplified using the principal curvatures as $\kappa_H = \dfrac{\kappa_1 + \kappa_2}{2}$. Additionally, the <em>Gaussian curvature</em> $\kappa_G$ is defined as $\kappa_G = \kappa_1\kappa_2$.</p> <p>The Laplace-Beltrami operator $\mathbf{L}(p)$ is defined as $\mathbf{L}(p) = 2\kappa_H(p)\mathbf{n}(p)$. The integration of the Laplace-Beltrami operator is then defined as</p> <p>\begin{equation} \int\int_{A}\mathbf{L}(\mathbf{x})d(A) = \frac{1}{2}\sum_{j\in N(i)}(\cot{\alpha_{ij}} + \cot{\beta_{ij}})(\mathbf{x}_i - \mathbf{x}_j). \label{eq:Laplacian_integration} \end{equation}</p> <p>Let us now consider a vertex and it’s own one ring neighbourhood defined as follow:</p> <p class="some-css-class" style="max-width: 400px; display: block; margin-left: auto; margin-right: auto "><img src="/assets/img/cotangent_angles.png" alt="one-ring neighbourhood" title="one-ring neighbourhood" /></p> <p>A more practical Laplace-Beltrami Operator implementation is to store the right hand side of equation \eqref{eq:Laplacian_integration} under a $n \times n$ sparse matrix form <script type="math/tex">\mathbf{L}</script> where each elements is the vertex to vertex relationship such as:</p> <script type="math/tex; mode=display">% <![CDATA[ \boldsymbol{L}_{ij} = \begin{cases} \frac{1}{2}\sum_{i\neq j} \cot \alpha_{ij} + \cot \beta_{ij} & \text{if } i = j,\\ -\frac{1}{2} \cot \alpha_{ij} + \cot \beta_{ij} & j \in \mathcal{N}(i),\\ 0 & j \notin \mathcal{N}(i). \end{cases} %]]></script> <p>With a one ring neighbourhood defined as follow:</p> <p>The C++ pseudo-code of that cotangent Laplacian (for a sparse matrix):</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">:</span> <span class="n">vertices</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">:</span> <span class="n">one_ring</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">:</span> <span class="n">triangle_on_edge</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">))</span> <span class="p">{</span> <span class="n">L</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">)</span> <span class="o">=</span> <span class="n">cot</span><span class="p">(</span><span class="n">angle</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">k</span><span class="p">));</span> <span class="n">L</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">i</span><span class="p">)</span> <span class="o">-=</span> <span class="n">cot</span><span class="p">(</span><span class="n">angle</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">k</span><span class="p">));</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>In practice more efficient implementation can be used, see <a href="https://libigl.github.io/tutorial/#laplacian">libigl implementation</a> for more details.</p> <h3 id="mass-matrix">Mass matrix</h3> <div class="alert alert-warning"> <strong>Warning!</strong> This formula is easy to compute but is slightly wrong for obtuse triangles (angles $&gt; 90^\circ$). </div> <p>The mass matrix defined here is the area of the polygon where each point is the barycenter of each triangle (barycentric cells). The are computed is the part filled in blue in the following figure:</p> <p class="some-css-class" style="max-width: 400px; display: block; margin-left: auto; margin-right: auto "><img src="/assets/img/mass_matrix.png" alt="barycentric cells" title="barycentric cells" /></p> <p>With A, an $n$ by $n$ diagonal matrix:</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">:</span> <span class="n">vertices</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">:</span> <span class="n">one_ring</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">:</span> <span class="n">triangle_on_edge</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">))</span> <span class="p">{</span> <span class="n">area</span> <span class="o">=</span> <span class="p">(</span><span class="n">j</span><span class="o">-</span><span class="n">i</span><span class="p">).</span><span class="n">cross</span><span class="p">(</span><span class="n">k</span><span class="o">-</span><span class="n">i</span><span class="p">).</span><span class="n">norm</span><span class="p">()</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// get area of the face</span> <span class="n">local_area</span> <span class="o">=</span> <span class="n">area</span> <span class="o">/</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// get the are towards the barycenter</span> <span class="n">A</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="o">+=</span> <span class="n">local_area</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// / 2 because added twice</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h2 id="references">References</h2> <div class="footnotes"> <ol> <li id="fn:Meyer2003"> <p>M. Meyer, M. Desbrun, P. Schroder, and A. H. Barr, <a href="http://multires.caltech.edu/pubs/diffGeoOps.pdf">Discrete Differential-Geometry Operators for Triangulated 2-Manifolds</a>, Springer, 2003. <a href="#fnref:Meyer2003" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>rFalqueSerie of notes on discrete differential-geometry.Geodesic In Heat For Pointcloud2019-07-25T00:00:00+00:002019-07-25T00:00:00+00:00/ddg/2019/07/25/geodesic-in-heat-for-pointcloud<p>Description of the geodesic in heat method applied on a point cloud.</p> <div class="alert alert-info"> <strong>Info!</strong> Unfinished notes </div> <h2 id="propagation-of-heat">Propagation of heat</h2> <p>Let’s consider the propagation of the heat generated by a source located at the point <script type="math/tex">x</script> over a point cloud. At the time <script type="math/tex">t</script>, the temperature at the point <script type="math/tex">y</script> can be obtained using the heat kernel</p> <script type="math/tex; mode=display">k_{t,x}(y).</script> <p>The Varadhan’s formula<sup id="fnref:Varadhan_1967"><a href="#fn:Varadhan_1967" class="footnote">1</a></sup> states that the geodesic distance <script type="math/tex">\phi</script> between any pair of points <script type="math/tex">x, y</script> can be obtained by</p> <script type="math/tex; mode=display">\phi(x,y) = \lim_{t\rightarrow 0} \sqrt{-4t\log k_{t,x} (y)}.</script> <p>Estimating the distance with this method can be inaccurate as it is hard to evaluate <script type="math/tex">k_{t,x} (y)</script> and Crane et. al<sup id="fnref:Crane_2013"><a href="#fn:Crane_2013" class="footnote">2</a></sup> proposed to use the gradient of the heat as a better approximation.</p> <h2 id="the-heat-method">The heat method</h2> <p>The heat method is performed in three steps:</p> <ol> <li><script type="math/tex">X = - \nabla u~/~\vert\nabla u\vert</script>,</li> <li><script type="math/tex">\Delta \phi = \nabla \cdot X</script>.</li> </ol> <h2 id="the-heat-method-on-a-point-cloud">The heat method on a point cloud</h2> <p>Estimation of the gradient with Weighted Least Squares from A. Nealen<sup id="fnref:Nealen_2013"><a href="#fn:Nealen_2013" class="footnote">3</a></sup>.</p> <h2 id="references">References</h2> <div class="footnotes"> <ol> <li id="fn:Varadhan_1967"> <p>S. R. S. Varadhan, <a href="https://onlinelibrary.wiley.com/doi/abs/10.1002/cpa.3160200210">On the behavior of the fundamental solution of the heat equation with variable coefficients</a>, Communications on pure and applied mathematics, 1967. <a href="#fnref:Varadhan_1967" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:Crane_2013"> <p>K. Crane and C. Weischedel, and M. Wardetzky, <a href="https://www.cs.cmu.edu/~kmcrane/Projects/HeatMethod/paperTOG.pdf">Geodesics in Heat:A New Approach to Computing Distance Based on Heat Flow</a>, ACM, 2013. <a href="#fnref:Crane_2013" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:Nealen_2013"> <p>A. Nealen, <a href="http://www.nealen.net/projects/mls/asapmls.pdf">An As-Short-As-Possible Introduction to the Least Squares, Weighted LeastSquares and Moving Least Squares Methods for Scattered DataApproximation and Interpolation</a>, 2004 <a href="#fnref:Nealen_2013" class="reversefootnote">&#8617;</a></p> </li> </ol> </div>rFalqueDescription of the geodesic in heat method applied on a point cloud.Simple C++ Graph Library2019-06-15T00:00:00+00:002019-06-15T00:00:00+00:00/graph/cpp/2019/06/15/simple-c++-graph-library<p>We provide a simple and light graph library for visualizing and analyzing graph structures. The library builds upon <a href="https://github.com/libigl/libigl">Libigl</a>, following its design principle and adapt it for graphs. The graphs are stored as <code class="language-plaintext highlighter-rouge">.obj</code> files (not all other formats support edges) which can be easily edited in <a href="https://www.blender.org/">Blender</a>.</p> <p>The repository hosting the code is available here: <a href="https://github.com/rFalque/libGraphCpp">https://github.com/rFalque/libGraphCpp</a></p> <p><img src="/assets/img/graph_library.png" alt="Graph visualization" title="light C++ graph library" /></p> <h2 id="installation">Installation:</h2> <p>Install <a href="https://github.com/libigl/libigl">libigl</a> and <a href="https://eigen.tuxfamily.org/">Eigen</a> which are dependencies. Depending on how Libigl has been installed, you might need to update the location of the library in the <code class="language-plaintext highlighter-rouge">src/cmake/FindLIBIGL.cmake</code> file.</p> <p>Then simply type the following commands in the command line:</p> <p>To build the library and the example</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>build <span class="nb">cd </span>build cmake ../src make </code></pre></div></div> <p>to build and run the tests:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>build_tests <span class="nb">cd </span>build_tests cmake ../src/tests make </code></pre></div></div> <h2 id="features">Features:</h2> <ul> <li>graph instanciation from Eigen matrices (‘Eigen::MatrixXd’ for nodes position and ‘Eigen::MatrixXi’ for edges definition)</li> <li>graph visualization (using <a href="https://github.com/libigl/libigl">libigl</a> and few headers from <a href="https://github.com/avaxman/Directional">directional</a>)</li> <li>removing nodes</li> <li>connectivity (linear time)</li> <li>biconnectivity (linear time)</li> <li>triconnectivity (quadratic time)</li> <li>bridges detection (linear time)</li> <li>shortest path on the graph</li> </ul> <h2 id="features-to-add">Features to add:</h2> <ul> <li>graph augmentation</li> <li>add nodes</li> </ul> <h2 id="design-principles">Design principles:</h2> <ul> <li>header only library</li> <li>simple structures for storing the graph (Eigen::Matrix) and adjacency lists (std::vector)</li> <li>easy to modify</li> </ul> <h2 id="graph-visualization">Graph visualization:</h2> <p><img src="/assets/img/graph_libigl_viewer.png" alt="Graph visualization" title="Graph visualization with libigl" /></p> <h2 id="graph-editing">Graph editing:</h2> <p>Open <a href="https://www.blender.org/">Blender</a> and start creating a graph. A mesh can be easily transformed into a graph by deleting the faces only (change to <code class="language-plaintext highlighter-rouge">edit mode</code>, select everything <code class="language-plaintext highlighter-rouge">a</code>, open the delete menu <code class="language-plaintext highlighter-rouge">del</code>, pick <code class="language-plaintext highlighter-rouge">Only faces</code>). <img src="/assets/img/graph_editing_blender.png" alt="Graph editing" title="Graph editing with blender" /></p> <h2 id="other-alternatives">Other alternatives:</h2> <ul> <li><a href="http://www.ogdf.net">OGDF</a>: (C++) self-contained class library for the automatic layout of diagrams. OGDF offers sophisticated algorithms and data structures to use within your own applications or scientific projects (including triconnectivity tests in linear time with SPQR decomposition)</li> <li><a href="https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/index.html">BOOST</a>: (more?) efficient implementation</li> <li><a href="https://www.mathworks.com/help/matlab/graph-and-network-algorithms.html">Matlab graph class</a>: sweet one</li> </ul>rFalqueWe provide a simple and light graph library for visualizing and analyzing graph structures. The library builds upon Libigl, following its design principle and adapt it for graphs. The graphs are stored as .obj files (not all other formats support edges) which can be easily edited in Blender.Welcome to Jekyll!2019-06-14T01:49:06+00:002019-06-14T01:49:06+00:00/jekyll/update/2019/06/14/welcome-to-jekyll<p>You’ll find this post in your <code class="language-plaintext highlighter-rouge">_posts</code> directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run <code class="language-plaintext highlighter-rouge">jekyll serve</code>, which launches a web server and auto-regenerates your site when a file is updated.</p> <p>To add new posts, simply add a file in the <code class="language-plaintext highlighter-rouge">_posts</code> directory that follows the convention <code class="language-plaintext highlighter-rouge">YYYY-MM-DD-name-of-post.ext</code> and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.</p> <p>Jekyll also offers powerful support for code snippets:</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">print_hi</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="nb">puts</span> <span class="s2">"Hi, </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span> <span class="k">end</span> <span class="n">print_hi</span><span class="p">(</span><span class="s1">'Tom'</span><span class="p">)</span> <span class="c1">#=&gt; prints 'Hi, Tom' to STDOUT.</span></code></pre></figure> <p>Check out the <a href="https://jekyllrb.com/docs/home">Jekyll docs</a> for more info on how to get the most out of Jekyll. File all bugs/feature requests at <a href="https://github.com/jekyll/jekyll">Jekyll’s GitHub repo</a>. If you have questions, you can ask them on <a href="https://talk.jekyllrb.com/">Jekyll Talk</a>.</p> <p>post options:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="ss">hidden: </span><span class="kp">true</span> <span class="ss">published: </span><span class="kp">false</span> </code></pre></div></div> <h1 id="html--css-links">HTML / CSS Links:</h1> <ul> <li><a href="https://www.w3schools.com/bootstrap/bootstrap_progressbars.asp">progress bars</a></li> <li><a href="https://www.w3schools.com/bootstrap/bootstrap_alerts.asp">alerts</a></li> <li><a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">bootstrap css</a></li> </ul>You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve, which launches a web server and auto-regenerates your site when a file is updated.