diff --git a/.codespellrc b/.codespellrc index 0269020..b53ba42 100644 --- a/.codespellrc +++ b/.codespellrc @@ -69,6 +69,7 @@ ignore-words-list = bessel,ist,sinces,nd,te,inout,nin,numer,neet,anc,sinks,doubl iff, categorise,categorised,categorises,categorising, optimisation,optimisations, + acknowledgement,acknowledgements,acknowledging, neighbour,neighbours,neighbouring,neighboured, labelled,labelling,labels,labelled, fulfil,fulfils,fulfilled,fulfilling, diff --git a/.gitea/workflows/cpp-tests.yml b/.gitea/workflows/cpp-tests.yml index 8b60d16..af8baf8 100644 --- a/.gitea/workflows/cpp-tests.yml +++ b/.gitea/workflows/cpp-tests.yml @@ -112,3 +112,51 @@ jobs: # the price of guaranteeing the documented workflow stays working. - name: End-to-end smoke test (scripts/try_it.sh) run: bash scripts/try_it.sh + +# ───────────────────────────────────────────────────────────────────────────── +# Job 3 — quality-gates (style + convention block) +# +# Cheap, deterministic checks that should never break unless a contributor +# introduces a regression. Each gate is a script under scripts/quality/ +# and exits 0 only when its tree is clean. These ran for weeks locally +# at zero findings before being promoted here. +# +# Tools installed at job-start (the ci-cpp image already has python3 + +# bash; we add codespell + shellcheck on top). Total wall-time: ~30 s +# on the eulernest runner. +# +# Strictly required for merges into main/dev — a regression fails the PR. +# ───────────────────────────────────────────────────────────────────────────── + quality-gates: + needs: test-fast + runs-on: eulernest + container: + image: git.eulernest.eu/conformallab/ci-cpp:latest + + steps: + - uses: actions/checkout@v4 + + - name: Install codespell + shellcheck (job-local) + run: | + apt-get update -qq + apt-get install -y --no-install-recommends \ + codespell shellcheck + + - name: License headers (every C++ source carries MIT SPDX) + run: bash scripts/quality/license-headers.sh + + - name: CGAL conventions (6 rules over CGAL public headers) + run: python3 scripts/quality/cgal-conventions.py + + - name: codespell (docs + source comments + script messages) + run: bash scripts/quality/codespell.sh + + - name: shellcheck (scripts/**/*.sh, severity=warning, strict) + run: bash scripts/quality/shellcheck.sh --strict + + - name: Summary + if: always() + run: | + echo "QUALITY ▸ all four gates passed." + echo " see scripts/quality/README.md for the full catalogue" + echo " (sanitizers, clang-tidy, coverage, etc. are local-only)" diff --git a/.gitea/workflows/doxygen-pages.yml b/.gitea/workflows/doxygen-pages.yml index f338277..4030d47 100644 --- a/.gitea/workflows/doxygen-pages.yml +++ b/.gitea/workflows/doxygen-pages.yml @@ -75,16 +75,31 @@ jobs: run: | set -eu # Build the publish payload in a clean scratch dir so the - # orphan branch contains only the Doxygen output (and a - # marker README), never any build/source artefacts. + # orphan branch contains only the reviewer hub + Doxygen + # output (and a marker README), never any build/source + # artefacts. publish_dir=$(mktemp -d) cp -r doc/doxygen/html/. "$publish_dir/" + # ── Reviewer hub override ───────────────────────────────── + # If doc/reviewer/hub.html is present, install it as the + # publish landing page and demote the auto-generated Doxygen + # index to /doxygen.html. The hub is hand-curated and lives + # under source control; this step keeps it visible after + # every push to main, surviving the auto-publish cycle. + if [ -f doc/reviewer/hub.html ]; then + mv "$publish_dir/index.html" "$publish_dir/doxygen.html" + cp doc/reviewer/hub.html "$publish_dir/index.html" + echo "DOC ▸ reviewer hub installed; Doxygen index now at /doxygen.html" + fi + cat > "$publish_dir/README.txt" </dev/null 2>&1 || apt-get install -y --no-install-recommends ccache + + # ─── Run 1: baseline (PCH OFF, Unity OFF, ccache cleared) ────── + - name: "Run 1: cold baseline (no PCH, no Unity, no ccache)" + run: | + ccache -C >/dev/null 2>&1 || true + rm -rf build-baseline + cmake -S code -B build-baseline -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_PCH=OFF \ + -DCMAKE_UNITY_BUILD=OFF \ + -DCONFORMALLAB_USE_CCACHE=OFF + start=$(date +%s) + nice -n 19 cmake --build build-baseline --target conformallab_cgal_tests -j1 + end=$(date +%s) + echo "PERF baseline_wall=$((end - start)) s" + echo "PERF_BASELINE_WALL=$((end - start))" >> $GITHUB_ENV + + # ─── Run 2: PCH only ────────────────────────────────────────── + - name: "Run 2: PCH only (Unity off, ccache off)" + run: | + ccache -C >/dev/null 2>&1 || true + rm -rf build-pch + cmake -S code -B build-pch -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_PCH=ON \ + -DCMAKE_UNITY_BUILD=OFF \ + -DCONFORMALLAB_USE_CCACHE=OFF + start=$(date +%s) + nice -n 19 cmake --build build-pch --target conformallab_cgal_tests -j1 + end=$(date +%s) + echo "PERF pch_only_wall=$((end - start)) s" + echo "PERF_PCH_WALL=$((end - start))" >> $GITHUB_ENV + + # ─── Run 3: default (PCH + Unity Build + #6 Dense→Core) ─────── + - name: "Run 3: default config (PCH + Unity + Dense→Core)" + run: | + ccache -C >/dev/null 2>&1 || true + rm -rf build-default + cmake -S code -B build-default -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_CCACHE=OFF + start=$(date +%s) + nice -n 19 cmake --build build-default --target conformallab_cgal_tests -j1 + end=$(date +%s) + echo "PERF default_wall=$((end - start)) s" + echo "PERF_DEFAULT_WALL=$((end - start))" >> $GITHUB_ENV + + # ─── Run 4: + FAST_TEST_BUILD (-O0 -g) ──────────────────────── + - name: "Run 4: default + FAST_TEST_BUILD=ON (-O0 -g for tests)" + run: | + ccache -C >/dev/null 2>&1 || true + rm -rf build-fast + cmake -S code -B build-fast -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_FAST_TEST_BUILD=ON \ + -DCONFORMALLAB_USE_CCACHE=OFF + start=$(date +%s) + nice -n 19 cmake --build build-fast --target conformallab_cgal_tests -j1 + end=$(date +%s) + echo "PERF fast_test_wall=$((end - start)) s" + echo "PERF_FAST_WALL=$((end - start))" >> $GITHUB_ENV + + # ─── Run 5: ccache hit-rate validation ───────────────────────── + - name: "Run 5: ccache hit-rate (rebuild build-default)" + run: | + ccache -C >/dev/null 2>&1 || true + ccache --zero-stats >/dev/null + # First rebuild: populate ccache. + rm -rf build-cc + cmake -S code -B build-cc -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_CCACHE=ON + nice -n 19 cmake --build build-cc --target conformallab_cgal_tests -j1 >/dev/null + ccache_first=$(ccache -s 2>&1 | grep -E "^\s*Hits" | head -1 | awk '{print $2}') + # Second rebuild: expect cache hits. + rm -rf build-cc-warm + cmake -S code -B build-cc-warm -G Ninja \ + -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_CCACHE=ON + start=$(date +%s) + nice -n 19 cmake --build build-cc-warm --target conformallab_cgal_tests -j1 + end=$(date +%s) + warm_wall=$((end - start)) + ccache_stats=$(ccache -s 2>&1 | grep -E "Hits|Misses" | head -4) + echo "── ccache stats after warm rebuild ──" + echo "$ccache_stats" + echo "PERF ccache_warm_wall=${warm_wall} s" + echo "PERF_CCACHE_WARM_WALL=$warm_wall" >> $GITHUB_ENV + + # ─── Test correctness (last gate; perf data already collected) ─ + - name: Verify all configs produced working binaries + if: always() + run: | + for build in build-baseline build-pch build-default build-fast; do + if [ -d "$build" ]; then + ctest --test-dir "$build" -R "^cgal\." --output-on-failure --timeout 120 \ + | tail -3 + fi + done + + # ─── Final summary ───────────────────────────────────────────── + - name: Compile-time perf summary + if: always() + run: | + echo "══════════════════════════════════════════════════════" + echo " COMPILE-TIME PERF BENCH — Linux ARM64 / g++ / -j1" + echo "══════════════════════════════════════════════════════" + printf " %-30s %4s s\n" "Run 1: cold baseline" "${PERF_BASELINE_WALL:-?}" + printf " %-30s %4s s\n" "Run 2: + PCH" "${PERF_PCH_WALL:-?}" + printf " %-30s %4s s\n" "Run 3: + PCH + Unity (default)" "${PERF_DEFAULT_WALL:-?}" + printf " %-30s %4s s\n" "Run 4: + FAST_TEST_BUILD" "${PERF_FAST_WALL:-?}" + printf " %-30s %4s s\n" "Run 5: + ccache warm rerun" "${PERF_CCACHE_WARM_WALL:-?}" + echo "──────────────────────────────────────────────────────" + echo "Predictions to validate vs Apple-M1 baseline:" + echo " ┃ FAST_TEST_BUILD: expected ~40 % faster than default" + echo " ┃ ccache warm: expected ≤ 10 s (vs Apple's 55 s)" + echo "──────────────────────────────────────────────────────" + # Compute relative deltas + if [ -n "${PERF_DEFAULT_WALL:-}" ] && [ -n "${PERF_FAST_WALL:-}" ]; then + pct=$(awk -v d="${PERF_DEFAULT_WALL}" -v f="${PERF_FAST_WALL}" \ + 'BEGIN { printf "%.0f", 100.0 * (d - f) / d }') + echo " Δ FAST_TEST_BUILD vs default: ${pct} % wall reduction" + fi + if [ -n "${PERF_DEFAULT_WALL:-}" ] && [ -n "${PERF_CCACHE_WARM_WALL:-}" ]; then + pct=$(awk -v d="${PERF_DEFAULT_WALL}" -v c="${PERF_CCACHE_WARM_WALL}" \ + 'BEGIN { printf "%.0f", 100.0 * (d - c) / d }') + echo " Δ ccache warm vs default: ${pct} % wall reduction" + fi + echo "══════════════════════════════════════════════════════" diff --git a/README.md b/README.md index 7fd9541..b00318c 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,41 @@ cmake --build build --target doc open doc/doxygen/html/index.html ``` +### Compile-time workflow modes + +The default build (PCH + Unity Build + Dense→Core trims) takes ~47 s +clean for the full CGAL test target. Five opt-in modes cover other +iteration scenarios: + +```bash +# Configure-only, no compile. ~1 s configure, 0 s build — emits +# compile_commands.json for IDE / clangd; skips the GTest fetch. +cmake -S code -B build -DBUILD_TESTING=OFF + +# Header smoke check: per-public-header isolated compile. ~12 s full, +# ~0.1 s after touching one header. "Does my refactor still parse?" +cmake -S code -B build -DBUILD_TESTING=OFF -DCONFORMALLAB_HEADERS_CHECK=ON +cmake --build build --target headers_check + +# Dev iteration: PCH on, Unity off. Slower full build (~75 s) but +# editing a single test rebuilds in ~16 s instead of ~46 s. +cmake -S code -B build -DWITH_CGAL_TESTS=ON -DCONFORMALLAB_DEV_BUILD=ON + +# Fast CI tests: -O0 -g for the test executables only (library / +# install targets keep -O3). Linux + g++ typically ~40 % faster +# build at the cost of 5–15× slower test RUN. Neutral on macOS. +cmake -S code -B build -DWITH_CGAL_TESTS=ON -DCONFORMALLAB_FAST_TEST_BUILD=ON + +# Pristine measurement: disable both performance levers, e.g. for +# scripts/quality/coverage.sh that needs every TU compiled fresh. +cmake -S code -B build -DWITH_CGAL_TESTS=ON \ + -DCONFORMALLAB_USE_PCH=OFF -DCMAKE_UNITY_BUILD=OFF +``` + +ccache is detected automatically when present on `PATH`; disable with +`-DCONFORMALLAB_USE_CCACHE=OFF`. Full mode matrix + measurements in +[`doc/architecture/compile-time.md`](doc/architecture/compile-time.md). + --- ## Minimal usage diff --git a/code/.gitignore b/code/.gitignore index 7c0f88c..d7ac37b 100644 --- a/code/.gitignore +++ b/code/.gitignore @@ -13,6 +13,7 @@ deps/* !deps/tarballs !deps/single_includes/ !deps/CMakeLists.txt +!deps/THIRD-PARTY-LICENSES.md # macOS iCloud Drive duplicates ("file 2.cpp", "file 2.hpp", …) * 2.* diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index b3dce95..3c2a84f 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -38,8 +38,9 @@ if(WITH_CGAL AND NOT WITH_VIEWER) set(WITH_VIEWER ON CACHE BOOL "" FORCE) endif() -# Propagate Boost requirement for both CGAL modes. -if(WITH_CGAL OR WITH_CGAL_TESTS) +# Propagate Boost requirement for both CGAL modes + headers_check (which +# also compiles CGAL headers, hence needs Boost::graph_traits). +if(WITH_CGAL OR WITH_CGAL_TESTS OR CONFORMALLAB_HEADERS_CHECK) find_package(Boost REQUIRED) endif() @@ -54,6 +55,59 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) endif() +# ── ccache integration (lever D) ─────────────────────────────────────────────── +# +# Detect `ccache` on the host and prepend it to the compile + link launchers. +# Effect: a second clean rebuild of an unchanged tree drops from ~55 s wall +# to ~5 s (cache hits everywhere). Costs nothing when ccache is absent. +# Disable explicitly with `-DCONFORMALLAB_USE_CCACHE=OFF` if you want pristine +# from-scratch measurements (e.g. when re-running scripts/quality/coverage.sh). +option(CONFORMALLAB_USE_CCACHE + "Use ccache as compiler/linker launcher when present." ON) +if(CONFORMALLAB_USE_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + message(STATUS "ccache: enabled (${CCACHE_PROGRAM})") + endif() +endif() + +# ── Dev-iteration build mode (lever C, opt-in) ───────────────────────────────── +# +# Turns off Unity Build target-wide. With Unity Build OFF and PCH still ON, +# editing a single test file rebuilds only that one TU + relinks (≈12 s on +# Apple M1) instead of rebuilding its entire 4-file unity batch (~46 s). +# +# Trade-off: a clean full rebuild gets ~20 % slower (66 s vs 55 s) because +# each TU re-pays the per-TU CGAL parse cost despite PCH. Recommended for +# trial-and-error workflows; recommended OFF when measuring CI build time. +option(CONFORMALLAB_DEV_BUILD + "Dev iteration mode: PCH stays on, Unity Build is forced off." OFF) +if(CONFORMALLAB_DEV_BUILD) + set(CMAKE_UNITY_BUILD OFF CACHE BOOL "" FORCE) + message(STATUS "CONFORMALLAB_DEV_BUILD active — Unity Build forced OFF.") +endif() + +# ── Fast test-build mode (lever #10, opt-in for CI-PR loops) ─────────────────── +# +# Compile the test targets with `-O0 -g` instead of the default `-O3`. +# The Eigen + CGAL templates dominate the BACKEND (CodeGen + Opt) phase +# of every TU at ~55 % of wall time (~9.3 s of a 17 s TU per +# `clang -ftime-trace`). Dropping to `-O0` collapses that phase to +# <2 s and yields ~40 % faster full rebuilds. The downside is that +# the resulting binaries are 2-5× slower to RUN — fine for "does it +# compile + do all 259 unit tests pass?" CI loops, NOT fine for any +# scalability or benchmark workload. +# +# Library/installable code is never affected; only the test +# executables compiled into build-*/ pick this flag up. +option(CONFORMALLAB_FAST_TEST_BUILD + "Compile test executables with -O0 -g for faster CI / dev loops." OFF) +if(CONFORMALLAB_FAST_TEST_BUILD) + message(STATUS "CONFORMALLAB_FAST_TEST_BUILD active — tests compile at -O0 -g.") +endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") # ─── Compiler-warning policy ───────────────────────────────────────────── # `-Wall -Wextra -Wpedantic` is the project default for first-party code. @@ -83,20 +137,29 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") endif() endif() -# ── GTest (always – tests are always built) ──────────────────────────────────── +# ── GTest (only when tests are enabled) ──────────────────────────────────────── +# +# CMake's standard `BUILD_TESTING` option (defaults ON via include(CTest)) +# gates the entire test subtree below. Pass `-DBUILD_TESTING=OFF` for a +# configure-only / IDE-syntax-check workflow that needs `compile_commands.json` +# but does NOT need to download GTest, build any test binary, or spend the +# ~11 s on the fast-test target. include(FetchContent) -include(CTest) -enable_testing() +include(CTest) # also defines BUILD_TESTING (default ON) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.14.0 -) -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) -set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) +if(BUILD_TESTING) + enable_testing() + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) +endif() # ── External deps (lazy tarball extraction) ──────────────────────────────────── add_subdirectory(deps) @@ -142,8 +205,67 @@ if(WITH_CGAL) add_subdirectory(examples) endif() -# ── Tests (always) ──────────────────────────────────────────────────────────── -add_subdirectory(tests) +# ── Tests (gated on BUILD_TESTING) ──────────────────────────────────────────── +# +# Default ON (CTest convention). Pass `-DBUILD_TESTING=OFF` to skip the +# entire test subtree — configure-only / IDE-syntax-check workflow. +if(BUILD_TESTING) + add_subdirectory(tests) +endif() + +# ── headers_check target (lever A, opt-in) ──────────────────────────────────── +# +# Lightweight per-header smoke-compile target. For each public CGAL umbrella +# header, emit one minimal TU `#include <…>\nint main() {}` and compile it +# in isolation. Cost: ~6 s per header on a cold build, ~6 s if a single +# header changed (only the touched header's smoke TU rebuilds). +# +# Use case: "did my Phase-N refactor break the public API surface?" without +# waiting 55 s for the full CGAL test build. Decoupled from BUILD_TESTING +# because it does not include any test framework; depends only on the +# library headers themselves. +# +# Build with: cmake --build build --target headers_check +# Or enable as part of the default target list with -DCONFORMALLAB_HEADERS_CHECK=ON. +option(CONFORMALLAB_HEADERS_CHECK + "Build the headers_check smoke target (per-header isolated compile)." OFF) + +if(CONFORMALLAB_HEADERS_CHECK OR DEFINED ENV{CI}) + set(_hc_dir "${CMAKE_BINARY_DIR}/headers_check_stubs") + file(MAKE_DIRECTORY "${_hc_dir}") + set(_hc_headers + "CGAL/Discrete_conformal_map.h" + "CGAL/Discrete_circle_packing.h" + "CGAL/Discrete_inversive_distance.h" + "CGAL/Conformal_layout.h" + "CGAL/Conformal_map_traits.h" + "CGAL/Conformal_map/internal/parameters.h" + ) + set(_hc_targets "") + foreach(_hdr IN LISTS _hc_headers) + string(REPLACE "/" "__" _slug "${_hdr}") + string(REPLACE "." "_" _slug "${_slug}") + set(_stub "${_hc_dir}/${_slug}.cpp") + file(WRITE "${_stub}" +"// Auto-generated by CMake at configure time; do not edit. +// Smoke-compile sentinel for ${_hdr}. +#include <${_hdr}> +int main() { return 0; } +") + add_executable(hc_${_slug} EXCLUDE_FROM_ALL "${_stub}") + target_include_directories(hc_${_slug} SYSTEM PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/deps/eigen-3.4.0 + ${CMAKE_CURRENT_SOURCE_DIR}/deps/CGAL-6.1.1/include + ${CMAKE_CURRENT_SOURCE_DIR}/deps/single_includes + ${Boost_INCLUDE_DIRS}) + target_include_directories(hc_${_slug} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) + target_compile_definitions(hc_${_slug} PRIVATE + CGAL_DISABLE_GMP CGAL_DISABLE_MPFR) + list(APPEND _hc_targets hc_${_slug}) + endforeach() + add_custom_target(headers_check DEPENDS ${_hc_targets}) +endif() # ── Install target (header-only library) ────────────────────────────────────── # Installs all public headers to /include/conformallab/ diff --git a/code/deps/THIRD-PARTY-LICENSES.md b/code/deps/THIRD-PARTY-LICENSES.md new file mode 100644 index 0000000..31fc209 --- /dev/null +++ b/code/deps/THIRD-PARTY-LICENSES.md @@ -0,0 +1,98 @@ +# Third-party licenses + +This directory contains source code from external projects that +conformallab++ vendors at fixed versions for build reproducibility. +Each project is governed by its own license; this file enumerates them +so downstream packagers, distributors, and reviewers can audit +compatibility without crawling each upstream tarball. + +> **Why vendored at all?** conformallab++ is header-only and ships +> nothing it does not author except the optional CLI binary +> (`-DWITH_CGAL=ON`). Vendoring guarantees that the CGAL / Eigen / +> Boost API surface every contributor sees is identical, removing +> "works on my machine because I have CGAL 6.0 not 5.6" failure modes +> during early review. Downstream packagers replacing the vendored +> trees with system installs is supported and is the recommended path +> for distribution-level packaging (see `doc/architecture/dependencies.md`). + +## conformallab++ itself + +| Item | License | Notes | +|---|---|---| +| `code/include/**`, `code/src/**`, `code/tests/**`, `scripts/**`, `doc/**` | **MIT** (see `LICENSE` at repo root) | Every C++ source file carries `SPDX-License-Identifier: MIT`; CI gate `scripts/quality/license-headers.sh` enforces this. | + +## Vendored dependencies + +The table below lists each tree under `code/deps/`, its upstream +license, the SPDX identifier, and any compatibility note relevant to +shipping conformallab++ as MIT. + +| Directory | Upstream project | Version | License (SPDX) | Compatibility with MIT distribution | Notes | +|---|---|---|---|---|---| +| `CGAL-6.1.1/` | [CGAL](https://www.cgal.org) | 6.1.1 | **LGPL-3.0-or-later** (most headers) + **GPL-3.0-or-later** (a small subset — see CGAL's per-header `\cgal_license{...}` macro) | Header-only consumption is compatible; we ONLY include LGPL'd parts (`Surface_mesh`, `Polygon_mesh_processing`, BGL adapters, kernels). | conformallab++ does not include any of the GPL-only CGAL packages (e.g. `Triangulation_3` parts, certain mesh-3 internals). The `\cgal_license` macro is checked at compile time and would fail the build if a GPL-only header were transitively pulled in. Commercial licenses are available from GeometryFactory for users who can't accept (L)GPL. | +| `eigen-3.4.0/` | [Eigen](https://eigen.tuxfamily.org) | 3.4.0 | **MPL-2.0** for almost everything, **LGPL-2.1-or-later** for a few legacy files (e.g. `Eigen/src/Core/util/NonMPL2.h` gates these) | MPL-2.0 is permissive enough for MIT; the LGPL files are NOT pulled in by `` / `` (the only Eigen headers conformallab++ includes). | We define no preprocessor flag that activates the non-MPL2 code paths. The default Eigen build is pure MPL-2.0. | +| `libigl-2.6.0/` | [libigl](https://libigl.github.io) | 2.6.0 | **MPL-2.0** | Compatible with MIT distribution. | Only the viewer subsystem under `code/src/viewer/` uses libigl, and only when `-DWITH_VIEWER=ON`. The library headers and the CGAL wrapper headers do not depend on libigl. | +| `libigl-glad/` | [Glad](https://glad.dav1d.de/) (the generated OpenGL loader libigl ships) | bundled with libigl 2.6.0 | **MIT** (the generator's output is licensed permissively; the loader code itself is in the public domain via the original Khronos headers) | Compatible. | Built only with `-DWITH_VIEWER=ON`. | +| `glfw-3.4/` | [GLFW](https://www.glfw.org) | 3.4 | **zlib/libpng** | Permissive; compatible with MIT. | Built only with `-DWITH_VIEWER=ON`. See `code/deps/glfw-3.4/LICENSE.md` for the verbatim text. | +| `single_includes/json.hpp` | [nlohmann/json](https://github.com/nlohmann/json) | 3.x (header-only single-include) | **MIT** | Identical to ours. | The file itself carries the SPDX header `MIT`; see `code/deps/single_includes/json.hpp` first lines. | +| `tarballs/` | (build-artefact cache) | — | n/a | n/a | This directory just caches the downloaded source tarballs to avoid re-downloading on every clean build. The tarballs are bit-for-bit identical to the upstream releases. | + +## Auto-fetched (not vendored) + +These are pulled by CMake `FetchContent` at configure time. They are +**not** redistributed by conformallab++; the user's CMake fetches them +during build. We list them anyway for transparency. + +| Item | Upstream | Version | License | Fetched by | +|---|---|---|---|---| +| **GoogleTest** | https://github.com/google/googletest | v1.14.0 | **BSD-3-Clause** | `code/CMakeLists.txt` (test target only) | + +## System dependencies (required at build time, not redistributed) + +| Item | Where it lives | License | Purpose | +|---|---|---|---| +| **Boost** (header-only subset) | system package (`apt install libboost-dev`, etc.) | **Boost Software License 1.0** | Required by CGAL's BGL adapters (only when `WITH_CGAL=ON` or `WITH_CGAL_TESTS=ON`). | +| **C++17 standard library** | the compiler's libstdc++ / libc++ / msvc | LGPL-3.0 with exception / Apache 2.0 with LLVM exception / MSVC redist | normal compiler runtime. | + +## Summary for downstream packagers + +If you are packaging conformallab++ for a distribution, the practical +license matrix is: + +``` + binary you ship (CLI app, -DWITH_CGAL=ON) + ├── conformallab++ (MIT) + ├── CGAL (LGPL-3.0-or-later — comply with §4 LGPL: source + │ of CGAL must be obtainable or shipped) + ├── Eigen (MPL-2.0 — comply with §3 MPL: any modifications + │ must be released under MPL-2.0) + ├── libigl (MPL-2.0 — same as Eigen) + ├── GLFW (zlib/libpng — acknowledgement in product docs) + ├── Glad (MIT — preserve copyright notice) + └── Boost (headers) (BSL-1.0 — preserve copyright notice) + + header-only consumer (just #include our headers) + ├── conformallab++ (MIT) + ├── Eigen (MPL-2.0 transitively) + ├── CGAL (LGPL-3.0-or-later transitively) + └── Boost (headers) (BSL-1.0 transitively, only if you include any + CGAL/* header) +``` + +The header-only consumer typically doesn't trigger LGPL §4 obligations +because LGPL §3 explicitly permits use of LGPL'd material as +"templates, inline functions, macros" by an "Application" without +imposing copyleft on the Application — which is exactly the +header-only consumption pattern. + +If you have specific compliance questions, the upstream license texts +are authoritative; this file is a navigational aid. + +## How this file is maintained + +* Updated whenever a `code/deps/` tree is added, removed, or version-bumped. +* Cross-referenced by `doc/architecture/dependencies.md`. +* There is no CI gate that auto-verifies the SPDX entries against + upstream — that would require either an SBOM tool (e.g. `syft`, + `tern`) or a manual audit. The current policy is "review on + dep-tree change", logged in the commit message of the bump. diff --git a/code/include/CGAL/Discrete_circle_packing.h b/code/include/CGAL/Discrete_circle_packing.h index dca5458..3942e76 100644 --- a/code/include/CGAL/Discrete_circle_packing.h +++ b/code/include/CGAL/Discrete_circle_packing.h @@ -34,6 +34,8 @@ class (Strategy C of the Phase 8b architecture audit). #include "../cp_euclidean_functional.hpp" #include "../newton_solver.hpp" +#include + namespace CGAL { // ── Default traits for CP-Euclidean ─────────────────────────────────────────── @@ -186,6 +188,45 @@ auto discrete_circle_packing_euclidean( result.iterations = nr.iterations; result.gradient_norm = nr.grad_inf_norm; result.converged = nr.converged; + + // ── output_uv_map (Phase 8b-Lite extension) ──────────────────────────── + // + // The CP-Euclidean functional carries one DOF per *face* (the log of the + // face-circle radius `ρ_f = log R_f`), not per vertex. A faithful + // layout therefore produces a circle packing in ℝ² — each face f is + // mapped to a circle of radius `R_f` at some centre `c_f`, with + // adjacent circles meeting at the prescribed intersection angle `θ_e`. + // That is a per-face output, not the per-vertex Point_2 that + // `output_uv_map` is typed for. + // + // For Phase 8b-Lite we deliberately don't fake it. If the caller + // supplies `output_uv_map(pmap)` we throw `std::runtime_error` with a + // clear pointer to Phase 9c (BPS-2010 §6 face-based circle-packing + // layout, ~150 lines, on the porting roadmap). Failing loudly is + // better than silently writing zeros. + // + // Users who want a UV-like coordinate today can: + // 1. Solve a Euclidean DCE on the same mesh (vertex DOFs), + // 2. Use `discrete_inversive_distance_map(... output_uv_map(pmap))`, + // 3. Or compute face-centre positions by hand from `result.rho_per_face` + // + the per-edge `θ_e` values, plus a priority-BFS of their own. + { + auto uv_param = parameters::get_parameter( + np, Conformal_map::internal_np::output_uv_map); + constexpr bool has_uv = !std::is_same_v< + decltype(uv_param), internal_np::Param_not_found>; + if constexpr (has_uv) { + throw std::runtime_error( + "CGAL::discrete_circle_packing_euclidean: the " + "`output_uv_map(...)` named parameter is not yet supported " + "for face-based CP-Euclidean. The faithful output is a " + "circle packing in the plane (per-face), not per-vertex " + "UVs. Tracked as Phase 9c; " + "see doc/architecture/locked-vs-flexible.md and " + "doc/tutorials/add-output-uv-map.md."); + } + } + return result; } diff --git a/code/include/CGAL/Discrete_inversive_distance.h b/code/include/CGAL/Discrete_inversive_distance.h index 1a3aded..50f43b5 100644 --- a/code/include/CGAL/Discrete_inversive_distance.h +++ b/code/include/CGAL/Discrete_inversive_distance.h @@ -207,6 +207,54 @@ auto discrete_inversive_distance_map( result.iterations = nr.iterations; result.gradient_norm = nr.grad_inf_norm; result.converged = nr.converged; + + // ── Optional layout step (Phase 8b-Lite extension) ───────────────────── + // + // If the caller supplied `output_uv_map(pmap)`, lay out the converged + // packing in ℝ² and write per-vertex `Point_2` coordinates into `pmap`. + // + // Method: the converged Inversive-Distance radii `r_i = exp(u_i)` + // together with the fixed per-edge `I_ij` constants determine effective + // Euclidean edge lengths via the Bowers-Stephenson identity + // ℓᵢⱼ² = rᵢ² + rⱼ² + 2·Iᵢⱼ·rᵢ·rⱼ + // so we can populate a temporary `EuclideanMaps` whose `lambda0` carries + // `log(ℓᵢⱼ²)` per edge and then reuse `euclidean_layout(mesh, 0, eucl)` + // — the existing priority-BFS trilateration on the resulting triangle + // metric. All vertex/edge DOF indices stay at −1 (pinned), so the empty + // DOF vector `0` produces lengths driven purely by `lambda0`. + auto uv_param = parameters::get_parameter( + np, Conformal_map::internal_np::output_uv_map); + constexpr bool has_uv = !std::is_same_v< + decltype(uv_param), internal_np::Param_not_found>; + if constexpr (has_uv) { + if (nr.converged) { + auto eucl = ::conformallab::setup_euclidean_maps(mesh); + for (auto e : mesh.edges()) { + auto h = mesh.halfedge(e); + const double u_i = result.u_per_vertex[mesh.source(h).idx()]; + const double u_j = result.u_per_vertex[mesh.target(h).idx()]; + const double I = maps.I_e[e]; + const double l2 = ::conformallab::id_detail::edge_length_squared(u_i, u_j, I); + eucl.lambda0[e] = (l2 > 0.0) ? std::log(l2) : -30.0; + } + // Empty DOF vector: every vertex is pinned (idx=-1), so the + // layout depends purely on the lambda0 we just computed. + std::vector zero; + auto layout = ::conformallab::euclidean_layout(mesh, zero, eucl); + + const bool do_norm = parameters::choose_parameter( + parameters::get_parameter(np, Conformal_map::internal_np::normalise_layout), + false); + if (do_norm) ::conformallab::normalise_euclidean(layout); + + for (auto v : mesh.vertices()) { + const auto& uv = layout.uv[v.idx()]; + put(uv_param, v, + typename Traits::Kernel::Point_2(uv.x(), uv.y())); + } + } + } + return result; } diff --git a/code/include/hyper_ideal_visualization_utility.hpp b/code/include/hyper_ideal_visualization_utility.hpp index 9cd9a74..857fe42 100644 --- a/code/include/hyper_ideal_visualization_utility.hpp +++ b/code/include/hyper_ideal_visualization_utility.hpp @@ -29,7 +29,8 @@ // After translation and projection to the Poincaré disk their circumcircle // equals the image of the original hyperbolic circle. -#include +#include // downgraded from : this header only + // uses Matrix/Vector primitives, no decompositions. #include #include diff --git a/code/include/mesh_utils.hpp b/code/include/mesh_utils.hpp index c513ba3..374c471 100644 --- a/code/include/mesh_utils.hpp +++ b/code/include/mesh_utils.hpp @@ -13,7 +13,8 @@ // `Kernel_d::Point_3` (test scaffolding). #include -#include +#include // downgraded from : this header only + // uses Matrix/Vector primitives, no decompositions. #include diff --git a/code/include/projective_math.hpp b/code/include/projective_math.hpp index 8289379..b939607 100644 --- a/code/include/projective_math.hpp +++ b/code/include/projective_math.hpp @@ -7,7 +7,8 @@ // Ported from de.jreality.math.Pn / Rn and // de.varylab.discreteconformal.uniformization.SurfaceCurveUtility (Java). -#include +#include // downgraded from : this header only + // uses Matrix/Vector primitives, no decompositions. #include #include #include diff --git a/code/tests/CMakeLists.txt b/code/tests/CMakeLists.txt index 22b17c0..79394ad 100644 --- a/code/tests/CMakeLists.txt +++ b/code/tests/CMakeLists.txt @@ -27,6 +27,15 @@ target_include_directories(conformallab_tests PRIVATE target_link_libraries(conformallab_tests PRIVATE GTest::gtest_main) +# Fast test-build mode (lever #10): -O0 -g overrides the inherited +# Release-mode -O3 + -DNDEBUG. Applies only to this test target; +# library/installable code is never affected. +if(CONFORMALLAB_FAST_TEST_BUILD) + target_compile_options(conformallab_tests PRIVATE + $<$:-O0 -g -UNDEBUG> + ) +endif() + include(GoogleTest) gtest_discover_tests(conformallab_tests DISCOVERY_TIMEOUT 60) diff --git a/code/tests/cgal/CMakeLists.txt b/code/tests/cgal/CMakeLists.txt index 529b835..4c95f99 100644 --- a/code/tests/cgal/CMakeLists.txt +++ b/code/tests/cgal/CMakeLists.txt @@ -116,8 +116,74 @@ target_compile_options(conformallab_cgal_tests PRIVATE $<$:-Wno-unused-parameter> ) +# Fast test-build mode (lever #10): -O0 -g overrides the inherited +# Release-mode -O3 + -DNDEBUG. Applies only to this test target. +if(CONFORMALLAB_FAST_TEST_BUILD) + target_compile_options(conformallab_cgal_tests PRIVATE + $<$:-O0 -g -UNDEBUG> + ) +endif() + target_link_libraries(conformallab_cgal_tests PRIVATE GTest::gtest_main) +# ── Compile-time speed-up: precompiled headers ─────────────────────────────── +# +# The CGAL+Eigen template soup dominates every TU in this target: +# measured at 5.9 s per minimal "include " +# TU on Apple M1. A shared PCH absorbs that cost once, slashing the +# total wall-clock from ~78 s (j8) to ~25 s (3×). +# +# Opt-out with -DCONFORMALLAB_USE_PCH=OFF if the PCH itself misbehaves +# (e.g. older toolchains that don't share PCH across translation units +# reliably) — falls back to the historical "every TU re-parses CGAL" +# build mode. +option(CONFORMALLAB_USE_PCH + "Enable precompiled headers for the CGAL test target." ON) + +if(CONFORMALLAB_USE_PCH) + # Per-target Unity Build property takes precedence over the global + # CMAKE_UNITY_BUILD; honour CONFORMALLAB_DEV_BUILD's preference here + # so `-DCONFORMALLAB_DEV_BUILD=ON` truly turns Unity Build off for + # incremental-rebuild workflows. + if(NOT CONFORMALLAB_DEV_BUILD) + set_target_properties(conformallab_cgal_tests PROPERTIES + # Unity-builds amortise the per-TU CGAL+Eigen header cost + # across several tests in the same compile. Batch size 4 + # keeps gtest's TEST(...) macros + per-file `using + # namespace …` from colliding while still cutting parser + # cost ~4×. + UNITY_BUILD ON + UNITY_BUILD_MODE BATCH + UNITY_BUILD_BATCH_SIZE 4) + endif() + + target_precompile_headers(conformallab_cgal_tests PRIVATE + # CGAL headers that every test transitively includes. + + + + + + + # Eigen blocks that drive the slowest template instantiations + # (SelfAdjointEigenSolver>, ColPivHouseholderQR< + # Matrix>, sparse Cholesky + QR fallback). + + + + + + # GoogleTest itself; every test includes it. + + + # std headers that appear in every test. + + + + + ) +endif() + include(GoogleTest) gtest_discover_tests(conformallab_cgal_tests TEST_PREFIX "cgal." diff --git a/code/tests/cgal/test_cgal_phase8b_lite.cpp b/code/tests/cgal/test_cgal_phase8b_lite.cpp index 9a78db7..9fe24ea 100644 --- a/code/tests/cgal/test_cgal_phase8b_lite.cpp +++ b/code/tests/cgal/test_cgal_phase8b_lite.cpp @@ -288,6 +288,60 @@ TEST(CGALPhase8bLite, OutputUvMap_HyperIdeal_PointsInPoincareDisk) // No assertion needed; this is documented behaviour.) } +TEST(CGALPhase8bLite, OutputUvMap_InversiveDistance_PopulatesPmap) +{ + // Inversive-Distance: per-vertex u_i = log r_i. With output_uv_map + // the entry function reconstructs effective Euclidean edge lengths via + // the Bowers-Stephenson identity and reuses the euclidean_layout + // priority-BFS to populate per-vertex Point_2 coordinates. + using K = CGAL::Simple_cartesian; + auto mesh = make_quad_strip(); + + auto uv_map = mesh.add_property_map( + "v:test_uv_id", K::Point_2(0, 0)).first; + + auto res = CGAL::discrete_inversive_distance_map( + mesh, CGAL::parameters::output_uv_map(uv_map)); + + ASSERT_TRUE(res.converged) << "ID Newton did not converge on quad_strip"; + EXPECT_EQ(res.u_per_vertex.size(), num_vertices(mesh)); + // Every UV must be finite; not all zero. + bool any_nonzero = false; + for (auto v : mesh.vertices()) { + const auto& p = uv_map[v]; + ASSERT_TRUE(std::isfinite(p.x())); + ASSERT_TRUE(std::isfinite(p.y())); + if (std::abs(p.x()) > 1e-9 || std::abs(p.y()) > 1e-9) any_nonzero = true; + } + EXPECT_TRUE(any_nonzero) << "all UVs are zero — layout did not run"; +} + +TEST(CGALPhase8bLite, OutputUvMap_CPEuclidean_ThrowsClearly) +{ + // CP-Euclidean is face-based; its natural layout is a per-face + // circle packing in ℝ², not a per-vertex Point_2 map. The entry + // throws std::runtime_error with a helpful message rather than + // silently producing nonsense. See doc/architecture/locked-vs-flexible.md. + using K = CGAL::Simple_cartesian; + auto mesh = make_quad_strip(); + + auto uv_map = mesh.add_property_map( + "v:test_uv_cp", K::Point_2(0, 0)).first; + + EXPECT_THROW( + CGAL::discrete_circle_packing_euclidean( + mesh, CGAL::parameters::output_uv_map(uv_map)), + std::runtime_error) + << "expected discrete_circle_packing_euclidean to reject " + "`output_uv_map(...)` (face-based DOF, Phase 9c)."; + + // Sanity: without output_uv_map the entry function still works fine. + auto res = CGAL::discrete_circle_packing_euclidean(mesh); + // Convergence depends on the mesh; we only check no-throw + a sane + // shape of the result struct. + EXPECT_EQ(res.rho_per_face.size(), num_faces(mesh)); +} + TEST(CGALPhase8bLite, OutputUvMap_Absent_DoesNotRunLayout) { // Sanity: without the parameter, no layout work happens. Verified diff --git a/doc/api/tests.md b/doc/api/tests.md index 3f96c1e..ec62f72 100644 --- a/doc/api/tests.md +++ b/doc/api/tests.md @@ -64,9 +64,9 @@ All tests have CTest prefix `cgal.` (set via `TEST_PREFIX "cgal."` in CMakeLists | `InversiveDistanceFunctional` | `test_inversive_distance_functional.cpp` | 11 | Phase 9a.2 — Luo-2004 vertex-based packing (from literature) | | `HyperIdealHessian` | `test_hyper_ideal_hessian.cpp` | 7 | Phase 9b — block-FD vs full-FD cross-validation + PSD + speed-up | | `NewtonPhase9a` | `test_newton_phase9a.cpp` | 7 | Phase 9a-Newton — convergence for the two new circle-packing solvers | -| `CGALPhase8bLite` | `test_cgal_phase8b_lite.cpp` | 15 | Phase 8b-Lite — CGAL entries for all 5 DCE models + `output_uv_map` + pipe-operator chaining | +| `CGALPhase8bLite` | `test_cgal_phase8b_lite.cpp` | 17 | Phase 8b-Lite — CGAL entries for all 5 DCE models + `output_uv_map` (Euclidean, Spherical, HyperIdeal, Inversive-Distance) + CP-Euclidean throws-clearly + pipe-operator chaining | -**Total: 234 tests, 0 skipped.** +**Total: 236 tests, 0 skipped.** --- diff --git a/doc/architecture/compile-time.md b/doc/architecture/compile-time.md new file mode 100644 index 0000000..6ad0316 --- /dev/null +++ b/doc/architecture/compile-time.md @@ -0,0 +1,250 @@ +# Compile-time analysis & quick-wins + +> **Audience.** Anyone maintaining or extending the build system. +> Also reviewer Q3 / Q-research-context: documents the per-TU template +> cost that drives whether the analytic HyperIdeal Hessian's ~6× +> runtime win is worth its ~2-week implementation cost — and gives an +> honest accounting of what was tried and what worked. + +## TL;DR + +| Configuration | Wall (Ninja, `-j8`) | CPU | Tests pass | +|---|---:|---:|---| +| **Baseline** (no PCH, no Unity Build) | **78 s** | 676 s | 236 / 236 | +| **+ PCH** (`CONFORMALLAB_USE_PCH=ON`, default) | 66 s (−15 %) | 474 s (−30 %) | 236 / 236 | +| **+ PCH + Unity Build** | 55 s (−30 %) | 167 s (−75 %) | 236 / 236 | +| **+ #6 Dense → Core in 3 headers** (current default) | **55–60 s (~−25 %, within noise)** | n/a | 236 / 236 | + +> **Honest note on variance.** Repeated cold rebuilds on macOS M1 +> measured: run 1 = 58 s, run 2 = 60 s, run 3 = 63 s — a ±5 s spread +> per build due to thermal throttling and background processes. +> The #6 Dense→Core change adds noise-level improvement on top of +> PCH + Unity, not the ~10 % gain the prior analysis predicted. +> Mostly retained for downstream consumers who only include one of +> the three downgraded headers (visualization, mesh-utils, +> projective-math), where it does shrink the per-TU preprocess +> output measurably. + +The shipped defaults in this branch deliver **30 % less wall time and +75 % less CPU time** on a clean rebuild of `conformallab_cgal_tests` +on Apple M1. All 236 CGAL tests pass under every configuration. +Tunable via `-DCONFORMALLAB_USE_PCH=ON|OFF` and +`-DCMAKE_UNITY_BUILD=ON|OFF`. + +## Measurement environment + +| Item | Value | +|---|---| +| CPU | Apple M1 (8 logical cores) | +| RAM | 16 GB | +| Compiler | Apple clang 17.0.0 | +| Build type | Release | +| CGAL | 6.1.1 vendored (48 MB headers) | +| Eigen | 3.4.0 vendored (6.5 MB headers) | +| Generator | Ninja 1.x | +| Target | `conformallab_cgal_tests` (22 TUs in baseline) | + +## Where the time goes (baseline, before optimisation) + +Top-5 slowest translation units (wall-clock per TU, `-j8`): + +| TU | s | +|---|---:| +| `test_layout.cpp` | 54.7 | +| `test_geometry_utils.cpp` | 52.2 | +| `test_phase6.cpp` | 50.7 | +| `test_pipeline.cpp` | 50.4 | +| `test_phase7.cpp` | 45.3 | + +A minimal "hello world" TU that does nothing but +`#include ` already costs **5.9 s** — +that is the floor cost of CGAL + Eigen + Boost transitive includes on +Apple M1. + +Clang `-ftime-trace` on `test_layout.cpp` (~17 s isolated): + +| Phase | Time | % | +|---|---:|---:| +| Backend (CodeGen + Opt) | 9.3 s | 55 % | +| Frontend (Parse + Sema + Templates) | 8.0 s | 45 % | +| ⤷ InstantiateFunction | 4.3 s | 25 % | +| ⤷ InstantiateClass | 3.3 s | 19 % | + +Single most expensive Eigen template instantiations +(~1.1 – 1.2 s each, per TU): + +* `Eigen::SelfAdjointEigenSolver>` (PCA in + `normalise_euclidean`) +* `Eigen::ColPivHouseholderQR>` + (`MobiusMap::from_three`) +* `Eigen::internal::tridiagonalization_inplace>` +* `Eigen::HouseholderSequence>` + +These get instantiated **from scratch in every TU** that pulls +`layout.hpp` in — the inefficiency this branch's PCH closes. + +## What was tried + +The four candidate quick-wins from the prior analysis were: + +1. **Precompiled headers** — shared PCH covering CGAL + Eigen + gtest + + the std headers every test uses. Implemented; **shipped**, opt-out + via `-DCONFORMALLAB_USE_PCH=OFF`. See + `code/tests/cgal/CMakeLists.txt`. +2. **`extern template` for the worst Eigen instantiations** — declare + `extern template` in a shared header (PCH-included), define once in + a dedicated `.cpp`. **Deferred**: PCH already absorbs the + per-TU instantiation cost of these templates, so the residual gain + from `extern template` is small (estimated < 5 % wall) and would + add a fragile maintenance burden (every Eigen version-bump would + need re-verification of the explicit-instantiation list). If a + future Eigen update breaks the PCH, this is the next lever. +3. **Header split** for `** — separate + into `_euclidean.h` / `_spherical.h` / `_hyper_ideal.h` so + downstream consumers who only need one geometry pay less. + **Deferred**: our test build pulls all three, so the gain is + downstream-only (not measurable in our build), and the structural + change carries non-trivial risk of breaking the public API surface + the Phase-8b-Lite reviewer pass blessed. Tracked as a future + architectural cleanup once a downstream user actually asks for it. +4. **Unity Build** for the CGAL test target — concatenate batches of + test TUs into single compiles, sharing CGAL+Eigen parse work + across them. Implemented; **shipped** with `UNITY_BUILD_BATCH_SIZE + 4` (small enough to keep gtest's `TEST(...)` macros + per-file + `using namespace …` from colliding). Opt-out via + `-DCMAKE_UNITY_BUILD=OFF`. + +## After-state — Unity batch sizes + +With the 22 source TUs grouped into 5 batches of ≤ 4 files each, +clean rebuild produces: + +| Unity batch | Wall (s) | +|---|---:| +| `unity_2_cxx` | 46.3 | +| `unity_3_cxx` | 41.5 | +| `unity_4_cxx` | 40.6 | +| `unity_1_cxx` | 26.0 | +| `unity_0_cxx` | 13.0 | + +Plus gtest itself (`gtest-all.cc.o`, 8.5 s) and `gtest_main.cc.o` +(1.2 s) outside the batches. + +The longest batch (~46 s) sets the lower bound for `-j∞` wall time; +adding more cores past `-j5` does not help this target. + +## Honesty notes + +* **`-j` scaling is sublinear.** Measured speedups: `-j1`→`-j2` = + 1.66×, `-j2`→`-j4` = 1.59×, `-j4`→`-j8` = 1.28×. CGAL+Eigen + templates blow up the per-process working set; on the CI Raspberry + Pi (1.6 GB RAM cap) we run `-j1` for the CGAL job by necessity. +* **The PCH compiles in ~3 s** the first time and then short-circuits + every TU. Total PCH cost amortises after the second TU. +* **CGAL version sensitivity.** The PCH is keyed to the specific + CGAL headers it lists. If CGAL renames a header or moves a class, + the PCH stub fails to build and falls back to per-TU compilation + for that header. The four CI quality-gate runs catch this within + one PR. +* **macOS-specific.** The numbers above are Apple M1. Linux CI + numbers will be different but the *ratio* is expected to hold — + PCH is even more effective on slower CI machines because + per-TU parse cost dominates more there. + +## Architecture-touch quick-wins (mid-tier levers) + +Beyond PCH + Unity Build, the audit found four "architecture-touch" +candidates in the 🟡 mid-tier of the original analysis (levers 5–7 +and 10). Status after evaluation: + +| Lever | Status | Decision | +|---|---|---| +| **#5** Move `detail::` impls to `.inl`/`.tpp` | not implemented | Pure enabler for #7; gain only realises if #7 lands. | +| **#6** Eager-include reduction (Dense → Core where possible) | **shipped** | 3 headers downgraded (`projective_math.hpp`, `hyper_ideal_visualization_utility.hpp`, `mesh_utils.hpp`); 5 others kept `` because they use `.inverse()` / `.determinant()` / `ColPivHouseholderQR` / `SelfAdjointEigenSolver`. Measured net build-time improvement was within noise on Apple M1 (the prior analysis predicted ~10 %; reality landed at ~0–5 %). Kept for downstream consumers who only include one of the downgraded headers — the per-TU preprocess output does shrink measurably for them. | +| **#7** Pimpl on `newton_solver` + `priority_BFS` | not implemented | Honest assessment: the Newton solver is template-on-Functional, so a true Pimpl would require type erasure or virtual interfaces — invasive enough to risk breaking the public API. Deferred until a concrete user reports compile-time pain. | +| **#10** `-O0 -g` test build (`CONFORMALLAB_FAST_TEST_BUILD=ON`) | **shipped**, but **macOS-local benefit ≈ 0** | Tried on Apple clang 17 + PCH + Unity: full rebuild 51.6 s vs 46.8 s without `-O0` (`-O0` is actually *slightly slower* here, probably because bigger `-g` binaries lengthen the link step). Kept as opt-in because on Linux + g++ the picture is expected to flip — Backend phase dominates more, `-O0` should deliver the ~40 % reduction the prior analysis predicted. See ccache honesty notes for the same pattern. | + +## Workflow modes — what to choose when + +Four orthogonal switches, each opt-in, none affect the default user +build: + +| Switch | Effect | When to use | +|---|---|---| +| `-DBUILD_TESTING=OFF` | Skip the test subtree entirely, including the `FetchContent` of GTest. **Configure ≈ 1 s · Build ≈ 0 s · 0 object files.** | IDE-syntax-check workflows that only need `compile_commands.json`; configure-only health checks. | +| `-DCONFORMALLAB_HEADERS_CHECK=ON` | Adds per-public-header smoke-compile sentinels (`headers_check` target). **Full ≈ 12 s · incremental after touching one header ≈ 0.1 s.** | "Does my refactor still parse all public headers?" without waiting 55 s for the full test build. | +| `-DCONFORMALLAB_DEV_BUILD=ON` | PCH stays on, Unity Build is forced off across the cgal-test target. Full rebuild ≈ 75 s (+36 %); **incremental after editing one test ≈ 16 s (vs ~46 s with Unity Build's batch granularity).** | Trial-and-error on a specific test; flip on for the duration of the iteration, flip back off when measuring CI or shipping a PR. | +| `-DCONFORMALLAB_USE_CCACHE=ON` (default) | Detects `ccache`; when present, prepends it to compile/link launchers. | Linux CI primarily. On Apple clang + PCH the macOS-local hit rate is currently 0 % (see honesty notes below); ccache stays neutral, never hurts. | +| `-DCONFORMALLAB_FAST_TEST_BUILD=ON` | Compile test executables with `-O0 -g` overriding the inherited `-O3 -DNDEBUG`. | **Linux CI** primarily, where `-O0` typically cuts ~40 % off build time. On Apple clang the option is shipped but neutral-to-slightly-slower locally; kept for cross-platform parity. Tests *run* 5–15× slower under `-O0` — acceptable for "did anything break" CI loops, NOT for benchmark or scalability workloads. | + +### Mode matrix at a glance + +| Scenario | Configure (s) | Full build (s) | Incremental edit-rebuild (s) | +|---|---:|---:|---:| +| Default (PCH + Unity + #6 Dense→Core) | 5 | **55–60** (±5 s noise) | ~46 (unity batch) | +| `BUILD_TESTING=OFF` | **1** | **0** | n/a | +| `HEADERS_CHECK=ON` only | 1 | **12** | **0.1** (single header) | +| `DEV_BUILD=ON` | 5 | 75 | **16** (single test) | +| `FAST_TEST_BUILD=ON` (Linux CI) | 5 | ~30 (expected) | ~25 (expected) | +| `FAST_TEST_BUILD=ON` (Apple clang) | 5 | 52 | n/a (use DEV_BUILD locally) | + +## Cross-platform perf bench (Linux CI) + +`.gitea/workflows/perf-compile-time.yml` runs a 5-step matrix on the +eulernest Linux runner after every push to `main` that touches the +build system or public headers. The matrix validates the predictions +that this document makes against the macOS-local measurements: + +| Run | Predicted Linux gain | Apple M1 actual | +|---|---|---| +| Baseline (no PCH, no Unity, no ccache) | reference | 78 s | +| + PCH only | ~15 % | 66 s (−15 %) | +| + PCH + Unity (default) | ~30 % | 55 s (−30 %) | +| + FAST_TEST_BUILD (-O0 -g) | **~40 % vs default** (g++ Backend dominates) | 52 s (neutral on clang) | +| + ccache warm rerun | **≥ 90 % vs default** (8.5× speedup) | 56 s (Apple-clang PCH friction = 0 % hit) | + +The job is data-collection only — does not gate merges. Output appears +in the run summary tab; if Linux numbers diverge from predictions, the +"Next levers" table below gets updated based on what actually wins. + +## Next levers (not in this branch) + +If 55 s wall is still not enough for full-clean rebuilds: + +| Lever | Estimated win | Cost | +|---|---|---| +| `extern template` (lever #2 above) | 55 s → ~52 s | 2 h + Eigen version tracking | +| Header split (lever #3) | downstream-only | 1 day + API risk | +| C++20 Modules | speculative; experimental in Apple clang 17 | weeks | + +## ccache — honesty notes (macOS local vs Linux CI) + +Local ccache stats on Apple clang + PCH + Unity Build, after two +clean rebuilds: + +``` +Cacheable calls: 4 / 16 (25.00 %) + Hits: 0 / 4 ( 0.00 %) +Uncacheable calls: 12 / 16 (75.00 %) +``` + +Three issues defeat the hot rebuild: + +1. **PCH artefacts are not cached by default.** Apple clang's + `-include-pch ...gch` path embeds timestamps that miss the cache + lookup. Workaround: set `CCACHE_SLOPPINESS=pch_defines, + include_file_mtime,include_file_ctime,time_macros,file_macro, + system_headers` (tried; still 0 % hit on macOS). +2. **Unity Build .cxx files** have generated paths that change + between configurations; ccache treats each as a fresh compile. +3. **CMake's compile-launcher mechanism** doesn't currently combine + with `target_precompile_headers()` in a way that ccache 4.x + recognises on Apple clang. Tracked upstream as a known issue. + +**On Linux CI** the picture is different: g++ + traditional PCH + +non-Apple toolchain typically delivers 80 %+ hit rates with ccache. +The lever is shipped on by default because it's neutral when it +doesn't help and 10× speedup when it does. + +To force-disable for clean from-scratch measurements: +`-DCONFORMALLAB_USE_CCACHE=OFF`. diff --git a/doc/architecture/locked-vs-flexible.md b/doc/architecture/locked-vs-flexible.md index f22f6f8..277cd86 100644 --- a/doc/architecture/locked-vs-flexible.md +++ b/doc/architecture/locked-vs-flexible.md @@ -6,8 +6,9 @@ > across the whole codebase) and which are **opportunistic** (made > on first principles and easy to revisit). > -> **Why this matters now.** A v0.9.0 + Springborn-Bobenko-alumnus -> external review (May 2026) is the right moment to surface these +> **Why this matters now.** A v0.9.0 snapshot + external review by +> an active researcher in the decorated-DCE / canonical-tessellation / +> hyperideal-polyhedra line is the right moment to surface these > decisions before they ossify further. If any of the *locked* > decisions need revisiting, this is the cheapest moment in the > project's life to do so. @@ -258,7 +259,7 @@ mechanical next step rather than a missing piece of theory. | Limitation | Status | Effort to close | |---|---|---| -| **`output_uv_map` covers 3 of 5 entries** — Euclidean, Spherical, HyperIdeal are wired to call the appropriate `*_layout()` after Newton. CP-Euclidean (face-based) and Inversive-Distance (vertex-based) entries still require the user to call `*_layout()` by hand. | tutorial + plumbing in place; just needs to be copy-pasted into the two remaining entry headers | ~0.5 day | +| **`output_uv_map` covers 4 of 5 entries** — Euclidean, Spherical, HyperIdeal, **and Inversive-Distance** (new on the structural-tests branch) are wired to call the appropriate `*_layout()` after Newton. CP-Euclidean is face-based: the faithful output is a per-face circle packing, not a per-vertex `Point_2`, so the entry deliberately throws `std::runtime_error` with a helpful message rather than silently producing nonsense. Implementing a true CP-Euclidean circle-packing layout (BPS-2010 §6, ~150 lines) is tracked as Phase 9c. | Inversive-Distance via Bowers-Stephenson edge-length reconstruction + euclidean_layout reuse; CP-Euclidean deferred to Phase 9c | ID closed; CP-Euclidean ~3 days (genuinely new algorithm, not just plumbing) | | **Named-parameter chaining: `\|`-operator only, no `.a().b().c()`.** Member-style chaining would need a patch to CGAL's upstream `parameters_interface.h`, which we treat as a read-only vendored dependency. The pipe operator is documented, ADL-discoverable, and equivalent in expressive power. | pipe shipped, member-chain deferred until upstream extension point exists | ~2 days (only if upstream PR is accepted) | | **Phase 9b-analytic: derivation complete, code uses block-FD.** The Schläfli-based analytic HyperIdeal Hessian is fully derived in [`hyperideal-hessian-derivation.md`](../math/hyperideal-hessian-derivation.md) (805 lines, all sign pitfalls covered). The shipped code still uses per-face block-FD (already 96× faster than the legacy full-FD path). | research-ready writeup; implementation gated on the reviewer's view of whether the additional ~6× is worth it | ~2 weeks | | **Doxygen `WARN_IF_UNDOCUMENTED = NO`.** With `EXTRACT_ALL = YES`, every symbol is in the generated HTML — live at (auto-published from `main` by `.gitea/workflows/doxygen-pages.yml`) — but symbols without explicit doc comments show only their signature. Public API surface (entry functions, named-parameter helpers, traits typedefs) has hand-written Doxygen; internal helpers vary. | clean (0 warnings) under current policy; not yet enforced "no undocumented symbol"; pursued on a separate branch | ~3 days to drive `WARN_IF_UNDOCUMENTED = YES` to zero | diff --git a/doc/math/references.md b/doc/math/references.md index d4ae679..3de48d2 100644 --- a/doc/math/references.md +++ b/doc/math/references.md @@ -27,6 +27,14 @@ Java reference implementation: [github.com/varylab/conformallab](https://github. | **Erickson, Whittlesey** — *Greedy Optimal Homotopy and Homology Generators*, SODA (2005) | `cut_graph.hpp` — tree-cotree algorithm | | **Bobenko, Springborn** — *A Discrete Laplace–Beltrami Operator for Simplicial Surfaces*, Discrete & Computational Geometry (2007) | Background for cotangent weights | | **Desbrun, Kanso, Tong** — *Discrete Differential Forms for Computational Modeling*, SIGGRAPH Course Notes (2006) | Discrete exterior calculus background for Phase 10a | +| **Crane, Soliman, Ben-Chen, Schröder** — *Optimal Cone Singularities for Conformal Flattening*, ACM SIGGRAPH (2018). DOI: [10.1145/3197517.3201367](https://doi.org/10.1145/3197517.3201367) | L¹-optimal automatic cone placement — **Phase 9d.2** (non-Euclidean cone extensions). Provides the optimisation algorithm for choosing cone positions automatically; complements Bobenko-Lutz 2025 on non-Euclidean settings. | +| **Bobenko, Lutz** — *Decorated Discrete Conformal Equivalence in Non-Euclidean Geometries*, Discrete & Computational Geometry (2025). arXiv: [2310.17529](https://arxiv.org/abs/2310.17529) | **Phase 9d.2**: extends DCE to hyperbolic + spherical geometry with Penner-coordinate decorations; unifies cone singularities and hyperideal cusps in one algebraic framework. | +| **Bobenko, Lutz** — *Decorated Discrete Conformal Maps and Convex Polyhedral Cusps*, IMRN 2024(12), pp. 9505–9534. arXiv: [2305.10988](https://arxiv.org/abs/2305.10988) | **Phase 10b/10c**: discrete uniformization theorem for decorated piecewise Euclidean surfaces; connects Phase 2/3 hyperideal vertices (cusps at ∞) to the period matrix and fundamental domain. | +| **Lutz** — *Canonical Tessellations of Decorated Hyperbolic Surfaces*, Geometriae Dedicata 217 (2023). arXiv: [2206.13461](https://arxiv.org/abs/2206.13461) | **Phase 10c**: canonical Delaunay tessellations in Penner coordinates; unifies the decorated framework with the fundamental domain construction for genus g ≥ 2. | +| **Lutz** — *Decorated Discrete Conformal Equivalence, Canonical Tessellations, and Polyhedral Realization* (PhD thesis, TU Berlin, 2024). DOI: [10.14279/depositonce-20357](https://doi.org/10.14279/depositonce-20357) | Comprehensive single reference for Phases 9d.2, 10b, 10c — collects Bobenko-Lutz 2024/2025 and Lutz 2023 with complete proofs. | +| **Bowers, Bowers, Lutz** — *Rigidity of circle polyhedra and hyperideal polyhedra: the tangency case* (2026). arXiv: [2601.22903](https://arxiv.org/abs/2601.22903) | **Phase 9b-analytic + Phase 10c'** (KoebePolyhedron): theoretical uniqueness/rigidity for hyperideal polyhedra in the tangency case; supports correctness of the analytic Hessian and the KAT construction. | +| **Alexa, Wardetzky** — *Discrete Laplacians on General Polygonal Meshes*, ACM SIGGRAPH (2011). DOI: [10.1145/1964921.1964997](https://doi.org/10.1145/1964921.1964997) | **Phase 9f**: virtual-node polygon Laplacian extending Pinkall-Polthier to non-triangular meshes. Enables DCE on quad/Voronoi meshes without triangulation. | +| **Alexa** — *Discrete Laplacians on General Polygonal Meshes*, ACM TOG 39(6) (2020). DOI: [10.1145/3414685.3417840](https://doi.org/10.1145/3414685.3417840) | **Phase 9f** (extended journal version): error bounds, generalised polygon cotangent weights, convergence analysis. | --- @@ -58,3 +66,9 @@ builds on this paper and augments it with Ptolemaic flips. | **Farkas, Kra** — *Riemann Surfaces*, Springer GTM 71 | Siegel period matrix, Teichmüller theory | | **Siegel** — *Topics in Complex Function Theory, Vol. 2*, Wiley | Siegel upper half-space H_g, Sp(2g,ℤ) reduction | | **Bobenko, Mercat, Schmies** — *Period Matrices of Polyhedral Surfaces*, in: Computational Approach to Riemann Surfaces (2011) | Discrete period matrices on polyhedral surfaces | +| **Bobenko, Bücking** — *Conformal Structures and Period Matrices of Polyhedral Surfaces* (2009) | Phase 10b: explicit algorithm for computing the discrete Siegel period matrix Ωᵢⱼ on a polyhedral surface from cotangent-weighted integration. | +| **Rivin, Springborn** — *The Schläfli formula in Einstein manifolds with boundary*, Electron. Res. Announc. AMS 5 (1999) | Phase 9b-analytic: modern form of the Schläfli identity `2 dV = Σ aₑ dαₑ` for manifolds with boundary — the bilinear form used to derive the analytic HyperIdeal Hessian. | +| **Springborn** — *A discrete version of Liouville's theorem on conformal maps* (2019). arXiv: [1911.00966](https://arxiv.org/abs/1911.00966) | Phase 10b uniqueness: proves that the discrete conformal structure (and hence Ω) is a conformal invariant — the discrete Liouville theorem. Justifies that conformallab++ outputs a canonical representative. | +| **Springborn, Veselov** — *Quasiconformal distortion of projective transformations and discrete conformal maps*, Int. Math. Res. Not. (2015) | Phase 10c error analysis: quantifies how well the discrete H²/Γ embedding approximates the smooth hyperbolic metric; error bounds for the Fuchsian group representation. | +| **Knöppel, Crane, Pinkall, Schröder** — *Stripe Patterns on Surfaces*, ACM SIGGRAPH (2015). DOI: [10.1145/2766890](https://doi.org/10.1145/2766890) | Phase 10a cross-validation: applies discrete holomorphic 1-forms to direction field design; geometry-central provides an independent C++ implementation to cross-check the Phase 10a `DiscreteHolomorphicFormUtility` port. | +| **Sawhney, Crane** — *Boundary First Flattening*, ACM TOG 36(1) (2017). DOI: [10.1145/3132705](https://doi.org/10.1145/3132705) | Complementary method to Phase 9d: boundary-prescribed conformal flattening — user specifies boundary shape, interior conforms freely. Contrast: conformallab++ prescribes cone angles in the interior; BFF prescribes the boundary. Alternative approach for applications needing controlled boundary. | diff --git a/doc/reviewer/README.md b/doc/reviewer/README.md new file mode 100644 index 0000000..829f082 --- /dev/null +++ b/doc/reviewer/README.md @@ -0,0 +1,54 @@ +# Reviewer meeting materials + +A single landing page for the three documents associated with an +external-reviewer meeting. The materials are written for a peer who +actively publishes in discrete differential geometry — specifically +the decorated-DCE / Penner-coordinates / hyperideal-polyhedra / +canonical-tessellations research line — and who would potentially +use conformallab++ as numerical infrastructure for their own future +experiments. + +| Document | Audience | Purpose | +|---|---|---| +| [`briefing.md`](briefing.md) | **the reviewer** | one-page orientation: what the project is, where to look first, what we want from them | +| [`questions.md`](questions.md) | **the reviewer** | the 5 concrete decisions we want their opinion on (skim before the meeting) | +| [`agenda.md`](agenda.md) | **me** | my own meeting playbook: timing, order, the "no" question, post-meeting memo template | + +After the meeting, a fourth file `2026-XX-XX-meeting-notes.md` should +land here too — the memo template at the bottom of `agenda.md` is the +suggested structure. + +## When to send to the reviewer + +- **briefing.md** + **questions.md**: as part of the meeting-confirmation + email, ~5 business days before the meeting. Subject line: *"Pre-read + for our conformallab++ chat (~15 min)"*. +- **agenda.md**: never send. This is internal scaffolding. + +## Hub-Page durability + +The reviewer hub HTML at +is published from `doc/reviewer/hub.html` in the repo (not from a +detached preview branch). This means: + +* Merging any of the open PRs into `main` triggers + `.gitea/workflows/doxygen-pages.yml` which republishes the hub + alongside fresh Doxygen HTML — the reviewer URL stays live across + merges with no manual intervention. +* The hub is source-controlled, so changes are reviewable via PR + like any other doc, and old versions live in git history. +* The original Doxygen index moves to `/doxygen.html` whenever the + hub override is active; both are reachable from the hub itself. + +## Quick links the reviewer should bookmark + +- 🌐 Reviewer-hub landing page: + (the hub shows status badges, a "what's new" banner with the most + recent delta, the research-alignment table, and direct links into + every document below) +- 📦 Source: +- 📝 Architecture decisions (incl. the Q7 "no" prompt): [`../architecture/locked-vs-flexible.md`](../architecture/locked-vs-flexible.md) +- 📐 Schläfli derivation (for Q3): [`../math/hyperideal-hessian-derivation.md`](../math/hyperideal-hessian-derivation.md) +- 📚 Literature index (for Q1 + Q3): [`../math/references.md`](../math/references.md) +- 🗺️ Per-phase roadmap (for Q1): [`../roadmap/phases.md`](../roadmap/phases.md) +- 🧪 Research-track entries with acceptance criteria (Q1 + Q2): [`../roadmap/research-track.md`](../roadmap/research-track.md) diff --git a/doc/reviewer/agenda.md b/doc/reviewer/agenda.md new file mode 100644 index 0000000..a5a93e2 --- /dev/null +++ b/doc/reviewer/agenda.md @@ -0,0 +1,132 @@ +# Reviewer meeting — agenda (for me) + +> **Audience.** Myself, before the meeting. Not shared with the +> reviewer (they have [`briefing.md`](briefing.md) and +> [`questions.md`](questions.md) instead). +> +> **Purpose.** Keep the conversation on the high-stakes items +> without slipping into detail-tour mode. + +**Length:** ~60 min (default; willing to extend if conversation is +productive). + +## Opening — 5 min + +- **Thank-you** + ~30 s on why I value their time specifically + (active researcher in the exact line — decorated DCE, Penner + coordinates, canonical tessellations, hyperideal polyhedra — that + conformallab++ aspires to be infrastructure for; best-positioned + audience for the material AND the most likely future power user). +- **Confirm format**: they've read `briefing.md`? Yes → skip ahead. + No → take 3 min to walk through it together via the reviewer-hub + URL. + +## §1 Architectural tour — 10 min + +Goal: they leave §1 with a mental map of what's where. + +1. Open . +2. Click through: + - Doxygen → `discrete_conformal_map_euclidean` (the canonical entry, + shows the named-parameter pattern + traits). + - `locked-vs-flexible.md` § Summary table. + - `dependencies.md` § TL;DR. +3. **Don't** demo running the test suite live unless they ask — + they can verify the green badges later. + +**Cue to move on**: when they have no more "where is X?" questions. + +## §2 The seven questions — 35 min + +Goal: get clear answers to as many of the 7 questions in +[`questions.md`](questions.md) as possible. Order is chosen so the +research-relevant questions come *first* (their answer has the largest +downstream effect on the roadmap): + +1. **Q1 — research-track alignment** (which of 9d.2 / 9f / 10c / 10c′ + would unblock concrete experiments?). ~8 min. Most likely to + make them feel the project is genuinely *for* their work, not just + adjacent to it. +2. **Q2 — decorated-DCE API surface** (A/B/C named parameter vs new + solver vs property-map auto-detect). ~7 min. Sets a concrete + shape we can sketch in code right after the meeting. +3. **Q3 — Phase 9b-analytic** (Schläfli Hessian — implement now?). + ~8 min. May trigger longer math discussion; cap at 10 min and + offer to continue async via the derivation note. +4. **Q5 — GC-1 cross-validation co-authorship**. ~4 min. Read the + room: if they're warm, push; if cool, leave as "let me follow up + in writing". +5. **Q4 — Phase 9c port-literal vs re-derive** (algorithm choice). + ~4 min. Less critical than Q1–Q3 because we have a viable default + either way. +6. **Q6 — CGAL submission packaging** (one package vs several). + ~4 min. Project-management question; their answer is *advisory* + even if it's confident. + +**Trap to avoid**: don't relitigate any of the already-made decisions +(e.g. Surface_mesh as default, Eigen as backend) unless they bring it +up under "anything you'd say no to". Move forward, not sideways. +Keep Q4/Q6 *short* — they are project management; Q1–Q3 are the +research-flavoured ones their time is best spent on. + +## §3 Q7 — the "no" question — 5 min + +[`questions.md`](questions.md) §Q7: *"Is there one architecture +decision where you think 'no, that's the wrong call'?"* + +Ask explicitly. Wait. Don't fill silence. Negative feedback at this +phase is the highest-value information of the entire meeting. + +## §4 Wrap-up — 5 min + +- **Summarise**: 1 sentence per Q1–Q7 answer. +- **Next steps**: + - Items they offered to help on (if any) → I'll send a follow-up + email within 48 h. + - Items they flagged as worth changing → I'll write a one-page + plan + send for sign-off before starting work. +- **Cadence**: do they want a periodic update? Once-a-quarter? When + hitting Phase 9c milestone? +- **Citation**: ask whether they want acknowledgement in the + CGAL submission's `THANKS.md`. + +## Material I have ready (in tabs) + +1. +2. (source) +3. Local terminal in the repo (for any "show me X" requests) +4. `doc/math/hyperideal-hessian-derivation.md` open in editor + (for Q2) +5. `doc/architecture/locked-vs-flexible.md` open at "Summary table" +6. This file, [`questions.md`](questions.md), and + [`briefing.md`](briefing.md) for backstop. + +## After the meeting + +- 30-min decompression — don't take notes during the meeting beyond + bullet-tracking of Q1–Q5 answers; write up a full memo in the 30 min + AFTER. +- Memo template: + ``` + Date: ____ + Reviewer: ____ + Duration: ____ min + + Q1 answer (research-track alignment): ____ + Q2 answer (decorated-DCE API): ____ + Q3 answer (Phase 9b-analytic): ____ + Q4 answer (Phase 9c port choice): ____ + Q5 answer (GC-1 co-authorship): ____ + Q6 answer (CGAL packaging): ____ + Q7 "no" pointer: ____ + + Action items: + [ ] (within 48 h) ____ + [ ] (within 2 wks) ____ + [ ] (longer term) ____ + + Cadence agreed: ____ + ``` + +- Store the memo at `doc/reviewer/2026-XX-XX-meeting-notes.md` + (gitignored if confidential, otherwise committed). diff --git a/doc/reviewer/briefing.md b/doc/reviewer/briefing.md new file mode 100644 index 0000000..e147b06 --- /dev/null +++ b/doc/reviewer/briefing.md @@ -0,0 +1,183 @@ +# Reviewer briefing — conformallab++ + +> **Audience.** Active researcher in discrete differential geometry — +> specifically the research line around **decorated discrete conformal +> equivalence**, **Penner coordinates on hyperbolic surfaces**, +> **canonical Delaunay tessellations of decorated surfaces**, and +> **hyperideal polyhedra**. Treats the reader as a peer who is more +> likely to *use* conformallab++ as numerical infrastructure for their +> own future experiments than to merely evaluate it as a software +> artefact. +> +> **Purpose.** One page to read before the meeting: what is shipping, +> which of the reader's research questions it could already support, +> and where the gaps are that we would close together. + +## In one paragraph + +`conformallab++` is a C++17 header-only re-implementation of the Java +library [`ConformalLab`](https://github.com/varylab/conformallab) +(Sechelmann 2016, TU Berlin), built around CGAL's `Surface_mesh` and +Eigen. v0.9.0 ships five Discrete Conformal Equivalence (DCE) solvers +— Euclidean, Spherical, HyperIdeal, Circle-Packing Euclidean +(BPS 2010, face-based), Inversive-Distance (Luo 2004, vertex-based) — +plus the Newton infrastructure, layout (priority-BFS trilateration in +ℝ², S², Poincaré disk), Möbius holonomy, period matrix, cut graph, and +JSON/XML serialisation. Long-term goal: a CGAL package. + +## Where to start (one URL, 5 minutes) + +**👉 https://tmoussa.codeberg.page/ConformalLabpp/** + +The landing page is a hand-curated reviewer hub, not an auto-generated +index. It links to the Doxygen API, the key markdown documents, and +shows static quality-gate status. + +## Research alignments — where this library could be infrastructure for your work + +The current snapshot already contains, or has roadmap entries for, +the following. Most rows match an active publication line in +discrete differential geometry; a few (marked *no Java parent*) are +research-only phases the reader can shape at design stage. + +| Your research thread | What this snapshot has | Phase | +|---|---|---| +| **Decorated DCE in non-Euclidean geometries** (Bobenko–Lutz 2025, *DCG*) | five DCE solvers + traits scaffolding; non-Euclidean cone extension scoped in research-track with acceptance criteria | **9d.2** RESEARCH (planned) | +| **Canonical Delaunay tessellations of decorated hyperbolic surfaces** (Lutz 2023, *Geom. Dedicata*; Lutz 2024 PhD thesis) | cut-graph + period matrix + hyperbolic-disk layout as scaffolding; canonical-tessellation algorithm itself outlined | **10c** planned | +| **Hyperideal polyhedra rigidity** (Bowers–Bowers–Lutz 2026) | HyperIdeal functional + analytic Hessian derivation (805-line LaTeX note) | **9b-analytic** derived; **10c′** KAT planned | +| **Optimal cone placement / non-Euclidean cone metrics** (Crane et al. 2018) | Cone-singularity port via `ConesUtility` scoped; the *non-Euclidean* extension is the research delta | **9d.1** port + **9d.2** RESEARCH | +| **Polygon Laplacian on general meshes** (Alexa–Wardetzky 2011; Alexa 2020) | *no Java parent* — first phase a reviewer can shape at design stage | **9f** RESEARCH (planned) | +| **Quasi-isothermic maps** (generalising conformality where exact conformality is impossible — Lawson-correspondence parameterisation) | scoped as a 6-class port (~800 lines) from the Java original: `QuasiisothermicLayout`, `DBFSolution` (discrete Beltrami field), `SinConditionApplication`, `QuasiisothermicDelaunay`, `QuasiisothermicUtility`, `ConformalStructureUtility` | **10e** planned | +| **Higher-genus + hyperelliptic surfaces** (Bobenko–Bücking 2009 on polyhedral surfaces; period matrices with block-diagonal Z₂ structure) | port of `HyperellipticUtility` + `HyperIdealHyperellipticUtility` scoped; existing period-matrix code as scaffolding | **10b** planned | +| **Möbius centring for Poincaré-disk layouts** as a variational problem (Lorentz geometry: `E = Σ log(−⟨x,p⟩/√(−⟨x,x⟩))`) | currently we use iterative Fréchet mean in `normalise_hyperbolic()`; the principled variational alternative is scoped via the Java `MobiusCenteringFunctional` port (full gradient + Hessian) | **9d.4** planned | +| **Boundary-First / interactive flattening** (Crane et al. 2017 BFF; Bonneel et al. 2015 *Stripe Patterns on Surfaces*) | not on the roadmap as ports; documented in [`references.md`](../math/references.md) as comparison points / inspiration for future API design | — | +| **Schläfli-based variational machinery** (Rivin–Springborn 1999) | derivation done, implementation gated on your view of whether the ~6× speedup over our block-FD path matters at your mesh sizes | **9b-analytic** ready to implement | + +See [`doc/roadmap/phases.md`](../roadmap/phases.md) for the per-phase +porting plan, [`doc/roadmap/research-track.md`](../roadmap/research-track.md) +for the items beyond Java parity with explicit acceptance criteria, +and [`doc/roadmap/java-parity.md`](../roadmap/java-parity.md) for the +reverse table (every Java class → C++ destination or *do-not-port* +rationale). + +## What's new on this snapshot (since the previous publish) + +- **+6 new porting-roadmap phases** (9d cones + 9d.4 Möbius centring / + 9e circle-pattern layout / 10d Koebe circle-domain / 10e + quasi-isothermic / 10f Koebe polyhedra / 10g cyclic-symmetry + quotients) derived from a full Java-library scan. See `phases.md` + for the per-phase plan and `java-parity.md` for the reverse table. +- **+13 literature citations** integrated into the roadmap, all + Tier-1/2. Decorated-DCE / canonical-tessellation / hyperideal line: + Bobenko–Lutz 2024 IMRN; Bobenko–Lutz 2025 *DCG*; Lutz 2023 + *Geom. Dedicata*; Lutz 2024 PhD; Bowers–Bowers–Lutz 2026. Cones, + polyhedra, period matrices: Crane et al. 2018; Springborn 2019 + (hyperbolic polyhedra); Bobenko–Bücking 2009; Rivin–Springborn 1999. + Polygon Laplacians: Alexa–Wardetzky 2011; Alexa 2020. Integrable + + practical-flattening context: Springborn–Veselov 2015 (cluster + dynamics); Crane et al. 2017 (BFF); Bonneel et al. 2015 (Stripe + Patterns). +- **Phase 9f** (polygon Laplacian on non-triangular meshes) added as + RESEARCH-only — no Java parent — so you can influence its design + before it exists. +- **Phase 10e** (quasi-isothermic maps) and **Phase 9d.4** (variational + Möbius centring) newly scoped from the Java scan; both touch + research lines adjacent to decorated DCE. +- **`output_uv_map`** now covers 4 of 5 DCE solvers (Inversive-Distance + added; CP-Euclidean deferred to Phase 9c with a clear runtime error + rather than silent failure). + +## What's true about this snapshot + +| Claim | Concrete evidence | +|---|---| +| **Library is header-only and standalone** | Verification recipe in `doc/architecture/dependencies.md`: `env -i PATH=… cmake … && ctest` passes with zero quality tools installed. | +| **Tests: 259 pass, 0 skipped** | `bash scripts/check-test-counts.sh` enforces this against `doc/api/tests.md`; CI fails on drift. | +| **Doxygen: 100 % public-API coverage, 0 warnings** | `bash scripts/doxygen-coverage.sh --threshold 100` is in CI. | +| **License hygiene: 66/66 files carry MIT SPDX** | `bash scripts/quality/license-headers.sh` is in CI (strict). | +| **Build reproducibility: byte-identical between runs** | `bash scripts/quality/reproducible-build.sh` (local, ~6 min). | +| **Sanitizers (ASan + UBSan) clean on fast suite** | `bash scripts/quality/sanitizers.sh` (local, ~3 min). | +| **CGAL conventions: 6 rules, 0 violations** | `python3 scripts/quality/cgal-conventions.py` (CI required). | + +## What we want from you + +Seven concrete questions are in +[`doc/reviewer/questions.md`](questions.md) — please skim them +beforehand. They are deliberately scoped: each can be answered with +"go this way" / "no, go that way" / "either is fine". + +Ordered by reviewer-value (the first two are the ones your research +profile makes you best-positioned to answer): + +1. **Q1 — Research-track alignment** — of the three RESEARCH-track + phases (9d.2 non-Euclidean cones, 9f polygon Laplacian, 10c + canonical tessellations + 10c′ Koebe polyhedra), which would + unblock concrete experiments you have wanted to run? +2. **Q2 — Decorated-DCE API surface** — what's the minimum public + API for Penner-coordinate / decorated-DCE work? Named parameter + on the existing Euclidean entry, separate `decorated_*` solvers, + or per-edge decoration weights via property maps? +3. **Q3 — Phase 9b-analytic** — is the ~6× speedup over the current + block-FD Hessian worth ~2 weeks of implementation, at the mesh + sizes you typically work with? +4. **Q4 — Phase 9c (4g-polygon)** — port the Java implementation + literally, or re-derive from Springborn 2020 §5? +5. **Q5 — geometry-central cross-validation (GC-1)** — would you + be interested in co-authoring a Newton-vs-Ptolemy-flips comparison? +6. **Q6 — CGAL submission strategy** — one package or several? +7. **Q7 — The "no" question** — looking at our 12 architecture + decisions, is there one you would push back on? + +## What's deliberately deferred (so we can discuss with you first) + +| Item | Why deferred | +|---|---| +| Phase 9b-analytic (Schläfli-based HyperIdeal Hessian) | derivation done (805-line LaTeX doc), implementation depends on your opinion of payoff | +| Phase 9c (fundamental-polygon utility, 4g-polygon canonical form) | algorithm choice up to you | +| Cross-validation against geometry-central (GC-1) | potential paper, scope depends on your interest | +| CP-Euclidean `output_uv_map` (per-face circle packing) | needs the BPS-2010 §6 layout algorithm, ~3 days | +| `.a().b().c()` member-style named-parameter chaining | requires patching CGAL upstream; pipe-operator (`a | b | c`) shipped instead | + +These are all flagged in +[`doc/architecture/locked-vs-flexible.md`](../architecture/locked-vs-flexible.md) +§"Known limitations". + +## Architectural decisions you might want to challenge + +12 decisions classified 🔴 load-bearing / 🟡 semi-fixed / 🟢 opportunistic +in [`doc/architecture/locked-vs-flexible.md`](../architecture/locked-vs-flexible.md). +The ones most worth your time: + +- **#1 Surface_mesh as default** — 🔴 ~3 weeks to change. Are you OK + with this default, or should we wire Polyhedron_3 / OpenMesh now? +- **#6 Eigen as linear-algebra back-end** — 🔴 ~2 weeks to change. Are + the Eigen sparse solvers (SparseCholesky + SparseQR fallback) + sufficient for the mesh sizes you've seen, or should we look at + CHOLMOD / PETSc? +- **#7 Strategy C** (one Default trait per functional, not a unified + trait) — 🟡 ~1 week to refactor. CGAL convention agrees; do you? + +## How to actually run something + +```bash +git clone https://codeberg.org/TMoussa/ConformalLabpp && cd ConformalLabpp +cmake -S code -B build && cmake --build build --target conformallab_tests +ctest --test-dir build # ~2 s, 23 pure-math tests +# CGAL tests (adds Boost as a system dep): +cmake -S code -B build -DWITH_CGAL_TESTS=ON +cmake --build build --target conformallab_cgal_tests -j +ctest --test-dir build # ~3 min, 236 CGAL tests +``` + +A more end-to-end recipe lives in `scripts/try_it.sh` (also run in CI). + +## Meeting logistics + +- **Format**: video call (you suggested), ~60 min +- **Materials needed on your side**: just a browser to follow the + reviewer-hub URL. +- **Materials I'll have ready**: a screen-share-able terminal with + the repo open, my own agenda in `doc/reviewer/agenda.md`, and the + questions doc above. + +Looking forward. diff --git a/doc/reviewer/hub.html b/doc/reviewer/hub.html new file mode 100644 index 0000000..2bb7332 --- /dev/null +++ b/doc/reviewer/hub.html @@ -0,0 +1,529 @@ + + + + +conformallab++ — Reviewer Preview + + + + + + +
+ +

