# conformallab++ — Architecture & Pipeline

## Origin

conformallab++ is a C++ reimplementation of
[ConformalLab](https://github.com/varylab/conformallab),
the Java research library for discrete conformal geometry by
**Stefan Sechelmann** (TU Berlin, Institut für Mathematik,
SFB/Transregio 109 *Discretization in Geometry and Dynamics*).

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) · CC BY-SA 4.0

The dissertation develops the variational framework for discrete conformal equivalence:
discrete uniformization of Riemann surfaces, cone metrics, period matrices, and
holonomy — all of which are directly implemented in this library.

Further links:  
**Java original:** [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/)

---

## Positioning

conformallab++ is a **specialised research library for discrete conformal geometry** on
triangulated surfaces. It is not a general geometry-processing framework.

The library revives the algorithms of the original ConformalLab by Stefan Sechelmann
in a modern C++ setting. Its core concern is one precise mathematical question:

> Given a triangulated surface, find a conformally equivalent metric that satisfies
> prescribed curvature (angle-sum) constraints at each vertex.

Everything in the library serves this goal:

| What it is | What it is not |
|------------|----------------|
| Discrete conformal maps (Euclidean, spherical, hyperbolic) | General mesh processing |
| Newton solver for angle-sum energy functionals | Remeshing / boolean / smoothing |
| Priority-BFS layout into ℝ², S², Poincaré disk | Point-cloud or implicit-field processing |
| Holonomy, period matrices, fundamental domains | NURBS / parametric modelling |
| Research platform — experiment-first, CLI-ready | Production rendering engine |

**Relation to existing libraries.**  
conformallab++ sits *on top of* CGAL, not beside it.
`CGAL::Surface_mesh` is the internal mesh representation; there is no additional
abstraction layer. Eigen handles all linear algebra. libigl provides the optional
interactive viewer. The library adds the conformal-geometry layer that none of these
provide.

---

## The conformal geometry pipeline

The full pipeline runs in three stages. All stages operate on the same
`ConformalMesh` (= `CGAL::Surface_mesh<Point3>` with attached property maps).

```mermaid
graph TD
    IN["Input\n(OFF / OBJ / PLY / builder)"]
    ADAPTER["Input Adapter\nload_mesh() · make_triangle() · make_quad_strip() …"]

    subgraph PRE["① PREPROCESSING"]
        MAPS["Maps Setup\nsetup_*_maps() + compute_lambda0_from_mesh()"]
        DOF["DOF Assignment\nv_idx[v] · e_idx[e] · pin / free"]
        THETA["Target Angles\ntheta_v[v] — cone metric or natural equilibrium"]
        GB["Gauss–Bonnet Check\ncheck_gauss_bonnet() / enforce_gauss_bonnet()"]
        MAPS --> DOF --> THETA --> GB
    end

    subgraph CORE["② PROCESSING CORE"]
        NEWTON["Newton Solver\nnewton_euclidean/spherical/hyper_ideal()\n→ NewtonResult  x* ∈ ℝⁿ"]
        CG["Cut Graph  [closed surfaces]\ncompute_cut_graph()\n→ CutGraph  (2g seam edges)"]
        LAYOUT["Layout / Embedding\neuclidean/spherical/hyper_ideal_layout()\n→ Layout2D / Layout3D\n  · uv[v]  · halfedge_uv[h]\n  · HolonomyData"]
        NORM["Normalisation\nnormalise_euclidean / _hyperbolic / _spherical()"]
        PERIOD["Period Matrix  [genus 1]\ncompute_period_matrix()\n→ PeriodData  τ ∈ ℍ"]
        FD["Fundamental Domain\ncompute_fundamental_domain()\n→ FundamentalDomain + tiling"]
        NEWTON --> CG --> LAYOUT --> NORM --> PERIOD --> FD
    end

    subgraph POST["③ POSTPROCESSING"]
        SERIAL["Serialisation\nsave_result_json/xml()\nsave_layout_off()"]
        VIZ["Visualisation\nexample_viewer  (libigl / GLFW)"]
        CLI["CLI App\nconformallab_core  -i -g -o -j -x -s"]
    end

    IN --> ADAPTER --> PRE
    PRE --> CORE
    CORE --> POST

    style PRE  fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#000
    style CORE fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#000
    style POST fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#000
```

---

## Stage ① — Preprocessing

### Input adapter

All mesh input flows through a single entry point:

