First step of the Phase 8 Hybrid MVP. Adds a thin CGAL-conformant public
API layer over the existing implementation, validated by 7 acceptance
tests. Total CGAL test count: 183 (was 176), 0 skipped.
New public headers
──────────────────
* code/include/CGAL/Conformal_map_traits.h
- ConformalMapTraits concept documentation
- Default_conformal_map_traits<Surface_mesh<P>, K> specialisation
- Static property-map accessors: vertex_points, theta_map,
vertex_index_map, lambda0_map
* code/include/CGAL/Discrete_conformal_map.h
- User-facing entry: discrete_conformal_map_euclidean(mesh, np)
- Conformal_map_result<FT> struct (u, iter, ‖G‖, converged flags)
- Natural-theta default: x = 0 is the equilibrium when no Θ supplied
- Honours user-provided Θ via vertex_curvature_map named parameter
* code/include/CGAL/Conformal_map/internal/parameters.h
- 4 named-parameter tags in CGAL::Conformal_map::internal_np:
vertex_curvature_map, gradient_tolerance,
max_iterations, fixed_vertex_map
- User-facing helpers in CGAL::parameters::*
Tests (test_cgal_traits_mvp.cpp, 7 cases)
─────────────────────────────────────────
* DefaultTraitsTypes: compile-time type sanity (static_assert)
* AccessorsReuseExistingMaps: traits accessors return identical pmaps
* SingleTriangleConverges,
QuadStripConverges: end-to-end Euclidean wrapper passes
* MaxIterationsTakesEffect: named parameter is read
* GradientToleranceTakesEffect: tolerance override changes Newton end-state
* WrapperMatchesLegacyAPI: cross-API result equality at 1e-10
Architecture
────────────
3-layer wrapper as designed (doc/api/cgal-package.md):
Layer 1: code/include/*.hpp (existing algorithms, unchanged)
Layer 2: CGAL/Conformal_map/internal/ (adapter, parameter tags)
Layer 3: CGAL/Conformal_map_traits.h, CGAL/Discrete_conformal_map.h
(user-facing)
No algorithm duplication. Existing 176 + 36 tests untouched.
Next: Phase 9a (Inversive-Distance) as the second client of this API —
the real acceptance test for the trait design.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
165 lines
7.6 KiB
C++
165 lines
7.6 KiB
C++
// Copyright (c) 2024-2026 Tarik Moussa.
|
||
// SPDX-License-Identifier: MIT
|
||
//
|
||
// Package: conformallab++ / Discrete_conformal_map (Phase 8 MVP, 2026-05-19)
|
||
|
||
/*!
|
||
\file CGAL/Conformal_map_traits.h
|
||
\ingroup PkgConformalMapRef
|
||
|
||
Defines the `ConformalMapTraits` concept and the default model
|
||
`Default_conformal_map_traits<TriangleMesh, K>` for the package.
|
||
|
||
The concept lists the types and property maps that the discrete-conformal
|
||
algorithms require from any backing data structure. By templatising the
|
||
algorithms on this concept, the package can run on any CGAL halfedge
|
||
mesh — `Surface_mesh`, `Polyhedron_3`, OpenMesh-adapter, pmp — without
|
||
changes to the algorithm code.
|
||
|
||
For Phase 8 MVP only the `Surface_mesh` specialisation is provided
|
||
(specialisation 8a.1). A generic `FaceGraph` specialisation is on the
|
||
roadmap as 8a.2.
|
||
|
||
\sa `CGAL::Discrete_conformal_map`
|
||
\sa `CGAL::parameters::vertex_curvature_map`
|
||
*/
|
||
|
||
#ifndef CGAL_CONFORMAL_MAP_TRAITS_H
|
||
#define CGAL_CONFORMAL_MAP_TRAITS_H
|
||
|
||
#include <CGAL/Surface_mesh.h>
|
||
#include <CGAL/Simple_cartesian.h>
|
||
#include <boost/graph/graph_traits.hpp>
|
||
|
||
namespace CGAL {
|
||
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
// \cgalConcept
|
||
//
|
||
// \concept ConformalMapTraits
|
||
// \ingroup PkgConformalMapConcepts
|
||
//
|
||
// The concept `ConformalMapTraits` describes the requirements that any
|
||
// Traits model must fulfil for the Discrete_conformal_map package.
|
||
//
|
||
// \cgalHasModelsBegin
|
||
// \cgalHasModels{CGAL::Default_conformal_map_traits<TriangleMesh, K>}
|
||
// \cgalHasModelsEnd
|
||
//
|
||
// \section RequiredTypes Required types
|
||
//
|
||
// | Type | Description |
|
||
// |------|-------------|
|
||
// | `Triangle_mesh` | A model of CGAL `FaceGraph` + `HalfedgeGraph`. |
|
||
// | `Kernel` | A CGAL kernel; defaults to `Simple_cartesian<double>`. |
|
||
// | `FT` | Field type used internally (typically `double`). |
|
||
// | `Vertex_descriptor` | `boost::graph_traits<Triangle_mesh>::vertex_descriptor`. |
|
||
// | `Halfedge_descriptor` | analogously. |
|
||
// | `Edge_descriptor` | analogously. |
|
||
// | `Face_descriptor` | analogously. |
|
||
//
|
||
// \section RequiredProperties Required property-map accessors
|
||
//
|
||
// The Traits class is responsible for *locating* the property maps that
|
||
// the algorithm reads from and writes to. The semantics follow the
|
||
// project conventions (see `doc/api/contracts.md` for the full table):
|
||
//
|
||
// | Property | Key | Value | Access | Used by |
|
||
// |----------------------|-------------------------|-------|---------|---------|
|
||
// | `vertex_points(m)` | `Vertex_descriptor` | `Point_3` | Read | input geometry |
|
||
// | `theta_map(m)` | `Vertex_descriptor` | `FT` | RW | target cone angle Θᵥ |
|
||
// | `vertex_index_map(m)`| `Vertex_descriptor` | `int` | RW | DOF index (−1 = pinned) |
|
||
// | `lambda0_map(m)` | `Edge_descriptor` | `FT` | RW | base log-length λ°ᵢⱼ |
|
||
//
|
||
// Each accessor is a `static` member that returns the map; it must be
|
||
// idempotent (calling twice yields the same map by name lookup).
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
|
||
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
// Default_conformal_map_traits — primary template (undefined)
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
//
|
||
// The undefined primary template forces specialisation per mesh type.
|
||
// MVP provides only the `Surface_mesh` specialisation below; further
|
||
// mesh types (Polyhedron_3, OpenMesh, pmp) are deferred to Phase 8a.2.
|
||
|
||
template <typename TriangleMesh,
|
||
typename Kernel_ = CGAL::Simple_cartesian<double>>
|
||
struct Default_conformal_map_traits;
|
||
|
||
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
// Specialisation: CGAL::Surface_mesh<K::Point_3>
|
||
// ════════════════════════════════════════════════════════════════════════════
|
||
|
||
/*!
|
||
\ingroup PkgConformalMapRef
|
||
|
||
Default traits for `CGAL::Surface_mesh`. Wraps the property maps that
|
||
the existing implementation (`code/include/euclidean_functional.hpp`)
|
||
attaches to a Surface_mesh under the `"ev:idx"`, `"ev:theta"`,
|
||
`"ee:lam0"` etc. names.
|
||
|
||
This specialisation is the only one available in Phase 8 MVP. It is
|
||
selected automatically when `TriangleMesh = CGAL::Surface_mesh<...>`.
|
||
|
||
\tparam K Any CGAL kernel. Defaults to `Simple_cartesian<double>`,
|
||
which is what `conformal_mesh.hpp` uses today.
|
||
*/
|
||
template <typename K>
|
||
struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
||
{
|
||
using Kernel = K;
|
||
using FT = typename K::FT;
|
||
using Point_3 = typename K::Point_3;
|
||
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
||
|
||
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
||
using Halfedge_descriptor = typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor;
|
||
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
||
using Face_descriptor = typename boost::graph_traits<Triangle_mesh>::face_descriptor;
|
||
|
||
// Property-map types — match the names used by setup_euclidean_maps().
|
||
using Vertex_point_map = typename Triangle_mesh::template Property_map<Vertex_descriptor, Point_3>;
|
||
using Theta_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
||
using Vertex_index_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, int>;
|
||
using Lambda0_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
||
|
||
// ─── Property-map accessors ───────────────────────────────────────────
|
||
//
|
||
// Each accessor returns a property map under its canonical legacy name.
|
||
// If no such map exists yet it is created with sensible defaults — so
|
||
// calling either `setup_euclidean_maps(m)` first or the accessor first
|
||
// is equivalent.
|
||
|
||
static Vertex_point_map vertex_points(Triangle_mesh& m) {
|
||
return m.points();
|
||
}
|
||
|
||
static Theta_pmap theta_map(Triangle_mesh& m) {
|
||
auto [pm, created] = m.template add_property_map<Vertex_descriptor, FT>(
|
||
"ev:theta", FT(2.0 * 3.141592653589793238));
|
||
(void)created;
|
||
return pm;
|
||
}
|
||
|
||
static Vertex_index_pmap vertex_index_map(Triangle_mesh& m) {
|
||
auto [pm, created] = m.template add_property_map<Vertex_descriptor, int>(
|
||
"ev:idx", -1);
|
||
(void)created;
|
||
return pm;
|
||
}
|
||
|
||
static Lambda0_pmap lambda0_map(Triangle_mesh& m) {
|
||
auto [pm, created] = m.template add_property_map<Edge_descriptor, FT>(
|
||
"ee:lam0", FT(0));
|
||
(void)created;
|
||
return pm;
|
||
}
|
||
};
|
||
|
||
} // namespace CGAL
|
||
|
||
#endif // CGAL_CONFORMAL_MAP_TRAITS_H
|