From b8d6e23adf8d9103dfcf45ef1ab9a01adf63b1e6 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Sun, 31 May 2026 01:44:36 +0200 Subject: [PATCH] docs(contracts): fix three errors in processing-unit contract table (U3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finding-U3 from doc/reviewer/usability-audit-2026-05-31.md. Three concrete errors corrected: 1. check_gauss_bonnet() row (was: implies all map types work) → now: 'EuclideanMaps/SphericalMaps only' — HyperIdealMaps overload is = delete (compile error since external-audit Finding-B fix) 2. enforce_gauss_bonnet() row (same issue) → same restriction note added 3. newton_hyper_ideal() preconditions (was: 'lambda0 initialised') → WRONG: HyperIdeal computes lengths from b_v, a_e via zeta functions internally; lambda0 plays no role. Correct precondition: 'no lambda0 needed · no GB pre-check' → Added explanation: strictly convex energy (Springborn 2020 Thm 1.3) → Newton converges for any targets without a pre-check Gauss-Bonnet prose section also updated: - Examples now show EuclideanMaps/SphericalMaps explicitly - HyperIdeal compile-error note added with the correct hyperbolic identity Σ(2π-Θ_v) - Area = 2π·χ and why no pre-check is needed - Open-mesh note added (GB identity does not hold for boundary meshes) Co-Authored-By: Claude Sonnet 4.6 --- doc/api/contracts.md | 27 +++++++++++++++------- doc/reviewer/usability-audit-2026-05-31.md | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/doc/api/contracts.md b/doc/api/contracts.md index e49383f..9de916e 100644 --- a/doc/api/contracts.md +++ b/doc/api/contracts.md @@ -13,11 +13,11 @@ of all preceding units. | `load_mesh()` | Valid file path, supported format (OFF/OBJ/PLY) | Manifold, oriented, triangulated `ConformalMesh` | | `setup_*_maps()` | Triangulated mesh | Initialised property maps; `lambda0` zeroed | | `compute_*_lambda0_from_mesh()` | `setup_*_maps()` called | `lambda0[e]` set from 3-D edge lengths | -| `check_gauss_bonnet()` | `theta_v[v]` set for all vertices | Throws `std::runtime_error` if Σ(2π−Θᵥ) ≠ 2π·χ(M) | -| `enforce_gauss_bonnet()` | `theta_v[v]` set | Σ(2π−Θᵥ) = 2π·χ(M) guaranteed; `theta_v` modified | +| `check_gauss_bonnet()` | `theta_v[v]` set · **EuclideanMaps or SphericalMaps only** | Throws `std::runtime_error` if Σ(2π−Θᵥ) ≠ 2π·χ(M). Deleted overload for `HyperIdealMaps` — see note below. | +| `enforce_gauss_bonnet()` | `theta_v[v]` set · **EuclideanMaps or SphericalMaps only** | Σ(2π−Θᵥ) = 2π·χ(M) guaranteed; `theta_v` modified. Deleted overload for `HyperIdealMaps` — see note below. | | `newton_euclidean()` | GB satisfied · DOFs assigned · `lambda0` initialised | `NewtonResult.x` — converged scale factors; `.converged`, `.iterations`, `.grad_inf_norm` | | `newton_spherical()` | GB satisfied · DOFs assigned · gauge vertex pinned | Same as above | -| `newton_hyper_ideal()` | `assign_all_dof_indices()` called · `lambda0` initialised | Same as above | +| `newton_hyper_ideal()` | `assign_all_dof_indices()` called · **no `lambda0` needed** · **no GB pre-check** | Same as above. HyperIdeal computes lengths internally from b_v, a_e (via ζ₁₃/ζ₁₄/ζ₁₅); `lambda0` is irrelevant. Energy is strictly convex → Newton converges for any targets without a pre-check. | | `compute_cut_graph()` | Closed, orientable, triangulated mesh | `CutGraph.cut_edge_flags` — 2g seam edges; `.genus` | | `euclidean_layout()` | `newton_euclidean()` converged | `Layout2D.uv[v]` · `.halfedge_uv[h]` · `HolonomyData.translations` | | `spherical_layout()` | `newton_spherical()` converged | `Layout3D.xyz[v]` | @@ -53,17 +53,28 @@ The solver never writes to pinned DOFs. All indexing is 0-based and contiguous. --- -## Gauss–Bonnet — the most common source of failure +## Gauss–Bonnet — the most common source of failure for Euclidean and Spherical Prescribing angles that violate Gauss–Bonnet means no conformal factor can realise the target metric — the Newton solver will iterate indefinitely without converging. ```cpp -// Option A: verify before solving (throws on violation) -check_gauss_bonnet(mesh, maps); +// Option A: verify before solving — throws std::runtime_error on violation. +// Works with EuclideanMaps and SphericalMaps only. +check_gauss_bonnet(mesh, euclidean_maps); // or spherical_maps +check_gauss_bonnet(mesh, spherical_maps); -// Option B: auto-correct (redistributes defect uniformly across all vertices) -enforce_gauss_bonnet(mesh, maps); +// Option B: auto-correct — redistributes the defect uniformly across all vertices. +enforce_gauss_bonnet(mesh, euclidean_maps); +enforce_gauss_bonnet(mesh, spherical_maps); + +// ⚠ HyperIdealMaps: both functions have deleted overloads — calling them is a +// compile error. Reason: the correct hyperbolic Gauss–Bonnet identity includes +// a surface area term: Σ(2π−Θᵥ) − Area(M) = 2π·χ(M), not Σ(2π−Θᵥ) = 2π·χ(M). +// No pre-check is needed for HyperIdeal: the energy is strictly convex +// (Springborn 2020 Theorem 1.3), so newton_hyper_ideal converges for any targets. ``` The target violation is `|Σ(2π−Θᵥ) − 2π·χ(M)| > tol`. Default tolerance: `1e-10`. +Applicable to closed meshes only — for open meshes, pin the boundary directly +and skip the Gauss–Bonnet check (the identity does not hold for meshes with boundary). diff --git a/doc/reviewer/usability-audit-2026-05-31.md b/doc/reviewer/usability-audit-2026-05-31.md index df760e6..5082224 100644 --- a/doc/reviewer/usability-audit-2026-05-31.md +++ b/doc/reviewer/usability-audit-2026-05-31.md @@ -538,7 +538,7 @@ exposed via the CGAL public API, but a CLI user cannot reach them. |----|------|------|----------|--------| | U1 | README + 4 examples | Usability | 🔴 Critical | ✅ Fixed 2026-05-31 | | U2 | examples/ (missing) | Usability | 🔴 Critical | ✅ Fixed 2026-05-31 | -| U3 | `contracts.md:16` | Doc error | 🟡 Medium | 🟡 Open | +| U3 | `contracts.md:16` | Doc error | 🟡 Medium | ✅ Fixed 2026-05-31 | | U4 | `README.md:17` | Stale | 🟡 Medium | 🟡 Open | | U5 | `Discrete_conformal_map.h:14` | Stale | 🟡 Medium | 🟡 Open | | U6 | README + 2 examples | Stale | 🟡 Medium | 🟡 Open |