feat(phase3a): introduce CGAL Surface_mesh as ConformalMesh foundation

Replaces the Java CoHDS with CGAL::Surface_mesh<Point3> (Simple_cartesian
kernel). Adds domain-specific property maps for lambda/theta/idx/alpha and
face geometry type — the direct C++ equivalent of CoVertex/CoEdge adapters.

New files:
  include/conformal_mesh.hpp   — ConformalMesh type + property-map helpers
  include/mesh_builder.hpp     — mesh factories (triangle, tetrahedron,
                                 quad-strip, fan) for tests and examples
  tests/cgal/                  — second test executable (conformallab_cgal_tests)
                                 built only with -DWITH_CGAL=ON

Test results (local, -DWITH_CGAL=ON):
  conformallab_tests:      36 registered | 23 passed | 13 skipped | 0 failed
  conformallab_cgal_tests: 14 registered | 14 passed |  0 skipped | 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-11 18:36:21 +02:00
parent ae5db7216e
commit bf9c323d60
5 changed files with 496 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
#pragma once
// conformal_mesh.hpp
//
// Central mesh type for the discrete conformal mapping algorithms.
// Replaces the Java CoHDS (de.varylab.discreteconformal.heds.CoHDS)
// and its associated vertex/edge/face types (CoVertex, CoEdge, CoFace).
//
// Design
// ------
// Java │ C++ (this file)
// ─────────────────────────────┼──────────────────────────────────────────
// CoHDS │ ConformalMesh (CGAL::Surface_mesh)
// CoVertex / CoEdge / CoFace │ Vertex_index / Edge_index / Face_index
// HyperIdealRadiusAdapter │ property_map<Vertex_index, double>
// HalfedgeInterface adapters │ named property maps ("v:lambda", …)
//
// Property-map naming convention
// ───────────────────────────────
// "v:lambda" per-vertex log scale factor (conformal variable u_i)
// "v:theta" per-vertex target cone angle
// "v:idx" per-vertex solver DOF index (-1 = pinned / boundary)
// "e:alpha" per-edge intersection angle (α_ij, hyperbolic geometry)
// "f:type" per-face geometry type (0=Euclidean, 1=Hyperbolic, 2=Spherical)
//
// All property maps are optional; add only what a given algorithm needs.
//
// Note on descriptor types (CGAL 6.x)
// ────────────────────────────────────
// CGAL::Surface_mesh exposes its index types as nested types:
// Surface_mesh::Vertex_index, ::Halfedge_index, ::Edge_index, ::Face_index
// The BGL graph_traits aliases expose the same types as vertex_descriptor etc.,
// but those live in boost::graph_traits<Surface_mesh>, not in Surface_mesh itself.
// We use the Surface_mesh member names throughout for clarity.
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <string>
namespace conformallab {
// ── Kernel ──────────────────────────────────────────────────────────────────
// Simple double-precision Cartesian. Conformal mapping algorithms never
// need exact arithmetic — they operate on floating-point lengths and angles.
using Kernel = CGAL::Simple_cartesian<double>;
using Point3 = Kernel::Point_3;
using Point2 = Kernel::Point_2;
// ── Mesh type ────────────────────────────────────────────────────────────────
using ConformalMesh = CGAL::Surface_mesh<Point3>;
// ── Index/descriptor aliases (CGAL 6.x naming) ───────────────────────────────
using Vertex_index = ConformalMesh::Vertex_index;
using Halfedge_index = ConformalMesh::Halfedge_index;
using Edge_index = ConformalMesh::Edge_index;
using Face_index = ConformalMesh::Face_index;
// ── Geometry type constant (replaces Java CoFace.type enum) ─────────────────
enum class GeometryType : int {
Euclidean = 0,
Hyperbolic = 1,
Spherical = 2
};
// ── Standard property-map bundles ────────────────────────────────────────────
// Add the vertex properties used by all conformal-map functionals.
// Returns {lambda, theta, idx}.
inline auto add_vertex_properties(ConformalMesh& mesh)
{
auto [lambda, ok1] = mesh.add_property_map<Vertex_index, double>("v:lambda", 0.0);
auto [theta, ok2] = mesh.add_property_map<Vertex_index, double>("v:theta", 0.0);
auto [idx, ok3] = mesh.add_property_map<Vertex_index, int> ("v:idx", -1);
(void)ok1; (void)ok2; (void)ok3;
return std::make_tuple(lambda, theta, idx);
}
// Add the edge intersection-angle property used by the hyperbolic functional.
inline auto add_edge_properties(ConformalMesh& mesh)
{
auto [alpha, ok] = mesh.add_property_map<Edge_index, double>("e:alpha", 0.0);
(void)ok;
return alpha;
}
// Add the face geometry-type property.
inline auto add_face_properties(ConformalMesh& mesh)
{
auto [ftype, ok] = mesh.add_property_map<Face_index, int>(
"f:type", static_cast<int>(GeometryType::Euclidean));
(void)ok;
return ftype;
}
} // namespace conformallab

