Phase 8a MVP: CGAL traits + Discrete_conformal_map.h Euclidean entry
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>
This commit is contained in:
164
code/include/CGAL/Conformal_map_traits.h
Normal file
164
code/include/CGAL/Conformal_map_traits.h
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user