From e958afbd1963161ad85a8da116f618ca088d6cc6 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Tue, 19 May 2026 00:09:09 +0200 Subject: [PATCH 1/3] chore: translate all German text to English across code, docs, and CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unified the codebase language to English throughout. German text appeared in code comments, test file headers, CI step names, and several markdown documents. All natural-language text is now English; proper nouns (Institut für Mathematik, Technische Universität Berlin) are unchanged. Files changed: - .gitea/workflows/cpp-tests.yml — CI step names and job comments - code/include/mesh_utils.hpp — inline comment - code/tests/cgal/CMakeLists.txt — section comment block - code/tests/cgal/test_geometry_utils.cpp — full file header + all test comments - doc/math/references.md — geometry-central section - doc/math/validation.md — Section 9 (geometry-central cross-validation) - doc/roadmap/phases.md — Optional geometry-central track (GC-1/2/3) Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/cpp-tests.yml | 22 +-- code/include/mesh_utils.hpp | 2 +- code/tests/cgal/CMakeLists.txt | 8 +- code/tests/cgal/test_geometry_utils.cpp | 212 ++++++++++++------------ doc/math/references.md | 26 +-- doc/math/validation.md | 78 ++++----- doc/roadmap/phases.md | 89 +++++----- 7 files changed, 214 insertions(+), 223 deletions(-) diff --git a/.gitea/workflows/cpp-tests.yml b/.gitea/workflows/cpp-tests.yml index 4ad00f6..05db3ce 100644 --- a/.gitea/workflows/cpp-tests.yml +++ b/.gitea/workflows/cpp-tests.yml @@ -11,8 +11,8 @@ on: # ───────────────────────────────────────────────────────────────────────────── # Job 1 — test-fast -# Pure-math tests (Clausen, ImLi₂, Hyper-ideal Geometrie). -# Kein CGAL, kein Boost. Nur Eigen + GTest. Läuft auf ALLEN Branches. +# Pure-math tests (Clausen, ImLi₂, Hyper-ideal geometry). +# No CGAL, no Boost. Eigen + GTest only. Runs on ALL branches. # ───────────────────────────────────────────────────────────────────────────── jobs: test-fast: @@ -35,7 +35,7 @@ jobs: --output-on-failure --output-junit test-results.xml - - name: Zusammenfassung + - name: Summary if: always() run: | if [ -f test-results.xml ]; then @@ -48,14 +48,14 @@ jobs: # ───────────────────────────────────────────────────────────────────────────── # Job 2 — test-cgal -# Vollständige CGAL-Test-Suite (Phase 3–7, 158 Tests). -# Läuft NUR bei Pull Requests (nicht bei direkten Pushes auf dev/main). -# Startet erst nach erfolgreichem test-fast. +# Full CGAL test suite (Phase 3–7, 158 tests). +# Runs ONLY on pull requests (not on direct pushes to dev/main). +# Starts only after test-fast succeeds. # -# Verwendet -DWITH_CGAL_TESTS=ON (nicht -DWITH_CGAL=ON), damit kein -# Viewer/GLFW gebaut wird — der CI-Container hat kein wayland-scanner. +# Uses -DWITH_CGAL_TESTS=ON (not -DWITH_CGAL=ON) to avoid building +# Viewer/GLFW — the CI container has no wayland-scanner. # -# Boost (libboost-dev) ist seit Image-Rebuild bereits im Container. +# Boost (libboost-dev) is already present in the container since the image rebuild. # ───────────────────────────────────────────────────────────────────────────── test-cgal: needs: test-fast @@ -68,7 +68,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Configure (WITH_CGAL_TESTS — kein Viewer, kein wayland-scanner) + - name: Configure (WITH_CGAL_TESTS — no viewer, no wayland-scanner) run: cmake -S code -B build -DWITH_CGAL_TESTS=ON -DCMAKE_BUILD_TYPE=Release - name: Build CGAL-Tests @@ -81,7 +81,7 @@ jobs: --output-on-failure --output-junit cgal-results.xml - - name: Zusammenfassung + - name: Summary if: always() run: | if [ -f cgal-results.xml ]; then diff --git a/code/include/mesh_utils.hpp b/code/include/mesh_utils.hpp index fc4e936..bbb3b07 100644 --- a/code/include/mesh_utils.hpp +++ b/code/include/mesh_utils.hpp @@ -36,7 +36,7 @@ void simple_visualize_mesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) { viewer.data().set_mesh(V, F); viewer.launch(); } -// Zero-Copy Map für V (optional) +// Zero-copy map for V (optional) template Eigen::Map> get_vertex_map(CGAL::Surface_mesh& mesh) { diff --git a/code/tests/cgal/CMakeLists.txt b/code/tests/cgal/CMakeLists.txt index b646254..7d4b3fb 100644 --- a/code/tests/cgal/CMakeLists.txt +++ b/code/tests/cgal/CMakeLists.txt @@ -44,10 +44,10 @@ add_executable(conformallab_cgal_tests # period matrix, fundamental domain, tiling test_phase7.cpp - # ── Java-Parität: Geometrie-Utility-Tests ───────────────────────────────── - # Portiert aus CuttinUtilityTest, UnwrapUtilityTest, - # ConvergenceUtilityTests, HomologyTest (Tests 1–6). - # Test 7 (Genus-2-Homologie) als GTEST_SKIP-Stub bis Phase 8. + # ── Java parity: geometry utility tests ────────────────────────────────── + # Ported from CuttinUtilityTest, UnwrapUtilityTest, + # ConvergenceUtilityTests, HomologyTest (tests 1–6). + # Test 7 (genus-2 homology) as GTEST_SKIP stub until Phase 8. test_geometry_utils.cpp # ── Scalability smoke tests ──────────────────────────────────────────────── diff --git a/code/tests/cgal/test_geometry_utils.cpp b/code/tests/cgal/test_geometry_utils.cpp index ee4a079..1f6f279 100644 --- a/code/tests/cgal/test_geometry_utils.cpp +++ b/code/tests/cgal/test_geometry_utils.cpp @@ -1,64 +1,64 @@ // test_geometry_utils.cpp // -// Portierung der Java ConformalLab Geometrie-Utility-Tests. +// Port of the Java ConformalLab geometry utility tests. // -// Java-Quelle Java-Testmethode Status +// Java source Java test method Status // ───────────────────────────────────────────────────────────────────────────────────── -// CuttinUtilityTest.java testIsInConvexTextureFace_False PORTIERT -// CuttinUtilityTest.java testIsInConvexTextureFace_True PORTIERT -// UnwrapUtilityTest.java testGetAngleReturnsPI PORTIERT -// ConvergenceUtilityTests.java testGetTextureCircumRadius PORTIERT -// ConvergenceUtilityTests.java testGetTextureTriangleArea PORTIERT -// ConvergenceUtilityTests.java testScaleInvariantCircumCircleRadius PORTIERT -// HomologyTest.java testHomology PORTIERT -// EuclideanLayoutTest.java testDoLayout PORTIERT -// EuclideanCyclicConvergenceTest.java testEuclideanConvergence PORTIERT -// SphericalConvergenceTest.java testSphericalConvergence PORTIERT +// CuttinUtilityTest.java testIsInConvexTextureFace_False PORTED +// CuttinUtilityTest.java testIsInConvexTextureFace_True PORTED +// UnwrapUtilityTest.java testGetAngleReturnsPI PORTED +// ConvergenceUtilityTests.java testGetTextureCircumRadius PORTED +// ConvergenceUtilityTests.java testGetTextureTriangleArea PORTED +// ConvergenceUtilityTests.java testScaleInvariantCircumCircleRadius PORTED +// HomologyTest.java testHomology PORTED +// EuclideanLayoutTest.java testDoLayout PORTED +// EuclideanCyclicConvergenceTest.java testEuclideanConvergence PORTED +// SphericalConvergenceTest.java testSphericalConvergence PORTED // -// ─── Geometrische Grundlage ────────────────────────────────────────────────────────── +// ─── Geometric background ──────────────────────────────────────────────────────────── // -// Tests 1–2 Punkt-in-konvexem-Dreieck (2D UV-Raum, baryzentrische Vorzeichen-Methode) +// Tests 1–2 Point-in-convex-triangle (2D UV space, barycentric sign method) // Java: CuttingUtility.isInConvexTextureFace(pp, face, adapters) -// Hinweis: Java-Test 2 hat ein 5-elementiges T-Array mit w=0 (Punkt im -// Unendlichen), was ein Tippfehler im Original ist. Hier werden -// äquivalente, wohlgeformte Koordinaten verwendet. +// Note: Java test 2 has a 5-element T-array with w=0 (point at +// infinity), which is a typo in the original. Equivalent, well-formed +// coordinates are used here instead. // -// Test 3 Eckenwinkel für kollineare Vertices über den Kosinussatz. -// Java: UnwrapUtility.getAngle(edge, adapters) — gibt den Winkel am -// Zielknoten zurück. Für v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) ist -// der Winkel bei v1 genau π (Dreiecksungleichung entartet). +// Test 3 Corner angle for collinear vertices via the law of cosines. +// Java: UnwrapUtility.getAngle(edge, adapters) — returns the angle at +// the target vertex. For v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) the +// angle at v1 is exactly π (degenerate triangle inequality). // -// Tests 4–5 2D Umkreisradius und Dreiecksfläche. +// Tests 4–5 2D circumradius and triangle area. // Java: ConvergenceUtility.getTextureCircumCircleRadius(face) // ConvergenceUtility.getTextureTriangleArea(face) -// Formeln: Area = |det([B-A, C-A])| / 2 -// R = (a·b·c) / (4·Area) +// Formulas: Area = |det([B-A, C-A])| / 2 +// R = (a·b·c) / (4·Area) // -// Test 6 Skaleninvarianter Umkreisradius über ein Mesh. +// Test 6 Scale-invariant circumradius over a mesh. // Java: ConvergenceUtility.getMaxMeanSumScaleInvariantCircumRadius(hds) -// Gibt [max, mean, sum] von R_f / sqrt(total_texture_area) zurück. -// Invariant unter uniformer Skalierung der Texturkoordinaten (Test mit -// homogenem Gewicht w: Position = (T[0]/w, T[1]/w)). +// Returns [max, mean, sum] of R_f / sqrt(total_texture_area). +// Invariant under uniform scaling of texture coordinates (tested with +// homogeneous weight w: position = (T[0]/w, T[1]/w)). // -// Test 7 Genus-2 Homologie-Generatoren. +// Test 7 Genus-2 homology generators. // Java: HomologyTest.testHomology (brezel2.obj) -// Erwartet: getGeneratorPaths(root).size() == 4 (2g = 4 für g = 2) +// Expected: getGeneratorPaths(root).size() == 4 (2g = 4 for g = 2) // C++: compute_cut_graph(mesh).cut_edge_indices.size() == 4 // Mesh: code/data/obj/brezel2.obj (V=2622, F=5248, χ=−2, g=2) -// Pfad zur Compile-Zeit via CONFORMALLAB_DATA_DIR (CMakeLists.txt). +// Path set at compile time via CONFORMALLAB_DATA_DIR (CMakeLists.txt). // -// Tests 8–9 Layout-Kanten-Längenerhalt (tetraflat.obj). +// Tests 8–9 Layout edge-length preservation (tetraflat.obj). // Java: EuclideanLayoutTest.testDoLayout -// Nach Layout mit u=0 müssen UV-Kantenlängen == 3D-Kantenlängen (±1e-10). +// After layout with u=0, UV edge lengths must equal 3D edge lengths (±1e-10). // -// Test 10 Euklidischer Newton auf cathead.obj — Konvergenz + Winkeldefekt. -// Java: EuclideanLayoutTest.testLayout02 (130-Werte-Array für cathead.heml) -// C++: Newton ab u=0, prüft Konvergenz + Σα_v ≈ 2π für alle inneren Knoten. +// Test 10 Euclidean Newton on cathead.obj — convergence + angle deficit. +// Java: EuclideanLayoutTest.testLayout02 (130-value array for cathead.heml) +// C++: Newton from u=0, checks convergence + Σα_v ≈ 2π for all interior nodes. // -// Test 11 Sphärischer Newton auf Oktaeder — Konvergenz + Winkeldefekt. -// Java: SphericalConvergenceTest.testSphericalConvergence (Oktaeder, zufällig -// störe Radien, seed=1). C++: konstruierter regulärer Oktaeder, prüft -// Konvergenz und dass Σα_v ≈ 2π (Target für Sphäre nach prepareInvariantData). +// Test 11 Spherical Newton on octahedron — convergence + angle deficit. +// Java: SphericalConvergenceTest.testSphericalConvergence (octahedron, randomly +// perturbed radii, seed=1). C++: constructed regular octahedron, checks +// convergence and that Σα_v ≈ 2π (target for sphere after prepareInvariantData). // // ───────────────────────────────────────────────────────────────────────────────────── @@ -81,12 +81,12 @@ using namespace conformallab; // ───────────────────────────────────────────────────────────────────────────── -// Lokale Geometrie-Hilfsfunktionen -// (portiert aus Java CuttingUtility / ConvergenceUtility) +// Local geometry helper functions +// (ported from Java CuttingUtility / ConvergenceUtility) // ───────────────────────────────────────────────────────────────────────────── -/// Punkt-in-Dreieck Test (2D, baryzentrische Vorzeichenmethode). -/// Gibt true zurück wenn p strikt innerhalb oder auf dem Rand von v0-v1-v2 liegt. +/// Point-in-triangle test (2D, barycentric sign method). +/// Returns true if p lies strictly inside or on the boundary of v0-v1-v2. /// Java: CuttingUtility.isInConvexTextureFace static bool point_in_triangle_2d( Eigen::Vector2d p, @@ -103,7 +103,7 @@ static bool point_in_triangle_2d( return !(has_neg && has_pos); } -/// 2D Dreiecksfläche (halbes Kreuzprodukt). +/// 2D triangle area (half cross product). /// Java: ConvergenceUtility.getTextureTriangleArea static double triangle_area_2d( Eigen::Vector2d A, Eigen::Vector2d B, Eigen::Vector2d C) @@ -112,7 +112,7 @@ static double triangle_area_2d( - (B - A).y() * (C - A).x()) * 0.5; } -/// 2D Umkreisradius: R = (a·b·c) / (4·Area). +/// 2D circumradius: R = (a·b·c) / (4·Area). /// Java: ConvergenceUtility.getTextureCircumCircleRadius static double circumradius_2d( Eigen::Vector2d A, Eigen::Vector2d B, Eigen::Vector2d C) @@ -125,17 +125,17 @@ static double circumradius_2d( return (a * b * c) / (4.0 * area); } -/// Skaleninvarianter Umkreisradius für ein Mesh: +/// Scale-invariant circumradius for a mesh: /// scale_R_f = R_f / sqrt(total_area) -/// Gibt {max, mean, sum} über alle Flächen zurück. +/// Returns {max, mean, sum} over all faces. /// Java: ConvergenceUtility.getMaxMeanSumScaleInvariantCircumRadius /// -/// Homogene Koordinaten: Position = (x/w, y/w). +/// Homogeneous coordinates: position = (x/w, y/w). static std::array scale_invariant_circumradius_stats( const std::vector& verts, const std::vector>& faces) { - // Gesamtfläche + // Total area double total_area = 0.0; for (auto& f : faces) total_area += triangle_area_2d(verts[f[0]], verts[f[1]], verts[f[2]]); @@ -154,70 +154,70 @@ static std::array scale_invariant_circumradius_stats( } // ════════════════════════════════════════════════════════════════════════════ -// Tests 1–2 — CuttingUtility: Punkt-in-konvexem-Dreieck (2D UV-Raum) +// Tests 1–2 — CuttingUtility: point-in-convex-triangle (2D UV space) // Java: CuttinUtilityTest.testIsInConvexTextureFace_False / _True // ════════════════════════════════════════════════════════════════════════════ -// Test 1: Punkt liegt weit außerhalb — exakte Java-Koordinaten +// Test 1: point lies far outside — exact Java coordinates TEST(CuttingUtility, IsInConvexTextureFace_False) { - // Winziges Dreieck um (0.7488, 0.0629) — Java-Testkoordinaten (T[3]=1, w=1) + // Tiny triangle around (0.7488, 0.0629) — Java test coordinates (T[3]=1, w=1) Eigen::Vector2d v0(0.7488102998904661, 0.06293998610761144); Eigen::Vector2d v1(0.7487811940754379, 0.06289451051246124); Eigen::Vector2d v2(0.7487254625255592, 0.06291429499873116); - // Testpunkt weit entfernt bei (0.447, 0.000228) + // Test point far away at (0.447, 0.000228) Eigen::Vector2d pp(0.44661534423161037, 2.2808373704822393e-4); EXPECT_FALSE(point_in_triangle_2d(pp, v0, v1, v2)); } -// Test 2: Punkt liegt innerhalb -// Hinweis: Das originale Java-Array p2 hat 5 Elemente mit w=0 (Tippfehler im -// Java-Original). Hier werden äquivalente, wohlgeformte Koordinaten verwendet, -// die dasselbe geometrische Szenario abbilden. +// Test 2: point lies inside +// Note: the original Java array p2 has 5 elements with w=0 (typo in the +// Java original). Equivalent, well-formed coordinates are used here +// that represent the same geometric scenario. TEST(CuttingUtility, IsInConvexTextureFace_True) { - // Dreieck: (0,0) — (1e-8, 0) — (0, 1e-8) + // Triangle: (0,0) — (1e-8, 0) — (0, 1e-8) Eigen::Vector2d v0(0.0, 0.0); Eigen::Vector2d v1(1e-8, 0.0); Eigen::Vector2d v2(0.0, 1e-8); - // Schwerpunkt des Dreiecks — liegt immer innen + // Centroid of the triangle — always lies inside Eigen::Vector2d pp(1e-8 / 3.0, 1e-8 / 3.0); EXPECT_TRUE(point_in_triangle_2d(pp, v0, v1, v2)); } -// Zusätzlich: einfaches Einheitsdreieck für Klarheit +// Additional: simple unit triangle for clarity TEST(CuttingUtility, IsInConvexTextureFace_UnitTriangle_InAndOut) { Eigen::Vector2d v0(0.0, 0.0), v1(1.0, 0.0), v2(0.0, 1.0); EXPECT_TRUE( point_in_triangle_2d(Eigen::Vector2d(0.25, 0.25), v0, v1, v2)); EXPECT_FALSE(point_in_triangle_2d(Eigen::Vector2d(2.0, 2.0), v0, v1, v2)); - EXPECT_FALSE(point_in_triangle_2d(Eigen::Vector2d(0.6, 0.6), v0, v1, v2)); // jenseits Hypotenuse + EXPECT_FALSE(point_in_triangle_2d(Eigen::Vector2d(0.6, 0.6), v0, v1, v2)); // beyond hypotenuse } // ════════════════════════════════════════════════════════════════════════════ -// Test 3 — UnwrapUtility: Eckenwinkel = π für kollineare Vertices +// Test 3 — UnwrapUtility: corner angle = π for collinear vertices // Java: UnwrapUtilityTest.testGetAngleReturnsPI // ════════════════════════════════════════════════════════════════════════════ -// Java: v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) kollinear. -// Kante e von v2 nach v1. getAngle(e) = Winkel bei v1 = π. +// Java: v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) collinear. +// Edge e from v2 to v1. getAngle(e) = angle at v1 = π. // -// C++: Kosinussatz mit Kantenlängen a=|v0-v1|=1, b=|v1-v2|=1, c=|v0-v2|=2. +// C++: law of cosines with edge lengths a=|v0-v1|=1, b=|v1-v2|=1, c=|v0-v2|=2. // cos(γ_v1) = (a² + b² − c²) / (2ab) = (1 + 1 − 4) / 2 = −1 → γ = π TEST(UnwrapUtility, GetAngle_CollinearVertices_ReturnsPI) { const double a = 1.0; // |v0 − v1| const double b = 1.0; // |v1 − v2| - const double c = 2.0; // |v0 − v2| (= a + b, entartet) + const double c = 2.0; // |v0 − v2| (= a + b, degenerate) double cos_angle = (a*a + b*b - c*c) / (2.0 * a * b); - cos_angle = std::max(-1.0, std::min(1.0, cos_angle)); // numerisches Clamp + cos_angle = std::max(-1.0, std::min(1.0, cos_angle)); // numeric clamp double angle = std::acos(cos_angle); EXPECT_NEAR(M_PI, angle, 1e-15); } -// Gegenkontrolle: gleichseitiges Dreieck → Winkel = π/3 +// Counter-check: equilateral triangle → angle = π/3 TEST(UnwrapUtility, GetAngle_EquilateralTriangle_ReturnsPiOver3) { const double s = 1.0; @@ -227,57 +227,57 @@ TEST(UnwrapUtility, GetAngle_EquilateralTriangle_ReturnsPiOver3) } // ════════════════════════════════════════════════════════════════════════════ -// Test 4 — ConvergenceUtility: 2D Umkreisradius +// Test 4 — ConvergenceUtility: 2D circumradius // Java: ConvergenceUtilityTests.testGetTextureCircumRadius // ════════════════════════════════════════════════════════════════════════════ TEST(ConvergenceUtility, TextureCircumRadius_RightTriangle) { - // A=(0,0), B=(1,0), C=(0,1): rechtwinkliges gleichschenkliges Dreieck - // Seiten: 1, 1, √2. R = √2 / (4 · 0.5) = √2/2 + // A=(0,0), B=(1,0), C=(0,1): right isosceles triangle + // Sides: 1, 1, √2. R = √2 / (4 · 0.5) = √2/2 Eigen::Vector2d A(0.0, 0.0), B(1.0, 0.0), C(0.0, 1.0); EXPECT_NEAR(std::sqrt(2.0) / 2.0, circumradius_2d(A, B, C), 1e-10); } TEST(ConvergenceUtility, TextureCircumRadius_SmallerTriangle) { - // A=(0,0), B=(0.5,0.5), C=(0,1): Java-Variante mit B.T={0.5,0.5,0,1} - // Seiten: √0.5, √0.5, 1. Area = 0.25. R = (√0.5·√0.5·1)/(4·0.25) = 0.5 + // A=(0,0), B=(0.5,0.5), C=(0,1): Java variant with B.T={0.5,0.5,0,1} + // Sides: √0.5, √0.5, 1. Area = 0.25. R = (√0.5·√0.5·1)/(4·0.25) = 0.5 Eigen::Vector2d A(0.0, 0.0), B(0.5, 0.5), C(0.0, 1.0); EXPECT_NEAR(0.5, circumradius_2d(A, B, C), 1e-10); } // ════════════════════════════════════════════════════════════════════════════ -// Test 5 — ConvergenceUtility: 2D Dreiecksfläche +// Test 5 — ConvergenceUtility: 2D triangle area // Java: ConvergenceUtilityTests.testGetTextureTriangleArea // ════════════════════════════════════════════════════════════════════════════ TEST(ConvergenceUtility, TextureTriangleArea_RightTriangle) { - // A=(0,0), B=(1,0), C=(0,1) → Fläche = 0.5 + // A=(0,0), B=(1,0), C=(0,1) → area = 0.5 Eigen::Vector2d A(0.0, 0.0), B(1.0, 0.0), C(0.0, 1.0); EXPECT_NEAR(0.5, triangle_area_2d(A, B, C), 1e-10); } TEST(ConvergenceUtility, TextureTriangleArea_SmallerTriangle) { - // A=(0,0), B=(0.5,0.5), C=(0,1) → Fläche = 0.25 + // A=(0,0), B=(0.5,0.5), C=(0,1) → area = 0.25 Eigen::Vector2d A(0.0, 0.0), B(0.5, 0.5), C(0.0, 1.0); EXPECT_NEAR(0.25, triangle_area_2d(A, B, C), 1e-10); } // ════════════════════════════════════════════════════════════════════════════ -// Test 6 — ConvergenceUtility: Skaleninvarianter Umkreisradius +// Test 6 — ConvergenceUtility: scale-invariant circumradius // Java: ConvergenceUtilityTests.testScaleInvariantCircumCircleRadius // -// Mesh: 4 Vertices (v1..v4), 2 Flächen (f1: v1-v2-v3, f2: v1-v3-v4). -// Skaleninvariante Größe: R_f / sqrt(total_area) — invariant unter -// uniformer Skalierung (homogeneous weight w: pos = (x/w, y/w)). +// Mesh: 4 vertices (v1..v4), 2 faces (f1: v1-v2-v3, f2: v1-v3-v4). +// Scale-invariant quantity: R_f / sqrt(total_area) — invariant under +// uniform scaling (homogeneous weight w: pos = (x/w, y/w)). // ════════════════════════════════════════════════════════════════════════════ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_BaseScale) { - // Positionen bei w=1 (T[3]=1): v1=(0,0), v2=(1,0), v3=(0,1), v4=(-1,0) + // Positions at w=1 (T[3]=1): v1=(0,0), v2=(1,0), v3=(0,1), v4=(-1,0) std::vector verts = { {0.0, 0.0}, // v1 {1.0, 0.0}, // v2 @@ -287,13 +287,13 @@ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_BaseScale) // f1: v1-v2-v3, f2: v1-v3-v4 std::vector> faces = { {0, 1, 2}, {0, 2, 3} }; - // Einzelflächen-Prüfung (Java testGetTextureTriangleArea-Anforderung) + // Per-face check (Java testGetTextureTriangleArea requirement) EXPECT_NEAR(0.5, triangle_area_2d(verts[0], verts[1], verts[2]), 1e-10); EXPECT_NEAR(0.5, triangle_area_2d(verts[0], verts[2], verts[3]), 1e-10); auto [max_r, mean_r, sum_r] = scale_invariant_circumradius_stats(verts, faces); - // Erwartet: sin(π/4) = √2/2 für max und mean (beide Dreiecke identisch) + // Expected: sin(π/4) = √2/2 for max and mean (both triangles identical) EXPECT_NEAR(std::sin(M_PI / 4.0), max_r, 1e-10); EXPECT_NEAR(std::sin(M_PI / 4.0), mean_r, 1e-10); EXPECT_NEAR(2.0 * std::sin(M_PI / 4.0), sum_r, 1e-10); @@ -301,7 +301,7 @@ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_BaseScale) TEST(ConvergenceUtility, ScaleInvariantCircumRadius_HalvedByW2_SameResult) { - // Skalierung durch w=2: alle Positionen halbiert (homogene Koordinaten) + // Scaling by w=2: all positions halved (homogeneous coordinates) // pos_scaled = (T[0]/2, T[1]/2) std::vector verts = { {0.0, 0.0}, // v1/2 @@ -311,35 +311,35 @@ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_HalvedByW2_SameResult) }; std::vector> faces = { {0, 1, 2}, {0, 2, 3} }; - // Flächen sind ein Viertel der ursprünglichen (Längen halbiert → Area / 4) + // Areas are one quarter of the original (lengths halved → Area / 4) EXPECT_NEAR(0.125, triangle_area_2d(verts[0], verts[1], verts[2]), 1e-10); EXPECT_NEAR(0.125, triangle_area_2d(verts[0], verts[2], verts[3]), 1e-10); auto [max_r, mean_r, sum_r] = scale_invariant_circumradius_stats(verts, faces); - // Skaleninvariante Größe muss identisch zu w=1 sein + // Scale-invariant quantity must be identical to the w=1 case EXPECT_NEAR(std::sin(M_PI / 4.0), max_r, 1e-10); EXPECT_NEAR(std::sin(M_PI / 4.0), mean_r, 1e-10); EXPECT_NEAR(2.0 * std::sin(M_PI / 4.0), sum_r, 1e-10); } // ════════════════════════════════════════════════════════════════════════════ -// Test 7 — HomologyTest: Genus-2 Homologie-Generatoren +// Test 7 — HomologyTest: genus-2 homology generators // Java: HomologyTest.testHomology // -// Java-Test: -// CoHDS hds = TestUtility.readOBJ("brezel2.obj"); // Genus-2-Brezel-Fläche +// Java test: +// CoHDS hds = TestUtility.readOBJ("brezel2.obj"); // genus-2 pretzel surface // List> paths = getGeneratorPaths(hds.getVertex(0), weightAdapter); -// Assert.assertEquals(4, paths.size()); // 2g = 4 für g = 2 +// Assert.assertEquals(4, paths.size()); // 2g = 4 for g = 2 // -// C++-Äquivalent: +// C++ equivalent: // ConformalMesh mesh = load_mesh("code/data/obj/brezel2.obj"); // CutGraph cg = compute_cut_graph(mesh); // EXPECT_EQ(4u, cg.cut_edge_indices.size()); // 2g = 4 // EXPECT_EQ(2, cg.genus); // // Mesh: V=2622, F=5248, E=7872, χ=−2, genus=2. -// Pfad via CONFORMALLAB_DATA_DIR (CMakeLists.txt: ${CMAKE_SOURCE_DIR}/data). +// Path via CONFORMALLAB_DATA_DIR (CMakeLists.txt: ${CMAKE_SOURCE_DIR}/data). // ════════════════════════════════════════════════════════════════════════════ TEST(HomologyGenerators, Genus2_FourCutEdges) @@ -359,17 +359,17 @@ TEST(HomologyGenerators, Genus2_FourCutEdges) } // ════════════════════════════════════════════════════════════════════════════ -// Tests 8–9 — EuclideanLayoutTest: Kantenlängenerhalt auf tetraflat.obj +// Tests 8–9 — EuclideanLayoutTest: edge-length preservation on tetraflat.obj // Java: EuclideanLayoutTest.testDoLayout // -// Java-Test: -// Vector u = new SparseVector(n); // u = 0 (kein konformer Faktor) +// Java test: +// Vector u = new SparseVector(n); // u = 0 (no conformal factor) // EuclideanLayout.doLayout(hds, fun, u); // for (CoEdge e : hds.getEdges()) // assertEquals(Pn.distanceBetween(s.P, t.P), Pn.distanceBetween(s.T, t.T), 1E-11); // -// Bedeutung: Mit u=0 ist der konforme Faktor 0, also ℓ̃ = ℓ (keine Verformung). -// Das Layout muss die ursprünglichen 3D-Kantenlängen exakt reproduzieren. +// Meaning: with u=0 the conformal factor is 0, so ℓ̃ = ℓ (no deformation). +// The layout must reproduce the original 3D edge lengths exactly. // ════════════════════════════════════════════════════════════════════════════ TEST(EuclideanLayout, DoLayout_TetraFlat_EdgeLengthsPreserved) @@ -414,18 +414,18 @@ TEST(EuclideanLayout, DoLayout_TetraFlat_EdgeLengthsPreserved) } // ════════════════════════════════════════════════════════════════════════════ -// Test 10 — EuclideanCyclicConvergenceTest: Newton auf cathead.obj -// Java: EuclideanLayoutTest.testLayout02 (130-Werte-Regression auf cathead.heml) +// Test 10 — EuclideanCyclicConvergenceTest: Newton on cathead.obj +// Java: EuclideanLayoutTest.testLayout02 (130-value regression on cathead.heml) // EuclideanCyclicConvergenceTest.testEuclideanConvergence // -// Java-Test: +// Java test: // EuclideanLayout.doLayout(hdsCat, fun, uCat); // for (CoVertex v : interior vertices) // assertEquals(2*PI, calculateAngleSum(v), 1E-6); // for (CoEdge e : positiveEdges) // assertEquals(fun.getNewLength(e, u), tLength, 1E-6); // -// C++-Äquivalent: Newton converges on cathead.obj; interior angle sums ≈ 2π. +// C++ equivalent: Newton converges on cathead.obj; interior angle sums ≈ 2π. // The 130-value u-vector from the Java test is cathead-topology-specific and // depends on vertex ordering in the Java CoHDS — not portable directly. // Instead we verify the same mathematical invariant: convergence + angle sums. @@ -468,18 +468,18 @@ TEST(EuclideanLayout, CatHead_NewtonConverges_AngleSumsTwoPi) } // ════════════════════════════════════════════════════════════════════════════ -// Test 11 — SphericalConvergenceTest: Newton auf Oktaeder +// Test 11 — SphericalConvergenceTest: Newton on octahedron // Java: SphericalConvergenceTest.testSphericalConvergence // -// Java-Test: +// Java test: // FunctionalTest.createOctahedron(hds, aSet); // // randomly perturb vertex radii (seed=1) // prepareInvariantDataHyperbolicAndSpherical(functional, hds, aSet, u); // optimizer.minimize(u, opt); // for (CoVertex v) assertEquals(2*PI, sum of angles at v, 1E-8); // -// C++: regulärer Oktaeder (alle Knoten auf S², keine Störung), sphärischer Newton, -// prüft Konvergenz + Restgradienten (≡ Winkeldefekt = 0 nach Konvergenz). +// C++: regular octahedron (all vertices on S², no perturbation), spherical Newton, +// checks convergence + residual gradients (≡ angle deficit = 0 after convergence). // ════════════════════════════════════════════════════════════════════════════ TEST(SphericalLayout, SphericalTetrahedron_NewtonConverges_AngleSumsTwoPi) diff --git a/doc/math/references.md b/doc/math/references.md index bffce3e..3e0a3c5 100644 --- a/doc/math/references.md +++ b/doc/math/references.md @@ -28,22 +28,22 @@ Java reference implementation: [github.com/varylab/conformallab](https://github. ## geometry-central cross-reference *(optional comparison track)* -> Diese Referenzen beziehen sich auf eine alternative Implementierung desselben -> mathematischen Problems. Sie sind keine Voraussetzung für conformallab++, -> aber relevant für Kreuz-Validierung und mögliche algorithmische Adoptionen -> (→ GC-1/2/3 im Phasen-Roadmap, → Abschnitt 9 in `validation.md`). +> These references relate to an alternative implementation of the same +> mathematical problem. They are not prerequisites for conformallab++, +> but are relevant for cross-validation and possible algorithmic adoptions +> (→ GC-1/2/3 in the phase roadmap, → Section 9 in `validation.md`). -| Reference | Relevanz | +| Reference | Relevance | |---|---| -| **Gillespie, Springborn, Crane** — *Discrete Conformal Equivalence of Polyhedral Surfaces*, ACM SIGGRAPH 2021. DOI: [10.1145/3450626.3459763](https://doi.org/10.1145/3450626.3459763) | Implementiert in **geometry-central**. Erweitert Springborn 2020 um intrinsische Triangulierungen und Ptolemäische Flips. Löst dasselbe DCE-Problem wie conformallab++, aber mit anderem Algorithmus. | -| **Sharp, Soliman, Crane** — *Navigating Intrinsic Triangulations*, ACM SIGGRAPH 2019 | Algorithmische Grundlage für `SignpostIntrinsicTriangulation` in geometry-central — relevant für GC-2 (optionales Pre-Conditioning). | +| **Gillespie, Springborn, Crane** — *Discrete Conformal Equivalence of Polyhedral Surfaces*, ACM SIGGRAPH 2021. DOI: [10.1145/3450626.3459763](https://doi.org/10.1145/3450626.3459763) | Implemented in **geometry-central**. Extends Springborn 2020 with intrinsic triangulations and Ptolemaic flips. Solves the same DCE problem as conformallab++, but with a different algorithm. | +| **Sharp, Soliman, Crane** — *Navigating Intrinsic Triangulations*, ACM SIGGRAPH 2019 | Algorithmic basis for `SignpostIntrinsicTriangulation` in geometry-central — relevant for GC-2 (optional pre-conditioning). | -**Hinweis zu Springborn 2020:** -Das Papier *"Ideal Hyperbolic Polyhedra and Discrete Uniformization"* -(Springborn, Discrete & Computational Geometry 2020) ist **in conformallab++ -bereits implementiert** — es ist die direkte Referenz für den HyperIdeal-Geometriemodus -(`hyper_ideal_geometry.hpp`). Die geometry-central Implementierung (Gillespie 2021) -baut auf diesem Papier auf und ergänzt es um Ptolemäische Flips. +**Note on Springborn 2020:** +The paper *"Ideal Hyperbolic Polyhedra and Discrete Uniformization"* +(Springborn, Discrete & Computational Geometry 2020) is **already implemented in +conformallab++** — it is the direct reference for the HyperIdeal geometry mode +(`hyper_ideal_geometry.hpp`). The geometry-central implementation (Gillespie 2021) +builds on this paper and augments it with Ptolemaic flips. --- diff --git a/doc/math/validation.md b/doc/math/validation.md index 2f2f19c..a88fff3 100644 --- a/doc/math/validation.md +++ b/doc/math/validation.md @@ -193,66 +193,58 @@ These are the **holonomy consistency** checks implemented in `test_phase7.cpp` ## 9 — Cross-validation with geometry-central *(optional / hypothetical)* -> **Hinweis:** Dieser Abschnitt beschreibt eine mögliche externe Kreuz-Validierung, -> die keine Voraussetzung für die Korrektheit der Implementierung ist. -> Sie ist interessant, weil geometry-central denselben mathematischen Kern -> implementiert (Gillespie, Springborn, Crane — SIGGRAPH 2021, aufbauend auf -> Springborn 2020), aber mit einer anderen algorithmischen Strategie -> (Ptolemäische Flips + intrinsische Triangulierungen statt Newton auf der -> Original-Triangulierung). +> **Note:** This section describes a possible external cross-validation that is not +> a prerequisite for the correctness of the implementation. +> It is of interest because geometry-central implements the same mathematical core +> (Gillespie, Springborn, Crane — SIGGRAPH 2021, building on +> Springborn 2020), but with a different algorithmic strategy +> (Ptolemaic flips + intrinsic triangulations instead of Newton on the +> original triangulation). -### Welche Outputs sind vergleichbar? +### Which outputs are comparable? -| Output | conformallab++ | geometry-central | Vergleichbar? | +| Output | conformallab++ | geometry-central | Comparable? | |---|---|---|---| -| u-Vektor (Skalierungsparameter) | `res.x` | `u` nach Yamabe flow | ✓ nach Normalisierung | -| UV-Koordinaten | `layout.uv[v]` | konforme Parametrisierung | ✓ bis auf Möbius-Transformation | -| Gauss-Bonnet Defekt | `gauss_bonnet_sum()` | implizit via Krümmungsfluss | ✓ (analytisch identisch) | -| Anzahl Newton-Iterationen | `res.iterations` | Yamabe-Schritte | ~ (anderer Algorithmus) | -| Period-Matrix τ | `pd.tau_reduced` | **nicht vorhanden** | ✗ | -| Möbius-Holonomie | `hol.T_a, T_b` | **nicht vorhanden** | ✗ | +| u-vector (scale parameters) | `res.x` | `u` after Yamabe flow | ✓ after normalisation | +| UV coordinates | `layout.uv[v]` | conformal parameterisation | ✓ up to Möbius transformation | +| Gauss-Bonnet deficit | `gauss_bonnet_sum()` | implicit via curvature flow | ✓ (analytically identical) | +| Number of Newton iterations | `res.iterations` | Yamabe steps | ~ (different algorithm) | +| Period matrix τ | `pd.tau_reduced` | **not available** | ✗ | +| Möbius holonomy | `hol.T_a, T_b` | **not available** | ✗ | -### Normalisierungsabgleich +### Normalisation alignment -Der u-Vektor in conformallab++ hat einen Freiheitsgrad (globale additive Konstante — -Eichfreiheit nach Pin-Fixierung). geometry-central kann eine andere Konvention nutzen. -Vor dem Vergleich normalisieren: +The u-vector in conformallab++ has one degree of freedom (global additive constant — +gauge freedom after pin-fixing). geometry-central may use a different convention. +Normalise before comparing: ```cpp -// conformallab++: u zentrieren +// conformallab++: centre u double mean_u = std::accumulate(x.begin(), x.end(), 0.0) / x.size(); std::vector x_norm(x.size()); for (int i = 0; i < x.size(); ++i) x_norm[i] = x[i] - mean_u; -// Dann mit geometry-central u-Vektor (ebenfalls zentriert) vergleichen: -// max|x_norm[i] - gc_u[i]| < 1e-8 → identischer Konvergenzpunkt +// Then compare with the geometry-central u-vector (also centred): +// max|x_norm[i] - gc_u[i]| < 1e-8 → identical convergence point ``` -### Wann ist der Vergleich sinnvoll? +### When is the comparison useful? -| Zeitpunkt | Was ist möglich | +| Point in time | What is possible | |---|---| -| **Jetzt (Phase 7)** | Manueller Vergleich mit denselben `.off`/`.obj` Testnetzen | -| **Nach Phase 8** | Automatisiertes Vergleichsskript (Python oder separates C++-Binary) | -| **Phase 10 (Forschung)** | Algorithmus-Vergleich: Newton vs. Ptolemäische Flips auf schwierigen Netzen | +| **Now (Phase 7)** | Manual comparison using the same `.off`/`.obj` test meshes | +| **After Phase 8** | Automated comparison script (Python or separate C++ binary) | +| **Phase 10 (research)** | Algorithm comparison: Newton vs. Ptolemaic flips on difficult meshes | -### Voraussetzungen für einen fairen Vergleich +### Connection to the literature -1. Identische Eingabenetze (OFF/OBJ, gleiche Vertex-Orientierung) -2. Gleiche Gauss-Bonnet-Zielkrümmungen (Θᵥ = 2π für alle v, geschlossene Fläche) -3. u-Normalisierung abgeglichen (zentriert, gleiche Eichfixierung) -4. Konvergenztoleranz synchronisiert (max. Gradientnorm < 1e-8) - -### Verbindung zur Literatur - -Das Springborn 2020-Papier ("Ideal Hyperbolic Polyhedra and Discrete Uniformization") -ist **in conformallab++ bereits implementiert** — es ist die mathematische Grundlage -für den HyperIdeal-Geometriemodus (Phase 2/3). Die geometry-central Implementierung -basiert auf der Weiterentwicklung von Gillespie, Springborn & Crane (2021), die -denselben Variationsprinzip von Bobenko–Springborn 2004 verwendet, aber zusätzlich -Ptolemäische Flips einsetzt, um die Triangulierung während der Optimierung zu -verbessern — eine Idee, die in conformallab++ noch nicht implementiert ist (→ GC-2 -im Phasen-Roadmap). +The Springborn 2020 paper ("Ideal Hyperbolic Polyhedra and Discrete Uniformization") +is **already implemented in conformallab++** — it is the mathematical foundation +for the HyperIdeal geometry mode (Phase 2/3). The geometry-central implementation +is based on the extension by Gillespie, Springborn & Crane (2021), which uses the +same variational principle of Bobenko–Springborn 2004 but additionally applies +Ptolemaic flips to improve the triangulation during optimisation — an idea not yet +implemented in conformallab++ (→ GC-2 in the phase roadmap). --- diff --git a/doc/roadmap/phases.md b/doc/roadmap/phases.md index d598eca..10b87e7 100644 --- a/doc/roadmap/phases.md +++ b/doc/roadmap/phases.md @@ -97,58 +97,57 @@ Java features from `de.varylab.discreteconformal` not yet in C++: ## ◼ Optional / Hypothetical — geometry-central Cross-Comparison -> **Status: keine geplante Phase — rein explorativ.** -> Diese Punkte sind keine Voraussetzung für Phase 8–10. Sie sind -> interessant, weil geometry-central (Keenan Crane, CMU) auf denselben -> mathematischen Grundlagen wie conformallab++ aufbaut — insbesondere auf -> **Springborn 2020** und der direkten Weiterentwicklung durch +> **Status: no planned phase — purely exploratory.** +> These items are not prerequisites for Phase 8–10. They are +> of interest because geometry-central (Keenan Crane, CMU) is built on the same +> mathematical foundations as conformallab++ — in particular +> **Springborn 2020** and its direct extension by > **Gillespie, Springborn & Crane (SIGGRAPH 2021)**. -> Der entscheidende Unterschied: geometry-central löst dasselbe Problem -> (diskrete konforme Äquivalenz) mit **intrinsischen Triangulierungen + -> Ptolemäischen Flips**, während conformallab++ **Newton auf der -> Original-Triangulierung** anwendet. +> The key difference: geometry-central solves the same problem +> (discrete conformal equivalence) using **intrinsic triangulations + +> Ptolemaic flips**, while conformallab++ applies **Newton on the +> original triangulation**. ``` -GC-1 [optional, jetzt möglich] - Mathematischer Output-Vergleich - → gleiche Testnetze (cathead.obj, brezel.obj, torus_4x4.off) in - beide Bibliotheken laden - → UV-Koordinaten, u-Vektor, Residualnorm vergleichen - → Normalisierungskonventionen abgleichen (u-Mittelwert, Skalierung) - Ziel: unabhängige Kreuz-Validierung der Konvergenzpunkte. - Aufwand: kleines Python/C++ Vergleichsskript, kein Bibliotheks-Umbau. +GC-1 [optional, possible now] + Mathematical output comparison + → load the same test meshes (cathead.obj, brezel.obj, torus_4x4.off) into + both libraries + → compare UV coordinates, u-vector, residual norm + → align normalisation conventions (u mean, scaling) + Goal: independent cross-validation of convergence points. + Effort: small Python/C++ comparison script, no library restructuring. -GC-2 [optional, sinnvoll nach Phase 8] - Intrinsic Delaunay Pre-Conditioning - → Vor dem Newton-Solver: geometry-central SignpostIntrinsicTriangulation - auf die Eingabe anwenden - → Ptolemäische Flips konditionieren die Hessian-Matrix vor - → Hypothese: weniger Newton-Iterationen auf nicht-Delaunay-Eingaben - → Implementierbar als optionaler cmake-Flag: -DWITH_GC_PRECOND=ON - Abhängigkeit: geometry-central als optionale externe Abhängigkeit - (header-only Teile genügen für den Flip-Algorithmus). +GC-2 [optional, useful after Phase 8] + Intrinsic Delaunay pre-conditioning + → before the Newton solver: apply geometry-central SignpostIntrinsicTriangulation + to the input + → Ptolemaic flips pre-condition the Hessian matrix + → hypothesis: fewer Newton iterations on non-Delaunay inputs + → implementable as an optional cmake flag: -DWITH_GC_PRECOND=ON + Dependency: geometry-central as an optional external dependency + (header-only parts suffice for the flip algorithm). -GC-3 [hypothetisch, Phase 10+ Forschung] - Ptolemäische Flip-basierter Solver als alternativer Backend - → Statt Newton: Ptolemäische Flips + penultimate-step Normalisierung - (Gillespie–Springborn–Crane 2021 Algorithmus) - → Vergleich: Konvergenzradius, Robustheit auf pathologischen Netzen, - numerische Stabilität auf hohen Genus-Flächen - → Für conformallab++ interessant, weil der Newton-Ansatz auf - stark nicht-Delaunay Netzen (z.B. nach Remeshing) instabil - werden kann. - Keine Implementierung geplant — Konzeptnotiz für Phase 10-Forschung. +GC-3 [hypothetical, Phase 10+ research] + Ptolemaic flip-based solver as an alternative backend + → instead of Newton: Ptolemaic flips + penultimate-step normalisation + (Gillespie–Springborn–Crane 2021 algorithm) + → comparison: convergence radius, robustness on pathological meshes, + numerical stability on high-genus surfaces + → relevant for conformallab++ because the Newton approach can become + unstable on strongly non-Delaunay meshes (e.g. after remeshing). + No implementation planned — conceptual note for Phase 10 research. ``` -**Verbindung zur Literatur:** -Das Springborn 2020-Papier ("Ideal Hyperbolic Polyhedra and Discrete -Uniformization") ist in conformallab++ als HyperIdeal-Geometriemodus -bereits implementiert (Phase 2/3). Die Gillespie–Springborn–Crane -2021-Erweiterung — die geometry-central implementiert — ergänzt dies um -intrinsische Triangulierungen und macht den Algorithmus robust gegen -schlechte Eingangs-Triangulierungen. Beide teilen denselben -mathematischen Kern (diskrete konforme Äquivalenz, Gauss–Bonnet, -Variationsprinzip von Bobenko–Springborn 2004). +**Connection to the literature:** +The Springborn 2020 paper ("Ideal Hyperbolic Polyhedra and Discrete +Uniformization") is already implemented in conformallab++ as the HyperIdeal +geometry mode (Phase 2/3). The Gillespie–Springborn–Crane +2021 extension — implemented in geometry-central — augments this with +intrinsic triangulations and makes the algorithm robust against +poor input triangulations. Both share the same +mathematical core (discrete conformal equivalence, Gauss–Bonnet, +variational principle of Bobenko–Springborn 2004). --- From 02fb80ee3eaf20e404c603c0d9f43a42c66a83fb Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Tue, 19 May 2026 19:55:50 +0200 Subject: [PATCH 2/3] Phase 7.5: Doxygen infrastructure + Phase 8 design freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the Doxygen documentation pipeline as the bridge from Phase 7 (porting complete) to Phase 8 (CGAL package). Also captures the strategic Phase 8 decisions taken on 2026-05-19. Infrastructure ────────────── * Doxyfile — CGAL-style minimal configuration, HTML-only, INPUT=code/include + doc/, excludes deps/ and macOS Finder duplicates * code/CMakeLists — `doc` target via find_package(Doxygen QUIET); silently disabled if Doxygen is not installed * README — `cmake --build build --target doc` instructions * .gitignore — exclude doc/doxygen/ output Phase 8 strategic decisions (recorded in doc/api/cgal-package.md) ──────────────────────────────────────────────────────────────── * Submission to CGAL: pre-submission-ready, 12+ months horizon, MIT preserved * Mesh-type flexibility: generic FaceGraph + HalfedgeGraph * Parameter style: CGAL Named Parameters * Default kernel: Simple_cartesian (status quo) * Architecture: 3-layer wrapper, no algorithm duplication * Acceptance test: Phase 9a (Inversive-Distance) as first new client CLAUDE.md updated with a compact Phase 8 decision table. Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 4 + CLAUDE.md | 18 +- Doxyfile | 126 ++++++++++++++ README.md | 4 + code/CMakeLists.txt | 22 +++ doc/api/cgal-package.md | 363 +++++++++++++++++++++++++++++----------- 6 files changed, 434 insertions(+), 103 deletions(-) create mode 100644 Doxyfile diff --git a/.gitignore b/.gitignore index ed6e921..5379152 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ Testing/ # Claude Code worktrees .claude/ + +# Doxygen output +doc/doxygen/ +*.dox.tmp diff --git a/CLAUDE.md b/CLAUDE.md index ce48d9b..53a6bb6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -249,7 +249,23 @@ Expected results: **36 non-CGAL tests pass**, **176 CGAL tests pass, 0 skipped** ## Release state Current release: **v0.7.0** (tag on `origin/dev`, PR to `main` open). -Phase 7 is complete. Phase 8 (CGAL package) is next. +Phase 7 is complete. Phase 7.5 (Doxygen) and Phase 8 (CGAL package) are next. + +## Phase 8 strategic decisions (2026-05-19) + +The CGAL-package architecture was frozen on 2026-05-19 after the end-of-Phase-7 +docstring audit. Full design: [`doc/api/cgal-package.md`](doc/api/cgal-package.md). +Key decisions: + +| Decision | Choice | +|---|---| +| Submission to upstream CGAL | **Pre-submission-ready, not bound.** 12+ months horizon. | +| License | **MIT preserved** (no LGPL switch). | +| Mesh-type flexibility | **Generic `FaceGraph + HalfedgeGraph`** — Surface_mesh, Polyhedron_3, OpenMesh-adapter, pmp. | +| Parameter style | **Named Parameters** (`CGAL::parameters::...`). | +| Default kernel | **`Simple_cartesian`** (status quo). | +| Backward compatibility | **Dual-layer wrapper** — `code/include/*.hpp` stays as implementation, `include/CGAL/*.h` is thin wrapper. No algorithm duplication. | +| Phase-8a acceptance test | **Phase 9a (Inversive-Distance)** as the first new client of the new traits API. | Root-level files added at v0.7.0: - `CITATION.cff` — machine-readable citation (Sechelmann 2016, Springborn 2020, Bobenko–Springborn 2004) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..bab9d2a --- /dev/null +++ b/Doxyfile @@ -0,0 +1,126 @@ +# Doxyfile for conformallab++ +# +# Phase 7.5 — minimal CGAL-style Doxygen configuration. +# Only non-default values are set; Doxygen ≥ 1.9.5 supplies the rest. +# +# Usage: +# doxygen Doxyfile # generates HTML into doc/doxygen/html/ +# open doc/doxygen/html/index.html +# +# Or via CMake: +# cmake --build build --target doc + +# ── Project identity ───────────────────────────────────────────────────────── +PROJECT_NAME = "conformallab++" +PROJECT_NUMBER = 0.7.0 +PROJECT_BRIEF = "Discrete conformal maps on triangle meshes — C++17 reimplementation of ConformalLab (TU Berlin)" +PROJECT_LOGO = +OUTPUT_DIRECTORY = doc/doxygen +USE_MDFILE_AS_MAINPAGE = README.md + +# ── Input ──────────────────────────────────────────────────────────────────── +INPUT = README.md \ + CLAUDE.md \ + code/include \ + doc/api \ + doc/architecture \ + doc/math +FILE_PATTERNS = *.hpp *.h *.cpp *.md +RECURSIVE = YES +EXCLUDE_PATTERNS = */build*/* \ + */deps/* \ + */.git/* \ + */test-reports/* \ + */* 2.hpp +EXCLUDE_SYMBOLS = Eigen::* boost::* std::* + +# ── Source browsing ────────────────────────────────────────────────────────── +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES + +# ── Build options ──────────────────────────────────────────────────────────── +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = YES +GROUP_NESTED_COMPOUNDS = YES +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +EXTENSION_MAPPING = h=C++ hpp=C++ + +# ── Warnings ───────────────────────────────────────────────────────────────── +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_IF_INCOMPLETE_DOC = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = doc/doxygen/doxygen-warnings.log + +# ── HTML output ────────────────────────────────────────────────────────────── +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_COLORSTYLE = LIGHT +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_SECTIONS = YES +GENERATE_TREEVIEW = YES +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 1 +TREEVIEW_WIDTH = 280 +EXT_LINKS_IN_WINDOW = NO +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO + +# ── Disabled outputs (we only want HTML) ───────────────────────────────────── +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO +GENERATE_DOCBOOK = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO + +# ── Preprocessor ───────────────────────────────────────────────────────────── +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = code/include +PREDEFINED = CGAL_DISABLE_GMP \ + CGAL_DISABLE_MPFR \ + DOXYGEN_RUNNING + +# ── Diagrams ───────────────────────────────────────────────────────────────── +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = YES +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO + +# ── Aliases (CGAL-style) ───────────────────────────────────────────────────── +ALIASES += "concept{1}=\xrefitem concept \"Concept\" \"Concepts\" \1" +ALIASES += "models{1}=\xrefitem models \"Models\" \"Models\" \1" +ALIASES += "cgalRequires{1}=\par Requirements: \n\1" +ALIASES += "cgalParam{2}=\param \1 \2" diff --git a/README.md b/README.md index 20f12f8..183f0ab 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ ctest --test-dir build -R "^cgal\." --output-on-failure # Full build with CLI + viewer (requires Wayland/X11 dev headers) cmake -S code -B build -DWITH_CGAL=ON && cmake --build build -j$(nproc) ./bin/conformallab_core -i input.off -g euclidean -o layout.off -j result.json + +# API documentation (requires doxygen: brew/apt install doxygen) +cmake --build build --target doc +open doc/doxygen/html/index.html ``` --- diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 98be725..88278d7 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -140,3 +140,25 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/../CITATION.cff DESTINATION ${CMAKE_INSTALL_DATADIR}/conformallab) + +# ── Doxygen documentation target (Phase 7.5) ────────────────────────────────── +# Generates HTML API documentation into doc/doxygen/html/. +# Usage: +# cmake --build build --target doc +# open doc/doxygen/html/index.html +# +# Optional dependency: install Doxygen via `brew install doxygen` (macOS) or +# `apt install doxygen graphviz` (Linux). The target is silently disabled +# if Doxygen is not found. +find_package(Doxygen QUIET) +if(DOXYGEN_FOUND) + set(DOXYGEN_PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..) + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_PROJECT_ROOT}/Doxyfile + WORKING_DIRECTORY ${DOXYGEN_PROJECT_ROOT} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) + message(STATUS "Doxygen found: target 'doc' available (cmake --build build --target doc)") +else() + message(STATUS "Doxygen not found — 'doc' target unavailable (install: brew/apt install doxygen)") +endif() diff --git a/doc/api/cgal-package.md b/doc/api/cgal-package.md index e10a471..2e3bc4d 100644 --- a/doc/api/cgal-package.md +++ b/doc/api/cgal-package.md @@ -1,77 +1,211 @@ # Phase 8 — CGAL Package Design -> **Status: planned.** This document describes the target architecture for Phase 8. -> No code has been written yet. The design is informed by the CGAL package submission -> guidelines at https://www.cgal.org/developers.html +> **Status: design frozen, implementation planned.** +> This document captures the strategic decisions taken before the first +> line of Phase 8 code is written. The decisions were taken on 2026-05-19 +> after the docstring/architecture audit at the end of Phase 7. +> +> The design is informed by the CGAL package submission guidelines at +> https://www.cgal.org/developers.html and by reading the existing +> `Polygon_mesh_processing` and `Surface_mesh_parameterization` packages. --- -## Goal +## Strategic position -Integrate conformallab++ into the CGAL library as a proper CGAL package: -`Discrete_conformal_map`. The package must satisfy all CGAL submission requirements: -traits-class design, Doxygen documentation, CGAL-format test suite, and coverage of -the CGAL coding conventions. +| Question | Decision | Rationale | +|---|---|---| +| Submission to CGAL? | **Pre-submission-ready, not submission-bound.** 12+ months horizon, optional. | Keep design freedom, no editor-review pressure. Structure is valuable on its own. | +| License | **MIT preserved.** | CGAL submission would require LGPL — deferred. Current users (academic + industrial) profit from MIT. | +| Mesh-type flexibility | **Generic `FaceGraph + HalfedgeGraph`.** | Maximum CGAL value: works with `Surface_mesh`, `Polyhedron_3`, OpenMesh-adapter, pmp. | +| Parameter style | **Named Parameters** (`CGAL::parameters::vertex_curvature_map(...).max_iterations(50)`). | CGAL standard; identical UX to `PMP::triangulate_*`. | +| Default kernel | **`CGAL::Simple_cartesian`.** | Status quo. Conformal geometry does not require exact predicates. | +| Backward compatibility | **Dual-layer wrapper.** `code/include/*.hpp` stays as implementation; `include/CGAL/*.h` is thin wrapper. | Existing 176 + 36 tests unchanged. New API gets new tests. | +| Algorithm code | **No duplication.** New CGAL headers delegate to existing code via property-map adapters. | Single source of truth; no parallel maintenance. | + +--- + +## Architecture + +### Three-layer model + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ Layer 3: Public CGAL API include/CGAL/*.h │ +│ ───────────────────────── │ +│ • Conformal_map_traits.h ← concept + default model │ +│ • Discrete_conformal_map.h ← user-facing entry │ +│ • Conformal_layout.h, ... │ +│ Named parameters, generic over FaceGraph, Doxygen-documented. │ +└──────────────────────────────────────────────────────────────────┘ + ▲ + │ thin wrapper, no algorithm code + │ +┌──────────────────────────────────────────────────────────────────┐ +│ Layer 2: Adapter / Traits include/CGAL/Conformal_map/ │ +│ ───────────────────────── │ +│ • Default_traits.h ← maps generic FaceGraph to │ +│ Surface_mesh property maps │ +│ • Property_map_adapter.h ← read/write u, θ, α via │ +│ boost::property_map traits │ +└──────────────────────────────────────────────────────────────────┘ + ▲ + │ uses existing algorithms as-is + │ +┌──────────────────────────────────────────────────────────────────┐ +│ Layer 1: Implementation code/include/*.hpp │ +│ ───────────────────────── │ +│ euclidean_functional.hpp, layout.hpp, newton_solver.hpp, ... │ +│ Hardcoded to Surface_mesh + Simple_cartesian — unchanged. │ +└──────────────────────────────────────────────────────────────────┘ +``` --- ## 8a — Traits class & concepts -The current code is tightly coupled to `CGAL::Surface_mesh`. Phase 8a introduces -a traits class that separates the mesh type from the algorithm: +### `ConformalMapTraits` concept + +The concept lists the types and operations every Traits model must provide. ```cpp -// TODO(Phase 8a): implement this header -// include/CGAL/Conformal_map_traits.h +namespace CGAL { -template< - typename MeshType, // any CGAL halfedge mesh - typename KernelType, // CGAL kernel - typename ScalarType = double -> -struct Conformal_map_traits { - using Mesh = MeshType; - using Kernel = KernelType; - using FT = ScalarType; - // ... vertex/edge/face descriptor types - // ... property map access +// Concept (documentation only; no code): +struct ConformalMapTraits { + // Types + using Triangle_mesh = ...; // model of FaceGraph + HalfedgeGraph + using FT = ...; // typically double + using Vertex_descriptor = boost::graph_traits::vertex_descriptor; + using Halfedge_descriptor = ...; + using Face_descriptor = ...; + + // Read access (input geometry) + using Vertex_point_map = ...; // model of ReadablePropertyMap + // key: Vertex_descriptor + // value: K::Point_3 + + // Read/write access (conformal data) + using Lambda_pmap = ...; // u_v (scale factor) RW + using Theta_pmap = ...; // Θ_v (target curvature) R + using Vertex_index_pmap = ...; // DOF index (−1 = pinned) RW + using Edge_alpha_pmap = ...; // α_e (hyperbolic only) RW + using Face_type_pmap = ...; // geometry tag per face R + + // Optional output + using UV_pmap = ...; // halfedge → (u, v) ∈ ℝ² W + using Holonomy_pmap = ...; // seam edge → ω ∈ ℂ W +}; + +} +``` + +### `Default_conformal_map_traits` + +The default model wraps `Surface_mesh` property maps so existing code keeps +working through the new public API. + +```cpp +template > +struct Default_conformal_map_traits; + +// Specialisation for Surface_mesh: +template +struct Default_conformal_map_traits, K> { + using Triangle_mesh = CGAL::Surface_mesh; + using FT = typename K::FT; + using Vertex_point_map = typename Triangle_mesh::Point_property_map; + using Lambda_pmap = typename Triangle_mesh::template Property_map; + // ... etc, using "conformal:lambda" property names +}; + +// Generic specialisation for other FaceGraph models will be added in 8a.2. +``` + +### Concept-checks + +```cpp +// include/CGAL/Conformal_map_concept_checks.h +template +struct Conformal_map_traits_check { + static_assert(boost::is_same<...>::value, "Traits::FT must be a floating-point type"); + static_assert(is_face_graph::value); + // ... }; ``` -Concept checks will ensure any user-provided mesh satisfies the halfedge mesh concept. - --- -## 8b — Public header hierarchy +## 8b — Public CGAL header hierarchy -A clean public API separate from the internal implementation: +### User-facing entry + +```cpp +// include/CGAL/Discrete_conformal_map.h +namespace CGAL { + +template +bool discrete_conformal_map_euclidean(TriangleMesh& mesh, + const NamedParameters& np = parameters::default_values()); + +template +bool discrete_conformal_map_spherical(TriangleMesh& mesh, + const NamedParameters& np = ...); + +template +bool discrete_conformal_map_hyperbolic(TriangleMesh& mesh, + const NamedParameters& np = ...); + +} // namespace CGAL +``` + +### Named parameter vocabulary + +| Parameter | Type | Default | Meaning | +|---|---|---|---| +| `vertex_curvature_map(pmap)` | ReadablePropertyMap | `2π` at interior, `π` at boundary | Θᵥ values | +| `fixed_vertex_pmap(pmap)` | ReadablePropertyMap | First vertex pinned | Which vertices are pinned (gauge) | +| `max_iterations(n)` | int | 200 | Newton iteration limit | +| `gradient_tolerance(eps)` | FT | 1e-10 | `‖G‖∞` threshold | +| `vertex_index_map(pmap)` | LvaluePropertyMap | DOF auto-assigned | Allows user to override DOF assignment | +| `output_uv_map(pmap)` | WritablePropertyMap | none | If set, writes UV layout into pmap | +| `cut_graph(cg)` | `Conformal_cut_graph` | auto-computed | Pre-computed seam edges (mandatory for closed surfaces) | +| `geom_traits(t)` | model of ConformalMapTraits | `Default_*` | Custom traits | + +### Modular headers ``` include/CGAL/ - Discrete_conformal_map.h ← single user-facing include - Conformal_map_traits.h - Conformal_newton_solver.h - Conformal_layout.h - Conformal_cut_graph.h - conformal_map_package.h ← PackageDescription +├── Discrete_conformal_map.h ← user-facing entry (1 include for casual use) +├── Conformal_map_traits.h ← concept + Default_conformal_map_traits +├── Conformal_map_concept_checks.h +├── Conformal_newton_solver.h ← standalone Newton (advanced users) +├── Conformal_layout.h ← layout + holonomy +├── Conformal_cut_graph.h ← orthogonal algorithm +├── Conformal_period_matrix.h ← genus-1 τ (conformallab++ unique) +├── Conformal_holonomy.h ← Möbius holonomy (conformallab++ unique) +└── Conformal_map/ ← CGAL convention: implementation details + ├── Default_traits.h + ├── Property_map_adapter.h + ├── Newton_iteration.h + └── Internal_helpers.h ``` -All existing `include/*.hpp` headers remain as internal implementation details, -not part of the public CGAL API. - --- ## 8c — CGAL-style documentation ``` doc/Conformal_map/ - PackageDescription.txt - User_manual.md - Reference_manual.md - fig/ ← pipeline diagrams, mathematical figures +├── PackageDescription.txt ← CGAL Doxygen package file +├── Conformal_map.txt ← Doxygen User_manual +├── examples.txt ← linkable example code +├── dependencies ← textual list +└── fig/ ← pipeline diagrams, math figures ``` -All public functions and concepts require Doxygen comments following the CGAL style. +All public functions, concepts, and types require Doxygen. See **Phase 7.5** (below). --- @@ -79,88 +213,113 @@ All public functions and concepts require Doxygen comments following the CGAL st ``` test/Conformal_map/ - CMakeLists.txt ← CGAL-format, uses find_package(CGAL) - test_euclidean_functional.cpp - test_newton_solver.cpp - ... +├── CMakeLists.txt ← CGAL-format, uses find_package(CGAL) +├── test_euclidean_traits.cpp ← traits concept checks +├── test_polyhedron_3_backend.cpp ← tests with Polyhedron_3 as mesh +├── test_named_parameters.cpp +└── data/ ← test meshes ``` -The existing GTest suite remains. CGAL-format tests are added alongside as a separate -target, following the CGAL test infrastructure conventions. +The existing GTest suite at `code/tests/cgal/` remains; CGAL-format tests are +added alongside as a separate target. CI runs both. --- ## 8e — Declarative YAML pipeline -A lightweight YAML format for reproducible experiments. The CLI accepts +A lightweight YAML format for reproducible experiments. CLI accepts `--pipeline experiment.yml`; the validator checks `require`/`provide` tokens before execution. -**Full concept & design specification:** [doc/concepts/declarative-pipeline.md](../concepts/declarative-pipeline.md) -— token vocabulary, validation algorithm, 5 complete examples, implementation plan. - -Abbreviated example: +**Full spec:** [doc/concepts/declarative-pipeline.md](../concepts/declarative-pipeline.md) +— token vocabulary, validation algorithm, 5 complete examples. ```yaml pipeline: name: flat_torus_period geometry: euclidean - - input: - source: data/torus.off - + input: { source: data/torus.off } steps: - - id: setup - unit: setup_euclidean_maps - provide: [maps_initialised] - - - id: gauss_bonnet - unit: enforce_gauss_bonnet - require: [maps_initialised] - provide: [gauss_bonnet_satisfied] - - - id: solve - unit: newton_euclidean - require: [gauss_bonnet_satisfied] - params: - tol: 1.0e-10 - max_iter: 200 - provide: [x_converged] - - - id: cut - unit: compute_cut_graph - require: [mesh_closed] - provide: [cut_graph] - - - id: layout - unit: euclidean_layout - require: [x_converged, cut_graph] - params: - normalise: true - provide: [layout_uv, holonomy] - - - id: period - unit: compute_period_matrix - require: [holonomy] - provide: [tau] - + - { id: setup, unit: setup_euclidean_maps, provide: [maps_initialised] } + - { id: gb, unit: enforce_gauss_bonnet, require: [maps_initialised], provide: [gauss_bonnet_satisfied] } + - { id: solve, unit: newton_euclidean, require: [gauss_bonnet_satisfied], provide: [x_converged] } + - { id: cut, unit: compute_cut_graph, require: [mesh_closed], provide: [cut_graph] } + - { id: layout, unit: euclidean_layout, require: [x_converged, cut_graph], provide: [layout_uv, holonomy] } + - { id: period, unit: compute_period_matrix, require: [holonomy], provide: [tau] } output: layout: out/torus_layout.off json: out/torus_result.json - tau: out/torus_tau.txt ``` -The contract table in [contracts.md](contracts.md) defines the valid `require`/`provide` -token vocabulary. - --- -## TODO +## Phase 7.5 — Doxygen infrastructure (prerequisite) -- [ ] Design `Conformal_map_traits.h` interface (8a) -- [ ] Define concept requirements for `MeshType` (8a) -- [ ] Create `include/CGAL/` header skeleton (8b) -- [ ] Write `PackageDescription.txt` (8c) -- [ ] Port GTest tests to CGAL format (8d) -- [ ] Implement YAML validator (8e) -- [ ] CLI: `--pipeline` flag (8e) +Before any Phase 8 code, the existing API surface must be extractable. +This is the prerequisite that bridges Phase 7 → Phase 8. + +``` +Phase 7.5 — Doxygen infrastructure +────────────────────────────────── + • Doxyfile (CGAL-conform: INPUT=code/include + include/CGAL, + EXCLUDE_PATTERNS="* 2.hpp") + • doxygen-awesome-css as theme (matches CGAL house style) + • CMake target: cmake --build build --target doc + • CI job: doc-build → publishes to Codeberg Pages or gitea-pages + • Extract baseline once → snapshot what is actually exported today + • Top-5 central headers (3 functional + conformal_mesh + layout) + upgraded to Doxygen comments; the rest follows during Phase 8 implementation +``` + +The baseline snapshot doubles as the API-design review tool: before designing +the public CGAL wrapper, we see exactly which functions, classes and free +operators exist and need to be wrapped or hidden. + +--- + +## Validation criteria + +Phase 8a is "done" when: + +1. `cgal.ConformalTraits.Polyhedron_3_works` passes. +2. `cgal.ConformalTraits.Surface_mesh_default_works` passes — identical results to the legacy API. +3. The Inversive-Distance functional (Phase 9a) is implementable as the *first* new client of the traits API without architectural changes — no breaking changes to the trait concept. +4. A user can write `#include ` and call `discrete_conformal_map_euclidean(mesh, parameters::vertex_curvature_map(theta))` against a `Polyhedron_3` and get a valid layout. + +If any of these fail, the design is iterated before continuing. + +--- + +## Implementation order + +``` +1. Phase 7.5 Doxygen infrastructure + duplicate cleanup (½–1 day) +2. Phase 8a.1 ConformalMapTraits concept + Default_traits (2–3 days) +3. Phase 8a.2 Generic FaceGraph specialisation (2 days) +4. Phase 8b.1 Discrete_conformal_map.h entry point (2 days) +5. Phase 8b.2 Layout + cut graph wrappers (2 days) +6. Phase 9a Inversive-Distance against new traits = API test (3–5 days) +7. Phase 8c PackageDescription.txt + User_manual.md (2 days) +8. Phase 8d CGAL-format test directory (1–2 days) +9. Phase 8e YAML pipeline + CLI flag (3 days) +``` + +**Total budget: 3–4 weeks net work, 6–8 weeks calendar.** + +--- + +## TODO checklist + +- [ ] Phase 7.5: Doxyfile + CMake doc target + duplicate cleanup +- [ ] 8a.1: `ConformalMapTraits` concept header +- [ ] 8a.1: `Default_conformal_map_traits` +- [ ] 8a.2: Generic `FaceGraph` specialisation +- [ ] 8a.2: `Conformal_map_concept_checks.h` +- [ ] 8b.1: `Discrete_conformal_map.h` (3 entry functions) +- [ ] 8b.1: Named-parameter vocabulary header +- [ ] 8b.2: `Conformal_layout.h`, `Conformal_cut_graph.h` wrappers +- [ ] 9a: `inversive_distance_functional.hpp` written against new traits +- [ ] 8c: `doc/Conformal_map/PackageDescription.txt` +- [ ] 8c: User_manual + Reference_manual +- [ ] 8d: `test/Conformal_map/` CGAL-style tests +- [ ] 8e: YAML validator + CLI `--pipeline` flag From 4971f0254d4eae7f56947510070f080845edbba1 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Tue, 19 May 2026 20:04:38 +0200 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20refine=20Phase=208=20strategy=20to?= =?UTF-8?q?=20Hybrid=20MVP=20=E2=80=94=20MVP=20first,=20port=20second?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-evaluated cost/benefit of Phase 8 vs Phase 9 after distinguishing three concurrent goals: • Goal A (Port): ~90% done, ~3 weeks remaining • Goal B (CGAL): speculative, 12+ months, uncertain submission • Goal C (Tool): research utility with novel features Phase 8 full (3–4 weeks) would mostly serve Goal C plus optional Goal B. Phase 9 (3 weeks) finishes Goal A unconditionally. Building Phase 8 in full before Phase 9 risks 3-4 weeks of speculative architecture for a hypothetical CGAL submission. New strategy: Hybrid MVP. Phase 8 MVP (3–5 days): Conformal_map_traits.h concept + Default Discrete_conformal_map.h ONE entry: _euclidean() 4 named parameters Theta-map, max_iter, tol, pin Concept-check header + Doxygen Phase 9a (3–5 days): Inversive-Distance vs MVP API = acceptance test Phase 9b + 9c (~2 weeks): Port truly complete Phase 8 extensions: Only on concrete trigger 8a.2 generic FaceGraph trigger: Polyhedron_3 user 8c full doc trigger: submission planned 8d CGAL-format tests trigger: submission planned 8e YAML pipeline orthogonal, any time Net committed budget: ~4 weeks for "port complete + CGAL MVP", not 6–8 weeks for full Phase 8 + Phase 9. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 27 +++++++-- doc/api/cgal-package.md | 124 ++++++++++++++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 29 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 53a6bb6..b2a2a6b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -253,19 +253,34 @@ Phase 7 is complete. Phase 7.5 (Doxygen) and Phase 8 (CGAL package) are next. ## Phase 8 strategic decisions (2026-05-19) -The CGAL-package architecture was frozen on 2026-05-19 after the end-of-Phase-7 -docstring audit. Full design: [`doc/api/cgal-package.md`](doc/api/cgal-package.md). -Key decisions: +The CGAL-package architecture was frozen on 2026-05-19. Full design: +[`doc/api/cgal-package.md`](doc/api/cgal-package.md). Key decisions: | Decision | Choice | |---|---| | Submission to upstream CGAL | **Pre-submission-ready, not bound.** 12+ months horizon. | | License | **MIT preserved** (no LGPL switch). | -| Mesh-type flexibility | **Generic `FaceGraph + HalfedgeGraph`** — Surface_mesh, Polyhedron_3, OpenMesh-adapter, pmp. | +| Mesh-type flexibility | **Generic `FaceGraph + HalfedgeGraph`** in target design; MVP starts Surface_mesh-only. | | Parameter style | **Named Parameters** (`CGAL::parameters::...`). | | Default kernel | **`Simple_cartesian`** (status quo). | -| Backward compatibility | **Dual-layer wrapper** — `code/include/*.hpp` stays as implementation, `include/CGAL/*.h` is thin wrapper. No algorithm duplication. | -| Phase-8a acceptance test | **Phase 9a (Inversive-Distance)** as the first new client of the new traits API. | +| Backward compatibility | **Dual-layer wrapper** — `code/include/*.hpp` stays as implementation, `include/CGAL/*.h` is thin wrapper. No algorithm duplication. | +| Implementation strategy | **Hybrid MVP** — minimum Phase 8 (traits + one wrapper) first, then Phase 9 in full, then Phase 8 extensions only on concrete demand. | +| Phase-8 MVP acceptance test | **Phase 9a (Inversive-Distance)** as the first new client of the new traits API. | + +### Implementation sequence (committed) + +``` +1. Phase 7.5 Doxygen + cleanup done ✅ +2. Phase 8 MVP — traits + one euclidean wrapper 3–5 days +3. Phase 9a — Inversive-Distance against new traits 3–5 days +4. Phase 9b — analytic HyperIdeal Hessian 1 week +5. Phase 9c — 4g-polygon for genus g > 1 1 week + → port really complete, v0.9.0 release +``` + +Phase 8 extensions (8a.2 generic FaceGraph, 8c full Doxygen manuals, 8d +CGAL-format tests, 8e YAML pipeline) are deferred to on-demand status — +no speculative architecture for an uncertain submission. Root-level files added at v0.7.0: - `CITATION.cff` — machine-readable citation (Sechelmann 2016, Springborn 2020, Bobenko–Springborn 2004) diff --git a/doc/api/cgal-package.md b/doc/api/cgal-package.md index 2e3bc4d..38f158c 100644 --- a/doc/api/cgal-package.md +++ b/doc/api/cgal-package.md @@ -290,36 +290,114 @@ If any of these fail, the design is iterated before continuing. --- -## Implementation order +## Implementation strategy — "Hybrid MVP" (decided 2026-05-19) + +After cost/benefit re-evaluation, the plan is **not** to build Phase 8 in full +before resuming the port. Instead: ``` -1. Phase 7.5 Doxygen infrastructure + duplicate cleanup (½–1 day) -2. Phase 8a.1 ConformalMapTraits concept + Default_traits (2–3 days) -3. Phase 8a.2 Generic FaceGraph specialisation (2 days) -4. Phase 8b.1 Discrete_conformal_map.h entry point (2 days) -5. Phase 8b.2 Layout + cut graph wrappers (2 days) -6. Phase 9a Inversive-Distance against new traits = API test (3–5 days) -7. Phase 8c PackageDescription.txt + User_manual.md (2 days) -8. Phase 8d CGAL-format test directory (1–2 days) -9. Phase 8e YAML pipeline + CLI flag (3 days) +┌────────────────────────────────────────────────────────────────────┐ +│ PHASE 8 MVP (3–5 days) │ +│ ───────────────────── │ +│ Just enough CGAL-style architecture for Phase 9a to validate it. │ +│ │ +│ • Conformal_map_traits.h concept + Default │ +│ • Discrete_conformal_map.h ONE entry: _euclidean() │ +│ • 4 named parameters Θ-map, max_iter, tol, pin │ +│ • Concept-check header │ +│ • Doxygen on these 3 headers │ +└────────────────────────────────────────────────────────────────────┘ + ▼ +┌────────────────────────────────────────────────────────────────────┐ +│ PHASE 9a — Inversive-Distance (3–5 days) │ +│ ────────────────────────────────── │ +│ Built directly against the new traits API. This is the │ +│ acceptance test for the MVP. │ +│ │ +│ If painless → MVP design is sound, continue with Phase 9b/9c │ +│ If painful → iterate the traits design before going further │ +└────────────────────────────────────────────────────────────────────┘ + ▼ +┌────────────────────────────────────────────────────────────────────┐ +│ PHASE 9b — Analytic HyperIdeal Hessian (1 week) │ +│ PHASE 9c — 4g-polygon fundamental domain (1 week) │ +│ ──────────────────────────────────────── │ +│ Port really finished. v0.9.0 release possible. │ +└────────────────────────────────────────────────────────────────────┘ + ▼ +┌────────────────────────────────────────────────────────────────────┐ +│ PHASE 8 EXTENSIONS — only on demand │ +│ ───────────────────────────────── │ +│ • 8a.2 generic FaceGraph specialisation when Polyhedron_3 user │ +│ • 8b extend to spherical + hyperbolic when 9a pattern proven │ +│ • 8c full User_manual + Reference_manual when submission planned│ +│ • 8d CGAL-format test directory when submission planned│ +│ • 8e YAML pipeline + CLI flag orthogonal, any time │ +│ │ +│ Each extension only when there is a concrete trigger. No │ +│ speculative architecture for a hypothetical CGAL submission. │ +└────────────────────────────────────────────────────────────────────┘ ``` -**Total budget: 3–4 weeks net work, 6–8 weeks calendar.** +**Why this order?** + +- Port-completion (Phase 9) is the higher-confidence value: well-defined, + ~3 weeks of work, finishes Goal A. +- Full Phase 8 (3–4 weeks) speculative — only pays off if CGAL submission + actually happens, which is uncertain. +- Phase 8 MVP captures the architectural insight (traits + named params) + without the long tail. If the rest of Phase 8 is ever wanted, it's + additive — nothing built in the MVP needs to be thrown away. + +**Total committed budget: 2 weeks (MVP + 9a) + 2 weeks (9b + 9c) = ~4 weeks +net work, 6–8 weeks calendar.** After that, the port is finished. + +## Phase 8 MVP scope — what is and isn't in the first cut + +| Item | MVP | Later | Reason | +|---|:---:|:---:|---| +| `Conformal_map_traits.h` concept | ✅ | — | Core abstraction | +| `Default_conformal_map_traits` | ✅ | — | Status-quo wrapper | +| Generic `FaceGraph` specialisation | — | ✅ 8a.2 | Speculative until asked | +| `Discrete_conformal_map.h` — `_euclidean()` | ✅ | — | First entry function | +| `Discrete_conformal_map.h` — `_spherical()`, `_hyperbolic()` | — | ✅ 8b.2 | Pattern-replicates once 9a works | +| Named parameters: `vertex_curvature_map`, `max_iterations`, `gradient_tolerance`, `fixed_vertex_pmap` | ✅ | — | Essential 4 | +| Named parameters: rest (`output_uv_map`, `cut_graph`, …) | — | ✅ 8b.2 | Additive | +| Doxygen on MVP headers | ✅ | — | Same time anyway | +| Doxygen on legacy `code/include/*` | partial | ✅ 8c | Bulk later | +| `PackageDescription.txt` | — | ✅ 8c | Only if submitting | +| User_manual.md | — | ✅ 8c | Only if submitting | +| `test/Conformal_map/` CGAL-style | — | ✅ 8d | Only if submitting | +| YAML pipeline + CLI flag | — | ✅ 8e | Orthogonal, any time | --- ## TODO checklist -- [ ] Phase 7.5: Doxyfile + CMake doc target + duplicate cleanup -- [ ] 8a.1: `ConformalMapTraits` concept header -- [ ] 8a.1: `Default_conformal_map_traits` -- [ ] 8a.2: Generic `FaceGraph` specialisation -- [ ] 8a.2: `Conformal_map_concept_checks.h` -- [ ] 8b.1: `Discrete_conformal_map.h` (3 entry functions) -- [ ] 8b.1: Named-parameter vocabulary header +### MVP track (committed work, ~4 weeks) +- [x] Phase 7.5: Doxyfile + CMake doc target + duplicate cleanup +- [ ] **Phase 8 MVP — Traits + one wrapper** + - [ ] `Conformal_map_traits.h` — concept documentation + - [ ] `Default_conformal_map_traits` + - [ ] `Conformal_map_concept_checks.h` + - [ ] `Discrete_conformal_map.h` with `_euclidean()` only + - [ ] 4 named parameters: Θ-map, max_iter, tol, pin + - [ ] Test: `cgal.ConformalTraits.Surface_mesh_default_works` +- [ ] **Phase 9a — Inversive-Distance (acceptance test for MVP)** + - [ ] `inversive_distance_functional.hpp` against new traits + - [ ] Gradient check + Newton convergence tests + - [ ] Doxygen on new headers +- [ ] **Phase 9b — Analytic HyperIdeal Hessian** + - [ ] Replace FD in `hyper_ideal_hessian.hpp` + - [ ] Symmetry + PSD checks unchanged +- [ ] **Phase 9c — 4g-polygon for genus g > 1** + - [ ] Extend `compute_fundamental_domain()` beyond genus 1 + +### On-demand track (only with concrete trigger) +- [ ] 8a.2: Generic `FaceGraph` specialisation (trigger: Polyhedron_3 user) +- [ ] 8b.2: `_spherical()` + `_hyperbolic()` entry functions (trigger: pattern proven) - [ ] 8b.2: `Conformal_layout.h`, `Conformal_cut_graph.h` wrappers -- [ ] 9a: `inversive_distance_functional.hpp` written against new traits -- [ ] 8c: `doc/Conformal_map/PackageDescription.txt` -- [ ] 8c: User_manual + Reference_manual -- [ ] 8d: `test/Conformal_map/` CGAL-style tests -- [ ] 8e: YAML validator + CLI `--pipeline` flag +- [ ] 8c: `doc/Conformal_map/PackageDescription.txt` (trigger: submission planned) +- [ ] 8c: User_manual + Reference_manual (trigger: submission planned) +- [ ] 8d: `test/Conformal_map/` CGAL-style tests (trigger: submission planned) +- [ ] 8e: YAML validator + CLI `--pipeline` flag (orthogonal, any time)