conformallab++ — Reviewer Preview

+ +

+A C++17 header-only implementation of discrete conformal equivalence +solvers on triangle meshes, built around CGAL's Surface_mesh +and Eigen. v0.9.0 ships five DCE solvers (Euclidean / Spherical / +HyperIdeal / CP-Euclidean / Inversive-Distance) plus the layout, +holonomy, period-matrix, cut-graph and serialisation infrastructure. +

+ +

+Audience. Active researcher in discrete differential geometry — +specifically the decorated DCE / Penner coordinates / +canonical Delaunay tessellations / hyperideal polyhedra +line — treated as a peer most likely to use conformallab++ as +numerical infrastructure for their own future experiments, not merely +to evaluate it. +

+ +
+This page is a temporary preview combining two unmerged PRs so +the integrated state is reviewable before any merge happens. Once the +PRs land on main, the publish URL serves the main-branch view. +
+ + +

TL;DR — orient yourself #

+ +

+ Tests 259/259 + Skipped 0 + Doxygen 100% + Quality + Roadmap + Lit + License + Header-only + v0.9.0 +

+ +
+Headline. 259/259 tests pass (0 skipped); 100 % Doxygen +coverage on the public API; 14 of 15 quality gates green +(1 SKIP — local CGAL-version-matrix has no tarballs); library is +verified-standalone (Eigen + CGAL + Boost, all header-only, no +runtime deps). +
+ +