View File

@@ -0,0 +1,109 @@
#pragma once
// mesh_builder.hpp
//
// Factory functions that build simple reference meshes for testing and examples.
// All functions return a ConformalMesh (CGAL::Surface_mesh<Point3>).
//
// Replaces Java mesh generators:
// CoHDS generators (convex hull, hyper-ideal generator) come later (Phase 3c/4).
// These builders cover the minimal meshes needed for functional unit tests.
#include "conformal_mesh.hpp"
#include <cmath>
#include <vector>
namespace conformallab {
// ── Single triangle ──────────────────────────────────────────────────────────
//
// v2
// | \
// | \
// v0 ─ v1
//
// Returns a mesh with 1 face, 3 vertices, 3 edges.
// The triangle lies in the xy-plane with a right angle at v0.
inline ConformalMesh make_triangle(
double x0=0, double y0=0,
double x1=1, double y1=0,
double x2=0, double y2=1)
{
ConformalMesh mesh;
auto v0 = mesh.add_vertex(Point3(x0, y0, 0));
auto v1 = mesh.add_vertex(Point3(x1, y1, 0));
auto v2 = mesh.add_vertex(Point3(x2, y2, 0));
mesh.add_face(v0, v1, v2);
return mesh;
}
// ── Regular tetrahedron ──────────────────────────────────────────────────────
//
// 4 vertices, 4 faces, 6 edges.
// Euler characteristic: V - E + F = 4 - 6 + 4 = 2 (sphere topology).
// Used to test closed-surface traversal.
inline ConformalMesh make_tetrahedron()
{
ConformalMesh mesh;
// Vertices of a regular tetrahedron centred at origin, edge length √2·2
auto v0 = mesh.add_vertex(Point3( 1, 1, 1));
auto v1 = mesh.add_vertex(Point3( 1, -1, -1));
auto v2 = mesh.add_vertex(Point3(-1, 1, -1));
auto v3 = mesh.add_vertex(Point3(-1, -1, 1));
// 4 outward-facing triangles (consistent winding)
mesh.add_face(v0, v2, v1); // bottom (z=-1 side)
mesh.add_face(v0, v1, v3); // front (y=-1 side)
mesh.add_face(v0, v3, v2); // left (x=-1 side)
mesh.add_face(v1, v2, v3); // back
return mesh;
}
// ── Two-triangle strip ───────────────────────────────────────────────────────
//
// v2 ─ v3
// | \ |
// v0 ─ v1
//
// 4 vertices, 2 faces, 5 edges (1 interior edge v1v2 shared by both faces).
// Useful for testing edge-interior vs edge-boundary distinction.
inline ConformalMesh make_quad_strip()
{
ConformalMesh mesh;
auto v0 = mesh.add_vertex(Point3(0, 0, 0));
auto v1 = mesh.add_vertex(Point3(1, 0, 0));
auto v2 = mesh.add_vertex(Point3(0, 1, 0));
auto v3 = mesh.add_vertex(Point3(1, 1, 0));
mesh.add_face(v0, v1, v2); // lower-left triangle
mesh.add_face(v1, v3, v2); // upper-right triangle (shares edge v1v2)
return mesh;
}
// ── Regular flat polygon fan ─────────────────────────────────────────────────
//
// n triangles sharing a central vertex; forms a disk topology (boundary).
// Used to verify valence-n vertex traversal.
inline ConformalMesh make_fan(int n)
{
CGAL_precondition(n >= 3);
ConformalMesh mesh;
auto center = mesh.add_vertex(Point3(0, 0, 0));
const double dtheta = 2.0 * M_PI / n;
std::vector<Vertex_index> rim(n);
for (int i = 0; i < n; ++i) {
double a = i * dtheta;
rim[i] = mesh.add_vertex(Point3(std::cos(a), std::sin(a), 0));
}
for (int i = 0; i < n; ++i)
mesh.add_face(center, rim[i], rim[(i+1) % n]);
return mesh;
}
} // namespace conformallab