MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
CoordUtils.cpp
Go to the documentation of this file.
1#include "CoordUtils.hpp"
3
5
6namespace MayaFlux::Kakshya {
7
8uint64_t coordinates_to_linear(const std::vector<uint64_t>& coords, const std::vector<DataDimension>& dimensions)
9{
10 uint64_t index = 0;
11 uint64_t multiplier = 1;
12 for (int i = dimensions.size() - 1; i >= 0; --i) {
13 if (i < coords.size()) {
14 index += coords[i] * multiplier;
15 }
16 multiplier *= dimensions[i].size;
17 }
18 return index;
19}
20
21std::vector<uint64_t> linear_to_coordinates(uint64_t index, const std::vector<DataDimension>& dimensions)
22{
23 std::vector<uint64_t> coords(dimensions.size());
24 for (int i = dimensions.size() - 1; i >= 0; --i) {
25 coords[i] = index % dimensions[i].size;
26 index /= dimensions[i].size;
27 }
28 return coords;
29}
30
31std::vector<uint64_t> calculate_strides(const std::vector<DataDimension>& dimensions)
32{
33 std::vector<uint64_t> strides(dimensions.size());
34 uint64_t stride = 1;
35 for (int i = dimensions.size() - 1; i >= 0; --i) {
36 strides[i] = stride;
37 stride *= dimensions[i].size;
38 }
39 return strides;
40}
41
42bool validate_region_bounds(const Region& region, const std::vector<DataDimension>& dimensions)
43{
44 if (region.start_coordinates.size() != dimensions.size() || region.end_coordinates.size() != dimensions.size()) {
45 return false;
46 }
47
48 for (size_t i = 0; i < dimensions.size(); ++i) {
49 if (region.start_coordinates[i] > region.end_coordinates[i] || region.end_coordinates[i] >= dimensions[i].size) {
50 return false;
51 }
52 }
53
54 return true;
55}
56
57bool validate_slice_bounds(const std::vector<uint64_t>& slice_start,
58 const std::vector<uint64_t>& slice_end,
59 const std::vector<DataDimension>& dimensions)
60{
61 if (slice_start.size() != dimensions.size() || slice_end.size() != dimensions.size()) {
62 return false;
63 }
64
65 for (size_t i = 0; i < dimensions.size(); ++i) {
66 if (slice_start[i] > slice_end[i] || slice_end[i] >= dimensions[i].size) {
67 return false;
68 }
69 }
70
71 return true;
72}
73
74void clamp_coordinates_to_bounds(std::vector<uint64_t>& coords,
75 const std::vector<DataDimension>& dimensions)
76{
77 for (size_t i = 0; i < coords.size() && i < dimensions.size(); ++i) {
78 coords[i] = std::min(coords[i], dimensions[i].size - 1);
79 }
80}
81
82std::vector<uint64_t> transform_coordinates(const std::vector<uint64_t>& coords,
83 const std::vector<double>& scale_factors,
84 const std::vector<int64_t>& offset_values,
85 const std::unordered_map<std::string, std::any>& rotation_params)
86{
87 std::vector<uint64_t> transformed = coords;
88
89 if (!scale_factors.empty()) {
90 for (size_t i = 0; i < coords.size() && i < scale_factors.size(); ++i) {
91 transformed[i] = static_cast<uint64_t>(scale_factors[i] * static_cast<double>(coords[i]));
92 }
93 }
94
95 if (!offset_values.empty()) {
96 for (size_t i = 0; i < transformed.size() && i < offset_values.size(); ++i) {
97 int64_t new_coord = static_cast<int64_t>(transformed[i]) + offset_values[i];
98 transformed[i] = static_cast<uint64_t>(std::max(int64_t(0), new_coord));
99 }
100 }
101
102 if (!rotation_params.empty() && coords.size() >= 2) {
103 auto angle_it = rotation_params.find("angle_radians");
104 if (angle_it != rotation_params.end()) {
105 try {
106 auto angle = safe_any_cast_or_throw<double>(angle_it->second);
107 double cos_a = std::cos(angle);
108 double sin_a = std::sin(angle);
109
110 auto x = static_cast<double>(transformed[0]);
111 auto y = static_cast<double>(transformed[1]);
112
113 transformed[0] = static_cast<uint64_t>(x * cos_a - y * sin_a);
114 transformed[1] = static_cast<uint64_t>(x * sin_a + y * cos_a);
115 } catch (const std::bad_any_cast&) {
116 // TODO: DO better than ignore invalid
118 "Invalid rotation angle parameter. Expected double, got {}", angle_it->second.type().name());
119 }
120 }
121 }
122
123 return transformed;
124}
125
126std::vector<uint64_t> wrap_position_with_loop(
127 const std::vector<uint64_t>& positions,
128 const Region& loop_region,
129 bool looping_enabled)
130{
131 if (!looping_enabled || loop_region.start_coordinates.size() < 2 || loop_region.end_coordinates.size() < 2) {
132 return positions;
133 }
134
135 uint64_t loop_start_frame = loop_region.start_coordinates[0];
136 uint64_t loop_start_channel = loop_region.start_coordinates[1];
137 uint64_t loop_end_frame = loop_region.end_coordinates[0];
138 uint64_t loop_end_channel = loop_region.end_coordinates[1];
139
140 if (loop_end_frame <= loop_start_frame) {
141 return positions;
142 }
143
144 std::vector<uint64_t> wrapped_positions = positions;
145 uint64_t loop_length = loop_end_frame - loop_start_frame + 1;
146
147 for (size_t ch = loop_start_channel; ch <= loop_end_channel && ch < positions.size(); ++ch) {
148 if (positions[ch] > loop_end_frame) {
149 uint64_t overflow = positions[ch] - loop_end_frame;
150 wrapped_positions[ch] = loop_start_frame + (overflow % loop_length);
151 }
152 }
153
154 return wrapped_positions;
155}
156
157std::vector<uint64_t> advance_position(
158 const std::vector<uint64_t>& current_positions,
159 uint64_t frames_to_advance,
160 const ContainerDataStructure& structure,
161 bool looping_enabled,
162 const Region& loop_region)
163{
164 uint64_t num_channels = structure.get_channel_count();
165 uint64_t total_frames = structure.get_samples_count_per_channel();
166
167 if (current_positions.size() != num_channels) {
168 error<std::invalid_argument>(Journal::Component::Kakshya, Journal::Context::Runtime, std::source_location::current(),
169 "Position vector size {} must match channel count {}", current_positions.size(), num_channels);
170 }
171
172 std::vector<uint64_t> new_positions;
173 new_positions.reserve(num_channels);
174
175 for (size_t ch = 0; ch < num_channels; ++ch) {
176 uint64_t current_frame = current_positions[ch];
177 uint64_t new_frame = current_frame + frames_to_advance;
178
179 if (new_frame >= total_frames) {
180 if (looping_enabled && !loop_region.start_coordinates.empty()) {
181 uint64_t loop_start = (ch < loop_region.start_coordinates.size())
182 ? loop_region.start_coordinates[ch]
183 : loop_region.start_coordinates[0];
184 uint64_t loop_end = (ch < loop_region.end_coordinates.size())
185 ? loop_region.end_coordinates[ch]
186 : loop_region.end_coordinates[0];
187
188 if (loop_end > loop_start) {
189 uint64_t loop_length = loop_end - loop_start + 1;
190 uint64_t overflow = new_frame - total_frames;
191 new_frame = loop_start + (overflow % loop_length);
192 } else {
193 new_frame = loop_start;
194 }
195 } else {
196 new_frame = total_frames - 1;
197 }
198 }
199
200 new_positions.push_back(new_frame);
201 }
202
203 return new_positions;
204}
205
206std::vector<uint64_t> advance_position(
207 const std::vector<uint64_t>& current_positions,
208 const std::vector<uint64_t>& frames_per_channel,
209 const ContainerDataStructure& structure,
210 bool looping_enabled,
211 const Region& loop_region)
212{
213 uint64_t num_channels = structure.get_channel_count();
214 uint64_t total_frames = structure.get_samples_count_per_channel();
215
216 if (current_positions.size() != num_channels || frames_per_channel.size() != num_channels) {
217 error<std::invalid_argument>(Journal::Component::Kakshya, Journal::Context::Runtime, std::source_location::current(),
218 "Position and frames vectors must match channel count {}. Got positions size {}, frames size {}",
219 num_channels, current_positions.size(), frames_per_channel.size());
220 }
221
222 std::vector<uint64_t> new_positions;
223 new_positions.reserve(num_channels);
224
225 for (size_t ch = 0; ch < num_channels; ++ch) {
226 uint64_t current_frame = current_positions[ch];
227 uint64_t frames_to_advance = frames_per_channel[ch];
228 uint64_t new_frame = current_frame + frames_to_advance;
229
230 if (new_frame >= total_frames) {
231 if (looping_enabled && !loop_region.start_coordinates.empty()) {
232 uint64_t loop_start = (ch < loop_region.start_coordinates.size())
233 ? loop_region.start_coordinates[ch]
234 : loop_region.start_coordinates[0];
235 uint64_t loop_end = (ch < loop_region.end_coordinates.size())
236 ? loop_region.end_coordinates[ch]
237 : loop_region.end_coordinates[0];
238
239 if (loop_end > loop_start) {
240 uint64_t loop_length = loop_end - loop_start + 1;
241 uint64_t overflow = new_frame - total_frames;
242 new_frame = loop_start + (overflow % loop_length);
243 } else {
244 new_frame = loop_start;
245 }
246 } else {
247 new_frame = total_frames - 1;
248 }
249 }
250
251 new_positions.push_back(new_frame);
252 }
253
254 return new_positions;
255}
256
257uint64_t time_to_position(double time, double sample_rate)
258{
259 return static_cast<uint64_t>(time * sample_rate);
260}
261
262double position_to_time(uint64_t position, double sample_rate)
263{
264 return static_cast<double>(position) / sample_rate;
265}
266
267uint64_t calculate_frame_size_for_dimension(const std::vector<DataDimension>& dimensions, size_t primary_dim)
268{
269 if (dimensions.empty() || primary_dim >= dimensions.size()) {
270 return 0;
271 }
272 uint64_t frame_size = 1;
273 for (size_t i = 0; i < dimensions.size(); ++i) {
274 if (i != primary_dim) {
275 frame_size *= dimensions[i].size;
276 }
277 }
278 return frame_size;
279}
280
281std::unordered_map<std::string, std::any> create_coordinate_mapping(const std::shared_ptr<SignalSourceContainer>& container)
282{
283 if (!container) {
284 error<std::invalid_argument>(Journal::Component::Kakshya, Journal::Context::Runtime, std::source_location::current(),
285 "Container is null");
286 }
287
288 std::unordered_map<std::string, std::any> mapping_info;
289 const auto dimensions = container->get_dimensions();
290
291 std::vector<std::unordered_map<std::string, std::any>> dim_mappings;
292 dim_mappings.reserve(dimensions.size());
293
294 for (size_t i = 0; i < dimensions.size(); ++i) {
295 std::unordered_map<std::string, std::any> dim_map;
296 dim_map["index"] = i;
297 dim_map["name"] = dimensions[i].name;
298 dim_map["size"] = dimensions[i].size;
299 dim_map["stride"] = dimensions[i].stride;
300 dim_map["role"] = static_cast<int>(dimensions[i].role);
301
302 uint64_t offset = (i == 0) ? 0ULL : std::accumulate(dimensions.begin(), dimensions.begin() + i, 1ULL, [](uint64_t acc, const auto& d) { return acc * d.size; });
303 dim_map["offset"] = offset;
304
305 dim_mappings.push_back(std::move(dim_map));
306 }
307
308 mapping_info["dimensions"] = std::move(dim_mappings);
309 mapping_info["total_elements"] = container->get_total_elements();
310 mapping_info["memory_layout"] = static_cast<int>(container->get_memory_layout());
311
312 auto strides = calculate_strides(dimensions);
313 mapping_info["calculated_strides"] = strides;
314
315 return mapping_info;
316}
317
318std::vector<int> extract_dimension_roles(const std::vector<DataDimension>& dimensions)
319{
320 std::vector<int> roles;
321 roles.reserve(dimensions.size());
322
323 for (const auto& dim : dimensions) {
324 roles.push_back(static_cast<int>(dim.role));
325 }
326
327 return roles;
328}
329
330std::vector<uint64_t> extract_dimension_sizes(const std::vector<DataDimension>& dimensions)
331{
332 std::vector<uint64_t> sizes;
333 sizes.reserve(dimensions.size());
334
335 for (const auto& dim : dimensions) {
336 sizes.push_back(dim.size);
337 }
338
339 return sizes;
340}
341
342std::vector<std::unordered_map<std::string, std::any>> create_dimension_info(const std::vector<DataDimension>& dimensions)
343{
344 std::vector<std::unordered_map<std::string, std::any>> dim_info;
345 dim_info.reserve(dimensions.size());
346
347 for (const auto& dim : dimensions) {
348 std::unordered_map<std::string, std::any> info;
349 info["name"] = dim.name;
350 info["size"] = dim.size;
351 info["stride"] = dim.stride;
352 info["role"] = static_cast<int>(dim.role);
353 dim_info.push_back(std::move(info));
354 }
355
356 return dim_info;
357}
358
359std::pair<size_t, uint64_t> coordinates_to_planar_indices(
360 const std::vector<uint64_t>& coords,
361 const std::vector<DataDimension>& dimensions)
362{
363 size_t channel_dim_idx = 0;
364 size_t time_dim_idx = 0;
365
366 for (size_t i = 0; i < dimensions.size(); ++i) {
367 if (dimensions[i].role == DataDimension::Role::CHANNEL) {
368 channel_dim_idx = i;
369 } else if (dimensions[i].role == DataDimension::Role::TIME) {
370 time_dim_idx = i;
371 }
372 }
373
374 return { coords[channel_dim_idx], coords[time_dim_idx] };
375}
376
377}
#define MF_ERROR(comp, ctx,...)
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
uint64_t coordinates_to_linear(const std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Convert N-dimensional coordinates to a linear index for interleaved data.
Definition CoordUtils.cpp:8
std::vector< std::unordered_map< std::string, std::any > > create_dimension_info(const std::vector< DataDimension > &dimensions)
Create structured dimension information.
void clamp_coordinates_to_bounds(std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Clamp coordinates to valid container bounds.
std::pair< size_t, uint64_t > coordinates_to_planar_indices(const std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Convert coordinates to planar indices (channel vector + frame index).
std::vector< int > extract_dimension_roles(const std::vector< DataDimension > &dimensions)
Extract dimension roles as integers.
bool validate_slice_bounds(const std::vector< uint64_t > &slice_start, const std::vector< uint64_t > &slice_end, const std::vector< DataDimension > &dimensions)
Validate slice coordinates against container bounds.
std::vector< uint64_t > advance_position(const std::vector< uint64_t > &current_positions, uint64_t frames_to_advance, const ContainerDataStructure &structure, bool looping_enabled, const Region &loop_region)
Advance current positions by a number of frames, with optional looping.
std::vector< uint64_t > calculate_strides(const std::vector< DataDimension > &dimensions)
Calculate memory strides for each dimension (row-major order).
std::vector< uint64_t > linear_to_coordinates(uint64_t index, const std::vector< DataDimension > &dimensions)
Convert a linear index to N-dimensional coordinates for interleaved data.
bool validate_region_bounds(const Region &region, const std::vector< DataDimension > &dimensions)
Validate region bounds against container dimensions.
double position_to_time(uint64_t position, double sample_rate)
Convert position (samples/frames) to time (seconds) given a sample rate.
uint64_t calculate_frame_size_for_dimension(const std::vector< DataDimension > &dimensions, size_t primary_dim)
Calculate the frame size for a specific primary dimension.
std::unordered_map< std::string, std::any > create_coordinate_mapping(const std::shared_ptr< SignalSourceContainer > &container)
Create coordinate mapping information for container.
std::vector< uint64_t > extract_dimension_sizes(const std::vector< DataDimension > &dimensions)
Extract dimension sizes.
uint64_t time_to_position(double time, double sample_rate)
Convert time (seconds) to position (samples/frames) given a sample rate.
std::vector< uint64_t > transform_coordinates(const std::vector< uint64_t > &coords, const std::vector< double > &scale_factors, const std::vector< int64_t > &offset_values, const std::unordered_map< std::string, std::any > &rotation_params)
Transform coordinates using scaling, translation, rotation.
std::vector< uint64_t > wrap_position_with_loop(const std::vector< uint64_t > &positions, const Region &loop_region, bool looping_enabled)
Wrap a position within loop boundaries if looping is enabled.
static uint64_t get_channel_count(const std::vector< DataDimension > &dimensions)
Extract channel count from dimensions.
static uint64_t get_samples_count_per_channel(const std::vector< DataDimension > &dimensions)
Get samples per channel (time dimension only).
Container structure for consistent dimension ordering.
@ TIME
Temporal progression (samples, frames, steps)
@ CHANNEL
Parallel streams (audio channels, color channels)
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:78
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:75
Represents a point or span in N-dimensional space.
Definition Region.hpp:73