feat(phase3f+3g): analytical Hessians + PI consolidation

Phase 3g — constants.hpp:
  - Introduce conformallab::PI and TWO_PI in a single constants.hpp
  - Remove scattered local PI/pi definitions from hyper_ideal_geometry.hpp,
    hyper_ideal_utility.hpp, euclidean_functional.hpp, mesh_builder.hpp,
    spherical_geometry.hpp (backward-compatible PI_SPHER alias kept)

Phase 3f — Euclidean Hessian (euclidean_hessian.hpp):
  - Cotangent-Laplace operator (Pinkall–Polthier 1993)
  - euclidean_cot_weights() helper + euclidean_hessian() + hessian_check_euclidean()
  - Correct Pinkall–Polthier 1/2 normalization factor
  - 8 tests: cot weights, symmetry, null-space (H·1=0), PSD, FD × 4 meshes

Phase 3f — Spherical Hessian (spherical_hessian.hpp):
  - Derives ∂α_i/∂u_j directly from the spherical law of cosines:
      ∂α1/∂l_opp  = sin(l_opp) / [sin(l_a)·sin(l_b)·sin(α1)]
      ∂α1/∂l_adj  = [cot(l_adj)·cos(α1) − cot(l_other)] / sin(α1)
    then chains with ∂l/∂λ = tan(l/2)
  - spherical_cot_weights() kept as a standalone helper (tested separately)
  - 8 tests: cot weights, symmetry, correct null-space & sign-convention
    (H·1 ≠ 0; H is NSD at equilibrium), FD × 3 meshes

All 62 cgal tests pass (3 skipped as before).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-12 17:22:28 +02:00
parent 8c353bb884
commit 194effba97
11 changed files with 929 additions and 22 deletions

View File

@@ -37,6 +37,7 @@
// Property-map name prefix: "ev:" (vertex) and "ee:" (edge).
#include "conformal_mesh.hpp"
#include "constants.hpp"
#include "euclidean_geometry.hpp"
#include <CGAL/boost/graph/iterator.h>
#include <vector>
@@ -66,9 +67,6 @@ struct EuclideanMaps {
// theta_v = 2π (flat vertex), phi_e = π (interior edge, flat surface).
inline EuclideanMaps setup_euclidean_maps(ConformalMesh& mesh)
{
constexpr double TWO_PI = 2.0 * 3.14159265358979323846;
constexpr double PI = 3.14159265358979323846;
EuclideanMaps m;
m.v_idx = mesh.add_property_map<Vertex_index, int> ("ev:idx", -1 ).first;
m.e_idx = mesh.add_property_map<Edge_index, int> ("ee:idx", -1 ).first;