MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Morphology.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Kinesis {
6
7// =============================================================================
8// PositionCarrying concept
9// =============================================================================
10
11/**
12 * @concept PositionCarrying
13 * @brief Satisfied by any type exposing a @c position field convertible to glm::vec3.
14 *
15 * Matched by all Kakshya vertex types (Vertex, PointVertex, LineVertex,
16 * MeshVertex, TextureQuadVertex), any user-defined struct following the same
17 * convention, and glm::vec3 itself when wrapped in a trivial aggregate.
18 *
19 * Functions constrained by PositionCarrying accept arbitrary point sets
20 * without specialisation: mesh vertices, point clouds, particle systems,
21 * Forma geometry, CV observations, or any future vertex kind.
22 */
23template <typename T>
24concept PositionCarrying = requires(const T& v) {
25 { v.position } -> std::convertible_to<glm::vec3>;
26};
27
28// =============================================================================
29// centroid
30// =============================================================================
31
32/**
33 * @brief Arithmetic centroid (uniform weight) of a PositionCarrying span.
34 *
35 * Computes the first geometric moment with measure 1 per point.
36 * Returns the zero vector for an empty span.
37 *
38 * @tparam T Any type satisfying PositionCarrying.
39 * @param pts Non-owning span of points.
40 * @return Mean position.
41 */
42template <PositionCarrying T>
43[[nodiscard]] glm::vec3 centroid(std::span<T> pts) noexcept
44{
45 if (pts.empty())
46 return glm::vec3(0.0F);
47 glm::vec3 acc(0.0F);
48 for (const auto& p : pts)
49 acc += static_cast<glm::vec3>(p.position);
50 return acc / static_cast<float>(pts.size());
51}
52
53/**
54 * @brief Scalar-weighted centroid of a PositionCarrying span.
55 *
56 * Each point contributes @p weight(point) to the measure. Suitable for
57 * mesh vertices weighted by deformation magnitude, particles weighted by
58 * energy (size / thickness field), or any domain-specific scalar.
59 *
60 * Falls back to the zero vector when total weight is zero or the span is empty.
61 *
62 * @tparam T Any type satisfying PositionCarrying.
63 * @tparam WeightFn Callable: @c const T& -> float (or any float-convertible type).
64 * @param pts Non-owning span of points.
65 * @param weight Per-point weight callable.
66 * @return Weighted mean position.
67 */
68template <PositionCarrying T, std::invocable<const T&> WeightFn>
69 requires std::convertible_to<std::invoke_result_t<WeightFn, const T&>, float>
70[[nodiscard]] glm::vec3 centroid(std::span<T> pts, WeightFn weight) noexcept
71{
72 if (pts.empty())
73 return glm::vec3(0.0F);
74 glm::vec3 acc(0.0F);
75 float total = 0.0F;
76 for (const auto& p : pts) {
77 const auto w = static_cast<float>(std::invoke(weight, p));
78 acc += static_cast<glm::vec3>(p.position) * w;
79 total += w;
80 }
81 return total > 0.0F ? acc / total : glm::vec3(0.0F);
82}
83
84/**
85 * @brief Fully explicit weighted centroid for arbitrary point types.
86 *
87 * Neither PositionCarrying nor any field convention is required. Both the
88 * position and the weight are extracted via caller-supplied callables. Use
89 * this overload for Nexus QueryResult spans, CV observation structs, Eigen
90 * column references, or any type that does not follow the vertex convention.
91 *
92 * Falls back to the zero vector when total weight is zero or the span is empty.
93 *
94 * @tparam T Element type. No concept constraint.
95 * @tparam PosFn Callable: @c const T& -> glm::vec3 (or convertible).
96 * @tparam WeightFn Callable: @c const T& -> float (or convertible).
97 * @param pts Non-owning span of elements.
98 * @param pos Position extractor.
99 * @param weight Weight extractor.
100 * @return Weighted mean position.
101 */
102template <typename T,
103 std::invocable<const T&> PosFn,
104 std::invocable<const T&> WeightFn>
105 requires std::convertible_to<std::invoke_result_t<PosFn, const T&>, glm::vec3>
106 && std::convertible_to<std::invoke_result_t<WeightFn, const T&>, float>
107[[nodiscard]] glm::vec3 centroid(std::span<T> pts, PosFn pos, WeightFn weight) noexcept
108{
109 if (pts.empty())
110 return glm::vec3(0.0F);
111 glm::vec3 acc(0.0F);
112 float total = 0.0F;
113 for (const auto& p : pts) {
114 const auto w = static_cast<float>(std::invoke(weight, p));
115 acc += static_cast<glm::vec3>(std::invoke(pos, p)) * w;
116 total += w;
117 }
118 return total > 0.0F ? acc / total : glm::vec3(0.0F);
119}
120
121// =============================================================================
122// aabb
123// =============================================================================
124
125/**
126 * @brief Axis-aligned bounding box of a PositionCarrying span.
127 *
128 * Returns AABB3D{zero, zero} for an empty span. No allocation; single
129 * linear pass.
130 *
131 * @tparam T Any type satisfying PositionCarrying.
132 * @param pts Non-owning span of points.
133 * @return Tightest AABB enclosing all positions in @p pts.
134 */
135template <PositionCarrying T>
136[[nodiscard]] AABB3D aabb(std::span<T> pts) noexcept
137{
138 if (pts.empty())
139 return AABB3D { .min = glm::vec3(0.0F), .max = glm::vec3(0.0F) };
140 constexpr float inf = std::numeric_limits<float>::max();
141 AABB3D box { .min = glm::vec3(inf), .max = glm::vec3(-inf) };
142 for (const auto& p : pts) {
143 const glm::vec3 q = static_cast<glm::vec3>(p.position);
144 box.min = glm::min(box.min, q);
145 box.max = glm::max(box.max, q);
146 }
147 return box;
148}
149
150} // namespace MayaFlux::Kinesis
double weight
double q
Satisfied by any type exposing a position field convertible to glm::vec3.
glm::vec3 centroid(std::span< T > pts) noexcept
Arithmetic centroid (uniform weight) of a PositionCarrying span.
AABB3D aabb(std::span< T > pts) noexcept
Axis-aligned bounding box of a PositionCarrying span.
Axis-aligned bounding box in 3D world space.
Definition Bounds.hpp:143