docs: restructure documentation into focused files
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>
This commit is contained in:
141
doc/api/extending.md
Normal file
141
doc/api/extending.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Extending conformallab++
|
||||
|
||||
## Adding a new functional
|
||||
|
||||
All three existing functionals (`euclidean_functional.hpp`, `spherical_functional.hpp`,
|
||||
`hyper_ideal_functional.hpp`) follow the same pattern. Copy the structure of the
|
||||
simplest one (`euclidean_functional.hpp`) as a template.
|
||||
|
||||
### Step 1 — Maps struct
|
||||
|
||||
```cpp
|
||||
// my_functional.hpp
|
||||
struct MyMaps {
|
||||
ConformalMesh::Property_map<Vertex_index, double> lambda0; // initial log-lengths
|
||||
ConformalMesh::Property_map<Vertex_index, double> theta_v; // target angles
|
||||
ConformalMesh::Property_map<Vertex_index, int> v_idx; // DOF index, -1 = pinned
|
||||
// add edge DOFs if needed:
|
||||
ConformalMesh::Property_map<Edge_index, int> e_idx;
|
||||
};
|
||||
|
||||
MyMaps setup_my_maps(ConformalMesh& mesh);
|
||||
void compute_my_lambda0_from_mesh(ConformalMesh& mesh, MyMaps& maps);
|
||||
```
|
||||
|
||||
### Step 2 — Gradient
|
||||
|
||||
The gradient must satisfy: `G_v = Σ(angle contributions at v) − theta_v[v]`.
|
||||
|
||||
```cpp
|
||||
std::vector<double> my_gradient(
|
||||
const ConformalMesh& mesh,
|
||||
const std::vector<double>& x,
|
||||
const MyMaps& maps)
|
||||
{
|
||||
std::vector<double> G(n_dofs, 0.0);
|
||||
for (auto f : mesh.faces()) {
|
||||
// compute angles in face f given current x
|
||||
// accumulate into G[maps.v_idx[v]] for each vertex v of f
|
||||
}
|
||||
// subtract target angles
|
||||
for (auto v : mesh.vertices())
|
||||
if (maps.v_idx[v] >= 0)
|
||||
G[maps.v_idx[v]] -= maps.theta_v[v];
|
||||
return G;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3 — Gradient check test
|
||||
|
||||
Before claiming correctness, verify with finite differences. Copy any `GradientCheck_*`
|
||||
test suite from `tests/cgal/test_*_functional.cpp`:
|
||||
|
||||
```cpp
|
||||
TEST(MyFunctional, GradientCheck_Triangle) {
|
||||
ConformalMesh mesh = make_single_triangle();
|
||||
MyMaps maps = setup_my_maps(mesh);
|
||||
// ... assign DOFs, set natural theta ...
|
||||
|
||||
const double eps = 1e-5;
|
||||
auto G = my_gradient(mesh, x0, maps);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
std::vector<double> xp = x0, xm = x0;
|
||||
xp[i] += eps; xm[i] -= eps;
|
||||
double fd = (my_energy(mesh, xp, maps) - my_energy(mesh, xm, maps)) / (2*eps);
|
||||
EXPECT_NEAR(G[i], fd, 1e-7) << "DOF " << i;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4 — Hessian and Newton wrapper
|
||||
|
||||
```cpp
|
||||
// Reuse solve_linear_system from newton_solver.hpp
|
||||
Eigen::SparseMatrix<double> H = my_hessian(mesh, x, maps);
|
||||
bool used_fallback;
|
||||
auto dx = conformallab::solve_linear_system(H, -G, &used_fallback);
|
||||
```
|
||||
|
||||
Or write a thin `newton_my()` wrapper following the structure of `newton_euclidean()`.
|
||||
|
||||
---
|
||||
|
||||
## Adding a new geometry mode
|
||||
|
||||
To add a new space (e.g. de Sitter, flat 3-torus):
|
||||
|
||||
1. **Trilateration function** — implement `trilaterate_my(p1, p2, l12, l13, l23)` that
|
||||
places a third point given two placed points and three edge lengths.
|
||||
This is the only geometry-specific part of the layout.
|
||||
|
||||
2. **BFS structure** — reuse the priority-BFS loop from `euclidean_layout()` in `layout.hpp`.
|
||||
The loop itself is geometry-agnostic; swap in your trilateration function.
|
||||
|
||||
3. **Holonomy** — if the space has a holonomy group, record the transition maps at seam edges
|
||||
the same way `HolonomyData` does for translations (Euclidean) or Möbius maps (hyperbolic).
|
||||
|
||||
---
|
||||
|
||||
## Adding a new processing unit
|
||||
|
||||
1. Declare preconditions and outputs explicitly (see [contracts.md](contracts.md)).
|
||||
|
||||
2. Write a header-only implementation in `code/include/my_unit.hpp`.
|
||||
|
||||
3. Add tests in `code/tests/cgal/test_my_unit.cpp` and register in
|
||||
`code/tests/cgal/CMakeLists.txt`.
|
||||
|
||||
4. Pipeline validation is currently manual (documented contracts). A compile-time or
|
||||
runtime check via the declarative YAML pipeline (Phase 8e) is the planned upgrade —
|
||||
see [cgal-package.md](cgal-package.md).
|
||||
|
||||
---
|
||||
|
||||
## Porting from Java
|
||||
|
||||
When porting a class from the Java library:
|
||||
|
||||
1. Locate the original at [github.com/varylab/conformallab](https://github.com/varylab/conformallab)
|
||||
under `src/main/java/de/varylab/discreteconformal/`.
|
||||
|
||||
2. The Java library uses `CoHDS` (half-edge data structure) with intrusive vertex/edge data.
|
||||
Map these to CGAL property maps:
|
||||
```
|
||||
Java: vertex.getLambda() → C++: maps.lambda0[v]
|
||||
Java: edge.getAlpha() → C++: maps.e_alpha[e]
|
||||
Java: vertex.getTheta() → C++: maps.theta_v[v]
|
||||
Java: vertex.getSolverIndex() → C++: maps.v_idx[v]
|
||||
```
|
||||
|
||||
3. Java uses `HalfedgeInterface` adapters with named accessors like `getOppositeVertex()`.
|
||||
C++ equivalent:
|
||||
```cpp
|
||||
// Java: h.getOppositeVertex()
|
||||
Vertex_index v_opp = mesh.target(mesh.next(h));
|
||||
|
||||
// Java: h.getNextHalfedge().getOppositeVertex()
|
||||
Vertex_index v_opp2 = mesh.target(mesh.next(mesh.next(h)));
|
||||
```
|
||||
|
||||
4. Port the test cases from the Java `@Test` methods. The "natural theta" trick
|
||||
(`theta_v[v] = actual_angle_sum`) works the same way in both languages.
|
||||
Reference in New Issue
Block a user