From e958afbd1963161ad85a8da116f618ca088d6cc6 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Tue, 19 May 2026 00:09:09 +0200 Subject: [PATCH] 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). ---