README.md: reduced from 703 to ~75 lines — what/why, status, quick start, minimal usage example, navigation table to doc/ files. doc/architecture/overall_pipeline.md: trimmed — roadmap, extension points, declarative pipeline YAML, and references sections removed (each now has its own dedicated file). Replaced with a link table. New files: doc/getting-started.md — build modes, single-test invocation, CLI doc/api/pipeline.md — full pipeline API with code for all 3 geometries doc/api/extending.md — new functionals, geometry modes, Java porting guide doc/api/contracts.md — processing unit preconditions/provides table doc/api/cgal-package.md — Phase 8 CGAL package design + YAML pipeline (TODO) doc/math/geometry-modes.md — Euclidean/Spherical/HyperIdeal comparison doc/math/references.md — all papers by module doc/roadmap/phases.md — Phases 1–10 with porting/research boundary doc/roadmap/java-parity.md — Java vs C++ feature parity table doc/contributing.md — language policy, test standards, release flow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.5 KiB
3.5 KiB
Processing Unit Contracts
Each pipeline unit has explicit preconditions and guarantees. A pipeline is valid if every unit's preconditions are satisfied by the outputs of all preceding units.
Contract table
| Unit | Preconditions | Provides |
|---|---|---|
load_mesh() |
Valid file path, supported format (OFF/OBJ/PLY) | Manifold, oriented, triangulated ConformalMesh |
setup_*_maps() |
Triangulated mesh | Initialised property maps; lambda0 zeroed |
compute_*_lambda0_from_mesh() |
setup_*_maps() called |
lambda0[e] set from 3-D edge lengths |
check_gauss_bonnet() |
theta_v[v] set for all vertices |
Throws std::runtime_error if Σ(2π−Θᵥ) ≠ 2π·χ(M) |
enforce_gauss_bonnet() |
theta_v[v] set |
Σ(2π−Θᵥ) = 2π·χ(M) guaranteed; theta_v modified |
newton_euclidean() |
GB satisfied · DOFs assigned · lambda0 initialised |
NewtonResult.x — converged scale factors; .converged, .iterations, .grad_inf_norm |
newton_spherical() |
GB satisfied · DOFs assigned · gauge vertex pinned | Same as above |
newton_hyper_ideal() |
assign_all_dof_indices() called · lambda0 initialised |
Same as above |
compute_cut_graph() |
Closed, orientable, triangulated mesh | CutGraph.cut_edge_flags — 2g seam edges; .genus |
euclidean_layout() |
newton_euclidean() converged |
Layout2D.uv[v] · .halfedge_uv[h] · HolonomyData.translations |
spherical_layout() |
newton_spherical() converged |
Layout3D.xyz[v] |
hyper_ideal_layout() |
newton_hyper_ideal() converged |
Layout2D.uv[v] · .halfedge_uv[h] · HolonomyData.mobius_maps |
normalise_euclidean() |
layout.success == true |
Centroid at origin, major axis = x-axis |
normalise_hyperbolic() |
layout.success == true |
Weighted centroid at Poincaré disk origin |
normalise_spherical() |
layout.success == true |
Area centroid at north pole |
compute_period_matrix() |
hol.translations.size() >= 2 |
PeriodData.tau ∈ ℍ; optionally SL(2,ℤ)-reduced |
compute_fundamental_domain() |
HolonomyData (genus 1) |
CCW parallelogram {0, ω₁, ω₁+ω₂, ω₂} + edge identifications |
tiling_neighbourhood() |
Layout2D + HolonomyData (genus 1) |
(2m+1)·(2n+1) translated layout copies |
save_layout_off() |
Layout2D + mesh |
OFF file with UV coordinates |
save_result_json/xml() |
NewtonResult + optional Layout2D |
JSON/XML serialised result |
DOF assignment conventions
// Euclidean / Spherical: pin one vertex, assign sequential indices
maps.v_idx[first_vertex] = -1; // pinned: u_v = 0
int idx = 0;
for (auto v : remaining_vertices)
maps.v_idx[v] = idx++;
// HyperIdeal: all vertices and edges are free DOFs
int n_dofs = assign_all_dof_indices(mesh, maps);
// maps.v_idx[v] ∈ [0, n_v)
// maps.e_idx[e] ∈ [n_v, n_v + n_e)
-1 in v_idx or e_idx means the DOF is pinned (fixed at its lambda0 value).
The solver never writes to pinned DOFs. All indexing is 0-based and contiguous.
Gauss–Bonnet — the most common source of failure
Prescribing angles that violate Gauss–Bonnet means no conformal factor can realise the target metric — the Newton solver will iterate indefinitely without converging.
// Option A: verify before solving (throws on violation)
check_gauss_bonnet(mesh, maps);
// Option B: auto-correct (redistributes defect uniformly across all vertices)
enforce_gauss_bonnet(mesh, maps);
The target violation is |Σ(2π−Θᵥ) − 2π·χ(M)| > tol. Default tolerance: 1e-10.