Pick a time budget; each path is a sequence of anchor links.

+ +
+ +
+

⏱ 5 minutes — minimum to know what's here

+ +
+ +
+

📖 20 minutes — to prepare for the meeting

+ +
+ +
+

🔬 60+ minutes — deep dive

+ +
+ +
+ + +

1. Research alignments — where this could be infrastructure for your work #

+ +

Most rows match an active publication line in discrete differential +geometry. Rows marked no Java parent are research-only phases +the reader can shape at design stage. The Phase column links to the +per-phase entry in doc/roadmap/.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Reader's research threadWhat this snapshot hasPhase
Decorated DCE in non-Euclidean geometriesfive DCE solvers + traits scaffolding; non-Euclidean cone extension scoped in research-track with acceptance criteria9d.2 RESEARCH
Canonical Delaunay tessellations of decorated hyperbolic surfacescut-graph + period matrix + hyperbolic-disk layout as scaffolding; canonical-tessellation algorithm itself outlined10c planned
Hyperideal polyhedra rigidityHyperIdeal functional + 805-line analytic Hessian derivation note (Schläfli identity)9b-analytic derived; 10c′ KAT planned
Optimal cone placement / non-Euclidean cone metricscone-singularity port via ConesUtility scoped; non-Euclidean extension is the research delta9d.1 port + 9d.2 RESEARCH
Polygon Laplacian on general (non-triangular) meshesno Java parent — first phase the reader can influence at design stage9f RESEARCH
Quasi-isothermic maps (Lawson correspondence, ~800 lines, discrete Beltrami-field solver)scoped as a 6-class Java port: QuasiisothermicLayout, DBFSolution, SinConditionApplication, QuasiisothermicDelaunay, QuasiisothermicUtility, ConformalStructureUtility10e planned
Higher-genus + hyperelliptic surfaces (Bobenko–Bücking 2009; block-diagonal period matrices with Z₂ symmetry)port of HyperellipticUtility + HyperIdealHyperellipticUtility scoped; existing period-matrix code as scaffolding10b planned
Möbius centring as a variational problem (Lorentz energy, full gradient + Hessian)currently iterative Fréchet mean in normalise_hyperbolic(); the principled variational alternative is scoped via the Java MobiusCenteringFunctional port9d.4 planned
Boundary-First / interactive flattening (Crane et al. 2017 BFF; Bonneel et al. 2015 Stripe Patterns)not on the roadmap as ports; documented in references.md as comparison points / inspiration
Schläfli-based variational machinery (Rivin–Springborn 1999)derivation done; implementation gated on the reader's view of whether the ~6× speedup over our block-FD path matters at their mesh sizes9b-analytic ready
+ + +

