docs: restructure documentation into focused files
Some checks failed
C++ Tests / test-fast (push) Successful in 2m25s
C++ Tests / test-cgal (push) Failing after 1m58s

README.md: reduced from 703 to ~75 lines — what/why, status, quick
start, minimal usage example, navigation table to doc/ files.

doc/architecture/overall_pipeline.md: trimmed — roadmap, extension
points, declarative pipeline YAML, and references sections removed
(each now has its own dedicated file). Replaced with a link table.

New files:
  doc/getting-started.md       — build modes, single-test invocation, CLI
  doc/api/pipeline.md          — full pipeline API with code for all 3 geometries
  doc/api/extending.md         — new functionals, geometry modes, Java porting guide
  doc/api/contracts.md         — processing unit preconditions/provides table
  doc/api/cgal-package.md      — Phase 8 CGAL package design + YAML pipeline (TODO)
  doc/math/geometry-modes.md   — Euclidean/Spherical/HyperIdeal comparison
  doc/math/references.md       — all papers by module
  doc/roadmap/phases.md        — Phases 1–10 with porting/research boundary
  doc/roadmap/java-parity.md   — Java vs C++ feature parity table
  doc/contributing.md          — language policy, test standards, release flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-17 21:17:15 +02:00
parent 279f964b96
commit 14134b99ce
12 changed files with 1229 additions and 835 deletions

703
README.md
View File

@@ -1,703 +1,94 @@
# conformallab++ # conformallab++
conformallab++ is a modern C++ reimplementation of C++17 reimplementation of [ConformalLab](https://github.com/varylab/conformallab) —
[ConformalLab](https://github.com/varylab/conformallab) — Stefan Sechelmann's Java research library for discrete conformal geometry (TU Berlin).
the research software for discrete conformal geometry by The long-term goal is a **CGAL package** for discrete conformal maps.
**Stefan Sechelmann** (TU Berlin, Institut für Mathematik).
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 — **Status:** Phase 7 complete. Newton solver for all three geometries (Euclidean / Spherical / HyperIdeal), priority-BFS layout in ℝ²/S²/Poincaré disk, GaussBonnet, 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.**
> **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, GaussBonnet, 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**.
--- ---
## Features ## Quick start
| 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, PinkallPolthier) | ✅ 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 |
| **GaussBonnet 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
```bash ```bash
cmake -S code -B build -DWITH_CGAL=ON git clone https://codeberg.org/TMoussa/ConformalLabpp && cd ConformalLabpp
cmake --build build -j4
# Konformes Layout für beliebige OFF/OBJ/PLY-Netze # Fast tests — no system dependencies
./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json -x result.xml 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 # CGAL tests headless (apt install libboost-dev / brew install boost)
./bin/conformallab_core -i input.off -g spherical -o sphere.off 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 # Full build with CLI + viewer (requires Wayland/X11 dev headers)
./bin/conformallab_core -i input.off -s 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
### 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)
``` ```
--- ---
## Bibliotheks-Nutzung ## Minimal usage
### Minimale euklidische Pipeline
```cpp ```cpp
#include "conformal_mesh.hpp" #include "conformal_mesh.hpp"
#include "mesh_io.hpp" #include "mesh_io.hpp"
#include "euclidean_functional.hpp" #include "euclidean_functional.hpp"
#include "gauss_bonnet.hpp"
#include "newton_solver.hpp" #include "newton_solver.hpp"
#include "layout.hpp" #include "layout.hpp"
using namespace conformallab; using namespace conformallab;
int main() {
ConformalMesh mesh = load_mesh("input.off"); ConformalMesh mesh = load_mesh("input.off");
EuclideanMaps maps = setup_euclidean_maps(mesh);
auto maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps); compute_euclidean_lambda0_from_mesh(mesh, maps);
// DOFs zuweisen — ersten Vertex pinnen (Gauge-Fix) // Assign DOFs — pin first vertex (gauge fix)
auto vit = mesh.vertices().begin(); auto vit = mesh.vertices().begin();
maps.v_idx[*vit++] = -1; maps.v_idx[*vit++] = -1;
int idx = 0; int idx = 0;
for (; vit != mesh.vertices().end(); ++vit) for (; vit != mesh.vertices().end(); ++vit) maps.v_idx[*vit] = idx++;
maps.v_idx[*vit] = idx++;
// Zielwinkel: natürliches Gleichgewicht (x* = 0) // Natural equilibrium target: x* = 0 by construction
std::vector<double> x0(idx, 0.0); std::vector<double> x0(idx, 0.0);
auto G0 = euclidean_gradient(mesh, x0, maps); auto G0 = euclidean_gradient(mesh, x0, maps);
for (auto v : mesh.vertices()) { for (auto v : mesh.vertices())
int iv = maps.v_idx[v]; if (maps.v_idx[v] >= 0) maps.theta_v[v] -= G0[maps.v_idx[v]];
if (iv >= 0) maps.theta_v[v] -= G0[iv];
}
auto res = newton_euclidean(mesh, x0, maps); check_gauss_bonnet(mesh, maps);
if (res.converged) NewtonResult res = newton_euclidean(mesh, x0, maps);
save_mesh("output.off", mesh); Layout2D layout = euclidean_layout(mesh, res.x, maps);
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";
``` ```
--- ---
## Build-Modi ## Documentation
| 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 / KolpakovMednykh) |
| `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 (PinkallPolthier) |
| `newton_solver.hpp` | `newton_{euclidean,spherical,hyper_ideal}` + öffentliches `solve_linear_system` |
| `conformal_mesh.hpp` | `ConformalMesh` = `CGAL::Surface_mesh<Point3>` + 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, EricksonWhittlesey 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, FlagIndex-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) | ✅ | ✅ |
| GaussBonnet Konsistenzprüfung | ✅ | ✅ |
| Tree-Cotree-Schnittgraph (2g Kanten) | ✅ | ✅ EricksonWhittlesey |
| 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<Vertex_index, double>("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 GaussBonnet-Bedingung Σ( Θ_v) = ·χ(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<Point3>` ersetzt die Java-`CoHDS`-Halfedge-Datenstruktur. Vertex/Edge/Face/Halfedge-Deskriptoren sind typisierte Integer.
**Property-Maps.** `mesh.add_property_map<Vertex_index, double>("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 17 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 (EricksonWhittlesey 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 / BowersStephenson)
→ 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: BobenkoSpringborn (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:
| | | | | |
|---|---| |---|---|
| **Dissertation** | Stefan Sechelmann *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, TU Berlin 2016 | | **Getting started** — build modes, single-test invocation, CLI, Docker | [doc/getting-started.md](doc/getting-started.md) |
| **DOI** | [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) | | **Pipeline API** — all three geometries, holonomy, serialisation | [doc/api/pipeline.md](doc/api/pipeline.md) |
| **Java-Originalbibliothek** | [github.com/varylab/conformallab](https://github.com/varylab/conformallab) | | **Extending** — new functionals, geometry modes, porting from Java | [doc/api/extending.md](doc/api/extending.md) |
| **Website** | [sechel.de](https://sechel.de/) | | **Processing unit contracts** — preconditions / provides table | [doc/api/contracts.md](doc/api/contracts.md) |
| **LinkedIn** | [linkedin.com/in/sechel](https://www.linkedin.com/in/sechel/) | | **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) |
Die Dissertation steht unter Creative Commons Attribution ShareAlike 4.0 (CC BY-SA 4.0). | **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 110 | [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.

163
doc/api/cgal-package.md Normal file
View File

@@ -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<Point3>`. 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)

