MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
EigenAccess.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "NDData.hpp"
4
6
7#include <Eigen/Dense>
8#include <glm/gtc/type_ptr.hpp>
9
10namespace MayaFlux::Kakshya {
11
12/**
13 * @class EigenAccess
14 * @brief Type-erased accessor for converting DataVariant to Eigen types
15 *
16 * Provides semantic, easy-to-follow conversion from NDData to Eigen.
17 * Companion to EigenInsertion for write operations.
18 *
19 * Design principle: All numeric types convert cleanly to Eigen's double representation.
20 * Complex types become 2-row matrices (real, imaginary).
21 */
22class MAYAFLUX_API EigenAccess {
23public:
24 explicit EigenAccess(const Kakshya::DataVariant& variant)
25 : m_variant(variant)
26 {
27 }
28
29 /**
30 * @brief Convert DataVariant to Eigen column vector
31 * @return Vector where each element is a sample
32 *
33 * For scalar data: direct conversion
34 * For complex data: magnitude by default (use to_matrix() for real/imag separation)
35 */
36 [[nodiscard]] Eigen::VectorXd to_vector() const;
37
38 /**
39 * @brief Convert DataVariant to Eigen matrix
40 * @return Matrix representation
41 *
42 * - Scalar types: 1xN matrix (single row)
43 * - Complex types: 2xN matrix (row 0 = real, row 1 = imag)
44 * - GLM vec2: 2xN matrix
45 * - GLM vec3: 3xN matrix
46 * - GLM vec4: 4xN matrix
47 * - GLM mat4: 16xN matrix (flattened)
48 */
49 [[nodiscard]] Eigen::MatrixXd to_matrix() const;
50
51 /**
52 * @brief Convert complex data to magnitude vector
53 * @return Vector of magnitudes (|z| = √(real² + imag²))
54 * @throws std::invalid_argument if not complex data
55 */
56 [[nodiscard]] Eigen::VectorXd to_magnitude_vector() const;
57
58 /**
59 * @brief Convert complex data to Eigen complex vector
60 * @return Complex vector (native Eigen representation)
61 * @throws std::invalid_argument if not complex data
62 */
63 [[nodiscard]] Eigen::VectorXcd to_complex_vector() const;
64
65 /**
66 * @brief Convert complex data to phase vector (angle in radians)
67 * @return Vector of phases (∠z = atan2(imag, real))
68 * @throws std::invalid_argument if not complex data
69 */
70 [[nodiscard]] Eigen::VectorXd to_phase_vector() const;
71
72 /**
73 * @brief Get number of elements (columns in matrix representation)
74 * @return Element count
75 */
76 [[nodiscard]] size_t element_count() const noexcept;
77
78 /**
79 * @brief Get number of components per element (rows in matrix representation)
80 * @return Component count (1 for scalar, 2 for complex/vec2, 3 for vec3, etc.)
81 */
82 [[nodiscard]] size_t component_count() const;
83
84 /**
85 * @brief Get matrix dimensions as (rows, cols)
86 * @return Pair of (component_count, element_count)
87 */
88 [[nodiscard]] std::pair<size_t, size_t> matrix_dimensions() const;
89
90 /**
91 * @brief Validate that data dimensions fit within Eigen limits
92 * @return true if valid
93 */
94 [[nodiscard]] bool validate_dimensions() const;
95
96 /**
97 * @brief Check if data contains complex numbers
98 * @return true if complex data
99 */
100 [[nodiscard]] bool is_complex() const;
101
102 /**
103 * @brief Check if data contains GLM types
104 * @return true if GLM structured data
105 */
106 [[nodiscard]] bool is_structured() const;
107
108 /**
109 * @brief Get underlying data type name
110 * @return String description of type
111 */
112 [[nodiscard]] std::string type_name() const;
113
114 /**
115 * @brief Get zero-copy Eigen::Map view of data
116 * @tparam EigenType Target Eigen type (VectorXd, VectorXf, VectorXcd, VectorXcf)
117 * @return Optional Eigen::Map pointing to variant's internal memory
118 *
119 * Returns std::nullopt if:
120 * - Type mismatch (e.g., vector<float> → VectorXd requires conversion)
121 * - Data is empty
122 *
123 * Supported zero-copy views:
124 * - vector<double> → Map<VectorXd>
125 * - vector<float> → Map<VectorXf>
126 * - vector<complex<double>> → Map<VectorXcd>
127 * - vector<complex<float>> → Map<VectorXcf>
128 */
129 template <typename EigenType>
130 auto view() const -> std::optional<Eigen::Map<const EigenType>>;
131
132 /**
133 * @brief Get zero-copy matrix view with explicit row count
134 * @tparam Scalar Element type (double or float)
135 * @param rows Number of rows in matrix interpretation
136 * @return Optional Eigen::Map as rowsxcols matrix (row-major)
137 *
138 * Returns std::nullopt if:
139 * - Type mismatch
140 * - Data size not divisible by rows
141 * - Data is empty
142 *
143 * Example: vector<double> with 12 elements, rows=3 → 3x4 matrix
144 */
145 template <typename Scalar = double>
146 auto view_as_matrix(Eigen::Index rows) const
147 -> std::optional<Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>>;
148
149 /**
150 * @brief Get zero-copy span view of GLM vector data
151 * @tparam GlmVecType GLM type (glm::vec2, glm::vec3, glm::vec4)
152 * @return Optional span pointing to variant's internal memory
153 *
154 * Returns std::nullopt if variant doesn't contain exactly GlmVecType
155 */
156 template <typename GlmVecType>
157 requires GlmType<GlmVecType>
158 auto view_as_glm() const -> std::optional<std::span<const GlmVecType>>;
159
160private:
161 const Kakshya::DataVariant& m_variant;
162
163 template <typename T>
164 Eigen::VectorXd scalar_to_vector(const std::vector<T>& vec) const
165 {
166 if (vec.empty()) {
167 return Eigen::VectorXd(0);
168 }
169 if (vec.size() > static_cast<size_t>(std::numeric_limits<Eigen::Index>::max())) {
170 error<std::overflow_error>(
171 Journal::Component::Kakshya,
172 Journal::Context::Runtime,
173 std::source_location::current(),
174 "Vector size {} exceeds Eigen::Index maximum {}",
175 vec.size(),
176 std::numeric_limits<Eigen::Index>::max());
177 }
178
179 Eigen::VectorXd result(vec.size());
180 for (Eigen::Index i = 0; i < vec.size(); ++i) {
181 result(i) = static_cast<double>(vec[i]);
182 }
183 return result;
184 }
185
186 template <typename T>
187 Eigen::MatrixXd scalar_to_matrix(const std::vector<T>& vec) const
188 {
189 Eigen::MatrixXd result(1, vec.size());
190 for (Eigen::Index i = 0; i < vec.size(); ++i) {
191 result(0, i) = static_cast<double>(vec[i]);
192 }
193 return result;
194 }
195
196 template <typename T>
197 Eigen::MatrixXd complex_to_matrix(const std::vector<std::complex<T>>& vec) const
198 {
199 Eigen::MatrixXd result(2, vec.size());
200 for (Eigen::Index i = 0; i < vec.size(); ++i) {
201 result(0, i) = static_cast<double>(vec[i].real());
202 result(1, i) = static_cast<double>(vec[i].imag());
203 }
204 return result;
205 }
206
207 template <typename T>
208 Eigen::MatrixXd glm_to_matrix(const std::vector<T>& vec) const
209 {
210 if constexpr (GlmVec2Type<T>) {
211 Eigen::MatrixXd result(2, vec.size());
212 for (Eigen::Index i = 0; i < vec.size(); ++i) {
213 result(0, i) = static_cast<double>(vec[i].x);
214 result(1, i) = static_cast<double>(vec[i].y);
215 }
216 return result;
217 } else if constexpr (GlmVec3Type<T>) {
218 Eigen::MatrixXd result(3, vec.size());
219 for (Eigen::Index i = 0; i < vec.size(); ++i) {
220 result(0, i) = static_cast<double>(vec[i].x);
221 result(1, i) = static_cast<double>(vec[i].y);
222 result(2, i) = static_cast<double>(vec[i].z);
223 }
224 return result;
225 } else if constexpr (GlmVec4Type<T>) {
226 Eigen::MatrixXd result(4, vec.size());
227 for (Eigen::Index i = 0; i < vec.size(); ++i) {
228 result(0, i) = static_cast<double>(vec[i].x);
229 result(1, i) = static_cast<double>(vec[i].y);
230 result(2, i) = static_cast<double>(vec[i].z);
231 result(3, i) = static_cast<double>(vec[i].w);
232 }
233 return result;
234 } else if constexpr (GlmMatrixType<T>) {
235 Eigen::MatrixXd result(16, vec.size());
236 for (Eigen::Index i = 0; i < vec.size(); ++i) {
237 const float* ptr = glm::value_ptr(vec[i]);
238 for (int j = 0; j < 16; ++j) {
239 result(j, i) = static_cast<double>(ptr[j]);
240 }
241 }
242 return result;
243 } else {
244 error<std::invalid_argument>(
245 Journal::Component::Kakshya,
246 Journal::Context::Runtime,
247 std::source_location::current(),
248 "Unknown GLM type: {}",
249 typeid(T).name());
250 }
251 }
252};
253
254/**
255 * @brief Convenience function for direct conversion
256 * @param variant Input DataVariant
257 * @return Eigen matrix representation
258 */
259inline Eigen::MatrixXd to_eigen_matrix(const Kakshya::DataVariant& variant)
260{
261 return EigenAccess(variant).to_matrix();
262}
263
264/**
265 * @brief Convenience function for vector conversion
266 * @param variant Input DataVariant
267 * @return Eigen vector representation
268 */
269inline Eigen::VectorXd to_eigen_vector(const Kakshya::DataVariant& variant)
270{
271 return EigenAccess(variant).to_vector();
272}
273
274template <typename EigenType>
275auto EigenAccess::view() const -> std::optional<Eigen::Map<const EigenType>>
276{
277 return std::visit([](const auto& vec) -> std::optional<Eigen::Map<const EigenType>> {
278 using T = typename std::decay_t<decltype(vec)>::value_type;
279
280 if (vec.empty()) {
281 return std::nullopt;
282 }
283
284 if constexpr (std::is_same_v<EigenType, Eigen::VectorXd>) {
285 if constexpr (std::is_same_v<T, double>) {
286 return Eigen::Map<const Eigen::VectorXd>(vec.data(), vec.size());
287 }
288 } else if constexpr (std::is_same_v<EigenType, Eigen::VectorXf>) {
289 if constexpr (std::is_same_v<T, float>) {
290 return Eigen::Map<const Eigen::VectorXf>(vec.data(), vec.size());
291 }
292 } else if constexpr (std::is_same_v<EigenType, Eigen::VectorXcd>) {
293 if constexpr (std::is_same_v<T, std::complex<double>>) {
294 return Eigen::Map<const Eigen::VectorXcd>(
295 reinterpret_cast<const std::complex<double>*>(vec.data()),
296 vec.size());
297 }
298 } else if constexpr (std::is_same_v<EigenType, Eigen::VectorXcf>) {
299 if constexpr (std::is_same_v<T, std::complex<float>>) {
300 return Eigen::Map<const Eigen::VectorXcf>(
301 reinterpret_cast<const std::complex<float>*>(vec.data()),
302 vec.size());
303 }
304 }
305
306 return std::nullopt;
307 },
308 m_variant);
309}
310
311template <typename Scalar>
312auto EigenAccess::view_as_matrix(Eigen::Index rows) const
313 -> std::optional<Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>>
314{
315 return std::visit([rows](const auto& vec)
316 -> std::optional<Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>> {
317 using T = typename std::decay_t<decltype(vec)>::value_type;
318
319 if (vec.empty()) {
320 return std::nullopt;
321 }
322
323 if constexpr (std::is_same_v<T, Scalar>) {
324 if (vec.size() % rows != 0) {
325 return std::nullopt;
326 }
327
328 Eigen::Index cols = vec.size() / rows;
329 return Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(
330 vec.data(), rows, cols);
331 }
332
333 return std::nullopt;
334 },
335 m_variant);
336}
337
338template <typename GlmVecType>
339 requires GlmType<GlmVecType>
340auto EigenAccess::view_as_glm() const -> std::optional<std::span<const GlmVecType>>
341{
342 return std::visit([](const auto& vec) -> std::optional<std::span<const GlmVecType>> {
343 using T = typename std::decay_t<decltype(vec)>::value_type;
344
345 if constexpr (std::is_same_v<T, GlmVecType>) {
346 return std::span<const GlmVecType>(vec.data(), vec.size());
347 }
348
349 return std::nullopt;
350 },
351 m_variant);
352}
353
354} // namespace MayaFlux::Kakshya
Eigen::VectorXd to_vector() const
Convert DataVariant to Eigen column vector.
auto view_as_glm() const -> std::optional< std::span< const GlmVecType > >
Get zero-copy span view of GLM vector data.
auto view_as_matrix(Eigen::Index rows) const -> std::optional< Eigen::Map< const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > >
Get zero-copy matrix view with explicit row count.
Eigen::MatrixXd to_matrix() const
Convert DataVariant to Eigen matrix.
EigenAccess(const Kakshya::DataVariant &variant)
auto view() const -> std::optional< Eigen::Map< const EigenType > >
Get zero-copy Eigen::Map view of data.
Eigen::MatrixXd scalar_to_matrix(const std::vector< T > &vec) const
Eigen::MatrixXd glm_to_matrix(const std::vector< T > &vec) const
const Kakshya::DataVariant & m_variant
Eigen::MatrixXd complex_to_matrix(const std::vector< std::complex< T > > &vec) const
Type-erased accessor for converting DataVariant to Eigen types.
Eigen::MatrixXd to_eigen_matrix(const Kakshya::DataVariant &variant)
Convenience function for direct conversion.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:73
Eigen::VectorXd to_eigen_vector(const Kakshya::DataVariant &variant)
Convenience function for vector conversion.