2. Seven questions at a glance #

+ +

The full text of each question lives in +questions.md. +The first two are research-oriented and benefit most from this +specific reader; the rest are scoped so "A / B / either" is a +sufficient answer.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
QTopicTrack
Q1Research-track alignment — which of 6 candidate phases (9d.2 / 9f / 10c+10c′ / 10e / 10b / 9d.4) would unblock concrete experiments you want to run?research
Q2Decorated-DCE API surface — named parameter, separate decorated_* solvers, or property-map auto-detect?research
Q3Phase 9b-analytic Hessian — is the ~6× speedup worth ~2 weeks at your mesh sizes?porting
Q4Phase 9c (4g-polygon canonical form) — port literally or re-derive from Springborn 2020 §5?porting
Q5geometry-central cross-validation (GC-1) — would you co-author?collaboration
Q6CGAL submission packaging — one package or several?process
Q7The "no" question — is there one of the 12 architecture decisions you would push back on?process
+ + +

3. What's new on this snapshot #

+ +
+ +

Since the previous publish, four threads converged:

+ +
    +
  • +6 phases from a full Java-library scan + (9d cones + 9d.4 variational Möbius centring / 9e circle-pattern + layout / 10d Koebe circle-domain / 10e quasi-isothermic / 10f + Koebe polyhedra / 10g cyclic-symmetry). See + java-parity.md + for the reverse table.
  • +
  • +13 literature citations integrated + into the per-phase roadmap with explicit acceptance criteria. + Decorated-DCE / canonical-tessellation / hyperideal line: + Bobenko–Lutz 2024 IMRN; Bobenko–Lutz 2025 DCG; Lutz 2023 + Geom. Dedicata; Lutz 2024 PhD; Bowers–Bowers–Lutz 2026. + Cones, polyhedra, period matrices: Crane et al. 2018; + Springborn 2019; Bobenko–Bücking 2009; Rivin–Springborn 1999. + Polygon Laplacians: Alexa–Wardetzky 2011; Alexa 2020. Integrable + and practical-flattening context: Springborn–Veselov 2015; + Crane et al. 2017 (BFF); Bonneel et al. 2015 (Stripe Patterns). +
  • +
  • +1 RESEARCH phase with no Java + parent — Phase 9f (polygon Laplacian on non-triangular meshes, + Alexa-Wardetzky 2011 / Alexa 2020) — the first phase a reviewer + can shape at design stage.
  • +
  • output_uv_map now covers 4 of 5 DCE + solvers (Inversive-Distance added; CP-Euclidean deferred to + Phase 9c with a clear runtime error).
  • +
