Structured the development roadmap into four blocks with an explicit boundary marker separating direct Java ports (Phase 1–7) from infrastructure (Phase 8), remaining porting (Phase 9), and new research territory (Phase 10+). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
698 lines
31 KiB
Markdown
698 lines
31 KiB
Markdown
# 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<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)
|
||
|
||
```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 |
|
||
|-------|------------|-----------------|
|
||
| **Nur Tests** (Standard) | *(keine)* | `conformallab_tests` — Eigen + GTest |
|
||
| **CGAL-Tests + Beispiele** | `-DWITH_CGAL=ON` | `conformallab_cgal_tests`, Beispiel-Programme, CLI |
|
||
| **Interaktiver Viewer** | `-DWITH_CGAL=ON` | + `example_viewer`, Viewer in CLI integriert |
|
||
|
||
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=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=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 + Beispiele (benötigt System-Boost)
|
||
|
||
```bash
|
||
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
|
||
./bin/conformallab_core -i input.off -g euclidean -o layout.off
|
||
```
|
||
|
||
Erwartet: **158 Tests bestanden, 2 skipped** (die zwei `@Ignore`-Hessian-Stubs).
|
||
|
||
### Interaktiver Viewer
|
||
|
||
```bash
|
||
cmake -S code -B build -DWITH_CGAL=ON
|
||
cmake --build build -t example_viewer -j$(nproc)
|
||
./build/examples/example_viewer data/off/example.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` (lokal — `-DWITH_CGAL=ON`)
|
||
|
||
| 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<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 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<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 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)).
|