09a68a456914621725d6fcb9c59d1c1efa30fcc4
29 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bc40a13e8d |
perf: architecture-touch quick-wins #6 + #10; skip #5 + #7 with honest notes
Evaluated all four mid-tier architecture-touch levers from doc/architecture/compile-time.md. Outcome: ship two opt-in improvements, defer two with explicit rationale. #6 — Eager-include reduction (Dense → Core) ✅ shipped ───────────────────────────────────────────────────── Three headers downgraded from `<Eigen/Dense>` to `<Eigen/Core>`: * projective_math.hpp * hyper_ideal_visualization_utility.hpp * mesh_utils.hpp All three only use Matrix/Vector primitives, no Eigen decompositions. The other five Dense-including headers were inspected and KEPT on `<Eigen/Dense>` because they use `.inverse()`, `.determinant()`, `ColPivHouseholderQR`, or `SelfAdjointEigenSolver`. Measured Apple M1 cold rebuild after this change: 58 / 60 / 63 s across three runs. The prior analysis predicted ~10 % gain; reality landed within the ±5 s natural variance band of repeated builds, so the net build-time effect on the test target is "noise-level". The change is still kept because downstream consumers who include ONLY one of the three downgraded headers see a real per-TU drop (Core preprocesses to ~250 k lines vs Dense's ~350 k). #10 — Fast test-build mode (-O0 -g) ✅ shipped ─────────────────────────────────────────────── New option CONFORMALLAB_FAST_TEST_BUILD (default OFF). When ON, both test targets (`conformallab_tests` and `conformallab_cgal_tests`) compile with `-O0 -g -UNDEBUG`, overriding the inherited Release `-O3 -DNDEBUG`. Measured Apple clang: 51.6 s vs 46.8 s without -O0 → slightly slower. The Backend phase that prior analysis predicted would drop from 9.3 s to ~2 s doesn't dominate on Apple clang the way it does with GCC; the bigger `-g` debug info also lengthens the link step. Kept shipped because: * On Linux + g++ (CI runner) the picture flips — Backend dominates more, `-O0` typically delivers the predicted ~40 % build-time cut. * Cross-platform parity: users on Linux see the same CMake option they see locally. Honest documentation in doc/architecture/compile-time.md notes that the Apple-clang-local benefit is currently 0 %. Tests RUN ~15× slower under `-O0` (1.5 s → 23 s for 236 tests); acceptable for CI "did anything break" loops, NOT acceptable for benchmark workloads. #5 — Move detail:: impls to .inl files ⏸ deferred ─────────────────────────────────────────────────── Pure enabler for #7. Without #7 landing, the .inl extraction would just add an extra hop to header reading. Reconsider once a concrete maintenance reason emerges (e.g. a downstream user wants to override a detail helper). #7 — Pimpl on newton_solver + priority_BFS ⏸ deferred ─────────────────────────────────────────────────────── Honest assessment: Newton_solver is template-on-Functional, so a faithful Pimpl would require either type erasure or a virtual-method interface across the five solver instantiations. Estimated 1-2 weeks of refactor with measurable API-surface risk. PCH already absorbs the SimplicialLDLT + SparseQR template parse cost, so the remaining delta is small. Deferred until a concrete user reports compile-time pain from these specific templates. Documentation ───────────── README.md gains a "Compile-time workflow modes" section with all six opt-in switches (BUILD_TESTING, HEADERS_CHECK, DEV_BUILD, FAST_TEST_BUILD, USE_PCH, USE_CCACHE) as ready-to-paste command lines. doc/architecture/compile-time.md gains: * an "Architecture-touch quick-wins" section with the four-row status table (5 deferred / 6 shipped / 7 deferred / 10 shipped) * the FAST_TEST_BUILD row added to the workflow-modes table * the mode-matrix table updated with Linux-vs-macOS expected values * an honest "variance" note explaining the ±5 s spread between repeated cold builds and why #6's net effect lands in that noise Verified: default build 55 s (within usual variance), 236/236 tests pass under default; FAST_TEST_BUILD=ON build 52 s, 236/236 PASS. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
9ea7d15aa0 |
perf: add 4 workflow modes (BUILD_TESTING / DEV_BUILD / HEADERS_CHECK / ccache)
Four orthogonal opt-in build modes on top of the existing PCH + Unity
defaults. Each addresses a specific iteration scenario; defaults are
unchanged (PCH + Unity stays the canonical fast full-rebuild path).
(A) HEADERS_CHECK target — opt-in via -DCONFORMALLAB_HEADERS_CHECK=ON
Per-public-header smoke-compile sentinels. For each of the six
public CGAL umbrella headers, a stub TU `#include <…>\nint main(){}`
is generated at configure time and compiled in isolation.
* Full headers_check build: ≈ 12 s
* Incremental after touching one header: ≈ 0.1 s
Use case: "did my refactor still parse the public API?" without
waiting 55 s for the full CGAL test build.
(C) DEV_BUILD mode — opt-in via -DCONFORMALLAB_DEV_BUILD=ON
PCH stays on; Unity Build is forced off (both globally AND on the
cgal-tests target which previously overrode the global setting).
Trade-off: full clean rebuild ~75 s (+36 % vs the 55 s default)
but incremental rebuild after editing a single test file drops
from ~46 s (unity batch) to ~16 s (single TU + relink).
Flip on for trial-and-error sessions, flip off before measuring
CI build time or shipping a PR.
(D) ccache integration — default ON, disable with -DCONFORMALLAB_USE_CCACHE=OFF
Detects `ccache` on PATH and prepends it to compile + link
launchers. On Apple clang + PCH + Unity the macOS-local hit
rate is currently 0 % (3 separate friction points documented
in doc/architecture/compile-time.md § "ccache — honesty notes");
stays neutral when it doesn't help. Real payoff on Linux CI
(g++ + traditional PCH) where 80 %+ hit rates are typical.
(BUILD_TESTING=OFF) Standard CMake gate, now respected end-to-end.
Wrapped both `add_subdirectory(tests)` AND the FetchContent of
GoogleTest in `if(BUILD_TESTING)`. Pass `-DBUILD_TESTING=OFF`:
* Configure ≈ 1 s
* Build ≈ 0 s
* 0 object files
* No GTest fetch
Use case: IDE-syntax-check workflow that needs
`compile_commands.json` but does NOT need to download GTest
or build any test binary.
doc/architecture/compile-time.md gains:
* a "Workflow modes — what to choose when" section with a 4-row
switch matrix and a "mode matrix at a glance" comparison table
* a ccache honesty-notes block listing the three macOS friction
points (PCH artefact caching, Unity Build path randomisation,
CMake launcher integration) — Linux CI is where the lever pays
off
Verified: default build 53 s wall, 236/236 tests pass; all opt-in
modes tested end-to-end with their expected workflow numbers
documented in the doc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
5fbc4bcc7f |
ci+perf: PCH + Unity Build cut CGAL test build wall-time 30% (78s -> 55s)
Two structural compile-time optimisations on the conformallab_cgal_tests
target, both opt-out-able and verified safe (236/236 tests pass under
every configuration).
(1) Precompiled headers — option CONFORMALLAB_USE_PCH (default ON)
target_precompile_headers(conformallab_cgal_tests PRIVATE
<CGAL/Surface_mesh.h>
<CGAL/Simple_cartesian.h>
<CGAL/Kernel_traits.h>
<CGAL/boost/graph/iterator.h>
<CGAL/Polygon_mesh_processing/triangulate_faces.h>
<Eigen/Dense> <Eigen/Sparse> <Eigen/SparseCholesky> <Eigen/SparseQR>
<gtest/gtest.h>
<vector> <string> <cmath> <complex>
)
Absorbs the per-TU CGAL+Eigen template-parse cost (measured at 5.9 s
per minimal "include <CGAL/Discrete_conformal_map.h>" hello-world TU
on Apple M1).
(2) Unity Build — UNITY_BUILD ON with UNITY_BUILD_BATCH_SIZE 4
Concatenates the 22 test TUs into 5 batches of <=4 files each;
CGAL+Eigen headers parsed once per batch instead of once per TU.
Batch size 4 keeps gtest's TEST(...) macros and per-file
`using namespace ...` from colliding across batched files.
Numbers (Apple M1, Ninja, -j8, clean rebuild)
─────────────────────────────────────────────
wall CPU tests
baseline 78 s 676 s 236/236
+ PCH 66 s 474 s 236/236 (-15% wall, -30% CPU)
+ PCH + Unity 55 s 167 s 236/236 (-30% wall, -75% CPU)
Honest deferred items (documented in doc/architecture/compile-time.md):
* `extern template` (lever #2 in the analysis) — subsumed by PCH;
estimated residual gain <5%, would add Eigen-version fragility.
* Header split <CGAL/Discrete_conformal_map_{euclidean,spherical,
hyper_ideal}.h> (lever #3) — downstream-only benefit (our test
build needs all three); kept as a future cleanup once a downstream
user actually requests it.
Opt-outs: `-DCONFORMALLAB_USE_PCH=OFF` and `-DCMAKE_UNITY_BUILD=OFF`.
Detailed measurement methodology, per-TU breakdowns, clang
-ftime-trace template hot-spots, and a "what comes next" lever list
live in doc/architecture/compile-time.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
f25174ed69 |
feat: output_uv_map for InversiveDistance, error for CP-Euclidean, reviewer trio
Three reviewer-meeting deliverables in one commit.
(1) output_uv_map for the two remaining DCE entries
─────────────────────────────────────────────────
* Discrete_inversive_distance.h: full implementation. After Newton,
reconstruct effective Euclidean edge lengths from the converged
log-radii via the Bowers-Stephenson identity
`ℓᵢⱼ² = rᵢ² + rⱼ² + 2·Iᵢⱼ·rᵢ·rⱼ`, populate a temporary
EuclideanMaps with `lambda0 = log(ℓᵢⱼ²)`, and reuse the existing
`euclidean_layout(mesh, 0, eucl)` priority-BFS. Per-vertex
Point_2 coordinates written into the user-supplied pmap.
Optional `normalise_layout(true)` applies the canonical PCA
centroid + major-axis rotation, same as the other 3 entries.
* Discrete_circle_packing.h: throws std::runtime_error with a
clear pointer to Phase 9c rather than silently producing
nonsense. CP-Euclidean is face-based; the faithful output is a
per-face circle packing in ℝ², not a per-vertex Point_2 map.
A true layout requires BPS-2010 §6 (~150 lines, on the porting
roadmap as Phase 9c). Failing loudly is the honest default.
Tests: 2 new cases in test_cgal_phase8b_lite.cpp
(OutputUvMap_InversiveDistance_PopulatesPmap;
OutputUvMap_CPEuclidean_ThrowsClearly). Both green.
Suite total now 259 (was 257, +2). CGAL subtotal: 234 → 236.
(2) Reviewer meeting documents
──────────────────────────
New directory doc/reviewer/ with three files:
* briefing.md — one-page orientation for the reviewer.
What the project is, where to look first
(https://tmoussa.codeberg.page/ConformalLabpp/), the headline
evidence (tests/coverage/sanitizers/license), what we want from
them, what's deferred and why, and the 5 questions in a separate
file.
* questions.md — the 5 concrete decisions we want their second
opinion on:
Q1 Phase 9c (port-literal vs re-derive)
Q2 Phase 9b-analytic (worth ~2 weeks for ~6× speedup?)
Q3 CP-Euclidean output_uv_map (build now or defer?)
Q4 CGAL submission strategy (one package or five?)
Q5 geometry-central cross-validation co-authorship
Plus an explicit "what would you say no to?" question at the
bottom — negative feedback is the highest-value information.
* agenda.md — my own internal playbook (NOT to be sent).
60-min flow: 5-min thank-you, 10-min architecture tour,
30-min for Q1-Q5 in the order Q4-Q1-Q2-Q5-Q3, 5-min "no"
question, 5-min wrap-up. Includes post-meeting memo template
to fill out in the 30 min after.
* README.md — index for the directory; says which file goes
to whom and when to send.
(3) locked-vs-flexible.md known-limitations update
─────────────────────────────────────────────
"output_uv_map covers 3 of 5 entries" → "covers 4 of 5".
CP-Euclidean's throws-clearly behaviour documented as a Phase 9c
deliverable rather than a passive gap.
Bonus: extended .codespellrc ignore list (acknowledgement, the
British-English spelling I used in agenda.md).
Verifications on this commit:
259/259 tests pass (0 skipped)
scripts/check-test-counts.sh: OK (23 + 236 = 259)
scripts/quality/license-headers.sh: OK (66/66 SPDX)
python3 scripts/quality/cgal-conventions.py: OK (0/6 violations)
scripts/quality/codespell.sh: OK (0 typos)
scripts/quality/shellcheck.sh: OK (0 findings)
python3 scripts/check-markdown-links.py: OK (143/143)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
d3c08b3bc0 |
quality: 2 new gates (cmake-format, codespell) + SPDX rollout (60 files)
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
140f50f707 |
fixup: deduce kernel from mesh point type instead of hard-coding it
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> |
||
|
|
a1e74c1370 |
Phase 8a MVP: CGAL traits + Discrete_conformal_map.h Euclidean entry
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>
|
||
|
|
e958afbd19 |
chore: translate all German text to English across code, docs, and CI
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> |
||
|
|
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> |
||
|
|
7edf699ac2 |
test/docs: Scalability Smoke Tests + Komplexitätsdokumentation
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>
|
||
|
|
de3a35ad4f |
test: Java-Konvergenz- + Homologie-Tests portiert — 173 CGAL-Tests
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>
|
||
|
|
a4438c9a1a |
test: Java-Geometrie-Utility-Tests portiert (Tests 1–6) + Genus-2-TODO-Stub
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|