Covers build commands (all three modes), single-test invocation, header-only architecture, the three geometry modes, Newton solver sign conventions, layout BFS design, test patterns, CI structure, and known quirks (Finder ` 2.hpp` duplicates, CGAL compile flags). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
conformallab++
conformallab++ is a modern C++ reimplementation of ConformalLab — the research software for discrete conformal geometry by Stefan Sechelmann (TU Berlin, Institut für Mathematik).
The algorithmic foundation is his doctoral dissertation:
Stefan Sechelmann —
Variational Methods for Discrete Surface Parameterization: Applications and Implementation
Doctoral thesis, Technische Universität Berlin, 2016.
DOI: 10.14279/depositonce-5415
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 (Java, ~850 commits, v1.0.0 2018)
Author's website: sechel.de · LinkedIn: 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.
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
cmake -S code -B build -DWITH_CGAL=ON
cmake --build build -j4
# 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
# Geometrien: euclidean | spherical | hyper_ideal
./bin/conformallab_core -i input.off -g spherical -o sphere.off
# Interaktiver Viewer
./bin/conformallab_core -i input.off -s
Beispiel-Programme
./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
Minimale euklidische Pipeline
#include "conformal_mesh.hpp"
#include "mesh_io.hpp"
#include "euclidean_functional.hpp"
#include "newton_solver.hpp"
#include "layout.hpp"
using namespace conformallab;
int main() {
ConformalMesh mesh = load_mesh("input.off");
auto maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps);
// 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++;
// Zielwinkel: natürliches Gleichgewicht (x* = 0)
std::vector<double> 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)
#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
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
#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
| 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
git clone https://codeberg.org/TMoussa/ConformalLabpp
cd ConformalLabpp
Nur Tests (CI-Standard — keine System-Abhängigkeiten)
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)
# 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)
# 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<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, 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
- Maps-Struct:
setup_my_maps(mesh)mit Property-Maps für λ, θ_v, v_idx - Energie + Gradient: Schleife über
mesh.faces(), akkumuliere ingrad[v_idx[v]] - Gradient-Check: Finite-Differenzen-Verifikation (Vorlage in jedem
test_*_functional.cpp) - Newton-Solver:
solve_linear_system(H, -G, &used_fallback)direkt nutzen
Halfedge-Mesh navigieren
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
auto [curv, created] = mesh.add_property_map<Vertex_index, double>("v:my_curv", 0.0);
curv[v] = 1.234;
Schnell-Start-Checkliste
examples/example_layout.cpplesen — zeigt die vollständige Pipeline in ~120 Zeilencmake -S code -B build -DWITH_CGAL=ON && cmake --build build --target example_layout- Gradient-Check-Test in
tests/cgal/hinzufügen (beliebigenGradientCheck_*-Block kopieren) - Verschiedene Zielwinkel ausprobieren:
maps.theta_v[v] = M_PI / 3für alle Innen-Vertices. Die Gauss–Bonnet-Bedingung Σ(2π − Θ_v) = 2π·χ(M) muss erfüllt sein. - Konvergenz beobachten:
NewtonResultenthältiterationsundgrad_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).
# 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:
| Dissertation | Stefan Sechelmann — Variational Methods for Discrete Surface Parameterization: Applications and Implementation, TU Berlin 2016 |
| DOI | 10.14279/depositonce-5415 |
| Java-Originalbibliothek | github.com/varylab/conformallab |
| Website | sechel.de |
| linkedin.com/in/sechel |
Die Dissertation steht unter Creative Commons Attribution ShareAlike 4.0 (CC BY-SA 4.0).
Lizenz
conformallab++ steht unter der MIT-Lizenz (siehe LICENSE).