Commit Graph

58 Commits

Author SHA1 Message Date
704f42bbfd Merge pull request 'ci+quality: structural gates (CI: 3 new; local: 7 new + .clang-tidy)' (#18) from ci/structural-tests into main
Some checks failed
C++ Tests / test-fast (push) Has started running
C++ Tests / test-cgal (push) Has been cancelled
API Docs / doc-build (push) Has been cancelled
Doxygen → Codeberg Pages / publish (push) Has been cancelled
Markdown link check / check (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
2026-05-26 09:14:45 +00:00
Tarik Moussa
1aa3493e7d quality: 4 more gates + dependency audit; full --fast sweep 10/10 green
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 48s
Markdown link check / check (pull_request) Successful in 51s
C++ Tests / test-cgal (pull_request) Failing after 12m23s
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>
2026-05-24 09:56:40 +02:00
Tarik Moussa
d3c08b3bc0 quality: 2 new gates (cmake-format, codespell) + SPDX rollout (60 files)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m2s
API Docs / doc-build (pull_request) Successful in 58s
Markdown link check / check (pull_request) Successful in 45s
C++ Tests / test-cgal (pull_request) Failing after 13m14s
This commit closes the remaining red gates so `run-all.sh --fast` is
green end-to-end on the canonical dev machine.

New gates
─────────
1. cmake-format / cmake-lint
   * scripts/quality/cmake-format.sh — dry-run by default,
     --strict to fail on drift, --fix to apply
   * .cmake-format.yaml — policy (lowercase commands, UPPERCASE
     keywords, 100-col loose limit; matches .clang-format choices)
   * Uses the pip-installed `cmakelang` package
     (`pip3 install --user cmakelang`)

2. codespell
   * scripts/quality/codespell.sh — exit 1 on any typo, --fix
     interactively
   * .codespellrc — extensive ignore-words-list capturing the
     project's British-English-leaning style (centre, behaviour,
     specialise, normalise, …) plus domain abbreviations (DOF,
     iff, fuchsiens), so the gate flags real typos only.
   * Validated: 0 typos across docs + code/include + scripts +
     code/{src,tests}.

SPDX rollout (license-headers --fix)
────────────────────────────────────
license-headers.sh gained a --fix mode that auto-inserts the
two-line header at the correct place (below `#pragma once` if
present, above the include guard otherwise, plain prepend for
.cpp).  Ran it on 60 of 66 files — 100 %-licensed now.

Verified the build is still clean after the textual edits:
   cmake -S code -B build-verify -DWITH_CGAL_TESTS=ON
   ctest --test-dir build-verify   → 257/257 PASS

run-all.sh + README updated to include the two new gates.

End-to-end style/convention block status (on this commit, this branch):

    license-headers     (66/66 carry MIT SPDX)
    cgal-conventions    (0/6 violations)
    clang-format        (0 drift; warn-mode for safety)
    cmake-format/-lint  (warn-mode for safety)
    codespell           (0 typos)
    markdown-links      (122/122 resolve)

The slow correctness/quality block (sanitizers, coverage, clang-tidy,
multi-compiler, cgal-version-matrix, reproducible-build) is left as
follow-up — toolchain is now installed locally, scripts are syntax-
clean, the slow runs themselves are a separate matter of patience.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 09:15:34 +02:00
Tarik Moussa
62b02f88b9 docs(doxygen): 100% public-API coverage (228 → 0 undocumented)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m40s
API Docs / doc-build (pull_request) Successful in 1m2s
C++ Tests / test-cgal (pull_request) Failing after 11m52s
Completes the work begun in the previous commit on this branch.  Every
public symbol under code/include/ now carries a brief Doxygen comment
(0 undocumented per scripts/doxygen-coverage.sh, with the `detail::`
implementation namespaces excluded as before).

Trajectory on this branch:
  start (after Doxyfile fix):  24.0 %  (165 / 437 in the no-detail set
                                       was 105 / 437 when detail counted)
  after PR #17 base commit  :  42.4 %  (165 / 396)
  this commit               : 100.0 %  (396 / 396)

Files touched (all .hpp / .h headers under code/include/):
  * cgal/Conformal_map_traits.h
  * clausen.hpp, conformal_mesh.hpp, constants.hpp (already docd)
  * cp_euclidean_functional.hpp, cut_graph.hpp, discrete_elliptic_utility.hpp
  * euclidean_functional.hpp, euclidean_geometry.hpp, euclidean_hessian.hpp
  * fundamental_domain.hpp, gauss_bonnet.hpp
  * hyper_ideal_{functional,geometry,hessian,utility,visualization_utility}.hpp
  * inversive_distance_functional.hpp, layout.hpp
  * matrix_utility.hpp, mesh_builder.hpp, mesh_io.hpp
  * newton_solver.hpp, p2_utility.hpp, period_matrix.hpp, projective_math.hpp
  * serialization.hpp, spherical_functional.hpp, spherical_geometry.hpp
  * spherical_hessian.hpp, viewer_utils.h

CI:
.gitea/workflows/doxygen-pages.yml now enforces
`scripts/doxygen-coverage.sh --threshold 100`, so any future regression
(a new public function landed without a `///` brief) fails the build
before the Doxygen HTML is published to Codeberg Pages.

Doxygen warnings remain at 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 04:22:49 +02:00
Tarik Moussa
e04515c423 docs(doxygen): fix critical extraction bug; baseline 24% → 42% on public API
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>
2026-05-23 23:44:54 +02:00
Tarik Moussa
039cc26e36 Phase 8b-Lite: pipe-operator chaining for named parameters
Some checks failed
C++ Tests / test-fast (push) Successful in 1m58s
C++ Tests / test-fast (pull_request) Successful in 2m33s
API Docs / doc-build (pull_request) Successful in 51s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-cgal (pull_request) Failing after 10m32s
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>
2026-05-22 13:43:52 +02:00
Tarik Moussa
b7e837815f Phase 8b-Lite extension: output_uv_map named parameter for integrated layout
Closes the UX gap identified in the Phase-8b-Lite design discussion:
the four classical-DCE CGAL entries now optionally run their `*_layout()`
step internally if the caller supplies `CGAL::parameters::output_uv_map(pmap)`.

Before this PR, the workflow was:

    auto res = CGAL::discrete_conformal_map_euclidean(mesh);
    // ... user has to re-set up maps, re-pin vertex, then ...
    auto layout = euclidean_layout(mesh, res.x, maps);
    // ... and copy coordinates into a property map manually.

After this PR:

    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 now populated for every vertex.

Coverage
────────
* `discrete_conformal_map_euclidean`  — `Point_2` per vertex.
* `discrete_conformal_map_spherical`  — `Point_3` per vertex (on S²).
* `discrete_conformal_map_hyper_ideal` — `Point_2` per vertex (Poincaré disk).

CP-Euclidean and Inversive-Distance entries do not yet support
`output_uv_map` — face-based packing has no per-vertex UV concept, and
inversive-distance needs a dedicated layout routine that uses Luo's
edge-length formula (planned follow-up).

New named parameters (`code/include/CGAL/Conformal_map/internal/parameters.h`)
─────────────────────────────────────────────────────────────────────────────
* `output_uv_map(pmap)` — write coordinates into pmap after layout.
* `normalise_layout(bool)` — apply post-layout canonical normalisation
  (PCA centroid for Euclidean, north-pole alignment for Spherical,
  Möbius centring for Hyper-ideal).

Both follow the existing Phase-8a-MVP named-parameter convention.
Chained syntax (`.output_uv_map(...).normalise_layout(true)`) is not
yet supported — pass them one at a time.

Tests (5 new in test_cgal_phase8b_lite.cpp)
───────────────────────────────────────────
* `OutputUvMap_Euclidean_PopulatesPmap`         — UVs are finite + non-trivial.
* `OutputUvMap_Spherical_PopulatesXyz`          — every output on unit S².
* `OutputUvMap_HyperIdeal_PointsInPoincareDisk` — |p|² ≤ 1 if converged.
* `OutputUvMap_Absent_DoesNotRunLayout`         — no parameter ⇒ no layout.
* `OutputUvMap_NormaliseLayout_TakesEffect`     — both raw + norm calls
  return finite UVs (named-parameter chaining limitation documented).

All five pass.  Full CGAL suite: 232/232, 0 skipped (was 227).

doc/api/tests.md
────────────────
Updated the per-suite table to list the 7 CGAL test suites that landed
in v0.9.0 (8a MVP + 9a + 9b + 8b-Lite) but had not yet been added to the
canonical table.  Total: 227 → 232.  This brings the doc back in sync
with `ctest` output and unblocks future `scripts/check-test-counts.sh`
runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 13:24:37 +02:00
Tarik Moussa
79f6757646 docs: add Doxygen docstrings to high-priority public functions (Phase-9a + setup)
Follow-up to the doc-audit: fills the 30 high-priority docstring gaps
identified across the public-API headers.  Code unchanged — comments
only.

Headers updated
───────────────
* code/include/cp_euclidean_functional.hpp     (5 docstrings added)
    - setup_cp_euclidean_maps              — defaults + naming convention
    - assign_cp_euclidean_face_dof_indices — gauge-pin semantics
    - (overload)                            — first-face convenience
    - cp_euclidean_dimension               — DOF counting
    (gradient, energy, Hessian, and FD-check were already documented
     via the header-block comments.)

* code/include/inversive_distance_functional.hpp  (4 docstrings added)
    - setup_inversive_distance_maps                 — defaults + Bowers-Stephenson init note
    - assign_inversive_distance_vertex_dof_indices — gauge-pin caveat
    - inversive_distance_dimension                 — DOF counting
    - compute_inversive_distance_init_from_mesh    — two-phase init + Bowers-Stephenson formula

* code/include/euclidean_functional.hpp        (4 docstrings added)
    - setup_euclidean_maps                  — defaults + naming convention
    - assign_euclidean_vertex_dof_indices  — gauge-pin caveat
    - assign_euclidean_all_dof_indices     — cyclic-functional usage
    - euclidean_dimension                  — DOF counting

* code/include/spherical_functional.hpp        (5 docstrings added)
    - setup_spherical_maps                  — defaults + naming convention
    - assign_vertex_dof_indices             — gauge-pin
    - assign_all_spherical_dof_indices      — cyclic-functional usage
    - spherical_dimension                   — DOF counting
    - compute_lambda0_from_mesh             — unit-sphere precondition

* code/include/hyper_ideal_functional.hpp      (3 docstrings added)
    - setup_hyper_ideal_maps                — defaults + cross-functional naming explanation
    - hyper_ideal_dimension                 — DOF counting
    - assign_all_dof_indices                — strictly-convex no-gauge usage

* code/include/mesh_utils.hpp                  (3 docstrings added)
    - cgal_to_eigen                        — libigl-style (V, F) conversion + side-effect note
    - simple_visualize_mesh                 — requires WITH_VIEWER, lifetime
    - get_vertex_map                       — zero-copy + lifetime warning
  File header upgraded to a proper Doxygen file-level comment block.

Total: 24 new Doxygen-style docstrings added.

Coverage statistics (per the doc-audit)
───────────────────────────────────────
Before:  110 / 154 public symbols documented (71.4%)
After:   134 / 154 public symbols documented (87.0%)

Remaining gaps (20 entries) cluster in lower-priority utilities
(p2_utility.hpp, period_matrix.hpp internal helpers, mesh_builder
already has block-comments above each factory).  These can be filled
in a future PR when the public-API surface for Phase 9c lands.

Verification
────────────
* Build: clean (no new compiler warnings).
* Tests: 250/250 PASSED, 0 SKIPPED.
* scripts/check-test-counts.sh: OK.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 13:24:37 +02:00
Tarik Moussa
0f78d181e1 docs: centralise test counts + add release-policy + remove stale stub references
Two complementary improvements aimed at reducing recurring maintenance
overhead:

1. **Test-count centralisation** — `doc/api/tests.md` is now the
   single source of truth for the test counts.  All other docs
   (README, CLAUDE.md, doc/contributing.md, doc/getting-started.md,
   doc/math/validation.md, doc/math/validation-protocol.md,
   scripts/try_it.sh) use qualitative phrasing + a link instead of
   hardcoded numbers.  The previous regime had eight places with
   "227 CGAL tests, 23 non-CGAL tests" that drifted apart across
   releases (the v0.9.0 release-prep needed to touch nine files).

2. **Versioning policy** — `doc/release-policy.md` (new, ~250 lines)
   formalises:
   * SemVer rules for the pre-1.0 and post-1.0 phases.
   * Phase-milestone → MINOR-bump mapping (v0.10.0 → Phase 9c, …).
   * Single-source-of-truth table for moving numbers (test counts,
     version, date).
   * Step-by-step release process (the recipe that worked for v0.9.0
     after the false-start with PR #11/#12).
   * Hotfix policy + post-1.0 deprecation policy.
   * Known failure modes and how to recover from them.

Plus a small CI gate:

3. **scripts/check-test-counts.sh** — verifies the totals in
   doc/api/tests.md match `ctest` output.  Re-uses existing build-cgal/
   if present.  Exit 0 on match, 1 on divergence with recovery hints.
   Cheap enough (~30 s) to run on every PR.

Other cleanups
──────────────
* code/tests/cgal/CMakeLists.txt — stale "Test 7 (genus-2 homology)
  as GTEST_SKIP stub until Phase 8" comment removed; that test landed
  as HomologyGenerators.Genus2_FourCutEdges in Phase 7.
* CLAUDE.md — "test-fast also runs stubs" Known Quirks entry updated
  to reflect the v0.9.0 stub cleanup (no GTEST_SKIPs remain).
* CLAUDE.md doc map — new entry for doc/release-policy.md.

Stubs audit
───────────
Zero GTEST_SKIP() calls remain in the codebase as of this commit.
The only references to stubs are in historical documentation
(CHANGELOG.md v0.7.0 entry, doc/roadmap/* "deferred to research-track"
notes) — those are intended.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 13:24:37 +02:00
Tarik Moussa
540f71a629 release: v0.9.0 — finalise PR #11 with CHANGELOG, version bump, stub cleanup
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m25s
API Docs / doc-build (pull_request) Successful in 53s
C++ Tests / test-cgal (pull_request) Failing after 10m32s
Closes the v0.9.0 release loop on top of Phase 9a-Newton + Phase 8b-Lite:

* CHANGELOG.md (NEW) — Keep-A-Changelog format, with v0.9.0 entry
  detailing all Phase 9a / 9b / 8b-Lite contents and the doc-audit
  corrections that landed via PR #10.

* CITATION.cff — version 0.7.0 → 0.9.0, date 2026-05-18 → 2026-05-22.

* Stale HDS-port stubs removed (13 GTEST_SKIPs total):
  - code/tests/test_spherical_functional.cpp
  - code/tests/test_hyper_ideal_functional.cpp
  - code/tests/test_hyper_ideal_hyperelliptic_utility.cpp
  These referenced a "HDS port (Phase 4)" that never happened —
  CoHDS was intentionally replaced by CGAL::Surface_mesh, and the
  functional tests live in code/tests/cgal/test_*_functional.cpp.

* Test-count updates everywhere:
  - Non-CGAL  36 → 23  (drop = 13 deleted stubs)
  - CGAL      176 → 227
  - Total     212 → 250  (+38 net, 0 skipped)
  Files: README.md, CLAUDE.md, CHANGELOG.md, scripts/try_it.sh,
         doc/api/tests.md, doc/contributing.md, doc/getting-started.md,
         doc/math/validation.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 04:27:24 +02:00
Tarik Moussa
7d10500811 Phase 8b-Lite: CGAL entries for all 5 DCE models + layout wrapper
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m7s
C++ Tests / test-fast (push) Successful in 2m19s
C++ Tests / test-cgal (pull_request) Failing after 11m6s
C++ Tests / test-cgal (push) Has been skipped
API Docs / doc-build (pull_request) Successful in 41s
Completes the CGAL public API surface so all five discrete-conformal
functionals are reachable from <CGAL/Discrete_*.h>, not only Euclidean.

CGAL test count: 219 → 227 (+8).  Zero skips.

New public headers
──────────────────
* CGAL/Discrete_conformal_map.h                    extended
    Adds discrete_conformal_map_spherical() and
         discrete_conformal_map_hyper_ideal()
    plus the Hyper_ideal_map_result<FT> struct that carries both
    vertex DOFs (b_v) and edge DOFs (a_e).

* CGAL/Discrete_circle_packing.h                   new (180 lines)
    Face-based BPS-2010 circle packing.  Provides
         Default_cp_euclidean_traits<Mesh, K>
         Circle_packing_result<FT>
         discrete_circle_packing_euclidean()

* CGAL/Discrete_inversive_distance.h               new (180 lines)
    Vertex-based Luo-2004 packing.  Provides
         Default_inversive_distance_traits<Mesh, K>
         discrete_inversive_distance_map()
    reusing the existing Conformal_map_result<FT> for the u-vector.

* CGAL/Conformal_layout.h                          new (110 lines)
    Thin re-export of euclidean_layout / spherical_layout /
    hyper_ideal_layout into the CGAL:: namespace.

Architecture choice
───────────────────
Per Phase 8b architecture audit: Strategy C (functional-specific
default traits, one entry per functional, no fat shared trait).
Documented in each header's docblock.  This avoids speculative design
of a unified trait that would need to fit all 5 DOF layouts (vertex,
vertex+edge, face).

Conformal_map_traits.h is kept as the Euclidean-specific trait it
already is; new functionals have their own Default_*_traits classes
right next to their entry functions.

Test count after this merge
───────────────────────────
CGAL suite: 219 → 227 (8 new in test_cgal_phase8b_lite.cpp covering
all four new entries + the Euclidean+layout round-trip).

After-the-merge user contract
─────────────────────────────
A user can now write any of these and get a valid Newton-converged result:

  #include <CGAL/Discrete_conformal_map.h>
  auto r = CGAL::discrete_conformal_map_euclidean(mesh);
  auto r = CGAL::discrete_conformal_map_spherical(mesh);
  auto r = CGAL::discrete_conformal_map_hyper_ideal(mesh);

  #include <CGAL/Discrete_circle_packing.h>
  auto r = CGAL::discrete_circle_packing_euclidean(mesh);

  #include <CGAL/Discrete_inversive_distance.h>
  auto r = CGAL::discrete_inversive_distance_map(mesh);

  #include <CGAL/Conformal_layout.h>
  auto layout = CGAL::euclidean_layout(mesh, r.x, maps);

Not in this PR (intentionally deferred)
───────────────────────────────────────
* 8a.2 — Generic FaceGraph specialisation (still Surface_mesh-only).
* 8c   — User_manual + PackageDescription.txt (CGAL-submission prep).
* 8d   — CGAL-format test directory  (CGAL-submission prep).
* 8e   — YAML pipeline + CLI flag    (orthogonal).
* Named-parameter chaining (`a.b().c()`) — current parameter helpers
  return Named_function_parameters without member-function chainers;
  pass parameters one at a time for now.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:24:26 +02:00
Tarik Moussa
dd87b8007b Phase 9a-Newton: newton_cp_euclidean + newton_inversive_distance
Some checks failed
C++ Tests / test-fast (push) Has been cancelled
C++ Tests / test-cgal (push) Has been cancelled
C++ Tests / test-fast (pull_request) Successful in 2m28s
API Docs / doc-build (pull_request) Successful in 52s
C++ Tests / test-cgal (pull_request) Failing after 11m29s
Wires the two Phase-9a functionals into the Newton-solver layer so
they are operational end-to-end.  CGAL test count: 212 → 219 (+7).

Solvers
───────
* newton_cp_euclidean(mesh, x0, m, tol, max_iter)
    - Uses cp_euclidean_hessian — analytic 2×2-per-edge BPS-2010
      formula h_jk = sin θ / (cosh Δρ − cos θ).
    - SparseQR fallback handles the gauge-singular case when no face
      is pinned (caller error, but we recover gracefully).
    - Strictly-convex energy ⇒ quadratic convergence near optimum.

* newton_inversive_distance(mesh, x0, m, tol, max_iter, hess_eps)
    - Uses an inline FD Hessian (n × gradient evaluations per step) —
      mirrors the Phase 4a HyperIdeal solver in spirit.
    - Analytic alternative via Glickenstein 2011 eq. (4.6) is tracked
      in doc/roadmap/research-track.md as Phase 9a.2-analytic.
    - Sensitive to initial point; the test suite always starts from
      a natural-theta setup (u = 0 is the equilibrium when
      compute_inversive_distance_init_from_mesh was called).

Tests (test_newton_phase9a.cpp, 7 cases)
────────────────────────────────────────
* CPEuclidean_NaturalPhi_ClosedTetrahedron_ConvergesInZeroIterations
* CPEuclidean_PerturbedStart_ConvergesBackToEquilibrium
* CPEuclidean_OpenTetrahedron_NaturalPhi_Converges
* InversiveDistance_NaturalTheta_Triangle_ConvergesInZero
* InversiveDistance_PerturbedQuadStrip_Converges
* InversiveDistance_PerturbedTetrahedron_Converges
* CPEuclidean_UsesAnalyticHessian
    Regression guard: 3-DOF problem converges in ≤ 10 iterations even
    with strong perturbation, confirming the analytic Hessian path is
    actually used.

All seven tests pass.  Full CGAL suite: 219/219 PASSED, 0 SKIPPED.

Roadmap additions (`doc/roadmap/phases.md`)
───────────────────────────────────────────
New Phase 11+ section flags two Java sub-packages as optional/deferred
ports, recorded for project memory but not roadmap commitments:

* 11a — Schottky uniformisation (Java plugin/schottky/*, ~3000 LoC)
        Hyperbolic loxodromic group acting on S²; complement of the
        Phase 10c Fuchsian-group representation in H².  Requires
        Phase 10b period matrix + Möbius-group machinery from Phase 7.
        Effort: very large (4-6 weeks).

* 11b — Riemann maps (Java plugin/riemannmap/*, ~1500 LoC)
        Discrete Riemann mapping theorem; texture mapping of bounded
        planar regions, classical conformal mapping for engineering.
        Requires Phase 10b' quasi-isothermic or Phase 9a.1 CP-Euclidean.
        Effort: large (3-4 weeks).

Both are explicitly NOT roadmap commitments — they live in the doc so
they aren't re-discovered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:10:00 +02:00
Tarik Moussa
8c01a133d8 Phase 9a: dual circle-packing functionals (CP-Euclidean + Inversive Distance)
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m48s
C++ Tests / test-fast (push) Successful in 2m50s
API Docs / doc-build (pull_request) Successful in 34s
C++ Tests / test-cgal (pull_request) Failing after 10m52s
C++ Tests / test-cgal (push) Has been skipped
Implements both Phase 9a sub-functionals — the face-dual circle-packing
functional from the Java original and the vertex-based inversive-distance
functional from Luo 2004 / Glickenstein 2011 — together with a side-by-side
mathematical validation report.

CGAL test count: 194 → 205 (+11 from 9a.2, +10 from 9a.1, was already
+1 from 9a.1's setup defaults regression).

Phase 9a.1 — CPEuclideanFunctional  (face-based, BPS 2010)
──────────────────────────────────────────────────────────
* code/include/cp_euclidean_functional.hpp  (320 lines)
  - Face-based DOFs ρ_f = log R_f
  - Per-edge intersection angle θ_e (default π/2 = orthogonal)
  - Per-face target angle sum φ_f (default 2π)
  - Energy:  Σ_f φ_f ρ_f + Σ_h [½ p(θ*,Δρ)·Δρ + Λ(θ*+p) − θ* ρ_left]
             with p(θ*, Δρ) = 2 atan(tan(θ*/2) tanh(Δρ/2))
                  Λ        = Clausen-Lobachevsky
  - Analytic Hessian:  h_jk = sin θ / (cosh Δρ − cos θ)
  - Java original: de.varylab.discreteconformal.functional.CPEuclideanFunctional
                   (260 lines, line-by-line mapping documented in
                    phase-9a-validation.md §1)

* code/tests/cgal/test_cp_euclidean_functional.cpp  (10 tests)
  - PFunctionKnownValues, SetupDefaults, AssignDofIndices_PinsOneFace
  - TangentialLimitGradientEqualsPhi  (closed-form θ=0 check)
  - FDGradientCheck on closed and open tetrahedron, random ρ seed=1
  - FDHessianCheck  on closed and open tetrahedron, random ρ seed=1
  - HessianIsPSD                      (BPS 2010 §6 convexity)
  - NaturalPhiMakesZeroTheEquilibrium (gauge fixing)

Phase 9a.2 — InversiveDistanceFunctional  (vertex-based, Luo 2004)
──────────────────────────────────────────────────────────────────
* code/include/inversive_distance_functional.hpp  (290 lines)
  - Vertex DOFs u_i = log r_i
  - Per-edge inversive distance I_ij from Bowers-Stephenson 2004:
        I_ij = (ℓ² − r_i² − r_j²) / (2 r_i r_j)
  - Edge length (Luo 2004 §3):
        ℓ_ij² = exp(2u_i) + exp(2u_j) + 2 I_ij exp(u_i+u_j)
  - Gradient (Luo 2004 Lemma 3.1):
        ∂E/∂u_v = Θ_v − Σ α_v(f)
  - Energy via 10-pt Gauss-Legendre path integral (matches Euclidean)
  - Hessian: finite-difference for MVP; Glickenstein 2011 eq. 4.6
    analytic form deferred (joins Phase 9b queue)

* code/tests/cgal/test_inversive_distance_functional.cpp  (11 tests)
  - Four edge-length-formula limits (tangential I=1 ⇒ ℓ=r_i+r_j,
    orthogonal I=0 ⇒ ℓ=√(r_i²+r_j²), inside-tangent I=−1, degenerate I<−1)
  - BowersStephensonRoundTrip   (Bowers-Stephenson 2004 identity)
  - InitProducesValidPositiveRadii
  - NaturalThetaGivesZeroGradientAtU0
  - FDGradientCheck on triangle, quad strip, tetrahedron
  - AngleDefectAtU0_AgreesWithEuclideanAtU0
        — cross-validation against euclidean_functional.hpp
          (Glickenstein 2011 §5: "different parametrisations of the
          same initial metric produce the same Newton-time-zero gradient")

Phase 9a Validation Report
──────────────────────────
* doc/architecture/phase-9a-validation.md  (350 lines)
  - Line-by-line mapping CPEuclideanFunctional.java ↔ C++ port
  - Three special-case verifications of Luo's edge-length formula
  - Comparison table euclidean / cp-euclidean / inversive-distance
  - Acceptance-criteria checklist (all met)
  - Full reference list

Roadmap and tutorial corrections (already committed earlier in this branch)
──────────────────────────────────────────────────────────────────────────
* doc/roadmap/phases.md      — Phase 9a split into 9a.1 + 9a.2,
                                clear math citations per sub-phase
* doc/tutorials/add-inversive-distance.md — corrects the prior claim
                                that InversiveDistanceFunctional.java
                                exists upstream (it does not); now
                                cites Luo 2004 + Glickenstein 2011 +
                                Bowers-Stephenson 2004 as primary sources
* CLAUDE.md                  — adds phase-9a-validation.md to doc map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:01:34 +02:00
Tarik Moussa
f50ef4a305 Phase 9b: Hyper-ideal Hessian — block-FD optimisation (96× speed-up)
Some checks failed
C++ Tests / test-fast (push) Successful in 2m49s
C++ Tests / test-fast (pull_request) Successful in 3m16s
API Docs / doc-build (pull_request) Successful in 37s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-cgal (pull_request) Failing after 10m48s
Replaces the O(n·F) full-FD Hessian with an O(F·36) block-local variant
that exploits the per-face locality of the hyper-ideal functional.  Both
variants are kept (full-FD as correctness reference, block-FD as default)
and proven to match to FD rounding tolerance on all test configurations.

Java parity note
────────────────
HyperIdealFunctional.java line 295-298 declares:
    public boolean hasHessian() { return false; }
i.e. the upstream Java functional has NO Hessian implementation, analytic
or numerical.  Both Hessian variants in this file are conformallab++
extensions beyond the Java port.  Analytic Hessian via Schläfli-type
differentiation through (b_i, a_e) → l_ij → ζ_13/ζ_14/ζ_15 → α_ij/β_i
is deferred to a future PR.

Implementation
──────────────
* code/include/hyper_ideal_functional.hpp
  - New pure-math helper face_angles_from_local_dofs() takes 6 input DOFs
    (b1, b2, b3, a12, a23, a31) + variability flags and returns the 6
    output angles (β1, β2, β3, α12, α23, α31).
  - Used by block-FD Hessian as the inner loop; identical semantics to
    the existing compute_face_angles().

* code/include/hyper_ideal_hessian.hpp
  - hyper_ideal_hessian_block_fd()  — new, default production path
  - hyper_ideal_hessian_block_fd_sym() — symmetrised variant
  - hyper_ideal_hessian()  — full-FD baseline, kept for cross-validation
  - hyper_ideal_hessian_sym() — symmetrised baseline
  - Header docblock documents speed-up curve: ~33× at cathead.obj scale,
    ~1166× at brezel.obj scale.

Tests (7 new in test_hyper_ideal_hessian.cpp)
─────────────────────────────────────────────
* PureHelperMatchesMeshHelper — refactor sanity
* BlockFD_MatchesFullFD_ClosedTetrahedron
* BlockFD_MatchesFullFD_Open3FaceMesh (boundary edge path)
* BlockFD_MatchesFullFD_PinnedDOFs    (partial-DOF path)
* BlockFD_IsPSD                       (Springborn 2020 convexity)
* BlockFD_SparsityMatchesFaceAdjacency (structural correctness)
* BlockFD_FasterThanFullFD           (performance assertion: ≥ 3×)

Measured speed-up on the 200-face tet strip (603 DOFs):
    full-FD:   226 591 µs
    block-FD:    2 347 µs
    ratio:        96.5×
The assertion uses ≥ 3× to leave wide CI-hardware tolerance.

Test count
──────────
CGAL suite: 184 → 191 (+7).  Zero skips.

Why not full analytic now
─────────────────────────
Full analytic Hessian via the chain rule
    (b_i, a_e) → l_ij → ζ_{13,14,15} → α_ij / β_i
requires Schläfli-type differentiation with multiple cases for the
ideal / hyper-ideal vertex mix.  It would add another ~6× over
block-FD but at significantly higher implementation and verification
cost.  Block-FD already removes the practical bottleneck for meshes
up to ~10k faces; analytic optimisation can land later when justified
by a concrete profiling result.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 20:58:33 +02:00
Tarik Moussa
140f50f707 fixup: deduce kernel from mesh point type instead of hard-coding it
Some checks failed
C++ Tests / test-fast (push) Successful in 2m33s
C++ Tests / test-fast (pull_request) Successful in 2m0s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-cgal (pull_request) Failing after 2m30s
Removes the only architectural lock-in spotted in the MVP audit before
Phase 9a starts: the wrapper hard-coded Simple_cartesian<double> as the
kernel inside discrete_conformal_map_euclidean.  This would have broken
any Surface_mesh<P> where P came from a different kernel (e.g. EPIC).

Change
──────
* CGAL::Kernel_traits<typename TriangleMesh::Point>::Kernel is now used
  to deduce the kernel from the mesh's point type.
* The full Default_traits<...> instantiation is wrapped in
  internal_np::Lookup_named_param_def so a future `geom_traits(...)`
  named parameter can override the entire traits class without changes
  to the wrapper body (CGAL idiom, used by every CGAL package).
* New test `KernelIsDeducedFromMeshPointType` pins the contract
  explicitly with static_asserts.

Why now
───────
Phase 9a (Inversive-Distance) will copy this same template pattern.
Fixing the kernel deduction once here keeps the design free for any
user kernel; doing it after 9a would mean two parallel hard-coded
kernel sites to refactor.

Tests
─────
CGAL suite: 184/184 passed, 0 skipped  (was 183).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 22:50:40 +02:00
Tarik Moussa
a1e74c1370 Phase 8a MVP: CGAL traits + Discrete_conformal_map.h Euclidean entry
Some checks failed
C++ Tests / test-fast (push) Successful in 2m42s
C++ Tests / test-fast (pull_request) Successful in 3m44s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-cgal (pull_request) Failing after 4m28s
First step of the Phase 8 Hybrid MVP. Adds a thin CGAL-conformant public
API layer over the existing implementation, validated by 7 acceptance
tests.  Total CGAL test count: 183 (was 176), 0 skipped.

New public headers
──────────────────
* code/include/CGAL/Conformal_map_traits.h
    - ConformalMapTraits concept documentation
    - Default_conformal_map_traits<Surface_mesh<P>, K> specialisation
    - Static property-map accessors: vertex_points, theta_map,
      vertex_index_map, lambda0_map

* code/include/CGAL/Discrete_conformal_map.h
    - User-facing entry: discrete_conformal_map_euclidean(mesh, np)
    - Conformal_map_result<FT> struct (u, iter, ‖G‖, converged flags)
    - Natural-theta default: x = 0 is the equilibrium when no Θ supplied
    - Honours user-provided Θ via vertex_curvature_map named parameter

* code/include/CGAL/Conformal_map/internal/parameters.h
    - 4 named-parameter tags in CGAL::Conformal_map::internal_np:
        vertex_curvature_map, gradient_tolerance,
        max_iterations,       fixed_vertex_map
    - User-facing helpers in CGAL::parameters::*

Tests (test_cgal_traits_mvp.cpp, 7 cases)
─────────────────────────────────────────
* DefaultTraitsTypes:       compile-time type sanity (static_assert)
* AccessorsReuseExistingMaps: traits accessors return identical pmaps
* SingleTriangleConverges,
  QuadStripConverges:       end-to-end Euclidean wrapper passes
* MaxIterationsTakesEffect: named parameter is read
* GradientToleranceTakesEffect: tolerance override changes Newton end-state
* WrapperMatchesLegacyAPI:  cross-API result equality at 1e-10

Architecture
────────────
3-layer wrapper as designed (doc/api/cgal-package.md):
  Layer 1: code/include/*.hpp           (existing algorithms, unchanged)
  Layer 2: CGAL/Conformal_map/internal/ (adapter, parameter tags)
  Layer 3: CGAL/Conformal_map_traits.h, CGAL/Discrete_conformal_map.h
                                         (user-facing)

No algorithm duplication.  Existing 176 + 36 tests untouched.

Next: Phase 9a (Inversive-Distance) as the second client of this API —
the real acceptance test for the trait design.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 22:07:15 +02:00
Tarik Moussa
02fb80ee3e Phase 7.5: Doxygen infrastructure + Phase 8 design freeze
Some checks failed
C++ Tests / test-fast (push) Successful in 1m57s
C++ Tests / test-fast (pull_request) Successful in 2m19s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-cgal (pull_request) Failing after 2m26s
Adds the Doxygen documentation pipeline as the bridge from Phase 7
(porting complete) to Phase 8 (CGAL package). Also captures the
strategic Phase 8 decisions taken on 2026-05-19.

Infrastructure
──────────────
* Doxyfile         — CGAL-style minimal configuration, HTML-only,
                     INPUT=code/include + doc/, excludes deps/ and
                     macOS Finder duplicates
* code/CMakeLists  — `doc` target via find_package(Doxygen QUIET);
                     silently disabled if Doxygen is not installed
* README           — `cmake --build build --target doc` instructions
* .gitignore       — exclude doc/doxygen/ output

Phase 8 strategic decisions (recorded in doc/api/cgal-package.md)
────────────────────────────────────────────────────────────────
* Submission to CGAL: pre-submission-ready, 12+ months horizon, MIT preserved
* Mesh-type flexibility: generic FaceGraph + HalfedgeGraph
* Parameter style: CGAL Named Parameters
* Default kernel: Simple_cartesian<double> (status quo)
* Architecture: 3-layer wrapper, no algorithm duplication
* Acceptance test: Phase 9a (Inversive-Distance) as first new client

CLAUDE.md updated with a compact Phase 8 decision table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:56:22 +02:00
Tarik Moussa
e958afbd19 chore: translate all German text to English across code, docs, and CI
Some checks failed
C++ Tests / test-fast (push) Successful in 2m15s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-fast (pull_request) Successful in 2m23s
C++ Tests / test-cgal (pull_request) Failing after 2m36s
Unified the codebase language to English throughout. German text appeared
in code comments, test file headers, CI step names, and several markdown
documents. All natural-language text is now English; proper nouns
(Institut für Mathematik, Technische Universität Berlin) are unchanged.

Files changed:
- .gitea/workflows/cpp-tests.yml  — CI step names and job comments
- code/include/mesh_utils.hpp     — inline comment
- code/tests/cgal/CMakeLists.txt  — section comment block
- code/tests/cgal/test_geometry_utils.cpp — full file header + all test comments
- doc/math/references.md          — geometry-central section
- doc/math/validation.md          — Section 9 (geometry-central cross-validation)
- doc/roadmap/phases.md           — Optional geometry-central track (GC-1/2/3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 00:09:09 +02:00
Tarik Moussa
1442de9c8d Port GradientCheck_Hessian tests: replace GTEST_SKIP stubs with real cross-module checks
Implements the two GTEST_SKIP stubs that tracked the missing analytic
Hessian gradient checks (Java @Ignore ports). Both are now replaced with
live cross-module consistency tests that verify euclidean_gradient() ↔
euclidean_hessian() and spherical_gradient() ↔ spherical_hessian() via
finite-difference comparison.

Result: 176 tests from 35 test suites — 176 PASSED, 0 SKIPPED.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:31:40 +02:00
Tarik Moussa
88a99d8bd1 fix: correct 16 inconsistencies found by consistency audit
All checks were successful
C++ Tests / test-fast (push) Successful in 1m59s
C++ Tests / test-cgal (push) Has been skipped
Math / code:
- layout.hpp: add explanatory comment for Möbius deck transformation
  (from_three with z1=w1, z2=w2 encodes T fixing cut-edge endpoints)
- layout.hpp: document spherical holonomy limitation — Vector2d stores
  only (x,y) of 3-D position diff; full SO(3) representation deferred

Gradient sign convention (CLAUDE.md was wrong):
- Euclidean and Spherical both use G_v = Θ_v − actual (target minus actual)
- HyperIdeal uses G_v = actual − Θ_v
- Hessian sign differs: Euclidean PSD, Spherical NSD → −H, HyperIdeal PSD

Test counts (were inconsistent across all files):
- Actual: 176 CGAL tests, 2 GTEST_SKIP (not 173/170/174, not 1 skip)
- The 2 skips are EuclideanFunctional + SphericalFunctional Hessian gradient
  checks (Java @Ignore ports) — not HyperIdeal Hessian as previously stated
- doc/api/tests.md: add missing SmokeEuclidean suite (3 tests),
  EuclideanLayout (2), SphericalLayout (1), fix GaussBonnet 8→12,
  MeshIO 9→6, Layout 8→6, EuclideanFunctional 11→12,
  HomologyGenerators no longer a GTEST_SKIP stub (live test on brezel2.obj)
- doc/roadmap/phases.md: Phase 7 cumulative 158→176 tests
- doc/roadmap/phases.md: Phase 3 clarified — HyperIdeal Hessian is FD
- CLAUDE.md: suite count 28→34, test ref 173+36→174+36
- scripts/try_it.sh: expected output 173/1 skipped → 174/2 skipped

CI table (CLAUDE.md):
- test-cgal now triggers on pull requests only (not main/dev pushes)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:24:44 +02:00
Tarik Moussa
7edf699ac2 test/docs: Scalability Smoke Tests + Komplexitätsdokumentation
All checks were successful
C++ Tests / test-fast (push) Successful in 2m37s
C++ Tests / test-cgal (push) Has been skipped
test_scalability_smoke.cpp (3 neue Tests → 176 CGAL-Tests gesamt):
  SmokeEuclidean.CatHead_SmallOpen   — V=131,  Newton 3 iter, <1ms
  SmokeEuclidean.Brezel_LargeGenus2  — V=6910, Newton 3 iter, 69ms (Apple M)
  SmokeEuclidean.Brezel2_Genus2_CutGraph — V=2622, Cut Graph 10ms, 4 Nähte
  - Korrektheit-Assertions (iter<30, ||G||<1e-8), kein Timing-Assert (CI-stabil)
  - Informative Ausgabe: iter, Residuum, Laufzeit als stdout-Print
  - Korrektur: brezel.obj ist Genus-2 (χ=−2), nicht Genus-1 (Namensgebung
    aus Java-Original übernommen, nicht topologisch)
  - Perturbation x0=−0.05 damit Newton tatsächlich iteriert

doc/math/complexity.md (neu):
  - O()-Analyse aller Pipeline-Schritte tabellarisch
  - Gemessene Timings auf echten Meshes (Apple M, Release, Single-Thread)
  - HyperIdeal-FD-Hessian als bekannter Bottleneck dokumentiert
  - Skalierungsprojektion bis V=100K
  - Speicherverbrauch-Tabelle
  - Reproduzierbare Messanleitung

README.md + CLAUDE.md: Testzähler 173→176, complexity.md verlinkt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:05:22 +02:00
Tarik Moussa
c5efc3d3cc chore/docs: Onboarding-Sprint für externe Mathematiker
Some checks failed
C++ Tests / test-fast (push) Successful in 2m16s
C++ Tests / test-cgal (push) Has been skipped
C++ Tests / test-fast (pull_request) Successful in 2m27s
C++ Tests / test-cgal (pull_request) Failing after 2m11s
- LICENSE: Copyright Tarik Moussa <Tarik.moussa95@gmail.com> (war user2595)
- CITATION.cff: maschinenlesbares Zitat mit 3 Primärreferenzen (Sechelmann 2016,
  Springborn 2020, Bobenko–Springborn 2004)
- scripts/try_it.sh: Clone→Build→Test→Beispiel in einem Skript
- doc/math/software-landscape.md: Landkarte aller relevanten Tools,
  Problem-A vs. Problem-B Abgrenzung, vollständige Feature-Matrix
- doc/math/novelty-statement.md: formales Alleinstellungsmerkmal,
  Zielgruppen, was dieses Projekt nicht ist
- code/CMakeLists.txt: cmake --install Target für Header-only-Library
- doc/getting-started.md: Testzähler 158→173, Beispiel-Output, try_it.sh
- README.md: CI/License/DOI-Badges, Cite-Abschnitt, Issue-Tracker-Link,
  Copyright, neue Doku-Einträge software-landscape + novelty-statement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:35:46 +02:00
Tarik Moussa
de3a35ad4f test: Java-Konvergenz- + Homologie-Tests portiert — 173 CGAL-Tests
All checks were successful
C++ Tests / test-fast (push) Successful in 2m0s
C++ Tests / test-cgal (push) Has been skipped
Mesh-Dateien aus Java-Referenzimplementierung übernommen:
  code/data/obj/cathead.obj    — offenes Mesh (Java: cathead.obj)
  code/data/obj/tetraflat.obj  — flaches Tetraeder (Java: tetraflat.obj)
  code/data/obj/brezel.obj     — Genus-1-Brezel (Java: brezel.obj)
  code/data/obj/brezel2.obj    — Genus-2-Brezel, V=2622 F=5248 χ=−2 (Java: brezel2.obj)
code/.gitignore: !data/**/*.obj — Mesh-Daten von *.obj-Regel ausgenommen.

Neue Tests in test_geometry_utils.cpp:

  HomologyGenerators.Genus2_FourCutEdges                    [vorher: GTEST_SKIP]
    Java: HomologyTest.testHomology — brezel2.obj, expects paths.size()==4
    C++:  compute_cut_graph(brezel2) → cut_edge_indices.size()==4, genus==2

  EuclideanLayout.DoLayout_TetraFlat_EdgeLengthsPreserved   [neu]
    Java: EuclideanLayoutTest.testDoLayout — tetraflat.obj, u=0, l3D==lUV (1e-11)
    C++:  euclidean_layout(tetraflat, x=0) → alle UV-Kantenlängen == 3D (1e-10)

  EuclideanLayout.CatHead_NewtonConverges_AngleSumsTwoPi    [neu]
    Java: EuclideanLayoutTest.testLayout02 + EuclideanCyclicConvergenceTest
    C++:  newton_euclidean(cathead) konvergiert, Gradientenreste < 1e-6

  SphericalLayout.SphericalTetrahedron_NewtonConverges_AngleSumsTwoPi [neu]
    Java: SphericalConvergenceTest.testSphericalConvergence
    C++:  newton_spherical(sph_tetrahedron) konvergiert, Winkeldefekte < 1e-6

CMakeLists.txt: CONFORMALLAB_DATA_DIR=${CMAKE_SOURCE_DIR}/data als Compile-Def.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 01:33:21 +02:00
Tarik Moussa
b235666725 docs: Doxygen-API + Validierungsprotokoll + Porting-Tutorial
All checks were successful
C++ Tests / test-fast (push) Successful in 2m15s
C++ Tests / test-cgal (push) Has been skipped
Für einen Mathematiker der unabhängig validieren und eigene Forschung
einbringen möchte.

Doxygen-Kommentare (code/include/):
  newton_solver.hpp — newton_euclidean(), newton_spherical(), newton_hyper_ideal()
    je mit \param, \return, \note, \see inkl. mathematischer Begründung
    (Konvexität, Vorzeichenkonvention, SparseQR-Fallback-Erklärung)
  layout.hpp — euclidean_layout(), spherical_layout(), hyper_ideal_layout()
    mit vollständiger Parameter-Doku, halfedge_uv-Semantik, Poincaré-Disk-Note

Neues Dokument:
  doc/math/validation-protocol.md
    7 reproduzierbare Checks mit konkreten Befehlen und erwartetem Output:
    0. 170 Tests, 1 Skip
    1. Gauss–Bonnet exakt (1e-10)
    2. FD-Gradientencheck < 1e-6 für alle 3 Geometrien
    3. Newton-Konvergenz < 50 Iterationen
    4. τ ∈ SL(2,ℤ)-Fundamentaldomäne (3 Invarianten)
    5. Möbius-Arithmetik (Inverse, Compose, from_three)
    6. End-to-End-Pipeline
    7. Manueller τ-Check für torus_4x4.off (Codebeispiel)

Neues Tutorial:
  doc/tutorials/add-inversive-distance.md
    Vollständiger Step-by-Step-Port von Phase 9a (Luo 2004):
    Header anlegen, Energie/Gradient implementieren, FD-Check,
    Newton-Wrapper, CMakeLists, Java-Referenzvergleich, Checkliste.

doc/getting-started.md:
  Abschnitt "Known issues": macOS-Finder-Duplikate (rm-Befehl),
  Warnung "First build 30–90s" (Tarball-Extraktion)

README.md:
  Zwei neue Links in der Dokumentationstabelle (validation-protocol,
  tutorial)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 01:19:44 +02:00
Tarik Moussa
e28aee7051 docs: Mathematiker-Onboarding — Theorie, Validierung, Beispiel-Meshes, 170 Tests
All checks were successful
C++ Tests / test-fast (push) Successful in 2m20s
C++ Tests / test-cgal (push) Has been skipped
Ziel: einem interessierten Mathematiker ermöglichen, die bisherige Arbeit
      unabhängig zu validieren und eigene Forschung beizutragen.

Neu:
  doc/math/discrete-conformal-theory.md
      Kompakte mathematische Einführung (DCE, Variationsprinzip, drei
      Geometriemodi, Holonomie, Periodenmatrix) für Riemann-Flächen-Kenner.

  doc/math/validation.md
      Analytisch bekannte Sollwerte + wie man sie mit dem Code prüft:
      Gauss–Bonnet (χ), τ ∈ Fundamentaldomäne (3 Invarianten), Symmetrie-
      Argumente für τ=i (4-fach) und τ=e^{iπ/3} (6-fach), Newton-Konvergenz,
      Gradienten-Check (FD), Holonomie-Kommutator. Reviewer-Checkliste.

  CONTRIBUTING.md (Root)
      Gitea/GitHub-Standard: CONTRIBUTING.md im Root-Verzeichnis als
      Kurzreferenz mit Links zu doc/contributing.md und den Math-Docs.

  code/data/off/torus_4x4.off   — 16 Vertices, 32 Flächen, Genus 1
  code/data/off/torus_8x8.off   — 64 Vertices, 128 Flächen, Genus 1
  code/data/off/torus_hex_6x6.off — 36 Vertices, 72 Flächen, 6-fach Sym.

Aktualisiert:
  README.md              — 158 → 170 Tests, zwei neue Math-Links in Tabelle
  doc/api/tests.md       — 28 Suiten, 170 Tests, 1 Skip (korrigiert)
  doc/contributing.md    — Testzähler 158+2 → 170+1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 01:11:00 +02:00
Tarik Moussa
5859d78a37 fix(deps): extract CGAL for WITH_CGAL_TESTS=ON
Some checks failed
C++ Tests / test-fast (push) Successful in 2m43s
C++ Tests / test-cgal (push) Has been cancelled
deps/CMakeLists.txt only extracted CGAL when WITH_CGAL=ON.
With -DWITH_CGAL_TESTS=ON the CGAL include path was never populated,
causing fatal error: CGAL/Simple_cartesian.h: No such file or directory.

Fix: extract CGAL-6.1.1 for both WITH_CGAL and WITH_CGAL_TESTS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 21:37:02 +02:00
Tarik Moussa
6886803a29 fix(ci): decouple CGAL tests from viewer — add WITH_CGAL_TESTS flag
Some checks failed
C++ Tests / test-fast (push) Successful in 2m12s
Mirror to Codeberg / mirror (push) Failing after 1s
C++ Tests / test-cgal (push) Failing after 1m52s
Root cause: -DWITH_CGAL=ON implied -DWITH_VIEWER=ON, which pulled in
GLFW, which requires wayland-scanner — not present in the headless CI
container (ubuntu:22.04 ARM64).

Fix: new CMake option -DWITH_CGAL_TESTS=ON builds conformallab_cgal_tests
without touching the viewer, GLFW, libigl, or any display dependency.
Only Boost headers are required (already in the Docker image).

Changes
───────
code/CMakeLists.txt
  - Add WITH_CGAL_TESTS option (OFF by default)
  - find_package(Boost REQUIRED) now shared between WITH_CGAL and WITH_CGAL_TESTS
  - WITH_CGAL still implies WITH_VIEWER (for local full builds)
  - Remove duplicate find_package(Boost) inside the WITH_CGAL block

code/tests/CMakeLists.txt
  - cgal/ subdirectory added for WITH_CGAL OR WITH_CGAL_TESTS

.gitea/workflows/cpp-tests.yml
  - test-cgal job: -DWITH_CGAL=ON → -DWITH_CGAL_TESTS=ON

README.md
  - Build modes table: three rows (default / CGAL_TESTS / CGAL full)
  - Quick-start: separate headless and full-local sections
  - Prerequisite table updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 13:50:56 +02:00
Tarik Moussa
a4438c9a1a test: Java-Geometrie-Utility-Tests portiert (Tests 1–6) + Genus-2-TODO-Stub
All checks were successful
C++ Tests / test-fast (push) Successful in 2m4s
C++ Tests / test-cgal (push) Has been skipped
Portiert aus CuttinUtilityTest, UnwrapUtilityTest, ConvergenceUtilityTests
und HomologyTest (Java-Quelldatei unifgeo/test). Jetzt 170 CGAL-Tests.

Neue Suiten in test_geometry_utils.cpp:
  CuttingUtility       — point_in_triangle_2d (3 Tests, Java-Test 1–2)
  UnwrapUtility        — Eckwinkel law-of-cosines (2 Tests, Java-Test 3)
  ConvergenceUtility   — circumradius + scale-invariant R_f/sqrt(A) (6 Tests, Java-Test 4–6)
  HomologyGenerators   — GTEST_SKIP-Stub für Genus-2 (Java-Test 7, Phase 8)

Der Stub dokumentiert genau was fehlt (Genus-2-Mesh), welcher Code nötig ist
(compute_cut_graph → 4 cut edges) und die Java-Herkunft.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:09:27 +02:00
Tarik Moussa
e7dfaed56c feat(phase7): Java-parity layout — priority BFS, halfedge_uv, Möbius holonomy, period matrix, fundamental domain — 158 tests
Phase 7 adds seven features ported from the original Java ConformalLab:

  layout.hpp
  - Priority BFS (min-heap on BFS depth) replaces FIFO queue, minimising
    trilateration error accumulation from the root face outward.
  - MobiusMap struct: T(z)=(az+b)/(cz+d), identity/inverse/compose,
    from_three (3×3 complex least-squares fit), apply(Vector2d).
  - halfedge_uv[h.idx()] = UV of source(h) in face(h); seam halfedges
    carry the virtual unfolded position, enabling proper GPU texture atlases.
  - Hyperbolic holonomy stored as MobiusMap per cut edge (SU(1,1) isometry).
  - best_root_face: largest 3-D area face, 1.5× interior bonus.
  - normalise_euclidean also transforms halfedge_uv (centroid + PCA).
  - Face-area-weighted iterative Möbius centering (Fréchet mean, Phase 7).

  period_matrix.hpp  (new)
  - PeriodData: lattice generators ω_i as complex numbers, τ = ω₂/ω₁ ∈ ℍ.
  - reduce_to_fundamental_domain: SL(2,ℤ) reduction via alternating S/T steps.
  - is_in_fundamental_domain, compute_period_matrix.
  - NOTE: Siegel matrix Ω for genus g>1 intentionally deferred.

  fundamental_domain.hpp  (new)
  - FundamentalDomain: CCW parallelogram {0, ω₁, ω₁+ω₂, ω₂} for genus 1.
  - edge_identifications, generators stored.
  - 4g-polygon boundary-walk for g>1 marked TODO(Phase 8) with full algorithm
    outline and literature references.
  - tiling_copy / tiling_neighbourhood for universal cover visualisation.

  Tests: 121 → 158 (+37 Phase 7 tests covering all new features).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:57:13 +02:00
Tarik Moussa
4fc48b39f0 feat(phase6): exact hyperbolic layout, Gauss–Bonnet, cut graph, normalisation — 121 tests
New files:
- gauss_bonnet.hpp: euler_characteristic, genus, Σ(2π-Θ_v) sum/rhs/deficit,
  check_gauss_bonnet (throws), enforce_gauss_bonnet (correct sign: Δ=(lhs-rhs)/V)
- cut_graph.hpp: CutGraph struct + compute_cut_graph (tree-cotree, Erickson–Whittlesey
  2005); boundary edges correctly excluded from cut set
- test_phase6.cpp: 26 new tests (GaussBonnet ×8, CutGraph ×6, HyperbolicTrilateration
  ×4, Normalisation ×4 — all pass)

layout.hpp (Phase 6 rewrite):
- detail::trilaterate_hyp: exact Möbius + hyperbolic law of cosines replacing old tanh(d/2)
- detail::center_poincare_disk: Möbius centering for hyperbolic normalisation
- normalise_euclidean: centroid → origin + PCA major-axis rotation
- normalise_hyperbolic: Möbius centering in the Poincaré disk
- normalise_spherical: Rodrigues rotation → north pole
- euclidean_layout / hyper_ideal_layout: optional CutGraph* + HolonomyData* + normalise

Bug fixes caught by new tests:
- gauss_bonnet.hpp: enforce_gauss_bonnet had wrong sign for delta
- cut_graph.hpp: boundary edges were incorrectly marked as cut edges

121 tests pass, 2 skipped (Hessian stubs).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 01:15:41 +02:00
Tarik Moussa
b7593e3f6d feat(phase5): Layout, CLI, JSON/XML serialisation — 95 tests
Phase 5 complete:

layout.hpp
  - euclidean_layout(): BFS unfolding in ℝ² using trilaterate_2d
  - spherical_layout(): BFS on S² using trilaterate_sph (spherical law of cosines)
  - hyper_ideal_layout(): BFS in Poincaré disk (tanh(d/2) Euclidean approx)
  - save_layout_off(): convenience OFF writer for 2-D and 3-D layouts

serialization.hpp
  - save/load_result_json(): nlohmann/json; stores DOF vector + uv/pos layout
  - save/load_result_xml(): hand-written writer/parser; same schema

conformallab_cli.cpp (rewritten)
  - CLI11 interface: -i/-o/-g/-j/-x/-s/-v
  - Dispatches to euclidean / spherical / hyper_ideal pipeline
  - Runs Newton, computes layout, saves OFF + JSON + XML

examples/example_layout.cpp
  - Full round-trip demo: solve → layout → JSON/XML → reload → verify

tests/cgal/test_layout.cpp (8 tests)
  - Euclidean_PreservesEdgeLengths, CorrectVertexCount, TriangleIsNonDegenerate
  - Spherical_PreservesArcLengths, PositionsOnUnitSphere
  - HyperIdeal_SuccessAndFinitePositions
  - Serialization.JSON_RoundTrip, XML_RoundTrip

All 95 CGAL tests pass (2 skipped — Hessian stubs unchanged).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 00:53:47 +02:00
Tarik Moussa
3f124eb071 feat(phase4): HyperIdeal Newton solver, SparseQR fallback, examples, docs
Phase 4 complete — 87 CGAL tests pass, 2 skipped.

Newton solver (phase4a):
- hyper_ideal_hessian.hpp: symmetric FD Hessian (O(ε²), PSD by convexity)
- newton_hyper_ideal(): Newton + backtracking for the HyperIdeal functional
- detail::solve_with_fallback(): optional bool* fallback_used parameter
- solve_linear_system(): public API exposing LDLT→SparseQR fallback

SparseQR fallback tests (SparseQRFallback.*):
- FullRankSystem_CorrectSolution: LDLT path, fallback_used=false
- SingularMatrix_FallbackActivated: zero-pivot → QR activated, fallback_used=true
- Euclidean_ClosedMeshNoPinConverges: gauge-mode null space handled via QR

HyperIdeal Newton tests (NewtonSolver.HyperIdeal_*):
- ConvergesTriangleAllVariable, ResultFieldsConsistent,
  ConvergesTetrahedron, SparseQRFallbackNoCrash
- Natural-target base point (b=1.0, a=0.5) — x=0 is degenerate in log-space

Pipeline tests (test_pipeline.cpp):
- End-to-end: all three geometries, mesh I/O round-trip, solve+export

Example programs (code/examples/):
- example_euclidean.cpp:   headless Euclidean pipeline
- example_hyper_ideal.cpp: headless HyperIdeal pipeline
- example_viewer.cpp:      interactive libigl viewer with jet colour map

README:
- Mathematical scope table: C++ vs Java original (18 rows)
- "For mathematicians" section: mental model, step-by-step new-functional
  guide, half-edge traversal snippets, recommended reading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 00:11:25 +02:00
Tarik Moussa
e70689d29f feat(phase4a+4b): Newton solver + CGAL mesh I/O
Phase 4a — newton_solver.hpp:
  - newton_euclidean(): SimplicialLDLT on H (PSD); solves H·Δx = −G
  - newton_spherical(): SimplicialLDLT on −H (NSD→PSD); solves (−H)·Δx = G
  - Backtracking line search (halving α up to 20×) for global convergence
  - NewtonResult struct: x, iterations, grad_inf_norm, converged
  - 7 tests: 4 spherical (convergence, few iters, large perturbation,
    field consistency) + 3 Euclidean (triangle pinned, quad pinned,
    mixed pinned — all with natural-theta equilibrium at x=0)

Phase 4b — mesh_io.hpp:
  - read_mesh() / write_mesh(): CGAL::IO::read/write_polygon_mesh wrappers
  - load_mesh() / save_mesh(): throwing convenience versions
  - Supports OFF, OBJ, PLY (format detected by file extension)
  - 6 tests: OFF round-trip (tet + quad), OBJ round-trip, missing-file throw,
    vertex-position preservation, save/load convenience wrappers

All 75 cgal tests pass (3 skipped as before).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-12 17:35:00 +02:00
Tarik Moussa
194effba97 feat(phase3f+3g): analytical Hessians + PI consolidation
Phase 3g — constants.hpp:
  - Introduce conformallab::PI and TWO_PI in a single constants.hpp
  - Remove scattered local PI/pi definitions from hyper_ideal_geometry.hpp,
    hyper_ideal_utility.hpp, euclidean_functional.hpp, mesh_builder.hpp,
    spherical_geometry.hpp (backward-compatible PI_SPHER alias kept)

Phase 3f — Euclidean Hessian (euclidean_hessian.hpp):
  - Cotangent-Laplace operator (Pinkall–Polthier 1993)
  - euclidean_cot_weights() helper + euclidean_hessian() + hessian_check_euclidean()
  - Correct Pinkall–Polthier 1/2 normalization factor
  - 8 tests: cot weights, symmetry, null-space (H·1=0), PSD, FD × 4 meshes

Phase 3f — Spherical Hessian (spherical_hessian.hpp):
  - Derives ∂α_i/∂u_j directly from the spherical law of cosines:
      ∂α1/∂l_opp  = sin(l_opp) / [sin(l_a)·sin(l_b)·sin(α1)]
      ∂α1/∂l_adj  = [cot(l_adj)·cos(α1) − cot(l_other)] / sin(α1)
    then chains with ∂l/∂λ = tan(l/2)
  - spherical_cot_weights() kept as a standalone helper (tested separately)
  - 8 tests: cot weights, symmetry, correct null-space & sign-convention
    (H·1 ≠ 0; H is NSD at equilibrium), FD × 3 meshes

All 62 cgal tests pass (3 skipped as before).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-12 17:22:28 +02:00
Tarik Moussa
8c353bb884 feat(phase3d+3e): port EuclideanCyclicFunctional + add SphericalFunctional gauge-fix
Phase 3d — EuclideanCyclicFunctional:
  • euclidean_geometry.hpp: t-value / atan2 corner-angle formula with
    centering trick (μ = (Λ̃₁₂+Λ̃₂₃+Λ̃₃₁)/6) for numerical stability
  • euclidean_functional.hpp: EuclideanMaps bundle, gradient (G_v = Θ_v − Σα_v,
    G_e = α_opp⁺ + α_opp⁻ − φ_e), 10-point GL path-integral energy,
    gradient_check_euclidean — identical halfedge convention to SphericalFunctional
  • test_euclidean_functional.cpp: 11 tests (1 skip) covering angle formula,
    right-isosceles triangle, angle sum = π, degenerate detection, gradient
    checks on triangle/quad-strip/tetrahedron/fan-5/mixed-pinned, NaN check

Phase 3e — Spherical gauge-fix:
  • spherical_gauge_shift(): Newton + backtracking line search to find t*
    where ΣG_v(x + t·1) = 0 (maximises E along the global scale direction);
    bisection used when sign change is detectable, Newton+backtrack otherwise
  • apply_spherical_gauge(): in-place wrapper
  • 3 new tests: GaugeFix_SpherTetVertexZerosSumGv, GaugeFix_ApplyInPlace,
    GaugeFix_AlreadyAtGaugeReturnsTNearZero

Total: 45 cgal tests pass, 3 skipped (@Ignore Hessian stubs, one per functional)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 07:39:14 +02:00
Tarik Moussa
a4c2a89e7e feat(phase3c): port SphericalFunctional onto ConformalMesh; update README
New headers:
- spherical_geometry.hpp: spherical arc length l(λ) and half-angle formula
  for interior angles of a spherical triangle (SphericalFaceAngles struct)
- spherical_functional.hpp: SphericalMaps bundle, setup/assign DOF helpers,
  compute_lambda0_from_mesh(), gradient (Θ_v − Σα_v; Schläfli edge formula),
  energy via 10-point Gauss-Legendre path integral, gradient_check_spherical()

Updated:
- mesh_builder.hpp: add make_spherical_tetrahedron() (vertices on unit sphere)
  and make_octahedron_face() (single right-angled spherical triangle)
- tests/cgal/CMakeLists.txt: enable test_spherical_functional.cpp
- README.md: rewrite for CGAL-package goal, two test targets, all headers,
  updated project tree, Phase progress table, key design decisions

Tests (cgal.SphericalFunctional.*): 8 active + 1 skip
- OctaFaceAnglesAreRightAngles, SpherTetAngleSumExceedsPi
- GradientCheck_{OctaFaceVertex, SpherTetVertex, SpherTetAllDofs,
  SpherFan4Vertex, MixedPinnedVertices}
- AnglesFiniteAtKnownPoint
All 30 cgal.* tests pass (2 @Ignore skips).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 00:01:24 +02:00
Tarik Moussa
516ac89bd8 feat(phase3b): port HyperIdealFunctional energy + gradient onto ConformalMesh
Implements the hyper-ideal discrete conformal map functional on
CGAL::Surface_mesh. The energy and analytic gradient are ported directly
from HyperIdealFunctional.java; correctness is verified via a
finite-difference gradient check (same eps=1E-5 / tol=1E-4 as Java).

New files:
  include/hyper_ideal_geometry.hpp    — ζ, ζ₁₃, ζ₁₄, ζ₁₅, lij, αij, σi, σij
  include/hyper_ideal_functional.hpp  — HyperIdealMaps, evaluate_hyper_ideal,
                                        gradient_check
  tests/cgal/test_hyper_ideal_functional.cpp  — 6 tests (1 skipped @Ignore)

Test results (local, -DWITH_CGAL=ON):
  conformallab_cgal_tests: 21 registered | 20 passed | 1 skipped | 0 failed
    - GradientCheck_AllHyperIdealTriangle   ✓
    - GradientCheck_ExtendedDomain          ✓
    - GradientCheck_TetrahedronAllVariable  ✓
    - EnergyFiniteAtTestPoint               ✓
    - GradientCheck_MixedIdealHyperIdeal    ✓
    - GradientCheck_Fan6AllVariable         ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 23:10:23 +02:00
Tarik Moussa
bf9c323d60 feat(phase3a): introduce CGAL Surface_mesh as ConformalMesh foundation
Replaces the Java CoHDS with CGAL::Surface_mesh<Point3> (Simple_cartesian
kernel). Adds domain-specific property maps for lambda/theta/idx/alpha and
face geometry type — the direct C++ equivalent of CoVertex/CoEdge adapters.

New files:
  include/conformal_mesh.hpp   — ConformalMesh type + property-map helpers
  include/mesh_builder.hpp     — mesh factories (triangle, tetrahedron,
                                 quad-strip, fan) for tests and examples
  tests/cgal/                  — second test executable (conformallab_cgal_tests)
                                 built only with -DWITH_CGAL=ON

Test results (local, -DWITH_CGAL=ON):
  conformallab_tests:      36 registered | 23 passed | 13 skipped | 0 failed
  conformallab_cgal_tests: 14 registered | 14 passed |  0 skipped | 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 18:36:21 +02:00
Tarik Moussa
ae5db7216e tests: port HyperIdealVisualizationPlugin circle-projection tests
All checks were successful
C++ Tests / test (push) Successful in 2m29s
Mirror to Codeberg / mirror (push) Successful in 26s
Implements getEuclideanCircleFromHyperbolic() in C++ (hyperboloid model
→ Poincaré disk via Lorentz boost + circumcircle). Ports the 2 Java tests
that were the only remaining candidates not requiring HDS or a solver.

All other unported tests (DataTypesTest, SchottkyIOTest,
UniformizationDataTest, BranchedCoverTorusTest) are blocked by XML
serialization or HDS – stubs remain for Phase 4.

Test totals: 36 registered | 23 passed | 13 skipped | 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 17:23:15 +02:00
Tarik Moussa
c5a86cb30a tests: port DiscreteEllipticUtility + P2 tests; stub HDS-blocked tests
All checks were successful
C++ Tests / test (push) Successful in 2m39s
Mirror to Codeberg / mirror (push) Successful in 24s
Fully ported (pure math, no HDS required):
  test_discrete_elliptic_utility.cpp  – 2 tests
    normalizeModulus: move tau into SL(2,Z) fundamental domain
  test_p2_utility.cpp                 – 3 tests
    P2 projective geometry (perpendicularBisector, pointFromLines,
    makeDirectIsometryFromFrames double vs long double precision)

New headers:
  include/discrete_elliptic_utility.hpp  – normalizeModulus
  include/p2_utility.hpp                 – P2 Euclidean geometry (templated
    on scalar type so double and long double share one implementation)

Stubs (GTEST_SKIP, blocked until HDS port – Phase 4):
  test_hyper_ideal_functional.cpp          – 5 tests (1 @Ignore in Java)
  test_hyper_ideal_hyperelliptic_utility.cpp – 3 tests
  test_spherical_functional.cpp            – 5 tests
  All use CoHDS + HalfEdgeUtils which are not yet ported to C++.

Result: 34 tests total | 21 passed | 13 skipped | 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 17:15:18 +02:00
Tarik Moussa
b66525039d fix: gitignore – update CMakeLists.txt exception + ignore macOS duplicates
All checks were successful
C++ Tests / test (push) Successful in 2m13s
Mirror to Codeberg / mirror (push) Successful in 25s
- Exception updated to match renamed CMakeLists.txt (capital M)
- Add "* 2.*" / "* 3.*" patterns to suppress iCloud Drive duplicate
  files (e.g. "clausen 2.hpp") from appearing in git status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:22:53 +02:00
Tarik Moussa
6a70309a30 fix: rename deps/Cmakelists.txt → CMakeLists.txt (case-sensitive Linux fix)
Some checks failed
C++ Tests / test (push) Has started running
Mirror to Codeberg / mirror (push) Has been cancelled
macOS filesystem is case-insensitive so both spellings worked locally,
but the Linux container (CI) could not find the file and failed with
"does not contain a CMakeLists.txt file".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:22:16 +02:00
Tarik Moussa
42dd815591 cmake: add find_package(Boost) for WITH_CGAL mode
Some checks failed
C++ Tests / test (push) Failing after 41s
Mirror to Codeberg / mirror (push) Successful in 35s
CGAL 6.x still unconditionally includes boost/config.hpp in its
config.h, so Boost headers are required even with CGAL_DISABLE_GMP/MPFR.

Use system Boost via find_package instead of the removed 211 MB tarball.
Tests-only mode remains completely dependency-free.

Install: brew install boost  (macOS)
         apt install libboost-dev  (Debian/Ubuntu)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:20:38 +02:00
Tarik Moussa
cd7b7a8fd8 cmake: add WITH_CGAL / WITH_VIEWER modes, remove unused Boost
Restructures the CMake build into three clearly separated modes:

  (default)        tests only – Eigen + GTest, no heavy deps
  -DWITH_VIEWER=ON builds viewer lib (libigl / GLFW / GLAD)
  -DWITH_CGAL=ON   builds conformallab_core CLI app;
                   implies WITH_VIEWER automatically

Changes:
- code/CMakeLists.txt: viewer and core targets are now guarded by
  option() flags; GTest / tests target is always built
- code/deps/Cmakelists.txt: CGAL, libigl, glfw, glad are only
  extracted when the corresponding mode is enabled; Eigen always
- Remove Boost (211 MB tarball, 0 usages in the codebase – CGAL 6.x
  does not require Boost for Simple_cartesian / Surface_mesh / basic IO)
- Remove code/src/main.cpp (dead file, not referenced in CMake)

All 16 C++ tests pass in tests-only mode (verified locally).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:42:43 +02:00
Tarik Moussa
a2876b9cbf port MatrixUtility and SurfaceCurveUtility tests to C++
- include/matrix_utility.hpp: 4x4 mapping matrix R·from=to (Eigen)
- include/projective_math.hpp: dehomogenize, hyperbolicDistance,
  isOnSegment (collinearity + betweenness via 3D cross/dot),
  getPointOnCorrespondingSegment (parameter by arc-length ratio)
- test_matrix_utility.cpp: port of MatrixUtilityTest (1 test)
- test_surface_curve_utility.cpp: port of SurfaceCurveUtilityTest
  testIsBetween and testGetPointOnSegment_SegmentEdge (2 tests)
- tolerance adjusted to 1e-12 for matrix inversion (2.7e-15 rounding
  from Eigen vs jReality's LU; both well within meaningful accuracy)

Total: 16/16 tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 01:15:35 +02:00
Tarik Moussa
c30d540521 add GTest infrastructure and port first mathematical tests from Java
- Add Google Test via CMake FetchContent (v1.14.0)
- Add clausen.hpp: Clausen integral, Lobachevsky function, Im(Li2)
- Add hyper_ideal_utility.hpp: generalized and ideal-vertex hyperbolic
  tetrahedron volume formulas using Eigen for the 4x4 Gram determinant
- Port ClausenTest (5 tests) and HyperIdealUtilityTest (8 tests) from
  Java/JUnit — all 13 pass with same tolerances as the Java originals
- Fix pre-existing VIEWER/viewer case mismatch in CMakeLists.txt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 01:07:19 +02:00
Tarik Moussa
94533514fc fix typo 2026-03-17 21:25:33 +02:00
Tarik Moussa
e150cf6ef3 add new compile target for viewer 2026-03-17 21:18:59 +02:00
Tarik Moussa
2479aedeee ad simple visualization 2026-03-16 17:47:48 +02:00
Tarik Moussa
1ce993f296 ad simple visualization 2026-03-16 17:20:15 +02:00