{"id":18364923,"url":"https://github.com/alecjacobson/geometry-processing-curvature","last_synced_at":"2026-03-07T06:03:06.387Z","repository":{"id":28949888,"uuid":"84757397","full_name":"alecjacobson/geometry-processing-curvature","owner":"alecjacobson","description":"Curvature assignment for Geometry Processing course","archived":false,"fork":false,"pushed_at":"2023-05-04T19:38:05.000Z","size":11751,"stargazers_count":156,"open_issues_count":37,"forks_count":74,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-10-21T22:39:59.382Z","etag":null,"topics":["curvature","geometry-processing-curvature","libigl"],"latest_commit_sha":null,"homepage":"https://github.com/alecjacobson/geometry-processing","language":"C++","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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-12T21:06:17.000Z","updated_at":"2025-06-30T02:59:28.000Z","dependencies_parsed_at":"2024-11-05T23:12:21.120Z","dependency_job_id":"8765d2c6-ab7e-47dd-a235-032325fe3ee0","html_url":"https://github.com/alecjacobson/geometry-processing-curvature","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alecjacobson/geometry-processing-curvature","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-curvature","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-curvature/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-curvature/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-curvature/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alecjacobson","download_url":"https://codeload.github.com/alecjacobson/geometry-processing-curvature/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecjacobson%2Fgeometry-processing-curvature/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30208802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T05:23:27.321Z","status":"ssl_error","status_checked_at":"2026-03-07T05:00:17.256Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["curvature","geometry-processing-curvature","libigl"],"created_at":"2024-11-05T23:12:10.239Z","updated_at":"2026-03-07T06:03:06.356Z","avatar_url":"https://github.com/alecjacobson.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Geometry Processing - Curvature \n\n\u003e **To get started:** Clone this repository then issue\n\u003e \n\u003e     git clone --recursive http://github.com/[username]/geometry-processing-curvature.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/` by running\non a given mesh:\n\n    ./curvature [path to mesh.obj]\n\n![Once built you may toggle between showing Gaussian curvature, mean/min/max\ncurvature and displaying principal curvature directions.](images/elephant-curvatures.jpg)\n\n## Background\n\nIn this assignment we explore discrete curvature quantities computed on a\nsurface. These quantities give us local information about a shape. Beyond\ninspecting the surface (the extent of this assignment), these quantities become\nthe building blocks to:\n\n - define energies to minimize during smoothing/deformation,\n - identify salient points and curves on the shape, and \n - provide initial conditions/constraints for _remeshing_.\n\nThe fundamental difference between a segment on the real line and a curve is\nthe introduction of [curvature](https://en.wikipedia.org/wiki/Curvature). This\nis quite natural and intuitive. When we draw a 1D object in the plane or in\nspace we have the freedom to let that object bend. We quantify this \"bending\"\nlocally as curvature.\n\n[Curvature](https://en.wikipedia.org/wiki/Curvature#Surfaces) is also the\nfundamental difference between a chunk (i.e., subregion) of the [Euclidean\nPlane](https://en.wikipedia.org/wiki/Plane_(geometry)) and a\n[surface](https://en.wikipedia.org/wiki/Surface_(mathematics)) that has been\n[immersed](https://en.wikipedia.org/wiki/Immersion_(mathematics)) in $\\mathbb{R}^3$ (or\nelsewhere). Unlike curves, surfaces can bend in each direction at any point.\n\nWe start our discussion assuming a smooth surface $\\mathcal{S}$. We would like to\ncategorize points on the surface $\\mathbf{p} \\in  \\mathcal{S}$ in terms of how the surface bends or\ncurves locally. \n\n### Curvature of planar curves\n\nLet us briefly recall how\n[curvature](https://en.wikipedia.org/wiki/Curvature#Precise_definition) is\ndefined for a [planar curve](https://en.wikipedia.org/wiki/Plane_curve)\n${\\gamma}:[0,1] \\rightarrow  \\mathbb{R}^{2}$.\n\nThere are multiple equivalent definitions.\n\n#### Osculating circle\n\nWe can define the [tangent](https://en.wikipedia.org/wiki/Tangent) direction at\na point $\\mathbf{p} = {\\gamma}(s)$ as the limit of the\n[secant](https://en.wikipedia.org/wiki/Secant_line) formed between $\\mathbf{p}$ and\nanother point on the curve $\\mathbf{q} = {\\gamma}(t)$ as $\\mathbf{q}$ approaches $\\mathbf{p}$:\n\n![](images/tangent-as-limit-of-secant.gif)\n\n$$\n\\mathbf{t}(s) = \\lim_{\\mathbf{q}\\rightarrow \\mathbf{p}} \\frac{\\mathbf{q}-\\mathbf{p}}{\\|\\|\\mathbf{q}-\\mathbf{p}\\|\\|} = \n\\lim_{t\\rightarrow s} \\frac{{\\gamma}(t)-{\\gamma}(s)}{\\|\\|{\\gamma}(t)-{\\gamma}(s)\\|\\|} = \\frac{{\\gamma}'(s)}{\\|\\|{\\gamma}'(s)\\|\\|}.\n$$\n\nIt always possible, and often convenient, to assume without loss of generality\nthat $s$ is an [arc length\nparameterization](https://en.wikipedia.org/wiki/Arc_length) of the curve ${\\gamma}$ so\nthat $\\|\\|{\\gamma}'\\|\\| = 1$ and therefore the unit tangent vector is simply ${\\mathbf{t}}(s) = {\\gamma}\\'(s)$ .\n\nIn an analogous fashion, we can consider the limit of the\n[circumcircle](https://en.wikipedia.org/wiki/Circumscribed_circle)\n$C(\\mathbf{q}\\_{1},\\mathbf{p},\\mathbf{q}\\_{2})$ that passes\nthrough $\\mathbf{p}$ and points $\\mathbf{q}\\_{1}$ and $\\mathbf{q}\\_{2}$ before and after it on the curve:\n\n$$\nC(\\mathbf{p}) = \\lim_{\\mathbf{q}_{1},\\mathbf{q}_{2}\\rightarrow \\mathbf{p}} C(\\mathbf{q}_{1},\\mathbf{p},\\mathbf{q}_{2}).\n$$\n\n\n![](images/osculating-circle.gif)\n\nThis limit circle is called the [osculating\ncircle](https://en.wikipedia.org/wiki/Osculating_circle) at the point $\\mathbf{p}$ on\nthe curve ${\\gamma}$. By construction the tangent of the curve and the circle match at\n$\\mathbf{p}$: they're both ${\\gamma}'$. The\n[radius](https://en.wikipedia.org/wiki/Radius_of_curvature) $R(\\mathbf{p})$ of the\nosculating circle $C(\\mathbf{p})$ at the the point $\\mathbf{p}$ is proportional to how straight\nthe curve is locally: as the curve becomes more and more straight then the\nradius tends toward infinity. This implies that the radius is inversely\nproportional to the \"curvy-ness\" of the curve. Hence, the inverse of the radius\nis dubbed the curvature:\n\n$$\n{\\kappa}(\\mathbf{p}) = \\frac{1}{R(\\mathbf{p})}.\n$$\n\nThe radius is a non-negative measure of length with units meters, so the\ncurvature ${\\kappa}$ is an non-negative scalar with units 1/meters. The radius of the\nosculating circle can also be written as a limit of the [circumcircle\nradius](https://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates_from_cross-_and_dot-products):\n\n$$\nR(\\mathbf{p}) = \\lim_{\\mathbf{q}_{1},\\mathbf{q}_{2}\\rightarrow \\mathbf{p}} \n  \\frac{\\|\\| \\mathbf{q}_{1}-\\mathbf{p}\\|\\|  \\|\\| \\mathbf{p}-\\mathbf{q}_{2}\\|\\|  \\|\\| \\mathbf{q}_{2}-\\mathbf{q}_{1}\\|\\| }\n  {2\\left| (\\mathbf{q}_{1}-\\mathbf{p}) \\quad (\\mathbf{p}-\\mathbf{q}_{2})\\right|}.\n$$\n\n\n#### Signed curvature\n\nPlugging in our arc-length parameterization this reveals that the curvature\n(inverse of radius) is equal to the magnitude of change in the tangent or\nequivalently the magnitude of second derivative of the curve:\n\n$$\n{\\kappa}(s) = \\lim_{t\\rightarrow s} \\left\\|\\left\\| \\frac{{\\gamma}'(t)-{\\gamma}'(s)}{t-s} \\right|\\right| = \\|\\| {\\gamma}''(s)\\|\\| .\n$$\n\n\nBecause we chose the arc-length parameterization, the only change to the\ntangent vector ${\\gamma}'$ is a change in _direction_ (as opposed to magnitude, since\n$\\|\\| {\\gamma}'\\|\\|  := 1$). This means that the change--as a vector itself--is\n_orthogonal_ to the tangent. In other words, the change in tangent ${\\gamma}''$ points\nalong the \u003ca id=curvature-normal\u003enormal direction\u003c/a\u003e $\\widehat{\\mathbf{n}}$:\n\n$$\n{\\gamma}'' \\cdot  {\\gamma}' = 0 \\quad \\rightarrow  \\quad {\\gamma}'' \\cdot  \\widehat{\\mathbf{n}} = \\pm  {\\kappa} \\widehat{\\mathbf{n}}.\n\\label{equ:curvature-normal}\n$$\n\n\nIf we define an orientation to our curve  then we can endow the curvature with\na [sign](https://en.wikipedia.org/wiki/Sign_(mathematics)) based on whether the\ncenter of the osculating circle lies on the [left or right\nside](https://en.wikipedia.org/wiki/Right-hand_rule) of the curve. As already\nestablished, the tangent of the osculating circle and the curve agree, so the\nvector pointing toward the circle's center must be\n[perpendicular](https://en.wikipedia.org/wiki/Perpendicular) to the tangent:\ni.e., in either the positive or negative\n[normal](https://en.wikipedia.org/wiki/Normal_(geometry)) directions. \n\nIf the orientation agrees with increasing the arc-length parameter $s$, then the sign can\nbe \ndetermined by comparing the second derivative vector ${\\gamma}''$ to the unit normal $\\widehat{\\mathbf{n}} := ({\\gamma}')^{\\perp}$. The [_**signed\ncurvature**_](https://en.wikipedia.org/wiki/Curvature#Signed_curvature) at a point $\\mathbf{p}$ is thus given by:\n\n$$\n\\begin{align*}\nk(\\mathbf{p}) \u0026= \\text{sign}({\\gamma}''(\\mathbf{p})\\cdot \\widehat{\\mathbf{n}}))\\ {\\kappa}(\\mathbf{p})  \\\\\n      \u0026= {\\gamma}''(\\mathbf{p}) \\cdot  \\widehat{\\mathbf{n}}.\n\\end{align*}\n$$\n\n#### Moving point analogy\n\nThis definition neatly conforms to our intuition of a curve as the trajectory\nof a moving point. Imagine the curved formed by driving along a particular\ntrajectory ${\\gamma}(t)$, where we really interpret $t$ as time.\n\nWhile ${\\gamma}'(t)$ corresponds to your velocity vector and $\\|\\| {\\gamma}'(t)\\|\\|$ corresponds to\nyour speed, the arc-length (re-)parameterization would correspond to having\nyour friend re-trace your path traveling at a perfectly uniform speed $\\|\\|{\\gamma}'(s)\\|\\| = 1$, where your friends \"time\" $s$ may be different from yours (it may take\nlonger or shorter depending if you drove fast or slow).\n\nCurvature in the path corresponds to _turning_ and quite literally the amount\nby which your friend needs to turn the steering wheel away from the \"straight\"\nposition: on a straight course, the steering wheel remains at zero-angle\nposition and the curvature is zero, on a circular course the steering wheel is\nfixed at a constant angle in the left or right direction corresponding to\nconstant positive or negative curvature respectively.\n\nChanging the steering wheel changes the _direction_ of the vehicle's velocity.\nFor your friend driving at constant speed, this is the _only_ change admissible\nto the velocity, hence the curvature exactly corresponds to ${\\gamma}''(s)$ and to the\nsteering wheel angle.\n\n\u003e If somebody wants to make a Sega [Out\n\u003e Run](https://en.wikipedia.org/wiki/Out_Run) inspired gif showing a steering\n\u003e wheel turning next to a little car tracing a curve, I'll be very impressed.\n\n#### Turning number\n\nThe integrated signed curvature around a [closed\ncurve](https://en.wikipedia.org/wiki/Curve) must be an integer multiple of\n$2{\\pi}$:\n\n$$\n{\\oint} k(s) \\ ds = 2{\\pi} {\\tau},\n$$\n\nwhere $\\tau$ is an integer called the \"turning number\" of the curve. \n\nThis is a bit surprising at first glance. However, in the _moving point\nanalogy_ a closed curve corresponds to a period trajectory (e.g., driving\naround a race-track). When we've made it once around the track, our velocity\ndirection (e.g., the direction the vehicle is facing) must be pointing in the\noriginal direction. That is, during the course, the car either have turned all the\nway around once ( ${\\tau} = 1$ ) or turned as much clockwise and it did\ncounter-clockwise (e.g., on a figure 8 course: ${\\tau}=0$), or made multiple\nloops, etc.\n\n#### Discrete curvature\n\nIn the discrete world, if a curve is represented as a piecewise-linear chain of\nsegments, then it's natural to associate curvature with vertices: the segments\nare flat and therefor contain no curvature.\n\nA natural analog to the definition of curvature as \nthe derivative of the tangent vector \n(i.e., $k = \\|\\| {\\gamma}''\\|\\|  = \\|\\| \\mathbf{t}'\\|\\| $) is to define _discrete curvature_ as the change in\ntangent direction between discrete segments meeting at a vertex:\n\n$$\nk_i =  {\\angle} (\\mathbf{v}_i - (\\mathbf{v}_{i-1}-\\mathbf{v}_i)) \\mathbf{v}_i\\mathbf{v}_{i+1} = {\\theta}_i,\n$$\n\nthat is, the signed [_exterior\nangle_](https://en.wikipedia.org/wiki/Internal_and_external_angles) ${\\theta}_i$ at\nthe vertex $\\mathbf{v}_i$.\n\n![](images/discrete-curvature.svg)\n\nThe turning number theorem for continuous curves finds an _immediate_ analog in\nthe discrete case. For a closed polygon the discrete signed angles must sum up\nto a multiple of $2{\\pi}$ in order to close up:\n\n$$\n\\sum\\limits_{i=1}^n k_i = 2{\\pi} {\\tau}.\n$$\n\n\nIn this way, we _preserve the structure_ found in the continuous case in our\ndiscrete analog. This structure preservation leads to an understanding of the\nexterior angle as an approximation or discrete analog of the _locally\nintegrated_ curvature.\n\nAlternatively, we could literally fit an circle to the discrete curve based on\nlocal samples and approximate curvature as the inverse radius of the osculating\ncircle. This curvature measure (in general) will not obey the turning number\ntheorem, but (conducted properly) it will converge to the pointwise continuous\nvalues under refinement (e.g., as segment length shrinks).\n\nWe will explore these two concepts for surfaces, too: discrete analogs that\npreserve continuous structures and discretizations that approximate continuous\nquantities in the limit.\n\n### Curvature(s) on surfaces\n\nA surface can be curved locally in multiple ways. Consider the difference\nbetween a flat piece of paper, a spherical ping-pong ball and a saddle-shaped\n[Pringles chip](https://en.wikipedia.org/wiki/Pringles). The Pringles chip is\nthe most interesting because it curves \"outward\" in one direction and \"inward\"\nin another direction. In this section, we will learn to distinguish and\nclassify points on a surface based on how it curves in each direction.\n\n#### Normal curvature\n\nThe simplest way to extend the curvature that we defined for planar curves to a\nsurface $\\mathcal{S}$ is to _slice_ the surface through a given point $\\mathbf{p}\\in \\mathcal{S}$ with a\n[plane](https://en.wikipedia.org/wiki/Plane_(geometry)) $\\mathbf{P}$ that is parallel\nto the [surface normal](https://en.wikipedia.org/wiki/Normal_(geometry))\n$\\mathbf{n}(\\mathbf{p})$.\n\nThe (local) intersection of the surface $\\mathcal{S}$ and the plane $\\mathbf{P}$ will trace a\ncurve ${\\gamma}$, upon which we can immediately use the planar curvature definition\nabove. \n\n![[Source](http://brickisland.net/cs177fa12/?p=214)](images/normal-curvature.svg)\n\nThere are infinitely many planes that pass through a given point $\\mathbf{p}$ and lie\nparallel to a given normal vector $\\mathbf{n}(\\mathbf{p})$: the plane can rotate around the\nnormal $\\mathbf{n}(\\mathbf{p})$ by any angle ${\\varphi}$. For each choice of ${\\varphi}$, the plane will define\nan intersecting curve ${\\gamma}_{\\varphi}$ and thus for every angle ${\\varphi}$ there will be a\n[_normal curvature_](https://en.wikipedia.org/wiki/Curvature#Normal_sections):\n\n$$\nk_\\mathbf{n}({\\varphi},\\mathbf{p}) = {\\gamma}''_\\varphi(\\mathbf{p}).\n$$\n\n\n#### Mean curvature\n\nNormal curvature requires choosing an angle, so it doesn't satiate our\ndesire to reduce the \"curvy-ness\" to a single number for any point on the\nsurface. A simple way to reduce this space of normal curvatures is to, well,\naverage all possible normal curvatures. This defines the [mean\ncurvature](https://en.wikipedia.org/wiki/Mean_curvature):\n\n$$\nH(\\mathbf{p}) = \\frac{1}{2{\\pi}}\\int\\limits_0^{2{\\pi}} k_\\mathbf{n}({\\varphi},\\mathbf{p}) \\ d{\\varphi}.\n$$\n\n\n#### Maximum and minimum curvature \n\nAnother obvious way to reduce the space of normal curvatures to a single number\nis to consider the maximum or minimum normal curvature over all choices of ${\\varphi}$:\n\n$$\n\\begin{align*}\nk_{1}(\\mathbf{p}) \u0026= \\max\\_{\\varphi} \\ k_\\mathbf{n}({\\varphi},\\mathbf{p}) \\\\\nk_{2}(\\mathbf{p}) \u0026= \\mathop{\\text{min}}\\_{\\varphi} \\ k_\\mathbf{n}({\\varphi},\\mathbf{p}).\n\\end{align*}\n$$\n\nCollectively, these are referred to as the [principal\ncurvatures](https://en.wikipedia.org/wiki/Principal_curvature) and\ncorrespondingly the angles that maximize and minimize curvature are referred to\nas the principal curvature directions:\n\n$$\n\\begin{align*}\n{\\varphi}\\_{1}(\\mathbf{p}) \u0026= \\mathop{\\text{argmax}}\\_{\\varphi} \\ k_\\mathbf{n}({\\varphi},\\mathbf{p}) \\\\\n{\\varphi}\\_{2}(\\mathbf{p}) \u0026= \\mathop{\\text{argmin}}\\_{\\varphi} \\ k_\\mathbf{n}({\\varphi},\\mathbf{p}).\n\\end{align*}\n$$\n\n[Euler's\ntheorem](https://en.wikipedia.org/wiki/Euler%27s_theorem_(differential_geometry))\nstates that the normal curvature is a quite simple function of ${\\varphi}$ and the\nprincipal curvatures:\n\n$$\nk_\\mathbf{n}({\\varphi},\\mathbf{p}) = k_{1} \\cos^2 {\\varphi} + k_{2} \\sin^2 {\\varphi},\n$$\n\n([proof](https://math.stackexchange.com/a/1783316/35376)).\n\nThere are two immediate and important consequences:\n\n 1. the principal curvature directions ( ${\\varphi}\\_{1}$ and ${\\varphi}\\_{2}$ ) are orthogonal, and \n 2. the mean curvature reduces to the average of principal curvatures:\n\n$$\nH = \\frac12 (k_{1} + k_{2}).\n$$\n\n\n\u003e For more theory and a proof of Euler's theorem, I recommend \"Elementary\n\u003e Differential Geometry\" by Barret O'Neill, Chapter 5.2.\n\n#### Gaussian curvature\n\nMaximum, minimum and mean curvature reduce curvature to a single number, but\nstill cannot (alone) distinguish between points lying on a round ping-pong\nball, a flat sheet of paper, the cylindrical Pringles can and a saddle-shaped\nPringles chip.\n\nThe neck of this cartoon elephant--like a Pringles chip--bends inward in one\ndirection (positive $k_{1} \u003e 0$) and outward in the other \ndirection (negative $k_{2} \u003c 0$).\n\n![](images/cartoon-elephant-principal-and-gaussian-curvature.jpg)\n\nFigure Caption: Maximum $k_{1}$, minimum $k_{2}$, and Gaussian curvature $K = k_{1}k_{2}$.\n\nThe _product_ of the principal curvatures maintains the disagreement in sign\nthat categories this saddle-like behavior. This product is called [Gaussian\ncurvature](https://en.wikipedia.org/wiki/Gaussian_curvature):\n\n$$\nK = k_1 k_2.\n$$\n\n\n#### Relationship to surface area\n\nBoth mean and Gaussian curvature have meaningful relationships to surface\narea.\n\n##### Mean Curvature as area gradient\n\nLet us consider a seemingly unrelated yet familiar problem. Suppose we would\nlike to _flow_ a given surface in the direction that shrinks its surface area.\nThat is, we would like to move each surface point in the direction that\nminimizes surface area.\n\nThe surface area of $\\mathcal{S}$ may be written as an integral of unit density:\n\n$$\nA(\\mathcal{S}) = \\int_\\mathcal{S} 1\\ d\\mathbf{x}.\n$$\n\nThere are many expressions that $=1$. We can choose an expression that is\nespecially easy to work with. Namely, the small change in position over a small\nchange in position is a unit vector. \n\n$$\n\\|\\| \\nabla x \\|\\| = \\left\\|\\left\\| \\frac{\\partial x}{\\partial x} \\right\\|\\right\\| = 1.\n$$\n\nThe norm of the gradient is a non-linear function involving square roots, but\nsince the magnitude is one then the squared magnitude is also one ( $\\|\\| \\nabla x \\|\\| ^2 = 1$ ). This allows us to write the surface area as a quadratic function of\npositions and familiarly as the Dirichlet energy:\n\n$$\nA(\\mathcal{S}) = \\int_\\mathcal{S} \\|\\| \\nabla \\mathbf{x} \\|\\|^2 \\ d\\mathbf{x}.\n$$\n\nBy abuse of notation we can say that $A(\\mathbf{x})$  is a functional (function\nthat takes a function as input) and measures the surface area of the surface\ndefined by the embedding function $\\mathbf{x}$. Now, let's consider the\n[functional derivative](https://en.wikipedia.org/wiki/Functional_derivative) of\n$A$ with respect to $\\mathbf{x}$. This special type of derivative can be written as:\n\n$$\n\\frac{d A}{d \\mathbf{x}} = \\lim_{\\epsilon \\rightarrow 0} \\frac{A(\\mathbf{x}+\n\\epsilon \\mathbf{y}) - A(\\mathbf{x})}{\\epsilon} \\quad \\forall \\mathbf{y}: \\Omega \\rightarrow \\mathbb{R}^3\n$$\n\nwhere $\\mathbf{y}$ is an _arbitrary_ function. That is, we consider the limit of\na tiny perturbation of the function in any way.\n\nWe can identify this limit by considering the derivative of the perturbation\nmagnitude $\\epsilon$ evaluated at zero:\n\n$$\n\\frac{d A}{d \\mathbf{x}} = \\left[ \\frac{d}{d \\epsilon} A(\\mathbf{x}+\\epsilon\n\\mathbf{y}) \\right]_{\\epsilon=0} \\quad \\forall \\mathbf{y}.\n$$\n\nFeeding in our Dirichlet energy definition of $A(\\mathbf{x})$ we can start\nworking through this derivative:\n\n$$\n\\begin{align}\n\\frac{d A}{d \\mathbf{x}} = \u0026 \n\\left[ \\frac{d}{d \\epsilon}\n\\int_\\mathcal{S} \\| \\nabla \\mathbf{x} + \\epsilon \\nabla \\mathbf{y} \\|^2 \\ d\\mathbf{x}\n\\right]\\_{\\epsilon=0} \\\\ \n\u0026 \n\\left[ \\frac{d}{d \\epsilon}\n\\int_\\mathcal{S} \\| \\nabla \\mathbf{x} \\|^2 + 2 \\epsilon \\nabla \\mathbf{y} \\cdot \\nabla \\mathbf{x} +  \\epsilon^2 \\| \\nabla \\mathbf{y} \\|^2 \\ d\\mathbf{x}\n\\right]\\_{\\epsilon=0} \\\\\n\u0026 \n\\left[\n\\int_\\mathcal{S}  2 \\nabla \\mathbf{y} \\cdot \\nabla \\mathbf{x} +  2 \\epsilon \\| \\nabla \\mathbf{y} \\|^2 \\ d\\mathbf{x}\n\\right]\\_{\\epsilon=0} \\\\\n\u0026 \n\\int_\\mathcal{S}  2 \\nabla \\mathbf{y} \\cdot \\nabla \\mathbf{x}  \\ d\\mathbf{x}.\\\\\n\\end{align}\n$$\n\nAssuming that $\\mathcal{S}$ is closed (no boundary), then applying [Green's identity](https://en.wikipedia.org/wiki/Green%27s_identities#Green's_first_identity) leaves us with:\n\n$$\n\\begin{align}\n\\frac{d A}{d \\mathbf{x}} = \u0026 \n-\\int_\\mathcal{S}  \\mathbf{y} \\Delta \\mathbf{x}  \\ d\\mathbf{x} \n\\quad \\forall \\mathbf{y}: \\Omega \\rightarrow \\mathbb{R}^3.\n\\end{align}\n$$\n\nThis still leaves us with an expression of the derivative written as an integral\ninvolving this arbitrary function $\\mathbf{y}$. We would like to have a more\ncompact expression to evaluate $\\frac{d A}{d \\mathbf{x}}$ at some query point\n$\\mathbf{u} = (u,v)$ on the surface.\n\nSince this must be true for any choice of perturbation function $\\mathbf{y}$, we\ncan choose $\\mathbf{y}$ to be a function that is $=0$ everywhere on the domain\nexcept in the region just around $\\mathbf{u}$, where $\\mathbf{y}$ makes a little\n\"bump\" maxing out at $\\mathbf{y}(\\mathbf{u}) = 1$. Since this bump can be made\narbitrarily skinny, we can argue that $\\mathbf{y}$ can be factored out of the\nintegral above (if $\\mathbf{y}=0$ everywhere except $\\mathbf{y}=1$ arbitrarily close to\n$\\mathbf{u}$, then the integral just evaluates to $\\Delta \\mathbf{x}$ at\n$\\mathbf{u}$):\n\n\n$$\n\\frac{d A}{d \\mathbf{x}}(\\mathbf{u}) = - 2 \\Delta \\mathbf{x} (\\mathbf{u}).\n$$\n\nThis reveals to us that the Laplacian of the embedding function indicates the\ndirection and amount that the surface should move to decrease surface area.\n\nThe Laplacian $\\Delta f$ of a function $f$ on the surface does not depend on the\nchoice of parameterization. It is defined as the divergence of the gradient of\nthe function or equivalently the trace of the Hessian:\n\n$$\n\\Delta f = {\\nabla}\\cdot  {\\nabla}f = \\mathop{\\text{tr}}\\left( \n\\left[ \n\\begin{array}{cc}\n\\frac{\\partial ^{2}f}{\\partial u^{2}} \u0026 \\frac{\\partial ^{2}f}{\\partial u\\partial v} \\\\\n\\frac{\\partial ^{2}f}{\\partial v\\partial u} \u0026 \\frac{\\partial ^{2}f}{\\partial v^{2}} \n\\end{array}\n\\right]\n\\right) =\n\\frac{\\partial ^{2}f}{\\partial u^{2}} + \\frac{\\partial ^{2}f}{\\partial v^{2}}.\n$$\n\nIf we generously choose $u$ and $v$ to vary in the principal directions ${\\varphi}\\_{1}$\nand ${\\varphi}\\_{2}$ above. In this case, the Laplacian $\\Delta \\mathbf{x}$ of the position function\nreduces to the sum of principal curvatures times the normal (recall the\ndefinition of [curvature normal](#curvature-normal)):\n\n$$\n\\begin{align*}\n\\Delta \\mathbf{x} \u0026= \\frac{\\partial ^{2}\\mathbf{x}}{\\partial u^{2}} + \\frac{\\partial ^{2}\\mathbf{x}}{\\partial v^{2}} \\\\\n    \u0026= k_{1} \\mathbf{n} + k_{2} \\mathbf{n} \\\\\n    \u0026= 2H\\mathbf{n},\n\\end{align*}\n$$\n\nwhere $H\\mathbf{n} \\in  \\mathbb{R}^{3}$ is called the _**mean curvature normal**_ vector. We have\nshown that the mean curvature normal is equal half the Laplacian of the\nembedding function, which is in turn the gradient of surface area.\n\n##### Gaussian Curvature as area distortion\n\nAs the product of principal curvatures, Gaussian curvature $K = k_{1}k_{2}$ measures zero\nanytime one (or both) of the principal curvatures are zero. Intuitively, this\nhappens only for surfaces that curve or bend in one direction. Imagine rolling\nup a sheet of paper. Surfaces with zero Gaussian curvature $K = 0$ are called\n_developable surfaces_ because the can be flattened (developed) on to the flat\nplane (just as you might unroll the piece of paper) _without_ stretching or\nshearing. As a corollary, surfaces with non-zero Gaussian curvature _cannot_ be\nflattened to the plane without stretching some part.\n\nLocally, Gaussian curvature measures how far from developable the surface is:\nhow much would the local area need to stretch to become flat.\n\nFirst, we introduce the [Gauss map](https://en.wikipedia.org/wiki/Gauss_map), a\ncontinuous map $N:\\mathcal{S}\\rightarrow S^{2}$ from every point $\\mathbf{p}$ on the surface $\\mathcal{S}$ to the unit\nsphere $S^{2}$ so that $N(\\mathbf{p}) := \\mathbf{n}(\\mathbf{p})$, the unit normal at $\\mathbf{p}$.\n\nConsider a small patch on a curved surface. Gaussian curvature $K$ can\nequivalently be defined as the limit of the ratio between the area\narea _swept_ out by the unit normal on the \u003ca id=gauss-map\u003eGauss map\u003c/a\u003e $A_G$ and \nthe area of the surface patch $A$:\n\n$$\nK = \\lim_{A\\rightarrow 0} \\frac{A_G}{A}.\n$$\n\n\nLet's consider different types of regions:\n\n - flat: $A_G=0$ because the Gauss map is a point,\n - cylindrical: $A_G=0$ because the Gauss map is a curve,\n - spherical: $A_G \u003e 0$ because the Gauss map will maintain positive swept-area,\n   and \n - saddle-shaped: $A_G \u003c 0$ because the area on the Gauss map will maintain\n   _oppositely_ oriented area (i.e., from the spherical case).\n\n![A patch on a plane and its corresponding patch on the Gauss map.](images/gauss-map-plane.gif)\n![A patch on a cylinder and its corresponding patch on the Gauss map.](images/gauss-map-cylinder.gif)\n![A patch on a sphere and its corresponding patch on the Gauss map.](images/gauss-map-sphere.gif)\n![A patch on a saddle and its corresponding patch on the Gauss map.](images/gauss-map-saddle.gif)\n\nSimilar to the turning number theorem for curves, there exists an analogous\n[theorem for surfaces](https://en.wikipedia.org/wiki/Gauss-Bonnet_theorem)\nstating that the \u003ca id=gauss-bonnet\u003etotal Gaussian curvature\u003c/a\u003e must be an integer multiple of $2{\\pi}$:\n\n$$\n\\int_S K dA = 2{\\pi} {\\chi}(\\mathcal{S}),\n\\label{equ:gauss-bonnet}\n$$\n\nwhere ${\\chi}(\\mathcal{S})$ is the [Euler\ncharacteristic](https://en.wikipedia.org/wiki/Euler_characteristic) of the\nsurfaces $\\mathcal{S}$ (a topological _invariant_ of the surface revealing how many\n[holes](https://en.wikipedia.org/wiki/Genus_(mathematics)) the surface has).\n\nIn stark contrast to mean curvature, this theorem tells us that we cannot add\nGaussian curvature to a surface without:\n\n  1. removing an equal amount some place else, or\n  2. changing the topology of the surface.\n\nSince changing the topology of the surface would require a discontinuous\ndeformation, adding and removing Gaussian curvature must also balance out for\nsmooth deformations. This simultaneously explains why a cloth must have\nwrinkles when draping over a table, and why a deflated basketball will not lie\nflat on the ground.\n\n#### Shape operator\n\nThere is yet another way to arrive at principal, mean and Gaussian curvatures.\nConsider a point $\\mathbf{p}$ on a surface $\\mathcal{S}$ with unit normal vector $\\mathbf{n}$. If we\npick a unit tangent vector $\\mathbf{v}$ (i.e., so that $\\mathbf{v} \\cdot  \\mathbf{n} = 0$), then we can ask\nhow does the normal $\\mathbf{n}$ change as we move in the direction of $\\mathbf{v}$ along the\nsurface:\n\n$$\nS_\\mathbf{p}(\\mathbf{v}) := {\\nabla} \\mathbf{n} \\cdot  \\mathbf{v}\n$$\n\nwe call $S_\\mathbf{p}$ the [_**shape\noperator**_](https://en.wikipedia.org/wiki/Differential_geometry_of_surfaces#Shape_operator)\nat the point $\\mathbf{p}$. Just as how in the definition of [curvature normal](#curvature-normal), the\ncurvature normal must point in the normal direction, the shape operator takes\nas input a tangent vector and outputs another tangent vector (i.e., the change\nin the unit normal must be tangential to the surface; no change can occur in\nthe normal direction itself).\n\nLocally, the tangent vector space is two-dimensional spanned by basis vectors\n$\\mathbf{e}\\_{1},\\mathbf{e}\\_{2} \\in \\mathbb{R}^{2}$ so we can think of the\nshape operator as a mapping from $\\mathbb{R}^{2}$ to $\\mathbb{R}^{2}$. As a differential operator,\nthe shape operator is a _linear operator_. This means we can represent its\naction on a tangent vector $\\mathbf{v} = x_{1} \\mathbf{e}\\_{1} + x\\_{2}\\mathbf{e}\\_{2}$  as a matrix:\n\n$$\nS_\\mathbf{p}(\\mathbf{v}) = \n\\left[\n\\begin{array}{cc}\nS_\\mathbf{p}(\\mathbf{e}_{1})\\cdot \\mathbf{e}_{1} \u0026 S_\\mathbf{p}(\\mathbf{e}_{1})\\cdot \\mathbf{e}_{2} \\\\\nS_\\mathbf{p}(\\mathbf{e}_{2})\\cdot \\mathbf{e}_{1} \u0026 S_\\mathbf{p}(\\mathbf{e}_{2})\\cdot \\mathbf{e}_{2}\n\\end{array}\n\\right] \\mathbf{v}\n$$\n\n\nGiven $\\mathbf{r}\\_{1}$ and $\\mathbf{r}\\_{2}$ are the principal curvature directions (as unit 2D tangent\nvectors) we can rotate our coordinate frame to align $\\mathbf{e}\\_{1}$ and $\\mathbf{e}\\_{2}$ with the\nprincipal curvature directions. The shape operator takes on a very special\nform:\n\n$$\nS_\\mathbf{p} = \n\\left[\\mathbf{r}_{1} \\quad \\mathbf{r}_{2}\\right]\n\\left[\n\\begin{array}{cc}\nk_{1} \u0026 0 \\\\\n0 \u0026 k^{2}\n\\end{array}\n\\right]\n\\left[\\mathbf{r}_{1} \\quad \\mathbf{r}_{2}\\right]^{\\mathsf T}\n$$\n\n\n\u003e Consider why the off-diagonal terms are zero. Think about the _extremality_\n\u003e of the principal curvatures.\n\nWe have actually conducted an [eigen\ndecomposition](https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix) on\nthe shape operator. Reading this progression backwards, the eigen decomposition\nof the shape operator expressed in any basis will reveal:\n\n 1. the principal curvatures as the eigen values, and\n 2. the principal curvature directions as the eigen vectors.\n\n\u003c!--\nCurvature for curves is the change in tangent vector under an arc-length. For a\ngiven tangent direction on a surface, we extended this definition to define the\nnormal curvature as the curvature of the curve made by interesting surface with\na plane aligned with the chosen direction. Given two orthonormal tangent\ndirections $\\mathbf{u}$ and $\\mathbf{v}$ (i.e., a local parameterization), let's collect the\nnormal curvature normal vectors:\n\n\\begin{align}\nk_\\mathbf{n}(\\mathbf{u},\\mathbf{p})\\mathbf{n} \u0026= {\\gamma}''_\\mathbf{u}(\\mathbf{p}) \\\\\nk_\\mathbf{n}(\\mathbf{v},\\mathbf{p})\\mathbf{n} \u0026= {\\gamma}''_\\mathbf{v}(\\mathbf{p}).\n\\end{align}\n\n\nIf instead we equivalent consider the _change in normal vector_ for each sliced\ncurve, our curvature vectors will live in the orthogonal space: the tangent\nspace. \n\n\\begin{align}\nk_\\mathbf{n}(\\mathbf{u},\\mathbf{p})\\mathbf{v} \u0026= {\\gamma}''_\\mathbf{u}(\\mathbf{p}) \\\\\nk_\\mathbf{n}(\\mathbf{v},\\mathbf{p})\\mathbf{u} \u0026= {\\gamma}''_\\mathbf{v}(\\mathbf{p}).\n\\end{align}\n\n\n\u003e Before we chose the normal direction by an angle ${\\varphi}$, but for any tangent\n\u003e direction $\\mathbf{u}$ we can determine its correspond ${\\varphi}$ so that $k_\\mathbf{n}({\\varphi}) =\n\u003e k_\\mathbf{n}(\\mathbf{u})$.\n--\u003e\n\n### Discrete curvatures on surfaces\n\n\n#### Discrete mean curvature normal via discrete Laplace \n\nBy now we are very familiar with the discrete Laplacian for triangle meshes:\n\n$$\n\\Delta f \\approx  \\mathbf{M}^{-1} \\mathbf{L} \\mathbf{f},\n$$\n\nwhere $\\mathbf{M},\\mathbf{L} \\in \\mathbb{R}^{n\\times n}$ are the mass and cotangent matrices respectively.\n\nWhen applied to the vertex positions, this operator gives a point-wise (or\nrather integral average) approximation of the mean curvature normal:\n\n$$\nH\\mathbf{n} \\approx  \\mathbf{H} = \\mathbf{M}^{-1} \\mathbf{L} \\mathbf{V} \\in  \\mathbb{R}^{n\\times 3}.\n$$\n\n\nStripping the magnitude off the rows of the resulting matrix would give the\n_unsigned_ mean curvature. To make sure that the sign is preserved we can check\nwhether each row in $\\mathbf{H}$ agrees or disagrees with consistently oriented\nper-vertex normals in $\\mathbf{N} \\in  \\mathbb{R}^{n\\times 3}$. \n\nThis connection between the Laplace operator and the mean curvature normal\nprovides additional understanding for its use as a geometric smoothing operator\n(see \"Computing Discrete Minimal Surfaces and Their Conjugates\" [Pinkall and\nPolthier 1993]).\n\n#### Discrete Gaussian curvature via angle defect\n\nOn a discrete surface represented as a triangle mesh, curvature certainly can't\nlive on the flat faces. Moreover, Gaussian curvature can't live along edges\nbecause we can always _develop_ the triangles on either side of an edge to the\nplane without stretching them. In fact we can develop any arbitrarily long\nchain of faces connected by edges so long as it doesn't form a loop or contain\nall faces incident on a vertex. This hints that discrete Gaussian curvature\n(like curvature for curves) must live at vertices.\n\nUsing the definition of Gaussian curvature in terms of the area on the [Gauss\nmap](#gauss-map), flat faces correspond\npoints on the Gauss map (contributing nothing), edges correspond to area-less\ncurves (traced by their [dihedral\nangles](https://en.wikipedia.org/wiki/Dihedral_angle)), but vertices correspond\nto spherical polygons connecting face normal-points. The area ${\\Omega}$ subtended on\nthe Gauss map is call the [solid\nangle](https://en.wikipedia.org/wiki/Solid_angle). Conveniently, this area is\nsimply the [angle\ndefect](https://en.wikipedia.org/wiki/Angular_defect#Descartes.27_theorem) of\ninternal angles ${\\theta}_f$ incident on the $i$-th vertex contributed by each $f$-th\nincident face:\n\n$$\n{\\Omega}_i = 2{\\pi} - \\sum\\limits\\_{f \\in  \\text{faces(i)}} {\\theta}\\_{if}.\n$$\n\n\n\n![\"Gaussian Curvature and Shell Structures\" [Calladine\n1986]](images/angle-defect.png)\n\nThus, our discrete analog of locally _integrated_ Gaussian curvature is given\nas the angle defect at the $i$-th vertex. The local integral average (or\n_pointwise_) discrete Gaussian curvature is the angle defect divided by the\nlocal area associated with the $i$-th vertex:\n\n$$\nK_i = \\frac{2{\\pi} - \\sum\\limits_{f \\in  \\text{faces(i)}} {\\theta}_{if}}{A_i}.\n$$\n\n\n\u003e Note: Some authors use $K_i$ for _angle defect_ at a vertex (without the divide by $A_i$). Keep in mind that angle defect has units _radians_ while curvature (and angle defect divided by local area) has units _m⁻²_.\n\nBy way of closing up the Gauss map, closed polyhedral surfaces (i.e., meshes)\nwill obey the\n[Gauss-Bonnet](https://en.wikipedia.org/wiki/Gauss-Bonnet_theorem)\n[above](#gauss-bonnet), too:\n\n$$\n\\sum\\limits_{i=1}^n K_i A_i = \\sum\\limits_{i=1}^n {\\Omega}_i = 2{\\pi} {\\chi}(\\mathcal{S}).\n$$\n\n\nWe can connect this to [Euler's\nformula](https://en.wikipedia.org/wiki/Euler_characteristic) for polyhedra in our very first\nassignment:\n\n$$\n\\frac{1}{2{\\pi}} \\sum\\limits_{i=1}^n {\\Omega}_i =  |V| - |E| + |F|,\n$$\n\nwhere $|V|, |E|, |F|$ are the number of vertices, edges and faces respectively.\n\n\n\n#### Approximation and eigen decomposition of the shape operator \n\nAlternatively, we can approximate all curvatures of a surface by locally\nfitting an analytic surface and _reading_ off its curvature values. Since\nplanes have no curvature, the simplest type of analytic surface that will give\na non-trivial curvature value is a quadratic surface.\n\nThus, the algorithm proceeds as follows. For each vertex $\\mathbf{v}$ of the given mesh,\n\n 1.  gather a sampling of points in the vicinity. For simplicity, let's just\n grab all other vertices that share an edge with $\\mathbf{v}$ or share an edge with a\n vertex that shares an edge with $\\mathbf{v}$ (i.e., the \"two-ring\" of $\\mathbf{v}$). For most\n sane meshes, this will provide enough points. Gather the positions of these\n $k$ points _relative_ to $\\mathbf{v}$ (i.e., $\\mathbf{v}_i - \\mathbf{v}$) into a matrix $\\mathbf{P} \\in \\mathbb{R}^{k\\times 3}$ .\n 2. Next, we are going to define a quadratic surface as a height field above\n some two-dimensional plane passing through $\\mathbf{v}$. Ideally, the plane is\n orthogonal to the normal at $\\mathbf{v}$. To find such a plane, compute the\n [principal-component\n analysis](https://en.wikipedia.org/wiki/Principal_component_analysis) of $\\mathbf{P}$\n (i.e., conduct eigen decomposition on $\\mathbf{P}^{\\mathsf T} \\mathbf{P}$). Let $\\mathcal{S} \\in  \\mathbb{R}^{k \\times 2}$ be the coefficients for two most principal directions (call them the $u$-\n and $v$- directions) corresponding to each point in $\\mathbf{P}$, and let $\\mathbf{B} \\in \\mathbb{R}^{k}$ be the \"height\" of each point in the least principal direction (call\n it the $w$-direction).\n 3. A quadratic function as a height-field surface passing through the origin\n is given by:\n \n$$\nw = a_{1} u + a\\_{2} v + a\\_{3} u^{2} + a\\_{4}uv + a\\_{5}v^{2}.\n$$\n\nWe have $k$ sets of $u,v$ values and $w$ values. Treat this as a\nleast-squares fitting problem and solve for the 5 unknown coefficients.\n(`igl::pinv` is good for solving this robustly).\n\n4. Each element of the shape operator for the graph of a quadratic function\nover the plane has a closed form expression. You need to derive these by hand.\nJust kidding. The shape operator can be constructed as the product of two\nmatrices:\n\n$$\nS = -\n\\left[\n\\begin{array}{cc}\ne \u0026 f \\\\\nf \u0026 g\n\\end{array}\n\\right]\n\\left[\n\\begin{array}{cc}\nE \u0026 F \\\\\nF \u0026 G\n\\end{array}\n\\right]^{-1}\n$$\n\n known as the second and first fundamental forms respectively. The entries of\n these matrices categorize the stretch and bending in each direction:\n \n$$\nE = 1+a_{1}^{2}, \\\\ \\quad\nF = a_{1}a_{2},  \\\\ \\quad\nG = 1+a_{2}^{2}, \\\\ \\quad\ne = \\frac{2a_{3}}{\\sqrt{a_{1}^{2} + 1 + a_{2}^{2}}}, \\\\ \\quad\nf = \\frac{a_{4}}{\\sqrt{a_{1}^{2} + 1 + a_{2}^{2}}}, \\\\ \\quad\ng = \\frac{2a_{5}}{\\sqrt{a_{1}^{2} + 1 + a_{2}^{2}}}\n$$\n\n See Table 1 of \"Estimating Differential Quantities Using Polynomial Fitting of\n Osculating Jets\" [Cazals \u0026 Pouget 2003] to double check for typos :-).\n\n 5. Eigen decomposition of $S$ reveals the principal curvatures $k_{1}$ and $k_{2}$\n _and_ the principal tangent directions (in the $uv$ PCA basis).\n\n 6. Lift the principal tangent directions back to world $\\mathbb{R}^{3}$ coordinates.\n\n## Tasks\n\n[Download](https://archive.org/details/ElementaryDifferentialGeometry) Barret\nO'Neill's book. This is my go-to differential geometry book. The section on\ncurvature and the shape operator should help resolve questions and fill in\nmissing proofs above.\n\n### Blacklist\n\n - `igl::gaussian_curvature`\n - `igl::internal_angles` (or any of the other overloads)\n - `igl::principal_curvatures`\n\n### Whitelist\n\n - `igl::adjacency_matrix.h`\n - `igl::cotmatrix`\n - `igl::invert_diag`\n - `igl::massmatrix`\n - `igl::per_vertex_normals`\n - `igl::pinv`\n - `igl::slice`\n - `igl::sort`\n - `igl::squared_edge_lengths`\n\n### `src/mean_curvature.cpp`\nCompute the discrete mean curvature at each vertex of a mesh (`V`,`F`) by\ntaking the signed magnitude of the mean curvature normal as a _pointwise_ (or\n_integral average_) quantity.\n\n### `src/internal_angles.cpp`\nGiven (squared) edge-lengths of a triangle mesh `l_sqr` compute the internal\nangles at each corner (a.k.a. wedge) of the mesh.\n\n### `src/angle_defect.cpp`\nCompute the discrete angle defect at each vertex of a triangle mesh\n(`V`,`F`), that is, the _locally integrated_ discrete Gaussian\ncurvature.\n\n### `src/principal_curvatures.cpp`\nApproximate principal curvature values and directions locally by considering\nthe two-ring neighborhood of each vertex in the mesh (`V`,`F`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecjacobson%2Fgeometry-processing-curvature","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falecjacobson%2Fgeometry-processing-curvature","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecjacobson%2Fgeometry-processing-curvature/lists"}