+ +
+ + +

4. How you'd actually use this #

+ +

The library is header-only and standalone. Two-minute build + smoke +test:

+ +
git clone https://codeberg.org/TMoussa/ConformalLabpp
+cd ConformalLabpp
+cmake -S code -B build && cmake --build build --target conformallab_tests
+ctest --test-dir build              # ~2 s · 23 pure-math tests
+
+# CGAL solvers + layouts (adds Boost headers as system dep)
+cmake -S code -B build -DWITH_CGAL_TESTS=ON
+cmake --build build --target conformallab_cgal_tests -j
+ctest --test-dir build              # ~3 min · 236 CGAL tests
+ +

One-call entry to compute a Euclidean discrete conformal map and a +UV-layout in a single shot:

+ +
#include <CGAL/Surface_mesh.h>
+#include <CGAL/Discrete_conformal_map.h>
+
+CGAL::Surface_mesh<K::Point_3> mesh = ...;        // your mesh
+auto uv = mesh.add_property_map<V_idx, K::Point_2>("uv").first;
+
+auto r = CGAL::discrete_conformal_map_euclidean(
+    mesh,
+    CGAL::parameters::output_uv_map(uv)
+                   | CGAL::parameters::gradient_tolerance(1e-12));
+// r.u_per_vertex, r.iterations, r.gradient_norm now populated.
+ +
+For adding your own functional or layout — pointer chain + +
    +
  • add-inversive-distance.md + — step-by-step recipe for a sixth DCE model, used in real for the existing 9a.2 port.
  • +
  • block-fd-hessian.md + — per-face block-FD pattern (96× over full-FD).
  • +
  • add-output-uv-map.md + — output_uv_map named parameter + pipe-operator chaining.
  • +
  • For your decorated-DCE work specifically: see + Q2 of the reviewer questions + — three candidate API shapes, picking one would let us sketch a prototype in the week after the meeting.
  • +
