docs(doxygen): fix critical extraction bug; baseline 24% → 42% on public API
ROOT CAUSE FIX
The Doxyfile EXCLUDE_PATTERNS line contained `*/* 2.hpp` (note the
space — a stray glob from macOS-style "foo 2.hpp" duplicate files).
That pattern was silently matching ALL .hpp / .h files, so Doxygen was
indexing nothing under code/include/. The pre-existing 556 KB of HTML
output was effectively documenting only README.md, CLAUDE.md and a
small stub for std:: — not the C++ API at all.
After fixing the pattern (and properly escaping the space-prefixed
"foo 2.hpp / foo 2.h" macOS-dup patterns), Doxygen now extracts 141
compounds and emits 248 HTML pages from the public headers.
WHAT THIS PR ADDS
1. Doxyfile fix: correct EXCLUDE_PATTERNS; add GENERATE_XML for the
coverage measurement script; add MathJax for `$$...$$` math in
markdown; add the missing CGAL `\cgalParamNBegin/End/Description/
Default/...` aliases so CGAL-style param blocks render correctly.
2. New headers:
- code/include/CGAL/Conformal_map/doxygen_groups.h
defines `PkgConformalMap{,Ref,Concepts,NamedParameters}`,
resolving 17 prior "non-existing group" warnings.
- code/include/CGAL/Conformal_map/doxygen_namespaces.h
gives every namespace under `CGAL::` and `conformallab::` a
brief description.
3. New tool: scripts/doxygen-coverage.sh
Parses the XML output and reports % of public symbols (excluding
the `detail::` implementation namespaces by default) that have a
non-empty brief/detailed description. Supports `--list-undoc`
and `--threshold N` for CI integration.
4. Substantial docstring additions to the public CGAL headers:
`Conformal_map_traits.h`, `Discrete_circle_packing.h`,
`Discrete_inversive_distance.h`, `conformal_mesh.hpp`,
`Discrete_conformal_map.h` (Hyper_ideal_map_result fields).
5. Markdown housekeeping that the strict-warning Doxygen run surfaced:
tests.md (escape literal `#` in table cell),
locked-vs-flexible.md (broken section anchor),
overall_pipeline.md (replace `$$LaTeX$$` with inline-unicode math).
CURRENT NUMBERS
before: ~24% documented (public API; the prior "87%" claim was
based on the broken extraction)
after: 42% documented (165 of 396 public symbols)
warnings: 0 (was 27 spurious + a flood of bogus undocumented
warnings hidden by the buggy EXCLUDE pattern)
NEXT (in a follow-up commit on this branch)
The remaining 231 public symbols (mostly in `layout.hpp`,
`hyper_ideal_functional.hpp`, `spherical_functional.hpp`, the per-mode
functional/Hessian files) can be brought to ~100% with another pass of
short `///` brief descriptions. The coverage script is the gate; CI
can begin enforcing `--threshold 95` once the next pass lands.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
39
Doxyfile
39
Doxyfile
@@ -31,7 +31,15 @@ EXCLUDE_PATTERNS = */build*/* \
|
|||||||
*/deps/* \
|
*/deps/* \
|
||||||
*/.git/* \
|
*/.git/* \
|
||||||
*/test-reports/* \
|
*/test-reports/* \
|
||||||
*/* 2.hpp
|
*\ 2.hpp \
|
||||||
|
*\ 2.h
|
||||||
|
# Research-quality LaTeX notes use raw \sinh / \cosh / \frac / \beta /
|
||||||
|
# \cdot / \partial / \zeta macros which are valid LaTeX but unknown to
|
||||||
|
# Doxygen. These files are intended to be read as PDF or in a LaTeX-
|
||||||
|
# aware markdown viewer, not as Doxygen pages. Excluding them removes
|
||||||
|
# ~500 spurious "unknown command" warnings while keeping the .md files
|
||||||
|
# discoverable on GitHub.
|
||||||
|
EXCLUDE = doc/math/hyperideal-hessian-derivation.md
|
||||||
EXCLUDE_SYMBOLS = Eigen::* boost::* std::*
|
EXCLUDE_SYMBOLS = Eigen::* boost::* std::*
|
||||||
|
|
||||||
# Markdown filter: rewrites repo-relative links like [x](doc/api/tests.md)
|
# Markdown filter: rewrites repo-relative links like [x](doc/api/tests.md)
|
||||||
@@ -70,7 +78,7 @@ EXTENSION_MAPPING = h=C++ hpp=C++
|
|||||||
# ── Warnings ─────────────────────────────────────────────────────────────────
|
# ── Warnings ─────────────────────────────────────────────────────────────────
|
||||||
QUIET = NO
|
QUIET = NO
|
||||||
WARNINGS = YES
|
WARNINGS = YES
|
||||||
WARN_IF_UNDOCUMENTED = NO
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
WARN_IF_DOC_ERROR = YES
|
WARN_IF_DOC_ERROR = YES
|
||||||
WARN_IF_INCOMPLETE_DOC = YES
|
WARN_IF_INCOMPLETE_DOC = YES
|
||||||
WARN_NO_PARAMDOC = NO
|
WARN_NO_PARAMDOC = NO
|
||||||
@@ -80,6 +88,16 @@ WARN_LOGFILE = doc/doxygen/doxygen-warnings.log
|
|||||||
|
|
||||||
# ── HTML output ──────────────────────────────────────────────────────────────
|
# ── HTML output ──────────────────────────────────────────────────────────────
|
||||||
GENERATE_HTML = YES
|
GENERATE_HTML = YES
|
||||||
|
|
||||||
|
# MathJax — render LaTeX math in markdown ($...$ and $$...$$) and in
|
||||||
|
# code-comment `\f$ ... \f$` blocks via MathJax in the generated HTML.
|
||||||
|
# Required for the conformal-mapping math notation (\Theta, \omega, \tau,
|
||||||
|
# \mathbb{H}, …) in doc/architecture/overall_pipeline.md and the
|
||||||
|
# header docstrings.
|
||||||
|
USE_MATHJAX = YES
|
||||||
|
MATHJAX_VERSION = MathJax_3
|
||||||
|
MATHJAX_FORMAT = HTML-CSS
|
||||||
|
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3/es5/
|
||||||
HTML_OUTPUT = html
|
HTML_OUTPUT = html
|
||||||
HTML_FILE_EXTENSION = .html
|
HTML_FILE_EXTENSION = .html
|
||||||
HTML_COLORSTYLE = LIGHT
|
HTML_COLORSTYLE = LIGHT
|
||||||
@@ -100,7 +118,9 @@ SERVER_BASED_SEARCH = NO
|
|||||||
GENERATE_LATEX = NO
|
GENERATE_LATEX = NO
|
||||||
GENERATE_RTF = NO
|
GENERATE_RTF = NO
|
||||||
GENERATE_MAN = NO
|
GENERATE_MAN = NO
|
||||||
GENERATE_XML = NO
|
GENERATE_XML = YES
|
||||||
|
XML_OUTPUT = xml
|
||||||
|
XML_PROGRAMLISTING = NO
|
||||||
GENERATE_DOCBOOK = NO
|
GENERATE_DOCBOOK = NO
|
||||||
GENERATE_AUTOGEN_DEF = NO
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
GENERATE_PERLMOD = NO
|
GENERATE_PERLMOD = NO
|
||||||
@@ -130,3 +150,16 @@ ALIASES += "concept{1}=\xrefitem concept \"Concept\" \"Concepts\"
|
|||||||
ALIASES += "models{1}=\xrefitem models \"Models\" \"Models\" \1"
|
ALIASES += "models{1}=\xrefitem models \"Models\" \"Models\" \1"
|
||||||
ALIASES += "cgalRequires{1}=\par Requirements: \n\1"
|
ALIASES += "cgalRequires{1}=\par Requirements: \n\1"
|
||||||
ALIASES += "cgalParam{2}=\param \1 \2"
|
ALIASES += "cgalParam{2}=\param \1 \2"
|
||||||
|
# CGAL named-parameter block aliases — replicates the upstream
|
||||||
|
# ${CGAL}/Documentation/doc/Documentation/Doxyfile_common conventions
|
||||||
|
# so that \cgalParamNBegin{name} … \cgalParamNEnd blocks render as
|
||||||
|
# nested HTML lists in our Doxygen output.
|
||||||
|
ALIASES += "cgalNamedParamsBegin=<dl class=\"params\"><dt>Optional named parameters</dt><dd><table class=\"params\">"
|
||||||
|
ALIASES += "cgalNamedParamsEnd=</table></dd></dl>"
|
||||||
|
ALIASES += "cgalParamNBegin{1}=<tr><td class=\"paramname\"><code>\1</code></td><td>"
|
||||||
|
ALIASES += "cgalParamNEnd=</td></tr>"
|
||||||
|
ALIASES += "cgalParamDescription{1}=<b>Description:</b> \1<br/>"
|
||||||
|
ALIASES += "cgalParamType{1}=<b>Type:</b> \1<br/>"
|
||||||
|
ALIASES += "cgalParamDefault{1}=<b>Default:</b> \1<br/>"
|
||||||
|
ALIASES += "cgalParamPrecondition{1}=<b>Precondition:</b> \1<br/>"
|
||||||
|
ALIASES += "cgalParamExtra{1}=<i>\1</i><br/>"
|
||||||
|
|||||||
44
code/include/CGAL/Conformal_map/doxygen_groups.h
Normal file
44
code/include/CGAL/Conformal_map/doxygen_groups.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2024-2026 Tarik Moussa.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// This header contains only Doxygen \defgroup commands. It is included
|
||||||
|
// nowhere in the build; its sole purpose is to register the package's
|
||||||
|
// Doxygen group hierarchy so that `@ingroup Pkg...` references in the
|
||||||
|
// other public headers resolve cleanly.
|
||||||
|
|
||||||
|
#ifndef CGAL_CONFORMAL_MAP_DOXYGEN_GROUPS_H
|
||||||
|
#define CGAL_CONFORMAL_MAP_DOXYGEN_GROUPS_H
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\defgroup PkgConformalMap CGAL Discrete Conformal Map package
|
||||||
|
\brief Discrete conformal maps on triangulated surfaces — five DCE models
|
||||||
|
(Euclidean, Spherical, Hyper-Ideal, Circle-Packing Euclidean,
|
||||||
|
Inversive-Distance).
|
||||||
|
|
||||||
|
This package provides the C++ implementation of the variational discrete
|
||||||
|
conformal equivalence solvers from Springborn 2020, Bobenko/Pinkall/Springborn
|
||||||
|
2010, and Luo 2004, together with a CGAL-style named-parameter API.
|
||||||
|
|
||||||
|
See `doc/api/cgal-package.md` for the full design rationale.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\defgroup PkgConformalMapRef Reference manual
|
||||||
|
\ingroup PkgConformalMap
|
||||||
|
\brief Public C++ API: entry functions, traits, layout helpers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\defgroup PkgConformalMapConcepts Concepts
|
||||||
|
\ingroup PkgConformalMap
|
||||||
|
\brief C++ concepts and traits classes consumed by the entry functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\defgroup PkgConformalMapNamedParameters Named function parameters
|
||||||
|
\ingroup PkgConformalMap
|
||||||
|
\brief Package-specific named-parameter helpers in `CGAL::parameters::*`,
|
||||||
|
plus the pipe-operator chaining convention.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif // CGAL_CONFORMAL_MAP_DOXYGEN_GROUPS_H
|
||||||
88
code/include/CGAL/Conformal_map/doxygen_namespaces.h
Normal file
88
code/include/CGAL/Conformal_map/doxygen_namespaces.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) 2024-2026 Tarik Moussa.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Doxygen namespace documentation only — no declarations. Centralised
|
||||||
|
// here so that each namespace gets a single, consistent description in
|
||||||
|
// the generated HTML, regardless of which header is parsed first.
|
||||||
|
|
||||||
|
#ifndef CGAL_CONFORMAL_MAP_DOXYGEN_NAMESPACES_H
|
||||||
|
#define CGAL_CONFORMAL_MAP_DOXYGEN_NAMESPACES_H
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace CGAL
|
||||||
|
\brief Root namespace of the CGAL library; conformallab++ adds its
|
||||||
|
public entry points (`discrete_conformal_map_*`, `Conformal_map_traits`,
|
||||||
|
…) directly into this namespace, matching CGAL package conventions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace CGAL::Conformal_map
|
||||||
|
\brief Implementation-detail namespace for the Discrete Conformal Map
|
||||||
|
package. Users normally do not need to enter this namespace; all
|
||||||
|
public entry points are re-exported into `CGAL::`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace CGAL::Conformal_map::internal_np
|
||||||
|
\brief Tag types backing the package-local named-function parameters
|
||||||
|
(`vertex_curvature_map_t`, `gradient_tolerance_t`, `output_uv_map_t`, …).
|
||||||
|
Users invoke them via the helpers in `CGAL::parameters::*`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace CGAL::parameters
|
||||||
|
\brief CGAL named-function-parameter helpers — both upstream CGAL's and
|
||||||
|
the conformallab++ package extensions (`vertex_curvature_map(...)`,
|
||||||
|
`gradient_tolerance(...)`, `output_uv_map(...)`, `normalise_layout(...)`).
|
||||||
|
Also home of the pipe-operator chaining convention; see
|
||||||
|
`doc/tutorials/add-output-uv-map.md` §3.4.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace conformallab
|
||||||
|
\brief Core math/algorithm namespace of conformallab++. Holds the five
|
||||||
|
DCE functionals (Euclidean / Spherical / HyperIdeal / CP-Euclidean /
|
||||||
|
Inversive-Distance), the Newton solver, layout helpers, mesh-property
|
||||||
|
typedefs, and serialisation utilities. Lives under
|
||||||
|
`code/include/*.hpp` and is consumed both by the standalone CLI and
|
||||||
|
by the thin CGAL wrappers under `CGAL::`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace conformallab::detail
|
||||||
|
\brief Implementation-private helpers for the `conformallab` namespace.
|
||||||
|
Not part of the stable public API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace conformallab::cp_detail
|
||||||
|
\brief Implementation-private helpers for the Circle-Packing Euclidean
|
||||||
|
functional (see `cp_euclidean_functional.hpp`).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace conformallab::id_detail
|
||||||
|
\brief Implementation-private helpers for the Inversive-Distance
|
||||||
|
functional (see `inversive_distance_functional.hpp`).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace conformallab::detail_xml
|
||||||
|
\brief Implementation-private XML helpers for the (de)serialisation
|
||||||
|
layer (see `serialization.hpp`).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace mesh_utils
|
||||||
|
\brief Small, opinion-free mesh utilities (loaders, validators,
|
||||||
|
property-map registration) used by both the standalone tools and the
|
||||||
|
CGAL wrappers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\namespace viewer_utils
|
||||||
|
\brief libigl-based interactive viewer helpers; built only when
|
||||||
|
`WITH_VIEWER=ON`. Not part of the headless / CGAL public surface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif // CGAL_CONFORMAL_MAP_DOXYGEN_NAMESPACES_H
|
||||||
@@ -198,11 +198,8 @@ inline auto normalise_layout(bool flag)
|
|||||||
// operator on the same type.
|
// operator on the same type.
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
/*!
|
// Close the PkgConformalMapNamedParameters group block that was opened
|
||||||
\addtogroup PkgConformalMapNamedParameters
|
// above the helper functions (see \addtogroup at the top of this section).
|
||||||
\{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// \}
|
/// \}
|
||||||
|
|
||||||
} // namespace parameters
|
} // namespace parameters
|
||||||
|
|||||||
@@ -110,20 +110,33 @@ selected automatically when `TriangleMesh = CGAL::Surface_mesh<...>`.
|
|||||||
template <typename K>
|
template <typename K>
|
||||||
struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
||||||
{
|
{
|
||||||
|
/// The CGAL kernel parameter; defaults to `Simple_cartesian<double>`.
|
||||||
using Kernel = K;
|
using Kernel = K;
|
||||||
|
/// Field type used for all scalar conformal-map data (lengths, λ, Θ, …).
|
||||||
using FT = typename K::FT;
|
using FT = typename K::FT;
|
||||||
|
/// 3-D point type used for vertex coordinates.
|
||||||
using Point_3 = typename K::Point_3;
|
using Point_3 = typename K::Point_3;
|
||||||
|
/// The triangle-mesh type this specialisation targets.
|
||||||
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
||||||
|
|
||||||
|
/// Boost-graph vertex descriptor for `Triangle_mesh`.
|
||||||
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
||||||
|
/// Boost-graph half-edge descriptor for `Triangle_mesh`.
|
||||||
using Halfedge_descriptor = typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor;
|
using Halfedge_descriptor = typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor;
|
||||||
|
/// Boost-graph edge descriptor for `Triangle_mesh`.
|
||||||
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
||||||
|
/// Boost-graph face descriptor for `Triangle_mesh`.
|
||||||
using Face_descriptor = typename boost::graph_traits<Triangle_mesh>::face_descriptor;
|
using Face_descriptor = typename boost::graph_traits<Triangle_mesh>::face_descriptor;
|
||||||
|
|
||||||
// Property-map types — match the names used by setup_euclidean_maps().
|
// Property-map types — match the names used by setup_euclidean_maps().
|
||||||
|
|
||||||
|
/// Property map vertex → `Point_3` (the mesh's geometric embedding).
|
||||||
using Vertex_point_map = typename Triangle_mesh::template Property_map<Vertex_descriptor, Point_3>;
|
using Vertex_point_map = typename Triangle_mesh::template Property_map<Vertex_descriptor, Point_3>;
|
||||||
|
/// Property map vertex → target cone angle Θᵥ in radians (legacy name `ev:theta`).
|
||||||
using Theta_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
using Theta_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
||||||
|
/// Property map vertex → contiguous integer index (legacy name `ev:idx`).
|
||||||
using Vertex_index_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, int>;
|
using Vertex_index_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, int>;
|
||||||
|
/// Property map edge → log of original edge length λ⁰ (legacy name `ee:lam0`).
|
||||||
using Lambda0_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
using Lambda0_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
||||||
|
|
||||||
// ─── Property-map accessors ───────────────────────────────────────────
|
// ─── Property-map accessors ───────────────────────────────────────────
|
||||||
@@ -133,10 +146,12 @@ struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
|||||||
// calling either `setup_euclidean_maps(m)` first or the accessor first
|
// calling either `setup_euclidean_maps(m)` first or the accessor first
|
||||||
// is equivalent.
|
// is equivalent.
|
||||||
|
|
||||||
|
/// Return the built-in vertex-point map of `m` (the geometric embedding).
|
||||||
static Vertex_point_map vertex_points(Triangle_mesh& m) {
|
static Vertex_point_map vertex_points(Triangle_mesh& m) {
|
||||||
return m.points();
|
return m.points();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return (or create with default 2π) the target-angle property map.
|
||||||
static Theta_pmap theta_map(Triangle_mesh& m) {
|
static Theta_pmap theta_map(Triangle_mesh& m) {
|
||||||
auto [pm, created] = m.template add_property_map<Vertex_descriptor, FT>(
|
auto [pm, created] = m.template add_property_map<Vertex_descriptor, FT>(
|
||||||
"ev:theta", FT(2.0 * 3.141592653589793238));
|
"ev:theta", FT(2.0 * 3.141592653589793238));
|
||||||
@@ -144,6 +159,7 @@ struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
|||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return (or create with default −1) the vertex-index property map.
|
||||||
static Vertex_index_pmap vertex_index_map(Triangle_mesh& m) {
|
static Vertex_index_pmap vertex_index_map(Triangle_mesh& m) {
|
||||||
auto [pm, created] = m.template add_property_map<Vertex_descriptor, int>(
|
auto [pm, created] = m.template add_property_map<Vertex_descriptor, int>(
|
||||||
"ev:idx", -1);
|
"ev:idx", -1);
|
||||||
@@ -151,6 +167,7 @@ struct Default_conformal_map_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
|||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return (or create with default 0) the λ⁰ (initial log-length) property map.
|
||||||
static Lambda0_pmap lambda0_map(Triangle_mesh& m) {
|
static Lambda0_pmap lambda0_map(Triangle_mesh& m) {
|
||||||
auto [pm, created] = m.template add_property_map<Edge_descriptor, FT>(
|
auto [pm, created] = m.template add_property_map<Edge_descriptor, FT>(
|
||||||
"ee:lam0", FT(0));
|
"ee:lam0", FT(0));
|
||||||
|
|||||||
@@ -38,26 +38,51 @@ namespace CGAL {
|
|||||||
|
|
||||||
// ── Default traits for CP-Euclidean ───────────────────────────────────────────
|
// ── Default traits for CP-Euclidean ───────────────────────────────────────────
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgConformalMapConcepts
|
||||||
|
\brief Traits class for `discrete_circle_packing_euclidean()` —
|
||||||
|
declares the kernel, mesh and property-map types used by the
|
||||||
|
BPS-2010 face-based circle-packing functional.
|
||||||
|
|
||||||
|
Primary template; specialise it for non-`Surface_mesh` triangle meshes.
|
||||||
|
*/
|
||||||
template <typename TriangleMesh,
|
template <typename TriangleMesh,
|
||||||
typename Kernel_ = CGAL::Simple_cartesian<double>>
|
typename Kernel_ = CGAL::Simple_cartesian<double>>
|
||||||
struct Default_cp_euclidean_traits;
|
struct Default_cp_euclidean_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgConformalMapConcepts
|
||||||
|
\brief Specialisation for `CGAL::Surface_mesh<P>`; the only one shipped
|
||||||
|
in Phase 8b-Lite.
|
||||||
|
*/
|
||||||
template <typename K>
|
template <typename K>
|
||||||
struct Default_cp_euclidean_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
struct Default_cp_euclidean_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
||||||
{
|
{
|
||||||
|
/// CGAL kernel parameter (defaults to `Simple_cartesian<double>`).
|
||||||
using Kernel = K;
|
using Kernel = K;
|
||||||
|
/// Scalar field type used for all CP-Euclidean DOFs (`ρ_f`, `θ_e`, `φ_f`).
|
||||||
using FT = typename K::FT;
|
using FT = typename K::FT;
|
||||||
|
/// 3-D point type (vertex coordinates).
|
||||||
using Point_3 = typename K::Point_3;
|
using Point_3 = typename K::Point_3;
|
||||||
|
/// Triangle-mesh type this specialisation targets.
|
||||||
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
||||||
|
|
||||||
|
/// Boost-graph vertex descriptor for `Triangle_mesh`.
|
||||||
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
||||||
|
/// Boost-graph half-edge descriptor for `Triangle_mesh`.
|
||||||
using Halfedge_descriptor = typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor;
|
using Halfedge_descriptor = typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor;
|
||||||
|
/// Boost-graph edge descriptor for `Triangle_mesh`.
|
||||||
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
||||||
|
/// Boost-graph face descriptor for `Triangle_mesh`.
|
||||||
using Face_descriptor = typename boost::graph_traits<Triangle_mesh>::face_descriptor;
|
using Face_descriptor = typename boost::graph_traits<Triangle_mesh>::face_descriptor;
|
||||||
|
|
||||||
// CP-Euclidean property maps — note the *face* DOF index map.
|
// CP-Euclidean property maps — note the *face* DOF index map.
|
||||||
|
|
||||||
|
/// Property map face → contiguous integer DOF index (legacy `cf:idx`).
|
||||||
using Face_index_pmap = typename Triangle_mesh::template Property_map<Face_descriptor, int>;
|
using Face_index_pmap = typename Triangle_mesh::template Property_map<Face_descriptor, int>;
|
||||||
|
/// Property map edge → intersection angle θₑ (legacy `ce:theta`).
|
||||||
using Theta_e_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
using Theta_e_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
||||||
|
/// Property map face → target angle sum φ_f (legacy `cf:phi`).
|
||||||
using Phi_f_pmap = typename Triangle_mesh::template Property_map<Face_descriptor, FT>;
|
using Phi_f_pmap = typename Triangle_mesh::template Property_map<Face_descriptor, FT>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,8 +100,11 @@ struct Circle_packing_result
|
|||||||
/// Face DOFs `ρ_f = log R_f` (length = num_faces(mesh); pinned face = 0).
|
/// Face DOFs `ρ_f = log R_f` (length = num_faces(mesh); pinned face = 0).
|
||||||
std::vector<FT> rho_per_face;
|
std::vector<FT> rho_per_face;
|
||||||
|
|
||||||
|
/// Newton iterations actually performed (≤ `max_iterations`).
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
|
/// Final infinity-norm of the gradient (Newton stopping criterion).
|
||||||
FT gradient_norm = FT(0);
|
FT gradient_norm = FT(0);
|
||||||
|
/// `true` iff `gradient_norm < gradient_tolerance` at exit.
|
||||||
bool converged = false;
|
bool converged = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -449,8 +449,11 @@ struct Hyper_ideal_map_result
|
|||||||
/// Edge DOFs `a_e` (length = num_edges(mesh); pinned edges = 0).
|
/// Edge DOFs `a_e` (length = num_edges(mesh); pinned edges = 0).
|
||||||
std::vector<FT> a_per_edge;
|
std::vector<FT> a_per_edge;
|
||||||
|
|
||||||
|
/// Newton iterations actually performed (≤ `max_iterations`).
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
|
/// Final infinity-norm of the gradient (Newton stopping criterion).
|
||||||
FT gradient_norm = FT(0);
|
FT gradient_norm = FT(0);
|
||||||
|
/// `true` iff `gradient_norm < gradient_tolerance` at exit.
|
||||||
bool converged = false;
|
bool converged = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -47,25 +47,49 @@ namespace CGAL {
|
|||||||
|
|
||||||
// ── Default traits for Inversive-Distance ────────────────────────────────────
|
// ── Default traits for Inversive-Distance ────────────────────────────────────
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgConformalMapConcepts
|
||||||
|
\brief Traits class for `discrete_inversive_distance_map()` — declares
|
||||||
|
the kernel, mesh and property-map types used by Luo's 2004 vertex-based
|
||||||
|
inversive-distance circle packing.
|
||||||
|
|
||||||
|
Primary template; specialise it for non-`Surface_mesh` triangle meshes.
|
||||||
|
*/
|
||||||
template <typename TriangleMesh,
|
template <typename TriangleMesh,
|
||||||
typename Kernel_ = CGAL::Simple_cartesian<double>>
|
typename Kernel_ = CGAL::Simple_cartesian<double>>
|
||||||
struct Default_inversive_distance_traits;
|
struct Default_inversive_distance_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgConformalMapConcepts
|
||||||
|
\brief Specialisation for `CGAL::Surface_mesh<P>`; the only one shipped
|
||||||
|
in Phase 8b-Lite.
|
||||||
|
*/
|
||||||
template <typename K>
|
template <typename K>
|
||||||
struct Default_inversive_distance_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
struct Default_inversive_distance_traits<CGAL::Surface_mesh<typename K::Point_3>, K>
|
||||||
{
|
{
|
||||||
|
/// CGAL kernel parameter (defaults to `Simple_cartesian<double>`).
|
||||||
using Kernel = K;
|
using Kernel = K;
|
||||||
|
/// Scalar field type used for all inversive-distance DOFs.
|
||||||
using FT = typename K::FT;
|
using FT = typename K::FT;
|
||||||
|
/// 3-D point type (vertex coordinates).
|
||||||
using Point_3 = typename K::Point_3;
|
using Point_3 = typename K::Point_3;
|
||||||
|
/// Triangle-mesh type this specialisation targets.
|
||||||
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
using Triangle_mesh = CGAL::Surface_mesh<Point_3>;
|
||||||
|
|
||||||
|
/// Boost-graph vertex descriptor for `Triangle_mesh`.
|
||||||
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
using Vertex_descriptor = typename boost::graph_traits<Triangle_mesh>::vertex_descriptor;
|
||||||
|
/// Boost-graph edge descriptor for `Triangle_mesh`.
|
||||||
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
using Edge_descriptor = typename boost::graph_traits<Triangle_mesh>::edge_descriptor;
|
||||||
|
|
||||||
// Inversive-distance specific property maps.
|
// Inversive-distance specific property maps.
|
||||||
|
|
||||||
|
/// Property map vertex → contiguous integer DOF index (legacy `iv:idx`).
|
||||||
using Vertex_index_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, int>;
|
using Vertex_index_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, int>;
|
||||||
|
/// Property map vertex → target cone angle Θᵥ in radians (legacy `iv:theta`).
|
||||||
using Theta_v_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
using Theta_v_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
||||||
|
/// Property map vertex → initial radius r⁰ᵥ (legacy `iv:r0`).
|
||||||
using R0_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
using R0_pmap = typename Triangle_mesh::template Property_map<Vertex_descriptor, FT>;
|
||||||
|
/// Property map edge → inversive distance Iᵢⱼ (legacy `ie:I`).
|
||||||
using I_e_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
using I_e_pmap = typename Triangle_mesh::template Property_map<Edge_descriptor, FT>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,30 +41,42 @@ namespace conformallab {
|
|||||||
// ── Kernel ──────────────────────────────────────────────────────────────────
|
// ── Kernel ──────────────────────────────────────────────────────────────────
|
||||||
// Simple double-precision Cartesian. Conformal mapping algorithms never
|
// Simple double-precision Cartesian. Conformal mapping algorithms never
|
||||||
// need exact arithmetic — they operate on floating-point lengths and angles.
|
// 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>;
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
/// 3-D point type (vertex coordinates).
|
||||||
using Point3 = Kernel::Point_3;
|
using Point3 = Kernel::Point_3;
|
||||||
|
/// 2-D point type (UV-domain layout coordinates).
|
||||||
using Point2 = Kernel::Point_2;
|
using Point2 = Kernel::Point_2;
|
||||||
|
|
||||||
// ── Mesh type ────────────────────────────────────────────────────────────────
|
// ── Mesh type ────────────────────────────────────────────────────────────────
|
||||||
|
/// Triangle mesh carrying all conformal-map data as property maps.
|
||||||
using ConformalMesh = CGAL::Surface_mesh<Point3>;
|
using ConformalMesh = CGAL::Surface_mesh<Point3>;
|
||||||
|
|
||||||
// ── Index/descriptor aliases (CGAL 6.x naming) ───────────────────────────────
|
// ── Index/descriptor aliases (CGAL 6.x naming) ───────────────────────────────
|
||||||
|
/// Vertex descriptor of `ConformalMesh`.
|
||||||
using Vertex_index = ConformalMesh::Vertex_index;
|
using Vertex_index = ConformalMesh::Vertex_index;
|
||||||
|
/// Half-edge descriptor of `ConformalMesh`.
|
||||||
using Halfedge_index = ConformalMesh::Halfedge_index;
|
using Halfedge_index = ConformalMesh::Halfedge_index;
|
||||||
|
/// Edge descriptor of `ConformalMesh`.
|
||||||
using Edge_index = ConformalMesh::Edge_index;
|
using Edge_index = ConformalMesh::Edge_index;
|
||||||
|
/// Face descriptor of `ConformalMesh`.
|
||||||
using Face_index = ConformalMesh::Face_index;
|
using Face_index = ConformalMesh::Face_index;
|
||||||
|
|
||||||
// ── Geometry type constant (replaces Java CoFace.type enum) ─────────────────
|
// ── 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 {
|
enum class GeometryType : int {
|
||||||
Euclidean = 0,
|
Euclidean = 0, ///< Flat metric (ℝ²).
|
||||||
Hyperbolic = 1,
|
Hyperbolic = 1, ///< Hyperbolic metric (ℍ²).
|
||||||
Spherical = 2
|
Spherical = 2 ///< Spherical metric (S²).
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Standard property-map bundles ────────────────────────────────────────────
|
// ── Standard property-map bundles ────────────────────────────────────────────
|
||||||
|
|
||||||
// Add the vertex properties used by all conformal-map functionals.
|
/// Register and return the vertex-side property maps used by all five
|
||||||
// Returns {lambda, theta, idx}.
|
/// DCE functionals: `v:lambda` (log conformal factor), `v:theta` (target
|
||||||
|
/// cone angle), `v:idx` (contiguous integer index).
|
||||||
inline auto add_vertex_properties(ConformalMesh& mesh)
|
inline auto add_vertex_properties(ConformalMesh& mesh)
|
||||||
{
|
{
|
||||||
auto [lambda, ok1] = mesh.add_property_map<Vertex_index, double>("v:lambda", 0.0);
|
auto [lambda, ok1] = mesh.add_property_map<Vertex_index, double>("v:lambda", 0.0);
|
||||||
@@ -74,7 +86,8 @@ inline auto add_vertex_properties(ConformalMesh& mesh)
|
|||||||
return std::make_tuple(lambda, theta, idx);
|
return std::make_tuple(lambda, theta, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the edge intersection-angle property used by the hyperbolic functional.
|
/// Register and return the edge intersection-angle property `e:alpha`
|
||||||
|
/// (used by the hyper-ideal functional).
|
||||||
inline auto add_edge_properties(ConformalMesh& mesh)
|
inline auto add_edge_properties(ConformalMesh& mesh)
|
||||||
{
|
{
|
||||||
auto [alpha, ok] = mesh.add_property_map<Edge_index, double>("e:alpha", 0.0);
|
auto [alpha, ok] = mesh.add_property_map<Edge_index, double>("e:alpha", 0.0);
|
||||||
@@ -82,7 +95,7 @@ inline auto add_edge_properties(ConformalMesh& mesh)
|
|||||||
return alpha;
|
return alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the face geometry-type property.
|
/// Register and return the per-face geometry-type property `f:type`.
|
||||||
inline auto add_face_properties(ConformalMesh& mesh)
|
inline auto add_face_properties(ConformalMesh& mesh)
|
||||||
{
|
{
|
||||||
auto [ftype, ok] = mesh.add_property_map<Face_index, int>(
|
auto [ftype, ok] = mesh.add_property_map<Face_index, int>(
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ All tests have CTest prefix `cgal.` (set via `TEST_PREFIX "cgal."` in CMakeLists
|
|||||||
| `Normalisation` | `test_phase6.cpp` | 4 | Euclidean centroid, length ratios, Möbius centring |
|
| `Normalisation` | `test_phase6.cpp` | 4 | Euclidean centroid, length ratios, Möbius centring |
|
||||||
| `MobiusMap` | `test_phase7.cpp` | 8 | Identity, inverse, compose, `from_three`, `apply(Vector2d)` |
|
| `MobiusMap` | `test_phase7.cpp` | 8 | Identity, inverse, compose, `from_three`, `apply(Vector2d)` |
|
||||||
| `BestRootFace` | `test_phase7.cpp` | 2 | Valid root face selection, interior bonus |
|
| `BestRootFace` | `test_phase7.cpp` | 2 | Valid root face selection, interior bonus |
|
||||||
| `HalfedgeUV` | `test_phase7.cpp` | 4 | Size = #halfedges, seam consistency, boundary halfedges = 0 |
|
| `HalfedgeUV` | `test_phase7.cpp` | 4 | Size = number of half-edges, seam consistency, boundary half-edges = 0 |
|
||||||
| `PriorityBFS` | `test_phase7.cpp` | 3 | Success, no seam on open meshes, all vertices placed |
|
| `PriorityBFS` | `test_phase7.cpp` | 3 | Success, no seam on open meshes, all vertices placed |
|
||||||
| `NormaliseEuclidean` | `test_phase7.cpp` | 2 | UV centroid = 0, halfedge_uv centroid = 0 |
|
| `NormaliseEuclidean` | `test_phase7.cpp` | 2 | UV centroid = 0, halfedge_uv centroid = 0 |
|
||||||
| `PeriodMatrix` | `test_phase7.cpp` | 7 | τ ∈ ℍ, SL(2,ℤ) reduction, exception outside ℍ |
|
| `PeriodMatrix` | `test_phase7.cpp` | 7 | τ ∈ ℍ, SL(2,ℤ) reduction, exception outside ℍ |
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ the CGAL-canonical syntax AND we are willing to fork CGAL upstream.
|
|||||||
| Status | 🟢 opportunistic |
|
| Status | 🟢 opportunistic |
|
||||||
| Locked since | Phase 3 + 9a (prefix `ev:`/`sv:`/`v:`/`cf:`/`ce:`/`iv:`/`ie:` set when each functional was introduced) |
|
| Locked since | Phase 3 + 9a (prefix `ev:`/`sv:`/`v:`/`cf:`/`ce:`/`iv:`/`ie:` set when each functional was introduced) |
|
||||||
| Cost to change | One sed-replace + recompile. No user-visible effect because the names are an *internal* convention; the CGAL public API never exposes them. |
|
| Cost to change | One sed-replace + recompile. No user-visible effect because the names are an *internal* convention; the CGAL public API never exposes them. |
|
||||||
| When to revisit | If a future functional reuses an existing letter prefix. Already discussed in [`locked-vs-flexible.md`](#4-five-dce-models-on-the-same-mesh). |
|
| When to revisit | If a future functional reuses an existing letter prefix. Already discussed in §4 "Five DCE models on the same mesh" above. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ maps.theta_v[v] = M_PI / 3; // 60° cone singularity
|
|||||||
|
|
||||||
Before solving, the prescribed angles must satisfy:
|
Before solving, the prescribed angles must satisfy:
|
||||||
|
|
||||||
$$\sum_{v} (2\pi - \Theta_v) = 2\pi \cdot \chi(M)$$
|
> ∑ᵥ (2π − Θᵥ) = 2π · χ(M)
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
check_gauss_bonnet(mesh, maps); // throws if violated
|
check_gauss_bonnet(mesh, maps); // throws if violated
|
||||||
@@ -283,7 +283,7 @@ Both `uv` and `halfedge_uv` are transformed identically.
|
|||||||
From the two holonomy translations ω₁, ω₂ ∈ ℂ read off from the cut graph,
|
From the two holonomy translations ω₁, ω₂ ∈ ℂ read off from the cut graph,
|
||||||
the conformal type of a flat torus is the SL(2,ℤ)-orbit of:
|
the conformal type of a flat torus is the SL(2,ℤ)-orbit of:
|
||||||
|
|
||||||
$$\tau = \omega_2 / \omega_1 \in \mathbb{H}$$
|
> τ = ω₂ / ω₁ ∈ ℍ
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PeriodData pd = compute_period_matrix(hol);
|
PeriodData pd = compute_period_matrix(hol);
|
||||||
|
|||||||
159
scripts/doxygen-coverage.sh
Executable file
159
scripts/doxygen-coverage.sh
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/doxygen-coverage.sh
|
||||||
|
#
|
||||||
|
# Measure Doxygen documentation coverage of the public C++ API by parsing
|
||||||
|
# the Doxygen XML output. Reports:
|
||||||
|
# * total documentable members (functions, classes, structs, enums,
|
||||||
|
# typedefs, variables) in code/include/**
|
||||||
|
# * how many have a non-empty briefdescription/detaileddescription
|
||||||
|
# * coverage % and list of undocumented members
|
||||||
|
#
|
||||||
|
# Prerequisite: doxygen must have been run with GENERATE_XML=YES (which
|
||||||
|
# the project's Doxyfile sets). This script invokes it if XML is missing.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/doxygen-coverage.sh # short summary
|
||||||
|
# bash scripts/doxygen-coverage.sh --list-undoc # list undocumented members
|
||||||
|
# bash scripts/doxygen-coverage.sh --threshold 95 # fail if coverage < 95 %
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 coverage ≥ threshold (default 0 — informational only)
|
||||||
|
# 1 coverage < threshold
|
||||||
|
# 2 XML output missing / could not be parsed
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
XML_DIR="doc/doxygen/xml"
|
||||||
|
THRESHOLD=0
|
||||||
|
LIST_UNDOC=0
|
||||||
|
|
||||||
|
INCLUDE_DETAIL=0
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--threshold) THRESHOLD="$2"; shift 2 ;;
|
||||||
|
--list-undoc) LIST_UNDOC=1; shift ;;
|
||||||
|
--include-detail) INCLUDE_DETAIL=1; shift ;;
|
||||||
|
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -d "$XML_DIR" ]; then
|
||||||
|
echo "XML output missing — running doxygen..."
|
||||||
|
doxygen Doxyfile >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$XML_DIR" ]; then
|
||||||
|
echo "ERROR: $XML_DIR still missing after doxygen run" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$XML_DIR" "$LIST_UNDOC" "$THRESHOLD" "$INCLUDE_DETAIL" <<'PYEOF'
|
||||||
|
import sys, os, glob, xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
xml_dir, list_undoc, threshold, include_detail = \
|
||||||
|
sys.argv[1], int(sys.argv[2]), float(sys.argv[3]), int(sys.argv[4])
|
||||||
|
|
||||||
|
# Implementation-detail namespaces — not part of the public API surface.
|
||||||
|
# Skipped by default; pass --include-detail to count them too.
|
||||||
|
DETAIL_NAMES = ("::detail::", "::detail_xml::", "::cp_detail::", "::id_detail::",
|
||||||
|
"::detail$", "::detail_xml$", "::cp_detail$", "::id_detail$")
|
||||||
|
|
||||||
|
def is_detail(qualified_name: str) -> bool:
|
||||||
|
if include_detail:
|
||||||
|
return False
|
||||||
|
return any(qualified_name.find(d.rstrip("$")) >= 0 for d in DETAIL_NAMES)
|
||||||
|
|
||||||
|
# Restrict to compounds whose location is under code/include/ (the
|
||||||
|
# public API). XML output also includes README.md and CLAUDE.md as
|
||||||
|
# "file" kind compounds, which we want to skip.
|
||||||
|
PUBLIC_PREFIX = os.path.abspath("code/include") + os.sep
|
||||||
|
|
||||||
|
KINDS = {"function", "class", "struct", "enum", "typedef", "variable", "namespace"}
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
documented = 0
|
||||||
|
undoc = []
|
||||||
|
|
||||||
|
for path in sorted(glob.glob(os.path.join(xml_dir, "*.xml"))):
|
||||||
|
if os.path.basename(path) in {"index.xml", "Doxyfile.xml", "indexpage.xml"}:
|
||||||
|
continue
|
||||||
|
if os.path.basename(path).startswith(("namespacestd", "md_")):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
tree = ET.parse(path)
|
||||||
|
except ET.ParseError:
|
||||||
|
continue
|
||||||
|
for cd in tree.iter("compounddef"):
|
||||||
|
kind = cd.attrib.get("kind", "")
|
||||||
|
# only count compounds living in our public include tree
|
||||||
|
loc = cd.find("location")
|
||||||
|
if loc is None:
|
||||||
|
continue
|
||||||
|
file_attr = loc.attrib.get("file", "")
|
||||||
|
if not file_attr.startswith(PUBLIC_PREFIX) and \
|
||||||
|
not file_attr.startswith("code/include/"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# The compound itself
|
||||||
|
if kind in {"class", "struct", "namespace"}:
|
||||||
|
cname = cd.findtext("compoundname", "?")
|
||||||
|
if not is_detail(cname):
|
||||||
|
total += 1
|
||||||
|
brief = cd.find("briefdescription")
|
||||||
|
detail = cd.find("detaileddescription")
|
||||||
|
has_doc = (brief is not None and len("".join(brief.itertext()).strip()) > 0) or \
|
||||||
|
(detail is not None and len("".join(detail.itertext()).strip()) > 0)
|
||||||
|
if has_doc:
|
||||||
|
documented += 1
|
||||||
|
else:
|
||||||
|
undoc.append(f"{kind:9s} {cname} ({file_attr}:{loc.attrib.get('line','?')})")
|
||||||
|
|
||||||
|
# Members inside the compound
|
||||||
|
for memberdef in cd.iter("memberdef"):
|
||||||
|
mkind = memberdef.attrib.get("kind", "")
|
||||||
|
if mkind not in KINDS:
|
||||||
|
continue
|
||||||
|
prot = memberdef.attrib.get("prot", "public")
|
||||||
|
if prot != "public":
|
||||||
|
continue
|
||||||
|
name = memberdef.findtext("name", "?")
|
||||||
|
qual = memberdef.findtext("qualifiedname", name)
|
||||||
|
if is_detail(qual):
|
||||||
|
continue
|
||||||
|
total += 1
|
||||||
|
brief = memberdef.find("briefdescription")
|
||||||
|
detail = memberdef.find("detaileddescription")
|
||||||
|
has_doc = (brief is not None and len("".join(brief.itertext()).strip()) > 0) or \
|
||||||
|
(detail is not None and len("".join(detail.itertext()).strip()) > 0)
|
||||||
|
if has_doc:
|
||||||
|
documented += 1
|
||||||
|
else:
|
||||||
|
mloc = memberdef.find("location")
|
||||||
|
fl = mloc.attrib.get("file", "?") if mloc is not None else "?"
|
||||||
|
ln = mloc.attrib.get("line", "?") if mloc is not None else "?"
|
||||||
|
undoc.append(f"{mkind:9s} {qual} ({fl}:{ln})")
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
print("ERROR: no public members found — check that GENERATE_XML=YES and EXTRACT_ALL=YES")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
pct = 100.0 * documented / total
|
||||||
|
print(f"Doxygen coverage (public symbols under code/include/):")
|
||||||
|
print(f" documented: {documented}")
|
||||||
|
print(f" total: {total}")
|
||||||
|
print(f" coverage: {pct:.1f}%")
|
||||||
|
print(f" undocumented: {total - documented}")
|
||||||
|
|
||||||
|
if list_undoc:
|
||||||
|
print()
|
||||||
|
print("Undocumented symbols:")
|
||||||
|
for s in undoc:
|
||||||
|
print(f" {s}")
|
||||||
|
|
||||||
|
if pct < threshold:
|
||||||
|
print(f"\nFAIL: coverage {pct:.1f}% < threshold {threshold}%", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
PYEOF
|
||||||
Reference in New Issue
Block a user