Files
ConformalLabpp/code/include/conformal_mesh.hpp
Tarik Moussa 59a26123c8 fix(minor): all five MINOR findings — doc, accuracy, and DRY
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>
2026-05-31 01:17:27 +02:00

131 lines
6.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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