```cpp
ConformalMesh mesh = load_mesh("input.off");   // OFF · OBJ · PLY
ConformalMesh mesh = make_quad_strip();         // built-in test meshes
```

**Precondition guarantee:** the adapter ensures the mesh is a valid, orientable,
triangulated surface with consistent halfedge structure (CGAL validity).
Downstream stages never check for degeneracies — they trust the adapter.

### Maps setup

Each geometry mode has its own maps struct that attaches property maps to the mesh:

| Geometry | Setup function | Key property maps |
|----------|---------------|-------------------|
| Euclidean (ℝ²) | `setup_euclidean_maps()` | `lambda0[e]`, `theta_v[v]`, `v_idx[v]` |
| Spherical (S²) | `setup_spherical_maps()` | `lambda0[e]`, `theta_v[v]`, `v_idx[v]` |
| Hyper-ideal (H²) | `setup_hyper_ideal_maps()` | `beta_v[v]`, `alpha_e[e]`, `v_idx[v]`, `e_idx[e]` |

`compute_*_lambda0_from_mesh()` initialises log-edge-lengths from the 3-D vertex
positions. After this step the solver works entirely in scale-factor space; the
original vertex positions are no longer needed.

### DOF assignment & target angles

```cpp
// Pin first vertex (gauge fix for open meshes)
maps.v_idx[*mesh.vertices().begin()] = -1;
int idx = 0;
for (auto v : rest_of_vertices) maps.v_idx[v] = idx++;

// Set target curvature (cone metric or natural equilibrium)
maps.theta_v[v] = 2 * M_PI;          // flat interior vertex
maps.theta_v[v] = M_PI / 3;          // 60° cone singularity
```

**Natural equilibrium shortcut:** evaluate the gradient at `x = 0`, subtract it from
`theta_v` — the solver then converges to `x* = 0` identically (useful for tests).

### Gauss–Bonnet check

Before solving, the prescribed angles must satisfy:

> ∑ᵥ (2π − Θᵥ) = 2π · χ(M)

```cpp
check_gauss_bonnet(mesh, maps);         // throws if violated
enforce_gauss_bonnet(mesh, maps);       // distributes residual uniformly
```

**This is the most common source of silent Newton non-convergence.**
Prescribing angles that violate Gauss–Bonnet means no conformal factor exists —
the solver will iterate without converging.

---

## Stage ② — Processing core

### The three geometry modes

conformallab++ implements discrete conformal geometry in three model spaces:

| Mode | Space | Curvature | Typical surfaces |
|------|-------|-----------|-----------------|
| **Euclidean** | ℝ² | K = 0 | Flat tori, developable surfaces, open patches |
| **Spherical** | S² | K = +1 | Genus-0 (sphere-like) surfaces |
| **Hyper-ideal** | H² (Poincaré disk) | K = −1 | Genus-g surfaces (g ≥ 1), hyperbolic structures |

All three share the same algorithmic structure — only the angle formula, the
trilateration geometry, and the Hessian sign differ.

### Newton solver

```
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])
```

Each iteration:
1. Evaluate gradient **G** (angle-sum defect per vertex)
2. Evaluate Hessian **H** (analytical for Euclidean/Spherical; symmetric FD for HyperIdeal)
3. Solve **H·Δx = −G** — try `SimplicialLDLT`, fall back to `SparseQR` on rank deficiency
4. Backtracking line search (up to 20 halvings)

**Preconditions:** mesh triangulated + manifold · Gauss–Bonnet satisfied · DOFs assigned  
**Provides:** `NewtonResult.x` — converged scale factors; `converged`, `iterations`, `grad_inf_norm`

**SparseQR fallback** handles gauge modes on closed meshes without a pinned vertex.
The fallback is public API: `solve_linear_system(H, rhs, &used_fallback)`.

### Cut graph (closed surfaces)

For a closed genus-g surface, cutting along 2g independent homology cycles
turns it into a topological disk — a prerequisite for globally consistent BFS layout.

```cpp
CutGraph cg = compute_cut_graph(mesh);
// cg.cut_edge_flags[e.idx()] — true for seam edges
// cg.cut_edge_indices         — ordered list of 2g seam edges
// cg.genus                    — g
```

**Algorithm:** tree-cotree decomposition (Erickson–Whittlesey 2005).  
**Preconditions:** closed, orientable, triangulated mesh  
**Provides:** exactly `2g` seam edges whose removal makes the surface simply connected

