docs(architecture): overall_pipeline.md vollständig neu geschrieben
All checks were successful
C++ Tests / test-fast (push) Successful in 2m12s
C++ Tests / test-cgal (push) Has been skipped

Ersetzt den generischen Geometry-Framework-Entwurf durch eine präzise
Beschreibung der tatsächlichen konformen Geometrie-Pipeline:

- Klare Positionierung: spezialisiertes Werkzeug für diskrete konforme
  Abbildungen, kein generisches Mesh-Processing-Framework
- Korrigiertes Mermaid-Diagramm: alle 3 Phasen mit realen Komponenten
  (load_mesh → setup_maps → GB-check → Newton → CutGraph → Layout →
   halfedge_uv → Holonomie → Periodenmatrix → Fundamentalbereich → Export)
- Preconditions/Capabilities-Tabelle für alle Processing-Units
- Drei Geometrie-Modi (Euklidisch/Sphärisch/Hyper-ideal) im Vergleich
- MobiusMap, halfedge_uv, Priority-BFS, SL(2,ℤ)-Reduktion dokumentiert
- Realistischer YAML-Pipeline-Entwurf als Phase-8-Ziel (Tokens statt Prosa)
- Erweiterungspunkte: neues Funktional, neue Geometrie, neues Unit
- Alle Literaturverweise direkt auf Implementierungsstellen gemappt
- "Nice To Have but maybe too much" komplett entfernt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-14 12:19:59 +02:00
parent a4438c9a1a
commit 9c61c9fc40

View File

