docs(euclidean-hessian): fix wrong cotangent formula in two comment blocks

Finding-C from doc/reviewer/external-audit-2026-05-30.md.

The box comment at the top and the function-level comment above
euclidean_cot_weights() both stated:

  cot_k = (t_adj1·l123 − t_adj2·t_opp) / denom2   ← WRONG

The correct formula (verified numerically on a 3-4-5 right triangle,
expected cot1=4/3, cot2=3/4, cot3=0) is:

  cot_k = (t_opp · l123 − t_a · t_b) / denom2

where t_opp is the t-value of the edge OPPOSITE vertex k, and t_a/t_b
are the t-values of the two edges ADJACENT to vertex k.

The implementation in euclidean_cot_weights() was already correct;
only the documentation was wrong.

Changes (documentation only, zero code changes):
- Box comment: rewritten with correct formula and explicit per-vertex
  assignment (cot1: t_opp=t23, t_a=t12, t_b=t31; etc.)
- Box comment: added missing ½ factor to Hessian contribution lines
- Function-level comment: corrected to (t_opp·l123 − t_a·t_b)/(8·Area)
  with a pointer to the box comment for the full assignment
- Inline return comment in euclidean_cot_weights(): now shows the
  mapping (cot1: t_opp=t23, t_a=t12, t_b=t31) directly at the formula

263/263 CGAL tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-31 00:16:31 +02:00
parent 46c0b63de8
commit bef5a0ceb7
2 changed files with 21 additions and 13 deletions

View File

@@ -17,18 +17,21 @@
// │ side lengths lij = exp(Λ̃ij/2): │
// │ │
// │ t12 = l12+l23+l31, t23 = l12l23+l31, t31 = l12+l23l31 │
// │ denom2 = 2·sqrt(t12·t23·t31·l123) = 8·Area
// │ l123 = l12+l23+l31, denom2 = 2·sqrt(t12·t23·t31·l123) = 8·Area │
// │ │
// │ cot_k = (t_adj1·l123 t_adj2·t_opp) / denom2
// │ = cotangent of the angle αk at vertex k
// │ Cotangent at vertex k (opposite t_opp, adjacent t_a and t_b):
// │ cot_k = (t_opp · l123 t_a · t_b) / denom2
// │ │
// │ Hessian contributions per face:
// │ H[vi, vi] += cot_vj + cot_vk (diagonal, both non-opp angles)
// │ H[vi, vj] -= cot_vk (off-diagonal, for variable vi,vj
// │ Assignment (k ↔ opposite edge ↔ opposite t-value):
// │ cot1 (v1, opp l23): t_opp=t23, t_a=t12, t_b=t31
// │ cot2 (v2, opp l31): t_opp=t31, t_a=t12, t_b=t23
// │ cot3 (v3, opp l12): t_opp=t12, t_a=t23, t_b=t31 │
// │ │
// │ This is exactly the cotangent-Laplace operator from PinkallPolthier.
// │ Hessian contributions per face (PinkallPolthier ½ factor):
// │ H[vi, vi] += (cot_vj + cot_vk) / 2 (diagonal) │
// │ H[vi, vj] = cot_vk / 2 (off-diagonal, variable pairs) │
// │ │
// │ Pinned vertices (v_idx = 1) contribute to diagonal of neighbours but
// │ Pinned vertices (v_idx = 1) contribute to diagonal of neighbours but │
// │ do not create a column/row in H themselves. │
// └──────────────────────────────────────────────────────────────────────────┘
//
@@ -54,7 +57,11 @@ namespace conformallab {
// Given three Euclidean SIDE LENGTHS l12, l23, l31 (already exp(Λ̃/2)),
// return the three cotangent weights (cot1, cot2, cot3).
//
// cot_k = (t_adj·l123 t_opp·t_other) / (8·Area)
// cot_k = (t_opp · l123 t_a · t_b) / (8·Area)
//
// where t_opp is the t-value of the edge OPPOSITE vertex k, and t_a, t_b are
// the t-values of the two edges ADJACENT to vertex k. See the box comment at
// the top of this file for the explicit assignment of t_opp/t_a/t_b per vertex.
//
// Returns {0,0,0} for degenerate faces (triangle inequality violated or Area=0).
/// Three Euclidean cotangent weights `(cot1, cot2, cot3)` for the
@@ -85,9 +92,10 @@ inline EuclCotWeights euclidean_cot_weights(double l12, double l23, double l31)
// denom2 = 2·sqrt(t12·t23·t31·l123) = 8·Area
const double denom2 = 2.0 * std::sqrt(denom2_sq);
// cot at v1 (opposite l23): adjacent t-values are t12 and t31.
// cot at v2 (opposite l31): adjacent t-values are t12 and t23.
// cot at v3 (opposite l12): adjacent t-values are t23 and t31.
// Formula: cot_k = (t_opp · l123 t_a · t_b) / denom2
// cot1: t_opp=t23, t_a=t12, t_b=t31 (v1 opposite l23)
// cot2: t_opp=t31, t_a=t12, t_b=t23 (v2 opposite l31)
// cot3: t_opp=t12, t_a=t23, t_b=t31 (v3 opposite l12)
return {
(t23 * l123 - t31 * t12) / denom2, // cot1
(t31 * l123 - t12 * t23) / denom2, // cot2

View File

@@ -701,7 +701,7 @@ index. Add a comment:
|----|------|-------|------|----------|--------|
| A | `hyper_ideal_functional.hpp` | 319344 | Bug | Critical (mixed config) | ✅ Fixed 2026-05-30 |
| B | `gauss_bonnet.hpp` | 8788, 128134 | API error | Medium | ✅ Fixed 2026-05-31 |
| C | `euclidean_hessian.hpp` | 2627, 5758 | Doc error | Medium | 🟡 Open |
| C | `euclidean_hessian.hpp` | 2627, 5758 | Doc error | Medium | ✅ Fixed 2026-05-31 |
| D | `euclidean_functional.hpp` + 2 others | 97107 | Doc error | Medium | 🟡 Open |
| E | `cp_euclidean_functional.hpp` | 338349, 373387 | Inconsistency | Medium | 🟡 Open |
| F | test files | — | Test gap | Medium | 🟠 Open |