chore: translate all German text to English across code, docs, and CI
Unified the codebase language to English throughout. German text appeared in code comments, test file headers, CI step names, and several markdown documents. All natural-language text is now English; proper nouns (Institut für Mathematik, Technische Universität Berlin) are unchanged. Files changed: - .gitea/workflows/cpp-tests.yml — CI step names and job comments - code/include/mesh_utils.hpp — inline comment - code/tests/cgal/CMakeLists.txt — section comment block - code/tests/cgal/test_geometry_utils.cpp — full file header + all test comments - doc/math/references.md — geometry-central section - doc/math/validation.md — Section 9 (geometry-central cross-validation) - doc/roadmap/phases.md — Optional geometry-central track (GC-1/2/3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,8 @@ on:
|
|||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Job 1 — test-fast
|
# Job 1 — test-fast
|
||||||
# Pure-math tests (Clausen, ImLi₂, Hyper-ideal Geometrie).
|
# Pure-math tests (Clausen, ImLi₂, Hyper-ideal geometry).
|
||||||
# Kein CGAL, kein Boost. Nur Eigen + GTest. Läuft auf ALLEN Branches.
|
# No CGAL, no Boost. Eigen + GTest only. Runs on ALL branches.
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
jobs:
|
jobs:
|
||||||
test-fast:
|
test-fast:
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
--output-on-failure
|
--output-on-failure
|
||||||
--output-junit test-results.xml
|
--output-junit test-results.xml
|
||||||
|
|
||||||
- name: Zusammenfassung
|
- name: Summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
if [ -f test-results.xml ]; then
|
if [ -f test-results.xml ]; then
|
||||||
@@ -48,14 +48,14 @@ jobs:
|
|||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Job 2 — test-cgal
|
# Job 2 — test-cgal
|
||||||
# Vollständige CGAL-Test-Suite (Phase 3–7, 158 Tests).
|
# Full CGAL test suite (Phase 3–7, 158 tests).
|
||||||
# Läuft NUR bei Pull Requests (nicht bei direkten Pushes auf dev/main).
|
# Runs ONLY on pull requests (not on direct pushes to dev/main).
|
||||||
# Startet erst nach erfolgreichem test-fast.
|
# Starts only after test-fast succeeds.
|
||||||
#
|
#
|
||||||
# Verwendet -DWITH_CGAL_TESTS=ON (nicht -DWITH_CGAL=ON), damit kein
|
# Uses -DWITH_CGAL_TESTS=ON (not -DWITH_CGAL=ON) to avoid building
|
||||||
# Viewer/GLFW gebaut wird — der CI-Container hat kein wayland-scanner.
|
# 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:
|
test-cgal:
|
||||||
needs: test-fast
|
needs: test-fast
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- 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
|
run: cmake -S code -B build -DWITH_CGAL_TESTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build CGAL-Tests
|
- name: Build CGAL-Tests
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
--output-on-failure
|
--output-on-failure
|
||||||
--output-junit cgal-results.xml
|
--output-junit cgal-results.xml
|
||||||
|
|
||||||
- name: Zusammenfassung
|
- name: Summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
if [ -f cgal-results.xml ]; then
|
if [ -f cgal-results.xml ]; then
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ void simple_visualize_mesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) {
|
|||||||
viewer.data().set_mesh(V, F);
|
viewer.data().set_mesh(V, F);
|
||||||
viewer.launch();
|
viewer.launch();
|
||||||
}
|
}
|
||||||
// Zero-Copy Map für V (optional)
|
// Zero-copy map for V (optional)
|
||||||
template <typename Kernel>
|
template <typename Kernel>
|
||||||
Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>>
|
Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>>
|
||||||
get_vertex_map(CGAL::Surface_mesh<typename Kernel::Point_3>& mesh) {
|
get_vertex_map(CGAL::Surface_mesh<typename Kernel::Point_3>& mesh) {
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ add_executable(conformallab_cgal_tests
|
|||||||
# period matrix, fundamental domain, tiling
|
# period matrix, fundamental domain, tiling
|
||||||
test_phase7.cpp
|
test_phase7.cpp
|
||||||
|
|
||||||
# ── Java-Parität: Geometrie-Utility-Tests ─────────────────────────────────
|
# ── Java parity: geometry utility tests ──────────────────────────────────
|
||||||
# Portiert aus CuttinUtilityTest, UnwrapUtilityTest,
|
# Ported from CuttinUtilityTest, UnwrapUtilityTest,
|
||||||
# ConvergenceUtilityTests, HomologyTest (Tests 1–6).
|
# ConvergenceUtilityTests, HomologyTest (tests 1–6).
|
||||||
# Test 7 (Genus-2-Homologie) als GTEST_SKIP-Stub bis Phase 8.
|
# Test 7 (genus-2 homology) as GTEST_SKIP stub until Phase 8.
|
||||||
test_geometry_utils.cpp
|
test_geometry_utils.cpp
|
||||||
|
|
||||||
# ── Scalability smoke tests ────────────────────────────────────────────────
|
# ── Scalability smoke tests ────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
// test_geometry_utils.cpp
|
// 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_False PORTED
|
||||||
// CuttinUtilityTest.java testIsInConvexTextureFace_True PORTIERT
|
// CuttinUtilityTest.java testIsInConvexTextureFace_True PORTED
|
||||||
// UnwrapUtilityTest.java testGetAngleReturnsPI PORTIERT
|
// UnwrapUtilityTest.java testGetAngleReturnsPI PORTED
|
||||||
// ConvergenceUtilityTests.java testGetTextureCircumRadius PORTIERT
|
// ConvergenceUtilityTests.java testGetTextureCircumRadius PORTED
|
||||||
// ConvergenceUtilityTests.java testGetTextureTriangleArea PORTIERT
|
// ConvergenceUtilityTests.java testGetTextureTriangleArea PORTED
|
||||||
// ConvergenceUtilityTests.java testScaleInvariantCircumCircleRadius PORTIERT
|
// ConvergenceUtilityTests.java testScaleInvariantCircumCircleRadius PORTED
|
||||||
// HomologyTest.java testHomology PORTIERT
|
// HomologyTest.java testHomology PORTED
|
||||||
// EuclideanLayoutTest.java testDoLayout PORTIERT
|
// EuclideanLayoutTest.java testDoLayout PORTED
|
||||||
// EuclideanCyclicConvergenceTest.java testEuclideanConvergence PORTIERT
|
// EuclideanCyclicConvergenceTest.java testEuclideanConvergence PORTED
|
||||||
// SphericalConvergenceTest.java testSphericalConvergence PORTIERT
|
// 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)
|
// Java: CuttingUtility.isInConvexTextureFace(pp, face, adapters)
|
||||||
// Hinweis: Java-Test 2 hat ein 5-elementiges T-Array mit w=0 (Punkt im
|
// Note: Java test 2 has a 5-element T-array with w=0 (point at
|
||||||
// Unendlichen), was ein Tippfehler im Original ist. Hier werden
|
// infinity), which is a typo in the original. Equivalent, well-formed
|
||||||
// äquivalente, wohlgeformte Koordinaten verwendet.
|
// coordinates are used here instead.
|
||||||
//
|
//
|
||||||
// Test 3 Eckenwinkel für kollineare Vertices über den Kosinussatz.
|
// Test 3 Corner angle for collinear vertices via the law of cosines.
|
||||||
// Java: UnwrapUtility.getAngle(edge, adapters) — gibt den Winkel am
|
// Java: UnwrapUtility.getAngle(edge, adapters) — returns the angle at
|
||||||
// Zielknoten zurück. Für v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) ist
|
// the target vertex. For v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) the
|
||||||
// der Winkel bei v1 genau π (Dreiecksungleichung entartet).
|
// 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)
|
// Java: ConvergenceUtility.getTextureCircumCircleRadius(face)
|
||||||
// ConvergenceUtility.getTextureTriangleArea(face)
|
// ConvergenceUtility.getTextureTriangleArea(face)
|
||||||
// Formeln: Area = |det([B-A, C-A])| / 2
|
// Formulas: Area = |det([B-A, C-A])| / 2
|
||||||
// R = (a·b·c) / (4·Area)
|
// 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)
|
// Java: ConvergenceUtility.getMaxMeanSumScaleInvariantCircumRadius(hds)
|
||||||
// Gibt [max, mean, sum] von R_f / sqrt(total_texture_area) zurück.
|
// Returns [max, mean, sum] of R_f / sqrt(total_texture_area).
|
||||||
// Invariant unter uniformer Skalierung der Texturkoordinaten (Test mit
|
// Invariant under uniform scaling of texture coordinates (tested with
|
||||||
// homogenem Gewicht w: Position = (T[0]/w, T[1]/w)).
|
// 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)
|
// 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
|
// C++: compute_cut_graph(mesh).cut_edge_indices.size() == 4
|
||||||
// Mesh: code/data/obj/brezel2.obj (V=2622, F=5248, χ=−2, g=2)
|
// 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
|
// 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.
|
// Test 10 Euclidean Newton on cathead.obj — convergence + angle deficit.
|
||||||
// Java: EuclideanLayoutTest.testLayout02 (130-Werte-Array für cathead.heml)
|
// Java: EuclideanLayoutTest.testLayout02 (130-value array for cathead.heml)
|
||||||
// C++: Newton ab u=0, prüft Konvergenz + Σα_v ≈ 2π für alle inneren Knoten.
|
// C++: Newton from u=0, checks convergence + Σα_v ≈ 2π for all interior nodes.
|
||||||
//
|
//
|
||||||
// Test 11 Sphärischer Newton auf Oktaeder — Konvergenz + Winkeldefekt.
|
// Test 11 Spherical Newton on octahedron — convergence + angle deficit.
|
||||||
// Java: SphericalConvergenceTest.testSphericalConvergence (Oktaeder, zufällig
|
// Java: SphericalConvergenceTest.testSphericalConvergence (octahedron, randomly
|
||||||
// störe Radien, seed=1). C++: konstruierter regulärer Oktaeder, prüft
|
// perturbed radii, seed=1). C++: constructed regular octahedron, checks
|
||||||
// Konvergenz und dass Σα_v ≈ 2π (Target für Sphäre nach prepareInvariantData).
|
// convergence and that Σα_v ≈ 2π (target for sphere after prepareInvariantData).
|
||||||
//
|
//
|
||||||
// ─────────────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -81,12 +81,12 @@
|
|||||||
using namespace conformallab;
|
using namespace conformallab;
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Lokale Geometrie-Hilfsfunktionen
|
// Local geometry helper functions
|
||||||
// (portiert aus Java CuttingUtility / ConvergenceUtility)
|
// (ported from Java CuttingUtility / ConvergenceUtility)
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Punkt-in-Dreieck Test (2D, baryzentrische Vorzeichenmethode).
|
/// Point-in-triangle test (2D, barycentric sign method).
|
||||||
/// Gibt true zurück wenn p strikt innerhalb oder auf dem Rand von v0-v1-v2 liegt.
|
/// Returns true if p lies strictly inside or on the boundary of v0-v1-v2.
|
||||||
/// Java: CuttingUtility.isInConvexTextureFace
|
/// Java: CuttingUtility.isInConvexTextureFace
|
||||||
static bool point_in_triangle_2d(
|
static bool point_in_triangle_2d(
|
||||||
Eigen::Vector2d p,
|
Eigen::Vector2d p,
|
||||||
@@ -103,7 +103,7 @@ static bool point_in_triangle_2d(
|
|||||||
return !(has_neg && has_pos);
|
return !(has_neg && has_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 2D Dreiecksfläche (halbes Kreuzprodukt).
|
/// 2D triangle area (half cross product).
|
||||||
/// Java: ConvergenceUtility.getTextureTriangleArea
|
/// Java: ConvergenceUtility.getTextureTriangleArea
|
||||||
static double triangle_area_2d(
|
static double triangle_area_2d(
|
||||||
Eigen::Vector2d A, Eigen::Vector2d B, Eigen::Vector2d C)
|
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;
|
- (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
|
/// Java: ConvergenceUtility.getTextureCircumCircleRadius
|
||||||
static double circumradius_2d(
|
static double circumradius_2d(
|
||||||
Eigen::Vector2d A, Eigen::Vector2d B, Eigen::Vector2d C)
|
Eigen::Vector2d A, Eigen::Vector2d B, Eigen::Vector2d C)
|
||||||
@@ -125,17 +125,17 @@ static double circumradius_2d(
|
|||||||
return (a * b * c) / (4.0 * area);
|
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)
|
/// 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
|
/// Java: ConvergenceUtility.getMaxMeanSumScaleInvariantCircumRadius
|
||||||
///
|
///
|
||||||
/// Homogene Koordinaten: Position = (x/w, y/w).
|
/// Homogeneous coordinates: position = (x/w, y/w).
|
||||||
static std::array<double, 3> scale_invariant_circumradius_stats(
|
static std::array<double, 3> scale_invariant_circumradius_stats(
|
||||||
const std::vector<Eigen::Vector2d>& verts,
|
const std::vector<Eigen::Vector2d>& verts,
|
||||||
const std::vector<std::array<int, 3>>& faces)
|
const std::vector<std::array<int, 3>>& faces)
|
||||||
{
|
{
|
||||||
// Gesamtfläche
|
// Total area
|
||||||
double total_area = 0.0;
|
double total_area = 0.0;
|
||||||
for (auto& f : faces)
|
for (auto& f : faces)
|
||||||
total_area += triangle_area_2d(verts[f[0]], verts[f[1]], verts[f[2]]);
|
total_area += triangle_area_2d(verts[f[0]], verts[f[1]], verts[f[2]]);
|
||||||
@@ -154,70 +154,70 @@ static std::array<double, 3> 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
|
// 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)
|
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 v0(0.7488102998904661, 0.06293998610761144);
|
||||||
Eigen::Vector2d v1(0.7487811940754379, 0.06289451051246124);
|
Eigen::Vector2d v1(0.7487811940754379, 0.06289451051246124);
|
||||||
Eigen::Vector2d v2(0.7487254625255592, 0.06291429499873116);
|
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);
|
Eigen::Vector2d pp(0.44661534423161037, 2.2808373704822393e-4);
|
||||||
|
|
||||||
EXPECT_FALSE(point_in_triangle_2d(pp, v0, v1, v2));
|
EXPECT_FALSE(point_in_triangle_2d(pp, v0, v1, v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 2: Punkt liegt innerhalb
|
// Test 2: point lies inside
|
||||||
// Hinweis: Das originale Java-Array p2 hat 5 Elemente mit w=0 (Tippfehler im
|
// Note: the original Java array p2 has 5 elements with w=0 (typo in the
|
||||||
// Java-Original). Hier werden äquivalente, wohlgeformte Koordinaten verwendet,
|
// Java original). Equivalent, well-formed coordinates are used here
|
||||||
// die dasselbe geometrische Szenario abbilden.
|
// that represent the same geometric scenario.
|
||||||
TEST(CuttingUtility, IsInConvexTextureFace_True)
|
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 v0(0.0, 0.0);
|
||||||
Eigen::Vector2d v1(1e-8, 0.0);
|
Eigen::Vector2d v1(1e-8, 0.0);
|
||||||
Eigen::Vector2d v2(0.0, 1e-8);
|
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);
|
Eigen::Vector2d pp(1e-8 / 3.0, 1e-8 / 3.0);
|
||||||
|
|
||||||
EXPECT_TRUE(point_in_triangle_2d(pp, v0, v1, v2));
|
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)
|
TEST(CuttingUtility, IsInConvexTextureFace_UnitTriangle_InAndOut)
|
||||||
{
|
{
|
||||||
Eigen::Vector2d v0(0.0, 0.0), v1(1.0, 0.0), v2(0.0, 1.0);
|
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_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(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: UnwrapUtilityTest.testGetAngleReturnsPI
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// Java: v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) kollinear.
|
// Java: v0=(-1,0,0), v1=(0,0,0), v2=(1,0,0) collinear.
|
||||||
// Kante e von v2 nach v1. getAngle(e) = Winkel bei v1 = π.
|
// 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 → γ = π
|
// cos(γ_v1) = (a² + b² − c²) / (2ab) = (1 + 1 − 4) / 2 = −1 → γ = π
|
||||||
TEST(UnwrapUtility, GetAngle_CollinearVertices_ReturnsPI)
|
TEST(UnwrapUtility, GetAngle_CollinearVertices_ReturnsPI)
|
||||||
{
|
{
|
||||||
const double a = 1.0; // |v0 − v1|
|
const double a = 1.0; // |v0 − v1|
|
||||||
const double b = 1.0; // |v1 − v2|
|
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);
|
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);
|
double angle = std::acos(cos_angle);
|
||||||
EXPECT_NEAR(M_PI, angle, 1e-15);
|
EXPECT_NEAR(M_PI, angle, 1e-15);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gegenkontrolle: gleichseitiges Dreieck → Winkel = π/3
|
// Counter-check: equilateral triangle → angle = π/3
|
||||||
TEST(UnwrapUtility, GetAngle_EquilateralTriangle_ReturnsPiOver3)
|
TEST(UnwrapUtility, GetAngle_EquilateralTriangle_ReturnsPiOver3)
|
||||||
{
|
{
|
||||||
const double s = 1.0;
|
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
|
// Java: ConvergenceUtilityTests.testGetTextureCircumRadius
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
TEST(ConvergenceUtility, TextureCircumRadius_RightTriangle)
|
TEST(ConvergenceUtility, TextureCircumRadius_RightTriangle)
|
||||||
{
|
{
|
||||||
// A=(0,0), B=(1,0), C=(0,1): rechtwinkliges gleichschenkliges Dreieck
|
// A=(0,0), B=(1,0), C=(0,1): right isosceles triangle
|
||||||
// Seiten: 1, 1, √2. R = √2 / (4 · 0.5) = √2/2
|
// 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);
|
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);
|
EXPECT_NEAR(std::sqrt(2.0) / 2.0, circumradius_2d(A, B, C), 1e-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ConvergenceUtility, TextureCircumRadius_SmallerTriangle)
|
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}
|
// A=(0,0), B=(0.5,0.5), C=(0,1): Java variant with 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
|
// 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);
|
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);
|
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
|
// Java: ConvergenceUtilityTests.testGetTextureTriangleArea
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
TEST(ConvergenceUtility, TextureTriangleArea_RightTriangle)
|
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);
|
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);
|
EXPECT_NEAR(0.5, triangle_area_2d(A, B, C), 1e-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ConvergenceUtility, TextureTriangleArea_SmallerTriangle)
|
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);
|
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);
|
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
|
// Java: ConvergenceUtilityTests.testScaleInvariantCircumCircleRadius
|
||||||
//
|
//
|
||||||
// Mesh: 4 Vertices (v1..v4), 2 Flächen (f1: v1-v2-v3, f2: v1-v3-v4).
|
// Mesh: 4 vertices (v1..v4), 2 faces (f1: v1-v2-v3, f2: v1-v3-v4).
|
||||||
// Skaleninvariante Größe: R_f / sqrt(total_area) — invariant unter
|
// Scale-invariant quantity: R_f / sqrt(total_area) — invariant under
|
||||||
// uniformer Skalierung (homogeneous weight w: pos = (x/w, y/w)).
|
// uniform scaling (homogeneous weight w: pos = (x/w, y/w)).
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
TEST(ConvergenceUtility, ScaleInvariantCircumRadius_BaseScale)
|
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<Eigen::Vector2d> verts = {
|
std::vector<Eigen::Vector2d> verts = {
|
||||||
{0.0, 0.0}, // v1
|
{0.0, 0.0}, // v1
|
||||||
{1.0, 0.0}, // v2
|
{1.0, 0.0}, // v2
|
||||||
@@ -287,13 +287,13 @@ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_BaseScale)
|
|||||||
// f1: v1-v2-v3, f2: v1-v3-v4
|
// f1: v1-v2-v3, f2: v1-v3-v4
|
||||||
std::vector<std::array<int, 3>> faces = { {0, 1, 2}, {0, 2, 3} };
|
std::vector<std::array<int, 3>> 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[1], verts[2]), 1e-10);
|
||||||
EXPECT_NEAR(0.5, triangle_area_2d(verts[0], verts[2], verts[3]), 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);
|
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), max_r, 1e-10);
|
||||||
EXPECT_NEAR(std::sin(M_PI / 4.0), mean_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);
|
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)
|
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)
|
// pos_scaled = (T[0]/2, T[1]/2)
|
||||||
std::vector<Eigen::Vector2d> verts = {
|
std::vector<Eigen::Vector2d> verts = {
|
||||||
{0.0, 0.0}, // v1/2
|
{0.0, 0.0}, // v1/2
|
||||||
@@ -311,35 +311,35 @@ TEST(ConvergenceUtility, ScaleInvariantCircumRadius_HalvedByW2_SameResult)
|
|||||||
};
|
};
|
||||||
std::vector<std::array<int, 3>> faces = { {0, 1, 2}, {0, 2, 3} };
|
std::vector<std::array<int, 3>> 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[1], verts[2]), 1e-10);
|
||||||
EXPECT_NEAR(0.125, triangle_area_2d(verts[0], verts[2], verts[3]), 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);
|
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), max_r, 1e-10);
|
||||||
EXPECT_NEAR(std::sin(M_PI / 4.0), mean_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);
|
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: HomologyTest.testHomology
|
||||||
//
|
//
|
||||||
// Java-Test:
|
// Java test:
|
||||||
// CoHDS hds = TestUtility.readOBJ("brezel2.obj"); // Genus-2-Brezel-Fläche
|
// CoHDS hds = TestUtility.readOBJ("brezel2.obj"); // genus-2 pretzel surface
|
||||||
// List<Set<CoEdge>> paths = getGeneratorPaths(hds.getVertex(0), weightAdapter);
|
// List<Set<CoEdge>> 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");
|
// ConformalMesh mesh = load_mesh("code/data/obj/brezel2.obj");
|
||||||
// CutGraph cg = compute_cut_graph(mesh);
|
// CutGraph cg = compute_cut_graph(mesh);
|
||||||
// EXPECT_EQ(4u, cg.cut_edge_indices.size()); // 2g = 4
|
// EXPECT_EQ(4u, cg.cut_edge_indices.size()); // 2g = 4
|
||||||
// EXPECT_EQ(2, cg.genus);
|
// EXPECT_EQ(2, cg.genus);
|
||||||
//
|
//
|
||||||
// Mesh: V=2622, F=5248, E=7872, χ=−2, genus=2.
|
// 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)
|
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: EuclideanLayoutTest.testDoLayout
|
||||||
//
|
//
|
||||||
// Java-Test:
|
// Java test:
|
||||||
// Vector u = new SparseVector(n); // u = 0 (kein konformer Faktor)
|
// Vector u = new SparseVector(n); // u = 0 (no conformal factor)
|
||||||
// EuclideanLayout.doLayout(hds, fun, u);
|
// EuclideanLayout.doLayout(hds, fun, u);
|
||||||
// for (CoEdge e : hds.getEdges())
|
// for (CoEdge e : hds.getEdges())
|
||||||
// assertEquals(Pn.distanceBetween(s.P, t.P), Pn.distanceBetween(s.T, t.T), 1E-11);
|
// 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).
|
// Meaning: with u=0 the conformal factor is 0, so ℓ̃ = ℓ (no deformation).
|
||||||
// Das Layout muss die ursprünglichen 3D-Kantenlängen exakt reproduzieren.
|
// The layout must reproduce the original 3D edge lengths exactly.
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
TEST(EuclideanLayout, DoLayout_TetraFlat_EdgeLengthsPreserved)
|
TEST(EuclideanLayout, DoLayout_TetraFlat_EdgeLengthsPreserved)
|
||||||
@@ -414,18 +414,18 @@ TEST(EuclideanLayout, DoLayout_TetraFlat_EdgeLengthsPreserved)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
// Test 10 — EuclideanCyclicConvergenceTest: Newton auf cathead.obj
|
// Test 10 — EuclideanCyclicConvergenceTest: Newton on cathead.obj
|
||||||
// Java: EuclideanLayoutTest.testLayout02 (130-Werte-Regression auf cathead.heml)
|
// Java: EuclideanLayoutTest.testLayout02 (130-value regression on cathead.heml)
|
||||||
// EuclideanCyclicConvergenceTest.testEuclideanConvergence
|
// EuclideanCyclicConvergenceTest.testEuclideanConvergence
|
||||||
//
|
//
|
||||||
// Java-Test:
|
// Java test:
|
||||||
// EuclideanLayout.doLayout(hdsCat, fun, uCat);
|
// EuclideanLayout.doLayout(hdsCat, fun, uCat);
|
||||||
// for (CoVertex v : interior vertices)
|
// for (CoVertex v : interior vertices)
|
||||||
// assertEquals(2*PI, calculateAngleSum(v), 1E-6);
|
// assertEquals(2*PI, calculateAngleSum(v), 1E-6);
|
||||||
// for (CoEdge e : positiveEdges)
|
// for (CoEdge e : positiveEdges)
|
||||||
// assertEquals(fun.getNewLength(e, u), tLength, 1E-6);
|
// 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
|
// 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.
|
// depends on vertex ordering in the Java CoHDS — not portable directly.
|
||||||
// Instead we verify the same mathematical invariant: convergence + angle sums.
|
// 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: SphericalConvergenceTest.testSphericalConvergence
|
||||||
//
|
//
|
||||||
// Java-Test:
|
// Java test:
|
||||||
// FunctionalTest.createOctahedron(hds, aSet);
|
// FunctionalTest.createOctahedron(hds, aSet);
|
||||||
// // randomly perturb vertex radii (seed=1)
|
// // randomly perturb vertex radii (seed=1)
|
||||||
// prepareInvariantDataHyperbolicAndSpherical(functional, hds, aSet, u);
|
// prepareInvariantDataHyperbolicAndSpherical(functional, hds, aSet, u);
|
||||||
// optimizer.minimize(u, opt);
|
// optimizer.minimize(u, opt);
|
||||||
// for (CoVertex v) assertEquals(2*PI, sum of angles at v, 1E-8);
|
// 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,
|
// C++: regular octahedron (all vertices on S², no perturbation), spherical Newton,
|
||||||
// prüft Konvergenz + Restgradienten (≡ Winkeldefekt = 0 nach Konvergenz).
|
// checks convergence + residual gradients (≡ angle deficit = 0 after convergence).
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
TEST(SphericalLayout, SphericalTetrahedron_NewtonConverges_AngleSumsTwoPi)
|
TEST(SphericalLayout, SphericalTetrahedron_NewtonConverges_AngleSumsTwoPi)
|
||||||
|
|||||||
@@ -28,22 +28,22 @@ Java reference implementation: [github.com/varylab/conformallab](https://github.
|
|||||||
|
|
||||||
## geometry-central cross-reference *(optional comparison track)*
|
## geometry-central cross-reference *(optional comparison track)*
|
||||||
|
|
||||||
> Diese Referenzen beziehen sich auf eine alternative Implementierung desselben
|
> These references relate to an alternative implementation of the same
|
||||||
> mathematischen Problems. Sie sind keine Voraussetzung für conformallab++,
|
> mathematical problem. They are not prerequisites for conformallab++,
|
||||||
> aber relevant für Kreuz-Validierung und mögliche algorithmische Adoptionen
|
> but are relevant for cross-validation and possible algorithmic adoptions
|
||||||
> (→ GC-1/2/3 im Phasen-Roadmap, → Abschnitt 9 in `validation.md`).
|
> (→ 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. |
|
| **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 | Algorithmische Grundlage für `SignpostIntrinsicTriangulation` in geometry-central — relevant für GC-2 (optionales Pre-Conditioning). |
|
| **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:**
|
**Note on Springborn 2020:**
|
||||||
Das Papier *"Ideal Hyperbolic Polyhedra and Discrete Uniformization"*
|
The paper *"Ideal Hyperbolic Polyhedra and Discrete Uniformization"*
|
||||||
(Springborn, Discrete & Computational Geometry 2020) ist **in conformallab++
|
(Springborn, Discrete & Computational Geometry 2020) is **already implemented in
|
||||||
bereits implementiert** — es ist die direkte Referenz für den HyperIdeal-Geometriemodus
|
conformallab++** — it is the direct reference for the HyperIdeal geometry mode
|
||||||
(`hyper_ideal_geometry.hpp`). Die geometry-central Implementierung (Gillespie 2021)
|
(`hyper_ideal_geometry.hpp`). The geometry-central implementation (Gillespie 2021)
|
||||||
baut auf diesem Papier auf und ergänzt es um Ptolemäische Flips.
|
builds on this paper and augments it with Ptolemaic flips.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -193,66 +193,58 @@ These are the **holonomy consistency** checks implemented in `test_phase7.cpp`
|
|||||||
|
|
||||||
## 9 — Cross-validation with geometry-central *(optional / hypothetical)*
|
## 9 — Cross-validation with geometry-central *(optional / hypothetical)*
|
||||||
|
|
||||||
> **Hinweis:** Dieser Abschnitt beschreibt eine mögliche externe Kreuz-Validierung,
|
> **Note:** This section describes a possible external cross-validation that is not
|
||||||
> die keine Voraussetzung für die Korrektheit der Implementierung ist.
|
> a prerequisite for the correctness of the implementation.
|
||||||
> Sie ist interessant, weil geometry-central denselben mathematischen Kern
|
> It is of interest because geometry-central implements the same mathematical core
|
||||||
> implementiert (Gillespie, Springborn, Crane — SIGGRAPH 2021, aufbauend auf
|
> (Gillespie, Springborn, Crane — SIGGRAPH 2021, building on
|
||||||
> Springborn 2020), aber mit einer anderen algorithmischen Strategie
|
> Springborn 2020), but with a different algorithmic strategy
|
||||||
> (Ptolemäische Flips + intrinsische Triangulierungen statt Newton auf der
|
> (Ptolemaic flips + intrinsic triangulations instead of Newton on the
|
||||||
> Original-Triangulierung).
|
> 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 |
|
| u-vector (scale parameters) | `res.x` | `u` after Yamabe flow | ✓ after normalisation |
|
||||||
| UV-Koordinaten | `layout.uv[v]` | konforme Parametrisierung | ✓ bis auf Möbius-Transformation |
|
| UV coordinates | `layout.uv[v]` | conformal parameterisation | ✓ up to Möbius transformation |
|
||||||
| Gauss-Bonnet Defekt | `gauss_bonnet_sum()` | implizit via Krümmungsfluss | ✓ (analytisch identisch) |
|
| Gauss-Bonnet deficit | `gauss_bonnet_sum()` | implicit via curvature flow | ✓ (analytically identical) |
|
||||||
| Anzahl Newton-Iterationen | `res.iterations` | Yamabe-Schritte | ~ (anderer Algorithmus) |
|
| Number of Newton iterations | `res.iterations` | Yamabe steps | ~ (different algorithm) |
|
||||||
| Period-Matrix τ | `pd.tau_reduced` | **nicht vorhanden** | ✗ |
|
| Period matrix τ | `pd.tau_reduced` | **not available** | ✗ |
|
||||||
| Möbius-Holonomie | `hol.T_a, T_b` | **nicht vorhanden** | ✗ |
|
| Möbius holonomy | `hol.T_a, T_b` | **not available** | ✗ |
|
||||||
|
|
||||||
### Normalisierungsabgleich
|
### Normalisation alignment
|
||||||
|
|
||||||
Der u-Vektor in conformallab++ hat einen Freiheitsgrad (globale additive Konstante —
|
The u-vector in conformallab++ has one degree of freedom (global additive constant —
|
||||||
Eichfreiheit nach Pin-Fixierung). geometry-central kann eine andere Konvention nutzen.
|
gauge freedom after pin-fixing). geometry-central may use a different convention.
|
||||||
Vor dem Vergleich normalisieren:
|
Normalise before comparing:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// conformallab++: u zentrieren
|
// conformallab++: centre u
|
||||||
double mean_u = std::accumulate(x.begin(), x.end(), 0.0) / x.size();
|
double mean_u = std::accumulate(x.begin(), x.end(), 0.0) / x.size();
|
||||||
std::vector<double> x_norm(x.size());
|
std::vector<double> x_norm(x.size());
|
||||||
for (int i = 0; i < x.size(); ++i) x_norm[i] = x[i] - mean_u;
|
for (int i = 0; i < x.size(); ++i) x_norm[i] = x[i] - mean_u;
|
||||||
|
|
||||||
// Dann mit geometry-central u-Vektor (ebenfalls zentriert) vergleichen:
|
// Then compare with the geometry-central u-vector (also centred):
|
||||||
// max|x_norm[i] - gc_u[i]| < 1e-8 → identischer Konvergenzpunkt
|
// 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 |
|
| **Now (Phase 7)** | Manual comparison using the same `.off`/`.obj` test meshes |
|
||||||
| **Nach Phase 8** | Automatisiertes Vergleichsskript (Python oder separates C++-Binary) |
|
| **After Phase 8** | Automated comparison script (Python or separate C++ binary) |
|
||||||
| **Phase 10 (Forschung)** | Algorithmus-Vergleich: Newton vs. Ptolemäische Flips auf schwierigen Netzen |
|
| **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)
|
The Springborn 2020 paper ("Ideal Hyperbolic Polyhedra and Discrete Uniformization")
|
||||||
2. Gleiche Gauss-Bonnet-Zielkrümmungen (Θᵥ = 2π für alle v, geschlossene Fläche)
|
is **already implemented in conformallab++** — it is the mathematical foundation
|
||||||
3. u-Normalisierung abgeglichen (zentriert, gleiche Eichfixierung)
|
for the HyperIdeal geometry mode (Phase 2/3). The geometry-central implementation
|
||||||
4. Konvergenztoleranz synchronisiert (max. Gradientnorm < 1e-8)
|
is based on the extension by Gillespie, Springborn & Crane (2021), which uses the
|
||||||
|
same variational principle of Bobenko–Springborn 2004 but additionally applies
|
||||||
### Verbindung zur Literatur
|
Ptolemaic flips to improve the triangulation during optimisation — an idea not yet
|
||||||
|
implemented in conformallab++ (→ GC-2 in the phase roadmap).
|
||||||
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).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -97,58 +97,57 @@ Java features from `de.varylab.discreteconformal` not yet in C++:
|
|||||||
|
|
||||||
## ◼ Optional / Hypothetical — geometry-central Cross-Comparison
|
## ◼ Optional / Hypothetical — geometry-central Cross-Comparison
|
||||||
|
|
||||||
> **Status: keine geplante Phase — rein explorativ.**
|
> **Status: no planned phase — purely exploratory.**
|
||||||
> Diese Punkte sind keine Voraussetzung für Phase 8–10. Sie sind
|
> These items are not prerequisites for Phase 8–10. They are
|
||||||
> interessant, weil geometry-central (Keenan Crane, CMU) auf denselben
|
> of interest because geometry-central (Keenan Crane, CMU) is built on the same
|
||||||
> mathematischen Grundlagen wie conformallab++ aufbaut — insbesondere auf
|
> mathematical foundations as conformallab++ — in particular
|
||||||
> **Springborn 2020** und der direkten Weiterentwicklung durch
|
> **Springborn 2020** and its direct extension by
|
||||||
> **Gillespie, Springborn & Crane (SIGGRAPH 2021)**.
|
> **Gillespie, Springborn & Crane (SIGGRAPH 2021)**.
|
||||||
> Der entscheidende Unterschied: geometry-central löst dasselbe Problem
|
> The key difference: geometry-central solves the same problem
|
||||||
> (diskrete konforme Äquivalenz) mit **intrinsischen Triangulierungen +
|
> (discrete conformal equivalence) using **intrinsic triangulations +
|
||||||
> Ptolemäischen Flips**, während conformallab++ **Newton auf der
|
> Ptolemaic flips**, while conformallab++ applies **Newton on the
|
||||||
> Original-Triangulierung** anwendet.
|
> original triangulation**.
|
||||||
|
|
||||||
```
|
```
|
||||||
GC-1 [optional, jetzt möglich]
|
GC-1 [optional, possible now]
|
||||||
Mathematischer Output-Vergleich
|
Mathematical output comparison
|
||||||
→ gleiche Testnetze (cathead.obj, brezel.obj, torus_4x4.off) in
|
→ load the same test meshes (cathead.obj, brezel.obj, torus_4x4.off) into
|
||||||
beide Bibliotheken laden
|
both libraries
|
||||||
→ UV-Koordinaten, u-Vektor, Residualnorm vergleichen
|
→ compare UV coordinates, u-vector, residual norm
|
||||||
→ Normalisierungskonventionen abgleichen (u-Mittelwert, Skalierung)
|
→ align normalisation conventions (u mean, scaling)
|
||||||
Ziel: unabhängige Kreuz-Validierung der Konvergenzpunkte.
|
Goal: independent cross-validation of convergence points.
|
||||||
Aufwand: kleines Python/C++ Vergleichsskript, kein Bibliotheks-Umbau.
|
Effort: small Python/C++ comparison script, no library restructuring.
|
||||||
|
|
||||||
GC-2 [optional, sinnvoll nach Phase 8]
|
GC-2 [optional, useful after Phase 8]
|
||||||
Intrinsic Delaunay Pre-Conditioning
|
Intrinsic Delaunay pre-conditioning
|
||||||
→ Vor dem Newton-Solver: geometry-central SignpostIntrinsicTriangulation
|
→ before the Newton solver: apply geometry-central SignpostIntrinsicTriangulation
|
||||||
auf die Eingabe anwenden
|
to the input
|
||||||
→ Ptolemäische Flips konditionieren die Hessian-Matrix vor
|
→ Ptolemaic flips pre-condition the Hessian matrix
|
||||||
→ Hypothese: weniger Newton-Iterationen auf nicht-Delaunay-Eingaben
|
→ hypothesis: fewer Newton iterations on non-Delaunay inputs
|
||||||
→ Implementierbar als optionaler cmake-Flag: -DWITH_GC_PRECOND=ON
|
→ implementable as an optional cmake flag: -DWITH_GC_PRECOND=ON
|
||||||
Abhängigkeit: geometry-central als optionale externe Abhängigkeit
|
Dependency: geometry-central as an optional external dependency
|
||||||
(header-only Teile genügen für den Flip-Algorithmus).
|
(header-only parts suffice for the flip algorithm).
|
||||||
|
|
||||||
GC-3 [hypothetisch, Phase 10+ Forschung]
|
GC-3 [hypothetical, Phase 10+ research]
|
||||||
Ptolemäische Flip-basierter Solver als alternativer Backend
|
Ptolemaic flip-based solver as an alternative backend
|
||||||
→ Statt Newton: Ptolemäische Flips + penultimate-step Normalisierung
|
→ instead of Newton: Ptolemaic flips + penultimate-step normalisation
|
||||||
(Gillespie–Springborn–Crane 2021 Algorithmus)
|
(Gillespie–Springborn–Crane 2021 algorithm)
|
||||||
→ Vergleich: Konvergenzradius, Robustheit auf pathologischen Netzen,
|
→ comparison: convergence radius, robustness on pathological meshes,
|
||||||
numerische Stabilität auf hohen Genus-Flächen
|
numerical stability on high-genus surfaces
|
||||||
→ Für conformallab++ interessant, weil der Newton-Ansatz auf
|
→ relevant for conformallab++ because the Newton approach can become
|
||||||
stark nicht-Delaunay Netzen (z.B. nach Remeshing) instabil
|
unstable on strongly non-Delaunay meshes (e.g. after remeshing).
|
||||||
werden kann.
|
No implementation planned — conceptual note for Phase 10 research.
|
||||||
Keine Implementierung geplant — Konzeptnotiz für Phase 10-Forschung.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verbindung zur Literatur:**
|
**Connection to the literature:**
|
||||||
Das Springborn 2020-Papier ("Ideal Hyperbolic Polyhedra and Discrete
|
The Springborn 2020 paper ("Ideal Hyperbolic Polyhedra and Discrete
|
||||||
Uniformization") ist in conformallab++ als HyperIdeal-Geometriemodus
|
Uniformization") is already implemented in conformallab++ as the HyperIdeal
|
||||||
bereits implementiert (Phase 2/3). Die Gillespie–Springborn–Crane
|
geometry mode (Phase 2/3). The Gillespie–Springborn–Crane
|
||||||
2021-Erweiterung — die geometry-central implementiert — ergänzt dies um
|
2021 extension — implemented in geometry-central — augments this with
|
||||||
intrinsische Triangulierungen und macht den Algorithmus robust gegen
|
intrinsic triangulations and makes the algorithm robust against
|
||||||
schlechte Eingangs-Triangulierungen. Beide teilen denselben
|
poor input triangulations. Both share the same
|
||||||
mathematischen Kern (diskrete konforme Äquivalenz, Gauss–Bonnet,
|
mathematical core (discrete conformal equivalence, Gauss–Bonnet,
|
||||||
Variationsprinzip von Bobenko–Springborn 2004).
|
variational principle of Bobenko–Springborn 2004).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user