@@ -1,251 +1,456 @@
# Draft High-level Architecture # conformallab++ — Architecture & Pipeline
## Positioning
## Positioning and relation to existing libraries (Nice To Have but maybe to much) conformallab++ is a **specialised research library for discrete conformal geometry** on
ConformalLab++ is not intended to replace established geometry processing libraries such as CGAL, libigl, or Geometry Central. Instead, it acts as an experiment-friendly pipeline layer on top of existing data structures and algorithms provided by these libraries. triangulated surfaces. It is not a general geometry-processing framework.
The project revives the ideas and algorithms of the original ConformalLab by Stefan Sechelmann in a modern C++ setting, making them easier to combine with contemporary mesh libraries (e.g. Geometry Central for intrinsic quantities and operators) and to reuse in new experimental setups. 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:
In practice, ConformalLab++ focuses on: > Given a triangulated surface, find a conformally equivalent metric that satisfies
> prescribed curvature (angle-sum) constraints at each vertex.
+ a clear, declarative pipeline description for conformal and Möbius-based mesh experiments, Everything in the library serves this goal:
+ a unified halfedge-based UniversalMesh representation plus optional alternative representations, | 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 |
adapters and plugin units that wrap robust external implementations where appropriate (e.g. CGAL-based repair or reconstruction, Geometry Centralstyle discrete geometry operators, or libigl functionality) instead of reimplementing them. **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
## High-level geometry pipeline The full pipeline runs in three stages. All stages operate on the same
The following diagram outlines the planned high-level pipeline for ConformalLab++. It shows the rough division into preprocessing, processing, and postprocessing. In preprocessing, various input types are first converted into a universal representation (half-edge mesh) via adapter layers and any necessary preprocessing steps. From there, they are processed by the various core processors units and then either exported or visualized in postprocessing, with minor optimizations as necessary. `ConformalMesh` (= `CGAL::Surface_mesh<Point3>` with attached property maps).
```mermaid ```mermaid
graph TD graph TD
A1["Explicit<br/>(OBJ, PLY, STL)"] IN["Input\n(OFF / OBJ / PLY / builder)"]
A2["Implicit<br/>(SDF, Formulas)"] ADAPTER["Input Adapter\nload_mesh() · make_triangle() · make_quad_strip() …"]
A3["Parametric<br/>(NURBS, Curves)"]
A4["Procedural<br/>(Noise, L-Sys)"] subgraph PRE["① PREPROCESSING"]
ADAPTER1["Input Adapter <br/>(Convert to Universal Represenation)"] MAPS["Maps Setup\nsetup_*_maps() + compute_lambda0_from_mesh()"]
PREPROCESSING["Preprocessor <br/>(refined triangulation etc.)"] DOF["DOF Assignment\nv_idx[v] · e_idx[e] · pin / free"]
EXT["EXTERNAL LIBRARIES<br/>(Plugin System)<br/>- CGAL"] THETA["Target Angles\ntheta_v[v] — cone metric or natural equilibrium"]
UNIVERSAL["UNIVERSAL REPRESENTATION FORM<br/>(HalfEdge Mesh Interface)"] GB["GaussBonnet Check\ncheck_gauss_bonnet() / enforce_gauss_bonnet()"]
EXPORT_ADAPTER["Export Adapter Layer"] MAPS --> DOF --> THETA --> GB
PROCESSING["PROCESSING CORE LAYER"] end
OPT["Optimization<br/>- Decimation<br/>- Denoising<br/>- Remeshing"]
ALGO["Algorithmic<br/>- Boolean Ops<br/>- Smoothing<br/>- Hole Filling"] subgraph CORE["② PROCESSING CORE"]
OTHER["Other<br/>- Custom Algos<br/>- Transforms<br/>- Filters"] NEWTON["Newton Solver\nnewton_euclidean/spherical/hyper_ideal()\n→ NewtonResult x* ∈ ℝⁿ"]
POSTPROCESSING["POSTPROCESSING <br/>(refined triangulation etc.)"] CG["Cut Graph [closed surfaces]\ncompute_cut_graph()\n→ CutGraph (2g seam edges)"]
EXPORT["Export Formats<br/>- OBJ<br/>- PLY<br/>- STL<br/>- Custom"] LAYOUT["Layout / Embedding\neuclidean/spherical/hyper_ideal_layout()\n→ Layout2D / Layout3D\n · uv[v] · halfedge_uv[h]\n · HolonomyData"]
VIZ["Visualization<br/>- OpenGL<br/>- QT<br/>- Screenshot"] NORM["Normalisation\nnormalise_euclidean / _hyperbolic / _spherical()"]
subgraph PRE[PREPROCESSING] PERIOD["Period Matrix [genus 1]\ncompute_period_matrix()\n→ PeriodData τ ∈ "]
A1 --> ADAPTER1 FD["Fundamental Domain\ncompute_fundamental_domain()\n→ FundamentalDomain + tiling"]
A2 --> ADAPTER1 NEWTON --> CG --> LAYOUT --> NORM --> PERIOD --> FD
A3 --> ADAPTER1 end
A4 --> ADAPTER1
PREPROCESSING <-->ADAPTER1 subgraph POST["③ POSTPROCESSING"]
end SERIAL["Serialisation\nsave_result_json/xml()\nsave_layout_off()"]
EXT <--> UNIVERSAL VIZ["Visualisation\nexample_viewer (libigl / GLFW)"]
UNIVERSAL --> PROCESSING CLI["CLI App\nconformallab_core -i -g -o -j -x -s"]
UNIVERSAL --> EXPORT_ADAPTER end
ADAPTER1 <--> EXT
ADAPTER1 <--> UNIVERSAL IN --> ADAPTER --> PRE
OPT --> UNIVERSAL PRE --> CORE
ALGO --> UNIVERSAL CORE --> POST
OTHER --> UNIVERSAL
subgraph CORE[PROCESSING ] style PRE fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#000
PROCESSING --> OPT style CORE fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#000
PROCESSING --> ALGO style POST fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#000
PROCESSING --> OTHER
end
subgraph POST[POSTPROCESSING]
EXPORT_ADAPTER <--> POSTPROCESSING
EXPORT_ADAPTER --> EXPORT
EXPORT_ADAPTER --> VIZ
end
style UNIVERSAL fill:#90EE90,stroke:#000,stroke-width:3px,color:#000
style PREPROCESSING fill:#87CEEB,stroke:#000,stroke-width:2px,color:#000
style POSTPROCESSING fill:#87CEEB,stroke:#000,stroke-width:2px,color:#000
style PROCESSING fill:#87CEEB,stroke:#000,stroke-width:2px,color:#000
style EXT fill:#DDA0DD,stroke:#000,stroke-width:2px,color:#000
style ADAPTER1 fill:#FFB347,stroke:#000,stroke-width:2px,color:#000
style EXPORT_ADAPTER fill:#FFB347,stroke:#000,stroke-width:2px,color:#000
``` ```
All external inputs (file-based, implicit, parametric, procedural) are first converted by an spezfic input adapter into a single universal interface, which is the canonical internal representation used by the processing stages. The processing core units operates on this universal representation and can use external libraries via a plugin-like extension, while results are passed through an export adapter to file formats or visualization frontends.
---
## Three-stage processing pipeline ## Stage ① — Preprocessing
The geometry processing pipeline in ConformalLab++ is structured into three main stages, all operating on the same universal mesh representation.
### Preprocessing ### Input adapter
Converts all external inputs (files, analytic models, procedural sources) into the canonical half-edge mesh form.
Performs optional cleaning and normalization (e.g. fixing degeneracies, rescaling, enforcing orientation). All mesh input flows through a single entry point:
Guarantees that downstream stages see a well-formed, consistent mesh (or fail early with clear errors). ```cpp
ConformalMesh mesh = load_mesh("input.off"); // OFF · OBJ · PLY
ConformalMesh mesh = make_quad_strip(); // built-in test meshes
```
### Processing (core processing units) **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.
Runs one or more core processing units in sequence on the canonical mesh. ### Maps setup
Each unit takes a mesh of the same type as input and produces a mesh of the same type as output (possibly with enriched attributes). Each geometry mode has its own maps struct that attaches property maps to the mesh:
Examples: remeshing, conformal parameterization, smoothing, boolean operations, experiment-specific transforms. | 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]` |
Each core processing unit conceptually has the shape UniversalMesh -> UniversalMesh, but units also declare: `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.
+ Preconditions: requirements on the input mesh (e.g. triangulated, manifold, oriented, specific attributes present). ### DOF assignment & target angles
+ Capabilities: guarantees on the output mesh (e.g. remains manifold, produces curvature attributes, preserves boundary). ```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++;
A simple example: // 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
```
+ RemeshingUnit **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).
+ Preconditions: triangulated, manifold, oriented ### GaussBonnet check
+ Capabilities: triangulated, manifold, vertex positions modified, edge count changed Before solving, the prescribed angles must satisfy:
Pipeline composition is valid if, for every adjacent pair of units, the capabilities of the previous unit satisfy the preconditions of the next unit. $$\sum_{v} (2\pi - \Theta_v) = 2\pi \cdot \chi(M)$$
```cpp
check_gauss_bonnet(mesh, maps); // throws if violated
enforce_gauss_bonnet(mesh, maps); // distributes residual uniformly
```
### Postprocessing **This is the most common source of silent Newton non-convergence.**
Prescribing angles that violate GaussBonnet means no conformal factor exists —
the solver will iterate without converging.
Adapts the processed mesh for external consumption: export to file formats, visualization, analysis tools. ---
May attach or convert attribute data (e.g. colors, scalar fields, experiment results) into a format suitable for rendering or further tools. ## Stage ② — Processing core
Does not change the core topology or semantics of the processed mesh, only its representation for the outside world. ### The three geometry modes
This structure makes it easy to reason about where data enters, is modified, and leaves the system, and keeps experimental algorithms clearly separated from IO concerns. conformallab++ implements discrete conformal geometry in three model spaces:
## Role of the universal mesh representation | Mode | Space | Curvature | Typical surfaces |
The universal mesh representation (a half-edge mesh interface) is the shared contract between all pipeline stages. |------|-------|-----------|-----------------|
| **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 |
### Single input/output type All three share the same algorithmic structure — only the angle formula, the
Every core processing unit exposes the same function shape conceptually: trilateration geometry, and the Hessian sign differ.
UniversalMesh → UniversalMesh.
This allows units to be chained in arbitrary order as long as their preconditions on the mesh are satisfied.
### Local, topology-aware access ### Newton solver
The half-edge structure encodes vertices, halfedges, faces, and their adjacency relations, which is essential for typical geometry operations (e.g. local neighborhood queries, traversal, edge flips).
Algorithms do not need to know where the mesh came from (file vs. analytic vs. procedural); they only rely on this uniform interface. ```
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])
```
### Extensibility via attributes Each iteration:
The universal mesh may carry extensible per-vertex, per-edge, and per-face attributes (e.g. UVs, curvature, experimental scalar fields). 1. Evaluate gradient **G** (angle-sum defect per vertex)
Core units can read and write these attributes while still conforming to the same mesh type, enabling complex pipelines without changing the central data structure. 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 · GaussBonnet 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)`.
Because all processing units speak the same “mesh language”, you can build pipelines like: ### 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 (EricksonWhittlesey 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:
$$\tau = \omega_2 / \omega_1 \in \mathbb{H}$$
```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 ```bash
Preprocessing conformallab_core \
-> RemeshingUnit -i input.off # input mesh
-> ConformalMapUnit -g euclidean # geometry: euclidean | spherical | hyper_ideal
-> ExperimentSpecificFilter -o layout.off # layout output
-> Export/Postprocessing -j result.json # JSON serialisation
-x result.xml # XML serialisation
-s # show input in viewer
-v # verbose solver output
``` ```
without changing the basic function signature or the surrounding infrastructure, only by reordering or swapping units.
### Interactive viewer
## Declarative pipeline descriptions (Nice To Have but maybe to much) `example_viewer` (libigl / GLFW) shows the 3-D mesh and the 2-D layout
ConformalLab++ optionally supports a lightweight YAML-based description format for experiments. side-by-side. Built automatically with `-DWITH_CGAL=ON`.
A pipeline consists of an input adapter, a sequence of steps (core processing units), and an output adapter. Each step can specify params as well as structural constraints via require and provide keys. A pipeline is considered valid if, for every step, all require conditions are satisfied by the accumulated provide and expect constraints of previous steps
---
## 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
```
---
## Extension points
### Adding a new functional
1. Create `my_functional.hpp` with a `Maps` struct and `evaluate_my_functional()`.
2. The gradient must satisfy: `G_v = Σ(angle contributions) theta_v[v]`.
3. Verify with a finite-difference gradient check (copy any `GradientCheck_*` test).
4. Pass to `solve_linear_system(H, -G)` or write a thin `newton_my` wrapper.
### Adding a new geometry mode
Implement `trilaterate_my()` with the correct local placement formula,
then follow the same BFS structure as `euclidean_layout` (see `layout.hpp`).
The priority BFS, holonomy tracking, and `halfedge_uv` population are geometry-agnostic.
### Adding a new processing unit
Declare its preconditions and capabilities explicitly (see table above).
The pipeline validation is currently manual (documented contracts); a
compile-time or runtime check is a natural Phase 8 extension.
---
## Declarative pipeline (target for Phase 8)
A lightweight YAML description for reproducible experiments:
```yaml ```yaml
pipeline: pipeline:
name: basic_conformal name: flat_torus_period
geometry: euclidean
input: input:
adapter: obj_reader source: data/torus.off
source: data/bunny.obj
steps: steps:
- id: clean - id: setup
unit: repair_soft_clean unit: setup_euclidean_maps
params: provide: [maps_initialised]
mode: soft
require:
representation: UniversalMesh
triangulated: true
provide:
triangulated: true
manifold: null
- id: param - id: gauss_bonnet
unit: conformal_parameterization unit: enforce_gauss_bonnet
require: [maps_initialised]
provide: [gauss_bonnet_satisfied]
- id: solve
unit: newton_euclidean
require: [gauss_bonnet_satisfied]
params: params:
method: cauchy_riemann tol: 1.0e-10
require: max_iter: 200
triangulated: true provide: [x_converged]
manifold: true
provide: - id: cut
attributes_add: [uv] unit: compute_cut_graph
require: [mesh_closed]
provide: [cut_graph]
- id: layout
unit: euclidean_layout
require: [x_converged, cut_graph]
params:
normalise: true
provide: [layout_uv, holonomy]
- id: period
unit: compute_period_matrix
require: [holonomy]
provide: [tau]
output: output:
adapter: gltf_writer layout: out/torus_layout.off
target: out/bunny.glb json: out/torus_result.json
tau: out/torus_tau.txt
``` ```
Each unit may declare a require section describing what it needs from its input (e.g. representation, triangulated, manifold, required attributes) and a provide section describing what it guarantees on its output (e.g. still triangulated, now manifold, adds a uv attribute). Pipelines are considered valid if for every step, the accumulated provide information of all previous steps satisfies the require constraints of the next step.
### Repair Units (Nice To Have but maybe to much) Each unit declares `require` (preconditions) and `provide` (capabilities).
A pipeline is valid if for every step, all `require` keys are satisfied by the
accumulated `provide` set of all preceding steps.
This mirrors exactly the contract table in this document.
Before entering the core conformal pipeline, input meshes are passed through an explicit preprocessing stage. This stage is responsible for turning “real-world” polygon data (often noisy, inconsistent, or nonmanifold) into a mesh that satisfies the structural requirements of the subsequent units ---
ConformalLab++ provides a small set of repair units that can be combined as needed:
+ removal of (almost) degenerate triangles (needles, caps),
+ stitching of compatible boundary cycles to close small gaps,
+ enforcing a consistent face orientation where possible.
These units are conceptually similar to geometric repair routines in modern polygon mesh processing toolkits
We distinguish between two typical modes of operation:
+ Soft cleaning: minimally invasive repairs intended for visualization and quick experiments. Soft cleaning tries to improve mesh quality without significantly altering topology or removing components.
+ Hard cleaning: more aggressive repairs intended for numerically robust experiments. Hard cleaning may merge vertices, remove tiny components, and modify local topology to enforce manifoldness and consistent orientation when possible.
## More internal representations (Nice To Have but maybe to much)
ConformalLab++ distinguishes between internal representation spaces that experiments may move between:
+ UniversalMesh: a halfedgebased surface mesh representation used for most combinatorial and conformal algorithms.
+ PointCloud: an unstructured set of sample points, optionally with normals and additional attributes, used for sampling, resampling, and certain reconstruction tasks.
+ ImplicitField: a scalar field $f: \mathbf{R}^n \rightarrow \mathbf{R}$
represented on a grid or as a procedural function, used for isosurface extraction and robust shape transformations.
Each space has its own natural algorithms, and dedicated conversion units allow pipelines to move between these representations when needed.
### Conversion units
Typical conversion units include:
+ mesh_to_pointcloud (sampling vertices and surfaces of a UniversalMesh into a PointCloud),
+ pointcloud_to_mesh (surface reconstruction from point samples),
+ mesh_to_implicit (rasterizing a UniversalMesh into a signed distance or occupancy field),
+ implicit_to_mesh (isosurface extraction from an ImplicitField).
These units are modeled as regular processing units in the pipeline but change the underlying representation space instead of just transforming a mesh
## Attribute behaviour under topology changes (Nice To Have but maybe to much)
ConformalLab++ treats attributes as first-class data attached to mesh entities (vertices, edges, faces, halfedges). When topology changes, attributes are updated according to simple, explicit rules:
+ Vertex splits / edge splits: attributes on newly created vertices or edges are initialized by interpolation of the incident elements (e.g. linear interpolation of scalar fields along an edge).
+ Edge collapse: attributes on the surviving vertex are computed from the incident vertices, typically by a convex combination or areaweighted average for scalar quantities.
+ Face operations (flip, subdivision): face attributes are either propagated (for categorical data) or interpolated (for scalar data) to new faces.
By default, ConformalLab++ uses linear interpolation for scalar attributes and nearestneighbour propagation for discrete or categorical attributes; units may override these policies where necessary
## Recommended reading
| Source | Relevance in conformallab++ |
|--------|----------------------------|
| Springborn — *Ideal Hyperbolic Polyhedra and Discrete Uniformization* (2020) | HyperIdeal functional; ζ₁₃/ζ₁₄/ζ₁₅ in `hyper_ideal_geometry.hpp` |
| Pinkall, Polthier — *Computing Discrete Minimal Surfaces* (1993) | Cotangent-Laplace Hessian in `euclidean_hessian.hpp` |
| Bobenko, Springborn — *Variational Principles for Circle Patterns* (2004) | Angle-sum variational framework used throughout |
| Luo — *Combinatorial Yamabe Flow on Surfaces* (2004) | Inversive-distance functional (not yet ported) |
| Erickson, Whittlesey — *Greedy Optimal Homotopy Generators* (SODA 2005) | Tree-cotree algorithm in `cut_graph.hpp` |