### Layout / Embedding

BFS-trilateration unfolds the mesh into the target geometry.
The root face (largest 3-D area, 1.5× interior bonus) is placed first;
all other faces are placed in **priority order by BFS depth** (min-heap),
minimising trilateration error accumulation.

```cpp
HolonomyData hol;
Layout2D layout = euclidean_layout(mesh, result.x, maps, &cg, &hol, /*normalise=*/true);
Layout3D slayout = spherical_layout(mesh, result.x, smaps);
Layout2D hlayout = hyper_ideal_layout(mesh, result.x, hmaps, &cg, &hol);
```

**Key outputs:**

| Field | Content |
|-------|---------|
| `layout.uv[v.idx()]` | Primary UV — first / shallowest-BFS visit |
| `layout.halfedge_uv[h.idx()]` | UV of `source(h)` as seen from `face(h)` — seam-aware |
| `layout.has_seam` | True when a vertex was reached via two different paths |
| `hol.translations[i]` | Translation ω_i across cut edge i (Euclidean / spherical) |
| `hol.mobius_maps[i]` | Möbius isometry T_i ∈ SU(1,1) across cut edge i (hyperbolic) |

**`halfedge_uv` — texture atlas semantics:**  
At a seam edge the two opposite halfedges carry *different* UV values.
This gives each face its own UV copy of a seam vertex, enabling
a proper GPU texture atlas without vertex duplication.

**Trilateration — geometry by mode:**

| Mode | Method | Accuracy |
|------|--------|---------|
| Euclidean | Analytic formula in ℝ² | Exact |
| Spherical | Spherical law of cosines on S² | Exact |
| Hyper-ideal | Möbius + hyperbolic law of cosines in Poincaré disk | Exact |

**Preconditions:** `NewtonResult.converged` · mesh · maps · optional `CutGraph`  
**Provides:** `Layout2D/3D` with `uv`, `halfedge_uv` · `HolonomyData` with translations / Möbius maps

### Möbius maps

`MobiusMap` (T(z) = (az+b)/(cz+d)) is the central algebraic object for hyperbolic geometry:

```cpp
MobiusMap T = MobiusMap::from_three(z1,w1, z2,w2, z3,w3); // fit to 3 correspondences
MobiusMap S = T.inverse().compose(U);                       // group operations
Eigen::Vector2d p2 = T.apply(p);                           // apply to 2-D point
```

Used for: hyperbolic trilateration · holonomy tracking · normalisation centering.

### Normalisation

After layout, a canonical post-processing step brings the result into a standard position:

| Mode | Method | Effect |
|------|--------|--------|
| Euclidean | PCA — centroid → origin, major axis → x-axis | Translation + rotation |
| Hyperbolic | Weighted Möbius centering (Fréchet mean, 30 iterations) | Maps centroid to disk origin |
| Spherical | Rodrigues rotation | Maps centroid to north pole |

Both `uv` and `halfedge_uv` are transformed identically.

### Period matrix (genus 1)

From the two holonomy translations ω₁, ω₂ ∈ ℂ read off from the cut graph,
the conformal type of a flat torus is the SL(2,ℤ)-orbit of:

> τ = ω₂ / ω₁ ∈ ℍ

```cpp
PeriodData pd = compute_period_matrix(hol);
// pd.tau                   — complex period ratio
// pd.omega[i]              — lattice generators as complex numbers
// pd.in_fundamental_domain — after SL(2,ℤ) reduction
// pd.genus()               — g = omega.size() / 2
```

SL(2,ℤ) reduction alternates T: τ↦τ+1 and S: τ↦−1/τ steps until
τ ∈ F = {|τ| ≥ 1, −½ ≤ Re(τ) < ½}.

**Note:** The Siegel period matrix Ω ∈ H_g for genus g ≥ 2 requires integrating
holomorphic differentials — deferred to Phase 8.

### Fundamental domain

```cpp
FundamentalDomain fd = compute_fundamental_domain(hol);
// genus 1: CCW parallelogram {0, ω₁, ω₁+ω₂, ω₂}
// genus g > 1: empty — 4g-polygon boundary walk deferred to Phase 8

auto tiles = tiling_neighbourhood(layout, hol, /*m_max=*/2, /*n_max=*/2);
// returns (2·m_max+1)·(2·n_max+1) translated copies of the layout
// for visualising the universal cover
```

