feat(phase6): exact hyperbolic layout, Gauss–Bonnet, cut graph, normalisation — 121 tests
New files: - gauss_bonnet.hpp: euler_characteristic, genus, Σ(2π-Θ_v) sum/rhs/deficit, check_gauss_bonnet (throws), enforce_gauss_bonnet (correct sign: Δ=(lhs-rhs)/V) - cut_graph.hpp: CutGraph struct + compute_cut_graph (tree-cotree, Erickson–Whittlesey 2005); boundary edges correctly excluded from cut set - test_phase6.cpp: 26 new tests (GaussBonnet ×8, CutGraph ×6, HyperbolicTrilateration ×4, Normalisation ×4 — all pass) layout.hpp (Phase 6 rewrite): - detail::trilaterate_hyp: exact Möbius + hyperbolic law of cosines replacing old tanh(d/2) - detail::center_poincare_disk: Möbius centering for hyperbolic normalisation - normalise_euclidean: centroid → origin + PCA major-axis rotation - normalise_hyperbolic: Möbius centering in the Poincaré disk - normalise_spherical: Rodrigues rotation → north pole - euclidean_layout / hyper_ideal_layout: optional CutGraph* + HolonomyData* + normalise Bug fixes caught by new tests: - gauss_bonnet.hpp: enforce_gauss_bonnet had wrong sign for delta - cut_graph.hpp: boundary edges were incorrectly marked as cut edges 121 tests pass, 2 skipped (Hessian stubs). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
50
README.md
50
README.md
@@ -4,7 +4,7 @@ conformallab++ is a modern C++ reimplementation of the [ConformalLab](https://gi
|
||||
|
||||
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 5 vollständig abgeschlossen. Alle drei Geometrien lösbar via Newton-Solver (SimplicialLDLT + SparseQR-Fallback). BFS-Layout in ℝ²/S²/Poincaré-Disk, JSON/XML-Serialisierung, vollständige CLI-App. **95 Tests, 2 skipped**.
|
||||
> **Status:** Phase 6 vollständig abgeschlossen. Alle drei Geometrien lösbar via Newton-Solver (SimplicialLDLT + SparseQR-Fallback). BFS-Layout in ℝ²/S²/Poincaré-Disk mit exakter hyperbolischer Trilateration (Möbius + Kosinussatz), Gauss–Bonnet-Konsistenzprüfung, Tree-Cotree-Schnittgraph, Normalisierung (PCA/Möbius-Zentrierung). JSON/XML-Serialisierung, vollständige CLI-App. **121 Tests, 2 skipped**.
|
||||
|
||||
---
|
||||
|
||||
@@ -29,6 +29,10 @@ The long-term goal is a **CGAL package** that brings discrete conformal maps (hy
|
||||
| **BFS Layout** (ℝ², S², Poincaré disk) | ✅ Phase 5 |
|
||||
| **CLI app** (`conformallab_core`) | ✅ Phase 5 |
|
||||
| **JSON + XML serialisation** | ✅ Phase 5 |
|
||||
| **Gauss–Bonnet check + enforce** | ✅ Phase 6 |
|
||||
| **Tree-cotree cut graph** (2g seam edges) | ✅ Phase 6 |
|
||||
| **Exact hyperbolic trilateration** (Möbius + law of cosines) | ✅ Phase 6 |
|
||||
| **Layout normalisation** (PCA centring / Möbius centering) | ✅ Phase 6 |
|
||||
|
||||
---
|
||||
|
||||
@@ -218,7 +222,7 @@ ctest --test-dir build -R "^cgal\." --output-on-failure
|
||||
./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json
|
||||
```
|
||||
|
||||
Expected: **95 tests pass, 2 skipped** (the two `@Ignore` Hessian stubs).
|
||||
Expected: **121 tests pass, 2 skipped** (the two `@Ignore` Hessian stubs).
|
||||
|
||||
### Interactive viewer
|
||||
|
||||
@@ -250,8 +254,10 @@ cmake -S code -B build -DWITH_CGAL=ON && cmake --build build -t example_viewer -
|
||||
| `mesh_io.hpp` | `read_mesh` / `write_mesh` / `load_mesh` / `save_mesh` |
|
||||
| `conformal_mesh.hpp` | `ConformalMesh` = `CGAL::Surface_mesh<Point3>` + property-map helpers |
|
||||
| `mesh_builder.hpp` | `make_triangle` / `make_tetrahedron` / `make_quad_strip` / `make_fan` / `make_spherical_tetrahedron` |
|
||||
| `layout.hpp` | `euclidean_layout` / `spherical_layout` / `hyper_ideal_layout` → `Layout2D/3D`; BFS unfolding |
|
||||
| `layout.hpp` | `euclidean_layout` / `spherical_layout` / `hyper_ideal_layout` → `Layout2D/3D`; exact hyperbolic trilateration; normalise_{euclidean,hyperbolic,spherical}; `CutGraph*` + `HolonomyData*` |
|
||||
| `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` struct + `compute_cut_graph` (tree-cotree, Erickson–Whittlesey 2005) |
|
||||
| `mesh_utils.hpp` | CGAL → Eigen conversion (`cgal_to_eigen`) |
|
||||
| `constants.hpp` | `conformallab::PI`, `TWO_PI` |
|
||||
|
||||
@@ -267,8 +273,10 @@ code/
|
||||
│ ├── mesh_io.hpp
|
||||
│ ├── mesh_utils.hpp
|
||||
│ ├── newton_solver.hpp # ← public solve_linear_system + 3 Newton solvers
|
||||
│ ├── layout.hpp # ← BFS layout (euclidean/spherical/hyper_ideal)
|
||||
│ ├── layout.hpp # ← BFS layout + exact hyp. trilateration + normalise
|
||||
│ ├── serialization.hpp # ← JSON + XML save/load
|
||||
│ ├── gauss_bonnet.hpp # ← Gauss–Bonnet check + enforce (Phase 6)
|
||||
│ ├── cut_graph.hpp # ← Tree-cotree cut-graph algorithm (Phase 6)
|
||||
│ ├── hyper_ideal_{functional,hessian,geometry,utility,visualization_utility}.hpp
|
||||
│ ├── spherical_{functional,hessian,geometry}.hpp
|
||||
│ ├── euclidean_{functional,hessian,geometry}.hpp
|
||||
@@ -297,7 +305,8 @@ code/
|
||||
│ ├── test_newton_solver.cpp # 14 tests (incl. 3 SparseQR tests)
|
||||
│ ├── test_mesh_io.cpp # 6 tests
|
||||
│ ├── test_pipeline.cpp # 5 tests
|
||||
│ └── test_layout.cpp # 8 tests (layout + JSON/XML)
|
||||
│ ├── test_layout.cpp # 8 tests (layout + JSON/XML)
|
||||
│ └── test_phase6.cpp # 26 tests (GB, cut graph, trilateration, normalisation)
|
||||
└── deps/
|
||||
├── eigen-3.4.0/ # always extracted
|
||||
├── CGAL-6.1.1/ # extracted with WITH_CGAL
|
||||
@@ -334,7 +343,11 @@ Pure-math tests requiring only Eigen: Clausen / Lobachevsky / ImLi₂, hyper-ide
|
||||
| `Pipeline` | 5 | End-to-end: build → setup → solve → export → reload, all three geometries |
|
||||
| `Layout` | 6 | Edge-length preservation (Euclidean/Spherical), arc-lengths on S², Poincaré disk |
|
||||
| `Serialization` | 2 | JSON and XML round-trips (DOF + layout) |
|
||||
| **Total** | **95** | 2 skipped (Hessian stubs) |
|
||||
| `GaussBonnet` | 8 | χ, genus, sum/rhs, deficit, check, enforce (sign-correct) |
|
||||
| `CutGraph` | 6 | Tree-cotree algorithm, open/closed meshes, flag–index consistency |
|
||||
| `HyperbolicTrilateration` | 4 | Möbius + law of cosines: exact distances, inside-disk, off-origin |
|
||||
| `Normalisation` | 4 | Euclidean centroid, length-ratio invariance, Möbius centering (hyp.) |
|
||||
| **Total** | **121** | 2 skipped (Hessian stubs) |
|
||||
|
||||
---
|
||||
|
||||
@@ -498,11 +511,13 @@ Gauss–Bonnet pre-check ✅ ❌
|
||||
|
||||
### What "cone metrics" still requires
|
||||
|
||||
**Cone metrics** — the property map `theta_v` is already subtracted in the gradient (`G_v = Σα_v − Θ_v`), so prescribing a cone angle is a one-liner: `maps.theta_v[v] = desired_angle`. What is missing is the *application layer*: checking Gauss–Bonnet consistency (Σ (2π − Θ_v) = 2π·χ), distributing angle defects sensibly, and special handling at boundary vertices.
|
||||
**Cone metrics** — the property map `theta_v` is already subtracted in the gradient (`G_v = Σα_v − Θ_v`), so prescribing a cone angle is a one-liner: `maps.theta_v[v] = desired_angle`. Use `check_gauss_bonnet(mesh, maps)` to verify Σ(2π−Θ_v) = 2π·χ before solving, and `enforce_gauss_bonnet` to fix floating-point drift.
|
||||
|
||||
**Layout (Phase 5 ✅)** — `layout.hpp` implements BFS unfolding for all three geometries via `euclidean_layout`, `spherical_layout`, and `hyper_ideal_layout`. For open meshes the embedding is globally consistent; for closed meshes the first BFS visit wins and `has_seam = true` is set. To get a proper parameterisation of a closed mesh, cut it to a disk first (not yet implemented).
|
||||
**Layout (Phase 6 ✅)** — `layout.hpp` implements BFS unfolding for all three geometries. The hyperbolic layout now uses **exact trilateration** via Möbius maps and the hyperbolic law of cosines (replacing the old `tanh(d/2)` approximation). Pass a `CutGraph*` to track seam edges on closed surfaces, and a `HolonomyData*` to capture the holonomy group elements. Set `normalise=true` for PCA centring (Euclidean), Möbius centering (hyperbolic), or Rodrigues rotation to the north pole (spherical).
|
||||
|
||||
**Next steps** — global uniformization (cutting closed meshes, period matrices, holonomy), Gauss–Bonnet checking, analytical HyperIdeal Hessian.
|
||||
**Cut graph (Phase 6 ✅)** — `compute_cut_graph(mesh)` implements the tree-cotree algorithm (Erickson–Whittlesey 2005). For a closed genus-g surface it returns exactly 2g seam edges whose removal turns the surface into a topological disk.
|
||||
|
||||
**Next steps** — holonomy matrices / period matrix (genus-1 torus: τ = ω₂/ω₁ ∈ ℍ), analytical HyperIdeal Hessian, full global uniformization pipeline.
|
||||
|
||||
---
|
||||
|
||||
@@ -928,10 +943,21 @@ Phase 5 Layout + CLI + Serialisierung ✅ abgeschlossen
|
||||
→ test_layout.cpp: 8 Tests (Eucl./Sphär./HyperIdeal + JSON/XML)
|
||||
→ 95 Tests gesamt (2 skipped)
|
||||
|
||||
Phase 6 (geplant)
|
||||
Phase 6 Layout-Erweiterung (Java-Parität) ✅ abgeschlossen
|
||||
→ gauss_bonnet.hpp: euler_characteristic, genus, Σ(2π-Θ_v) check + enforce
|
||||
→ cut_graph.hpp: Tree-Cotree-Algorithmus (Erickson-Whittlesey); 2g Schnittkan.
|
||||
→ layout.hpp: exakte hyperbolische Trilateration (Möbius + hyperb. Kosinussatz)
|
||||
→ layout.hpp: normalise_euclidean (Schwerpunkt→0, PCA-Rotation)
|
||||
→ layout.hpp: normalise_hyperbolic (Möbius-Zentrierung im Poincaré-Disk)
|
||||
→ layout.hpp: normalise_spherical (Rodrigues-Rotation zum Nordpol)
|
||||
→ layout.hpp: CutGraph* + HolonomyData* Parameter für alle Layout-Funktionen
|
||||
→ test_phase6.cpp: 26 Tests (GB, CutGraph, Trilateration, Normalisierung)
|
||||
→ 121 Tests gesamt (2 skipped)
|
||||
|
||||
Phase 7 (geplant)
|
||||
→ Analytischer HyperIdeal-Hessian (direkte Ableitung durch ζ-Kette)
|
||||
→ Gauss–Bonnet Konsistenzprüfung für Kegelmetriken
|
||||
→ Mesh-Cut für geschlossene Flächen → globale Parameterisierung
|
||||
→ Holonomie-Matrizen für Tori (Periodenmatrix τ = ω₂/ω₁ ∈ ℍ)
|
||||
→ Vollständige globale Parameterisierung geschlossener Flächen
|
||||
→ Inversive-Distance-Funktional (Luo 2004)
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user