Phase 7.5: language unification + Doxygen + Phase 8 Hybrid MVP strategy #5
@@ -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
|
||||
|
||||
@@ -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 <typename Kernel>
|
||||
Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>>
|
||||
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
|
||||
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 ────────────────────────────────────────────────
|
||||
|
||||
@@ -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<double, 3> scale_invariant_circumradius_stats(
|
||||
const std::vector<Eigen::Vector2d>& verts,
|
||||
const std::vector<std::array<int, 3>>& 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<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
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// 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<Eigen::Vector2d> 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<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[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<Eigen::Vector2d> verts = {
|
||||
{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} };
|
||||
|
||||
// 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<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");
|
||||
// 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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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<double> 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).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user