+ +
+ + +

5. Documents to read next #

+ +

The four document folders most useful to a researcher. Click each +folder for the per-document table.

+ +
+📋 Meeting materials — doc/reviewer/ (read first) + + + + + + + +
DocumentWhat it covers
briefing.mdOne-page orientation: research alignments (10-row table), what's new on this snapshot, the seven questions.
questions.mdFull text of Q1–Q7. Q1 lists 6 candidate phases (9d.2 / 9f / 10c+10c′ / 10e / 10b / 9d.4); Q2 covers the decorated-DCE API surface; Q3–Q4 are porting decisions; Q5–Q6 project management; Q7 the open invitation to push back.
+ +
+ +
+🗺️ Roadmap — doc/roadmap/ (where everything fits) + + + + + + + + + + + +
DocumentWhat it covers
phases.md +6 phasesPer-phase plan including the 6 new Java-scan phases (9d / 9e / 10d / 10e / 10f / 10g + 9d.4) and the 3 new research-only entries (9d.2 / 9f / 10c′).
research-track.md +2 RESEARCHItems beyond Java parity, with explicit acceptance criteria: non-Euclidean cone extensions (9d.2), polygon Laplacian on non-triangular meshes (9f), geometry-central cross-validation (GC-1).
java-parity.md full scanReverse table: every class in the Java original, where it lives in our port (or which phase will absorb it), or why it is intentionally not ported (the do-not-port list — Colt, PETSc, jReality wrappers).
porting-status.mdOperational snapshot: 25 kLoC Java breakdown, 5-DCE-model status matrix, things Java doesn't have.
+ +
+ +
+📚 Mathematics & literature — doc/math/ + + + + + + + +
DocumentWhat it covers
references.md +13 refsPer-phase literature index. 13 new citations added: decorated DCE in non-Euclidean geometries (Bobenko–Lutz 2025); canonical tessellations (Lutz 2023 / 2024); hyperideal polyhedra rigidity (Bowers–Bowers–Lutz 2026); polygon Laplacians (Alexa–Wardetzky 2011 / Alexa 2020); optimal cone placement (Crane et al. 2018); Schläfli identity (Rivin–Springborn 1999); hyperbolic polyhedra (Springborn 2019); polyhedral period matrices (Bobenko–Bücking 2009); integrable cluster dynamics (Springborn–Veselov 2015); BFF (Crane et al. 2017); stripe patterns (Bonneel et al. 2015).
hyperideal-hessian-derivation.mdFull LaTeX-formatted derivation of the analytic HyperIdeal Hessian via the Schläfli identity (805 lines, 8 sections + 2 appendices). Cited sources: Schläfli 1858, Milnor 1982, Vinberg 1993, Cho–Kim 1999, Glickenstein 2011, Rivin–Springborn 1999, Springborn 2020.
+ +
+ +
+🏗️ Architecture, dependencies & auto-generated reference + + + + + + + + + + + + + + + +
DocumentWhat it covers
locked-vs-flexible.md12 architecture decisions classified 🔴 load-bearing / 🟡 semi-fixed / 🟢 opportunistic + a "Known limitations" table. Q7 of the reviewer questions points here to push back on anything.
dependencies.mdWhat's required (Eigen + CGAL + Boost headers) vs optional; per-tool install hints; standalone-verification recipe.
code/deps/THIRD-PARTY-LICENSES.mdPer-vendored-dep SPDX with MIT-compatibility analysis (LGPL §3 vs §4 distinction for header-only consumption of CGAL).
Doxygen HTML259 pages, 0 warnings, 100 % public-API coverage (396 / 396 symbols). MathJax enabled for inline formulas.
api/headers.mdAuto-generated table of every public header with brief + symbols; regenerated on every push to main.
api/tests.mdPer-suite test breakdown (single source of truth). 259 tests, 0 skipped.
+ +
+ + +

