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