Files
ConformalLabpp/code/include/euclidean_geometry.hpp
Tarik Moussa 8c353bb884 feat(phase3d+3e): port EuclideanCyclicFunctional + add SphericalFunctional gauge-fix
Phase 3d — EuclideanCyclicFunctional:
  • euclidean_geometry.hpp: t-value / atan2 corner-angle formula with
    centering trick (μ = (Λ̃₁₂+Λ̃₂₃+Λ̃₃₁)/6) for numerical stability
  • euclidean_functional.hpp: EuclideanMaps bundle, gradient (G_v = Θ_v − Σα_v,
    G_e = α_opp⁺ + α_opp⁻ − φ_e), 10-point GL path-integral energy,
    gradient_check_euclidean — identical halfedge convention to SphericalFunctional
  • test_euclidean_functional.cpp: 11 tests (1 skip) covering angle formula,
    right-isosceles triangle, angle sum = π, degenerate detection, gradient
    checks on triangle/quad-strip/tetrahedron/fan-5/mixed-pinned, NaN check

Phase 3e — Spherical gauge-fix:
  • spherical_gauge_shift(): Newton + backtracking line search to find t*
    where ΣG_v(x + t·1) = 0 (maximises E along the global scale direction);
    bisection used when sign change is detectable, Newton+backtrack otherwise
  • apply_spherical_gauge(): in-place wrapper
  • 3 new tests: GaugeFix_SpherTetVertexZerosSumGv, GaugeFix_ApplyInPlace,
    GaugeFix_AlreadyAtGaugeReturnsTNearZero

Total: 45 cgal tests pass, 3 skipped (@Ignore Hessian stubs, one per functional)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:39:14 +02:00

92 lines
3.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
// 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 <cmath>
namespace conformallab {
struct EuclideanFaceAngles {
double alpha1; ///< corner angle at v1 (opposite l23)
double alpha2; ///< corner angle at v2 (opposite l31)
double alpha3; ///< corner angle at v3 (opposite l12)
bool valid;
};
// ── From side lengths ─────────────────────────────────────────────────────────
//
// Given three Euclidean side lengths l12, l23, l31 > 0 satisfying the triangle
// inequality, compute the corner angles.
//
// Returns valid=false if the triangle inequality is violated (any t-value ≤ 0).
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)
if (t12 <= 0.0 || t23 <= 0.0 || t31 <= 0.0)
return {0.0, 0.0, 0.0, false};
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
};
}
// ── From effective log-lengths Λ̃ ─────────────────────────────────────────────
//
// Converts to side lengths l_ij = exp(Λ̃_ij / 2), applying the centering
// trick for numerical safety, then delegates to euclidean_angles_from_lengths.
//
// The centering constant μ = (Λ̃12 + Λ̃23 + Λ̃31) / 6 ensures
// l12 · l23 · l31 = 1 (geometric mean = 1)
// which keeps all l values near 1 and prevents 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