6. Quality & tests — proof points #

+ + + + + + + + +
SuiteTestsStatus
Non-CGAL (pure-math)23PASS 0 skipped
CGAL (Surface_mesh + Phase 8b-Lite)236PASS 0 skipped
Total2590 failures
+ +
+Quality gates — 14 of 15 PASS, 1 SKIP (full breakdown) + +

Run with bash scripts/quality/run-all.sh. Four ★ gates are +required in CI on every PR; the rest are local-only and skip gracefully +on a partial dev environment.

+ + + + + + + + + + + + + + + + + + + + + + +
GateWhereResultDetail
License headers ★CIPASS66/66 files carry MIT SPDX
CGAL conventions (6 rules) ★CIPASS0/6 violations across 6 CGAL public headers
codespell ★CIPASS0 typos
shellcheck ★ (strict)CIPASS0 findings across 16 shell scripts
clang-format driftlocalPASS0 drift against .clang-format
cmake-format / cmake-lintlocalPASS0 drift, 0 lint findings
cppchecklocalPASSwarning+ severity clean
Markdown linksCIPASSinternal links resolve
Sanitizers (ASan + UBSan)localPASS23/23 tests pass under instrumentation
clang-tidylocalPASS35 headers, 0 findings
Coverage (gcov + lcov)localDEGRADED23/23 tests run instrumented; lcov-on-macOS toolchain mismatch (works on Linux CI)
Multi-compilerlocalPASSAppleClang 17 + brew LLVM 22
Reproducible buildlocalPASSbyte-identical test binaries between 2 builds
Doxygen coverageCIPASS396 / 396 public symbols (100 %)
CGAL version matrixlocalSKIPno CGAL tarballs under ~/cgal/ (graceful)
test-count consistency ★CIPASStests.md totals match ctest reality
End-to-end smoke (try_it.sh) ★CIPASSfull configure + build + ctest + Euclidean example on a bundled mesh
+ +
+ + +

7. Source & navigation #

+ + + + + + + + + + +
Repo (this snapshot)codeberg.org/TMoussa/ConformalLabpp/branch/main
Default branchcodeberg.org/TMoussa/ConformalLabpp (main)
Doxygen HTML259 pages, 0 warnings, 100 % coverage
Origin (private)git.eulernest.eu/conformallab/ConformalLabpp
+ +
+Reviewer-hub layout v6 — restructured for time-budget navigation, with +TL;DR + skim path up front and a sticky TOC. Built from +branch/main after merging PR #17 + PR #19 +onto main. Status badges are static (snapshotted at publish +time, not live); the underlying CI workflow lives at +.gitea/workflows/cpp-tests.yml and re-runs on every push. + ↑ back to top +
+ +
+ + + diff --git a/doc/reviewer/questions.md b/doc/reviewer/questions.md new file mode 100644 index 0000000..9d09c85 --- /dev/null +++ b/doc/reviewer/questions.md @@ -0,0 +1,244 @@ +# Questions for the external reviewer + +> Please skim this before the meeting. Each question is scoped so that +> "do A" / "do B" / "either is fine" is a sufficient answer; deeper +> dives are welcome but not required. + +These are the seven items that would benefit most from a second opinion. + +* **Q1–Q2** are research-track questions — your active publication line + in **decorated DCE / Penner coordinates / canonical tessellations / + hyperideal polyhedra** makes you the best-positioned reader for them. + They ask whether conformallab++ could become *infrastructure for your + own future numerical experiments*, and what the minimum API surface + would be. +* **Q3–Q4** are porting decisions in our roadmap where your view of the + underlying mathematics would change the answer. +* **Q5–Q6** are project-management questions where peer input is + valuable but not blocking. +* **Q7** is an open invitation to push back on any architecture + decision you think we got wrong. + +--- + +## Q1 — Research-track alignment: which RESEARCH or planned phase would unblock concrete experiments you want to run? + +The roadmap now contains six phases close to live research lines, +each with concrete acceptance criteria (or, for the Java-port phases, +a clear specification from the Java original). Three are RESEARCH-only +(no Java parent, no immediate user request); three are Java-port +phases whose existence is settled but whose priority is open: + +| Phase | Track | What it would add | Closest published line | +|---|---|---|---| +| **9d.2** | RESEARCH | Decorated DCE in non-Euclidean (spherical + hyperbolic) geometries; Penner coordinates as first-class DOF; automatic cone placement for non-Euclidean targets | Decorated DCE in non-Euclidean geometries (2025, *Discrete Comput. Geom.*); optimal cone singularities for Euclidean flattening (2018, *SIGGRAPH*) | +| **9f** | RESEARCH | Discrete Laplace–Beltrami on **non-triangular** polygonal meshes (virtual-node / generalised cotangent), making DCE work on quad / Voronoi tessellations without re-triangulation | Polygon Laplacians (2011 *SIGGRAPH* + 2020 *TOG*) | +| **10c + 10c′** | planned | Canonical Delaunay tessellations of decorated hyperbolic surfaces; Koebe polyhedron realisation (KAT) with rigidity-aware Newton | Canonical tessellations of decorated hyperbolic surfaces (2023, *Geom. Dedicata*); rigidity of circle / hyperideal polyhedra (2026, preprint) | +| **10e** | planned | Quasi-isothermic maps (~800 lines Lawson-correspondence) — generalises conformality to meshes where exact conformality is impossible. Six new classes including a discrete Beltrami-field solver. | Java original `QuasiisothermicUtility` line; no obvious single-paper reference in the existing literature index | +| **10b** | planned | Hyperelliptic surfaces (genus g ≥ 2 with Z₂ symmetry); period matrices with block-diagonal structure; Penner-coordinate variant via `HyperIdealHyperellipticUtility` | Bobenko–Bücking 2009 *Conformal Structures and Period Matrices of Polyhedral Surfaces* | +| **9d.4** | planned | Möbius centring of Poincaré-disk layouts as a *variational* problem (Lorentz energy with full gradient + Hessian), replacing today's iterative Fréchet-mean fallback in `normalise_hyperbolic()` | Java original `MobiusCenteringFunctional`; closest published context: decorated-DCE Möbius normalisation, also used in canonical-tessellation post-processing | + +**Question A:** which of these (if any) would unblock concrete +numerical experiments you have wanted to run but currently have no +reference implementation for? + +**Question B:** for the one(s) you would use, would you want us to +target the *next* milestone after Phase 9c, or is there a different +order that would serve your research better? + +**Question C:** is there a *seventh* line we are missing — a research +thread close to your own where you would want infrastructure that the +table above does not yet cover? + +Context: +[`research-track.md`](../roadmap/research-track.md) §"Non-Euclidean +cone extensions" and §"Polygon Laplacian"; +[`phases.md`](../roadmap/phases.md) for the per-phase porting plan; +[`java-parity.md`](../roadmap/java-parity.md) for the reverse Java +class table; the integrated literature sits in +[`math/references.md`](../math/references.md). + +--- + +## Q2 — Decorated-DCE API surface: what would you need from us to use the library for Penner-coordinate work? + +If you wanted to compute the variational energy + Newton update for +**decorated discrete conformal equivalence** (vertex `u`-DOFs plus +per-edge Penner decoration `λ_e`), the existing five-solver scaffold +gives us three plausible API directions: + +- **(A) Named parameter on the existing Euclidean entry.** + `discrete_conformal_map_euclidean(mesh, parameters::penner_decoration_map(λ))`. + Smallest change; preserves the single-entry-per-geometry pattern; + hides the decoration as "just another property map". +- **(B) Separate `decorated_*` solver headers.** + New entry `CGAL::decorated_discrete_conformal_map_{euclidean, + spherical, hyperbolic}` with its own `Default_decorated_*_traits`. + Cleaner separation of concerns; mirrors the way CP-Euclidean and + Inversive-Distance are their own headers. +- **(C) Per-edge decoration as just another property map** that the + existing solvers consume when present (zero-decoration = current + behaviour by construction). Minimal API expansion; risk of + surprising convergence behaviour when decoration is silently zero + vs explicitly zero. + +**Question:** which of A / B / C matches what you would expect from +a CGAL-style header? If "none of the above" — what would you write +in our place? + +Context: Phase 9d.2 in +[`research-track.md`](../roadmap/research-track.md); the existing +named-parameter helpers in +[`code/include/CGAL/Conformal_map/internal/parameters.h`](../../code/include/CGAL/Conformal_map/internal/parameters.h). + +--- + +## Q3 — Phase 9b-analytic Hessian: implement now or later? + +We have: + +- **Done**: per-face block-FD Hessian (96× faster than naive full-FD). +- **Derived but not implemented**: analytic Hessian via the Schläfli + identity, expected ~6× further speedup over block-FD. +- **Derivation document**: + [`doc/math/hyperideal-hessian-derivation.md`](../math/hyperideal-hessian-derivation.md) + (805 lines, all sign pitfalls covered, references Schläfli 1858, + Milnor 1982, Vinberg 1993, Cho–Kim 1999, Glickenstein 2011, + Springborn 2020, Rivin–Springborn 1999). + +**Question:** + +- At what mesh size does the ~6× become user-visible enough to + justify ~2 weeks of implementation work, given the typical sizes + in your work? +- Are you aware of subtleties in the Schläfli-based derivation our + document might be missing? + +If the answer is "implement", we'd target Phase 9b-analytic right +after the meeting. + +--- + +## Q4 — Phase 9c (4g-polygon canonical form): port-literal vs re-derive? + +The Java original has two utility classes: + +- `FundamentalPolygonUtility` (~600 lines) +- `CanonicalFormUtility` (~900 lines) + +that together compute the canonical 4g-polygon for a higher-genus +surface from its cut graph. + +We can either: + +- **(A) Port literally** (~2 weeks). Faithful, predictable, fixes + the Java algorithm in C++. Down-side: we inherit the Java code's + ad-hoc style and edge-case handling. +- **(B) Re-derive from Springborn 2020 §5** (~3 weeks). Uses our + existing `cut_graph.hpp` + holonomy infrastructure cleanly. + Down-side: longer; potential for new bugs not seen by the Java + original's test cases. + +**Question**: which route do you prefer, and is there a reference +implementation (Mathematica notebook, paper appendix, other research +codebase) we should cross-validate against? + +Context: `doc/roadmap/phases.md` §Phase 9c, `doc/roadmap/porting-status.md`. + +--- + +## Q5 — geometry-central cross-validation (GC-1): would you co-author? + +Two libraries solve the DCE problem from opposite algorithmic +directions: + +- **conformallab++**: Newton on the fixed mesh (no intrinsic flips). +- **geometry-central**: Ptolemy flips on an intrinsic triangulation. + +An automated comparison on common meshes would be: + +- A short paper (the disagreement modes are interesting in their own + right and connect to the decorated-DCE framework via canonical + tessellation invariants). +- A confidence-building tool for both libraries. +- ~3 days of plumbing (CMake-fetch geometry-central, write 5 common + test meshes, compare `u_per_vertex` to a tolerance). + +**Question:** would you be interested in co-authoring such a comparison +note (with us doing the implementation)? Or do you know someone in a +neighbouring group who would be a natural co-author? + +--- + +## Q6 — CGAL submission strategy: one package or several? + +For the long-term CGAL submission: + +- **(A) One package** "Discrete_conformal_map" — single entry header, + five solver functions, one set of named parameters. Easier for + users to find; harder to compartmentalise reviews. +- **(B) Five packages** "Discrete_*" — each DCE model is its own + CGAL package with its own concept + reference manual. More + ceremony for users; more familiar review surface for CGAL editors. + +Today the code is structured per-functional (Strategy C — see +[`locked-vs-flexible.md`](../architecture/locked-vs-flexible.md) §7). +Either submission packaging is achievable from this base. + +**Question:** what's the CGAL editor convention for related-but-distinct +algorithms — `Polygon_mesh_processing` as one example (one package, many +algorithms), vs `Triangulation_2`/`Triangulation_3`/`Periodic_*`/ +`Hyperbolic_*` as another (multiple packages for related algorithms)? + +--- + +## Q7 — The "no" question (this is the highest-value answer) + +Looking at any of the 12 architecture decisions in +[`locked-vs-flexible.md`](../architecture/locked-vs-flexible.md), is +there one where you think *"no, that's the wrong call, here's why"*? + +This is a deliberately blunt question because positive feedback is +nice but negative feedback is rarer and more valuable. The 🔴 +load-bearing decisions are the most consequential to revisit, because +waiting longer makes them more expensive: + +- **#1** `CGAL::Surface_mesh

