Merge pull request 'reviewer-meeting prep: CI promotion + 3rd-party licenses + output_uv_map(ID) + briefing trio' (#19) from reviewer/meeting-prep into main
Some checks failed
C++ Tests / test-fast (push) Successful in 2m15s
API Docs / doc-build (push) Has been skipped
Markdown link check / check (push) Successful in 49s
Mirror to Codeberg / mirror (push) Successful in 33s
Compile-time perf bench / compile-time-matrix (push) Failing after 38s
C++ Tests / test-cgal (push) Has been cancelled
C++ Tests / quality-gates (push) Has been cancelled

This commit is contained in:
2026-05-26 09:15:37 +00:00
28 changed files with 2301 additions and 31 deletions

View File

@@ -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,

View File

@@ -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)"

View File

@@ -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" <<EOF
conformallab++ — Doxygen HTML API documentation.
conformallab++ — Doxygen HTML API documentation + reviewer hub.
Auto-generated by .gitea/workflows/doxygen-pages.yml from
commit ${GITHUB_SHA:-$(git rev-parse HEAD)} on $(date -Iseconds).
Source: https://codeberg.org/TMoussa/ConformalLabpp
Reviewer hub: doc/reviewer/hub.html (in-repo)
Doxygen index: /doxygen.html
EOF
cd "$publish_dir"

View File

@@ -0,0 +1,175 @@
name: Compile-time perf bench
# Cross-platform compile-time benchmark. Validates the predictions
# made in doc/architecture/compile-time.md against the eulernest CI
# runner (Linux + g++ on ARM64).
#
# Specifically tests whether:
# 1. FAST_TEST_BUILD=ON delivers the ~40 % wall-time reduction on
# Linux + g++ that was predicted from `-ftime-trace` profiling
# (recall: on Apple clang + Apple M1 it was net-neutral).
# 2. ccache hit rate is in the predicted 80%+ range on a warm
# rerun (where the macOS-local hit rate was 0 % due to
# Apple-clang + PCH friction).
#
# When run:
# * push to main (after PR #19 lands)
# * workflow_dispatch (manual trigger for ad-hoc verification)
#
# NOT run on every PR — this is a perf data-collection job, not a
# correctness gate. Pollutes the summary with timings but does not
# block merges.
on:
push:
branches:
- main
paths:
- "code/CMakeLists.txt"
- "code/tests/**/CMakeLists.txt"
- "code/include/**"
- ".gitea/workflows/perf-compile-time.yml"
workflow_dispatch: {}
jobs:
compile-time-matrix:
runs-on: eulernest
container:
image: git.eulernest.eu/conformallab/ci-cpp:latest
options: "--memory=2400m --memory-swap=2400m"
steps:
- uses: actions/checkout@v4
- name: Install ccache (idempotent)
run: |
which ccache >/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 "══════════════════════════════════════════════════════"

View File

@@ -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 515× 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

1
code/.gitignore vendored
View File

@@ -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.*

View File

@@ -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(
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)
)
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 <prefix>/include/conformallab/

View File

@@ -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 `<Eigen/Dense>` / `<Eigen/Sparse>` (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.

View File

@@ -34,6 +34,8 @@ class (Strategy C of the Phase 8b architecture audit).
#include "../cp_euclidean_functional.hpp"
#include "../newton_solver.hpp"
#include <stdexcept>
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;
}

View File

@@ -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<double> 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;
}

View File

@@ -29,7 +29,8 @@
// After translation and projection to the Poincaré disk their circumcircle
// equals the image of the original hyperbolic circle.
#include <Eigen/Dense>
#include <Eigen/Core> // downgraded from <Eigen/Dense>: this header only
// uses Matrix/Vector primitives, no decompositions.
#include <array>
#include <cmath>

View File

@@ -13,7 +13,8 @@
// `Kernel_d::Point_3` (test scaffolding).
#include <CGAL/Surface_mesh.h>
#include <Eigen/Dense>
#include <Eigen/Core> // downgraded from <Eigen/Dense>: this header only
// uses Matrix/Vector primitives, no decompositions.
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>

View File

@@ -7,7 +7,8 @@
// Ported from de.jreality.math.Pn / Rn and
// de.varylab.discreteconformal.uniformization.SurfaceCurveUtility (Java).
#include <Eigen/Dense>
#include <Eigen/Core> // downgraded from <Eigen/Dense>: this header only
// uses Matrix/Vector primitives, no decompositions.
#include <cmath>
#include <algorithm>
#include <array>

View File

@@ -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
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-O0 -g -UNDEBUG>
)
endif()
include(GoogleTest)
gtest_discover_tests(conformallab_tests DISCOVERY_TIMEOUT 60)

