Assignment 2 (Coding): Investigating Curvature (Due 3/22)

For the coding portion of this assignment, you will implement various expressions for discrete curvatures and surfaces normals that you will derive in the written assignment. (However, the final expressions are given below in case you want to do the coding first.) Once implemented, you will be able to visualize these geometric quantities on a mesh. For simplicity, you may assume that the mesh has no boundary.

Getting Started
Please implement the following routines in core/geometry.js:

  1. angle
  2. dihedralAngle
  3. vertexNormalAngleWeighted
  4. vertexNormalSphereInscribed
  5. vertexNormalAreaWeighted
  6. vertexNormalGaussianCurvature
  7. vertexNormalMeanCurvature
  8. angleDefect
  9. totalAngleDefect
  10. scalarMeanCurvature
  11. circumcentricDualArea
  12. principalCurvatures


1. The dihedral angle between the normals $N_{ijk}$ and $N_{ijl}$ of two adjacent faces $ijk$ and $ijl$ (respectively) is given by
$$ \theta_{ij} := \text{atan2}\left(\frac{e_{ij}}{\|e_{ij}\|} \cdot \left(N_{ijk} \times N_{jil}\right), N_{ijk} \cdot N_{jil}\right)$$

where $e_{ij}$ is the vector from vertex $i$ to vertex $j$.

2. The formulas for the angle weighted normal, sphere inscribed normal, area weighted normal, discrete Gaussian curvature normal and discrete mean curvature normal at vertex $i$ are
N_i^\phi &:= \sum_{ijk \in F} \phi_i^{jk}N_{ijk}\\
N_i^S &:= \sum_{ijk \in F} \frac{e_{ij} \times e_{ik}} {\|e_{ij}\|^2\|e_{ik}\|^2}\\
N_i^A &:= \sum_{ijk \in F} A_{ijk}N_{ijk}\\
KN_i &= \frac 12 \sum_{ij \in E} \frac{\theta_{ij}}{\|e_{ij}\|}e_{ij}\\
HN_i &= \frac 12 \sum_{ij \in E}\left(\cot\left(\alpha_k^{ij}\right) + \cot\left(\beta_l^{ij}\right)\right)e_{ij}

where $\phi_i^{jk}$ is the interior angle between edges $e_{ij}$ and $e_{ik}$, and $A_{ijk}$ is the area of face $ijk$. Note that sums are taken only over elements (edges or faces) containing vertex $i$. Normalize the final value of all your normal vectors before returning them.

3. The circumcentric dual area at vertex $i$ is given by
\[A_i := \frac 18 \sum_{ijk \in F} \|e_{ik}\|^2\cot\left(\alpha_j^{ki}\right) + \|e_{ij}\|^2\cot\left(\beta_k^{ij}\right)\]

4. The discrete scalar Gaussian curvature (also known as angle defect) and discrete scalar mean curvature at vertex $i$ are given by
K_i &:= 2\pi – \sum_{ijk \in F} \phi_i^{jk}\\
H_i &:= \frac 12 \sum_{ij \in E} \theta_{ij}\|e_{ij}\|

Note that these quantities are discrete 2-forms, i.e., they represent the total Gaussian and mean curvature integrated over a neighborhood of a vertex. They can be converted to pointwise quantities (i.e., discrete 0-forms at vertices) by dividing them by the  circumcentric dual area of the vertex (i.e., by applying the discrete Hodge star).

5. You are required to derive expressions for the principal curvatures $\kappa_1$ and $\kappa_2$ in exercise 4 of the written assignment. Your implementation of principalCurvatures should return the (pointwise) minimum and maximum principal curvature values at a vertex (in that order).

Submission Instructions

Please rename your geometry.js file to geometry.txt and put it in a single zip file called This file and your solution to the written exercises should be submitted together in a single email to with the subject line DDG20A2.

16 thoughts on “Assignment 2 (Coding): Investigating Curvature (Due 3/22)”

  1. Regarding principal curvature, it seems I can only pass the test if I trivially return [NaN, NaN]. If I used the formula derived in exercise 4 I would always fail the test. (I checked min and max before returning btw). What could be the issue here?

    1. Same here. I found that the test cases actually run the function for thousands of times and definitely the correct answers cannot all be NaN. I think there are some problems with the test cases.

      1. The test file is checking to see if your curvatures are within 10e-5 of the correct answer. But the check always evaluates to false because it seems NaN when compared to any other real number evaluates to false(instead of throwing an error). We will update the tests to account for this.

        If you implement principalCurvatures correctly using the formula this should pass all the test cases. When you fail a test case your result and the correct result are printed in the developer’s console. Are either of you very close(off by less than 10e-2) when not returning NaN?

        1. Same problem, I added following code to test files :

          let kk1 = minPrincipalCurvatures_sol[v.index];
          let kk2 = maxPrincipalCurvatures_sol[v.index];
          let H = scalarMeanCurvatures_sol[v.index];
          let K = scalarGaussCurvatures_sol[v.index];
          console.log(kk1 * kk2 – K);
          console.log(kk1 + kk2 – 2 * H);

          And here is the output after two loops:


          I think there might be something wrong since all values are extracted from the solution file and K = k1k2 and H = (k1 + k2)/2 failed.

          1. Sure, because discrete scalar Gaussian/Mean curvature are discrete 2-forms, you have to convert them into 0-forms by dividing the area.

          2. I’ll clarify a bit more here. Our standard definitions of discrete Gaussian/Mean curvature have them as discrete 2-forms. These are integrated quantities. As an example, angle defect doesn’t describe the discrete Gaussian curvature technically. It describes the discrete Gaussian curvature integrated over the dual cell. Thus, we actually need to divide by the area of the dual cell to recover the discrete Gaussian curvature. If we carry this factor of area in our calculations for principal curvature, if you work through the math again, you will see that our answers get messed up because of the square root terms. Hope this helps!

  2. Should Corner.vertex() in corner.js be instead of this.halfedge.prev.vertex?
    Suppose we have a triangle ijk, if I understand correctly:
    – halfedge ij (opposite to k) corresponds to the corner k,
    – ij.prev.vertex is i; is k.
    Please let me know if I made a mistake. Thanks!

    1. We identify a halfedge h with the vertex at its base(ie. the one “pointed to” h.prev) so I think this.halfedge.prev.vertex does correctly return the corner vertex opposite halfedge h.

    1. The notes to this assignment recommend using atan2 to compute dihedral angle. atan2 calculates one unique arctangent value as opposed to just atan. I don’t really see where else you may need trig in this assignment.

  3. Do the Gauss curvature normal and mean curvature normal need to be divided by K and H, respectively? I’ve tried just using the summation, dividing by K or H, and dividing K or H by the circumcentric dual area before dividing, and I still can’t pass the tests.

    1. I’m not sure how the TAs have the test setup, but at most it would make sense to divide the mean/Gauss curvature normals by the dual area of the vertex. For instance, the mean curvature normal is (assuming I’m remembering the constant correctly…)

      \[ (HN)_i := \tfrac{1}{4} \sum_{ij} (\cot\alpha_{ij} + \cot\beta_{ij})(f_j – f_i). \]

      This quantity is a dual discrete differential 2-form, i.e., it corresponds to an integral over a dual 2-cell. If \(A_i\) is the dual area associated with vertex \(i\), then \((HN)_i/A_i\) is the corresponding primal 0-form, i.e., a value at a primal vertex. Dividing the mean curvature normal by the scalar mean curvature does not have (to me) any clear meaning; likewise for Gaussian curvature.

Leave a Reply