# 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 ```cpp // 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. ```cpp // 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`.