New headers:
- spherical_geometry.hpp: spherical arc length l(λ) and half-angle formula
for interior angles of a spherical triangle (SphericalFaceAngles struct)
- spherical_functional.hpp: SphericalMaps bundle, setup/assign DOF helpers,
compute_lambda0_from_mesh(), gradient (Θ_v − Σα_v; Schläfli edge formula),
energy via 10-point Gauss-Legendre path integral, gradient_check_spherical()
Updated:
- mesh_builder.hpp: add make_spherical_tetrahedron() (vertices on unit sphere)
and make_octahedron_face() (single right-angled spherical triangle)
- tests/cgal/CMakeLists.txt: enable test_spherical_functional.cpp
- README.md: rewrite for CGAL-package goal, two test targets, all headers,
updated project tree, Phase progress table, key design decisions
Tests (cgal.SphericalFunctional.*): 8 active + 1 skip
- OctaFaceAnglesAreRightAngles, SpherTetAngleSumExceedsPi
- GradientCheck_{OctaFaceVertex, SpherTetVertex, SpherTetAllDofs,
SpherFan4Vertex, MixedPinnedVertices}
- AnglesFiniteAtKnownPoint
All 30 cgal.* tests pass (2 @Ignore skips).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
83 lines
3.2 KiB
C++
83 lines
3.2 KiB
C++
#pragma once
|
||
// 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 <cmath>
|
||
#include <algorithm>
|
||
|
||
namespace conformallab {
|
||
|
||
constexpr double PI_SPHER = 3.14159265358979323846264338328;
|
||
|
||
// ── 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
|