# conformallab++ conformallab++ is a modern C++ reimplementation of [ConformalLab](https://github.com/varylab/conformallab) — the research software for discrete conformal geometry by **Stefan Sechelmann** (TU Berlin, Institut für Mathematik). 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](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) > License: CC BY-SA 4.0 The dissertation develops the variational framework for discrete conformal equivalence on triangulations — discrete uniformization of Riemann surfaces, cone metrics, period matrices — that forms the mathematical core of both the Java original and this C++ port. **Original Java library:** [github.com/varylab/conformallab](https://github.com/varylab/conformallab) (Java, ~850 commits, v1.0.0 2018) **Author's website:** [sechel.de](https://sechel.de/) · **LinkedIn:** [linkedin.com/in/sechel](https://www.linkedin.com/in/sechel/) The long-term goal is a **CGAL package** that brings discrete conformal maps (hyper-ideal, spherical, Euclidean) to the CGAL ecosystem using `CGAL::Surface_mesh` as the underlying half-edge data structure. > **Status:** Phase 7 vollständig abgeschlossen. Alle drei Geometrien lösbar via Newton-Solver (SimplicialLDLT + SparseQR-Fallback). Priority-BFS-Layout in ℝ²/S²/Poincaré-Disk mit exakter hyperbolischer Trilateration, Gauss–Bonnet, Tree-Cotree-Schnittgraph, Möbius-Holonomie, Periodenmatrix (Genus 1), Fundamentalbereich-Polygon, halfedge_uv-Texturatlas. JSON/XML-Serialisierung, vollständige CLI-App. **158 Tests, 2 skipped**. --- ## 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 ```bash 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 ```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 ### Minimale euklidische Pipeline ```cpp #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 x0(idx, 0.0); auto G0 = euclidean_gradient(mesh, x0, maps); for (auto v : mesh.vertices()) { int iv = maps.v_idx[v]; if (iv >= 0) maps.theta_v[v] -= G0[iv]; } auto res = newton_euclidean(mesh, x0, maps); if (res.converged) save_mesh("output.off", mesh); return res.converged ? 0 : 1; } ``` ### Layout + Holonomie (geschlossene Flächen) ```cpp #include "layout.hpp" #include "cut_graph.hpp" #include "period_matrix.hpp" #include "fundamental_domain.hpp" // Schnittgraph berechnen (Tree-Cotree, 2g Kanten) CutGraph cg = compute_cut_graph(mesh); // Layout mit Holonomie-Tracking HolonomyData hol; Layout2D lay = euclidean_layout(mesh, res.x, maps, &cg, &hol, /*normalise=*/true); // Periodenmatrix (Genus 1) PeriodData pd = compute_period_matrix(hol); // τ = ω₂/ω₁ ∈ ℍ, SL(2,ℤ)-reduziert std::cout << "τ = " << pd.tau << "\n"; // Fundamentalbereich-Parallelogramm FundamentalDomain fd = compute_fundamental_domain(hol); // fd.vertices — 4 Ecken (CCW) // fd.generators — ω₁, ω₂ // Kachelkopie für Universalüberlagerung Layout2D tile = tiling_copy(lay, fd.generators[0], fd.generators[1], 1, -1); // halfedge_uv — Naht-bewusstes UV pro Halfedge (GPU-Texturatlas) // lay.halfedge_uv[h.idx()] = UV von source(h) aus Sicht von face(h) ``` ### HyperIdeal-Geometrie ```cpp auto maps = setup_hyper_ideal_maps(mesh); int n = assign_all_dof_indices(mesh, maps); auto result = newton_hyper_ideal(mesh, x0, maps); // Möbius-Holonomie (SU(1,1)-Isometrien pro Schnitt-Kante) HolonomyData hol; Layout2D lay = hyper_ideal_layout(mesh, result.x, maps, &cg, &hol); // hol.mobius_maps[i] = T_i (Möbius-Abbildung für i-te Schnitt-Kante) ``` ### SparseQR-Fallback direkt nutzen ```cpp #include "newton_solver.hpp" bool used_fallback = false; auto dx = conformallab::solve_linear_system(H, rhs, &used_fallback); if (used_fallback) std::cout << "SparseQR verwendet (H ist rangdefizient)\n"; ``` --- ## Build-Modi | Modus | CMake-Flags | Was wird gebaut | System-Deps | |-------|------------|-----------------|-------------| | **Nur Tests** (Standard / CI fast) | *(keine)* | `conformallab_tests` — 36 Tests | keine | | **CGAL-Tests headless** (CI full) | `-DWITH_CGAL_TESTS=ON` | `conformallab_cgal_tests` — 158 Tests | Boost | | **Vollständig** (lokal mit Viewer) | `-DWITH_CGAL=ON` | + CLI, Beispiel-Programme, interaktiver Viewer | Boost + Wayland/X11 | **`-DWITH_CGAL_TESTS=ON`** ist der empfohlene CI-Modus: kein GLFW, kein wayland-scanner, kein Display benötigt. **`-DWITH_CGAL=ON`** aktiviert automatisch den Viewer — nur für lokale Entwicklung mit Display. Externe Abhängigkeiten sind als Tarballs in `code/deps/tarballs/` enthalten und werden beim CMake-Configure-Schritt extrahiert (GTest via `FetchContent`). **Boost** wird nur mit `-DWITH_CGAL_TESTS=ON` oder `-DWITH_CGAL=ON` benötigt (Header-Only durch CGAL 6.x). --- ## Voraussetzungen | Tool | Minimum | |------|---------| | C++ Compiler (GCC oder Clang) | C++17 | | CMake | 3.20 | | Boost Headers | 1.70 *(nur mit `-DWITH_CGAL_TESTS=ON` oder `-DWITH_CGAL=ON`)* | --- ## Einstieg ```bash git clone https://codeberg.org/TMoussa/ConformalLabpp cd ConformalLabpp ``` ### Nur Tests (CI-Standard — keine System-Abhängigkeiten) ```bash cmake -S code -B build cmake --build build --target conformallab_tests -j$(nproc) ctest --test-dir build --output-on-failure ``` ### CGAL-Tests headless (CI — kein Display, kein wayland-scanner) ```bash # benötigt: apt install libboost-dev (Ubuntu) / brew install boost (macOS) cmake -S code -B build -DWITH_CGAL_TESTS=ON cmake --build build --target conformallab_cgal_tests -j$(nproc) ctest --test-dir build -R "^cgal\." --output-on-failure ``` Erwartet: **158 Tests bestanden, 2 skipped** (die zwei `@Ignore`-Hessian-Stubs). ### Vollständig lokal (CLI + Beispiele + interaktiver Viewer) ```bash # benötigt zusätzlich: Wayland/X11 dev-Pakete (wayland-scanner, libx11-dev …) cmake -S code -B build -DWITH_CGAL=ON cmake --build build -j$(nproc) ctest --test-dir build -R "^cgal\." --output-on-failure ./build/examples/example_layout ./build/examples/example_viewer data/off/example.off ./bin/conformallab_core -i input.off -g euclidean -o layout.off ``` --- ## Öffentliche Header (`code/include/`) | Header | Beschreibung | |--------|-------------| | `clausen.hpp` | Clausen Cl₂, Lobachevsky Л, ImLi₂ | | `hyper_ideal_geometry.hpp` | ζ-Funktionen, lᵢⱼ, αᵢⱼ, σᵢ, σᵢⱼ | | `hyper_ideal_utility.hpp` | Tetraeder-Volumen (Meyerhoff / Kolpakov–Mednykh) | | `hyper_ideal_functional.hpp` | HyperIdeal Energie + Gradient auf `ConformalMesh` | | `hyper_ideal_hessian.hpp` | HyperIdeal Hessian (numerisch FD, symmetrisiert) | | `hyper_ideal_visualization_utility.hpp` | Poincaré-Disk-Projektion, Umkreis-Helfer | | `spherical_geometry.hpp` | Sphärische Bogenlänge, Halbwinkelformel | | `spherical_functional.hpp` | Sphärisch: Energie + Gradient + Gauge-Fix | | `spherical_hessian.hpp` | Sphärischer Hessian (∂α/∂u, Kosinussatz) | | `euclidean_geometry.hpp` | Euklidischer Eckenwinkel (t-Wert / atan2) | | `euclidean_functional.hpp` | Euklidisch: Energie + Gradient | | `euclidean_hessian.hpp` | Kotangenten-Laplace Hessian (Pinkall–Polthier) | | `newton_solver.hpp` | `newton_{euclidean,spherical,hyper_ideal}` + öffentliches `solve_linear_system` | | `conformal_mesh.hpp` | `ConformalMesh` = `CGAL::Surface_mesh` + Property-Map-Helfer | | `mesh_builder.hpp` | `make_triangle` / `make_tetrahedron` / `make_quad_strip` / `make_fan` / … | | `mesh_io.hpp` | `read_mesh` / `write_mesh` / `load_mesh` / `save_mesh` | | `mesh_utils.hpp` | CGAL → Eigen Konvertierung (`cgal_to_eigen`) | | `serialization.hpp` | `save/load_result_json` + `save/load_result_xml` | | `gauss_bonnet.hpp` | `euler_characteristic`, `genus`, `gauss_bonnet_sum/rhs/deficit`, `check_gauss_bonnet`, `enforce_gauss_bonnet` | | `cut_graph.hpp` | `CutGraph` + `compute_cut_graph` (Tree-Cotree, Erickson–Whittlesey 2005) | | `layout.hpp` | `euclidean/spherical/hyper_ideal_layout` → `Layout2D/3D`; `MobiusMap`; `halfedge_uv`; Priority-BFS; `HolonomyData`; `normalise_*` | | `period_matrix.hpp` | `PeriodData`, `compute_period_matrix`, `reduce_to_fundamental_domain`, `is_in_fundamental_domain` | | `fundamental_domain.hpp` | `FundamentalDomain`, `compute_fundamental_domain_{genus1,}`, `tiling_copy`, `tiling_neighbourhood` | | `constants.hpp` | `conformallab::PI`, `TWO_PI` | --- ## Projektstruktur ``` code/ ├── include/ # Alle öffentlichen Header (Header-Only-Bibliothek) │ ├── conformal_mesh.hpp │ ├── mesh_builder.hpp │ ├── mesh_io.hpp / mesh_utils.hpp │ ├── newton_solver.hpp # 3 Newton-Solver + solve_linear_system │ ├── layout.hpp # Priority-BFS, MobiusMap, halfedge_uv, Holonomie │ ├── serialization.hpp │ ├── gauss_bonnet.hpp │ ├── cut_graph.hpp # Tree-Cotree │ ├── period_matrix.hpp # τ ∈ ℍ, SL(2,ℤ)-Reduktion (Genus 1) │ ├── fundamental_domain.hpp # Parallelogramm, Kacheln; 4g-Polygon TODO Phase 8 │ ├── hyper_ideal_{functional,hessian,geometry,utility,visualization_utility}.hpp │ ├── spherical_{functional,hessian,geometry}.hpp │ ├── euclidean_{functional,hessian,geometry}.hpp │ ├── clausen.hpp │ └── constants.hpp ├── examples/ │ ├── example_euclidean.cpp │ ├── example_hyper_ideal.cpp │ ├── example_layout.cpp # Solve → Layout → OFF/JSON/XML + Round-Trip │ └── example_viewer.cpp # Interaktiver libigl-Viewer ├── src/ │ ├── apps/v0/conformallab_cli.cpp # CLI-App (Phase 5) │ └── viewer/simple_viewer.cpp ├── tests/ │ ├── *.cpp # conformallab_tests (kein CGAL) │ └── cgal/ │ ├── test_conformal_mesh.cpp # 14 Tests │ ├── test_hyper_ideal_functional.cpp # 7 Tests │ ├── test_spherical_functional.cpp # 12 Tests (1 skipped) │ ├── test_euclidean_functional.cpp # 11 Tests (1 skipped) │ ├── test_euclidean_hessian.cpp # 9 Tests │ ├── test_spherical_hessian.cpp # 8 Tests │ ├── test_newton_solver.cpp # 14 Tests │ ├── test_mesh_io.cpp # 9 Tests │ ├── test_pipeline.cpp # 5 Tests │ ├── test_layout.cpp # 8 Tests (Layout + JSON/XML) │ ├── test_phase6.cpp # 26 Tests (GB, CutGraph, Trilateration) │ └── test_phase7.cpp # 37 Tests (MobiusMap, Priority-BFS, │ # halfedge_uv, Periodenmatrix, FD) └── deps/ ├── eigen-3.4.0/ ├── CGAL-6.1.1/ ├── libigl-2.6.0/ ├── glfw-3.4/ └── single_includes/ # CLI11, json.hpp ``` --- ## Test-Suiten ### `conformallab_tests` (CI — immer gebaut) Reine Mathe-Tests, nur Eigen: Clausen / Lobachevsky / ImLi₂, Hyper-ideal Geometrie, Tetraeder-Volumina. ### `conformallab_cgal_tests` (`-DWITH_CGAL_TESTS=ON` headless / `-DWITH_CGAL=ON` voll) | Suite | Tests | Was geprüft wird | |-------|------:|-----------------| | `ConformalMeshTopology` | 4 | Euler-Charakteristik, Vertex/Edge/Face-Anzahl | | `ConformalMeshTraversal` | 4 | Halfedge-Iteration, Valenz, Opposite | | `ConformalMeshProperties` | 5 | Property-Maps (λ, θ, idx, α, Geometrietyp) | | `ConformalMeshValidity` | 1 | CGAL-Validität für alle Factory-Meshes | | `HyperIdealFunctional` | 7 | FD-Gradient-Checks + Hessian-Symmetrie | | `SphericalFunctional` | 12 | Winkelformel + Gradient + Gauge-Fix (1 skipped) | | `EuclideanFunctional` | 11 | Winkelformel + Gradient (1 skipped) | | `EuclideanHessian` | 9 | Kotangenten-Laplace-Struktur, FD-Übereinstimmung, PSD, Nullraum | | `SphericalHessian` | 8 | Ableitungskorrektheit, NSD am Gleichgewicht | | `NewtonSolver` | 11 | Konvergenz (Eucl. ×3, Sphär. ×4, HyperIdeal ×4) | | `SparseQRFallback` | 3 | Full-Rank-LDLT · singuläre Matrix → QR · geschlossenes Mesh | | `MeshIO` | 9 | OFF/OBJ Round-Trips, Fehlerbehandlung | | `Pipeline` | 5 | End-to-End: Build → Setup → Solve → Export → Reload | | `Layout` | 8 | Kantenlängen-Erhaltung (Eucl./Sphär.), Poincaré-Disk | | `Serialization` | 2 | JSON- und XML-Round-Trips (DOF + Layout) | | `GaussBonnet` | 8 | χ, Genus, Summe/RHS, Defizit, Check, Enforce | | `CutGraph` | 6 | Tree-Cotree, offene/geschlossene Meshes, Flag–Index-Konsistenz | | `HyperbolicTrilateration` | 4 | Möbius + Kosinussatz: exakte Abstände, Disk-Inneres, off-origin | | `Normalisation` | 4 | Eukl. Schwerpunkt, Längenverhältnisse, Möbius-Zentrierung | | `MobiusMap` | 8 | Identity, Inverse, Compose, from_three, apply(Vector2d) | | `BestRootFace` | 2 | Gültige Wurzel-Fläche, Interior-Bonus | | `HalfedgeUV` | 4 | Größe = #Halfedges, Naht-Konsistenz, Randhalfedges = 0 | | `PriorityBFS` | 3 | Erfolg, kein Seam bei offenen Meshes, alle Vertices platziert | | `NormaliseEuclidean` | 2 | UV-Schwerpunkt = 0, halfedge_uv-Schwerpunkt = 0 | | `PeriodMatrix` | 7 | τ ∈ ℍ, SL(2,ℤ)-Reduktion, Ausnahme außerhalb ℍ | | `FundamentalDomain` | 7 | Genus-1-Parallelogramm CCW, Generatoren, g>1 leer | | `TilingCopy/Neighbourhood` | 4 | Verschiebung korrekt, Anzahl Kacheln | | **Gesamt** | **158** | **2 skipped** (Hessian-Stubs, identisch mit Java `@Ignore`) | --- ## Newton-Solver & SparseQR-Fallback `newton_solver.hpp` stellt drei Solver mit einheitlicher Schnittstelle bereit: ``` NewtonResult newton_euclidean (mesh, x0, maps [, tol, max_iter]) NewtonResult newton_spherical (mesh, x0, maps [, tol, max_iter]) NewtonResult newton_hyper_ideal(mesh, x0, maps [, tol, max_iter, hess_eps]) ``` Jede Iteration: Gradient **G** → Hessian **H** → löse **H·Δx = −G** (SimplicialLDLT, Fallback SparseQR) → Backtracking-Liniensuche. | Geometrie | **G** | **H**-Vorzeichen | |-----------|-------|-----------------| | Euklidisch | Θ_v − Σα_v | PSD → LDLT auf H | | Sphärisch | Θ_v − Σα_v | NSD → LDLT auf **−H** | | HyperIdeal | Σβ_v − Θ_v | PSD → LDLT auf H | **SparseQR-Fallback** (`solve_linear_system`): Bei rangdefizientem **H** (Gauge-Moden bei geschlossenen Meshes ohne gepinnten Vertex) findet SparseQR den minimalen Newton-Schritt orthogonal zum Nullraum. --- ## Mathematischer Umfang — C++ vs. Java-Original | Mathematische Schicht | Java ConformalLab | conformallab++ | |---|---|---| | Euklidisches Funktional — Energie, Gradient | ✅ | ✅ | | Sphärisches Funktional — Energie, Gradient, Gauge-Fix | ✅ | ✅ | | HyperIdeal Funktional — Energie, Gradient | ✅ | ✅ | | Inversive-Distance-Funktional (Luo 2004) | ✅ | ❌ nicht portiert | | Euklidischer Hessian — Kotangenten-Laplace | ✅ analytisch | ✅ analytisch | | Sphärischer Hessian — ∂α/∂u aus Kosinussatz | ✅ analytisch | ✅ analytisch | | HyperIdeal Hessian — ζ → lᵢⱼ → β/α Kette | ✅ analytisch | ⚠️ symmetrisches FD | | Newton-Solver | ✅ | ✅ | | SparseQR-Fallback für Gauge-Moden | ? | ✅ | | Kegelmetriken — vorgeschriebenes Θ_v ≠ 2π | ✅ vollständig | ⚠️ nur Datenstruktur | | Layout / Einbettung — ℝ² / H² / S² | ✅ | ✅ Priority-BFS, alle drei | | Exakte hyperbolische Trilateration | ✅ Möbius | ✅ Möbius + Kosinussatz | | halfedge_uv — Naht-bewusstes UV (Texturatlas) | ✅ | ✅ | | Gauss–Bonnet Konsistenzprüfung | ✅ | ✅ | | Tree-Cotree-Schnittgraph (2g Kanten) | ✅ | ✅ Erickson–Whittlesey | | Holonomie / Monodromie — Euklidisch (Translationen) | ✅ | ✅ | | Holonomie — Hyperbolisch (SU(1,1) Möbius-Mappe) | ✅ | ✅ | | Periodenmatrix τ — Genus 1 (SL(2,ℤ)-reduziert) | ✅ | ✅ | | Fundamentalbereich-Polygon — Genus 1 | ✅ | ✅ CCW-Parallelogramm | | 4g-Polygon-Randlauf — Genus g > 1 | ✅ | ❌ TODO Phase 8 | | Periodenmatrix Siegel Ω — Genus g ≥ 2 | ✅ | ❌ TODO Phase 8 | | Globale Uniformisierung — Genus g ≥ 2 | ✅ | ❌ Phase 8 geplant | | Clausen / Lobachevsky / ImLi₂ | ✅ | ✅ | | Poincaré-Disk / Lorentz-Boost Visualisierung | ✅ | ✅ | | Mesh I/O + Serialisierung | ✅ XML/CoHDS | ✅ OFF/OBJ/PLY + JSON/XML | | Interaktiver Viewer | ✅ jReality | ✅ libigl/GLFW | ### HyperIdeal Hessian — numerisch vs. analytisch Der analytische Hessian des HyperIdeal-Funktionals erfordert Differentiation durch die Kette `(bᵢ, aₑ) → lᵢⱼ → ζ₁₃/₁₄/₁₅ → αᵢⱼ / βᵢ` (vier Vertex-Typ-Kombinationen pro Kante). conformallab++ verwendet stattdessen einen symmetrisierten Finite-Differenzen-Hessian: ``` H[i,j] = ( G(x + ε·eⱼ)[i] − G(x − ε·eⱼ)[i] ) / (2ε) ``` O(ε²)-genau (≈ 10⁻¹⁰ relativer Fehler bei ε = 10⁻⁵), PSD durch strikte Konvexität (Springborn 2020), kostet n zusätzliche Gradient-Auswertungen pro Newton-Schritt. Für Meshes mit < 500 DOFs ist der Unterschied in der Wandzeit vernachlässigbar. Der analytische Hessian ist für Phase 8 geplant. --- ## Für Mathematiker — Bibliothek erweitern ### Mentales Modell ``` ConformalMesh — Halfedge-Mesh (CGAL::Surface_mesh) + Property-Maps — per-Vertex/Edge Daten (λ, θ, α, DOF-Index, …) Maps-Struct — sammelt alle Property-Maps für ein Funktional theta_v[v] — Zielwinkel bei Vertex v (Eingabe) v_idx[v] — DOF-Index, oder −1 wenn gepinnt e_idx[e] — DOF-Index für Kanten-DOFs (nur HyperIdeal) x ∈ ℝⁿ — DOF-Vektor, den der Solver optimiert evaluate_*(mesh, x, maps) → { Energie, Gradient, … } newton_*(mesh, x0, maps) → { x*, Iterationen, converged, … } ``` Die Mesh-Geometrie (Vertex-Positionen) dient nur zur Initialisierung der Log-Kantenlängen λ°. Danach arbeitet der Solver ausschließlich im x-Raum. ### Neues Funktional hinzufügen 1. **Maps-Struct**: `setup_my_maps(mesh)` mit Property-Maps für λ, θ_v, v_idx 2. **Energie + Gradient**: Schleife über `mesh.faces()`, akkumuliere in `grad[v_idx[v]]` 3. **Gradient-Check**: Finite-Differenzen-Verifikation (Vorlage in jedem `test_*_functional.cpp`) 4. **Newton-Solver**: `solve_linear_system(H, -G, &used_fallback)` direkt nutzen ### Halfedge-Mesh navigieren ```cpp for (auto f : mesh.faces()) { auto h0 = mesh.halfedge(f); auto h1 = mesh.next(h0); auto h2 = mesh.next(h1); Vertex_index v_opp = mesh.target(h2); // dem Halfedge h0 gegenüberliegend int dof = maps.v_idx[v_opp]; // −1 = gepinnt bool is_boundary = mesh.is_border(mesh.opposite(h0)); } ``` ### Neue Daten am Mesh befestigen ```cpp auto [curv, created] = mesh.add_property_map("v:my_curv", 0.0); curv[v] = 1.234; ``` ### Schnell-Start-Checkliste 1. `examples/example_layout.cpp` lesen — zeigt die vollständige Pipeline in ~120 Zeilen 2. `cmake -S code -B build -DWITH_CGAL=ON && cmake --build build --target example_layout` 3. Gradient-Check-Test in `tests/cgal/` hinzufügen (beliebigen `GradientCheck_*`-Block kopieren) 4. Verschiedene Zielwinkel ausprobieren: `maps.theta_v[v] = M_PI / 3` für alle Innen-Vertices. Die Gauss–Bonnet-Bedingung Σ(2π − Θ_v) = 2π·χ(M) muss erfüllt sein. 5. Konvergenz beobachten: `NewtonResult` enthält `iterations` und `grad_inf_norm` ### Weiterführende Literatur | Quelle | Bezug | |--------|-------| | Springborn — *Ideal Hyperbolic Polyhedra and Discrete Uniformization* (2020) | HyperIdeal-Funktional; ζ₁₃/₁₄/₁₅ in `hyper_ideal_geometry.hpp` | | Pinkall, Polthier — *Computing Discrete Minimal Surfaces* (1993) | Kotangenten-Laplace in `euclidean_hessian.hpp` | | Luo — *Combinatorial Yamabe Flow on Surfaces* (2004) | Inversive-Distance-Funktional (noch nicht portiert) | | Bobenko, Springborn — *Variational Principles for Circle Patterns* (2004) | Hintergrund für das Winkelsum-Variationsprinzip | | Erickson, Whittlesey — *Greedy Optimal Homotopy and Homology Generators* (SODA 2005) | Tree-Cotree-Algorithmus in `cut_graph.hpp` | --- ## Schlüssel-Designentscheidungen **CGAL als CoHDS-Ersatz.** `CGAL::Surface_mesh` ersetzt die Java-`CoHDS`-Halfedge-Datenstruktur. Vertex/Edge/Face/Halfedge-Deskriptoren sind typisierte Integer. **Property-Maps.** `mesh.add_property_map("v:lambda", 0.0)` ersetzt das Java-Adapter/Decorator-Pattern. **DOF-Vektor-Konvention.** Alle Funktionale verwenden `x` indiziert durch `v_idx[v]` / `e_idx[e]` (−1 = gepinnt). Einheitlich über alle drei Geometrien. **Priority-BFS.** Faces werden in aufsteigender BFS-Tiefe verarbeitet (Min-Heap über `depth = max(depth[v_src], depth[v_tgt]) + 1`). Dies minimiert die Akkumulation von Trilaterations-Fehlern — je weiter eine Fläche vom Ursprung entfernt ist, desto später wird sie platziert. **halfedge_uv-Semantik.** `halfedge_uv[h.idx()]` ist das UV von `source(h)` aus Sicht von `face(h)`. An Naht-Halfedges tragen die beiden gegenüberliegenden Halfedges unterschiedliche UV-Werte — so erhält jede Fläche ihre eigene Kopie eines Naht-Vertex für GPU-Texturatlas ohne Vertex-Duplizierung. **HyperIdeal Hessian via FD.** Der analytische Hessian durch `ζ13/14/15 → lij → β/α` ist auf Phase 8 verschoben. Ein symmetrischer FD-Hessian ist O(ε²)-genau, PSD durch strikte Konvexität und für < 500 DOFs ausreichend. **Sphärischer Hessian Vorzeichen.** Die sphärische Energie ist **konkav** (Hessian NSD). Newton löst `(−H)·Δx = G`, das Vorzeichen wird transparent in `newton_spherical` behandelt. --- ## CI Tests laufen automatisch bei Push auf `main`, `dev` und `claude/**`-Branches via selbst-gehostetem Gitea-Actions-Runner (`eulernest`, ARM64). **Nur `conformallab_tests` läuft in CI** (kein Boost/CGAL dort). ```bash # CI-Image neu bauen und pushen docker buildx build \ --platform linux/arm64 \ -f .gitea/docker/Dockerfile.ci-cpp \ -t git.eulernest.eu/conformallab/ci-cpp:latest \ --push \ .gitea/docker/ ``` --- ## Roadmap > **Legende:** ✅ abgeschlossen · 🔲 geplant > > **Grenze Portierung / neue Forschung:** > Phase 1–7 sind direkte Portierungen aus dem Java-Original bzw. seiner Dissertation. > Ab Phase 8 geht die Arbeit über den Umfang der Java-Bibliothek hinaus. > — Phase 8 (CGAL-Paket) ist **Infrastruktur**, kein neuer Algorithmus. > — Phase 9 (Inversive-Distance, Analytischer Hessian) ist **Portierung** ausstehender Java-Features. > — Phase 10+ ist **eigenständige Forschung**, die über das Java-Original hinausgeht. --- ### ◼ Portierungsphase abgeschlossen ``` Phase 1 Clausen / Lobachevsky / ImLi₂ ✅ Phase 2 Hyper-ideal Geometrie (ζ, lᵢⱼ, αᵢⱼ, σᵢ) ✅ Phase 3 CGAL-Infrastruktur + alle drei Funktionale + analytische Hessians (Eucl. + Sphär.) ✅ Phase 4 Newton-Solver (SimplicialLDLT + SparseQR-Fallback) + Mesh-I/O + Beispielprogramme ✅ Phase 5 Priority-BFS-Layout + CLI + JSON/XML ✅ 95 Tests Phase 6 Layout-Parität I ✅ 121 Tests → gauss_bonnet.hpp — χ, Genus, Σ(2π-Θ_v) Check + Enforce → cut_graph.hpp — Tree-Cotree (Erickson–Whittlesey 2005), 2g Schnitt-Kanten → Exakte hyperbolische Trilateration (Möbius + Kosinussatz) → normalise_{euclidean,hyperbolic,spherical} Phase 7 Layout-Parität II ✅ 158 Tests → MobiusMap — T(z)=(az+b)/(cz+d), from_three, compose, inverse → halfedge_uv — naht-bewusstes UV pro Halfedge (GPU-Texturatlas) → Möbius-Holonomie als SU(1,1)-Isometrie (hyperbolisch) → period_matrix.hpp — τ = ω₂/ω₁ ∈ ℍ, SL(2,ℤ)-Reduktion → fundamental_domain.hpp — CCW-Parallelogramm, Kachelung ``` --- ### ◼ Infrastruktur (über Java-Bibliothek hinaus) ``` Phase 8 CGAL-Paket-Struktur 🔲 (nächste Phase) Ziel: conformallab++ als eigenständiges CGAL-Paket, das in CGAL integriert werden kann und dessen Konventionen vollständig erfüllt. 8a — Traits-Klasse & Konzepte → include/CGAL/Conformal_map_traits.h Trennt MeshType, KernelType, ScalarType vom Algorithmus. Ermöglicht Nutzung mit beliebigem CGAL-kompatiblem Mesh. → Konzept-Checks (static_assert / CGAL_concept_check) 8b — Öffentliche CGAL-Header-Hierarchie → include/CGAL/Discrete_conformal_map.h (zentraler Nutzer-Header) → include/CGAL/Conformal_newton_solver.h → include/CGAL/Conformal_layout.h → include/CGAL/Conformal_cut_graph.h → include/CGAL/conformal_map_package.h (Package-Description) Alle bestehenden include/conformallab/*.hpp bleiben als Impl.-Detail. 8c — Dokumentation im CGAL-Stil → doc/Conformal_map/PackageDescription.txt → doc/Conformal_map/fig/ (Pipeline-Diagramme) → Doxygen-Kommentare für alle öffentlichen Konzepte + Funktionen → User_manual.md + Reference_manual.md 8d — CGAL-Testformat → test/Conformal_map/ (CMakeLists.txt im CGAL-Format) Bestehende GTest-Tests bleiben; CGAL-Tests kommen als zweites Format. 8e — Declarative YAML-Pipeline → Leichtgewichtiges YAML-Format für reproduzierbare Experimente (Spezifikation bereits in doc/architecture/overall_pipeline.md) → Validator: prüft require/provide-Tokens vor der Ausführung → Einbindung in CLI-App: conformallab_core --pipeline experiment.yml ``` --- ### ◼ Ausstehende Portierung (Java-Features noch nicht übertragen) ``` Phase 9 Verbleibende Java-Parität 🔲 9a — Inversive-Distance-Funktional (Luo 2004 / Bowers–Stephenson) → inversive_distance_functional.hpp (folgt exakt dem Muster der bestehenden drei Funktionale — niedrigstes Risiko) → newton_inversive_distance() → Neue Test-Suite: test_inversive_distance.cpp 9b — Analytischer HyperIdeal-Hessian → Direkte Ableitung durch die Kette (b_i, a_e) → l_ij → ζ₁₃/ζ₁₄/ζ₁₅ → α_ij / β_i → Ersetzt den symmetrischen FD-Hessian in hyper_ideal_hessian.hpp → Relevant für Meshes > 500 DOFs (aktueller FD-Hessian ist dort langsam) → Aufwand: ~2 Wochen (viele verschachtelte Fallunterscheidungen) 9c — 4g-Polygon-Randlauf (Genus g > 1) → Boundary-Walk auf dem aufgeschnittenen Mesh → Befüllt fundamental_domain.hpp für g > 1 (aktuell: leeres Objekt) → Algorithmus-Skizze bereits als TODO(Phase 8) in fundamental_domain.hpp ``` --- ### ◼ Neue Forschung (über das Java-Original hinaus) > Ab hier gibt es keine direkte Java-Referenzimplementierung mehr. > Jedes Item ist eigenständige mathematische Arbeit. ``` Phase 10 Globale Uniformisierung Genus g ≥ 2 🔲 (Forschung) 10a — Holomorphe Differentiale auf diskreten Flächen Integration ω_i längs der b-Zyklen des Schnittgraphen. Mathematische Grundlage: Bobenko–Springborn (2004), §6. 10b — Siegel-Periodenmatrix Ω ∈ H_g (g×g, g ≥ 2) Ω_ij = ∫_{b_j} ω_i — komplexe symmetrische Matrix, Im(Ω) positiv definit (Siegel-Oberhalbebene H_g). Reduktion auf den Siegel-Fundamentalbereich via Sp(2g,ℤ). 10c — Vollständige Uniformisierung Für g ≥ 2: Einbettung als H²/Γ mit Γ ⊂ PSL(2,ℝ) Fuchssche Gruppe. Erfordert 10a + 10b + stabilen Cut-Graph für g ≥ 2 (Phase 9c). ``` --- ## Ursprung & Danksagung conformallab++ wäre ohne die Grundlagenarbeit von **Stefan Sechelmann** nicht möglich. Die Algorithmen, die Variationsformulierung und die Idee, diskrete konforme Geometrie als Newton-Problem auf Winkel-Summen-Energie-Funktionalen zu behandeln, stammen aus: | | | |---|---| | **Dissertation** | Stefan Sechelmann — *Variational Methods for Discrete Surface Parameterization: Applications and Implementation*, TU Berlin 2016 | | **DOI** | [10.14279/depositonce-5415](https://depositonce.tu-berlin.de/items/8e2988b2-d991-45b5-aad5-9fb7988f3b2f) | | **Java-Originalbibliothek** | [github.com/varylab/conformallab](https://github.com/varylab/conformallab) | | **Website** | [sechel.de](https://sechel.de/) | | **LinkedIn** | [linkedin.com/in/sechel](https://www.linkedin.com/in/sechel/) | Die Dissertation steht unter Creative Commons Attribution ShareAlike 4.0 (CC BY-SA 4.0). --- ## Lizenz conformallab++ steht unter der MIT-Lizenz (siehe [LICENSE](LICENSE)).