69
doc/api/contracts.md Normal file
View File

@@ -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.
---
## GaussBonnet — the most common source of failure
Prescribing angles that violate GaussBonnet 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`.

141
doc/api/extending.md Normal file
View File

@@ -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<Vertex_index, double> lambda0; // initial log-lengths
ConformalMesh::Property_map<Vertex_index, double> theta_v; // target angles
ConformalMesh::Property_map<Vertex_index, int> v_idx; // DOF index, -1 = pinned
// add edge DOFs if needed:
ConformalMesh::Property_map<Edge_index, int> 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<double> my_gradient(
const ConformalMesh& mesh,
const std::vector<double>& x,
const MyMaps& maps)
{
std::vector<double> 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<double> 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<double> 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.

214
doc/api/pipeline.md Normal file
View File

@@ -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<double> 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 GaussBonnet (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<double>
// 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<double> 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<double> 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<Vertex_index, double>("v:my_curv", 0.0);
curv[v] = 3.14;
// Retrieve an existing map
auto [curv2, found] = mesh.property_map<Vertex_index, double>("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 v1v2)
// 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));
}
```

View File

@@ -388,177 +388,14 @@ Normalise PCA centring Rodrigues to N pole weighted Möbius centrin
--- ---
## Extension points ## Further documentation
### Adding a new functional | Topic | Document |
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 17 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 17
```
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 GaussBonnet, 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
| | |
|---|---| |---|---|
| **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) | | Extending the library (new functionals, geometry modes, Java porting guide) | [api/extending.md](../api/extending.md) |
| **Java original** — [github.com/varylab/conformallab](https://github.com/varylab/conformallab) | Reference implementation in Java — the direct source for all algorithms ported to C++ | | 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) |
### Further references | Three geometry modes in detail | [math/geometry-modes.md](../math/geometry-modes.md) |
| References and papers | [math/references.md](../math/references.md) |
| Source | Relevance in conformallab++ | | Development roadmap (Phases 110) | [roadmap/phases.md](../roadmap/phases.md) |
|--------|----------------------------| | Java vs. C++ feature parity table | [roadmap/java-parity.md](../roadmap/java-parity.md) |
| 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` |

88
doc/contributing.md Normal file
View File

@@ -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/<topic>`, `fix/<topic>`, `phase<N>-<topic>`
---
## 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 — <one-line summary>"
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

142
doc/getting-started.md Normal file
View File

@@ -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/
```

112
doc/math/geometry-modes.md Normal file
View File

@@ -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.
---
## GaussBonnet constraint
Before calling any Newton solver, the target angles must satisfy the GaussBonnet 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.

35
doc/math/references.md Normal file
View File

@@ -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 LaplaceBeltrami 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 |

View File

@@ -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 | PinkallPolthier (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) | ✅ | ✅ | |
| GaussBonnet consistency check | ✅ | ✅ | |
| Tree-cotree cut graph (2g edges) | ✅ | ✅ EricksonWhittlesey (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.

116
doc/roadmap/phases.md Normal file
View File

@@ -0,0 +1,116 @@
# Development Roadmap
> **Legend:** ✅ complete · 🔲 planned
>
> **Porting / research boundary:**
> Phases 17 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 17
```
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 GaussBonnet 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 / BowersStephenson)
→ 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: BobenkoSpringborn (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)
```