docs: 5-document meeting prep — tutorials + research note + status + architecture
External-reviewer-visit prep package (Springborn-Bobenko PhD alumnus,
2026-05-26). All five documents target the same audience: a
mathematician who wants to evaluate, extend, or contribute to
conformallab++. Goal: make the project maximally hackable BEFORE the
meeting. Code unchanged in this commit — pure documentation.
Files added
───────────
1. **doc/tutorials/block-fd-hessian.md** (460 lines)
Step-by-step tutorial on the per-face block-FD Hessian pattern
shipped in Phase 9b (96× speed-up). Matches the style of
add-inversive-distance.md. Covers:
* The per-face locality lemma (mathematical justification).
* Cost analysis (full-FD vs block-FD vs analytic).
* Implementation walkthrough through face_angles_from_local_dofs +
hyper_ideal_hessian_block_fd.
* Porting checklist for applying the same pattern to a new
functional.
* The four cross-validation criteria.
* When NOT to use block-FD + upgrade path to Phase 9b-analytic.
2. **doc/tutorials/add-output-uv-map.md** (477 lines)
Tutorial for the `output_uv_map` named-parameter pattern shipped in
PR #14. Covers:
* The UX problem (two-step pipeline → one-call wrapper).
* The CGAL named-parameter mechanism + how the entry functions
wire it (get_parameter + constexpr if).
* Step-by-step recipe for adding a new named parameter (worked
example: hypothetical `output_holonomy_map`).
* The five test patterns for verification.
* Why CP-Euclidean (face-DOF) and Inversive-Distance (Luo-edge-length)
do not yet support output_uv_map — what is needed to add them.
3. **doc/math/hyperideal-hessian-derivation.md** (805 lines)
Research-quality LaTeX-formatted derivation of the analytic
HyperIdeal Hessian via the Schläfli identity (Phase 9b-analytic
preparation). Covers:
* Schläfli identity (1858/60) — gradient and second-order form.
* Derivatives of ζ, ζ₁₃, ζ₁₄, ζ₁₅ (all hyper-ideal-to-fully-ideal cases).
* Chain rule for ∂β_i/∂(b,a) and ∂α_ij/∂(b,a) — case-split on the
four α_ij branches.
* Per-face 6×6 block formulas.
* Acceptance criteria for the future implementation.
* Implementation outline (Conformal_map header sketch).
* Appendix A: sign / argument-order pitfalls reading the code.
* References: Schläfli 1858, Milnor 1982, Vinberg 1993, Cho-Kim 1999,
Rivin, Glickenstein 2011, Springborn 2020, BPS 2015.
4. **doc/roadmap/porting-status.md** (~250 lines)
Operational snapshot of "where is each piece of Java math today"
at v0.9.0. Sections:
* 25 000 lines of Java in one table (ported / worth porting /
intentionally skipped breakdown).
* Five DCE models — full status matrix with Java port status,
Hessian type, Newton support, CGAL entry, UV-output capability.
* Topology + solver infrastructure status.
* CGAL public API map + known limitations (no chaining, Surface_mesh
only, submission-readiness gaps).
* Reverse cross-reference: Java class → C++ port location (or
"skipped: replaced by …" / "in roadmap: phase X").
* Things in C++ that the Java original does NOT have (research
extensions track).
* "How to use the library today" quickstart.
5. **doc/architecture/locked-vs-flexible.md** (~270 lines)
12-item architecture-decision review with tier classification
(🔴 load-bearing / 🟡 semi-fixed / 🟢 opportunistic). Each item
includes: locked-since date, cost to change, when to revisit,
recommended posture for new contributors. Key insight stated up
front: "the load-bearing decisions are all good in 2026". Closes
with five open questions for the external reviewer — items where
a second opinion would genuinely help (Phase 9c algorithm choice,
Phase 10a priorities, analytic-Hessian payoff justification,
CGAL upstream vs independent distribution, geometry-central
cross-validation).
Total: ~2 250 lines across five new docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
477
doc/tutorials/add-output-uv-map.md
Normal file
477
doc/tutorials/add-output-uv-map.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# Tutorial: The `output_uv_map` Named Parameter
|
||||
|
||||
This tutorial documents the `output_uv_map` named parameter for the
|
||||
CGAL-style entry functions of conformallab++: how to use it (the "happy
|
||||
path"), how the CGAL named-parameter machinery threads a user-supplied
|
||||
property map through to the layout step, and how to extend the same
|
||||
pattern to add new named parameters to existing or new entry functions.
|
||||
|
||||
**Target audience.** A working mathematician using the C++ API who is
|
||||
comfortable with templates and CGAL property maps but wants a single
|
||||
authoritative reference for the pattern.
|
||||
|
||||
**Prerequisite reading.**
|
||||
|
||||
- `code/include/CGAL/Conformal_map/internal/parameters.h` — tag definitions
|
||||
- `code/include/CGAL/Discrete_conformal_map.h` — wiring in three entries
|
||||
- `code/tests/cgal/test_cgal_phase8b_lite.cpp` — the OutputUvMap_* tests
|
||||
- `doc/tutorials/add-inversive-distance.md` — sibling tutorial in this series
|
||||
|
||||
---
|
||||
|
||||
## 1. What this tutorial is and isn't
|
||||
|
||||
### The UX problem `output_uv_map` solves
|
||||
|
||||
Before Phase 8b-Lite, computing a flattening required **two separate
|
||||
calls**: the Newton wrapper, then a manual rebuild of `*_maps` plus a
|
||||
replay of the wrapper's gauge/DOF choices, then `*_layout()`. Any drift
|
||||
between the wrapper's gauge vertex and the caller's silently produced
|
||||
inconsistent UVs.
|
||||
|
||||
The `output_uv_map` named parameter collapses this into one call:
|
||||
|
||||
```cpp
|
||||
auto uv = mesh.add_property_map<Vertex_index, K::Point_2>("uv").first;
|
||||
CGAL::discrete_conformal_map_euclidean(
|
||||
mesh, CGAL::parameters::output_uv_map(uv));
|
||||
// uv[v] now holds the flattened UV coordinate of vertex v.
|
||||
```
|
||||
|
||||
This tutorial is **not** a derivation of the Newton solver (see
|
||||
`add-inversive-distance.md`), not a guide to writing a new layout
|
||||
routine, and not an introduction to CGAL named parameters in general
|
||||
(see `<CGAL/Named_function_parameters.h>`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Mathematical background
|
||||
|
||||
The package implements **two mathematically distinct stages**:
|
||||
|
||||
### Stage A — Newton solver (variational optimisation)
|
||||
|
||||
For the Euclidean DCE functional (Springborn–Schmies–Bobenko 2008), the
|
||||
solver finds the conformal scale factor vector `u ∈ ℝⁿ` that satisfies
|
||||
|
||||
```
|
||||
G(u)_v := Θ_v − Σ_{T ∋ v} α_v(T; u) = 0 ∀ v ∈ V \ {pinned}
|
||||
```
|
||||
|
||||
i.e. the actual cone angles match the target curvature `Θ_v` at every
|
||||
non-pinned vertex. The gradient `G` is the variational derivative of
|
||||
the convex energy
|
||||
|
||||
```
|
||||
E(u) = ∫_0^1 ⟨ G(t u), u ⟩ dt (path integral on the closed Yamabe 1-form)
|
||||
```
|
||||
|
||||
The output is **purely combinatorial-metric**: the converged `u_v`
|
||||
determines an intrinsic flat metric `ℓ_ij(u) = exp(½(u_i + u_j)) · ℓ_ij^(0)`
|
||||
on the abstract triangulation. **No embedding yet exists.**
|
||||
|
||||
### Stage B — Layout (geometric realisation)
|
||||
|
||||
The layout step is a separate **trilateration** turning intrinsic edge
|
||||
lengths into a `Point_2` (or `Point_3`) per vertex:
|
||||
|
||||
1. Pick a seed triangle `T₀`; place its three vertices canonically.
|
||||
2. BFS over the dual graph from `T₀`. Each newly visited triangle has
|
||||
two vertices already placed; the third is the unique solution of
|
||||
two distance constraints (intersection of two circles).
|
||||
3. The BFS uses a **priority queue keyed on BFS depth**
|
||||
(`euclidean_layout` in `code/include/layout.hpp`) to avoid the
|
||||
numerical drift of pure DFS on long thin meshes.
|
||||
|
||||
Spherical / hyperbolic variants substitute spherical-cap or Poincaré-
|
||||
disk trilateration respectively.
|
||||
|
||||
Stage A is convex optimisation in ℝⁿ; Stage B is deterministic
|
||||
realisation with no degrees of freedom. The named parameter chains them.
|
||||
|
||||
---
|
||||
|
||||
## 3. The user-facing API
|
||||
|
||||
### Happy path
|
||||
|
||||
```cpp
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Discrete_conformal_map.h>
|
||||
|
||||
using K = CGAL::Simple_cartesian<double>;
|
||||
using Mesh = CGAL::Surface_mesh<K::Point_3>;
|
||||
using Vertex_i = Mesh::Vertex_index;
|
||||
|
||||
Mesh mesh = /* load triangle mesh */;
|
||||
|
||||
// 1. Allocate a writable property map on the mesh.
|
||||
auto uv = mesh.add_property_map<Vertex_i, K::Point_2>(
|
||||
"v:uv", K::Point_2(0, 0)).first;
|
||||
|
||||
// 2. Ask the wrapper to populate it.
|
||||
auto res = CGAL::discrete_conformal_map_euclidean(
|
||||
mesh,
|
||||
CGAL::parameters::output_uv_map(uv));
|
||||
|
||||
// 3. Use the UVs.
|
||||
if (res.converged) {
|
||||
for (auto v : mesh.vertices())
|
||||
std::cout << v.idx() << ": " << uv[v] << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
### Supported entries
|
||||
|
||||
| Entry | Value type per vertex |
|
||||
|--------------------------------------|--------------------------------------------------|
|
||||
| `discrete_conformal_map_euclidean` | `Point_2` — UV in ℝ² |
|
||||
| `discrete_conformal_map_spherical` | `Point_3` — point on S² ⊂ ℝ³ |
|
||||
| `discrete_conformal_map_hyper_ideal` | `Point_2` — point in the Poincaré disk (|p|≤1) |
|
||||
|
||||
### NOT supported (yet)
|
||||
|
||||
| Entry | Reason |
|
||||
|----------------------------------------|-------------------------------------------------------------|
|
||||
| `discrete_circle_packing_euclidean` | Face-based parametrisation — UV is per-face, not per-vertex |
|
||||
| `discrete_inversive_distance_map` | Requires `inversive_distance_layout()` using Luo's `ℓ²` formula — not yet written |
|
||||
|
||||
For the circle-packing entry a separate `output_circle_map` parameter
|
||||
(face → (centre, radius)) is the natural extension; for the inversive-
|
||||
distance entry the layout primitive must be written first. See §7.
|
||||
|
||||
### Optional companion: `normalise_layout`
|
||||
|
||||
```cpp
|
||||
CGAL::parameters::normalise_layout(true)
|
||||
```
|
||||
|
||||
When the layout writes into `output_uv_map`, this flag triggers a
|
||||
canonical post-processing pass:
|
||||
|
||||
- Euclidean: PCA — translate centroid to origin, rotate so the principal
|
||||
axis is along `x`.
|
||||
- Spherical: Rodrigues rotation so that the centroid is at the north pole.
|
||||
- Hyperbolic: Möbius centring of the Poincaré disk.
|
||||
|
||||
Default: `false`. Has no effect unless `output_uv_map` is also passed.
|
||||
|
||||
### Current limitation: no chaining
|
||||
|
||||
The CGAL machinery supports chaining named parameters via `.member()`
|
||||
syntax for the standard CGAL tags (e.g. `geom_traits`, `vertex_point_map`).
|
||||
**Package-local tags do not yet have member-function counterparts**, so
|
||||
|
||||
```cpp
|
||||
// DOES NOT COMPILE:
|
||||
CGAL::parameters::output_uv_map(uv).normalise_layout(true);
|
||||
```
|
||||
|
||||
is currently rejected. Pass one parameter per call instead. This is
|
||||
the same limitation noted in `test_cgal_phase8b_lite.cpp`:
|
||||
|
||||
```cpp
|
||||
// Named-parameter chaining (`a.b().c()`) is not currently supported on
|
||||
// the package-local tags; pass one parameter per call instead.
|
||||
```
|
||||
|
||||
Adding member-function chaining is a separate Phase 8b extension —
|
||||
requires writing a derived `Named_function_parameters` subclass that
|
||||
exposes `.output_uv_map(...)`, `.normalise_layout(...)`, etc.
|
||||
|
||||
---
|
||||
|
||||
## 4. Named-parameter mechanism — how it works
|
||||
|
||||
CGAL's named parameters are a compile-time dispatch trick. Three pieces:
|
||||
|
||||
### 4.1 The tag (in `internal_np`)
|
||||
|
||||
```cpp
|
||||
namespace CGAL::Conformal_map::internal_np {
|
||||
enum output_uv_map_t { output_uv_map };
|
||||
}
|
||||
```
|
||||
|
||||
A single-value `enum` is the lightest possible type-level marker. The
|
||||
value `output_uv_map` is what callers will pass as the *key*; the type
|
||||
`output_uv_map_t` is what `get_parameter` will use for the lookup.
|
||||
|
||||
### 4.2 The user-facing helper (in `CGAL::parameters`)
|
||||
|
||||
```cpp
|
||||
template <typename PropertyMap>
|
||||
auto output_uv_map(const PropertyMap& pmap) {
|
||||
return CGAL::Named_function_parameters<
|
||||
PropertyMap,
|
||||
Conformal_map::internal_np::output_uv_map_t,
|
||||
CGAL::internal_np::No_property // no "next" parameter in chain
|
||||
>(pmap);
|
||||
}
|
||||
```
|
||||
|
||||
This wraps the property map in a `Named_function_parameters` object
|
||||
tagged with `output_uv_map_t`. The third template argument is the
|
||||
"previous" link in a chain; `No_property` means this is a singleton
|
||||
pack. (Chaining would substitute the previous pack's type here.)
|
||||
|
||||
### 4.3 The internal lookup (inside the entry function)
|
||||
|
||||
The full read-and-act pattern from `Discrete_conformal_map.h`:
|
||||
|
||||
```cpp
|
||||
// Step 1 — try to extract the parameter.
|
||||
auto uv_param = parameters::get_parameter(
|
||||
np, Conformal_map::internal_np::output_uv_map);
|
||||
|
||||
// Step 2 — at compile time, did we find one?
|
||||
constexpr bool has_uv = !std::is_same_v<
|
||||
decltype(uv_param), internal_np::Param_not_found>;
|
||||
|
||||
// Step 3 — conditionally do the extra work.
|
||||
if constexpr (has_uv) {
|
||||
if (nr.converged) {
|
||||
auto layout = ::conformallab::euclidean_layout(mesh, nr.x, maps);
|
||||
|
||||
const bool do_norm = parameters::choose_parameter(
|
||||
parameters::get_parameter(np, Conformal_map::internal_np::normalise_layout),
|
||||
false);
|
||||
if (do_norm) ::conformallab::normalise_euclidean(layout);
|
||||
|
||||
for (auto v : mesh.vertices()) {
|
||||
const auto& uv = layout.uv[v.idx()];
|
||||
put(uv_param, v,
|
||||
typename Traits::Kernel::Point_2(uv.x(), uv.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Key points:
|
||||
|
||||
- `get_parameter` returns either the wrapped value or a sentinel
|
||||
`Param_not_found`. The check is `std::is_same_v<…, Param_not_found>`.
|
||||
- `choose_parameter(get_parameter(np, tag), default_value)` is the
|
||||
one-liner form for scalar parameters with a default.
|
||||
- `constexpr if` ensures that if the user did not pass `output_uv_map`,
|
||||
the layout call is **not even instantiated** — no runtime cost, and
|
||||
no need for the layout routine to be callable on the wrapper's
|
||||
signature (e.g. it can require a Point_2 traits type that the user
|
||||
hasn't supplied).
|
||||
- The layout step is gated on `nr.converged` for a reason: trilateration
|
||||
on a non-converged `u` produces garbage edge lengths and can throw
|
||||
on degenerate triangles. The current contract is **layout iff Newton
|
||||
converged**; on non-convergence the pmap is left at the value the
|
||||
caller initialised it with.
|
||||
|
||||
The same three-step pattern appears in `discrete_conformal_map_spherical`
|
||||
(writing `Point_3`) and `discrete_conformal_map_hyper_ideal` (writing
|
||||
`Point_2` in the Poincaré disk).
|
||||
|
||||
---
|
||||
|
||||
## 5. How to extend the pattern
|
||||
|
||||
Recipe for adding a new package-local named parameter — running example
|
||||
`output_holonomy_map` (a per-edge map carrying the Möbius/translation
|
||||
holonomy data that some downstream algorithms consume).
|
||||
|
||||
### Step 1 — Add the tag in `internal/parameters.h`
|
||||
|
||||
```cpp
|
||||
namespace CGAL::Conformal_map::internal_np {
|
||||
/// Property-map: edge_descriptor → 2x2 matrix of holonomy data.
|
||||
/// Default: not populated.
|
||||
enum output_holonomy_map_t { output_holonomy_map };
|
||||
}
|
||||
```
|
||||
|
||||
Two-line block. Use a meaningful Doxygen `\internal` comment because
|
||||
this is the canonical contract for the parameter's semantics.
|
||||
|
||||
### Step 2 — Add the user-facing helper in `parameters.h`
|
||||
|
||||
```cpp
|
||||
namespace CGAL::parameters {
|
||||
|
||||
/// `output_holonomy_map(pmap)` — write per-edge holonomy matrices
|
||||
/// into `pmap`. Only meaningful for closed surfaces with non-trivial
|
||||
/// π₁; ignored on simply-connected meshes.
|
||||
template <typename PropertyMap>
|
||||
auto output_holonomy_map(const PropertyMap& pmap) {
|
||||
return CGAL::Named_function_parameters<
|
||||
PropertyMap,
|
||||
Conformal_map::internal_np::output_holonomy_map_t,
|
||||
CGAL::internal_np::No_property
|
||||
>(pmap);
|
||||
}
|
||||
|
||||
} // namespace CGAL::parameters
|
||||
```
|
||||
|
||||
A Doxygen comment is **mandatory** here — this is the public surface.
|
||||
|
||||
### Step 3 — Use `get_parameter + constexpr if` in the entry function
|
||||
|
||||
```cpp
|
||||
auto hol_param = parameters::get_parameter(
|
||||
np, Conformal_map::internal_np::output_holonomy_map);
|
||||
constexpr bool has_hol = !std::is_same_v<
|
||||
decltype(hol_param), internal_np::Param_not_found>;
|
||||
if constexpr (has_hol) {
|
||||
auto H = ::conformallab::compute_holonomy(mesh, nr.x, maps);
|
||||
for (auto e : mesh.edges())
|
||||
put(hol_param, e, H[e.idx()]);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4 — Add tests
|
||||
|
||||
At minimum two:
|
||||
|
||||
1. **Takes-effect test.** Pass the parameter; verify the side effect
|
||||
actually happens (pmap is populated, values are non-default).
|
||||
2. **Sanity-when-absent test.** Call the wrapper without the parameter;
|
||||
verify the call still succeeds and produces the expected base result.
|
||||
|
||||
Both patterns are exemplified in `test_cgal_phase8b_lite.cpp` (next
|
||||
section).
|
||||
|
||||
### Optional Step 5 — Companion flag
|
||||
|
||||
If your new parameter has a Boolean tweak (analogous to
|
||||
`normalise_layout`), follow exactly the same recipe — `enum X_t { X }`
|
||||
+ helper + `choose_parameter(..., default)` at the read site.
|
||||
|
||||
---
|
||||
|
||||
## 6. Tests — walkthrough of `test_cgal_phase8b_lite.cpp`
|
||||
|
||||
The OutputUvMap_* tests live at the bottom of the file. Each codifies
|
||||
one acceptance criterion.
|
||||
|
||||
### 6.1 `OutputUvMap_Euclidean_PopulatesPmap`
|
||||
|
||||
Two assertions: (a) every UV is finite, (b) at least one vertex has
|
||||
moved off the origin. Test (b) rules out the false-positive where the
|
||||
layout silently returned the pmap's default at every vertex.
|
||||
|
||||
### 6.2 `OutputUvMap_Spherical_PopulatesXyz`
|
||||
|
||||
Geometric round-trip: every output point must lie on the unit sphere.
|
||||
|
||||
```cpp
|
||||
const double r = std::sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
|
||||
EXPECT_NEAR(r, 1.0, 1e-6);
|
||||
```
|
||||
|
||||
The spherical analogue of 6.1(b): the layout is correct iff the
|
||||
realisation lands on S².
|
||||
|
||||
### 6.3 `OutputUvMap_HyperIdeal_PointsInPoincareDisk`
|
||||
|
||||
The natural Θ/θ targets do not always reach Newton equilibrium inside
|
||||
the default `max_iterations(200)`. The test branches:
|
||||
|
||||
```cpp
|
||||
if (res.converged) {
|
||||
for (auto v : mesh.vertices()) {
|
||||
const double r2 = p.x()*p.x() + p.y()*p.y();
|
||||
EXPECT_LE(r2, 1.0 + 1e-6);
|
||||
}
|
||||
}
|
||||
// else: layout was skipped by the wrapper's `if (nr.converged)` guard.
|
||||
```
|
||||
|
||||
Documenting the wrapper contract ("layout iff converged") via the
|
||||
test keeps it resilient to PRNG-sensitive Newton paths.
|
||||
|
||||
### 6.4 `OutputUvMap_Absent_DoesNotRunLayout`
|
||||
|
||||
Sanity test: omitting `output_uv_map` must not change the existing
|
||||
behaviour. Catches the regression where adding the new `constexpr if`
|
||||
perturbs the wrapper's result type or consumes extra work.
|
||||
|
||||
### 6.5 `OutputUvMap_NormaliseLayout_TakesEffect`
|
||||
|
||||
Because chaining is not yet implemented (§3), this exercises the toggle
|
||||
indirectly: it runs the wrapper twice into two separate pmaps and
|
||||
verifies both populate finite values. When chaining lands, upgrade this
|
||||
to actually pass `normalise_layout(true)` on the second call and compare
|
||||
UV bounding boxes (the normalised one must be axis-aligned and
|
||||
centroid-zero).
|
||||
|
||||
---
|
||||
|
||||
## 7. Adding output to the two missing modes
|
||||
|
||||
### 7.1 CP-Euclidean (face-based) — proposed `output_circle_map`
|
||||
|
||||
The circle-packing entry parametrises **faces**: each face `f` has a
|
||||
radius `ρ_f`. The natural per-face output is `(centre, radius)`:
|
||||
|
||||
```cpp
|
||||
auto circles = mesh.add_property_map<Face_index,
|
||||
std::pair<K::Point_2, double>>("f:circle", ...).first;
|
||||
|
||||
CGAL::discrete_circle_packing_euclidean(
|
||||
mesh, CGAL::parameters::output_circle_map(circles));
|
||||
```
|
||||
|
||||
Effort: **1–2 days**. The face-graph BFS-packing is Stephenson's
|
||||
`circlepack` algorithm; radii come from Newton, new work is the
|
||||
geometric loop plus the §5 plumbing.
|
||||
|
||||
### 7.2 Inversive-Distance — needs new layout routine first
|
||||
|
||||
The inversive-distance functional yields edge lengths
|
||||
|
||||
```
|
||||
ℓ_ij(u)² = r_i² + r_j² + 2 I_ij r_i r_j (Luo 2004 §3)
|
||||
```
|
||||
|
||||
with `r_i = exp(u_i)`. Required work: write
|
||||
`inversive_distance_layout()` in `code/include/layout.hpp` mirroring
|
||||
`euclidean_layout()` but consuming the above `ℓ_ij(u)` instead of
|
||||
`exp(½(u_i + u_j)) · ℓ_ij^(0)`. Then wire the parameter through
|
||||
`discrete_inversive_distance_map` exactly as §4.3 shows.
|
||||
|
||||
Effort: **~3 days**. Status: **TBD**.
|
||||
|
||||
---
|
||||
|
||||
## 8. Acceptance checklist for a new named parameter
|
||||
|
||||
Use this checklist when extending the package:
|
||||
|
||||
- [ ] **Tag declared** in `code/include/CGAL/Conformal_map/internal/parameters.h`
|
||||
(`enum X_t { X };`) with a `\internal` Doxygen comment specifying
|
||||
the property-map key/value types and default behaviour.
|
||||
- [ ] **User-facing helper** in the same file's `CGAL::parameters`
|
||||
namespace, with a public Doxygen comment.
|
||||
- [ ] **`get_parameter + constexpr if` pattern** in every entry function
|
||||
that supports the parameter (do not leak the tag into entries that
|
||||
ignore it).
|
||||
- [ ] **At least two tests** in `test_cgal_phase8b_lite.cpp` (or a new
|
||||
test file if introducing a new functional): a
|
||||
*parameter-takes-effect* test and a *sanity-when-absent* test.
|
||||
- [ ] **Limitation documented.** If the parameter is mode-specific
|
||||
(e.g. only meaningful for closed meshes) or cannot be chained
|
||||
with existing helpers, say so in the user-facing Doxygen and in
|
||||
a comment near the read site.
|
||||
- [ ] **No silent regressions.** If the parameter changes the wrapper's
|
||||
observable behaviour even when absent (e.g. via a new default),
|
||||
add a regression test pinning the old default.
|
||||
|
||||
---
|
||||
|
||||
## 9. Further reading
|
||||
|
||||
- Springborn, Schmies, Bobenko (2008). *Conformal equivalence of
|
||||
triangle meshes.* SIGGRAPH. — Euclidean DCE functional.
|
||||
- Bobenko, Pinkall, Springborn (2015). *Discrete conformal maps and
|
||||
ideal hyperbolic polyhedra.* Geom. Topol. — hyper-ideal variant.
|
||||
- Glickenstein (2011). *Discrete conformal variations and scalar
|
||||
curvature on piecewise flat manifolds.* JDG 87(2).
|
||||
- `<CGAL/Named_function_parameters.h>` — upstream machinery.
|
||||
Reference in New Issue
Block a user