# Pipeline API Reference

The full pipeline from mesh loading to layout and serialisation.
See [doc/architecture/overall_pipeline.md](../architecture/overall_pipeline.md) for the
Mermaid diagram and stage descriptions.

---

## Minimal Euclidean pipeline

```cpp
#include "conformal_mesh.hpp"
#include "mesh_io.hpp"
#include "euclidean_functional.hpp"
#include "gauss_bonnet.hpp"
#include "newton_solver.hpp"
#include "layout.hpp"

using namespace conformallab;

ConformalMesh mesh = load_mesh("input.off");

// 1. Set up property maps and initialise log-edge-lengths from 3-D positions
EuclideanMaps maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps);

// 2. Assign DOF indices — pin first vertex (gauge fix for open meshes)
auto vit = mesh.vertices().begin();
maps.v_idx[*vit++] = -1;           // pinned: u_v = 0
int idx = 0;
for (; vit != mesh.vertices().end(); ++vit)
    maps.v_idx[*vit] = idx++;

// 3. Set target angles — "natural equilibrium" makes x* = 0
std::vector<double> x0(idx, 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]];

// 4. Check Gauss–Bonnet (mandatory before solving)
check_gauss_bonnet(mesh, maps);     // throws if Σ(2π−Θᵥ) ≠ 2π·χ(M)

// 5. Solve
NewtonResult res = newton_euclidean(mesh, x0, maps);
// res.converged, res.x, res.iterations, res.grad_inf_norm

// 6. Layout
Layout2D layout = euclidean_layout(mesh, res.x, maps);
// layout.uv[v.idx()], layout.halfedge_uv[h.idx()]
```

---

## Layout with holonomy (closed surfaces)

```cpp
#include "cut_graph.hpp"
#include "period_matrix.hpp"
#include "fundamental_domain.hpp"

// Tree-cotree cut graph — 2g seam edges
CutGraph cg = compute_cut_graph(mesh);
// cg.cut_edge_flags[e.idx()] — true if seam edge
// cg.genus                   — topological genus g

// Layout with holonomy tracking
HolonomyData hol;
Layout2D layout = euclidean_layout(mesh, res.x, maps, &cg, &hol, /*normalise=*/true);
// hol.translations[i] — lattice generator ωᵢ ∈ ℂ (Euclidean)

// Period matrix (genus 1 flat torus)
PeriodData pd = compute_period_matrix(hol);
// pd.tau               — complex period ratio τ = ω₂/ω₁ ∈ ℍ
// pd.omega             — {ω₁, ω₂} as complex<double>
// pd.in_fundamental_domain — true if SL(2,ℤ)-reduced to |τ|≥1, |Re(τ)|<½

// Fundamental domain parallelogram
FundamentalDomain fd = compute_fundamental_domain(hol);
// fd.vertices[0..3]    — CCW corners: {0, ω₁, ω₁+ω₂, ω₂}
// fd.generators[0..1]  — ω₁, ω₂

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

---

## HyperIdeal pipeline

```cpp
#include "hyper_ideal_functional.hpp"
#include "newton_solver.hpp"
#include "layout.hpp"
#include "cut_graph.hpp"

HyperIdealMaps maps = setup_hyper_ideal_maps(mesh);
compute_hyper_ideal_lambda0_from_mesh(mesh, maps);

// HyperIdeal has both vertex and edge DOFs — assign all automatically
int n_dofs = assign_all_dof_indices(mesh, maps);
// No vertex needs to be pinned — strictly convex functional

std::vector<double> x0(n_dofs, 0.0);
NewtonResult res = newton_hyper_ideal(mesh, x0, maps);

// Layout with Möbius holonomy
CutGraph cg = compute_cut_graph(mesh);
HolonomyData hol;
Layout2D layout = hyper_ideal_layout(mesh, res.x, maps, &cg, &hol);
// hol.mobius_maps[i] — Möbius isometry Tᵢ ∈ SU(1,1) per cut edge
```

---

## Spherical pipeline

```cpp
#include "spherical_functional.hpp"
#include "newton_solver.hpp"
#include "layout.hpp"

SphericalMaps maps = setup_spherical_maps(mesh);
compute_spherical_lambda0_from_mesh(mesh, maps);

// Pin one vertex (gauge fix) + assign DOFs
auto vit = mesh.vertices().begin();
maps.v_idx[*vit++] = -1;
int idx = 0;
for (; vit != mesh.vertices().end(); ++vit)
    maps.v_idx[*vit] = idx++;

std::vector<double> x0(idx, 0.0);
NewtonResult res = newton_spherical(mesh, x0, maps);

// 3-D layout on the unit sphere
Layout3D slayout = spherical_layout(mesh, res.x, maps);
// slayout.xyz[v.idx()] — 3-D position on S²
```

---

## SparseQR fallback — direct use

```cpp
#include "newton_solver.hpp"

bool used_fallback = false;
Eigen::VectorXd dx = conformallab::solve_linear_system(H, rhs, &used_fallback);
if (used_fallback)
    std::cerr << "Warning: H is rank-deficient, used SparseQR\n";
```

The fallback is automatically invoked by all three `newton_*` functions when
`SimplicialLDLT` fails. It finds the minimum-norm Newton step orthogonal to the null space.

---

## Serialisation

```cpp
#include "serialization.hpp"
#include "mesh_io.hpp"

// Save layout as OFF mesh file
save_layout_off("layout.off", mesh, layout);

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

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

---

## Attaching custom property maps

```cpp
// Add a custom per-vertex map
auto [curv, created] = mesh.add_property_map<Vertex_index, double>("v:my_curv", 0.0);
curv[v] = 3.14;

// Retrieve an existing map
auto [curv2, found] = mesh.property_map<Vertex_index, double>("v:my_curv");
```

---

## Halfedge traversal conventions

```cpp
for (auto f : mesh.faces()) {
    auto h0 = mesh.halfedge(f);   // canonical halfedge of face f
    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 halfedge h0 (edge v1–v2)
    // h_alpha[h0] = α₃,  h_alpha[h1] = α₁,  h_alpha[h2] = α₂

    Edge_index e0 = mesh.edge(h0);
    bool is_seam = cg.cut_edge_flags[e0.idx()];
    bool is_boundary = mesh.is_border(mesh.opposite(h0));
}
```
