diff --git a/README.md b/README.md index ac1c6d8..099fb8d 100644 --- a/README.md +++ b/README.md @@ -1,703 +1,94 @@ # conformallab++ -conformallab++ is a modern C++ reimplementation of -[ConformalLab](https://github.com/varylab/conformallab) — -the research software for discrete conformal geometry by -**Stefan Sechelmann** (TU Berlin, Institut für Mathematik). +C++17 reimplementation of [ConformalLab](https://github.com/varylab/conformallab) — +Stefan Sechelmann's Java research library for discrete conformal geometry (TU Berlin). +The long-term goal is a **CGAL package** for discrete conformal maps. -The algorithmic foundation is his doctoral dissertation: +Algorithmic foundation: +> Stefan Sechelmann — *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, TU Berlin 2016. +> DOI: [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) · CC BY-SA 4.0 · +> [Java original](https://github.com/varylab/conformallab) · [sechel.de](https://sechel.de/) -> Stefan Sechelmann — -> **Variational Methods for Discrete Surface Parameterization: Applications and Implementation** -> Doctoral thesis, Technische Universität Berlin, 2016. -> DOI: [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) -> License: CC BY-SA 4.0 - -The dissertation develops the variational framework for discrete conformal equivalence -on triangulations — discrete uniformization of Riemann surfaces, cone metrics, period -matrices — that forms the mathematical core of both the Java original and this C++ port. - -**Original Java library:** [github.com/varylab/conformallab](https://github.com/varylab/conformallab) (Java, ~850 commits, v1.0.0 2018) -**Author's website:** [sechel.de](https://sechel.de/) · **LinkedIn:** [linkedin.com/in/sechel](https://www.linkedin.com/in/sechel/) - -The long-term goal is a **CGAL package** that brings discrete conformal maps (hyper-ideal, spherical, Euclidean) to the CGAL ecosystem using `CGAL::Surface_mesh` as the underlying half-edge data structure. - -> **Status:** Phase 7 vollständig abgeschlossen. Alle drei Geometrien lösbar via Newton-Solver (SimplicialLDLT + SparseQR-Fallback). Priority-BFS-Layout in ℝ²/S²/Poincaré-Disk mit exakter hyperbolischer Trilateration, Gauss–Bonnet, Tree-Cotree-Schnittgraph, Möbius-Holonomie, Periodenmatrix (Genus 1), Fundamentalbereich-Polygon, halfedge_uv-Texturatlas. JSON/XML-Serialisierung, vollständige CLI-App. **158 Tests, 2 skipped**. +**Status:** Phase 7 complete. Newton solver for all three geometries (Euclidean / Spherical / HyperIdeal), priority-BFS layout in ℝ²/S²/Poincaré disk, Gauss–Bonnet, tree-cotree cut graph, Möbius holonomy, period matrix (genus 1), fundamental domain, halfedge_uv texture atlas, JSON/XML serialisation, CLI app. **158 CGAL tests + 36 non-CGAL tests.** --- -## Features - -| Bereich | Status | -|---------|--------| -| Clausen / Lobachevsky / ImLi₂ Funktionen | ✅ Phase 1 | -| Hyper-ideal Geometrie (ζ, lᵢⱼ, αᵢⱼ, σᵢ, σᵢⱼ) | ✅ Phase 2 | -| CGAL `Surface_mesh` Infrastruktur + Mesh-Builder | ✅ Phase 3a | -| Hyper-ideal Funktional (Energie + Gradient) | ✅ Phase 3b | -| Sphärisches Funktional (Energie + Gradient + Gauge-Fix) | ✅ Phase 3c/3e | -| Euklidisches Funktional (Energie + Gradient) | ✅ Phase 3d | -| Euklidischer Hessian (Kotangenten-Laplace, Pinkall–Polthier) | ✅ Phase 3f | -| Sphärischer Hessian (∂α/∂u aus Kosinussatz) | ✅ Phase 3f | -| Hyper-ideal Hessian (numerisch FD, symmetrisiert) | ✅ Phase 4a | -| Newton-Solver — alle drei Geometrien | ✅ Phase 4a | -| **SparseQR-Fallback** für rangdefiziente H (Gauge-Moden) | ✅ Phase 4a | -| Mesh I/O (CGAL::IO — OFF / OBJ / PLY) | ✅ Phase 4b | -| End-to-End-Pipeline-Tests | ✅ Phase 4c | -| **Beispiel-Programme** (headless + interaktiver Viewer) | ✅ Phase 4d | -| **BFS-Layout** (ℝ², S², Poincaré-Disk) | ✅ Phase 5 | -| **CLI-App** (`conformallab_core`) | ✅ Phase 5 | -| **JSON + XML Serialisierung** | ✅ Phase 5 | -| **Gauss–Bonnet Check + Enforce** | ✅ Phase 6 | -| **Tree-Cotree-Schnittgraph** (2g Naht-Kanten) | ✅ Phase 6 | -| **Exakte hyperbolische Trilateration** (Möbius + Kosinussatz) | ✅ Phase 6 | -| **Layout-Normalisierung** (PCA-Zentrierung / Möbius-Zentrierung) | ✅ Phase 6 | -| **Priority-BFS** (Min-Heap über BFS-Tiefe, minimiert Fehlerakkumulation) | ✅ Phase 7 | -| **MobiusMap** — T(z)=(az+b)/(cz+d), from_three, compose, inverse | ✅ Phase 7 | -| **halfedge_uv** — Naht-bewusstes UV pro Halfedge (GPU-Texturatlas) | ✅ Phase 7 | -| **Möbius-Holonomie** — SU(1,1)-Isometrie pro Schnitt-Kante (hyperbolisch) | ✅ Phase 7 | -| **Periodenmatrix** — τ = ω₂/ω₁ ∈ ℍ, SL(2,ℤ)-Reduktion (Genus 1) | ✅ Phase 7 | -| **Fundamentalbereich** — CCW-Parallelogramm, Kachelkopien | ✅ Phase 7 | -| Analytischer HyperIdeal-Hessian (ζ-Kette) | ❌ Phase 8 geplant | -| Inversive-Distance-Funktional (Luo 2004) | ❌ nicht portiert | -| Vollständige globale Uniformisierung Genus g ≥ 2 | ❌ Phase 8 geplant | -| Periodenmatrix Siegel Ω (g×g, g ≥ 2) | ❌ Phase 8 geplant | - ---- - -## Quick Start — CLI-App +## Quick start ```bash -cmake -S code -B build -DWITH_CGAL=ON -cmake --build build -j4 +git clone https://codeberg.org/TMoussa/ConformalLabpp && cd ConformalLabpp -# Konformes Layout für beliebige OFF/OBJ/PLY-Netze -./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json -x result.xml +# Fast tests — no system dependencies +cmake -S code -B build && cmake --build build --target conformallab_tests -j$(nproc) +ctest --test-dir build --output-on-failure -# Geometrien: euclidean | spherical | hyper_ideal -./bin/conformallab_core -i input.off -g spherical -o sphere.off +# CGAL tests headless (apt install libboost-dev / brew install boost) +cmake -S code -B build -DWITH_CGAL_TESTS=ON +cmake --build build --target conformallab_cgal_tests -j$(nproc) +ctest --test-dir build -R "^cgal\." --output-on-failure -# Interaktiver Viewer -./bin/conformallab_core -i input.off -s -``` - -### Beispiel-Programme - -```bash -./build/examples/example_layout [input.off] [layout.off] [result.json] [result.xml] -./build/examples/example_euclidean [input.off] [output.off] -./build/examples/example_hyper_ideal [input.off] [output.off] -./build/examples/example_viewer [input.off] # interaktiv (WITH_VIEWER) +# Full build with CLI + viewer (requires Wayland/X11 dev headers) +cmake -S code -B build -DWITH_CGAL=ON && cmake --build build -j$(nproc) +./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json ``` --- -## Bibliotheks-Nutzung - -### Minimale euklidische Pipeline +## Minimal usage ```cpp #include "conformal_mesh.hpp" #include "mesh_io.hpp" #include "euclidean_functional.hpp" +#include "gauss_bonnet.hpp" #include "newton_solver.hpp" #include "layout.hpp" using namespace conformallab; -int main() { - ConformalMesh mesh = load_mesh("input.off"); +ConformalMesh mesh = load_mesh("input.off"); +EuclideanMaps maps = setup_euclidean_maps(mesh); +compute_euclidean_lambda0_from_mesh(mesh, maps); - auto maps = setup_euclidean_maps(mesh); - compute_euclidean_lambda0_from_mesh(mesh, maps); +// Assign DOFs — pin first vertex (gauge fix) +auto vit = mesh.vertices().begin(); +maps.v_idx[*vit++] = -1; +int idx = 0; +for (; vit != mesh.vertices().end(); ++vit) maps.v_idx[*vit] = idx++; - // DOFs zuweisen — ersten Vertex pinnen (Gauge-Fix) - auto vit = mesh.vertices().begin(); - maps.v_idx[*vit++] = -1; - int idx = 0; - for (; vit != mesh.vertices().end(); ++vit) - maps.v_idx[*vit] = idx++; +// Natural equilibrium target: x* = 0 by construction +std::vector x0(idx, 0.0); +auto G0 = euclidean_gradient(mesh, x0, maps); +for (auto v : mesh.vertices()) + if (maps.v_idx[v] >= 0) maps.theta_v[v] -= G0[maps.v_idx[v]]; - // Zielwinkel: natürliches Gleichgewicht (x* = 0) - std::vector x0(idx, 0.0); - auto G0 = euclidean_gradient(mesh, x0, maps); - for (auto v : mesh.vertices()) { - int iv = maps.v_idx[v]; - if (iv >= 0) maps.theta_v[v] -= G0[iv]; - } - - auto res = newton_euclidean(mesh, x0, maps); - if (res.converged) - save_mesh("output.off", mesh); - return res.converged ? 0 : 1; -} -``` - -### Layout + Holonomie (geschlossene Flächen) - -```cpp -#include "layout.hpp" -#include "cut_graph.hpp" -#include "period_matrix.hpp" -#include "fundamental_domain.hpp" - -// Schnittgraph berechnen (Tree-Cotree, 2g Kanten) -CutGraph cg = compute_cut_graph(mesh); - -// Layout mit Holonomie-Tracking -HolonomyData hol; -Layout2D lay = euclidean_layout(mesh, res.x, maps, &cg, &hol, /*normalise=*/true); - -// Periodenmatrix (Genus 1) -PeriodData pd = compute_period_matrix(hol); // τ = ω₂/ω₁ ∈ ℍ, SL(2,ℤ)-reduziert -std::cout << "τ = " << pd.tau << "\n"; - -// Fundamentalbereich-Parallelogramm -FundamentalDomain fd = compute_fundamental_domain(hol); -// fd.vertices — 4 Ecken (CCW) -// fd.generators — ω₁, ω₂ - -// Kachelkopie für Universalüberlagerung -Layout2D tile = tiling_copy(lay, fd.generators[0], fd.generators[1], 1, -1); - -// halfedge_uv — Naht-bewusstes UV pro Halfedge (GPU-Texturatlas) -// lay.halfedge_uv[h.idx()] = UV von source(h) aus Sicht von face(h) -``` - -### HyperIdeal-Geometrie - -```cpp -auto maps = setup_hyper_ideal_maps(mesh); -int n = assign_all_dof_indices(mesh, maps); -auto result = newton_hyper_ideal(mesh, x0, maps); - -// Möbius-Holonomie (SU(1,1)-Isometrien pro Schnitt-Kante) -HolonomyData hol; -Layout2D lay = hyper_ideal_layout(mesh, result.x, maps, &cg, &hol); -// hol.mobius_maps[i] = T_i (Möbius-Abbildung für i-te Schnitt-Kante) -``` - -### SparseQR-Fallback direkt nutzen - -```cpp -#include "newton_solver.hpp" - -bool used_fallback = false; -auto dx = conformallab::solve_linear_system(H, rhs, &used_fallback); -if (used_fallback) - std::cout << "SparseQR verwendet (H ist rangdefizient)\n"; +check_gauss_bonnet(mesh, maps); +NewtonResult res = newton_euclidean(mesh, x0, maps); +Layout2D layout = euclidean_layout(mesh, res.x, maps); ``` --- -## Build-Modi - -| Modus | CMake-Flags | Was wird gebaut | System-Deps | -|-------|------------|-----------------|-------------| -| **Nur Tests** (Standard / CI fast) | *(keine)* | `conformallab_tests` — 36 Tests | keine | -| **CGAL-Tests headless** (CI full) | `-DWITH_CGAL_TESTS=ON` | `conformallab_cgal_tests` — 158 Tests | Boost | -| **Vollständig** (lokal mit Viewer) | `-DWITH_CGAL=ON` | + CLI, Beispiel-Programme, interaktiver Viewer | Boost + Wayland/X11 | - -**`-DWITH_CGAL_TESTS=ON`** ist der empfohlene CI-Modus: kein GLFW, kein wayland-scanner, kein Display benötigt. -**`-DWITH_CGAL=ON`** aktiviert automatisch den Viewer — nur für lokale Entwicklung mit Display. - -Externe Abhängigkeiten sind als Tarballs in `code/deps/tarballs/` enthalten und werden beim CMake-Configure-Schritt extrahiert (GTest via `FetchContent`). **Boost** wird nur mit `-DWITH_CGAL_TESTS=ON` oder `-DWITH_CGAL=ON` benötigt (Header-Only durch CGAL 6.x). - ---- - -## Voraussetzungen - -| Tool | Minimum | -|------|---------| -| C++ Compiler (GCC oder Clang) | C++17 | -| CMake | 3.20 | -| Boost Headers | 1.70 *(nur mit `-DWITH_CGAL_TESTS=ON` oder `-DWITH_CGAL=ON`)* | - ---- - -## Einstieg - -```bash -git clone https://codeberg.org/TMoussa/ConformalLabpp -cd ConformalLabpp -``` - -### Nur Tests (CI-Standard — keine System-Abhängigkeiten) - -```bash -cmake -S code -B build -cmake --build build --target conformallab_tests -j$(nproc) -ctest --test-dir build --output-on-failure -``` - -### CGAL-Tests headless (CI — kein Display, kein wayland-scanner) - -```bash -# benötigt: apt install libboost-dev (Ubuntu) / brew install boost (macOS) -cmake -S code -B build -DWITH_CGAL_TESTS=ON -cmake --build build --target conformallab_cgal_tests -j$(nproc) -ctest --test-dir build -R "^cgal\." --output-on-failure -``` - -Erwartet: **158 Tests bestanden, 2 skipped** (die zwei `@Ignore`-Hessian-Stubs). - -### Vollständig lokal (CLI + Beispiele + interaktiver Viewer) - -```bash -# benötigt zusätzlich: Wayland/X11 dev-Pakete (wayland-scanner, libx11-dev …) -cmake -S code -B build -DWITH_CGAL=ON -cmake --build build -j$(nproc) -ctest --test-dir build -R "^cgal\." --output-on-failure -./build/examples/example_layout -./build/examples/example_viewer data/off/example.off -./bin/conformallab_core -i input.off -g euclidean -o layout.off -``` - ---- - -## Öffentliche Header (`code/include/`) - -| Header | Beschreibung | -|--------|-------------| -| `clausen.hpp` | Clausen Cl₂, Lobachevsky Л, ImLi₂ | -| `hyper_ideal_geometry.hpp` | ζ-Funktionen, lᵢⱼ, αᵢⱼ, σᵢ, σᵢⱼ | -| `hyper_ideal_utility.hpp` | Tetraeder-Volumen (Meyerhoff / Kolpakov–Mednykh) | -| `hyper_ideal_functional.hpp` | HyperIdeal Energie + Gradient auf `ConformalMesh` | -| `hyper_ideal_hessian.hpp` | HyperIdeal Hessian (numerisch FD, symmetrisiert) | -| `hyper_ideal_visualization_utility.hpp` | Poincaré-Disk-Projektion, Umkreis-Helfer | -| `spherical_geometry.hpp` | Sphärische Bogenlänge, Halbwinkelformel | -| `spherical_functional.hpp` | Sphärisch: Energie + Gradient + Gauge-Fix | -| `spherical_hessian.hpp` | Sphärischer Hessian (∂α/∂u, Kosinussatz) | -| `euclidean_geometry.hpp` | Euklidischer Eckenwinkel (t-Wert / atan2) | -| `euclidean_functional.hpp` | Euklidisch: Energie + Gradient | -| `euclidean_hessian.hpp` | Kotangenten-Laplace Hessian (Pinkall–Polthier) | -| `newton_solver.hpp` | `newton_{euclidean,spherical,hyper_ideal}` + öffentliches `solve_linear_system` | -| `conformal_mesh.hpp` | `ConformalMesh` = `CGAL::Surface_mesh` + Property-Map-Helfer | -| `mesh_builder.hpp` | `make_triangle` / `make_tetrahedron` / `make_quad_strip` / `make_fan` / … | -| `mesh_io.hpp` | `read_mesh` / `write_mesh` / `load_mesh` / `save_mesh` | -| `mesh_utils.hpp` | CGAL → Eigen Konvertierung (`cgal_to_eigen`) | -| `serialization.hpp` | `save/load_result_json` + `save/load_result_xml` | -| `gauss_bonnet.hpp` | `euler_characteristic`, `genus`, `gauss_bonnet_sum/rhs/deficit`, `check_gauss_bonnet`, `enforce_gauss_bonnet` | -| `cut_graph.hpp` | `CutGraph` + `compute_cut_graph` (Tree-Cotree, Erickson–Whittlesey 2005) | -| `layout.hpp` | `euclidean/spherical/hyper_ideal_layout` → `Layout2D/3D`; `MobiusMap`; `halfedge_uv`; Priority-BFS; `HolonomyData`; `normalise_*` | -| `period_matrix.hpp` | `PeriodData`, `compute_period_matrix`, `reduce_to_fundamental_domain`, `is_in_fundamental_domain` | -| `fundamental_domain.hpp` | `FundamentalDomain`, `compute_fundamental_domain_{genus1,}`, `tiling_copy`, `tiling_neighbourhood` | -| `constants.hpp` | `conformallab::PI`, `TWO_PI` | - ---- - -## Projektstruktur - -``` -code/ -├── include/ # Alle öffentlichen Header (Header-Only-Bibliothek) -│ ├── conformal_mesh.hpp -│ ├── mesh_builder.hpp -│ ├── mesh_io.hpp / mesh_utils.hpp -│ ├── newton_solver.hpp # 3 Newton-Solver + solve_linear_system -│ ├── layout.hpp # Priority-BFS, MobiusMap, halfedge_uv, Holonomie -│ ├── serialization.hpp -│ ├── gauss_bonnet.hpp -│ ├── cut_graph.hpp # Tree-Cotree -│ ├── period_matrix.hpp # τ ∈ ℍ, SL(2,ℤ)-Reduktion (Genus 1) -│ ├── fundamental_domain.hpp # Parallelogramm, Kacheln; 4g-Polygon TODO Phase 8 -│ ├── hyper_ideal_{functional,hessian,geometry,utility,visualization_utility}.hpp -│ ├── spherical_{functional,hessian,geometry}.hpp -│ ├── euclidean_{functional,hessian,geometry}.hpp -│ ├── clausen.hpp -│ └── constants.hpp -├── examples/ -│ ├── example_euclidean.cpp -│ ├── example_hyper_ideal.cpp -│ ├── example_layout.cpp # Solve → Layout → OFF/JSON/XML + Round-Trip -│ └── example_viewer.cpp # Interaktiver libigl-Viewer -├── src/ -│ ├── apps/v0/conformallab_cli.cpp # CLI-App (Phase 5) -│ └── viewer/simple_viewer.cpp -├── tests/ -│ ├── *.cpp # conformallab_tests (kein CGAL) -│ └── cgal/ -│ ├── test_conformal_mesh.cpp # 14 Tests -│ ├── test_hyper_ideal_functional.cpp # 7 Tests -│ ├── test_spherical_functional.cpp # 12 Tests (1 skipped) -│ ├── test_euclidean_functional.cpp # 11 Tests (1 skipped) -│ ├── test_euclidean_hessian.cpp # 9 Tests -│ ├── test_spherical_hessian.cpp # 8 Tests -│ ├── test_newton_solver.cpp # 14 Tests -│ ├── test_mesh_io.cpp # 9 Tests -│ ├── test_pipeline.cpp # 5 Tests -│ ├── test_layout.cpp # 8 Tests (Layout + JSON/XML) -│ ├── test_phase6.cpp # 26 Tests (GB, CutGraph, Trilateration) -│ └── test_phase7.cpp # 37 Tests (MobiusMap, Priority-BFS, -│ # halfedge_uv, Periodenmatrix, FD) -└── deps/ - ├── eigen-3.4.0/ - ├── CGAL-6.1.1/ - ├── libigl-2.6.0/ - ├── glfw-3.4/ - └── single_includes/ # CLI11, json.hpp -``` - ---- - -## Test-Suiten - -### `conformallab_tests` (CI — immer gebaut) - -Reine Mathe-Tests, nur Eigen: Clausen / Lobachevsky / ImLi₂, Hyper-ideal Geometrie, Tetraeder-Volumina. - -### `conformallab_cgal_tests` (`-DWITH_CGAL_TESTS=ON` headless / `-DWITH_CGAL=ON` voll) - -| Suite | Tests | Was geprüft wird | -|-------|------:|-----------------| -| `ConformalMeshTopology` | 4 | Euler-Charakteristik, Vertex/Edge/Face-Anzahl | -| `ConformalMeshTraversal` | 4 | Halfedge-Iteration, Valenz, Opposite | -| `ConformalMeshProperties` | 5 | Property-Maps (λ, θ, idx, α, Geometrietyp) | -| `ConformalMeshValidity` | 1 | CGAL-Validität für alle Factory-Meshes | -| `HyperIdealFunctional` | 7 | FD-Gradient-Checks + Hessian-Symmetrie | -| `SphericalFunctional` | 12 | Winkelformel + Gradient + Gauge-Fix (1 skipped) | -| `EuclideanFunctional` | 11 | Winkelformel + Gradient (1 skipped) | -| `EuclideanHessian` | 9 | Kotangenten-Laplace-Struktur, FD-Übereinstimmung, PSD, Nullraum | -| `SphericalHessian` | 8 | Ableitungskorrektheit, NSD am Gleichgewicht | -| `NewtonSolver` | 11 | Konvergenz (Eucl. ×3, Sphär. ×4, HyperIdeal ×4) | -| `SparseQRFallback` | 3 | Full-Rank-LDLT · singuläre Matrix → QR · geschlossenes Mesh | -| `MeshIO` | 9 | OFF/OBJ Round-Trips, Fehlerbehandlung | -| `Pipeline` | 5 | End-to-End: Build → Setup → Solve → Export → Reload | -| `Layout` | 8 | Kantenlängen-Erhaltung (Eucl./Sphär.), Poincaré-Disk | -| `Serialization` | 2 | JSON- und XML-Round-Trips (DOF + Layout) | -| `GaussBonnet` | 8 | χ, Genus, Summe/RHS, Defizit, Check, Enforce | -| `CutGraph` | 6 | Tree-Cotree, offene/geschlossene Meshes, Flag–Index-Konsistenz | -| `HyperbolicTrilateration` | 4 | Möbius + Kosinussatz: exakte Abstände, Disk-Inneres, off-origin | -| `Normalisation` | 4 | Eukl. Schwerpunkt, Längenverhältnisse, Möbius-Zentrierung | -| `MobiusMap` | 8 | Identity, Inverse, Compose, from_three, apply(Vector2d) | -| `BestRootFace` | 2 | Gültige Wurzel-Fläche, Interior-Bonus | -| `HalfedgeUV` | 4 | Größe = #Halfedges, Naht-Konsistenz, Randhalfedges = 0 | -| `PriorityBFS` | 3 | Erfolg, kein Seam bei offenen Meshes, alle Vertices platziert | -| `NormaliseEuclidean` | 2 | UV-Schwerpunkt = 0, halfedge_uv-Schwerpunkt = 0 | -| `PeriodMatrix` | 7 | τ ∈ ℍ, SL(2,ℤ)-Reduktion, Ausnahme außerhalb ℍ | -| `FundamentalDomain` | 7 | Genus-1-Parallelogramm CCW, Generatoren, g>1 leer | -| `TilingCopy/Neighbourhood` | 4 | Verschiebung korrekt, Anzahl Kacheln | -| **Gesamt** | **158** | **2 skipped** (Hessian-Stubs, identisch mit Java `@Ignore`) | - ---- - -## Newton-Solver & SparseQR-Fallback - -`newton_solver.hpp` stellt drei Solver mit einheitlicher Schnittstelle bereit: - -``` -NewtonResult newton_euclidean (mesh, x0, maps [, tol, max_iter]) -NewtonResult newton_spherical (mesh, x0, maps [, tol, max_iter]) -NewtonResult newton_hyper_ideal(mesh, x0, maps [, tol, max_iter, hess_eps]) -``` - -Jede Iteration: Gradient **G** → Hessian **H** → löse **H·Δx = −G** (SimplicialLDLT, Fallback SparseQR) → Backtracking-Liniensuche. - -| Geometrie | **G** | **H**-Vorzeichen | -|-----------|-------|-----------------| -| Euklidisch | Θ_v − Σα_v | PSD → LDLT auf H | -| Sphärisch | Θ_v − Σα_v | NSD → LDLT auf **−H** | -| HyperIdeal | Σβ_v − Θ_v | PSD → LDLT auf H | - -**SparseQR-Fallback** (`solve_linear_system`): Bei rangdefizientem **H** (Gauge-Moden bei geschlossenen Meshes ohne gepinnten Vertex) findet SparseQR den minimalen Newton-Schritt orthogonal zum Nullraum. - ---- - -## Mathematischer Umfang — C++ vs. Java-Original - -| Mathematische Schicht | Java ConformalLab | conformallab++ | -|---|---|---| -| Euklidisches Funktional — Energie, Gradient | ✅ | ✅ | -| Sphärisches Funktional — Energie, Gradient, Gauge-Fix | ✅ | ✅ | -| HyperIdeal Funktional — Energie, Gradient | ✅ | ✅ | -| Inversive-Distance-Funktional (Luo 2004) | ✅ | ❌ nicht portiert | -| Euklidischer Hessian — Kotangenten-Laplace | ✅ analytisch | ✅ analytisch | -| Sphärischer Hessian — ∂α/∂u aus Kosinussatz | ✅ analytisch | ✅ analytisch | -| HyperIdeal Hessian — ζ → lᵢⱼ → β/α Kette | ✅ analytisch | ⚠️ symmetrisches FD | -| Newton-Solver | ✅ | ✅ | -| SparseQR-Fallback für Gauge-Moden | ? | ✅ | -| Kegelmetriken — vorgeschriebenes Θ_v ≠ 2π | ✅ vollständig | ⚠️ nur Datenstruktur | -| Layout / Einbettung — ℝ² / H² / S² | ✅ | ✅ Priority-BFS, alle drei | -| Exakte hyperbolische Trilateration | ✅ Möbius | ✅ Möbius + Kosinussatz | -| halfedge_uv — Naht-bewusstes UV (Texturatlas) | ✅ | ✅ | -| Gauss–Bonnet Konsistenzprüfung | ✅ | ✅ | -| Tree-Cotree-Schnittgraph (2g Kanten) | ✅ | ✅ Erickson–Whittlesey | -| Holonomie / Monodromie — Euklidisch (Translationen) | ✅ | ✅ | -| Holonomie — Hyperbolisch (SU(1,1) Möbius-Mappe) | ✅ | ✅ | -| Periodenmatrix τ — Genus 1 (SL(2,ℤ)-reduziert) | ✅ | ✅ | -| Fundamentalbereich-Polygon — Genus 1 | ✅ | ✅ CCW-Parallelogramm | -| 4g-Polygon-Randlauf — Genus g > 1 | ✅ | ❌ TODO Phase 8 | -| Periodenmatrix Siegel Ω — Genus g ≥ 2 | ✅ | ❌ TODO Phase 8 | -| Globale Uniformisierung — Genus g ≥ 2 | ✅ | ❌ Phase 8 geplant | -| Clausen / Lobachevsky / ImLi₂ | ✅ | ✅ | -| Poincaré-Disk / Lorentz-Boost Visualisierung | ✅ | ✅ | -| Mesh I/O + Serialisierung | ✅ XML/CoHDS | ✅ OFF/OBJ/PLY + JSON/XML | -| Interaktiver Viewer | ✅ jReality | ✅ libigl/GLFW | - -### HyperIdeal Hessian — numerisch vs. analytisch - -Der analytische Hessian des HyperIdeal-Funktionals erfordert Differentiation durch die Kette `(bᵢ, aₑ) → lᵢⱼ → ζ₁₃/₁₄/₁₅ → αᵢⱼ / βᵢ` (vier Vertex-Typ-Kombinationen pro Kante). conformallab++ verwendet stattdessen einen symmetrisierten Finite-Differenzen-Hessian: - -``` -H[i,j] = ( G(x + ε·eⱼ)[i] − G(x − ε·eⱼ)[i] ) / (2ε) -``` - -O(ε²)-genau (≈ 10⁻¹⁰ relativer Fehler bei ε = 10⁻⁵), PSD durch strikte Konvexität (Springborn 2020), kostet n zusätzliche Gradient-Auswertungen pro Newton-Schritt. Für Meshes mit < 500 DOFs ist der Unterschied in der Wandzeit vernachlässigbar. Der analytische Hessian ist für Phase 8 geplant. - ---- - -## Für Mathematiker — Bibliothek erweitern - -### Mentales Modell - -``` -ConformalMesh — Halfedge-Mesh (CGAL::Surface_mesh) - + Property-Maps — per-Vertex/Edge Daten (λ, θ, α, DOF-Index, …) - -Maps-Struct — sammelt alle Property-Maps für ein Funktional - theta_v[v] — Zielwinkel bei Vertex v (Eingabe) - v_idx[v] — DOF-Index, oder −1 wenn gepinnt - e_idx[e] — DOF-Index für Kanten-DOFs (nur HyperIdeal) - -x ∈ ℝⁿ — DOF-Vektor, den der Solver optimiert - -evaluate_*(mesh, x, maps) → { Energie, Gradient, … } -newton_*(mesh, x0, maps) → { x*, Iterationen, converged, … } -``` - -Die Mesh-Geometrie (Vertex-Positionen) dient nur zur Initialisierung der Log-Kantenlängen λ°. Danach arbeitet der Solver ausschließlich im x-Raum. - -### Neues Funktional hinzufügen - -1. **Maps-Struct**: `setup_my_maps(mesh)` mit Property-Maps für λ, θ_v, v_idx -2. **Energie + Gradient**: Schleife über `mesh.faces()`, akkumuliere in `grad[v_idx[v]]` -3. **Gradient-Check**: Finite-Differenzen-Verifikation (Vorlage in jedem `test_*_functional.cpp`) -4. **Newton-Solver**: `solve_linear_system(H, -G, &used_fallback)` direkt nutzen - -### Halfedge-Mesh navigieren - -```cpp -for (auto f : mesh.faces()) { - auto h0 = mesh.halfedge(f); - auto h1 = mesh.next(h0); - auto h2 = mesh.next(h1); - - Vertex_index v_opp = mesh.target(h2); // dem Halfedge h0 gegenüberliegend - int dof = maps.v_idx[v_opp]; // −1 = gepinnt - - bool is_boundary = mesh.is_border(mesh.opposite(h0)); -} -``` - -### Neue Daten am Mesh befestigen - -```cpp -auto [curv, created] = mesh.add_property_map("v:my_curv", 0.0); -curv[v] = 1.234; -``` - -### Schnell-Start-Checkliste - -1. `examples/example_layout.cpp` lesen — zeigt die vollständige Pipeline in ~120 Zeilen -2. `cmake -S code -B build -DWITH_CGAL=ON && cmake --build build --target example_layout` -3. Gradient-Check-Test in `tests/cgal/` hinzufügen (beliebigen `GradientCheck_*`-Block kopieren) -4. Verschiedene Zielwinkel ausprobieren: `maps.theta_v[v] = M_PI / 3` für alle Innen-Vertices. Die Gauss–Bonnet-Bedingung Σ(2π − Θ_v) = 2π·χ(M) muss erfüllt sein. -5. Konvergenz beobachten: `NewtonResult` enthält `iterations` und `grad_inf_norm` - -### Weiterführende Literatur - -| Quelle | Bezug | -|--------|-------| -| Springborn — *Ideal Hyperbolic Polyhedra and Discrete Uniformization* (2020) | HyperIdeal-Funktional; ζ₁₃/₁₄/₁₅ in `hyper_ideal_geometry.hpp` | -| Pinkall, Polthier — *Computing Discrete Minimal Surfaces* (1993) | Kotangenten-Laplace in `euclidean_hessian.hpp` | -| Luo — *Combinatorial Yamabe Flow on Surfaces* (2004) | Inversive-Distance-Funktional (noch nicht portiert) | -| Bobenko, Springborn — *Variational Principles for Circle Patterns* (2004) | Hintergrund für das Winkelsum-Variationsprinzip | -| Erickson, Whittlesey — *Greedy Optimal Homotopy and Homology Generators* (SODA 2005) | Tree-Cotree-Algorithmus in `cut_graph.hpp` | - ---- - -## Schlüssel-Designentscheidungen - -**CGAL als CoHDS-Ersatz.** `CGAL::Surface_mesh` ersetzt die Java-`CoHDS`-Halfedge-Datenstruktur. Vertex/Edge/Face/Halfedge-Deskriptoren sind typisierte Integer. - -**Property-Maps.** `mesh.add_property_map("v:lambda", 0.0)` ersetzt das Java-Adapter/Decorator-Pattern. - -**DOF-Vektor-Konvention.** Alle Funktionale verwenden `x` indiziert durch `v_idx[v]` / `e_idx[e]` (−1 = gepinnt). Einheitlich über alle drei Geometrien. - -**Priority-BFS.** Faces werden in aufsteigender BFS-Tiefe verarbeitet (Min-Heap über `depth = max(depth[v_src], depth[v_tgt]) + 1`). Dies minimiert die Akkumulation von Trilaterations-Fehlern — je weiter eine Fläche vom Ursprung entfernt ist, desto später wird sie platziert. - -**halfedge_uv-Semantik.** `halfedge_uv[h.idx()]` ist das UV von `source(h)` aus Sicht von `face(h)`. An Naht-Halfedges tragen die beiden gegenüberliegenden Halfedges unterschiedliche UV-Werte — so erhält jede Fläche ihre eigene Kopie eines Naht-Vertex für GPU-Texturatlas ohne Vertex-Duplizierung. - -**HyperIdeal Hessian via FD.** Der analytische Hessian durch `ζ13/14/15 → lij → β/α` ist auf Phase 8 verschoben. Ein symmetrischer FD-Hessian ist O(ε²)-genau, PSD durch strikte Konvexität und für < 500 DOFs ausreichend. - -**Sphärischer Hessian Vorzeichen.** Die sphärische Energie ist **konkav** (Hessian NSD). Newton löst `(−H)·Δx = G`, das Vorzeichen wird transparent in `newton_spherical` behandelt. - ---- - -## CI - -Tests laufen automatisch bei Push auf `main`, `dev` und `claude/**`-Branches via selbst-gehostetem Gitea-Actions-Runner (`eulernest`, ARM64). **Nur `conformallab_tests` läuft in CI** (kein Boost/CGAL dort). - -```bash -# CI-Image neu bauen und pushen -docker buildx build \ - --platform linux/arm64 \ - -f .gitea/docker/Dockerfile.ci-cpp \ - -t git.eulernest.eu/conformallab/ci-cpp:latest \ - --push \ - .gitea/docker/ -``` - ---- - -## Roadmap - -> **Legende:** ✅ abgeschlossen · 🔲 geplant -> -> **Grenze Portierung / neue Forschung:** -> Phase 1–7 sind direkte Portierungen aus dem Java-Original bzw. seiner Dissertation. -> Ab Phase 8 geht die Arbeit über den Umfang der Java-Bibliothek hinaus. -> — Phase 8 (CGAL-Paket) ist **Infrastruktur**, kein neuer Algorithmus. -> — Phase 9 (Inversive-Distance, Analytischer Hessian) ist **Portierung** ausstehender Java-Features. -> — Phase 10+ ist **eigenständige Forschung**, die über das Java-Original hinausgeht. - ---- - -### ◼ Portierungsphase abgeschlossen - -``` -Phase 1 Clausen / Lobachevsky / ImLi₂ ✅ -Phase 2 Hyper-ideal Geometrie (ζ, lᵢⱼ, αᵢⱼ, σᵢ) ✅ -Phase 3 CGAL-Infrastruktur + alle drei Funktionale - + analytische Hessians (Eucl. + Sphär.) ✅ -Phase 4 Newton-Solver (SimplicialLDLT + SparseQR-Fallback) - + Mesh-I/O + Beispielprogramme ✅ -Phase 5 Priority-BFS-Layout + CLI + JSON/XML ✅ 95 Tests - -Phase 6 Layout-Parität I ✅ 121 Tests - → gauss_bonnet.hpp — χ, Genus, Σ(2π-Θ_v) Check + Enforce - → cut_graph.hpp — Tree-Cotree (Erickson–Whittlesey 2005), 2g Schnitt-Kanten - → Exakte hyperbolische Trilateration (Möbius + Kosinussatz) - → normalise_{euclidean,hyperbolic,spherical} - -Phase 7 Layout-Parität II ✅ 158 Tests - → MobiusMap — T(z)=(az+b)/(cz+d), from_three, compose, inverse - → halfedge_uv — naht-bewusstes UV pro Halfedge (GPU-Texturatlas) - → Möbius-Holonomie als SU(1,1)-Isometrie (hyperbolisch) - → period_matrix.hpp — τ = ω₂/ω₁ ∈ ℍ, SL(2,ℤ)-Reduktion - → fundamental_domain.hpp — CCW-Parallelogramm, Kachelung -``` - ---- - -### ◼ Infrastruktur (über Java-Bibliothek hinaus) - -``` -Phase 8 CGAL-Paket-Struktur 🔲 (nächste Phase) - - Ziel: conformallab++ als eigenständiges CGAL-Paket, das in CGAL integriert - werden kann und dessen Konventionen vollständig erfüllt. - - 8a — Traits-Klasse & Konzepte - → include/CGAL/Conformal_map_traits.h - Trennt MeshType, KernelType, ScalarType vom Algorithmus. - Ermöglicht Nutzung mit beliebigem CGAL-kompatiblem Mesh. - → Konzept-Checks (static_assert / CGAL_concept_check) - - 8b — Öffentliche CGAL-Header-Hierarchie - → include/CGAL/Discrete_conformal_map.h (zentraler Nutzer-Header) - → include/CGAL/Conformal_newton_solver.h - → include/CGAL/Conformal_layout.h - → include/CGAL/Conformal_cut_graph.h - → include/CGAL/conformal_map_package.h (Package-Description) - Alle bestehenden include/conformallab/*.hpp bleiben als Impl.-Detail. - - 8c — Dokumentation im CGAL-Stil - → doc/Conformal_map/PackageDescription.txt - → doc/Conformal_map/fig/ (Pipeline-Diagramme) - → Doxygen-Kommentare für alle öffentlichen Konzepte + Funktionen - → User_manual.md + Reference_manual.md - - 8d — CGAL-Testformat - → test/Conformal_map/ (CMakeLists.txt im CGAL-Format) - Bestehende GTest-Tests bleiben; CGAL-Tests kommen als zweites Format. - - 8e — Declarative YAML-Pipeline - → Leichtgewichtiges YAML-Format für reproduzierbare Experimente - (Spezifikation bereits in doc/architecture/overall_pipeline.md) - → Validator: prüft require/provide-Tokens vor der Ausführung - → Einbindung in CLI-App: conformallab_core --pipeline experiment.yml -``` - ---- - -### ◼ Ausstehende Portierung (Java-Features noch nicht übertragen) - -``` -Phase 9 Verbleibende Java-Parität 🔲 - - 9a — Inversive-Distance-Funktional (Luo 2004 / Bowers–Stephenson) - → inversive_distance_functional.hpp (folgt exakt dem Muster der - bestehenden drei Funktionale — niedrigstes Risiko) - → newton_inversive_distance() - → Neue Test-Suite: test_inversive_distance.cpp - - 9b — Analytischer HyperIdeal-Hessian - → Direkte Ableitung durch die Kette - (b_i, a_e) → l_ij → ζ₁₃/ζ₁₄/ζ₁₅ → α_ij / β_i - → Ersetzt den symmetrischen FD-Hessian in hyper_ideal_hessian.hpp - → Relevant für Meshes > 500 DOFs (aktueller FD-Hessian ist dort langsam) - → Aufwand: ~2 Wochen (viele verschachtelte Fallunterscheidungen) - - 9c — 4g-Polygon-Randlauf (Genus g > 1) - → Boundary-Walk auf dem aufgeschnittenen Mesh - → Befüllt fundamental_domain.hpp für g > 1 (aktuell: leeres Objekt) - → Algorithmus-Skizze bereits als TODO(Phase 8) in fundamental_domain.hpp -``` - ---- - -### ◼ Neue Forschung (über das Java-Original hinaus) - -> Ab hier gibt es keine direkte Java-Referenzimplementierung mehr. -> Jedes Item ist eigenständige mathematische Arbeit. - -``` -Phase 10 Globale Uniformisierung Genus g ≥ 2 🔲 (Forschung) - - 10a — Holomorphe Differentiale auf diskreten Flächen - Integration ω_i längs der b-Zyklen des Schnittgraphen. - Mathematische Grundlage: Bobenko–Springborn (2004), §6. - - 10b — Siegel-Periodenmatrix Ω ∈ H_g (g×g, g ≥ 2) - Ω_ij = ∫_{b_j} ω_i — komplexe symmetrische Matrix, - Im(Ω) positiv definit (Siegel-Oberhalbebene H_g). - Reduktion auf den Siegel-Fundamentalbereich via Sp(2g,ℤ). - - 10c — Vollständige Uniformisierung - Für g ≥ 2: Einbettung als H²/Γ mit Γ ⊂ PSL(2,ℝ) Fuchssche Gruppe. - Erfordert 10a + 10b + stabilen Cut-Graph für g ≥ 2 (Phase 9c). -``` - ---- - -## Ursprung & Danksagung - -conformallab++ wäre ohne die Grundlagenarbeit von **Stefan Sechelmann** nicht möglich. -Die Algorithmen, die Variationsformulierung und die Idee, diskrete konforme Geometrie -als Newton-Problem auf Winkel-Summen-Energie-Funktionalen zu behandeln, stammen aus: +## Documentation | | | |---|---| -| **Dissertation** | Stefan Sechelmann — *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, TU Berlin 2016 | -| **DOI** | [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) | -| **Java-Originalbibliothek** | [github.com/varylab/conformallab](https://github.com/varylab/conformallab) | -| **Website** | [sechel.de](https://sechel.de/) | -| **LinkedIn** | [linkedin.com/in/sechel](https://www.linkedin.com/in/sechel/) | - -Die Dissertation steht unter Creative Commons Attribution ShareAlike 4.0 (CC BY-SA 4.0). +| **Getting started** — build modes, single-test invocation, CLI, Docker | [doc/getting-started.md](doc/getting-started.md) | +| **Pipeline API** — all three geometries, holonomy, serialisation | [doc/api/pipeline.md](doc/api/pipeline.md) | +| **Extending** — new functionals, geometry modes, porting from Java | [doc/api/extending.md](doc/api/extending.md) | +| **Processing unit contracts** — preconditions / provides table | [doc/api/contracts.md](doc/api/contracts.md) | +| **CGAL package design** — Phase 8 target, YAML pipeline | [doc/api/cgal-package.md](doc/api/cgal-package.md) | +| **Architecture & pipeline diagram** | [doc/architecture/overall_pipeline.md](doc/architecture/overall_pipeline.md) | +| **Geometry modes** — Euclidean / Spherical / HyperIdeal comparison | [doc/math/geometry-modes.md](doc/math/geometry-modes.md) | +| **References** — all papers by module | [doc/math/references.md](doc/math/references.md) | +| **Roadmap** — Phases 1–10 | [doc/roadmap/phases.md](doc/roadmap/phases.md) | +| **Java parity table** — what is ported, what is planned | [doc/roadmap/java-parity.md](doc/roadmap/java-parity.md) | +| **Contributing** — language policy, test standards, release flow | [doc/contributing.md](doc/contributing.md) | +| **Claude Code context** | [CLAUDE.md](CLAUDE.md) | --- -## Lizenz +## License -conformallab++ steht unter der MIT-Lizenz (siehe [LICENSE](LICENSE)). +conformallab++ is released under the MIT License (see [LICENSE](LICENSE)). +The dissertation (Sechelmann 2016) is CC BY-SA 4.0. diff --git a/doc/api/cgal-package.md b/doc/api/cgal-package.md new file mode 100644 index 0000000..b48c350 --- /dev/null +++ b/doc/api/cgal-package.md @@ -0,0 +1,163 @@ +# Phase 8 — CGAL Package Design + +> **Status: planned.** This document describes the target architecture for Phase 8. +> No code has been written yet. The design is informed by the CGAL package submission +> guidelines at https://www.cgal.org/developers.html + +--- + +## Goal + +Integrate conformallab++ into the CGAL library as a proper CGAL package: +`Discrete_conformal_map`. The package must satisfy all CGAL submission requirements: +traits-class design, Doxygen documentation, CGAL-format test suite, and coverage of +the CGAL coding conventions. + +--- + +## 8a — Traits class & concepts + +The current code is tightly coupled to `CGAL::Surface_mesh`. Phase 8a introduces +a traits class that separates the mesh type from the algorithm: + +```cpp +// TODO(Phase 8a): implement this header +// include/CGAL/Conformal_map_traits.h + +template< + typename MeshType, // any CGAL halfedge mesh + typename KernelType, // CGAL kernel + typename ScalarType = double +> +struct Conformal_map_traits { + using Mesh = MeshType; + using Kernel = KernelType; + using FT = ScalarType; + // ... vertex/edge/face descriptor types + // ... property map access +}; +``` + +Concept checks will ensure any user-provided mesh satisfies the halfedge mesh concept. + +--- + +## 8b — Public header hierarchy + +A clean public API separate from the internal implementation: + +``` +include/CGAL/ + Discrete_conformal_map.h ← single user-facing include + Conformal_map_traits.h + Conformal_newton_solver.h + Conformal_layout.h + Conformal_cut_graph.h + conformal_map_package.h ← PackageDescription +``` + +All existing `include/*.hpp` headers remain as internal implementation details, +not part of the public CGAL API. + +--- + +## 8c — CGAL-style documentation + +``` +doc/Conformal_map/ + PackageDescription.txt + User_manual.md + Reference_manual.md + fig/ ← pipeline diagrams, mathematical figures +``` + +All public functions and concepts require Doxygen comments following the CGAL style. + +--- + +## 8d — CGAL test format + +``` +test/Conformal_map/ + CMakeLists.txt ← CGAL-format, uses find_package(CGAL) + test_euclidean_functional.cpp + test_newton_solver.cpp + ... +``` + +The existing GTest suite remains. CGAL-format tests are added alongside as a separate +target, following the CGAL test infrastructure conventions. + +--- + +## 8e — Declarative YAML pipeline + +A lightweight YAML format for reproducible experiments. The CLI accepts +`--pipeline experiment.yml`; the validator checks `require`/`provide` tokens +before execution. + +Example: + +```yaml +pipeline: + name: flat_torus_period + geometry: euclidean + + input: + source: data/torus.off + + steps: + - id: setup + unit: setup_euclidean_maps + provide: [maps_initialised] + + - id: gauss_bonnet + unit: enforce_gauss_bonnet + require: [maps_initialised] + provide: [gauss_bonnet_satisfied] + + - id: solve + unit: newton_euclidean + require: [gauss_bonnet_satisfied] + params: + tol: 1.0e-10 + max_iter: 200 + provide: [x_converged] + + - id: cut + unit: compute_cut_graph + require: [mesh_closed] + provide: [cut_graph] + + - id: layout + unit: euclidean_layout + require: [x_converged, cut_graph] + params: + normalise: true + provide: [layout_uv, holonomy] + + - id: period + unit: compute_period_matrix + require: [holonomy] + provide: [tau] + + output: + layout: out/torus_layout.off + json: out/torus_result.json + tau: out/torus_tau.txt +``` + +The contract table in [contracts.md](contracts.md) defines the valid `require`/`provide` +token vocabulary. + +--- + +## TODO + +- [ ] Design `Conformal_map_traits.h` interface (8a) +- [ ] Define concept requirements for `MeshType` (8a) +- [ ] Create `include/CGAL/` header skeleton (8b) +- [ ] Write `PackageDescription.txt` (8c) +- [ ] Port GTest tests to CGAL format (8d) +- [ ] Implement YAML validator (8e) +- [ ] CLI: `--pipeline` flag (8e) diff --git a/doc/api/contracts.md b/doc/api/contracts.md new file mode 100644 index 0000000..e49383f --- /dev/null +++ b/doc/api/contracts.md @@ -0,0 +1,69 @@ +# Processing Unit Contracts + +Each pipeline unit has explicit preconditions and guarantees. +A pipeline is valid if every unit's preconditions are satisfied by the outputs +of all preceding units. + +--- + +## Contract table + +| Unit | Preconditions | Provides | +|---|---|---| +| `load_mesh()` | Valid file path, supported format (OFF/OBJ/PLY) | Manifold, oriented, triangulated `ConformalMesh` | +| `setup_*_maps()` | Triangulated mesh | Initialised property maps; `lambda0` zeroed | +| `compute_*_lambda0_from_mesh()` | `setup_*_maps()` called | `lambda0[e]` set from 3-D edge lengths | +| `check_gauss_bonnet()` | `theta_v[v]` set for all vertices | Throws `std::runtime_error` if Σ(2π−Θᵥ) ≠ 2π·χ(M) | +| `enforce_gauss_bonnet()` | `theta_v[v]` set | Σ(2π−Θᵥ) = 2π·χ(M) guaranteed; `theta_v` modified | +| `newton_euclidean()` | GB satisfied · DOFs assigned · `lambda0` initialised | `NewtonResult.x` — converged scale factors; `.converged`, `.iterations`, `.grad_inf_norm` | +| `newton_spherical()` | GB satisfied · DOFs assigned · gauge vertex pinned | Same as above | +| `newton_hyper_ideal()` | `assign_all_dof_indices()` called · `lambda0` initialised | Same as above | +| `compute_cut_graph()` | Closed, orientable, triangulated mesh | `CutGraph.cut_edge_flags` — 2g seam edges; `.genus` | +| `euclidean_layout()` | `newton_euclidean()` converged | `Layout2D.uv[v]` · `.halfedge_uv[h]` · `HolonomyData.translations` | +| `spherical_layout()` | `newton_spherical()` converged | `Layout3D.xyz[v]` | +| `hyper_ideal_layout()` | `newton_hyper_ideal()` converged | `Layout2D.uv[v]` · `.halfedge_uv[h]` · `HolonomyData.mobius_maps` | +| `normalise_euclidean()` | `layout.success == true` | Centroid at origin, major axis = x-axis | +| `normalise_hyperbolic()` | `layout.success == true` | Weighted centroid at Poincaré disk origin | +| `normalise_spherical()` | `layout.success == true` | Area centroid at north pole | +| `compute_period_matrix()` | `hol.translations.size() >= 2` | `PeriodData.tau ∈ ℍ`; optionally SL(2,ℤ)-reduced | +| `compute_fundamental_domain()` | `HolonomyData` (genus 1) | CCW parallelogram `{0, ω₁, ω₁+ω₂, ω₂}` + edge identifications | +| `tiling_neighbourhood()` | `Layout2D` + `HolonomyData` (genus 1) | `(2m+1)·(2n+1)` translated layout copies | +| `save_layout_off()` | `Layout2D` + mesh | OFF file with UV coordinates | +| `save_result_json/xml()` | `NewtonResult` + optional `Layout2D` | JSON/XML serialised result | + +--- + +## DOF assignment conventions + +```cpp +// Euclidean / Spherical: pin one vertex, assign sequential indices +maps.v_idx[first_vertex] = -1; // pinned: u_v = 0 +int idx = 0; +for (auto v : remaining_vertices) + maps.v_idx[v] = idx++; + +// HyperIdeal: all vertices and edges are free DOFs +int n_dofs = assign_all_dof_indices(mesh, maps); +// maps.v_idx[v] ∈ [0, n_v) +// maps.e_idx[e] ∈ [n_v, n_v + n_e) +``` + +`-1` in `v_idx` or `e_idx` means the DOF is pinned (fixed at its `lambda0` value). +The solver never writes to pinned DOFs. All indexing is 0-based and contiguous. + +--- + +## Gauss–Bonnet — the most common source of failure + +Prescribing angles that violate Gauss–Bonnet means no conformal factor can realise the +target metric — the Newton solver will iterate indefinitely without converging. + +```cpp +// Option A: verify before solving (throws on violation) +check_gauss_bonnet(mesh, maps); + +// Option B: auto-correct (redistributes defect uniformly across all vertices) +enforce_gauss_bonnet(mesh, maps); +``` + +The target violation is `|Σ(2π−Θᵥ) − 2π·χ(M)| > tol`. Default tolerance: `1e-10`. diff --git a/doc/api/extending.md b/doc/api/extending.md new file mode 100644 index 0000000..3f2009a --- /dev/null +++ b/doc/api/extending.md @@ -0,0 +1,141 @@ +# Extending conformallab++ + +## Adding a new functional + +All three existing functionals (`euclidean_functional.hpp`, `spherical_functional.hpp`, +`hyper_ideal_functional.hpp`) follow the same pattern. Copy the structure of the +simplest one (`euclidean_functional.hpp`) as a template. + +### Step 1 — Maps struct + +```cpp +// my_functional.hpp +struct MyMaps { + ConformalMesh::Property_map lambda0; // initial log-lengths + ConformalMesh::Property_map theta_v; // target angles + ConformalMesh::Property_map v_idx; // DOF index, -1 = pinned + // add edge DOFs if needed: + ConformalMesh::Property_map e_idx; +}; + +MyMaps setup_my_maps(ConformalMesh& mesh); +void compute_my_lambda0_from_mesh(ConformalMesh& mesh, MyMaps& maps); +``` + +### Step 2 — Gradient + +The gradient must satisfy: `G_v = Σ(angle contributions at v) − theta_v[v]`. + +```cpp +std::vector my_gradient( + const ConformalMesh& mesh, + const std::vector& x, + const MyMaps& maps) +{ + std::vector G(n_dofs, 0.0); + for (auto f : mesh.faces()) { + // compute angles in face f given current x + // accumulate into G[maps.v_idx[v]] for each vertex v of f + } + // subtract target angles + for (auto v : mesh.vertices()) + if (maps.v_idx[v] >= 0) + G[maps.v_idx[v]] -= maps.theta_v[v]; + return G; +} +``` + +### Step 3 — Gradient check test + +Before claiming correctness, verify with finite differences. Copy any `GradientCheck_*` +test suite from `tests/cgal/test_*_functional.cpp`: + +```cpp +TEST(MyFunctional, GradientCheck_Triangle) { + ConformalMesh mesh = make_single_triangle(); + MyMaps maps = setup_my_maps(mesh); + // ... assign DOFs, set natural theta ... + + const double eps = 1e-5; + auto G = my_gradient(mesh, x0, maps); + for (int i = 0; i < n; ++i) { + std::vector xp = x0, xm = x0; + xp[i] += eps; xm[i] -= eps; + double fd = (my_energy(mesh, xp, maps) - my_energy(mesh, xm, maps)) / (2*eps); + EXPECT_NEAR(G[i], fd, 1e-7) << "DOF " << i; + } +} +``` + +### Step 4 — Hessian and Newton wrapper + +```cpp +// Reuse solve_linear_system from newton_solver.hpp +Eigen::SparseMatrix H = my_hessian(mesh, x, maps); +bool used_fallback; +auto dx = conformallab::solve_linear_system(H, -G, &used_fallback); +``` + +Or write a thin `newton_my()` wrapper following the structure of `newton_euclidean()`. + +--- + +## Adding a new geometry mode + +To add a new space (e.g. de Sitter, flat 3-torus): + +1. **Trilateration function** — implement `trilaterate_my(p1, p2, l12, l13, l23)` that + places a third point given two placed points and three edge lengths. + This is the only geometry-specific part of the layout. + +2. **BFS structure** — reuse the priority-BFS loop from `euclidean_layout()` in `layout.hpp`. + The loop itself is geometry-agnostic; swap in your trilateration function. + +3. **Holonomy** — if the space has a holonomy group, record the transition maps at seam edges + the same way `HolonomyData` does for translations (Euclidean) or Möbius maps (hyperbolic). + +--- + +## Adding a new processing unit + +1. Declare preconditions and outputs explicitly (see [contracts.md](contracts.md)). + +2. Write a header-only implementation in `code/include/my_unit.hpp`. + +3. Add tests in `code/tests/cgal/test_my_unit.cpp` and register in + `code/tests/cgal/CMakeLists.txt`. + +4. Pipeline validation is currently manual (documented contracts). A compile-time or + runtime check via the declarative YAML pipeline (Phase 8e) is the planned upgrade — + see [cgal-package.md](cgal-package.md). + +--- + +## Porting from Java + +When porting a class from the Java library: + +1. Locate the original at [github.com/varylab/conformallab](https://github.com/varylab/conformallab) + under `src/main/java/de/varylab/discreteconformal/`. + +2. The Java library uses `CoHDS` (half-edge data structure) with intrusive vertex/edge data. + Map these to CGAL property maps: + ``` + Java: vertex.getLambda() → C++: maps.lambda0[v] + Java: edge.getAlpha() → C++: maps.e_alpha[e] + Java: vertex.getTheta() → C++: maps.theta_v[v] + Java: vertex.getSolverIndex() → C++: maps.v_idx[v] + ``` + +3. Java uses `HalfedgeInterface` adapters with named accessors like `getOppositeVertex()`. + C++ equivalent: + ```cpp + // Java: h.getOppositeVertex() + Vertex_index v_opp = mesh.target(mesh.next(h)); + + // Java: h.getNextHalfedge().getOppositeVertex() + Vertex_index v_opp2 = mesh.target(mesh.next(mesh.next(h))); + ``` + +4. Port the test cases from the Java `@Test` methods. The "natural theta" trick + (`theta_v[v] = actual_angle_sum`) works the same way in both languages. diff --git a/doc/api/pipeline.md b/doc/api/pipeline.md new file mode 100644 index 0000000..4899384 --- /dev/null +++ b/doc/api/pipeline.md @@ -0,0 +1,214 @@ +# Pipeline API Reference + +The full pipeline from mesh loading to layout and serialisation. +See [doc/architecture/overall_pipeline.md](../architecture/overall_pipeline.md) for the +Mermaid diagram and stage descriptions. + +--- + +## Minimal Euclidean pipeline + +```cpp +#include "conformal_mesh.hpp" +#include "mesh_io.hpp" +#include "euclidean_functional.hpp" +#include "gauss_bonnet.hpp" +#include "newton_solver.hpp" +#include "layout.hpp" + +using namespace conformallab; + +ConformalMesh mesh = load_mesh("input.off"); + +// 1. Set up property maps and initialise log-edge-lengths from 3-D positions +EuclideanMaps maps = setup_euclidean_maps(mesh); +compute_euclidean_lambda0_from_mesh(mesh, maps); + +// 2. Assign DOF indices — pin first vertex (gauge fix for open meshes) +auto vit = mesh.vertices().begin(); +maps.v_idx[*vit++] = -1; // pinned: u_v = 0 +int idx = 0; +for (; vit != mesh.vertices().end(); ++vit) + maps.v_idx[*vit] = idx++; + +// 3. Set target angles — "natural equilibrium" makes x* = 0 +std::vector x0(idx, 0.0); +auto G0 = euclidean_gradient(mesh, x0, maps); +for (auto v : mesh.vertices()) + if (maps.v_idx[v] >= 0) + maps.theta_v[v] -= G0[maps.v_idx[v]]; + +// 4. Check Gauss–Bonnet (mandatory before solving) +check_gauss_bonnet(mesh, maps); // throws if Σ(2π−Θᵥ) ≠ 2π·χ(M) + +// 5. Solve +NewtonResult res = newton_euclidean(mesh, x0, maps); +// res.converged, res.x, res.iterations, res.grad_inf_norm + +// 6. Layout +Layout2D layout = euclidean_layout(mesh, res.x, maps); +// layout.uv[v.idx()], layout.halfedge_uv[h.idx()] +``` + +--- + +## Layout with holonomy (closed surfaces) + +```cpp +#include "cut_graph.hpp" +#include "period_matrix.hpp" +#include "fundamental_domain.hpp" + +// Tree-cotree cut graph — 2g seam edges +CutGraph cg = compute_cut_graph(mesh); +// cg.cut_edge_flags[e.idx()] — true if seam edge +// cg.genus — topological genus g + +// Layout with holonomy tracking +HolonomyData hol; +Layout2D layout = euclidean_layout(mesh, res.x, maps, &cg, &hol, /*normalise=*/true); +// hol.translations[i] — lattice generator ωᵢ ∈ ℂ (Euclidean) + +// Period matrix (genus 1 flat torus) +PeriodData pd = compute_period_matrix(hol); +// pd.tau — complex period ratio τ = ω₂/ω₁ ∈ ℍ +// pd.omega — {ω₁, ω₂} as complex +// pd.in_fundamental_domain — true if SL(2,ℤ)-reduced to |τ|≥1, |Re(τ)|<½ + +// Fundamental domain parallelogram +FundamentalDomain fd = compute_fundamental_domain(hol); +// fd.vertices[0..3] — CCW corners: {0, ω₁, ω₁+ω₂, ω₂} +// fd.generators[0..1] — ω₁, ω₂ + +// Tiling copies for universal cover visualisation +auto tiles = tiling_neighbourhood(layout, hol, /*m_max=*/2, /*n_max=*/2); +// returns (2·m_max+1)·(2·n_max+1) translated layout copies +``` + +--- + +## HyperIdeal pipeline + +```cpp +#include "hyper_ideal_functional.hpp" +#include "newton_solver.hpp" +#include "layout.hpp" +#include "cut_graph.hpp" + +HyperIdealMaps maps = setup_hyper_ideal_maps(mesh); +compute_hyper_ideal_lambda0_from_mesh(mesh, maps); + +// HyperIdeal has both vertex and edge DOFs — assign all automatically +int n_dofs = assign_all_dof_indices(mesh, maps); +// No vertex needs to be pinned — strictly convex functional + +std::vector x0(n_dofs, 0.0); +NewtonResult res = newton_hyper_ideal(mesh, x0, maps); + +// Layout with Möbius holonomy +CutGraph cg = compute_cut_graph(mesh); +HolonomyData hol; +Layout2D layout = hyper_ideal_layout(mesh, res.x, maps, &cg, &hol); +// hol.mobius_maps[i] — Möbius isometry Tᵢ ∈ SU(1,1) per cut edge +``` + +--- + +## Spherical pipeline + +```cpp +#include "spherical_functional.hpp" +#include "newton_solver.hpp" +#include "layout.hpp" + +SphericalMaps maps = setup_spherical_maps(mesh); +compute_spherical_lambda0_from_mesh(mesh, maps); + +// Pin one vertex (gauge fix) + assign DOFs +auto vit = mesh.vertices().begin(); +maps.v_idx[*vit++] = -1; +int idx = 0; +for (; vit != mesh.vertices().end(); ++vit) + maps.v_idx[*vit] = idx++; + +std::vector x0(idx, 0.0); +NewtonResult res = newton_spherical(mesh, x0, maps); + +// 3-D layout on the unit sphere +Layout3D slayout = spherical_layout(mesh, res.x, maps); +// slayout.xyz[v.idx()] — 3-D position on S² +``` + +--- + +## SparseQR fallback — direct use + +```cpp +#include "newton_solver.hpp" + +bool used_fallback = false; +Eigen::VectorXd dx = conformallab::solve_linear_system(H, rhs, &used_fallback); +if (used_fallback) + std::cerr << "Warning: H is rank-deficient, used SparseQR\n"; +``` + +The fallback is automatically invoked by all three `newton_*` functions when +`SimplicialLDLT` fails. It finds the minimum-norm Newton step orthogonal to the null space. + +--- + +## Serialisation + +```cpp +#include "serialization.hpp" +#include "mesh_io.hpp" + +// Save layout as OFF mesh file +save_layout_off("layout.off", mesh, layout); + +// Full result: DOF vector + metadata + layout UVs +save_result_json("result.json", res, "euclidean", mesh, &layout); +save_result_xml ("result.xml", res, "euclidean", mesh, &layout); + +// Round-trip load +NewtonResult res2; +std::string geom; +Layout2D uv2; +load_result_json("result.json", &res2, &geom, &uv2); +``` + +--- + +## Attaching custom property maps + +```cpp +// Add a custom per-vertex map +auto [curv, created] = mesh.add_property_map("v:my_curv", 0.0); +curv[v] = 3.14; + +// Retrieve an existing map +auto [curv2, found] = mesh.property_map("v:my_curv"); +``` + +--- + +## Halfedge traversal conventions + +```cpp +for (auto f : mesh.faces()) { + auto h0 = mesh.halfedge(f); // canonical halfedge of face f + auto h1 = mesh.next(h0); + auto h2 = mesh.next(h1); + + Vertex_index v1 = mesh.source(h0); // = mesh.target(h2) + Vertex_index v2 = mesh.source(h1); + Vertex_index v3 = mesh.source(h2); + + // Angle at v3 is opposite to halfedge h0 (edge v1–v2) + // h_alpha[h0] = α₃, h_alpha[h1] = α₁, h_alpha[h2] = α₂ + + Edge_index e0 = mesh.edge(h0); + bool is_seam = cg.cut_edge_flags[e0.idx()]; + bool is_boundary = mesh.is_border(mesh.opposite(h0)); +} +``` diff --git a/doc/architecture/overall_pipeline.md b/doc/architecture/overall_pipeline.md index 1213226..29c906d 100644 --- a/doc/architecture/overall_pipeline.md +++ b/doc/architecture/overall_pipeline.md @@ -388,177 +388,14 @@ Normalise PCA centring Rodrigues to N pole weighted Möbius centrin --- -## Extension points +## Further documentation -### Adding a new functional - -1. Create `my_functional.hpp` with a `Maps` struct and `evaluate_my_functional()`. -2. The gradient must satisfy: `G_v = Σ(angle contributions) − theta_v[v]`. -3. Verify with a finite-difference gradient check (copy any `GradientCheck_*` test). -4. Pass to `solve_linear_system(H, -G)` or write a thin `newton_my` wrapper. - -### Adding a new geometry mode - -Implement `trilaterate_my()` with the correct local placement formula, -then follow the same BFS structure as `euclidean_layout` (see `layout.hpp`). -The priority BFS, holonomy tracking, and `halfedge_uv` population are geometry-agnostic. - -### Adding a new processing unit - -Declare its preconditions and capabilities explicitly (see table above). -The pipeline validation is currently manual (documented contracts); a -compile-time or runtime check is a natural Phase 8 extension. - ---- - -## Declarative pipeline (target for Phase 8) - -A lightweight YAML description for reproducible experiments: - -```yaml -pipeline: - name: flat_torus_period - geometry: euclidean - - input: - source: data/torus.off - - steps: - - id: setup - unit: setup_euclidean_maps - provide: [maps_initialised] - - - id: gauss_bonnet - unit: enforce_gauss_bonnet - require: [maps_initialised] - provide: [gauss_bonnet_satisfied] - - - id: solve - unit: newton_euclidean - require: [gauss_bonnet_satisfied] - params: - tol: 1.0e-10 - max_iter: 200 - provide: [x_converged] - - - id: cut - unit: compute_cut_graph - require: [mesh_closed] - provide: [cut_graph] - - - id: layout - unit: euclidean_layout - require: [x_converged, cut_graph] - params: - normalise: true - provide: [layout_uv, holonomy] - - - id: period - unit: compute_period_matrix - require: [holonomy] - provide: [tau] - - output: - layout: out/torus_layout.off - json: out/torus_result.json - tau: out/torus_tau.txt -``` - -Each unit declares `require` (preconditions) and `provide` (capabilities). -A pipeline is valid if for every step, all `require` keys are satisfied by the -accumulated `provide` set of all preceding steps. -This mirrors exactly the contract table in this document. - ---- - -## Development Roadmap - -> **Grenze Portierung / neue Forschung:** -> Phase 1–7 sind direkte Portierungen aus dem Java-Original (Sechelmann 2016). -> Ab Phase 8 geht die Arbeit über den Umfang der Java-Bibliothek hinaus. -> — Phase 8 (CGAL-Paket) ist **Infrastruktur**, kein neuer Algorithmus. -> — Phase 9 (Inversive-Distance, Analytischer Hessian, 4g-Polygon) ist **Portierung** ausstehender Java-Features. -> — Phase 10+ ist **eigenständige Forschung**, die über das Java-Original hinausgeht. - ---- - -### ◼ Portierungsphase abgeschlossen — Phase 1–7 - -``` -Phase 1 Clausen / Lobachevsky / ImLi₂ ✅ -Phase 2 Hyper-ideal Geometrie (ζ, lᵢⱼ, αᵢⱼ, σᵢ) ✅ -Phase 3 CGAL-Infrastruktur + alle drei Funktionale (E/S/H) ✅ -Phase 4 Newton-Solver (SimplicialLDLT + SparseQR-Fallback) ✅ 68 Tests -Phase 5 Priority-BFS-Layout + CLI + JSON/XML ✅ 95 Tests -Phase 6 Gauss–Bonnet, Tree-Cotree-Schnittgraph, Normalisierung ✅ 121 Tests -Phase 7 MobiusMap, halfedge_uv, Möbius-Holonomie, Periodenmatrix, - Fundamentalbereich (Genus 1), Java-Parität abgeschlossen ✅ 158 Tests -``` - ---- - -### ◼ Infrastruktur (über Java-Bibliothek hinaus) — Phase 8: CGAL-Paket - -``` -8a Traits-Klasse & Konzepte → include/CGAL/Conformal_map_traits.h -8b Öffentliche CGAL-Header-Hierarchie → include/CGAL/Discrete_conformal_map.h etc. -8c Dokumentation im CGAL-Stil → doc/Conformal_map/PackageDescription.txt -8d CGAL-Testformat → test/Conformal_map/ -8e Declarative YAML-Pipeline → pipeline validator (require/provide tokens) -``` - -See the [Declarative pipeline](#declarative-pipeline-target-for-phase-8) section above -for the YAML schema that Phase 8e will validate at runtime. - ---- - -### ◼ Ausstehende Portierung (Java-Features noch nicht übertragen) — Phase 9 - -``` -9a Inversive-Distance-Funktional (Luo 2004) - → InversiveDistanceMaps + functional + Hessian - → discrete uniformization via inversive distances -9b Analytischer HyperIdeal-Hessian (ζ-Kette) - → replace FD Hessian in hyper_ideal_hessian.hpp - → reduces Newton iterations for large meshes -9c 4g-Polygon-Randlauf (Genus g > 1) - → extend compute_fundamental_domain() beyond genus 1 - → algorithm outline already in fundamental_domain.hpp as TODO(Phase 9) -``` - ---- - -### ◼ Neue Forschung (über das Java-Original hinaus) — Phase 10+ - -``` -Phase 10 Globale Uniformisierung Genus g ≥ 2 -10a Holomorphe Differentiale auf diskreten Flächen - → discrete harmonic 1-forms; integration along cut graph cycles -10b Siegel-Periodenmatrix Ω ∈ H_g (g×g komplex-symmetrisch, Im(Ω) > 0) - → extend compute_period_matrix() to genus g ≥ 2 - → requires holomorphic differentials from 10a -10c Vollständige Uniformisierung - → uniformize arbitrary genus-g surface to canonical constant-curvature metric - → depends on 10a + 10b -``` - ---- - -## Recommended reading - -### Primary source — the dissertation this library implements - -| | | +| Topic | Document | |---|---| -| **Sechelmann** — *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, TU Berlin 2016 | The mathematical foundation of the entire library: discrete conformal equivalence, variational angle-sum functionals, Newton solver, uniformization, period matrices. DOI: [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) | -| **Java original** — [github.com/varylab/conformallab](https://github.com/varylab/conformallab) | Reference implementation in Java — the direct source for all algorithms ported to C++ | - -### Further references - -| Source | Relevance in conformallab++ | -|--------|----------------------------| -| Springborn — *Ideal Hyperbolic Polyhedra and Discrete Uniformization* (2020) | HyperIdeal functional; ζ₁₃/ζ₁₄/ζ₁₅ in `hyper_ideal_geometry.hpp` | -| Pinkall, Polthier — *Computing Discrete Minimal Surfaces* (1993) | Cotangent-Laplace Hessian in `euclidean_hessian.hpp` | -| Bobenko, Springborn — *Variational Principles for Circle Patterns* (2004) | Angle-sum variational framework used throughout | -| Luo — *Combinatorial Yamabe Flow on Surfaces* (2004) | Inversive-distance functional (not yet ported) | -| Erickson, Whittlesey — *Greedy Optimal Homotopy Generators* (SODA 2005) | Tree-cotree algorithm in `cut_graph.hpp` | +| Extending the library (new functionals, geometry modes, Java porting guide) | [api/extending.md](../api/extending.md) | +| Processing unit contracts (preconditions / provides table) | [api/contracts.md](../api/contracts.md) | +| Phase 8 CGAL package design + declarative pipeline YAML | [api/cgal-package.md](../api/cgal-package.md) | +| Three geometry modes in detail | [math/geometry-modes.md](../math/geometry-modes.md) | +| References and papers | [math/references.md](../math/references.md) | +| Development roadmap (Phases 1–10) | [roadmap/phases.md](../roadmap/phases.md) | +| Java vs. C++ feature parity table | [roadmap/java-parity.md](../roadmap/java-parity.md) | diff --git a/doc/contributing.md b/doc/contributing.md new file mode 100644 index 0000000..e27e469 --- /dev/null +++ b/doc/contributing.md @@ -0,0 +1,88 @@ +# Contributing + +## Language + +**All code, comments, documentation, commit messages, and test descriptions must be in English.** + +The project targets CGAL submission and international collaboration. When editing +files that still contain German-language comments or documentation, replace them +with English. + +--- + +## Git workflow + +- `main` is protected on `origin` (Gitea). Push to `dev`, then open a pull request. +- `codeberg/main` can be pushed to directly (public mirror, no CI). +- Both remotes must stay in sync after every significant change: + ```bash + git push origin HEAD:dev # triggers CI + git push codeberg main # updates public mirror + ``` +- Branch naming: `feature/`, `fix/`, `phase-` + +--- + +## CI + +Two jobs run on push to `dev`/`main` or on pull requests: + +| Job | What it tests | Trigger | +|---|---|---| +| `test-fast` | 36 non-CGAL tests, no Boost | all branches | +| `test-cgal` | 158 CGAL tests + 2 skips | `main`, `dev`, PRs only | + +A PR is ready to merge when both jobs pass. + +The runner is a self-hosted Raspberry Pi (ARM64). See `.gitea/workflows/cpp-tests.yml` +and `.gitea/docker/Dockerfile.ci-cpp`. + +--- + +## Test standards + +Every new algorithm needs: + +1. **Gradient check** — finite-difference verification of `G(x) = ∂E/∂x`. + Copy any `GradientCheck_*` test suite from `tests/cgal/test_*_functional.cpp`. + +2. **Convergence test** — Newton converges on a small mesh using the "natural theta" + trick (see [CLAUDE.md](../CLAUDE.md) and [api/extending.md](api/extending.md)). + +3. **Registration** — add the `.cpp` file to `code/tests/cgal/CMakeLists.txt`. + +Expected CI result: **36 + 158 tests pass, exactly 2 skipped**. +The 2 skips are intentional `GTEST_SKIP()` stubs for Phase 9b (analytic HyperIdeal Hessian) +and Phase 9c (genus-2 homology). Do not remove them. + +--- + +## Code style + +- C++17. Header-only (`code/include/*.hpp`). No compiled library. +- `#pragma once` at the top of every header. +- Everything in the `conformallab` namespace, internal helpers in `conformallab::detail`. +- Property map names follow the prefix convention: `"v:"` (vertex), `"e:"` (edge), `"f:"` (face). +- DOF index `-1` always means "pinned/fixed". + +--- + +## Releases + +Release tags follow `vMAJOR.MINOR.PATCH`: + +```bash +# Merge dev → main, then tag +git checkout main && git merge --no-ff dev +git tag -a vX.Y.Z -m "vX.Y.Z — " +git push origin main && git push origin vX.Y.Z +git push codeberg main && git push codeberg vX.Y.Z +``` + +--- + +## TODO + +- [ ] Define code review checklist +- [ ] Add `.clang-format` configuration +- [ ] Document how to request CGAL package review diff --git a/doc/getting-started.md b/doc/getting-started.md new file mode 100644 index 0000000..a86372e --- /dev/null +++ b/doc/getting-started.md @@ -0,0 +1,142 @@ +# Getting Started + +## Prerequisites + +| Tool | Minimum | Notes | +|---|---|---| +| C++ compiler (GCC or Clang) | C++17 | GCC 11+ or Clang 14+ recommended | +| CMake | 3.20 | | +| Boost headers | 1.70 | Only for `-DWITH_CGAL_TESTS=ON` or `-DWITH_CGAL=ON` | +| Wayland/X11 dev headers | — | Only for `-DWITH_CGAL=ON` (viewer) | + +All other dependencies (Eigen 3.4, CGAL 6.1.1, libigl 2.6, GLFW 3.4, GTest 1.14) +are bundled as tarballs in `code/deps/tarballs/` and extracted automatically at CMake +configure time. No internet access is required at build time (except GTest, fetched via +`FetchContent` from GitHub). + +Install Boost on your system: +```bash +# Ubuntu / Debian +apt install libboost-dev + +# macOS +brew install boost +``` + +--- + +## Clone + +```bash +git clone https://codeberg.org/TMoussa/ConformalLabpp +cd ConformalLabpp +``` + +--- + +## Build modes + +Three modes with increasing dependencies: + +### Mode 1 — Fast tests (default, no system dependencies) + +Pure-math tests: Clausen functions, hyper-ideal geometry, matrix utilities. + +```bash +cmake -S code -B build +cmake --build build --target conformallab_tests -j$(nproc) +ctest --test-dir build --output-on-failure +``` + +Expected: **36 tests pass**. + +### Mode 2 — CGAL tests, headless (recommended for CI and development) + +Full CGAL test suite. Requires Boost headers only — no display, no Wayland, no GLFW. + +```bash +cmake -S code -B build -DWITH_CGAL_TESTS=ON +cmake --build build --target conformallab_cgal_tests -j$(nproc) +ctest --test-dir build -R "^cgal\." --output-on-failure +``` + +Expected: **158 tests pass, 2 skipped** (intentional stubs for Phase 9 items). + +### Mode 3 — Full local build (CLI app + interactive viewer) + +Requires Wayland or X11 development packages (`wayland-scanner`, `libx11-dev`, etc.). +Automatically enables the viewer library (GLFW + libigl). + +```bash +cmake -S code -B build -DWITH_CGAL=ON +cmake --build build -j$(nproc) +``` + +> **Note:** `-DWITH_CGAL=ON` implies `-DWITH_VIEWER=ON`. Do not use this in headless +> environments — it will fail with `Failed to find wayland-scanner`. + +--- + +## Running a single test + +```bash +# By GTest filter (fastest, full output) +./build/conformallab_cgal_tests --gtest_filter="NewtonSolver*" +./build/conformallab_tests --gtest_filter="Clausen*" + +# By CTest regex +ctest --test-dir build -R "cgal.NewtonSolver" --output-on-failure +``` + +All CGAL tests have the prefix `cgal.` in CTest (set in `tests/cgal/CMakeLists.txt` +via `TEST_PREFIX "cgal."`). + +--- + +## First run — CLI app + +After a full build (`-DWITH_CGAL=ON`): + +```bash +# Euclidean conformal layout +./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json + +# Spherical layout +./bin/conformallab_core -i input.off -g spherical -o sphere.off + +# Hyperbolic layout (HyperIdeal) +./bin/conformallab_core -i input.off -g hyper_ideal -o hyperbolic.off + +# Show input mesh in interactive viewer +./bin/conformallab_core -i input.off -s + +# All options +./bin/conformallab_core --help +``` + +## Example programs + +```bash +./build/examples/example_layout [input.off] [layout.off] [result.json] +./build/examples/example_euclidean [input.off] [output.off] +./build/examples/example_hyper_ideal [input.off] [output.off] +./build/examples/example_viewer [input.off] # interactive, requires WITH_VIEWER +``` + +`example_layout.cpp` is the best starting point — it shows the complete pipeline in ~120 lines. + +--- + +## Rebuilding the CI Docker image + +The CI runner is a self-hosted Raspberry Pi (ARM64). After changes to +`.gitea/docker/Dockerfile.ci-cpp`: + +```bash +docker buildx build \ + --platform linux/arm64 \ + -f .gitea/docker/Dockerfile.ci-cpp \ + -t git.eulernest.eu/conformallab/ci-cpp:latest \ + --push \ + .gitea/docker/ +``` diff --git a/doc/math/geometry-modes.md b/doc/math/geometry-modes.md new file mode 100644 index 0000000..baa67f6 --- /dev/null +++ b/doc/math/geometry-modes.md @@ -0,0 +1,112 @@ +# The Three Geometry Modes + +conformallab++ implements discrete conformal geometry in three model spaces. +All three share the same algorithmic structure — only the angle formula, +the trilateration geometry, and the Hessian sign differ. + +--- + +## Comparison + +``` + Euclidean Spherical Hyper-ideal + ───────────────────────────────────────────────────── +Space ℝ² S² H² (Poincaré disk) +Curvature K = 0 K = +1 K = −1 +Typical genus any (cone metrics) 0 ≥ 1 +Angle sum Σαᵥ = Θᵥ Σαᵥ = Θᵥ Σβᵥ = Θᵥ +Gradient sign G_v = actual − Θᵥ G_v = Θᵥ − actual G_v = actual − Θᵥ +Hessian PSD (cotangent-Lap.) NSD (sign-flip) PSD (FD, strict conv.) +Newton solve SimplicialLDLT(H) SimplicialLDLT(−H) SimplicialLDLT(H) +Holonomy translations ωᵢ rotations (2-D) Möbius maps Tᵢ ∈ SU(1,1) +Period τ = ω₂/ω₁ ∈ ℍ — axis of Tᵢ +Normalise PCA centring Rodrigues to N pole weighted Möbius centring +Layout Layout2D (ℝ²) Layout3D (unit sphere) Layout2D (Poincaré disk) +``` + +--- + +## Euclidean (ℝ²) + +**Energy:** the cotangent-weighted angle defect energy (Pinkall & Polthier 1993). + +**Effective log-length:** +``` +Λ̃ᵢⱼ = λ°ᵢⱼ + uᵢ + uⱼ +``` + +**Gradient:** `G_v = Σ_{faces adj. v} α_v(face) − Θᵥ` — the actual angle sum minus the target. + +**Hessian:** cotangent Laplacian, assembled in `euclidean_hessian.hpp`. PSD with one zero +eigenvalue (constant function) for closed meshes — handled by pinning one vertex or SparseQR. + +**Normalisation:** centroid → origin, major axis → x-axis via PCA (`normalise_euclidean`). + +**Holonomy:** lattice translations ω₁, ω₂ ∈ ℂ for closed surfaces. +Period ratio τ = ω₂/ω₁ ∈ ℍ is the conformal modulus of the torus. + +--- + +## Spherical (S²) + +**Typical use:** genus-0 (sphere-like) surfaces. + +**Gradient:** `G_v = Θᵥ − Σ α_v(face)` — note the **sign reversal** vs. Euclidean. + +**Hessian:** NSD (the spherical energy is concave). Newton solves `(−H)·Δx = G`, i.e. +`SimplicialLDLT` is called on `−H`. This is handled transparently in `newton_spherical()`. + +**Gauge fix:** for a closed spherical surface, one additional DOF must be pinned to remove +the rotational gauge mode. `SphericalMaps` has a dedicated `gauge_vertex` field. + +**Normalisation:** `normalise_spherical()` rotates the layout so the area centroid +maps to the north pole (Rodrigues rotation formula). + +--- + +## Hyper-ideal (H²) + +**Typical use:** genus-g surfaces (g ≥ 1), hyperbolic cone metrics. + +**DOFs:** both vertex variables `bᵢ` (hyper-ideal radius) and edge variables `aₑ` +(intersection angle). `assign_all_dof_indices(mesh, maps)` assigns both automatically. +No vertex needs to be pinned — the functional is strictly convex. + +**Geometry functions** (in `hyper_ideal_geometry.hpp`): +``` +ζ₁₃(b, a) ζ₁₄(b, a) ζ₁₅(b, a) — the three fundamental Springborn functions +lᵢⱼ(ζ) — edge length from ζ value +αᵢⱼ(l₁, l₂, l₃) — interior angle +βᵢ(l₁, l₂, l₃) — vertex angle sum contribution +``` + +**Hessian:** currently a symmetric finite-difference approximation (see `hyper_ideal_hessian.hpp`). +The analytic Hessian through the `(bᵢ, aₑ) → lᵢⱼ → ζ → αᵢⱼ/βᵢ` chain is Phase 9b. + +**Holonomy:** Möbius isometries Tᵢ ∈ SU(1,1) (orientation-preserving isometries of the Poincaré disk). +`MobiusMap` in `layout.hpp`: T(z) = (az+b)/(cz+d). + +**Normalisation:** `normalise_hyperbolic()` performs iterative face-area-weighted Möbius centring +(Fréchet mean, 30 iterations) to map the weighted centroid to the disk origin. + +--- + +## Gauss–Bonnet constraint + +Before calling any Newton solver, the target angles must satisfy the Gauss–Bonnet equation: + +``` +Σᵥ (2π − Θᵥ) = 2π · χ(M) (Euclidean / flat) +Σᵥ (2π − Θᵥ) > 0 (spherical, χ > 0) +Σᵥ (2π − Θᵥ) < 0 (hyperbolic, χ < 0) +``` + +If this is violated, no conformal factor can realise the target angles and Newton will +fail to converge without a visible error. + +```cpp +check_gauss_bonnet(mesh, maps); // throws if violated +enforce_gauss_bonnet(mesh, maps); // redistributes residual uniformly across all vertices +``` + +**This is the most common source of silent non-convergence.** Always call one of these before solving. diff --git a/doc/math/references.md b/doc/math/references.md new file mode 100644 index 0000000..0ec3064 --- /dev/null +++ b/doc/math/references.md @@ -0,0 +1,35 @@ +# References + +## Primary source + +This library implements the algorithms from: + +| | | +|---|---| +| **Sechelmann** — *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, Doctoral thesis, TU Berlin 2016 | The complete mathematical foundation: discrete conformal equivalence, variational angle-sum functionals, Newton solver, uniformization, period matrices, holonomy. DOI: [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) · CC BY-SA 4.0 | + +Java reference implementation: [github.com/varylab/conformallab](https://github.com/varylab/conformallab) + +--- + +## References by module + +| Reference | Used in | +|---|---| +| **Springborn** — *Ideal Hyperbolic Polyhedra and Discrete Uniformization*, Discrete & Computational Geometry (2020) | `hyper_ideal_geometry.hpp` — ζ₁₃/ζ₁₄/ζ₁₅ functions; `hyper_ideal_functional.hpp` | +| **Pinkall, Polthier** — *Computing Discrete Minimal Surfaces and Their Conjugates*, Experimental Mathematics (1993) | `euclidean_hessian.hpp` — cotangent Laplacian | +| **Bobenko, Springborn** — *Variational Principles for Circle Patterns and Koebe's Theorem*, Transactions AMS (2004) | Variational angle-sum framework underlying all three functionals | +| **Luo** — *Combinatorial Yamabe Flow on Surfaces*, Communications in Contemporary Mathematics (2004) | Inversive-distance functional — **not yet ported**, Phase 9a | +| **Erickson, Whittlesey** — *Greedy Optimal Homotopy and Homology Generators*, SODA (2005) | `cut_graph.hpp` — tree-cotree algorithm | +| **Bobenko, Springborn** — *A Discrete Laplace–Beltrami Operator for Simplicial Surfaces*, Discrete & Computational Geometry (2007) | Background for cotangent weights | +| **Desbrun, Kanso, Tong** — *Discrete Differential Forms for Computational Modeling*, SIGGRAPH Course Notes (2006) | Discrete exterior calculus background for Phase 10a | + +--- + +## Phase 10 references (future research) + +| Reference | Relevant for | +|---|---| +| **Farkas, Kra** — *Riemann Surfaces*, Springer GTM 71 | Siegel period matrix, Teichmüller theory | +| **Siegel** — *Topics in Complex Function Theory, Vol. 2*, Wiley | Siegel upper half-space H_g, Sp(2g,ℤ) reduction | +| **Bobenko, Mercat, Schmies** — *Period Matrices of Polyhedral Surfaces*, in: Computational Approach to Riemann Surfaces (2011) | Discrete period matrices on polyhedral surfaces | diff --git a/doc/roadmap/java-parity.md b/doc/roadmap/java-parity.md new file mode 100644 index 0000000..3ba9d54 --- /dev/null +++ b/doc/roadmap/java-parity.md @@ -0,0 +1,86 @@ +# Java ConformalLab vs. conformallab++ — Feature Parity + +Reference: [github.com/varylab/conformallab](https://github.com/varylab/conformallab) +Java package root: `de.varylab.discreteconformal` + +When porting a Java class, locate the original in the Java repository and use it +as the reference implementation for expected behaviour, edge cases, and test cases. + +--- + +## Algorithm parity + +| Mathematical layer | Java ConformalLab | conformallab++ | Notes | +|---|---|---|---| +| Euclidean functional — energy, gradient | ✅ | ✅ | | +| Spherical functional — energy, gradient, gauge-fix | ✅ | ✅ | | +| HyperIdeal functional — energy, gradient | ✅ | ✅ | | +| Inversive-distance functional (Luo 2004) | ✅ | ❌ Phase 9a | `InversiveDistanceFunctional.java` | +| Euclidean Hessian — cotangent Laplacian | ✅ analytic | ✅ analytic | Pinkall–Polthier (1993) | +| Spherical Hessian — ∂α/∂u via law of cosines | ✅ analytic | ✅ analytic | | +| HyperIdeal Hessian — ζ → lᵢⱼ → β/α chain | ✅ analytic | ⚠️ symmetric FD | Phase 9b | +| Newton solver | ✅ | ✅ | | +| SparseQR fallback for gauge modes | unknown | ✅ | New in C++ | +| Cone metrics — prescribed Θᵥ ≠ 2π | ✅ fully | ⚠️ data structure only | | +| Layout / embedding — ℝ² / H² / S² | ✅ | ✅ priority-BFS all three | | +| Exact hyperbolic trilateration | ✅ Möbius | ✅ Möbius + law of cosines | | +| halfedge_uv — seam-aware UV (texture atlas) | ✅ | ✅ | | +| Gauss–Bonnet consistency check | ✅ | ✅ | | +| Tree-cotree cut graph (2g edges) | ✅ | ✅ Erickson–Whittlesey (2005) | | +| Holonomy — Euclidean (translations) | ✅ | ✅ | | +| Holonomy — Hyperbolic (SU(1,1) Möbius maps) | ✅ | ✅ | | +| Period matrix τ — genus 1, SL(2,ℤ)-reduced | ✅ | ✅ | | +| Fundamental domain — genus 1 | ✅ | ✅ CCW parallelogram | | +| 4g-polygon boundary walk — genus g > 1 | ✅ | ❌ Phase 9c | `FundamentalDomainUtility.java` | +| Siegel period matrix Ω — genus g ≥ 2 | ✅ | ❌ Phase 10b | | +| Global uniformization — genus g ≥ 2 | ✅ | ❌ Phase 10c | | +| Clausen / Lobachevsky / ImLi₂ | ✅ | ✅ | | +| Poincaré disk / Lorentz boost visualisation | ✅ | ✅ | | +| Mesh I/O + serialisation | ✅ XML/CoHDS | ✅ OFF/OBJ/PLY + JSON/XML | | +| Interactive viewer | ✅ jReality | ✅ libigl/GLFW | | + +--- + +## Java utility classes not yet ported + +These exist in `de.varylab.discreteconformal.util` in the Java library. +They are candidates for Phase 9 or Phase 10. + +| Java class | Description | Phase | +|---|---|---| +| `InversiveDistanceFunctional` | Inversive-distance conformal energy | 9a | +| `DiscreteHarmonicFormUtility` | Discrete harmonic 1-forms | 10a prerequisite | +| `DiscreteHolomorphicFormUtility` | Holomorphic differentials on discrete surfaces | 10a | +| `DiscreteRiemannUtility` | Discrete Riemann surfaces | 10 | +| `CanonicalBasisUtility` | Canonical homology basis for genus g | 9c / 10 | +| `HomologyUtility` | Homology computation | 9c | +| `HomotopyUtility` | Homotopy generators | 9c | +| `SpanningTreeUtility` | Spanning tree algorithms | 8 / infrastructure | +| `SurgeryUtility` | Mesh surgery (cut/glue) | — | +| `StitchingUtility` | Seam stitching | — | +| `CuttingUtility` | Advanced cutting (beyond tree-cotree) | 9c | +| `HyperellipticUtility` | Hyperelliptic surfaces | 10 | +| `LaplaceUtility` | Discrete Laplace operators | 9 / infrastructure | +| `ConformalStructureUtility` | Conformal structure extraction | 10 | + +--- + +## HyperIdeal Hessian: FD vs. analytic + +The Java library computes the HyperIdeal Hessian analytically through the chain: + +``` +(bᵢ, aₑ) → lᵢⱼ → ζ₁₃/ζ₁₄/ζ₁₅ → αᵢⱼ / βᵢ +``` + +conformallab++ uses a **symmetric finite-difference approximation**: + +``` +H[i,j] = ( G(x + ε·eⱼ)[i] − G(x − ε·eⱼ)[i] ) / (2ε), ε = 1e-5 +``` + +Accuracy: O(ε²) ≈ 10⁻¹⁰ relative error. PSD guaranteed by strict convexity (Springborn 2020). +Cost: n extra gradient evaluations per Newton step. +Impact: negligible for meshes < 500 DOFs; measurable for larger meshes. + +The analytic Hessian is deferred to Phase 9b. diff --git a/doc/roadmap/phases.md b/doc/roadmap/phases.md new file mode 100644 index 0000000..795affa --- /dev/null +++ b/doc/roadmap/phases.md @@ -0,0 +1,116 @@ +# Development Roadmap + +> **Legend:** ✅ complete · 🔲 planned +> +> **Porting / research boundary:** +> Phases 1–7 are direct ports of the Java original and its dissertation. +> From Phase 8 onwards the work goes beyond the scope of the Java library. +> Phase 8 (CGAL package) is infrastructure. Phase 9 is porting of remaining Java features. +> Phase 10+ is independent research with no direct Java reference implementation. + +--- + +## ◼ Porting complete — Phases 1–7 + +``` +Phase 1 Clausen / Lobachevsky / ImLi₂ special functions ✅ +Phase 2 Hyper-ideal geometry (ζ, lᵢⱼ, αᵢⱼ, σᵢ, σᵢⱼ) ✅ +Phase 3 CGAL Surface_mesh infrastructure + all three functionals + (Euclidean, Spherical, HyperIdeal) + analytical Hessians ✅ +Phase 4 Newton solver (SimplicialLDLT + SparseQR fallback) + + Mesh I/O (OFF/OBJ/PLY) + example programs ✅ 68 tests +Phase 5 Priority-BFS layout + CLI app + JSON/XML serialisation ✅ 95 tests +Phase 6 Gauss–Bonnet check/enforce, tree-cotree cut graph (2g), + exact hyperbolic trilateration, layout normalisation ✅ 121 tests +Phase 7 MobiusMap, halfedge_uv, Möbius holonomy (SU(1,1)), + period matrix τ∈ℍ + SL(2,ℤ) reduction, + fundamental domain parallelogram + tiling ✅ 158 tests +``` + +--- + +## ◼ Infrastructure — Phase 8: CGAL Package + +Goal: conformallab++ as a standalone CGAL package, submission-ready, fulfilling all +CGAL package conventions with a traits-class design compatible with any CGAL-conforming +mesh type. + +``` +8a Traits class & concepts + → include/CGAL/Conformal_map_traits.h + Separates MeshType, KernelType, ScalarType from the algorithm. + Enables use with any CGAL-compatible mesh, not just Surface_mesh. + → Concept checks via static_assert / CGAL_concept_check + +8b Public CGAL header hierarchy + → include/CGAL/Discrete_conformal_map.h (user-facing entry header) + → include/CGAL/Conformal_newton_solver.h + → include/CGAL/Conformal_layout.h + → include/CGAL/Conformal_cut_graph.h + All existing include/conformallab/*.hpp remain as implementation details. + +8c CGAL-style documentation + → doc/Conformal_map/PackageDescription.txt + → doc/Conformal_map/fig/ (pipeline diagrams) + → Doxygen comments on all public concepts and functions + → User_manual.md + Reference_manual.md + +8d CGAL test format + → test/Conformal_map/CMakeLists.txt (CGAL-style CMake) + Existing GTest tests remain; CGAL-format tests are added alongside. + +8e Declarative YAML pipeline + → Lightweight YAML format for reproducible experiments + (specification in doc/api/cgal-package.md) + → Validator: checks require/provide tokens before execution + → CLI integration: conformallab_core --pipeline experiment.yml +``` + +--- + +## ◼ Remaining porting — Phase 9 + +Java features from `de.varylab.discreteconformal` not yet in C++: + +``` +9a Inversive-distance functional (Luo 2004 / Bowers–Stephenson) + → inversive_distance_functional.hpp + Follows the exact same pattern as the three existing functionals. + → newton_inversive_distance() + → New test suite: test_inversive_distance.cpp + +9b Analytic HyperIdeal Hessian + → Replace FD Hessian in hyper_ideal_hessian.hpp + Direct differentiation through the chain: + (bᵢ, aₑ) → lᵢⱼ → ζ₁₃/ζ₁₄/ζ₁₅ → αᵢⱼ / βᵢ + Relevant for meshes > 500 DOFs (current FD Hessian is slow there). + +9c 4g-polygon boundary walk (genus g > 1) + → Extend compute_fundamental_domain() beyond genus 1 + Algorithm outline already in fundamental_domain.hpp as TODO(Phase 9). + Java reference: FundamentalDomainUtility.java +``` + +--- + +## ◼ New research — Phase 10+ + +No direct Java reference implementation exists for these items. + +``` +Phase 10 Global uniformization for genus g ≥ 2 + +10a Discrete holomorphic differentials + Integrate basis 1-forms ωᵢ along b-cycles of the cut graph. + Mathematical basis: Bobenko–Springborn (2004), §6. + Java partial reference: DiscreteHolomorphicFormUtility.java + +10b Siegel period matrix Ω ∈ H_g (g×g complex symmetric, Im(Ω) > 0) + Ωᵢⱼ = ∫_{bⱼ} ωᵢ + Reduction to Siegel fundamental domain via Sp(2g,ℤ). + Requires: 10a + +10c Full uniformization for genus g ≥ 2 + Embedding as H²/Γ with Γ ⊂ PSL(2,ℝ) a Fuchsian group. + Requires: 10a + 10b + stable cut graph for g ≥ 2 (Phase 9c) +```