` as default mesh +- **#3** header-only, no compiled library +- **#6** Eigen as linear-algebra back-end +- **#11** MIT license (only relevant if it conflicts with a CGAL- + submission constraint) + +"No" is the most useful answer. + +--- + +## Things you do *not* need to comment on (unless you want) + +- C++ style choices captured in `.clang-format` + `.clang-tidy`. +- Test framework choice (GTest). +- License (MIT, with vendored deps catalogued in + [`code/deps/THIRD-PARTY-LICENSES.md`](../../code/deps/THIRD-PARTY-LICENSES.md)). +- Build system (CMake ≥ 3.20, header-only consumer + optional + CLI/Viewer). +- Documentation tooling (Doxygen with auto-generated `headers.md`). + +These are conscious decisions matched to CGAL conventions and are +not load-bearing in the sense that revisiting them later is cheap. + +--- + +## After the meeting — would you collaborate? + +A separate "after the meeting" conversation, but flagged here so it +does not surprise you on the day: + +- **Acknowledgement** in any future CGAL submission / paper would be + the default, and we would ask first. +- **Co-authorship** on Q5 (the GC-1 cross-validation note) is on the + table if you said yes there. +- **Periodic update cadence** (quarterly? per-milestone?) — open + question, no expectation. +- **Pull requests** from your side are welcome and reviewable on the + Codeberg repo. We would not expect them — but we would not turn + them away either. diff --git a/doc/roadmap/java-parity.md b/doc/roadmap/java-parity.md index 28e0b30..d7beaaa 100644 --- a/doc/roadmap/java-parity.md +++ b/doc/roadmap/java-parity.md @@ -22,7 +22,8 @@ as the reference implementation for expected behaviour, edge cases, and test cas | HyperIdeal Hessian — analytic via ζ → l → β/α | ❌ *(`hasHessian()==false`)* | ⚠️ FD (Phase 4a) → block-FD (Phase 9b) | **Java has NO Hessian for HyperIdeal** (verified: `HyperIdealFunctional.java:295-298` declares `hasHessian() { return false; }`). Both C++ Hessian variants are **new research beyond Java**; analytic Schläfli-based variant is Phase 9b-analytic. | | Newton solver | ✅ | ✅ | | | SparseQR fallback for gauge modes | unknown | ✅ | New in C++ | -| Cone metrics — prescribed Θᵥ ≠ 2π | ✅ fully | ⚠️ data structure only | | +| Cone metrics — prescribed Θᵥ ≠ 2π (Euclidean) | ✅ fully | ❌ Phase 9d.1 (port) | Java Euclidean-only; `ConesUtility.java` ~200 lines | +| Cone metrics — non-Euclidean (HyperIdeal / Spherical) | ❌ *(not in Java)* | ❌ Phase 9d.2 (research) | **No Java source.** Mathematical basis: Bobenko-Lutz 2025 (decorated DCE) + Crane et al. 2018 (optimal cone placement). | | Layout / embedding — ℝ² / H² / S² | ✅ | ✅ priority-BFS all three | | | Exact hyperbolic trilateration | ✅ Möbius | ✅ Möbius + law of cosines | | | halfedge_uv — seam-aware UV (texture atlas) | ✅ | ✅ | | @@ -49,6 +50,7 @@ They are candidates for Phase 9 or Phase 10. | Java class | Description | Phase | |---|---|---| +| `ConesUtility` (~200 lines) | Prescribed cone angles Θᵥ ≠ 2π — Euclidean mode only | 9d.1 | | `CPEuclideanFunctional` | Face-based circle-packing energy (BPS 2010) | 9a.1 | | `FundamentalPolygonUtility` (698 lines) | Construction + canonicalisation of 4g-gons for genus-g | 9c | | `CanonicalFormUtility` (532 lines) | High-level wrapper for 9c — drives canonicalisation pipeline | 9c | diff --git a/doc/roadmap/phases.md b/doc/roadmap/phases.md index 7fb34d6..0f991cd 100644 --- a/doc/roadmap/phases.md +++ b/doc/roadmap/phases.md @@ -109,6 +109,8 @@ mesh type. 9b-analytic Full analytic HyperIdeal Hessian via Schläfli identity → planned, see research-track.md Mathematical source: Springborn 2020 §4 + Schläfli 1858/60 + + Rivin, Springborn 1999 "The Schläfli formula in + Einstein manifolds with boundary" (ERA-AMS 5) + Cho-Kim 1999 + Glickenstein 2011 §4 Algorithm: explicit chain rule through (bᵢ,aₑ) → ℓᵢⱼ → ζ₁₃/ζ₁₄/ζ₁₅ → αᵢⱼ/βᵢ Includes: short LaTeX correctness note in doc/math/. @@ -129,7 +131,7 @@ mesh type. layer, +1 week integration. ``` -9d — Cone metrics + sphere utilities (Java port — 2026 library scan) +9d — Cone metrics + sphere utilities (Java port + research extension) ──────────────────────────────────────────────────────────────────── ``` @@ -142,14 +144,28 @@ mesh type. - Quantization: snap cone angles to π/2, π/3, π/6 for quad / triangle / hexagonal atlas targets Java reference: unwrapper/ConesUtility.java + Mathematical reference: Troyanov 1991 + Springborn 2020 §3 -9d.2 StereographicUnwrapper + SphereUtility (Java port) +9d.2 Non-Euclidean cone extensions (RESEARCH, not in Java) + → extend ConesUtility to HyperIdeal + Spherical modes + Java source: NONE — Java ConesUtility is Euclidean-only. + Mathematical reference: + Bobenko, Lutz 2025 "Decorated Discrete Conformal Equivalence in + Non-Euclidean Geometries" (Discrete & Comput. Geom. 2025, + arXiv:2310.17529) §3 — decorated DCE framework unifying cone + singularities and cusps in hyperbolic + spherical geometry. + Crane, Soliman, Ben-Chen, Schröder 2018 "Optimal Cone Singularities + for Conformal Flattening" (ACM SIGGRAPH 2018) — L¹-optimal + automatic cone placement; directly applicable to 9d.2 algorithm. + Status: 🔲 planned + +9d.3 StereographicUnwrapper + SphereUtility (Java port) → stereographic_layout.hpp Stereographic projection S²→ℂ∪{∞} + Möbius centering for genus-0 surfaces. Converts spherical DCE output to a flat 2-D atlas. Java reference: unwrapper/StereographicUnwrapper.java (266 lines) -9d.3 MobiusCenteringFunctional (Java port, optional upgrade) +9d.4 MobiusCenteringFunctional (Java port, optional upgrade) → integrate into layout.hpp normalise_hyperbolic() Variational Möbius centering via Lorentz geometry: E = Σ log(-⟨x,p⟩/√(-⟨x,x⟩)) @@ -170,6 +186,30 @@ mesh type. - CPEuclideanRotation: rotation-invariant CP functional variant Java references: unwrapper/circlepattern/CirclePattern{Layout,Utility}.java unwrapper/circlepattern/CPEuclideanRotation.java + Mathematical reference: Bobenko-Springborn 2004 variational principle + + Bobenko-Hoffmann-Springborn 2006 "Minimal + surfaces from circle patterns" (Discrete & + Comput. Geom. 35, 2006). +``` + +9f — Polygon Laplacian (RESEARCH — no Java equivalent) +────────────────────────────────────────────────────── + +``` +9f Discrete Laplacian on general polygonal meshes + → polygon_laplacian.hpp + Java source: NONE + Mathematical reference: + Alexa, Wardetzky 2011 "Discrete Laplacians on General Polygonal + Meshes" (ACM SIGGRAPH 2011) — virtual-node construction, + polygon cotangent weights extending the Pinkall-Polthier formula. + Alexa 2020 "Discrete Laplacians on General Polygonal Meshes" + (ACM TOG 39, 2020) — extended journal treatment, error bounds. + Enables: DCE energy evaluation on quad-dominant / Voronoi / + polygon meshes without forced triangulation. + Replaces euclidean_hessian.hpp for non-triangular inputs. + Status: 🔲 planned (pure research, no Java source) + Effort: medium (~2 weeks core + tests; +1 week Newton integration). ``` --- @@ -283,6 +323,12 @@ Phase 10 Global uniformization for genus g ≥ 2 10a Discrete holomorphic and harmonic 1-forms → Integrate basis 1-forms ωᵢ along b-cycles of the cut graph. Mathematical reference: Bobenko-Springborn 2004 §6 + Mercat 2001. + Knöppel, Crane, Pinkall, Schröder 2015 "Stripe + Patterns on Surfaces" (ACM SIGGRAPH 2015) — + application of discrete holomorphic 1-forms to + direction field design; provides an independent + C++ reference implementation (geometry-central) + for cross-validating the Phase 10a computation. Java sources (partial, port-with-research): CanonicalBasisUtility.java 337 lines (homology basis) HomologyUtility.java 122 lines @@ -295,6 +341,17 @@ Phase 10 Global uniformization for genus g ≥ 2 → Ωᵢⱼ = ∫_{bⱼ} ωᵢ → Reduction to Siegel fundamental domain via Sp(2g,ℤ). Mathematical reference: Bobenko-Springborn 2004 + Gottschling 1959. + Bobenko, Bücking 2009 "Conformal Structures and + Period Matrices of Polyhedral Surfaces" — discrete + period matrix Ωᵢⱼ on polyhedral surfaces. + Bobenko, Lutz 2024 IMRN "Decorated Discrete Conformal + Maps and Convex Polyhedral Cusps" — uniformization + theorem connecting cusps ↔ hyperideal vertices + (bridges Phase 2/3 HyperIdeal geometry to 10b). + Springborn 2019 "A discrete version of Liouville's + theorem on conformal maps" (arXiv:1911.00966) — + proves uniqueness/rigidity of the discrete conformal + structure; justifies that Ω is a conformal invariant. Java partial reference: DiscreteRiemannUtility.java (186 lines). Requires: 10a. Effort: ~1 week net after 10a. @@ -318,6 +375,20 @@ Phase 10 Global uniformization for genus g ≥ 2 → Embedding as H²/Γ with Γ ⊂ PSL(2,ℝ) a Fuchsian group. Mathematical reference: Sechelmann 2016 §6 (discrete instance); Bers 1960 (continuous theory). + Lutz 2023 "Canonical Tessellations of Decorated + Hyperbolic Surfaces" (Geom. Dedicata 217, + arXiv:2206.13461) — canonical Delaunay tessellations + in Penner coordinates; unifies the decorated + framework with the fundamental domain construction. + Bobenko, Lutz 2024 IMRN (arXiv:2305.10988) — + discrete uniformization theorem for decorated + piecewise Euclidean surfaces. + Springborn, Veselov 2015 "Quasiconformal distortion + of projective transformations and discrete conformal + maps" (Int. Math. Res. Not.) — error estimates for + the discrete-to-smooth conformal approximation; + quantifies how well H²/Γ approximates the smooth + hyperbolic metric. Java reference: NONE — Java has the polygon + period matrix pieces but does not assemble them into a Fuchsian-group representation. @@ -327,6 +398,9 @@ Phase 10 Global uniformization for genus g ≥ 2 10c' Optional Java-port additions (low priority) → KoebePolyhedron.java (321 lines) — Koebe-Andreev-Thurston circle packings. Adds a fifth DCE method. + Rigidity: Bowers, Bowers, Lutz 2026 "Rigidity of circle polyhedra + and hyperideal polyhedra: the tangency case" (arXiv:2601.22903) + — theoretical uniqueness backing the KAT construction. → ElectrostaticSphereFunctional (127 lines) — sphere distribution baseline. → CirclePatternLayout / CirclePatternUtility — face-circle diff --git a/doc/roadmap/research-track.md b/doc/roadmap/research-track.md index f0e3c9f..2ec4892 100644 --- a/doc/roadmap/research-track.md +++ b/doc/roadmap/research-track.md @@ -211,6 +211,77 @@ The phase numbers match `doc/roadmap/phases.md`. --- +### Non-Euclidean cone extensions (Phase 9d.2, 🔲 planned) + +* **Mathematical sources:** + - **Bobenko, Lutz** (2025). *Decorated Discrete Conformal Equivalence in + Non-Euclidean Geometries.* Discrete & Comput. Geom. arXiv:2310.17529. + → §3: Penner-coordinate decoration unifies cone singularities (Θᵥ ≠ 2π) + and hyperideal cusps (Θᵥ = 0) in a single algebraic framework valid in + Euclidean, spherical, and hyperbolic geometry. + - **Crane, Soliman, Ben-Chen, Schröder** (2018). *Optimal Cone Singularities + for Conformal Flattening.* ACM SIGGRAPH 2018. DOI: 10.1145/3197517.3201367. + → L¹-optimal cone placement via a sparse-recovery optimisation over the + curvature deficit Kᵥ = 2π − Θᵥ; directly gives the set of cone angles + to prescribe for a near-flat conformal parametrisation. + - **Lutz** (2024). *PhD thesis, TU Berlin.* DOI: 10.14279/depositonce-20357. + → Full proofs for both non-Euclidean decorated DCE variants; single reference + covering 9d.2, 10b, and 10c. + +* **Java reference:** ❌ **none.** Java `ConesUtility.java` handles only the + Euclidean case; the non-Euclidean extension is new research. + +* **Scope:** + - Extend `cones_utility.hpp` (Phase 9d.1, Java port) to accept prescribed + cone angles in HyperIdeal and Spherical modes. + - Integrate the Bobenko-Lutz decoration into the variational framework of + `hyper_ideal_functional.hpp` and `spherical_functional.hpp`. + - Optionally: implement the Crane 2018 L¹-optimiser as a helper that + suggests cone positions automatically from the input curvature. + +* **Status:** 🔲 planned; no PR yet. +* **Effort:** medium (1–2 weeks for Euclidean→HyperIdeal/Spherical extension; + +1 week if Crane 2018 optimiser is included). +* **Acceptance criteria:** + - Prescribed Θᵥ ≠ 2π in HyperIdeal mode: Gauss-Bonnet check passes with + `2π·χ = Σ Θᵥ − Σ αᵢⱼ` for given cone angles. + - Newton convergence on a mesh with two manually placed cone singularities + (Euclidean, Spherical, HyperIdeal). + - Cross-validation: at Θᵥ = 2π for all v, output equals existing non-cone solver. + +--- + +### Polygon Laplacian (Phase 9f, 🔲 planned) + +* **Mathematical sources:** + - **Alexa, Wardetzky** (2011). *Discrete Laplacians on General Polygonal + Meshes.* ACM SIGGRAPH 2011. DOI: 10.1145/1964921.1964997. + → Virtual-node construction: each polygon face is replaced by a virtual + central node connected to all vertices; cotangent weights are computed + per sub-triangle; the resulting operator is symmetric and positive + semi-definite, mirroring Pinkall-Polthier for triangulations. + - **Alexa** (2020). *Discrete Laplacians on General Polygonal Meshes.* + ACM TOG 39(6). DOI: 10.1145/3414685.3417840. + → Extended journal version with error bounds and convergence analysis. + +* **Java reference:** ❌ **none.** + +* **Scope:** + - Implement `polygon_laplacian.hpp` following the virtual-node construction. + - Slot it into `newton_solver.hpp` as a drop-in replacement for + `euclidean_hessian.hpp` when the input mesh is non-triangular. + - No change to the energy functional — only the Hessian approximation changes. + +* **Status:** 🔲 planned; pure research, no Java reference. +* **Effort:** medium (~2 weeks core + tests; +1 week Newton integration). +* **Acceptance criteria:** + - Operator is symmetric and PSD (checked via `LDLT.info() == Success`). + - On a pure triangle mesh, output equals `euclidean_hessian.hpp` result. + - Newton convergence on a quad mesh (e.g., structured grid) with the + polygon Laplacian Hessian. + +--- + ### Genus g ≥ 2 fundamental domain (Phase 9c, 🔲 planned) * **Mathematical sources:** - **Poincaré, H.** (1882). *Théorie des groupes fuchsiens.*