---

## Stage ③ — Postprocessing

### Serialisation

```cpp
// Layout as mesh file
save_layout_off("layout.off", mesh, layout);

// Full result: DOF vector + metadata + layout UVs
save_result_json("result.json", result, "euclidean", V, F, &layout);
save_result_xml ("result.xml",  result, "euclidean", V, F, &layout);

// Round-trip load
NewtonResult res2; std::string geom; Layout2D uv2;
load_result_json("result.json", &res2, &geom, &uv2);
```

### CLI app

```bash
conformallab_core \
  -i input.off        # input mesh
  -g euclidean        # geometry: euclidean | spherical | hyper_ideal
  -o layout.off       # layout output
  -j result.json      # JSON serialisation
  -x result.xml       # XML serialisation
  -s                  # show input in viewer
  -v                  # verbose solver output
```

### Interactive viewer

`example_viewer` (libigl / GLFW) shows the 3-D mesh and the 2-D layout
side-by-side. Built automatically with `-DWITH_CGAL=ON`.

---

## Processing unit contracts

Each stage has explicit preconditions and guarantees.
A pipeline is valid if every unit's preconditions are satisfied
by the outputs of all preceding units.

| Unit | Preconditions | Provides |
|------|--------------|---------|
| `load_mesh` | Valid file path, supported format | Manifold, oriented, triangulated `ConformalMesh` |
| `setup_*_maps` | Triangulated mesh | Initialised property maps; `lambda0` from 3-D positions |
| `check_gauss_bonnet` | `theta_v` set | Throws if Σ(2π−Θ_v) ≠ 2π·χ |
| `enforce_gauss_bonnet` | `theta_v` set | Σ(2π−Θ_v) = 2π·χ guaranteed |
| `newton_*` | GB satisfied · DOFs assigned | `NewtonResult.converged` · `x*` · gradient norm |
| `compute_cut_graph` | Closed, orientable mesh | `2g` seam edges · `CutGraph.genus` |
| `euclidean_layout` | `newton_euclidean` converged | `uv[v]` · `halfedge_uv[h]` · `HolonomyData` |
| `normalise_euclidean` | `layout.success == true` | Centroid at origin · major axis = x-axis |
| `compute_period_matrix` | `hol.translations.size() >= 2` | `τ ∈ ℍ` · optionally reduced to F |
| `compute_fundamental_domain` | `HolonomyData` (genus 1) | CCW parallelogram · edge identifications |

---

## The three geometry modes in detail

```
              Euclidean            Spherical            Hyper-ideal
              ─────────────────────────────────────────────────────
Space         ℝ²                   S²                   H² (Poincaré disk)
Curvature     K = 0                K = +1               K = −1
Genus         any (cone metrics)   0                    ≥ 1
Angle sum     Σα_v = Θ_v           Σα_v = Θ_v           Σβ_v = Θ_v
Hessian       PSD (cotangent-Lap.) NSD (sign-flip)      PSD (FD, strict conv.)
Holonomy      translations ω_i     rotations (2-D)      Möbius maps T_i ∈ SU(1,1)
Period        τ = ω₂/ω₁ ∈ ℍ        —                    axis of T_i
Normalise     PCA centring         Rodrigues to N pole  weighted Möbius centring
```

---

## Further documentation

| Topic | Document |
|---|---|
| Public headers (all 24, with descriptions) | [api/headers.md](../api/headers.md) |
| Test suites (26 suites, 158 tests, individual counts) | [api/tests.md](../api/tests.md) |
| Extending the library (new functionals, geometry modes, Java porting guide) | [api/extending.md](../api/extending.md) |
| Processing unit contracts (preconditions / provides table) | [api/contracts.md](../api/contracts.md) |
| Phase 8 CGAL package design + declarative pipeline YAML | [api/cgal-package.md](../api/cgal-package.md) |
| Key design decisions + rationale | [architecture/design-decisions.md](design-decisions.md) |
| Project structure (directory tree + build targets) | [architecture/project-structure.md](project-structure.md) |
| Three geometry modes in detail | [math/geometry-modes.md](../math/geometry-modes.md) |
| References and papers | [math/references.md](../math/references.md) |
| Development roadmap (Phases 1–10) | [roadmap/phases.md](../roadmap/phases.md) |
| Java vs. C++ feature parity table | [roadmap/java-parity.md](../roadmap/java-parity.md) |
