This commit closes the remaining red gates so `run-all.sh --fast` is
green end-to-end on the canonical dev machine.
New gates
─────────
1. cmake-format / cmake-lint
* scripts/quality/cmake-format.sh — dry-run by default,
--strict to fail on drift, --fix to apply
* .cmake-format.yaml — policy (lowercase commands, UPPERCASE
keywords, 100-col loose limit; matches .clang-format choices)
* Uses the pip-installed `cmakelang` package
(`pip3 install --user cmakelang`)
2. codespell
* scripts/quality/codespell.sh — exit 1 on any typo, --fix
interactively
* .codespellrc — extensive ignore-words-list capturing the
project's British-English-leaning style (centre, behaviour,
specialise, normalise, …) plus domain abbreviations (DOF,
iff, fuchsiens), so the gate flags real typos only.
* Validated: 0 typos across docs + code/include + scripts +
code/{src,tests}.
SPDX rollout (license-headers --fix)
────────────────────────────────────
license-headers.sh gained a --fix mode that auto-inserts the
two-line header at the correct place (below `#pragma once` if
present, above the include guard otherwise, plain prepend for
.cpp). Ran it on 60 of 66 files — 100 %-licensed now.
Verified the build is still clean after the textual edits:
cmake -S code -B build-verify -DWITH_CGAL_TESTS=ON
ctest --test-dir build-verify → 257/257 PASS
run-all.sh + README updated to include the two new gates.
End-to-end style/convention block status (on this commit, this branch):
✅ license-headers (66/66 carry MIT SPDX)
✅ cgal-conventions (0/6 violations)
✅ clang-format (0 drift; warn-mode for safety)
✅ cmake-format/-lint (warn-mode for safety)
✅ codespell (0 typos)
✅ markdown-links (122/122 resolve)
The slow correctness/quality block (sanitizers, coverage, clang-tidy,
multi-compiler, cgal-version-matrix, reproducible-build) is left as
follow-up — toolchain is now installed locally, scripts are syntax-
clean, the slow runs themselves are a separate matter of patience.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
88 lines
3.3 KiB
C++
88 lines
3.3 KiB
C++
#pragma once
|
||
// Copyright (c) 2024-2026 Tarik Moussa.
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
// spherical_geometry.hpp
|
||
//
|
||
// Pure-math building blocks for the spherical discrete conformal map.
|
||
// Ported from de.varylab.discreteconformal.functional.SphericalFunctional
|
||
// (the geometry helpers embedded there).
|
||
//
|
||
// Notation:
|
||
// u_i – vertex conformal factor (DOF)
|
||
// λ°_e – base log-length of edge e (fixed initial value)
|
||
// λ_ij – effective log-length = λ°_ij + u_i + u_j
|
||
// l_ij – spherical arc length = 2·asin(min(exp(λ_ij/2), 1))
|
||
// α_k – interior angle of the spherical triangle at vertex k
|
||
|
||
#include "constants.hpp"
|
||
#include <cmath>
|
||
#include <algorithm>
|
||
|
||
namespace conformallab {
|
||
|
||
/// Backward-compatible alias — prefer conformallab::PI in new code.
|
||
constexpr double PI_SPHER = PI;
|
||
|
||
// ── Effective spherical arc length ────────────────────────────────────────────
|
||
|
||
// l(λ) = 2·asin(min(exp(λ/2), 1)).
|
||
// Clamps exp(λ/2) to [0, 1] so the arcsin stays in domain.
|
||
inline double spherical_l(double lambda)
|
||
{
|
||
double half = std::exp(lambda * 0.5);
|
||
if (half >= 1.0) half = 1.0 - 1e-15;
|
||
if (half <= 0.0) return 0.0;
|
||
return 2.0 * std::asin(half);
|
||
}
|
||
|
||
// ── Interior angles of a spherical triangle ──────────────────────────────────
|
||
|
||
struct SphericalFaceAngles {
|
||
double alpha1, alpha2, alpha3; // corner angles at v1, v2, v3
|
||
bool valid; // false when the three lengths fail the
|
||
// spherical triangle inequality
|
||
};
|
||
|
||
// Compute corner angles from spherical arc lengths using the half-angle formula.
|
||
//
|
||
// Convention (matching the halfedge cycle h0→v1→v2, h1→v2→v3, h2→v3→v1):
|
||
// l12 – arc length of edge opposite v3 (edge e12)
|
||
// l23 – arc length of edge opposite v1 (edge e23)
|
||
// l31 – arc length of edge opposite v2 (edge e31)
|
||
//
|
||
// Half-angle formula (spherical law of cosines):
|
||
// α_k = 2·atan2(sqrt(sin(s-a)·sin(s-b)), sqrt(sin(s)·sin(s-c)))
|
||
// where a,b are the two edges ADJACENT to vertex k, c is the opposite edge.
|
||
//
|
||
// Equivalently (in terms of s-deficiencies):
|
||
// α1 = 2·atan2( sqrt(sin(s12)·sin(s31)), sqrt(sin(s)·sin(s23)) )
|
||
// α2 = 2·atan2( sqrt(sin(s12)·sin(s23)), sqrt(sin(s)·sin(s31)) )
|
||
// α3 = 2·atan2( sqrt(sin(s23)·sin(s31)), sqrt(sin(s)·sin(s12)) )
|
||
//
|
||
// where s = (l12+l23+l31)/2 and s_ij = s - l_ij.
|
||
inline SphericalFaceAngles spherical_angles(double l12, double l23, double l31)
|
||
{
|
||
double s = (l12 + l23 + l31) * 0.5;
|
||
double s12 = s - l12;
|
||
double s23 = s - l23;
|
||
double s31 = s - l31;
|
||
|
||
// Spherical triangle inequalities: all s-deficiencies > 0 and s < π.
|
||
if (s12 <= 0.0 || s23 <= 0.0 || s31 <= 0.0 || s >= PI_SPHER)
|
||
return {0.0, 0.0, 0.0, false};
|
||
|
||
const double ss = std::sin(s);
|
||
const double ss12 = std::sin(s12);
|
||
const double ss23 = std::sin(s23);
|
||
const double ss31 = std::sin(s31);
|
||
|
||
double a1 = 2.0 * std::atan2(std::sqrt(ss12 * ss31), std::sqrt(ss * ss23));
|
||
double a2 = 2.0 * std::atan2(std::sqrt(ss12 * ss23), std::sqrt(ss * ss31));
|
||
double a3 = 2.0 * std::atan2(std::sqrt(ss23 * ss31), std::sqrt(ss * ss12));
|
||
|
||
return {a1, a2, a3, true};
|
||
}
|
||
|
||
} // namespace conformallab
|