This commit closes the structural-tests work on PR #18. Every gate
in `run-all.sh --fast` now passes end-to-end on the canonical dev
machine.
New gates
─────────
1. shellcheck (scripts/quality/shellcheck.sh)
* Scans every `scripts/**/*.sh` at severity=warning+
* 16 scripts inspected; cleanup pass took the tree from 7 findings
(SC2164 + SC2034) to 0 findings.
2. cppcheck (scripts/quality/cppcheck.sh)
* Complementary static analyser to clang-tidy; different heuristics,
fewer false-positives on heavy CGAL/Eigen templates.
* Default severity warning+, --strict adds style, --all = everything.
* Suppresses 4 noise classes (missingIncludeSystem, etc.) explicitly.
3. .editorconfig
* Cross-IDE fallback for editors that don't honour clang-format.
* Covers Markdown (preserve trailing whitespace), Python, YAML,
JSON, shell, Makefile (tabs) — the file types clang-format
doesn't cover.
4. CONFORMALLAB_WARNINGS_AS_ERRORS CMake option
* Off by default → regular builds don't break on new GCC warnings.
* `-DCONFORMALLAB_WARNINGS_AS_ERRORS=ON` adds `-Werror`, intended for
CI promotion-track and sanitizer runs.
Dependency audit (doc/architecture/dependencies.md)
────────────────────────────────────────────────────
New single-source-of-truth document listing:
* what the library requires (Eigen + CGAL + Boost — all header-only)
* what tests require (auto-fetched GTest, no system install)
* what each quality tool is for, install command per OS, and
behaviour when missing (each gate exits 2 = SKIP, run-all
recognises this and continues)
* a verification recipe that strips PATH down and shows the
library still configures + builds + tests cleanly with zero
quality tools installed.
run-all.sh enhanced
───────────────────
* Recognises "tool not in PATH" → SKIP (not FAIL).
* Summary now reports `passed / skipped / failed` separately.
Bug fixes uncovered by the sweep
────────────────────────────────
* sanitizers.sh: gtest_discover_tests ran the ASan-instrumented
binary at build time and aborted → added
`-DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=PRE_TEST` to defer
discovery to ctest invocation. Now 23/23 sanitizer-instrumented
tests pass.
* clang-tidy.sh on macOS: brew-installed clang-tidy couldn't find
Apple SDK system headers (<cmath>, <complex>, …) → added
`--extra-arg=-isysroot $(xcrun --show-sdk-path)` on Darwin.
* clang-tidy.sh: needed `-DWITH_CGAL_TESTS=ON` in compile_commands
generation so CGAL include paths are part of at least one
compile entry. Now resolves CGAL/Surface_mesh.h etc.
* clang-tidy.sh: viewer-only headers (`viewer_utils.h`, `mesh_utils.hpp`)
excluded — they need `WITH_VIEWER=ON` + system GLFW/libigl that the
lint build doesn't drag in.
* `.codespellrc`: extended ignore list (recognise, signalled, modelled,
travelled, …) for British-English consistency across own writing.
Final state — local quality block on this commit, this branch:
✅ License headers (66/66 carry MIT SPDX)
✅ CGAL conventions (0/6 violations on 6 CGAL headers)
✅ clang-format drift (0 drift)
✅ cmake-format/-lint (0 drift, 0 lint findings)
✅ codespell (0 typos in scope)
✅ shellcheck (0 findings across 16 .sh files)
✅ cppcheck (warning+ severity clean)
✅ Markdown links (122/122 resolve)
✅ Sanitizers (ASan+UBSan) (23/23 fast tests pass)
✅ clang-tidy (35 headers inspected, 0 findings)
Library standalone-ness verified:
env -i PATH=... cmake -S code -B /tmp/build-standalone
cmake --build /tmp/build-standalone --target conformallab_tests
ctest -E '^cgal\.' → all green
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the gap "no code-quality / convention gate" from the structural
review. Three new artefacts, all local-only (CI promotion deferred
until the existing tree is 100 % clean under each):
1. .clang-format — project's existing style mechanically captured
(4-space indent, opening brace on new line for class/struct/function,
left-aligned pointer/reference modifiers, aligned `using = ...` blocks,
100-col loose limit, no include re-ordering — matches code/include/
today).
2. scripts/quality/clang-format.sh — drift detector. Dry-run mode by
default (always exits 0); --strict to fail on drift; --fix to apply
suggested changes in place. Skips code/deps/ and macOS-duplicate
files.
3. scripts/quality/cgal-conventions.py — checker for the CGAL idioms
that clang-format/clang-tidy cannot express:
CGAL-1 include-guard format `CGAL_<DIRS>_<FILE>_H`
CGAL-2 every public header has a `\\file` Doxygen brief
CGAL-3 no nested namespaces beyond the allowed set
(CGAL::parameters, CGAL::Conformal_map, internal_np, IO)
CGAL-4 named-parameter tag types end in `_t`; value object does not
CGAL-5 no `using namespace ...` at file scope (header leakage)
CGAL-6 no #define beyond CGAL_* / include-guard
Result on the current tree: 6 CGAL public headers, 0 violations.
The checker therefore doubles as documentation of the conventions
we already follow.
Both are wired into scripts/quality/run-all.sh's fast subset (~5 s
combined wall time). README.md updated to split the gates into a
"style/convention" group (cheap, run-on-every-commit material) and a
"correctness/quality" group (slow, run-before-tag material).
The reviewer-facing locked-vs-flexible.md gains another "✅ Closed"
row documenting both gates and the 0-violation baseline.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI gates (active on every PR via .gitea/workflows/)
───────────────────────────────────────────────────
1. test-count consistency
cpp-tests.yml gains a step after test-cgal that runs
`scripts/check-test-counts.sh` against the just-built ./build dir
(reuse via new BUILD_DIR env var, ~5 s overhead). Drift between
`doc/api/tests.md` and ctest reality now fails the PR.
2. End-to-end smoke
`scripts/try_it.sh` (the documented user quick-start) is now part of
the CGAL job, so README quick-start regressions fail the PR rather
than silently breaking when users land.
3. Internal markdown link checker
New `.gitea/workflows/markdown-links.yml` + `scripts/check-markdown
-links.py`. PRs that touch any *.md file run the check; main pushes
trigger it too; a weekly cron catches external link rot. Pure
Python, no third-party action. Validated against the current tree:
122 internal links across 37 *.md files, 0 broken.
Local quality scripts (`scripts/quality/`, not in CI)
─────────────────────────────────────────────────────
* `license-headers.sh` — `SPDX-License-Identifier: MIT` audit over
code/{include,src,tests}/. Currently
reports 60/66 files missing it — that's
a follow-up; the script captures the
structural gap.
* `sanitizers.sh` — ASan + UBSan over the fast test suite.
* `coverage.sh` — gcov/lcov line + branch coverage of
code/include/, HTML report under
build-coverage/lcov-html/.
* `clang-tidy.sh` — runs the curated `.clang-tidy` policy over
every public header.
* `multi-compiler.sh` — sequential build + test against every
detected g++/clang++ (auto-discovery or
explicit list).
* `cgal-version-matrix.sh`— sequential build + CGAL test suite against
every CGAL tree under `~/cgal/<ver>/` (or
via `CGAL_ROOTS=...` env var).
* `reproducible-build.sh`— two `Release -j1` builds, fail if any test
executable byte-differs.
* `run-all.sh` — driver: `--fast` for the ~5-min subset,
no arg for the ~25–40 min full sweep;
captures per-gate logs to
build-quality-logs/.
+ `.clang-tidy` — curated, deliberately-small policy (only
checks that fire on OUR code, never on
transitive CGAL/Eigen/Boost headers).
+ `scripts/quality/README.md` — explains the structure, lists each
gate's wall-time + prereqs, and codifies
the promotion path: a gate moves into CI
only when it's green on the dev machine
AND has a recovery-instructions paragraph
in `doc/release-policy.md`.
Doc updates
───────────
`doc/architecture/locked-vs-flexible.md` (reviewer-facing) gains 4
"closed" rows in the limitations table — the 3 CI gates above and the
local quality-script suite.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ROOT CAUSE FIX
The Doxyfile EXCLUDE_PATTERNS line contained `*/* 2.hpp` (note the
space — a stray glob from macOS-style "foo 2.hpp" duplicate files).
That pattern was silently matching ALL .hpp / .h files, so Doxygen was
indexing nothing under code/include/. The pre-existing 556 KB of HTML
output was effectively documenting only README.md, CLAUDE.md and a
small stub for std:: — not the C++ API at all.
After fixing the pattern (and properly escaping the space-prefixed
"foo 2.hpp / foo 2.h" macOS-dup patterns), Doxygen now extracts 141
compounds and emits 248 HTML pages from the public headers.
WHAT THIS PR ADDS
1. Doxyfile fix: correct EXCLUDE_PATTERNS; add GENERATE_XML for the
coverage measurement script; add MathJax for `$$...$$` math in
markdown; add the missing CGAL `\cgalParamNBegin/End/Description/
Default/...` aliases so CGAL-style param blocks render correctly.
2. New headers:
- code/include/CGAL/Conformal_map/doxygen_groups.h
defines `PkgConformalMap{,Ref,Concepts,NamedParameters}`,
resolving 17 prior "non-existing group" warnings.
- code/include/CGAL/Conformal_map/doxygen_namespaces.h
gives every namespace under `CGAL::` and `conformallab::` a
brief description.
3. New tool: scripts/doxygen-coverage.sh
Parses the XML output and reports % of public symbols (excluding
the `detail::` implementation namespaces by default) that have a
non-empty brief/detailed description. Supports `--list-undoc`
and `--threshold N` for CI integration.
4. Substantial docstring additions to the public CGAL headers:
`Conformal_map_traits.h`, `Discrete_circle_packing.h`,
`Discrete_inversive_distance.h`, `conformal_mesh.hpp`,
`Discrete_conformal_map.h` (Hyper_ideal_map_result fields).
5. Markdown housekeeping that the strict-warning Doxygen run surfaced:
tests.md (escape literal `#` in table cell),
locked-vs-flexible.md (broken section anchor),
overall_pipeline.md (replace `$$LaTeX$$` with inline-unicode math).
CURRENT NUMBERS
before: ~24% documented (public API; the prior "87%" claim was
based on the broken extraction)
after: 42% documented (165 of 396 public symbols)
warnings: 0 (was 27 spurious + a flood of bogus undocumented
warnings hidden by the buggy EXCLUDE pattern)
NEXT (in a follow-up commit on this branch)
The remaining 231 public symbols (mostly in `layout.hpp`,
`hyper_ideal_functional.hpp`, `spherical_functional.hpp`, the per-mode
functional/Hessian files) can be brought to ~100% with another pass of
short `///` brief descriptions. The coverage script is the gate; CI
can begin enforcing `--threshold 95` once the next pass lands.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New .gitea/workflows/doxygen-pages.yml runs on every push to main that
touches the public headers, Doxyfile, the markdown-link filter, any
doc/**/*.md, README.md, or the workflow itself. It reuses the existing
ci-cpp container image and the existing CODEBERG_TOKEN secret already
used by mirror-to-codeberg.yml — no new secret setup needed.
The job force-pushes an orphan commit to the `pages` branch on
codeberg.org/TMoussa/ConformalLabpp, which Codeberg Pages serves from
https://tmoussa.codeberg.page/ConformalLabpp/ (verified live).
README.md gains a Doxygen badge and a Documentation table row pointing
at the Pages URL. locked-vs-flexible.md (the reviewer-facing doc) is
updated to mention the Pages URL next to the Doxygen-coverage gap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Doxygen now builds with **0 warnings** (was 27).
Root cause: `[label](doc/api/tests.md)`-style relative markdown links in
README.md and CLAUDE.md were being interpreted by Doxygen as \ref
commands and failed to resolve (Doxygen indexes .md files by basename,
not by repo-relative path).
Fix: add a per-file `FILTER_PATTERNS` to Doxyfile that rewrites
`[label](path/to/file.md)` into `<a href="path/to/file.md">label</a>`
just for Doxygen. HTML anchors bypass \ref resolution entirely; the
generated Doxygen HTML still hyperlinks correctly. The on-disk
markdown is untouched, so GitHub rendering is unaffected.
New file: scripts/doxygen-md-filter.sh (24 lines, documented).
Also: append a "Known limitations (state at the time of the reviewer
meeting)" table to doc/architecture/locked-vs-flexible.md so the
external reviewer sees the 7 deliberate gaps (output_uv_map covers
3 of 5 entries; pipe-only chaining; Phase 9b-analytic derived but not
implemented; Doxygen WARN_IF_UNDOCUMENTED policy; CI test-count gate;
research-track utilities) with effort estimates next to each.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `operator|` in `namespace CGAL` so package-local named parameters
can be combined left-to-right without modifying CGAL upstream:
auto p = CGAL::parameters::gradient_tolerance(1e-12)
| CGAL::parameters::max_iterations(500)
| CGAL::parameters::output_uv_map(uv);
CGAL::discrete_conformal_map_euclidean(mesh, p);
Why not the canonical `.a().b().c()` syntax
───────────────────────────────────────────
CGAL's standard chaining mechanism requires registering each named
parameter as a member function on `Named_function_parameters` via the
`CGAL_add_named_parameter` macro in
`CGAL/STL_Extension/internal/parameters_interface.h` — a vendored
upstream file that conformallab++ deliberately treats as read-only.
Adding member-function chainers for our package-local tags would
require either forking CGAL or modifying the vendored copy. Neither
is acceptable for a library that wants to remain portable across
future CGAL releases.
The pipe-operator achieves the same compositional semantics via a
free function in `namespace CGAL` (so ADL finds it for
`Named_function_parameters` operands). Implementation: rebuild the
right-hand-side `Named_function_parameters` with the left-hand-side
as its `Base`, producing an indistinguishable chain that every entry
function accepts unchanged.
Implementation: `code/include/CGAL/Conformal_map/internal/parameters.h`
lines 158-187. The operator is constrained to right-hand-sides with
`No_property` base (i.e. fresh single-parameter packs from the helper
functions), so it never collides with any future CGAL operator on the
same type.
Tests (2 new, total Phase-8b-Lite suite 15 → 17)
────────────────────────────────────────────────
* CGALPhase8bLite.NamedParamPipe_MultipleParamsTakeEffect
Chain three parameters; verify all three take effect (tight
tolerance respected + UV pmap populated + iteration cap honoured).
* CGALPhase8bLite.NamedParamPipe_TwoParams
Chain two parameters; verify max_iterations(0) blocks the loop
even when combined with another param.
Full CGAL suite: 234/234 PASSED, 0 SKIPPED (was 232).
Total: 257/257 PASSED, 0 SKIPPED (was 255).
scripts/check-test-counts.sh: OK.
Documentation updates
─────────────────────
* doc/tutorials/add-output-uv-map.md §3.4: "Current limitation: no
chaining" → "Chaining: use the pipe operator `|`". Explains why
CGAL's `.member()` syntax isn't available and shows the `|`
workaround with a working code example.
* doc/architecture/locked-vs-flexible.md §8: chaining now flagged as
shipped via pipe; recommended posture says `.member()` chaining
only if a user pushes for the CGAL-canonical syntax.
* doc/roadmap/porting-status.md §5: API limitations table updated.
* doc/api/tests.md: CGALPhase8bLite row 15 → 17, total 232 → 234.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
doc/api/headers.md — all 24 public headers with descriptions
doc/api/tests.md — 26 test suites, individual counts, run instructions
doc/architecture/design-decisions.md — 5 key design choices with rationale
doc/architecture/project-structure.md — full directory tree + build targets
README + overall_pipeline.md link tables updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Structured the development roadmap into four blocks with an explicit
boundary marker separating direct Java ports (Phase 1–7) from
infrastructure (Phase 8), remaining porting (Phase 9), and new
research territory (Phase 10+).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
README:
- Neuer Einstieg mit vollständiger Dissertation-Referenz (Titel, TU Berlin 2016,
DOI 10.14279/depositonce-5415, CC BY-SA 4.0)
- Links zu Original-Java-Repo, sechel.de und linkedin.com/in/sechel
- Neuer Abschnitt "Ursprung & Danksagung" vor der Lizenz
doc/architecture/overall_pipeline.md:
- Neuer "Origin"-Abschnitt ganz oben mit vollständiger Quellenangabe
- Literaturabschnitt erweitert: Dissertation als "Primary source" hervorgehoben,
Java-Original-Repo als direkter Port-Bezug dokumentiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ersetzt den generischen Geometry-Framework-Entwurf durch eine präzise
Beschreibung der tatsächlichen konformen Geometrie-Pipeline:
- Klare Positionierung: spezialisiertes Werkzeug für diskrete konforme
Abbildungen, kein generisches Mesh-Processing-Framework
- Korrigiertes Mermaid-Diagramm: alle 3 Phasen mit realen Komponenten
(load_mesh → setup_maps → GB-check → Newton → CutGraph → Layout →
halfedge_uv → Holonomie → Periodenmatrix → Fundamentalbereich → Export)
- Preconditions/Capabilities-Tabelle für alle Processing-Units
- Drei Geometrie-Modi (Euklidisch/Sphärisch/Hyper-ideal) im Vergleich
- MobiusMap, halfedge_uv, Priority-BFS, SL(2,ℤ)-Reduktion dokumentiert
- Realistischer YAML-Pipeline-Entwurf als Phase-8-Ziel (Tokens statt Prosa)
- Erweiterungspunkte: neues Funktional, neue Geometrie, neues Unit
- Alle Literaturverweise direkt auf Implementierungsstellen gemappt
- "Nice To Have but maybe too much" komplett entfernt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>