All checks were successful
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 1m3s
Markdown link check / check (pull_request) Successful in 44s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Successful in 2m11s
Bundles the 2026-05-29 Java↔C++ math-correctness audit (doc/reviewer/ java-port-audit.md, 11 findings) with two follow-up fixes. Audit code changes: - Finding 3 (spherical_functional): edge-DOF replacement parameterization via spher_eff_lambda; edge gradient α_opp⁺+α_opp⁻−θ_e (drops additive −(S⁺+S⁻)/2) - Finding 4 (spherical_hessian): always-compiled edge-DOF throw guard - Finding 6 (period_matrix): faithful normalizeModulus (0≤Re≤½, Im≥0, |τ|≥1) - Finding 9 (inversive_distance): degenerate-face limiting angles, no skip - Findings 1/2 (euclidean): degenerate gradient limiting angles + Hessian guard Euclidean holonomy/τ fix: develop the cut surface across the dual spanning tree only (cut_graph now exposes is_dual_tree), so genus-1 cut edges yield non-degenerate lattice generators. Previously τ came out 0 / NaN / 1e13 on the bundled tori; now matches the analytic revolution modulus i·√(R²−r²)/r. Re-enabled τ reporting in the Euclidean CLI; rewrote validation.md §3/§4 accordingly. Tests (240 CGAL, 0 skipped): - HolonomyEndToEnd ×3 — tori of revolution (4×4, hex 6×6, 8×8) vs analytic modulus - SphericalFunctional.EdgeGradient_RegularTetClosedForm — independent closed-form π/3 oracle locking the Finding-3 edge formula (the path-integral FD check cannot detect a wrong-but-conservative gradient) Also documents the latent spherical/hyperbolic holonomy-extraction bug (same single-development pattern, dead code today) in research-track.md (Phase 9c/10), and adds favour/normalisations to the codespell ignore list. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
97 lines
4.0 KiB
C++
97 lines
4.0 KiB
C++
#pragma once
|
||
// Copyright (c) 2024-2026 Tarik Moussa.
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
// euclidean_geometry.hpp
|
||
//
|
||
// Corner-angle formula for Euclidean triangles in the discrete conformal
|
||
// (log-length) parametrisation.
|
||
//
|
||
// Ported from de.varylab.discreteconformal.functional.EuclideanCyclicFunctional.
|
||
//
|
||
// In the discrete conformal parametrisation a Euclidean triangle is described by
|
||
// its three effective log-lengths Λ̃_ij = λ°_ij + u_i + u_j (+ edge DOF).
|
||
// The corresponding side lengths are l_ij = exp(Λ̃_ij / 2).
|
||
//
|
||
// Vertex ordering convention (matches EuclideanCyclicFunctional.java):
|
||
// v1 is opposite edge l23, v2 is opposite l31, v3 is opposite l12.
|
||
//
|
||
// t-value trick (Springborn 2008 §3):
|
||
// t12 = −l12 + l23 + l31 = 2(s − l12)
|
||
// t23 = +l12 − l23 + l31 = 2(s − l23)
|
||
// t31 = +l12 + l23 − l31 = 2(s − l31)
|
||
// denom = sqrt(t12 · t23 · t31 · l123) = 4 · Area
|
||
//
|
||
// α_v = 2 · atan2( product of t-values adjacent to v, denom )
|
||
//
|
||
// The centering trick (l_ij ← exp((Λ̃_ij − 2·μ)/2), μ = (Λ̃12+Λ̃23+Λ̃31)/6)
|
||
// rescales all three sides by the same factor, leaving angles unchanged but
|
||
// keeping the arguments of exp in a safe numerical range.
|
||
|
||
#include "constants.hpp"
|
||
#include <cmath>
|
||
|
||
namespace conformallab {
|
||
|
||
/// Interior corner angles of a Euclidean triangle.
|
||
struct EuclideanFaceAngles {
|
||
double alpha1; ///< Corner angle at v₁ (opposite l₂₃).
|
||
double alpha2; ///< Corner angle at v₂ (opposite l₃₁).
|
||
double alpha3; ///< Corner angle at v₃ (opposite l₁₂).
|
||
bool valid; ///< `false` when the triangle is degenerate.
|
||
};
|
||
|
||
/// Compute the corner angles of a Euclidean triangle from its three
|
||
/// side lengths. Returns `valid = false` when the triangle inequality
|
||
/// is violated.
|
||
inline EuclideanFaceAngles euclidean_angles_from_lengths(
|
||
double l12, double l23, double l31)
|
||
{
|
||
const double t12 = -l12 + l23 + l31; // 2*(s − l12)
|
||
const double t23 = +l12 - l23 + l31; // 2*(s − l23)
|
||
const double t31 = +l12 + l23 - l31; // 2*(s − l31)
|
||
|
||
// Degenerate (triangle inequality violated): return the *limiting* angles
|
||
// of the flat-out triangle — the corner opposite the over-long edge is π,
|
||
// the other two are 0. This is the convex C¹ extension of the BPS energy
|
||
// onto the infeasible region and is what the Java reference
|
||
// (EuclideanCyclicFunctional.triangleEnergyAndAlphas) does. `valid` stays
|
||
// false so the cotangent Hessian still skips this face. At most one t can
|
||
// be ≤ 0 (t12+t23 = 2·l31 > 0, etc.), so the order of these checks is moot.
|
||
if (t23 <= 0.0) return {PI, 0.0, 0.0, false}; // l23 too long → α₁ = π
|
||
if (t31 <= 0.0) return {0.0, PI, 0.0, false}; // l31 too long → α₂ = π
|
||
if (t12 <= 0.0) return {0.0, 0.0, PI, false}; // l12 too long → α₃ = π
|
||
|
||
const double l123 = l12 + l23 + l31;
|
||
const double denom2 = t12 * t23 * t31 * l123; // = (4·Area)²
|
||
if (denom2 <= 0.0)
|
||
return {0.0, 0.0, 0.0, false};
|
||
|
||
const double denom = std::sqrt(denom2);
|
||
|
||
// α at v1 (opposite l23): adjacent t-values are t12 and t31
|
||
// α at v2 (opposite l31): adjacent t-values are t12 and t23
|
||
// α at v3 (opposite l12): adjacent t-values are t23 and t31
|
||
return {
|
||
2.0 * std::atan2(t12 * t31, denom),
|
||
2.0 * std::atan2(t12 * t23, denom),
|
||
2.0 * std::atan2(t23 * t31, denom),
|
||
true
|
||
};
|
||
}
|
||
|
||
/// Compute the corner angles of a Euclidean triangle from its three
|
||
/// effective log-lengths `Λ̃ᵢⱼ`. Internally centres lengths so that
|
||
/// `l₁₂·l₂₃·l₃₁ = 1` to avoid float overflow for large `|Λ̃|`.
|
||
inline EuclideanFaceAngles euclidean_angles(
|
||
double lam12, double lam23, double lam31)
|
||
{
|
||
const double mu = (lam12 + lam23 + lam31) / 6.0;
|
||
const double l12 = std::exp((lam12 - 2.0 * mu) * 0.5);
|
||
const double l23 = std::exp((lam23 - 2.0 * mu) * 0.5);
|
||
const double l31 = std::exp((lam31 - 2.0 * mu) * 0.5);
|
||
return euclidean_angles_from_lengths(l12, l23, l31);
|
||
}
|
||
|
||
} // namespace conformallab
|