View File

@@ -116,8 +116,74 @@ target_compile_options(conformallab_cgal_tests PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-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
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-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 <CGAL/Discrete_conformal_map.h>"
# 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.
<CGAL/Surface_mesh.h>
<CGAL/Simple_cartesian.h>
<CGAL/Kernel_traits.h>
<CGAL/boost/graph/iterator.h>
<CGAL/Polygon_mesh_processing/triangulate_faces.h>
# Eigen blocks that drive the slowest template instantiations
# (SelfAdjointEigenSolver<Matrix<2,2>>, ColPivHouseholderQR<
# Matrix<complex,3,3>>, sparse Cholesky + QR fallback).
<Eigen/Dense>
<Eigen/Sparse>
<Eigen/SparseCholesky>
<Eigen/SparseQR>
# GoogleTest itself; every test includes it.
<gtest/gtest.h>
# std headers that appear in every test.
<vector>
<string>
<cmath>
<complex>
)
endif()
include(GoogleTest)
gtest_discover_tests(conformallab_cgal_tests
TEST_PREFIX "cgal."

View File

@@ -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<double>;
auto mesh = make_quad_strip();
auto uv_map = mesh.add_property_map<Vertex_index, K::Point_2>(
"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<double>;
auto mesh = make_quad_strip();
auto uv_map = mesh.add_property_map<Vertex_index, K::Point_2>(
"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

View File

@@ -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.**
---

View File

@@ -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) | **5560 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 <CGAL/Discrete_conformal_map.h>` 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<Matrix<2,2>>` (PCA in
`normalise_euclidean`)
* `Eigen::ColPivHouseholderQR<Matrix<complex,3,3>>`
(`MobiusMap::from_three`)
* `Eigen::internal::tridiagonalization_inplace<Matrix<2,2>>`
* `Eigen::HouseholderSequence<Matrix<2,2>>`
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 `<CGAL/Discrete_conformal_map.h>** — 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 57
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 `<Eigen/Dense>` 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 ~05 %). 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* 515× 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 | **5560** (±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`.

View File

@@ -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 <https://tmoussa.codeberg.page/ConformalLabpp/> (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 |

View File

@@ -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 LaplaceBeltrami 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. 95059534. 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. |

54
doc/reviewer/README.md Normal file
View File

@@ -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 <https://tmoussa.codeberg.page/ConformalLabpp/>
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: <https://tmoussa.codeberg.page/ConformalLabpp/>
(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: <https://codeberg.org/TMoussa/ConformalLabpp>
- 📝 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)

132
doc/reviewer/agenda.md Normal file
View File

@@ -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 <https://tmoussa.codeberg.page/ConformalLabpp/>.
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 Q1Q3 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; Q1Q3 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 Q1Q7 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. <https://tmoussa.codeberg.page/ConformalLabpp/>
2. <https://codeberg.org/TMoussa/ConformalLabpp> (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 Q1Q5 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).

183
doc/reviewer/briefing.md Normal file
View File

@@ -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** (BobenkoLutz 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** (BowersBowersLutz 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** (AlexaWardetzky 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** (BobenkoBü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** (RivinSpringborn 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:
BobenkoLutz 2024 IMRN; BobenkoLutz 2025 *DCG*; Lutz 2023
*Geom. Dedicata*; Lutz 2024 PhD; BowersBowersLutz 2026. Cones,
polyhedra, period matrices: Crane et al. 2018; Springborn 2019
(hyperbolic polyhedra); BobenkoBücking 2009; RivinSpringborn 1999.
Polygon Laplacians: AlexaWardetzky 2011; Alexa 2020. Integrable +
practical-flattening context: SpringbornVeselov 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.

529
doc/reviewer/hub.html Normal file
View File

@@ -0,0 +1,529 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>conformallab++ — Reviewer Preview</title>
<style>
:root {
--c-primary: #2e86c1;
--c-primary-dark: #1f618d;
--c-bg-soft: #f8fbfd;
--c-border: #d6eaf8;
--c-text: #2c3e50;
--c-muted: #7b8a8b;
--c-green-bg: #d5f5e3; --c-green-fg: #186a3b;
--c-yellow-bg: #fcf3cf; --c-yellow-fg: #7d6608;
--c-skip-bg: #ebdef0; --c-skip-fg: #6c3483;
--c-new-bg: #fdebd0; --c-new-fg: #6e2c00;
--c-research-bg: #ebf5fb; --c-research-fg: #1b4f72;
}
* { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
max-width: 1100px; margin: 0 auto; padding: 0;
color: var(--c-text); line-height: 1.55;
background: var(--c-bg-soft); }
main { padding: 2em 1.5em 5em 1.5em; background: #fff; }
/* ── Sticky TOC nav ───────────────────────────────────────────────── */
nav.toc {
position: sticky; top: 0; z-index: 10;
background: var(--c-primary); color: #fff;
padding: 0.7em 1.5em; font-size: 0.92em;
border-bottom: 3px solid var(--c-primary-dark);
display: flex; flex-wrap: wrap; gap: 0.4em 1.2em;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
nav.toc a { color: #fff; text-decoration: none; font-weight: 500; padding: 2px 6px;
border-radius: 3px; transition: background 0.15s; }
nav.toc a:hover { background: rgba(255,255,255,0.2); }
nav.toc strong { color: #fce4a6; font-weight: 700; margin-right: 0.6em; }
nav.toc .skim a { background: rgba(255,255,255,0.15); }
/* ── Headings ────────────────────────────────────────────────────── */
h1 { color: var(--c-primary-dark); border-bottom: 3px solid var(--c-primary);
padding-bottom: 0.3em; margin: 0.2em 0 0.5em 0; font-size: 1.8em; }
h2 { color: var(--c-primary-dark); margin-top: 2.5em;
padding: 0.5em 0.8em; background: var(--c-border);
border-left: 5px solid var(--c-primary); border-radius: 3px;
font-size: 1.25em; scroll-margin-top: 4em; }
h2 .anchor { color: var(--c-primary); opacity: 0.4; font-size: 0.7em;
margin-left: 0.4em; text-decoration: none; }
h2:hover .anchor { opacity: 1; }
h3 { color: var(--c-primary-dark); margin-top: 1.5em; font-size: 1.05em; }
/* ── Badges + status pills ───────────────────────────────────────── */
.badges { margin: 1em 0 1.5em 0; }
.badges img { margin: 2px 6px 2px 0; vertical-align: middle; }
.pill { display: inline-block; padding: 2px 8px; border-radius: 4px;
font-size: 0.85em; margin-right: 4px; font-weight: 600;
white-space: nowrap; }
.green { background: var(--c-green-bg); color: var(--c-green-fg); }
.yellow { background: var(--c-yellow-bg); color: var(--c-yellow-fg); }
.skip { background: var(--c-skip-bg); color: var(--c-skip-fg); }
.new { background: var(--c-new-bg); color: var(--c-new-fg); }
.research{ background: var(--c-research-bg); color: var(--c-research-fg); }
/* ── Tables ──────────────────────────────────────────────────────── */
table { border-collapse: collapse; width: 100%; margin: 1em 0;
font-size: 0.95em; }
th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid #e8e8e8;
vertical-align: top; }
th { background: #ecf0f1; font-weight: 600; color: var(--c-primary-dark); }
tr:hover { background: #fafcfe; }
/* ── Boxes ───────────────────────────────────────────────────────── */
.box { padding: 0.8em 1.1em; margin: 1.2em 0; border-radius: 4px;
border-left-width: 5px; border-left-style: solid; }
.box-tldr { background: #fff; border: 1px solid var(--c-border);
border-left-color: var(--c-primary); }
.box-result { background: #eafaf1; border-left-color: #28b463; }
.box-delta { background: #fdf2e9; border-left-color: #e67e22; }
.box-note { background: #fef9e7; border-left-color: #f1c40f; }
/* ── Code & details ──────────────────────────────────────────────── */
code { background: #f4f4f4; padding: 1px 5px; border-radius: 3px;
font-size: 0.92em; }
pre { background: #f4f4f4; padding: 0.8em 1em; border-radius: 4px;
overflow-x: auto; font-size: 0.88em; line-height: 1.45; }
details { margin: 0.8em 0; }
details summary { cursor: pointer; padding: 0.4em 0.6em;
background: #ecf0f1; border-radius: 3px;
font-weight: 600; color: var(--c-primary-dark); }
details[open] summary { border-bottom: 1px solid var(--c-border); margin-bottom: 0.5em; }
/* ── Time-budget grid ────────────────────────────────────────────── */
.time-grid { display: grid; grid-template-columns: repeat(3, 1fr);
gap: 1em; margin: 1.2em 0; }
.time-card { background: #fff; border: 1px solid var(--c-border);
border-top: 4px solid var(--c-primary); border-radius: 4px;
padding: 0.9em 1em; }
.time-card h3 { margin: 0 0 0.4em 0; font-size: 1em; color: var(--c-primary-dark); }
.time-card ul { margin: 0; padding-left: 1.2em; font-size: 0.92em; }
.time-card li { margin: 0.2em 0; }
@media (max-width: 700px) {
.time-grid { grid-template-columns: 1fr; }
}
/* ── Question table compact ──────────────────────────────────────── */
table.qtable td:first-child { width: 50px; font-weight: 700;
color: var(--c-primary-dark); }
table.qtable td:last-child { width: 110px; }
/* ── Footer ──────────────────────────────────────────────────────── */
footer { margin-top: 3em; padding-top: 1em; border-top: 1px solid #e8e8e8;
font-size: 0.88em; color: var(--c-muted); }
/* ── Small helpers ───────────────────────────────────────────────── */
.small { color: var(--c-muted); font-size: 0.9em; }
.lead { font-size: 1.05em; }
.topshift { scroll-margin-top: 4em; }
</style>
</head>
<body>
<nav class="toc">
<strong>Jump to:</strong>
<a href="#tldr">TL;DR</a>
<a href="#alignments">1. Alignments</a>
<a href="#questions">2. Questions</a>
<a href="#new">3. What's new</a>
<a href="#use">4. How to use</a>
<a href="#docs">5. Documents</a>
<a href="#proof">6. Proof</a>
<a href="#source">7. Source</a>
<span class="skim"><a href="#tldr">⏱ skim path</a></span>
</nav>
<main>
<h1 id="top">conformallab++ — Reviewer Preview</h1>
<p class="lead">
A C++17 header-only implementation of discrete conformal equivalence
solvers on triangle meshes, built around CGAL's <code>Surface_mesh</code>
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.
</p>
<p>
<b>Audience.</b> Active researcher in discrete differential geometry —
specifically the <b>decorated DCE</b> / <b>Penner coordinates</b> /
<b>canonical Delaunay tessellations</b> / <b>hyperideal polyhedra</b>
line — treated as a peer most likely to <i>use</i> conformallab++ as
numerical infrastructure for their own future experiments, not merely
to evaluate it.
</p>
<div class="box box-note">
<b>This page is a temporary preview</b> combining two unmerged PRs so
the integrated state is reviewable before any merge happens. Once the
PRs land on <code>main</code>, the publish URL serves the <code>main</code>-branch view.
</div>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="tldr">TL;DR — orient yourself <a class="anchor" href="#tldr">#</a></h2>
<p class="badges">
<img src="https://img.shields.io/badge/tests-259%20%2F%20259-brightgreen?style=flat-square" alt="Tests 259/259">
<img src="https://img.shields.io/badge/skipped-0-brightgreen?style=flat-square" alt="Skipped 0">
<img src="https://img.shields.io/badge/doxygen-100%25-brightgreen?style=flat-square" alt="Doxygen 100%">
<img src="https://img.shields.io/badge/quality%20gates-14%2F15%20PASS-brightgreen?style=flat-square" alt="Quality">
<img src="https://img.shields.io/badge/roadmap-5%20ported%20%2B%2011%20planned%20%2B%203%20research-blue?style=flat-square" alt="Roadmap">
<img src="https://img.shields.io/badge/literature-13%20Tier--1%20%26%20Tier--2-blue?style=flat-square" alt="Lit">
<img src="https://img.shields.io/badge/license-MIT-yellow?style=flat-square" alt="License">
<img src="https://img.shields.io/badge/header--only-yes-blue?style=flat-square" alt="Header-only">
<img src="https://img.shields.io/badge/version-v0.9.0-blue?style=flat-square" alt="v0.9.0">
</p>
<div class="box box-result">
<b>Headline.</b> 259/259 tests pass (0 skipped); 100&nbsp;% 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).
</div>
<p>Pick a time budget; each path is a sequence of anchor links.</p>
<div class="time-grid">
<div class="time-card">
<h3>⏱ 5 minutes — minimum to know what's here</h3>
<ul>
<li><a href="#alignments">§1 Research alignments</a> (10-row table)</li>
<li><a href="#new">§3 What's new</a> (delta since last publish)</li>
<li><a href="doxygen.html">→ Doxygen HTML index</a></li>
</ul>
</div>
<div class="time-card">
<h3>📖 20 minutes — to prepare for the meeting</h3>
<ul>
<li>5-min path above, plus:</li>
<li><a href="#questions">§2 Seven questions at a glance</a></li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/briefing.md">briefing.md</a> (1 page)</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/questions.md">questions.md</a> (the full text of Q1Q7)</li>
<li><a href="#use">§4 How to actually use this</a></li>
</ul>
</div>
<div class="time-card">
<h3>🔬 60+ minutes — deep dive</h3>
<ul>
<li>20-min path above, plus:</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/phases.md">roadmap/phases.md</a> (full per-phase plan)</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/math/hyperideal-hessian-derivation.md">hyperideal-hessian-derivation.md</a> (805-line Schläfli derivation)</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/architecture/locked-vs-flexible.md">locked-vs-flexible.md</a> (12 architecture decisions to push back on)</li>
<li><a href="#proof">§6 Quality + test proof points</a></li>
</ul>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="alignments">1. Research alignments — where this could be infrastructure for your work <a class="anchor" href="#alignments">#</a></h2>
<p>Most rows match an active publication line in discrete differential
geometry. Rows marked <i>no Java parent</i> are research-only phases
the reader can shape at design stage. The Phase column links to the
per-phase entry in <code>doc/roadmap/</code>.</p>
<table>
<thead><tr><th>Reader's research thread</th><th>What this snapshot has</th><th>Phase</th></tr></thead>
<tbody>
<tr><td>Decorated DCE in non-Euclidean geometries</td>
<td>five DCE solvers + traits scaffolding; non-Euclidean cone extension scoped in research-track with acceptance criteria</td>
<td><span class="pill research">9d.2</span> RESEARCH</td></tr>
<tr><td>Canonical Delaunay tessellations of decorated hyperbolic surfaces</td>
<td>cut-graph + period matrix + hyperbolic-disk layout as scaffolding; canonical-tessellation algorithm itself outlined</td>
<td><span class="pill new">10c</span> planned</td></tr>
<tr><td>Hyperideal polyhedra rigidity</td>
<td>HyperIdeal functional + 805-line analytic Hessian derivation note (Schläfli identity)</td>
<td><span class="pill new">9b-analytic</span> derived; <span class="pill new">10c</span> KAT planned</td></tr>
<tr><td>Optimal cone placement / non-Euclidean cone metrics</td>
<td>cone-singularity port via <code>ConesUtility</code> scoped; non-Euclidean extension is the research delta</td>
<td><span class="pill new">9d.1</span> port + <span class="pill research">9d.2</span> RESEARCH</td></tr>
<tr><td>Polygon Laplacian on general (non-triangular) meshes</td>
<td><i>no Java parent</i> — first phase the reader can influence at design stage</td>
<td><span class="pill research">9f</span> RESEARCH</td></tr>
<tr><td>Quasi-isothermic maps (Lawson correspondence, ~800 lines, discrete Beltrami-field solver)</td>
<td>scoped as a 6-class Java port: <code>QuasiisothermicLayout</code>, <code>DBFSolution</code>, <code>SinConditionApplication</code>, <code>QuasiisothermicDelaunay</code>, <code>QuasiisothermicUtility</code>, <code>ConformalStructureUtility</code></td>
<td><span class="pill new">10e</span> planned</td></tr>
<tr><td>Higher-genus + hyperelliptic surfaces (BobenkoBücking 2009; block-diagonal period matrices with Z₂ symmetry)</td>
<td>port of <code>HyperellipticUtility</code> + <code>HyperIdealHyperellipticUtility</code> scoped; existing period-matrix code as scaffolding</td>
<td><span class="pill new">10b</span> planned</td></tr>
<tr><td>Möbius centring as a variational problem (Lorentz energy, full gradient + Hessian)</td>
<td>currently iterative Fréchet mean in <code>normalise_hyperbolic()</code>; the principled variational alternative is scoped via the Java <code>MobiusCenteringFunctional</code> port</td>
<td><span class="pill new">9d.4</span> planned</td></tr>
<tr><td>Boundary-First / interactive flattening (Crane et al. 2017 BFF; Bonneel et al. 2015 <i>Stripe Patterns</i>)</td>
<td>not on the roadmap as ports; documented in <code>references.md</code> as comparison points / inspiration</td>
<td></td></tr>
<tr><td>Schläfli-based variational machinery (RivinSpringborn 1999)</td>
<td>derivation done; implementation gated on the reader's view of whether the ~6× speedup over our block-FD path matters at their mesh sizes</td>
<td><span class="pill new">9b-analytic</span> ready</td></tr>
</tbody>
</table>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="questions">2. Seven questions at a glance <a class="anchor" href="#questions">#</a></h2>
<p>The full text of each question lives in
<a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/questions.md">questions.md</a>.
The first two are research-oriented and benefit most from this
specific reader; the rest are scoped so &quot;A / B / either&quot; is a
sufficient answer.</p>
<table class="qtable">
<thead><tr><th>Q</th><th>Topic</th><th>Track</th></tr></thead>
<tbody>
<tr><td>Q1</td>
<td>Research-track alignment — which of 6 candidate phases (9d.2 / 9f / 10c+10c / 10e / 10b / 9d.4) would unblock concrete experiments you want to run?</td>
<td><span class="pill research">research</span></td></tr>
<tr><td>Q2</td>
<td>Decorated-DCE API surface — named parameter, separate <code>decorated_*</code> solvers, or property-map auto-detect?</td>
<td><span class="pill research">research</span></td></tr>
<tr><td>Q3</td>
<td>Phase 9b-analytic Hessian — is the ~6× speedup worth ~2 weeks at your mesh sizes?</td>
<td><span class="pill new">porting</span></td></tr>
<tr><td>Q4</td>
<td>Phase 9c (4g-polygon canonical form) — port literally or re-derive from Springborn 2020 §5?</td>
<td><span class="pill new">porting</span></td></tr>
<tr><td>Q5</td>
<td>geometry-central cross-validation (GC-1) — would you co-author?</td>
<td>collaboration</td></tr>
<tr><td>Q6</td>
<td>CGAL submission packaging — one package or several?</td>
<td>process</td></tr>
<tr><td>Q7</td>
<td>The &quot;no&quot; question — is there one of the 12 architecture decisions you would push back on?</td>
<td>process</td></tr>
</tbody>
</table>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="new">3. What's new on this snapshot <a class="anchor" href="#new">#</a></h2>
<div class="box box-delta">
<p><b>Since the previous publish</b>, four threads converged:</p>
<ul>
<li><span class="pill new">+6 phases</span> 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
<a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/java-parity.md">java-parity.md</a>
for the reverse table.</li>
<li><span class="pill new">+13 literature citations</span> integrated
into the per-phase roadmap with explicit acceptance criteria.
Decorated-DCE / canonical-tessellation / hyperideal line:
BobenkoLutz 2024 IMRN; BobenkoLutz 2025 <i>DCG</i>; Lutz 2023
<i>Geom. Dedicata</i>; Lutz 2024 PhD; BowersBowersLutz 2026.
Cones, polyhedra, period matrices: Crane et al. 2018;
Springborn 2019; BobenkoBücking 2009; RivinSpringborn 1999.
Polygon Laplacians: AlexaWardetzky 2011; Alexa 2020. Integrable
and practical-flattening context: SpringbornVeselov 2015;
Crane et al. 2017 (BFF); Bonneel et al. 2015 (Stripe Patterns).
</li>
<li><span class="pill research">+1 RESEARCH phase</span> 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.</li>
<li><span class="pill new">output_uv_map</span> now covers 4 of 5 DCE
solvers (Inversive-Distance added; CP-Euclidean deferred to
Phase 9c with a clear runtime error).</li>
</ul>
</div>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="use">4. How you'd actually use this <a class="anchor" href="#use">#</a></h2>
<p>The library is header-only and standalone. Two-minute build + smoke
test:</p>
<pre><code>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</code></pre>
<p>One-call entry to compute a Euclidean discrete conformal map and a
UV-layout in a single shot:</p>
<pre><code>#include &lt;CGAL/Surface_mesh.h&gt;
#include &lt;CGAL/Discrete_conformal_map.h&gt;
CGAL::Surface_mesh&lt;K::Point_3&gt; mesh = ...; // your mesh
auto uv = mesh.add_property_map&lt;V_idx, K::Point_2&gt;("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.</code></pre>
<details>
<summary>For adding your own functional or layout — pointer chain</summary>
<ul>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/tutorials/add-inversive-distance.md">add-inversive-distance.md</a>
— step-by-step recipe for a sixth DCE model, used in real for the existing 9a.2 port.</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/tutorials/block-fd-hessian.md">block-fd-hessian.md</a>
— per-face block-FD pattern (96× over full-FD).</li>
<li><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/tutorials/add-output-uv-map.md">add-output-uv-map.md</a>
<code>output_uv_map</code> named parameter + pipe-operator chaining.</li>
<li><b>For your decorated-DCE work specifically</b>: see
<a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/questions.md#q2--decorated-dce-api-surface-what-would-you-need-from-us-to-use-the-library-for-penner-coordinate-work">Q2 of the reviewer questions</a>
— three candidate API shapes, picking one would let us sketch a prototype in the week after the meeting.</li>
</ul>
</details>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="docs">5. Documents to read next <a class="anchor" href="#docs">#</a></h2>
<p>The four document folders most useful to a researcher. Click each
folder for the per-document table.</p>
<details open>
<summary>📋 Meeting materials — <code>doc/reviewer/</code> (read first)</summary>
<table>
<tr><th>Document</th><th>What it covers</th></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/briefing.md"><code>briefing.md</code></a></td>
<td>One-page orientation: research alignments (10-row table), what's new on this snapshot, the seven questions.</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/reviewer/questions.md"><code>questions.md</code></a></td>
<td>Full text of Q1Q7. Q1 lists 6 candidate phases (9d.2 / 9f / 10c+10c / 10e / 10b / 9d.4); Q2 covers the decorated-DCE API surface; Q3Q4 are porting decisions; Q5Q6 project management; Q7 the open invitation to push back.</td></tr>
</table>
</details>
<details>
<summary>🗺️ Roadmap — <code>doc/roadmap/</code> (where everything fits)</summary>
<table>
<tr><th>Document</th><th>What it covers</th></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/phases.md"><code>phases.md</code></a> <span class="pill new">+6 phases</span></td>
<td>Per-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).</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/research-track.md"><code>research-track.md</code></a> <span class="pill research">+2 RESEARCH</span></td>
<td>Items 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).</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/java-parity.md"><code>java-parity.md</code></a> <span class="pill new">full scan</span></td>
<td>Reverse 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 <i>do-not-port</i> list — Colt, PETSc, jReality wrappers).</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/roadmap/porting-status.md"><code>porting-status.md</code></a></td>
<td>Operational snapshot: 25 kLoC Java breakdown, 5-DCE-model status matrix, things Java doesn't have.</td></tr>
</table>
</details>
<details>
<summary>📚 Mathematics &amp; literature — <code>doc/math/</code></summary>
<table>
<tr><th>Document</th><th>What it covers</th></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/math/references.md"><code>references.md</code></a> <span class="pill new">+13 refs</span></td>
<td>Per-phase literature index. 13 new citations added: decorated DCE in non-Euclidean geometries (BobenkoLutz 2025); canonical tessellations (Lutz 2023 / 2024); hyperideal polyhedra rigidity (BowersBowersLutz 2026); polygon Laplacians (AlexaWardetzky 2011 / Alexa 2020); optimal cone placement (Crane et al. 2018); Schläfli identity (RivinSpringborn 1999); hyperbolic polyhedra (Springborn 2019); polyhedral period matrices (BobenkoBücking 2009); integrable cluster dynamics (SpringbornVeselov 2015); BFF (Crane et al. 2017); stripe patterns (Bonneel et al. 2015).</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/math/hyperideal-hessian-derivation.md"><code>hyperideal-hessian-derivation.md</code></a></td>
<td>Full 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, ChoKim 1999, Glickenstein 2011, RivinSpringborn 1999, Springborn 2020.</td></tr>
</table>
</details>
<details>
<summary>🏗️ Architecture, dependencies &amp; auto-generated reference</summary>
<table>
<tr><th>Document</th><th>What it covers</th></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/architecture/locked-vs-flexible.md"><code>locked-vs-flexible.md</code></a></td>
<td>12 architecture decisions classified 🔴 load-bearing / 🟡 semi-fixed / 🟢 opportunistic + a "Known limitations" table. Q7 of the reviewer questions points here to push back on anything.</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/architecture/dependencies.md"><code>dependencies.md</code></a></td>
<td>What's required (Eigen + CGAL + Boost headers) vs optional; per-tool install hints; standalone-verification recipe.</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/code/deps/THIRD-PARTY-LICENSES.md"><code>code/deps/THIRD-PARTY-LICENSES.md</code></a></td>
<td>Per-vendored-dep SPDX with MIT-compatibility analysis (LGPL §3 vs §4 distinction for header-only consumption of CGAL).</td></tr>
<tr><td><a href="doxygen.html">Doxygen HTML</a></td>
<td>259 pages, 0 warnings, 100&nbsp;% public-API coverage (396 / 396 symbols). MathJax enabled for inline formulas.</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/api/headers.md"><code>api/headers.md</code></a></td>
<td>Auto-generated table of every public header with brief + symbols; regenerated on every push to main.</td></tr>
<tr><td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/doc/api/tests.md"><code>api/tests.md</code></a></td>
<td>Per-suite test breakdown (single source of truth). 259 tests, 0 skipped.</td></tr>
</table>
</details>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="proof">6. Quality &amp; tests — proof points <a class="anchor" href="#proof">#</a></h2>
<table>
<thead><tr><th>Suite</th><th>Tests</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Non-CGAL (pure-math)</td><td>23</td><td><span class="pill green">PASS</span> 0 skipped</td></tr>
<tr><td>CGAL (Surface_mesh + Phase 8b-Lite)</td><td>236</td><td><span class="pill green">PASS</span> 0 skipped</td></tr>
<tr><th>Total</th><th>259</th><th>0 failures</th></tr>
</tbody>
</table>
<details>
<summary>Quality gates — 14 of 15 PASS, 1 SKIP (full breakdown)</summary>
<p class="small">Run with <code>bash scripts/quality/run-all.sh</code>. Four ★ gates are
required in CI on every PR; the rest are local-only and skip gracefully
on a partial dev environment.</p>
<table>
<thead><tr><th>Gate</th><th>Where</th><th>Result</th><th>Detail</th></tr></thead>
<tbody>
<tr><td>License headers ★</td><td>CI</td><td><span class="pill green">PASS</span></td><td>66/66 files carry MIT SPDX</td></tr>
<tr><td>CGAL conventions (6 rules) ★</td><td>CI</td><td><span class="pill green">PASS</span></td><td>0/6 violations across 6 CGAL public headers</td></tr>
<tr><td>codespell ★</td><td>CI</td><td><span class="pill green">PASS</span></td><td>0 typos</td></tr>
<tr><td>shellcheck ★ (strict)</td><td>CI</td><td><span class="pill green">PASS</span></td><td>0 findings across 16 shell scripts</td></tr>
<tr><td>clang-format drift</td><td>local</td><td><span class="pill green">PASS</span></td><td>0 drift against <code>.clang-format</code></td></tr>
<tr><td>cmake-format / cmake-lint</td><td>local</td><td><span class="pill green">PASS</span></td><td>0 drift, 0 lint findings</td></tr>
<tr><td>cppcheck</td><td>local</td><td><span class="pill green">PASS</span></td><td>warning+ severity clean</td></tr>
<tr><td>Markdown links</td><td>CI</td><td><span class="pill green">PASS</span></td><td>internal links resolve</td></tr>
<tr><td>Sanitizers (ASan + UBSan)</td><td>local</td><td><span class="pill green">PASS</span></td><td>23/23 tests pass under instrumentation</td></tr>
<tr><td>clang-tidy</td><td>local</td><td><span class="pill green">PASS</span></td><td>35 headers, 0 findings</td></tr>
<tr><td>Coverage (gcov + lcov)</td><td>local</td><td><span class="pill yellow">DEGRADED</span></td><td>23/23 tests run instrumented; lcov-on-macOS toolchain mismatch (works on Linux CI)</td></tr>
<tr><td>Multi-compiler</td><td>local</td><td><span class="pill green">PASS</span></td><td>AppleClang 17 + brew LLVM 22</td></tr>
<tr><td>Reproducible build</td><td>local</td><td><span class="pill green">PASS</span></td><td>byte-identical test binaries between 2 builds</td></tr>
<tr><td>Doxygen coverage</td><td>CI</td><td><span class="pill green">PASS</span></td><td>396 / 396 public symbols (100&nbsp;%)</td></tr>
<tr><td>CGAL version matrix</td><td>local</td><td><span class="pill skip">SKIP</span></td><td>no CGAL tarballs under <code>~/cgal/</code> (graceful)</td></tr>
<tr><td>test-count consistency ★</td><td>CI</td><td><span class="pill green">PASS</span></td><td><code>tests.md</code> totals match ctest reality</td></tr>
<tr><td>End-to-end smoke (try_it.sh) ★</td><td>CI</td><td><span class="pill green">PASS</span></td><td>full configure + build + ctest + Euclidean example on a bundled mesh</td></tr>
</tbody>
</table>
</details>
<!-- ════════════════════════════════════════════════════════════════ -->
<h2 id="source">7. Source &amp; navigation <a class="anchor" href="#source">#</a></h2>
<table>
<tr><td>Repo (this snapshot)</td>
<td><a href="https://codeberg.org/TMoussa/ConformalLabpp/src/branch/branch/main/">codeberg.org/TMoussa/ConformalLabpp/branch/main</a></td></tr>
<tr><td>Default branch</td>
<td><a href="https://codeberg.org/TMoussa/ConformalLabpp">codeberg.org/TMoussa/ConformalLabpp (main)</a></td></tr>
<tr><td>Doxygen HTML</td>
<td><a href="doxygen.html">259 pages, 0 warnings, 100 % coverage</a></td></tr>
<tr><td>Origin (private)</td>
<td><code>git.eulernest.eu/conformallab/ConformalLabpp</code></td></tr>
</table>
<footer>
Reviewer-hub layout v6 — restructured for time-budget navigation, with
TL;DR + skim path up front and a sticky TOC. Built from
<code>branch/main</code> after merging PR #17 + PR #19
onto <code>main</code>. Status badges are static (snapshotted at publish
time, not live); the underlying CI workflow lives at
<code>.gitea/workflows/cpp-tests.yml</code> and re-runs on every push.
&nbsp;<a href="#top">↑ back to top</a>
</footer>
</main>
</body>
</html>

244
doc/reviewer/questions.md Normal file
View File

@@ -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.
* **Q1Q2** 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.
* **Q3Q4** are porting decisions in our roadmap where your view of the
underlying mathematics would change the answer.
* **Q5Q6** 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 LaplaceBeltrami 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` | BobenkoBü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, ChoKim 1999, Glickenstein 2011,
Springborn 2020, RivinSpringborn 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<P>` 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.

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 (12 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.*