MINOR-1 (spherical_functional.hpp:426)
Wrong comment said "second derivative < 0 for a convex functional".
The spherical energy is *concave* (NSD Hessian); the monotone-f argument
applies to both convex and concave functionals equally. Comment rewritten
to explain the actual physics: increasing scale increases all angles and
thus reduces Σ G_v.
MINOR-2 (spherical_functional.hpp:494-495)
Forward finite difference O(ε) → central finite difference O(ε²):
old: dft = (sum_Gv(t + fd_eps) - ft) / fd_eps
new: dft = (sum_Gv(t + fd_eps) - sum_Gv(t - fd_eps)) / (2*fd_eps)
Same cost when the extra sum_Gv(t - fd_eps) replaces the cached ft.
MINOR-3 (euclidean_functional.hpp, spherical_functional.hpp,
inversive_distance_functional.hpp)
New header gauss_legendre.hpp centralises the 10-point Gauss-Legendre
nodes and weights (gl10_nodes() / gl10_weights()). The three energy
functions now use the shared accessors instead of duplicated local
static arrays.
MINOR-4 (euclidean_functional.hpp, spherical_functional.hpp,
hyper_ideal_functional.hpp, inversive_distance_functional.hpp)
halfedge_to_index() centralised in conformal_mesh.hpp. All four local
aliases (eucl_hidx, spher_hidx, hidx, id_detail::hidx) now delegate to
it as one-line wrappers; the aliases are kept for now to avoid a larger
call-site churn, clearly documented as thin wrappers.
MINOR-5 (clausen.hpp:33-38)
Added a comment above inits() explaining the intentional off-by-one
return value and how it interacts with csevl() — matching the Java
Clausen.inits() / csevl() contract.
277/277 CGAL + 26/26 pure-math tests pass, 0 failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
131 lines
6.4 KiB
C++
131 lines
6.4 KiB
C++
#pragma once
|
||
// Copyright (c) 2024-2026 Tarik Moussa.
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
// 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 <cstdint>
|
||
#include <string>
|
||
|
||
namespace conformallab {
|
||
|
||
// ── Kernel ──────────────────────────────────────────────────────────────────
|
||
// Simple double-precision Cartesian. Conformal mapping algorithms never
|
||
// need exact arithmetic — they operate on floating-point lengths and angles.
|
||
|
||
/// CGAL kernel used by all conformallab algorithms (double precision).
|
||
using Kernel = CGAL::Simple_cartesian<double>;
|
||
/// 3-D point type (vertex coordinates).
|
||
using Point3 = Kernel::Point_3;
|
||
/// 2-D point type (UV-domain layout coordinates).
|
||
using Point2 = Kernel::Point_2;
|
||
|
||
// ── Mesh type ────────────────────────────────────────────────────────────────
|
||
/// Triangle mesh carrying all conformal-map data as property maps.
|
||
using ConformalMesh = CGAL::Surface_mesh<Point3>;
|
||
|
||
// ── Index/descriptor aliases (CGAL 6.x naming) ───────────────────────────────
|
||
/// Vertex descriptor of `ConformalMesh`.
|
||
using Vertex_index = ConformalMesh::Vertex_index;
|
||
/// Half-edge descriptor of `ConformalMesh`.
|
||
using Halfedge_index = ConformalMesh::Halfedge_index;
|
||
/// Edge descriptor of `ConformalMesh`.
|
||
using Edge_index = ConformalMesh::Edge_index;
|
||
/// Face descriptor of `ConformalMesh`.
|
||
using Face_index = ConformalMesh::Face_index;
|
||
|
||
// ── Geometry type constant (replaces Java CoFace.type enum) ─────────────────
|
||
/// Discrete geometry type that a face/mesh is interpreted in.
|
||
/// Replaces the original Java `CoFace.type` enum.
|
||
enum class GeometryType : int {
|
||
Euclidean = 0, ///< Flat metric (ℝ²).
|
||
Hyperbolic = 1, ///< Hyperbolic metric (ℍ²).
|
||
Spherical = 2 ///< Spherical metric (S²).
|
||
};
|
||
|
||
// ── Standard property-map bundles ────────────────────────────────────────────
|
||
|
||
/// Register and return the vertex-side property maps used by all five
|
||
/// DCE functionals: `v:lambda` (log conformal factor), `v:theta` (target
|
||
/// cone angle), `v:idx` (contiguous integer index).
|
||
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);
|
||
}
|
||
|
||
/// Register and return the edge intersection-angle property `e:alpha`
|
||
/// (used by the hyper-ideal 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;
|
||
}
|
||
|
||
/// Register and return the per-face geometry-type property `f:type`.
|
||
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;
|
||
}
|
||
|
||
// ── Half-edge index helper ────────────────────────────────────────────────────
|
||
//
|
||
// Convert a Halfedge_index to std::size_t for use as a vector subscript.
|
||
// Each functional previously duplicated this one-liner under a name like
|
||
// eucl_hidx / spher_hidx / hidx. Centralised here (MINOR-4 fix).
|
||
//
|
||
// Implementation: the double-cast uint32_t → size_t is intentional. CGAL 6.x
|
||
// Surface_mesh stores half-edge indices internally as 32-bit unsigned integers.
|
||
// Casting directly to size_t on a 64-bit system would produce the same result
|
||
// because CGAL index types use non-negative values, but the explicit intermediate
|
||
// cast documents the assumption and silences spurious sign-conversion warnings.
|
||
|
||
/// Convert a `Halfedge_index` to `std::size_t` for vector subscript use.
|
||
/// Replaces the duplicated `eucl_hidx`, `spher_hidx`, `hidx` helpers.
|
||
inline std::size_t halfedge_to_index(Halfedge_index h) noexcept
|
||
{
|
||
return static_cast<std::size_t>(static_cast<std::uint32_t>(h));
|
||
}
|
||
|
||
} // namespace conformallab
|