Closes the v0.9.0 release loop on top of Phase 9a-Newton + Phase 8b-Lite: * CHANGELOG.md (NEW) — Keep-A-Changelog format, with v0.9.0 entry detailing all Phase 9a / 9b / 8b-Lite contents and the doc-audit corrections that landed via PR #10. * CITATION.cff — version 0.7.0 → 0.9.0, date 2026-05-18 → 2026-05-22. * Stale HDS-port stubs removed (13 GTEST_SKIPs total): - code/tests/test_spherical_functional.cpp - code/tests/test_hyper_ideal_functional.cpp - code/tests/test_hyper_ideal_hyperelliptic_utility.cpp These referenced a "HDS port (Phase 4)" that never happened — CoHDS was intentionally replaced by CGAL::Surface_mesh, and the functional tests live in code/tests/cgal/test_*_functional.cpp. * Test-count updates everywhere: - Non-CGAL 36 → 23 (drop = 13 deleted stubs) - CGAL 176 → 227 - Total 212 → 250 (+38 net, 0 skipped) Files: README.md, CLAUDE.md, CHANGELOG.md, scripts/try_it.sh, doc/api/tests.md, doc/contributing.md, doc/getting-started.md, doc/math/validation.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
393 lines
20 KiB
Markdown
393 lines
20 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## Project purpose and long-term goal
|
||
|
||
conformallab++ is a C++17 reimplementation of [ConformalLab](https://github.com/varylab/conformallab) — Stefan Sechelmann's Java research library for discrete conformal geometry (TU Berlin, ~850 commits, v1.0.0 2018). The algorithmic foundation is his 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) · CC BY-SA 4.0
|
||
|
||
**The long-term goal is a CGAL package** — a submission to the CGAL library that brings discrete conformal maps (hyper-ideal, spherical, Euclidean) to the CGAL ecosystem using `CGAL::Surface_mesh` as the underlying halfedge data structure, with a traits-class design compatible with arbitrary CGAL-conforming mesh types.
|
||
|
||
The project has three distinct phases:
|
||
- **Phase 1–7 (done):** Direct port of the Java library algorithms to C++
|
||
- **Phase 8–9 (planned):** CGAL package infrastructure + remaining Java features not yet ported (inversive-distance functional, analytic HyperIdeal Hessian, genus-g > 1 fundamental domain)
|
||
- **Phase 10+ (research):** New mathematics beyond the Java original — holomorphic differentials, Siegel period matrix Ω ∈ H_g, full uniformization for genus g ≥ 2
|
||
|
||
## Language
|
||
|
||
**All code, comments, documentation, commit messages, and test descriptions must be in English.** The project is intended for international collaboration and CGAL submission. Existing German-language comments in older files should be replaced with English when editing those files.
|
||
|
||
## Build commands
|
||
|
||
All source lives under `code/`. Three build modes:
|
||
|
||
```bash
|
||
# Mode 1 — fast tests, no CGAL, no Boost, no display (CI default)
|
||
cmake -S code -B build
|
||
cmake --build build --target conformallab_tests -j$(nproc)
|
||
ctest --test-dir build --output-on-failure
|
||
|
||
# Mode 2 — CGAL tests, headless (CI full, requires Boost headers only)
|
||
# macOS: brew install boost Linux: apt install libboost-dev
|
||
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
|
||
|
||
# Mode 3 — full local build: CLI app + viewer + examples (requires Wayland/X11)
|
||
cmake -S code -B build -DWITH_CGAL=ON
|
||
cmake --build build -j$(nproc)
|
||
```
|
||
|
||
`-DWITH_CGAL=ON` automatically enables `-DWITH_VIEWER=ON`, which pulls in GLFW and requires `wayland-scanner`. Never use this in headless CI.
|
||
|
||
### Running a single test
|
||
|
||
```bash
|
||
# By GTest suite/test name
|
||
./build/conformallab_cgal_tests --gtest_filter="NewtonSolver*"
|
||
./build/conformallab_tests --gtest_filter="Clausen*"
|
||
|
||
# By CTest regex (prefix "cgal." for all CGAL tests)
|
||
ctest --test-dir build -R "cgal.NewtonSolver" --output-on-failure
|
||
```
|
||
|
||
### Rebuilding the CI Docker image
|
||
|
||
```bash
|
||
docker buildx build \
|
||
--platform linux/arm64 \
|
||
-f .gitea/docker/Dockerfile.ci-cpp \
|
||
-t git.eulernest.eu/conformallab/ci-cpp:latest \
|
||
--push \
|
||
.gitea/docker/
|
||
```
|
||
|
||
## Architecture
|
||
|
||
### Everything is header-only
|
||
|
||
All algorithms live in `code/include/*.hpp`. There is no compiled library. The three CMake targets (`conformallab_tests`, `conformallab_cgal_tests`, `conformallab_core`) compile headers directly from their `.cpp` entry points. To add a new algorithm: create a `.hpp` in `code/include/`, add a test in `code/tests/cgal/`, and register the test file in `code/tests/cgal/CMakeLists.txt`.
|
||
|
||
### Central type: `ConformalMesh`
|
||
|
||
`conformal_mesh.hpp` defines the core type:
|
||
```cpp
|
||
using ConformalMesh = CGAL::Surface_mesh<Point3>; // CGAL::Simple_cartesian<double>
|
||
```
|
||
|
||
This replaces the Java `CoHDS` (half-edge data structure) and its intrusive `CoVertex`/`CoEdge`/`CoFace` types. Data is attached via named CGAL property maps instead of intrusive fields:
|
||
|
||
| Property map name | Type | Meaning |
|
||
|---|---|---|
|
||
| `"v:lambda"` | `double` per vertex | log scale factor (conformal variable uᵢ) |
|
||
| `"v:theta"` | `double` per vertex | target cone angle Θᵥ |
|
||
| `"v:idx"` | `int` per vertex | solver DOF index; `-1` = pinned/boundary |
|
||
| `"e:alpha"` | `double` per edge | intersection angle αᵢⱼ (hyperbolic only) |
|
||
| `"f:type"` | `int` per face | geometry type (0=Euclidean, 1=Hyperbolic, 2=Spherical) |
|
||
|
||
`CGAL_DISABLE_GMP` and `CGAL_DISABLE_MPFR` are defined for all CGAL targets — the library deliberately uses `Simple_cartesian<double>` (floating-point, no exact arithmetic) because conformal geometry does not require exact predicates.
|
||
|
||
### The three geometry modes
|
||
|
||
Each mode has its own Maps struct that bundles all property maps, plus functional, Hessian, and Newton function:
|
||
|
||
| Mode | Space | Maps struct | Key headers | Newton function |
|
||
|---|---|---|---|---|
|
||
| Euclidean | ℝ² | `EuclideanMaps` | `euclidean_functional.hpp`, `euclidean_hessian.hpp` | `newton_euclidean()` |
|
||
| Spherical | S² | `SphericalMaps` | `spherical_functional.hpp`, `spherical_hessian.hpp` | `newton_spherical()` |
|
||
| Hyper-ideal | H² (Poincaré disk) | `HyperIdealMaps` | `hyper_ideal_functional.hpp`, `hyper_ideal_hessian.hpp` | `newton_hyper_ideal()` |
|
||
|
||
HyperIdeal also has edge DOFs (`e_idx[e]`); Euclidean and Spherical are vertex-DOF only. For HyperIdeal: `assign_all_dof_indices(mesh, maps)` assigns all vertex and edge DOFs automatically. For Euclidean/Spherical: pin one vertex manually (`maps.v_idx[first_vertex] = -1`) then assign sequential indices.
|
||
|
||
### The full pipeline
|
||
|
||
```
|
||
load_mesh() → ConformalMesh (OFF/OBJ/PLY)
|
||
setup_*_maps(mesh) → *Maps (property maps created, all zero)
|
||
compute_*_lambda0_from_mesh(mesh, m) → λ° initialised from 3-D edge lengths
|
||
DOF assignment → v_idx[v] set; -1 = pinned
|
||
check_gauss_bonnet(mesh, maps) → throws if Σ(2π−Θᵥ) ≠ 2π·χ(M)
|
||
enforce_gauss_bonnet(mesh, maps) → redistributes angle defect uniformly
|
||
newton_*(mesh, x0, maps) → NewtonResult{x*, iterations, converged}
|
||
compute_cut_graph(mesh) → CutGraph (2g seam edges, tree-cotree)
|
||
*_layout(mesh, x*, maps, &cg, &hol) → Layout2D/3D + HolonomyData
|
||
normalise_*(layout) → canonical position (PCA / Möbius / Rodrigues)
|
||
compute_period_matrix(hol) → PeriodData{τ∈ℍ} (genus 1 flat torus)
|
||
compute_fundamental_domain(hol) → FundamentalDomain{vertices, generators}
|
||
tiling_neighbourhood(layout, hol) → vector of translated layout copies
|
||
save_result_json/xml() → serialised result
|
||
```
|
||
|
||
After `compute_*_lambda0_from_mesh()` the original vertex positions are no longer used — all subsequent computation is in log-length/scale-factor space.
|
||
|
||
### Newton solver (`newton_solver.hpp`)
|
||
|
||
The gradient sign convention differs between modes:
|
||
- **Euclidean/Spherical:** `G_v = Θ_v − actual_angle_sum` (target minus actual)
|
||
- **HyperIdeal:** `G_v = actual_angle_sum − Θ_v` (actual minus target)
|
||
|
||
Hessian sign and solver:
|
||
- **Euclidean:** H is PSD (cotangent Laplacian) → `SimplicialLDLT(H)`
|
||
- **Spherical:** H is NSD (concave energy) → `SimplicialLDLT(−H)` (sign flip inside `newton_spherical`)
|
||
- **HyperIdeal:** H is PSD (strictly convex) → `SimplicialLDLT(H)`
|
||
|
||
When `SimplicialLDLT` fails (rank-deficient H — gauge mode on closed mesh without pinned vertex), the solver automatically retries with `Eigen::SparseQR` to find the minimum-norm step orthogonal to the null space. Public API: `solve_linear_system(H, rhs, &used_fallback)`.
|
||
|
||
The HyperIdeal Hessian is currently a **symmetric finite-difference approximation** (O(ε²), costs n extra gradient evaluations per Newton step). The analytic Hessian via the chain `(bᵢ, aₑ) → lᵢⱼ → ζ₁₃/ζ₁₄/ζ₁₅ → αᵢⱼ/βᵢ` is deferred to Phase 9b.
|
||
|
||
### Layout and holonomy (`layout.hpp`)
|
||
|
||
BFS-trilateration with a **priority min-heap on BFS depth** (`depth = max(depth[src], depth[tgt]) + 1`). Root face = largest 3-D area face. This minimises trilateration error accumulation compared to simple BFS.
|
||
|
||
Key output fields:
|
||
- `layout.uv[v.idx()]` — primary UV (first/shallowest BFS visit per vertex)
|
||
- `layout.halfedge_uv[h.idx()]` — UV of `source(h)` as seen from `face(h)`; at seam halfedges the two opposite halfedges carry *different* UV values, enabling proper GPU texture atlasing without vertex duplication
|
||
- `hol.translations[i]` — lattice generator ωᵢ ∈ ℂ (Euclidean/spherical)
|
||
- `hol.mobius_maps[i]` — Möbius isometry Tᵢ ∈ SU(1,1) (hyperbolic, Poincaré disk)
|
||
|
||
`MobiusMap` is defined in `layout.hpp`: T(z) = (az+b)/(cz+d). Key methods: `from_three()` (fit to 3 point correspondences via 3×3 complex linear system), `compose()`, `inverse()`, `apply(Vector2d)`.
|
||
|
||
### Key mathematical reference for each header
|
||
|
||
| Header | Java original | Key reference |
|
||
|---|---|---|
|
||
| `hyper_ideal_geometry.hpp` | `HyperIdealGeometry.java` | Springborn (2020) — ζ₁₃/ζ₁₄/ζ₁₅ functions |
|
||
| `euclidean_hessian.hpp` | `EuclideanHessian.java` | Pinkall & Polthier (1993) — cotangent Laplacian |
|
||
| `spherical_hessian.hpp` | `SphericalHessian.java` | ∂α/∂u from spherical law of cosines |
|
||
| `cut_graph.hpp` | `CuttingUtility.java` | Erickson & Whittlesey (SODA 2005) — tree-cotree |
|
||
| `period_matrix.hpp` | `PeriodMatrixUtility.java` | Sechelmann (2016) §4 — SL(2,ℤ) reduction |
|
||
| `gauss_bonnet.hpp` | (distributed across Java) | Gauss–Bonnet: Σ(2π−Θᵥ) = 2π·χ(M) |
|
||
|
||
### Java features not yet ported (Phase 9)
|
||
|
||
The Java library under `de.varylab.discreteconformal` contains these items not yet in C++:
|
||
|
||
| Java class | Planned C++ header | Phase |
|
||
|---|---|---|
|
||
| `InversiveDistanceFunctional` | `inversive_distance_functional.hpp` | 9a |
|
||
| Analytic HyperIdeal Hessian | `hyper_ideal_hessian.hpp` (replace FD) | 9b |
|
||
| 4g-polygon boundary walk in `FundamentalDomainUtility` | `fundamental_domain.hpp` (extend) | 9c |
|
||
| `DiscreteHarmonicFormUtility` | Phase 10a prerequisite | 10 |
|
||
| `DiscreteHolomorphicFormUtility` | Phase 10a | 10 |
|
||
| `HomologyUtility`, `CanonicalBasisUtility` | Phase 10 | 10 |
|
||
|
||
When porting a Java class, locate the original in `de.varylab.discreteconformal.*` at [github.com/varylab/conformallab](https://github.com/varylab/conformallab) and use it as the reference implementation.
|
||
|
||
## Test design patterns
|
||
|
||
### "Natural theta" — constructing a known equilibrium at x* = 0
|
||
|
||
```cpp
|
||
// Evaluate gradient at x=0; set target angles = actual angle sums → x*=0 by definition
|
||
std::vector<double> x0(n_dofs, 0.0);
|
||
auto G0 = euclidean_gradient(mesh, x0, maps);
|
||
for (auto v : mesh.vertices())
|
||
if (maps.v_idx[v] >= 0)
|
||
maps.theta_v[v] -= G0[maps.v_idx[v]]; // shift so G(x=0) = 0
|
||
```
|
||
|
||
This is used in virtually every Newton convergence test — it avoids hardcoding specific angle values.
|
||
|
||
### Gradient check pattern
|
||
|
||
```cpp
|
||
// Copy from any test_*_functional.cpp — GradientCheck_* test suite
|
||
double eps = 1e-5;
|
||
for (int i = 0; i < n; ++i) {
|
||
xp[i] += eps; auto Gp = euclidean_gradient(mesh, xp, maps);
|
||
xm[i] -= eps; auto Gm = euclidean_gradient(mesh, xm, maps);
|
||
double fd = (energy(xp) - energy(xm)) / (2*eps);
|
||
EXPECT_NEAR(G[i], fd, 1e-7);
|
||
xp[i] = xm[i] = x0[i];
|
||
}
|
||
```
|
||
|
||
All new functionals must have a gradient-check test before being considered complete.
|
||
|
||
### Halfedge traversal
|
||
|
||
```cpp
|
||
for (auto f : mesh.faces()) {
|
||
auto h0 = mesh.halfedge(f); // canonical halfedge of face
|
||
auto h1 = mesh.next(h0);
|
||
auto h2 = mesh.next(h1);
|
||
|
||
Vertex_index v1 = mesh.source(h0); // = mesh.target(h2)
|
||
Vertex_index v2 = mesh.source(h1);
|
||
Vertex_index v3 = mesh.source(h2);
|
||
|
||
// Angle at v3 is opposite to h0 (edge v1–v2)
|
||
// h_alpha[h0] = α₃, h_alpha[h1] = α₁, h_alpha[h2] = α₂
|
||
|
||
bool is_boundary = mesh.is_border(mesh.opposite(h0));
|
||
}
|
||
```
|
||
|
||
### Attaching custom data to the mesh
|
||
|
||
```cpp
|
||
auto [my_map, created] = mesh.add_property_map<Vertex_index, double>("v:my_data", 0.0);
|
||
my_map[v] = 3.14;
|
||
```
|
||
|
||
## CI pipeline
|
||
|
||
Two jobs in `.gitea/workflows/cpp-tests.yml`:
|
||
|
||
| Job | CMake flags | Deps | Triggers on |
|
||
|---|---|---|---|
|
||
| `test-fast` | *(none)* | Eigen + GTest only | all branches |
|
||
| `test-cgal` | `-DWITH_CGAL_TESTS=ON` | + Boost | pull requests only |
|
||
|
||
Runner: `eulernest` — self-hosted Raspberry Pi, ARM64, Ubuntu 22.04. Docker image: `git.eulernest.eu/conformallab/ci-cpp:latest`. `test-cgal` needs `test-fast` to pass first (`needs: test-fast`).
|
||
|
||
Expected results: **23 non-CGAL tests pass**, **227 CGAL tests pass, 0 skipped**.
|
||
|
||
## Release state
|
||
|
||
Current release: **v0.7.0** (tag on `origin/dev`, PR to `main` open).
|
||
Phase 7 is complete. Phase 7.5 (Doxygen) and Phase 8 (CGAL package) are next.
|
||
|
||
## Phase 8 strategic decisions (2026-05-19)
|
||
|
||
The CGAL-package architecture was frozen on 2026-05-19. Full design:
|
||
[`doc/api/cgal-package.md`](doc/api/cgal-package.md). Key decisions:
|
||
|
||
| Decision | Choice |
|
||
|---|---|
|
||
| Submission to upstream CGAL | **Pre-submission-ready, not bound.** 12+ months horizon. |
|
||
| License | **MIT preserved** (no LGPL switch). |
|
||
| Mesh-type flexibility | **Generic `FaceGraph + HalfedgeGraph`** in target design; MVP starts Surface_mesh-only. |
|
||
| Parameter style | **Named Parameters** (`CGAL::parameters::...`). |
|
||
| Default kernel | **`Simple_cartesian<double>`** (status quo). |
|
||
| Backward compatibility | **Dual-layer wrapper** — `code/include/*.hpp` stays as implementation, `include/CGAL/*.h` is thin wrapper. No algorithm duplication. |
|
||
| Implementation strategy | **Hybrid MVP** — minimum Phase 8 (traits + one wrapper) first, then Phase 9 in full, then Phase 8 extensions only on concrete demand. |
|
||
| Phase-8 MVP acceptance test | **Phase 9a (Inversive-Distance)** as the first new client of the new traits API. |
|
||
|
||
### Implementation sequence (committed)
|
||
|
||
```
|
||
1. Phase 7.5 Doxygen + cleanup done ✅
|
||
2. Phase 8 MVP — traits + one euclidean wrapper 3–5 days
|
||
3. Phase 9a — Inversive-Distance against new traits 3–5 days
|
||
4. Phase 9b — analytic HyperIdeal Hessian 1 week
|
||
5. Phase 9c — 4g-polygon for genus g > 1 1 week
|
||
→ port really complete, v0.9.0 release
|
||
```
|
||
|
||
Phase 8 extensions (8a.2 generic FaceGraph, 8c full Doxygen manuals, 8d
|
||
CGAL-format tests, 8e YAML pipeline) are deferred to on-demand status —
|
||
no speculative architecture for an uncertain submission.
|
||
|
||
Root-level files added at v0.7.0:
|
||
- `CITATION.cff` — machine-readable citation (Sechelmann 2016, Springborn 2020, Bobenko–Springborn 2004)
|
||
- `CONTRIBUTING.md` — short root-level pointer to `doc/contributing.md`
|
||
- `scripts/try_it.sh` — one-script quickstart: build → 209 tests → example run
|
||
- CMake install target: `cmake --install build --prefix /usr/local` → headers land in `include/conformallab/`
|
||
|
||
## Port-vs-research maintenance rule (2026-05-21 audit)
|
||
|
||
Before claiming something "ports X from Java", **verify empirically**:
|
||
|
||
```bash
|
||
find /Users/tarikmoussa/Desktop/conformallab -iname "*X*"
|
||
grep -r "ClassName" /Users/tarikmoussa/Desktop/conformallab/src
|
||
```
|
||
|
||
If zero matches, the work is **new research** — add it to
|
||
`doc/roadmap/research-track.md` with primary literature citations,
|
||
**not** to `doc/roadmap/java-parity.md`.
|
||
|
||
The 2026-05-21 audit found four pre-existing mis-labels:
|
||
|
||
| Item | Wrong claim | Reality |
|
||
|---|---|---|
|
||
| `InversiveDistanceFunctional` | "Java port (Luo 2004)" | No such Java class exists |
|
||
| HyperIdeal Hessian (FD) | "Phase 4a" | Research — Java has `hasHessian()==false` |
|
||
| HyperIdeal Hessian (analytic) | "Phase 9b port" | Research — derivation via Schläfli 1858 |
|
||
| Tutorial framing | "ports `InversiveDistanceFunctional.java`" | Implementation from Luo 2004 + Glickenstein 2011 |
|
||
|
||
All four are corrected as of this commit. Future contributors must
|
||
follow the empirical verification rule above before any new claim.
|
||
|
||
## Documentation map
|
||
|
||
24 documents across 6 categories. Read the relevant one before reasoning from scratch
|
||
— do not hallucinate content that is already written down.
|
||
|
||
### Mathematics & theory
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| What problem does this library solve mathematically? | `doc/math/discrete-conformal-theory.md` |
|
||
| How do the three geometry modes differ (Euclidean/Spherical/HyperIdeal)? | `doc/math/geometry-modes.md` |
|
||
| What analytic invariants can be used to validate correctness? | `doc/math/validation.md` |
|
||
| What are the exact ctest commands with expected terminal output? | `doc/math/validation-protocol.md` |
|
||
| What is the O() complexity and how does it scale with mesh size? | `doc/math/complexity.md` |
|
||
| Which papers are referenced by which header? | `doc/math/references.md` |
|
||
| How does conformallab++ compare to libigl, CGAL, geometry-central, pmp-library? | `doc/math/software-landscape.md` |
|
||
| What is unique about conformallab++ (novelty, target audience)? | `doc/math/novelty-statement.md` |
|
||
|
||
### Architecture & design
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| Full pipeline diagram and data-flow overview | `doc/architecture/overall_pipeline.md` |
|
||
| Directory tree, build targets, file organisation | `doc/architecture/project-structure.md` |
|
||
| Key architectural decisions and their rationale | `doc/architecture/design-decisions.md` |
|
||
| Detailed comparison with geometry-central (CMU): overlap, adoption, scientific value | `doc/architecture/geometry-central-comparison.md` |
|
||
| Phase 9a validation report (CP-Euclidean port + Luo-inversive-distance literature check) | `doc/architecture/phase-9a-validation.md` |
|
||
|
||
### API & extension
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| All 24 public headers with descriptions | `doc/api/headers.md` |
|
||
| Full pipeline API for all three geometries | `doc/api/pipeline.md` |
|
||
| What does each processing unit require/provide (contracts)? | `doc/api/contracts.md` |
|
||
| How to add a new functional / geometry mode / port from Java | `doc/api/extending.md` |
|
||
| All 39 test suites, 227+23 tests, individual counts | `doc/api/tests.md` |
|
||
| Phase 8 CGAL package design + Declarative YAML pipeline spec | `doc/api/cgal-package.md` |
|
||
|
||
### Concepts & specs
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| Declarative YAML pipeline: token vocabulary, 5 examples, validation algorithm | `doc/concepts/declarative-pipeline.md` |
|
||
|
||
### Roadmap & porting
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| Phases 1–10 with status and sub-tasks | `doc/roadmap/phases.md` |
|
||
| Which Java classes are ported, which are planned, which are skipped? | `doc/roadmap/java-parity.md` |
|
||
| New research items (beyond Java) — citations, acceptance criteria | `doc/roadmap/research-track.md` |
|
||
|
||
### Tutorials & onboarding
|
||
|
||
| Question | Document |
|
||
|---|---|
|
||
| Build modes, single-test invocation, CLI, Docker image rebuild | `doc/getting-started.md` |
|
||
| Step-by-step: port the Inversive Distance functional (Phase 9a template) | `doc/tutorials/add-inversive-distance.md` |
|
||
| Language policy, test standards, release flow | `doc/contributing.md` |
|
||
|
||
### geometry-central context
|
||
|
||
**geometry-central** (Keenan Crane, CMU) implements the same discrete conformal
|
||
equivalence problem (Gillespie, Springborn & Crane, SIGGRAPH 2021) but uses
|
||
Ptolemaic flips on intrinsic triangulations instead of Newton on the original mesh.
|
||
It has no period matrix, holonomy, or spherical geometry mode.
|
||
The shared mathematical core (Springborn 2020) means cross-validation is meaningful.
|
||
Full analysis: `doc/architecture/geometry-central-comparison.md`.
|
||
Optional adoption roadmap (GC-1/2/3): `doc/roadmap/phases.md` (Optional section).
|
||
|
||
## Known quirks
|
||
|
||
- **`test-fast` also runs stubs**: `conformallab_tests` (non-CGAL) contains `GTEST_SKIP`-based stubs for functionals that need CGAL. This is intentional — those tests document what was in the Java port scope but requires the CGAL mesh type.
|
||
- **Boost is header-only**: CGAL 6.x uses only Boost headers (`Boost.Config`, `Boost.Graph`). No compiled Boost libraries are needed. `find_package(Boost REQUIRED)` only locates the include path.
|
||
- **`main` branch is protected** on `origin` (Gitea). Push to `dev`, then merge via pull request. Codeberg `main` can be pushed to directly.
|
||
- **Both remotes must stay in sync**: `origin` = `git.eulernest.eu` (CI runs here), `codeberg` = `codeberg.org/TMoussa/ConformalLabpp` (public mirror). Push to both after every significant change.
|