{"id":18364908,"url":"https://github.com/alecjacobson/geometry-processing-mesh-reconstruction","last_synced_at":"2026-01-21T05:40:16.860Z","repository":{"id":67929056,"uuid":"79467602","full_name":"alecjacobson/geometry-processing-mesh-reconstruction","owner":"alecjacobson","description":"Mesh Reconstruction assignment for Geometry Processing course","archived":false,"fork":false,"pushed_at":"2022-09-20T18:18:39.000Z","size":8303,"stargazers_count":106,"open_issues_count":36,"forks_count":74,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-02-15T19:49:20.348Z","etag":null,"topics":["geometry-processing","libigl"],"latest_commit_sha":null,"homepage":"https://github.com/alecjacobson/geometry-processing","language":"Pawn","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alecjacobson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-01-19T15:37:45.000Z","updated_at":"2025-01-20T11:23:36.000Z","dependencies_parsed_at":"2023-03-07T07:01:10.115Z","dependency_job_id":null,"html_url":"https://github.com/alecjacobson/geometry-processing-mesh-reconstruction","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-mesh-reconstruction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-mesh-reconstruction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-mesh-reconstruction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-mesh-reconstruction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alecjacobson","download_url":"https://codeload.github.com/alecjacobson/geometry-processing-mesh-reconstruction/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248199090,"owners_count":21063641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["geometry-processing","libigl"],"created_at":"2024-11-05T23:12:08.069Z","updated_at":"2026-01-21T05:40:16.834Z","avatar_url":"https://github.com/alecjacobson.png","language":"Pawn","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Geometry Processing – Mesh Reconstruction\n\n\u003e **To get started:** Clone this repository then issue\n\u003e \n\u003e     git clone --recursive http://github.com/alecjacobson/geometry-processing-mesh-reconstruction.git\n\u003e\n\n## Installation, Layout, and Compilation\n\nSee\n[introduction](http://github.com/alecjacobson/geometry-processing-introduction).\n\n## Execution\n\nOnce built, you can execute the assignment from inside the `build/` using \n\n    ./mesh-reconstruction [path to point cloud]\n\n## Background\n\nIn this assignment, we will be implementing a simplified version of the method\nin  [\"Poisson Surface Reconstruction\" by Kazhdan et al. 2006.](papers/poisson-surface-reconstruction.pdf) (Your first \"task\"\nwill be to read and understand this paper).\n\nMany scanning technologies output a set of $n$ point samples $\\mathbf{P}$ on the \nsurface of the object in question. From these points and perhaps the location\nof the camera, one can also estimate normals $\\mathbf{N}$ to the surface for each point\n$\\mathbf{p} \\in  \\mathbf{P}$. This image shows the `data/elephant.pwn` input\ndata with a white dot for each point and a red line segment pointing outward for\neach corresponding normal vector.\n\n![](images/elephant-points-normals.jpg)\n\nFor shape analysis, visualization and other downstream geometry processing\nphases, we would like to convert this finitely sampled _point cloud_ data into\nan _explicit continuous surface representation_: i.e., a [triangle\nmesh](https://en.wikipedia.org/wiki/Triangulation%5F(topology)) (a special case\nof a [polygon mesh](https://en.wikipedia.org/wiki/Polygon%5Fmesh)). This image\nshows the corresponding output mesh for `data/elephant.pwn` input data above:\n\n![](images/elephant-mesh.jpg)\n\n### Voxel-based Implicit Surface\n\nConverting the point cloud directly to a triangle mesh makes it very difficult\nto ensure that the mesh meets certain _topological_ postconditions: i.e., that\nit is [manifold](https://en.wikipedia.org/wiki/Piecewise%5Flinear%5Fmanifold),\n[closed](https://en.wikipedia.org/wiki/Manifold#Manifold%5Fwith%5Fboundary),\nand has a small number of\n[holes](https://en.wikipedia.org/wiki/Genus%5F(mathematics)).\n\nInstead we will first convert the point cloud _sampling representation_ into a\nan _[implicit surface\nrepresentation](https://en.wikipedia.org/wiki/Implicit%5Fsurface)_: where the\nunknown surface is defined as the\n[level-set](https://en.wikipedia.org/wiki/Level%5Fset) of some function $g: \\mathbf{R}^3 \\Rightarrow  \\mathbf{R}$\nmapping all points in space to a scalar value. For example, we may define\nthe surface $\\partial \\mathbf{S}$ of some solid, \nvolumetric shape $\\mathbf{S}$ to be all points $\\mathbf{x} \\in \\mathbf{R}^3$ such that $g(x) = {\\sigma}$, where we may arbitrarily set ${\\sigma}=\\frac12 $.\n\n$$\n\\partial \\mathbf{S} = \\{\\mathbf{x} \\in  \\mathbf{R}^3  | g(\\mathbf{x})  = {\\sigma}\\}.\n$$\n\n\nOn the computer, it is straightforward\n[discretize](https://en.wikipedia.org/wiki/Discretization) an implicit\nfunction. We define a regular 3D grid of\n[voxels](http://en.wikipedia.org/wiki/Voxel) containing at least the [bounding\nbox](https://en.wikipedia.org/wiki/Minimum%5Fbounding%5Fbox) of $\\mathbf{S}$. At each\nnode in the grid $\\mathbf{x}\\_{i,j,k}$ we store the value of \nthe implicit function $g(\\mathbf{x}\\_{i,j,k})$. This defines $g$ _everywhere_ in the grid via [trilinear\ninterpolation](https://en.wikipedia.org/wiki/Trilinear_interpolation). \n\n![](images/trilinear-interpolation.jpg)\n\nFor example, consider a point $\\mathbf{x} = (\\frac13 ,\\frac14 ,\\frac12)$ lying in the middle of the\nbottom-most, front-most, left-most cell. We know the values at the eight\ncorners. Trilinear interpolation can be understood as [linear\ninterpolation](https://en.wikipedia.org/wiki/Linear_interpolation) in the\n$x$-direction by $\\frac13$ on each $x$-axis-aligned edge, resulting in four values\n_living_ on the same plane. These can then be linearly interpolated in the $y$\ndirection by $\\frac14$ resulting in two points on the same line, and finally in the\n$z$ direction by $\\frac12$ to get to our evaluation point $(\\frac13 ,\\frac14 ,\\frac12 )$.\n\nAn implicit surface stored as the level-set of a trilinearly interpolated grid\ncan be _contoured_ into a triangle mesh via the [Marching Cubes\nAlgorithm](https://en.wikipedia.org/wiki/Marching\u00265Fcubes). \nFor the purposes of this assignment, we will treat this as a [black\nbox](https://en.wikipedia.org/wiki/Black%5Fbox). Instead, we focus on\ndetermining what values for $g$ to store on the grid.\n\n## Characteristic functions of solids\n\nWe assume that our set of points $\\mathbf{P}$ lie on the surface $\\partial \\mathbf{S}$ of some physical\n[solid](https://en.wikipedia.org/wiki/Solid) object $\\mathbf{S}$. This solid object\nmust have some non-trivial volume that we can calculate _abstractly_ as the\nintegral of unit density over the solid:\n\n$$\n\\int \\limits_\\mathbf{S} 1 \\;dA.                                                        %_\n$$\n\n\nWe can rewrite this definite integral as an indefinite integral over all of\n$\\mathbf{R}^3$:\n\n$$\n\\int \\limits_{\\mathbf{R}^3 } {\\chi}_\\mathbf{S}(\\mathbf{x}) \\;dA,\n$$\n\n\nby introducing the [characteristic\nfunction](https://en.wikipedia.org/wiki/Indicator%5Ffunction) of $\\mathbf{S}$, that is\n_one_ for points inside of the shape and _zero_ for points outside of $\\mathbf{S}$:\n\n$$\n{\\chi}_\\mathbf{S}(\\mathbf{x}) = \\begin{cases}\n  1 \u0026 \\text{ if $\\mathbf{x} \\in  \\mathbf{S}$ } \\\\\n  0 \u0026 \\text{ otherwise}.\n\\end{cases}                                                             %_\n$$\n\n\nCompared to typical [implicit surface\nfunctions](https://en.wikipedia.org/wiki/Implicit%5Fsurface), this function\nrepresents the surface $\\partial \\mathbf{S}$ of the shape $\\mathbf{S}$ as the _discontinuity_ between\nthe one values and the zero values. Awkwardly, the gradient of the\ncharacteristic function ${\\nabla}{\\chi}_\\mathbf{S}$ is _not defined_ along $\\partial \\mathbf{S}$.\n\nOne of the key observations made in [Kazhdan et al. 2006] is that the gradient\nof a infinitesimally [mollified](https://en.wikipedia.org/wiki/Mollifier)\n(smoothed) characteristic function: \n\n  1. points in the direction of the normal near the surface $\\partial \\mathbf{S}$, and \n  2. is zero everywhere else.\n\nOur goal will be to use our points $\\mathbf{P}$ and normals $\\mathbf{N}$ to _optimize_ an\nimplicit function $g$ over a regular grid, so that its gradient ${\\nabla}g$ meets\nthese two criteria. In that way, our $g$ will be an approximation of the\nmollified characteristic function.\n\n## Poisson surface reconstruction\n\n### Or: how I learned to stop worrying and minimize squared Gradients\n\n\u003cspan id=assumptions\u003e\nLet us start by making two assumptions:\n\u003c/span\u003e\n\n  1. we know how to compute ${\\nabla}g$ at each node location $\\mathbf{x}_{i,j,k}$, and\n  2. our input points $\\mathbf{P}$ all lie perfectly at grid nodes: \n  $\\exists\\ \\mathbf{x}\\_{i,j,k}$ = $\\mathbf{p}\\_\\ell$.\n\nWe will find out these assumptions are not realistic and we will have to relax\nthem (i.e., we **_will not_** make these assumptions in the completion of the\ntasks). However, it will make the following algorithmic description easier on\nthe first pass.\n\nIf our points $\\mathbf{P}$ lie at grid points, then our corresponding normals $\\mathbf{N}$ also\n_live_ at grid points. This leads to a very simple set of linear equations to\ndefine a function $g$ with a gradient equal to the surface normal at the\nsurface and zero gradient away from the surface:\n\n\n$$\n{\\nabla}g(\\mathbf{x}\\_{i,j,k}) = \\mathbf{v}\\_{i,j,k} := \\begin{cases}\n  \\vphantom{\\left(\\begin{array}{c}\n  0\\\\\n  0\\\\\n  0\\end{array}\\right)}\n  \\mathbf{n}\\_\\ell \u0026 \\text{ if $\\exists\\ \\mathbf{p}\\_\\ell = \\mathbf{x}\\_{i,j,k}$}, \\\\\n  \\left(\\begin{array}{c}\n    0\\\\\n    0\\\\\n    0\\end{array}\\right) \u0026 \\text{ otherwise}.\n\\end{cases}\n$$\n\n\nThis is a _vector-valued_ equation. The gradients, normals and zero-vectors are\nthree-dimensional (e.g., ${\\nabla}g \\in  \\mathbf{R}^3 $). In effect, this is _three equations_ for\nevery grid node.\n\nSince we only need a single number at each grid node (the value of $g$), we\nhave _too many_ equations.\n\nLike many geometry processing algorithms confronted with such an [over\ndetermined](https://en.wikipedia.org/wiki/Overdetermined%5Fsystem), we will\n_optimize_ for the solution that best _minimizes_ the error of equation:\n\n$$\n\\|  {\\nabla}g(\\mathbf{x}\\_{i,j,k})  - \\mathbf{v}\\_{i,j,k}\\| ^2 .\n$$\n\n\nWe will treat the error of each grid location equally by minimizing the sum\nover all grid locations:\n\n$$\n\\mathop{\\text{min}}\\_\\mathbf{g} \\sum \\_i \\sum \\_j \\sum \\_k \\frac12  \\|  {\\nabla}g(\\mathbf{x}\\_{i,j,k})  - \\mathbf{v}\\_{i,j,k}\\| ^2 ,\n$$\n\n\nwhere $\\mathbf{g}$ (written in boldface) is a vector of _unknown_ grid-nodes values,\nwhere $g_{i,j,k} = g(\\mathbf{x}_{i,j,k})$. \n\nPart of the convenience of working on a regular grid is that we can use the\n[finite difference\nmethod](https://en.wikipedia.org/wiki/Finite_difference_method) to approximate\nthe gradient ${\\nabla}g$ on the grid.\n\nAfter revisiting [our assumptions](#assumptions), we will be able to compute\napproximations of \nthe $x$-, $y$- and $z$-components of ${\\nabla}g$ via a [sparse\nmatrix](https://en.wikipedia.org/wiki/Sparse%5Fmatrix) multiplication of\na \"gradient matrix\" $\\mathbf{G}$ and our vector of unknown grid values $\\mathbf{g}$. We will be\nable to write the\nminimization problem above in matrix form:\n\n$$\n\\mathop{\\text{min}}_\\mathbf{g} \\frac12  \\|  \\mathbf{G} \\mathbf{g} - \\mathbf{v} \\| ^2 ,\n$$\n\n\nor equivalently after expanding the norm:\n\n$$\n\\mathop{\\text{min}}_\\mathbf{g} \\frac12  \\mathbf{g}^{\\mathsf T} \\mathbf{G}^{\\mathsf T} \\mathbf{G} \\mathbf{g} - \\mathbf{g}^{\\mathsf T} \\mathbf{G}^{\\mathsf T} \\mathbf{v} + \\text{constant},\n$$\n\n\nThis is a quadratic \"energy\" function of the variables of $\\mathbf{g}$, its minimum occurs when\nan infinitesimal change in $\\mathbf{g}$ produces no change in the energy:\n\n$$\n\\frac{\\partial }{\\partial \\mathbf{g}} \\frac12  \\mathbf{g}^{\\mathsf T} \\mathbf{G}^{\\mathsf T} \\mathbf{G} \\mathbf{g} - \\mathbf{g}^{\\mathsf T} \\mathbf{G}^{\\mathsf T} \\mathbf{v} = 0.\n$$\n\n\nApplying this derivative gives us a _sparse_ system of linear equations\n\n$$\n\\mathbf{G}^{\\mathsf T} \\mathbf{G} \\mathbf{g} = \\mathbf{G}^{\\mathsf T} \\mathbf{v}.\n$$\n\n\nWe will assume that we can solve this using a black box sparse solver.\n\nNow, let's revisit [our assumptions](#assumptions).\n\n### Gradients on a regular grid\n\nThe gradient of a function $g$ in 3D is nothing more than a vector containing\npartial derivatives in each coordinate direction:\n\n$$\n{\\nabla}g(\\mathbf{x}) = \\left(\\begin{array}{c}\n    \\frac{\\partial g(\\mathbf{x})}{\\partial x} \\\\\n    \\frac{\\partial g(\\mathbf{x})}{\\partial y} \\\\\n    \\frac{\\partial g(\\mathbf{x})}{\\partial z}\n  \\end{array}\\right).\n$$\n\n\nWe will approximate each partial derivative individually. Let's consider the\npartial derivative in the $x$ direction, $\\partial g(\\mathbf{x})/\\partial x$, and we will assume\nwithout loss of generality that what we derive applies _symmetrically_ for $y$\nand $z$.\n\nThe partial derivative in the $x$-direction is a one-dimensional derivative.\nThis couldn't be easier to do with finite differences. We approximate the\nderivative of the function $g$ with respect to the $x$ direction is the\ndifference between the function evaluated at one grid node and at the grid node\n_before_ it in the $x$-direction then divided by the spatial distance between\nadjacent nodes $h$ (i.e., the grid step size):\n\n$$\n\\frac{\\partial g(\\mathbf{x}\\_{i-\\frac12 ,j,k})}{\\partial x} = \\frac{g\\_{i,j,k} - g\\_{i-1,j,k}}{h},\n$$\n\n\nwhere we use the index $i-\\frac12$ to indicate that this derivative in the\n$x$-direction lives on a [staggered\ngrid](https://en.wikipedia.org/wiki/Staggered%5Fgrid) _in between_ the grid\nnodes where the function values for $g$.\n\nThe following pictures show a 2D example, where $g$ lives on the nodes of a\n$5\\times 5$ blue grid:\n\n![](images/primary-grid.jpg)\n\nThe partial derivatives of $g$ with respect to the $x$-direction $\\partial g(\\mathbf{x})/\\partial x$\nlive on a $4 \\times 5$ green, staggered grid:\n\n![](images/staggered-grid-x.jpg)\n\nThe partial derivatives of $g$ with respect to the $y$-direction $\\partial g(\\mathbf{x})/\\partial y$\nlive on a $5\\times 4$ yellow, staggered grid:\n\n![](images/staggered-grid-x-and-y.jpg)\n\nLetting $\\mathbf{g} \\in  \\mathbf{R}^{n_xn_yn_z \\times  1}$ be column vector of function values on the\n_primary grid_ (blue in the example pictures), we can construct a sparse matrix\n$\\mathbf{D}^x \\in  \\mathbf{R}^{(n_x-1)n_yn_z \\times  n_xn_yn_z}$ so that each row $\\mathbf{D}^x\\_{i-\\frac12 ,j,k} \\in \\mathbf{R}^{1 \\times  n\\_xn\\_yn\\_z}$ computes the partial derivative at the corresponding\nstaggered grid location $\\mathbf{x}_{i-\\frac12 ,j,k}$. The ℓth entry in that row receives a\nvalue only for neighboring primary grid nodes:\n\n$$\n\\mathbf{D}^x_{i-\\frac12 ,j,k}(\\ell) = \n  \\begin{cases}\n  -1 \u0026 \\text{ if $\\ell = i-1$ }\\\\\n   1 \u0026 \\text{ if $\\ell = i$ }\\\\\n   0 \u0026 \\text{ otherwise}\n  \\end{cases}.\n$$\n\n\n\u003e #### Indexing 3D arrays\n\u003e \n\u003e Now, obviously in our code we cannot _index_ the column vector $\\mathbf{g}$ by a\n\u003e triplet of numbers $\\{i,j,k\\}$ or the rows of $\\mathbf{D}^x$ by the triplet\n\u003e ${i-\\frac12 ,j,k}$. We will assume that $\\mathbf{g}\\_{i,j,k}$ refers to\n\u003e `g(i+j*n_x+k*n_y*n_x)`. Similarly, for the staggered grid subscripts\n\u003e ${i-\\frac12 ,j,k}$ we will assume that $\\mathbf{D}^x\\_{i-\\frac12 ,j,k}(\\ell)$ refers to the matrix\n\u003e entry `Dx(i+j*n_x+k*n_y*n_x,l)`, where the $i-\\frac12$ has been _rounded down_.\n\u003e\n\nWe can similarly build matrices $\\mathbf{D}^y$ and $\\mathbf{D}^z$ and _stack_ these matrices\nvertically to create a gradient matrix $\\mathbf{G}$:\n\n$$\n\\mathbf{G} = \n\\left(\\begin{array}{c}\n  \\mathbf{D}^x \\\\\n  \\mathbf{D}^y \\\\\n  \\mathbf{D}^z\n\\end{array}\\right)\n  \\in  \\mathbf{R}^{ \\left((n_x-1)n_yn_z + n_x(n_y-1)n_z + n_xn_y(n_z-1)\\right)\\times n_xn_yn_z}\n$$\n\n\nThis implies that our vector $\\mathbf{v}$ of zeros and normals in our minimization\nproblem should not _live_ on the primary, but rather it, too, should be broken\ninto $x$-, $y$- and $z$-components that live of their resepctive staggered\ngrids:\n\n$$\n\\mathbf{v} = \n\\left(\\begin{array}{c}\n  \\mathbf{v}^x \\\\\n  \\mathbf{v}^y \\\\\n  \\mathbf{v}^z \n\\end{array}\\right)\n  \\in  \\mathbf{R}^{ \\left((n_x-1)n_yn_z + n_x(n_y-1)n_z + n_xn_y(n_z-1)\\right)\\times 1}.\n$$\n\n\nThis leads to addressing our second assumption.\n\n### B-b-b-b-but the input normals might not be at grid node locations?\n\nAt this point, we would _actually_ liked to have had that our input normals\nwere given component-wise on the staggered grid. Then we could immediate stick\nthem into $\\mathbf{v}$. But this doesn't make much sense as each normal $\\mathbf{n}\\_\\ell$ _lives_\nat its associated point $\\mathbf{p}\\_\\ell$, regardless of any grids.\n\nTo remedy this, we will distribute each component of each input normal $\\mathbf{n}_\\ell$\nto $\\mathbf{v}$ at the corresponding staggered grid node location.\n\nFor example, consider the normal $\\mathbf{n}$ at some point $\\mathbf{x}_{1,\\frac14 ,\\frac12 }$. Conceptually,\nwe'll think of the $x$-component of the normal $n_x$ as floating in the\nstaggered grid corresponding to $\\mathbf{D}^x$, in between the eight staggered grid\nlocations:\n\n$$\n\\mathbf{x}\\_{\\frac12 ,0,0},  \\\\\n\\mathbf{x}\\_{1\\frac12 ,0,0},   \\\\\n\\mathbf{x}\\_{\\frac12 ,1,0},  \\\\\n\\mathbf{x}\\_{1\\frac12 ,1,0},   \\\\\n\\mathbf{x}\\_{\\frac12 ,0,1},  \\\\\n\\mathbf{x}\\_{1\\frac12 ,0,1},   \\\\\n\\mathbf{x}\\_{\\frac12 ,1,1}, \\text{ and } \\\\\n\\mathbf{x}\\_{1\\frac12 ,1,1}\n$$\n\n\nEach of these staggered grid nodes has a corresponding $x$ value in the vector\n$\\mathbf{v}^x$.\n\nWe will distribute $n_x$ to these entries in $\\mathbf{v}^x$ by _adding_ a partial amount\nof $n_x$ to each. I.e., \n\n$$\nv^x\\_{\\frac12 ,0,0}  = w_{ \\frac12 ,0,0}\\left(\\mathbf{x}\\_{1,\\frac14 ,\\frac12 }\\right)\\ n_x,  \\\\\nv^x\\_{1\\frac12 ,0,0} = w_{1\\frac12 ,0,0}\\left(\\mathbf{x}\\_{1,\\frac14 ,\\frac12 }\\right)\\ n_x,  \\\\\n\\vdots \\\\\nv^x\\_{1\\frac12 ,1,1} = w_{1\\frac12 ,1,1}\\left(\\mathbf{x}\\_{1,\\frac14 ,\\frac12 }\\right)\\ n_x.\n$$\n\nwhere $w_{iِ+\\frac12 ,j,k}(\\mathbf{p})$ is the trilinear interpolation _weight_ associate with\nstaggered grid node $\\mathbf{x}_{iِ+\\frac12 ,j,k}$ to interpolate a value at the point $\\mathbf{p}$.\nThe trilinear interpolation weights so that:\n\n$$\nn_x =   \\\\\n  w_{ \\frac12 ,0,0}( \\mathbf{x}\\_{1,\\frac14 ,\\frac12 } ) \\  v^x\\_{ \\frac12 ,0,0} +  \\\\\n  w_{1\\frac12 ,0,0}( \\mathbf{x}\\_{1,\\frac14 ,\\frac12 } ) \\  v^x\\_{1\\frac12 ,0,0} +  \\\\\n  \\vdots \\\\\n  w_{1\\frac12 ,1,1}( \\mathbf{x}\\_{1,\\frac14 ,\\frac12 } )\\ v^x\\_{1\\frac12 ,1,1}.\n$$\n\n\nSince we need to do these for the $x$-component of each input normal, we will\nassemble a sparse matrix $\\mathbf{W}^x \\in  n \\times  (n_x-1)n_yn_z$ that _interpolates_\n$\\mathbf{v}^x$ at each point $\\mathbf{p}$:\n\n$$\n  ( \\mathbf{W}^x \\mathbf{v}^x ) \\in  \\mathbf{R}^{n\\times 1}\n$$\n\n\nthe transpose of $\\mathbf{W}^x$ is not quite its\n[_inverse_](https://en.wikipedia.org/wiki/Invertible_matrix), but instead can\nbe interpreted as _distributing_ values onto staggered grid locations where\n$\\mathbf{v}^x$ lives:\n\n$$\n  \\mathbf{v}^x = (\\mathbf{W}^x)^{\\mathsf T} \\mathbf{N}^x.\n$$\n\n\nUsing this definition of $\\mathbf{v}^x$ and analogously for $\\mathbf{v}^y$ and $\\mathbf{v}^z$ we can\nconstruct the vector $\\mathbf{v}$ in our energy minimization problem above.\n\n\u003e ### BTW, what's [Poisson](https://en.wikipedia.org/wiki/Siméon_Denis_Poisson) got to do with it?\n\u003e \n\u003e The discrete energy minimization problem we've written looks like the squared\n\u003e norm of some gradients. An analogous energy in the smooth world is the\n\u003e [Dirichlet energy](https://en.wikipedia.org/wiki/Dirichlet's_energy):\n\u003e\n\u003e $$\n   E(g) = \\int _{\\Omega} \\| {\\nabla}g\\| ^2  dA\n$$\n\u003e\n\u003e\n\u003e to _minimize_ this energy with respect to $g$ as an unknown _function_, we\n\u003e need to invoke [Calculus of\n\u003e Variations](https://en.wikipedia.org/wiki/Calculus_of_variations) and\n\u003e [Green's First\n\u003e Identity](https://en.wikipedia.org/wiki/Green's_identities#Green.27s_first_identity).\n\u003e In doing so we find that minimizers will satisfy:\n\u003e\n\u003e $$\n  {\\nabla}\\cdot {\\nabla} g = 0 \\text{ on ${\\Omega}$},\n$$\n\u003e\n\u003e \n\u003e known as [Laplaces'\n\u003e Equation](https://en.wikipedia.org/wiki/Laplace%27s_equation).\n\u003e\n\u003e If we instead start with a slightly different energy:\n\u003e \n\u003e $$\n  E(g) = \\int _{\\Omega} \\| {\\nabla}g - V\\| ^2  dA,\n$$\n\u003e\n\u003e \n\u003e where $V$ is a vector-valued function. Then applying the same machinery we\n\u003e find that minimizers will satisfy:\n\u003e\n\u003e $$\n  {\\nabla}\\cdot {\\nabla} g = {\\nabla}\\cdot V \\text{ on ${\\Omega}$},\n$$\n\u003e\n\u003e known as [Poisson's\n\u003e equation](https://en.wikipedia.org/wiki/Poisson%27s_equation). \n\u003e\n\u003e\n\u003e Notice that if we interpret the transpose of our gradient matrix\n\u003e $\\mathbf{G}^{\\mathsf T}$ as a _divergence matrix_ (we can and we should), then the\n\u003e structure of these smooth energies and equations are directly preserved in\n\u003e our discrete energies and equations.\n\u003e\n\u003e This kind of _structure preservation_ is a major criterion for judging\n\u003e discrete methods.\n\u003e\n\n### Choosing a good iso-value\n\nConstant functions have no gradient. This means that we can add a constant\nfunction to our implicit function $g$ without changing its gradient:\n\n$$\n{\\nabla}g = {\\nabla}(g+c) = {\\nabla}g + {\\nabla}c = {\\nabla}g + 0.\n$$\n\n\nThe same is true for our discrete gradient matrix $\\mathbf{G}$: if the vector of grid\nvalues $\\mathbf{g}$ is constant then $\\mathbf{G} \\mathbf{g}$ will be a vector zeros.\n\nThis is potentially problematic for our least squares solve: there are many\nsolutions, since we can just add a constant. Fortunately, we _don't really\ncare_. It's elegant to say that our surface is defined at $g=0$, but we'd be\njust as happy declaring that our surface is defined at $g=c$.\n\nTo this end we just need to find _a solution_ $\\mathbf{g}$, and then to pick a good\niso-value ${\\sigma}$.\n\nAs suggested in \\[Kazhdan et al. 2006\\], we can pick a good iso-value by\ninterpolating our solution $\\mathbf{g}$ at each of the input points (since we know\nthey're on the surface) and averaging their values. For an appropriate\ninterpolation matrix $\\mathbf{W}$ on the _primary (non-staggered) grid_ this can be\nwritten as:\n\n$$\n{\\sigma} = \\frac{1}{n} \\mathbf{1}^{\\mathsf T} \\mathbf{W} \\mathbf{g},\n$$\n\n\nwhere $\\mathbf{1} \\in  \\mathbf{R}^{n\\times 1}$ is a vector of ones.\n\n### Just how much does this assignment simplify \\[Kazhdan et al. 2006\\]?\n\nBesides the insights above, a major contribution of \\[Kazhdan et al. 2006\\] was\nto setup and solve this problem on an [adaptive\ngrid](https://en.wikipedia.org/wiki/Adaptive_mesh_refinement) rather than a\nregular grid. They also track \"confidence\" of their input data effecting how\nthey smooth and interpolate values. As a result, their method is one of the\nmost highly used surface reconstruction techniques to this day.\n\n\u003e Consider adding your own insights to the wikipedia entry for [this\n\u003e method](https://en.wikipedia.org/wiki/Poisson's%5Fequation#Surface%5Freconstruction).\n\n## Tasks\n\n### Read \\[Kazhdan et al. 2006\\]\n\nThis reading task is not directly graded, but it's expected that you read and\nunderstand this paper before moving on to the other tasks.\n\n### `src/fd_interpolate.cpp`\n\nGiven a regular finite-difference grid described by the number of nodes on each\nside (`nx`, `ny` and `nz`), the grid spacing (`h`), and the location of the\nbottom-left-front-most corner node (`corner`), and a list of points (`P`),\nconstruct a sparse matrix `W` of trilinear interpolation weights so that \n`P = W * x`. \n\n### `src/fd_partial_derivative.cpp`\n\nGiven a regular finite-difference grid described by the number of nodes on each\nside (`nx`, `ny` and `nz`), the grid spacing (`h`), and a desired direction,\nconstruct a sparse matrix `D` to compute first partial derivatives in the given\ndirection onto the _staggered grid_ in that direction.\n\n### `src/fd_grad.cpp`\n\nGiven a regular finite-difference grid described by the number of nodes on each\nside (`nx`, `ny` and `nz`), and the grid spacing (`h`), construct a sparse\nmatrix `G` to compute gradients with each component on its respective staggered\ngrid.\n\n\u003e #### Hint \n\u003e Use `fd_partial_derivative` to compute `Dx`, `Dy`, and `Dz` and\n\u003e then simply concatenate these together to make `G`.\n\n### `src/poisson_surface_reconstruction.cpp`\n\nGiven a list of points `P` and the list of corresponding normals `N`, construct\na implicit function `g` over a regular grid (_built for you_) using approach\ndescribed above.\n\nYou will need to _distribute_ the given normals `N` onto the staggered grid\nvalues in `v` via sparse trilinear interpolation matrices `Wx`, `Wy` and `Wz`\nfor each staggered grid. \n\nThen you will need to construct and solve the linear system $\\mathbf{G}^{\\mathsf T} \\mathbf{G} \\mathbf{g} = \\mathbf{G}^{\\mathsf T} \\mathbf{v}$.\n\nDetermine the iso-level `sigma` to extract from the `g`.\n\nFeed this implicit function `g` to `igl::copyleft::marching_cubes` to contour\nthis function into a triangle mesh `V` and `F`.\n\nMake use of `fd_interpolate` and `fd_grad`.\n\n\u003e #### Hint\n\u003e Eigen has many different [sparse matrix\n\u003e solvers](https://eigen.tuxfamily.org/dox-devel/group__TopicSparseSystems.html).\n\u003e For these _very regular_ matrices, it seems that the [conjugate gradient\n\u003e method](https://en.wikipedia.org/wiki/Conjugate_gradient_method) will\n\u003e outperform direct methods such as [Cholesky\n\u003e factorization](https://en.wikipedia.org/wiki/Cholesky_decomposition). Try\n\u003e `Eigen::BiCGSTAB`.\n\n-------------------------------------------------------------------------------\n\n\u003e #### Hint \n\u003e Debug in debug mode with assertions enabled. For Unix users on the\n\u003e command line use: \n\u003e \n\u003e     cmake -DCMAKE_BUILD_TYPE=Debug ../\n\u003e \n\u003e but then try out your code in _release mode_ for much better performance\n\u003e \n\u003e     cmake -DCMAKE_BUILD_TYPE=Release ../\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecjacobson%2Fgeometry-processing-mesh-reconstruction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falecjacobson%2Fgeometry-processing-mesh-reconstruction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecjacobson%2Fgeometry-processing-mesh-reconstruction/lists"}