# 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 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 // 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 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 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("v:my_curv", 0.0); curv[v] = 3.14; // Retrieve an existing map auto [curv2, found] = mesh.property_map("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)); } ```