MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
WindowContainer.cpp
Go to the documentation of this file.
1#include "WindowContainer.hpp"
2
11
13
14namespace MayaFlux::Kakshya {
15
16namespace {
17
18 /**
19 * @brief Map a MayaFlux surface format to the closest Portal ImageFormat.
20 *
21 * Packed A2B10G10R10 has no direct ImageFormat equivalent; it is widened
22 * to RGBA16F so TextureLoom can allocate a sampled image without data loss.
23 */
24 Portal::Graphics::ImageFormat surface_format_to_image_format(
26 {
29 switch (fmt) {
30 case SF::B8G8R8A8_SRGB:
31 return IF::BGRA8_SRGB;
32 case SF::B8G8R8A8_UNORM:
33 return IF::BGRA8;
34 case SF::R8G8B8A8_SRGB:
35 return IF::RGBA8_SRGB;
36 case SF::R8G8B8A8_UNORM:
37 return IF::RGBA8;
38 case SF::R16G16B16A16_SFLOAT:
39 case SF::A2B10G10R10_UNORM:
40 return IF::RGBA16F;
41 case SF::R32G32B32A32_SFLOAT:
42 return IF::RGBA32F;
43 default:
44 return IF::BGRA8_SRGB;
45 }
46 }
47
48} // namespace
49
50WindowContainer::WindowContainer(std::shared_ptr<Core::Window> window)
51 : m_window(std::move(window))
52{
53 if (!m_window) {
54 error<std::invalid_argument>(
57 std::source_location::current(),
58 "WindowContainer requires a valid window");
59 }
60
62
64 "WindowContainer created for window '{}' ({}x{})",
65 m_window->get_create_info().title,
67}
68
70{
71 return surface_format_to_image_format(query_surface_format(m_window));
72}
73
74// =========================================================================
75// Setup
76// =========================================================================
77
79{
80 const auto& fmt = m_window->get_create_info().container_format;
81 const uint32_t w = m_window->get_create_info().width;
82 const uint32_t h = m_window->get_create_info().height;
83 const uint32_t c = fmt.color_channels;
84
88 { static_cast<uint64_t>(h), static_cast<uint64_t>(w), static_cast<uint64_t>(c) },
90
91 const size_t sz = static_cast<size_t>(w) * h * c;
92 m_processed_data.resize(1);
93 m_processed_data[0] = std::vector<uint8_t>(sz, 0U);
94}
95
96// =========================================================================
97// NDDimensionalContainer
98// =========================================================================
99
100std::vector<DataDimension> WindowContainer::get_dimensions() const
101{
102 return m_structure.dimensions;
103}
104
109
114
119
120std::vector<DataVariant> WindowContainer::get_region_data(const Region& region) const
121{
122 std::shared_lock lock(m_data_mutex);
123
124 if (m_processed_data.empty())
125 return {};
126
127 const auto* src = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
128 if (!src || src->empty())
129 return {};
130
131 const auto& dims = m_structure.dimensions;
132 const std::span<const uint8_t> src_span { src->data(), src->size() };
133
134 std::vector<DataVariant> result;
135
136 for (const auto& [name, group] : m_region_groups) {
137 for (const auto& r : group.regions) {
138 if (!regions_intersect(r, region))
139 continue;
140 try {
141 result.emplace_back(extract_nd_region<uint8_t>(src_span, r, dims));
142 } catch (const std::exception& e) {
144 "WindowContainer::get_region_data extraction failed — {}", e.what());
145 }
146 }
147 }
148
149 return result;
150}
151
152std::shared_ptr<Core::VKImage> WindowContainer::to_image() const
153{
154 std::shared_lock lock(m_data_mutex);
155
156 if (m_processed_data.empty()) {
158 "WindowContainer::to_image — no readback data available for '{}'",
159 m_window->get_create_info().title);
160 return nullptr;
161 }
162
163 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
164 if (!pixels || pixels->empty()) {
166 "WindowContainer::to_image — processed_data[0] is not uint8_t or is empty for '{}'",
167 m_window->get_create_info().title);
168 return nullptr;
169 }
170
171 const auto fmt = query_surface_format(m_window);
172 const auto img_fmt = surface_format_to_image_format(fmt);
173 const uint32_t w = m_structure.get_width();
174 const uint32_t h = m_structure.get_height();
175
177 w, h, img_fmt, pixels->data());
178
179 if (!img) {
181 "WindowContainer::to_image — TextureLoom::create_2d failed for '{}'",
182 m_window->get_create_info().title);
183 }
184
185 return img;
186}
187
188std::shared_ptr<Core::VKImage> WindowContainer::to_image(
189 const std::shared_ptr<Buffers::VKBuffer>& staging) const
190{
191 std::shared_lock lock(m_data_mutex);
192
193 if (m_processed_data.empty()) {
195 "WindowContainer::to_image(staging) — no readback data for '{}'",
196 m_window->get_create_info().title);
197 return nullptr;
198 }
199
200 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
201 if (!pixels || pixels->empty()) {
203 "WindowContainer::to_image(staging) — processed_data[0] is not uint8_t or is empty for '{}'",
204 m_window->get_create_info().title);
205 return nullptr;
206 }
207
208 const auto fmt = query_surface_format(m_window);
209 const auto img_fmt = surface_format_to_image_format(fmt);
210 const uint32_t w = m_structure.get_width();
211 const uint32_t h = m_structure.get_height();
212
214
215 auto img = loom.create_2d(w, h, img_fmt, nullptr);
216 if (!img) {
218 "WindowContainer::to_image(staging) — VKImage allocation failed for '{}'",
219 m_window->get_create_info().title);
220 return nullptr;
221 }
222
223 loom.upload_data(img, pixels->data(), pixels->size(), staging);
224 return img;
225}
226
227std::shared_ptr<Core::VKImage> WindowContainer::region_to_image(const Region& region) const
228{
229 std::shared_lock lock(m_data_mutex);
230
231 if (m_processed_data.empty()) {
233 "WindowContainer::region_to_image — no readback data for '{}'",
234 m_window->get_create_info().title);
235 return nullptr;
236 }
237
238 const auto* src = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
239 if (!src || src->empty()) {
241 "WindowContainer::region_to_image — processed_data[0] is not uint8_t or is empty for '{}'",
242 m_window->get_create_info().title);
243 return nullptr;
244 }
245
246 std::vector<uint8_t> cropped;
247 try {
248 cropped = extract_region_data<uint8_t>(
249 std::span<const uint8_t> { src->data(), src->size() },
250 region,
252 } catch (const std::exception& e) {
254 "WindowContainer::region_to_image — crop failed for '{}': {}",
255 m_window->get_create_info().title, e.what());
256 return nullptr;
257 }
258
259 if (region.start_coordinates.size() < 2 || region.end_coordinates.size() < 2) {
261 "WindowContainer::region_to_image — region must have at least 2 coordinates (SPATIAL_Y, SPATIAL_X)");
262 return nullptr;
263 }
264
265 const auto rh = static_cast<uint32_t>(
266 region.end_coordinates[0] - region.start_coordinates[0] + 1);
267 const auto rw = static_cast<uint32_t>(
268 region.end_coordinates[1] - region.start_coordinates[1] + 1);
269
270 const auto fmt = query_surface_format(m_window);
271 const auto img_fmt = surface_format_to_image_format(fmt);
272
274 rw, rh, img_fmt, cropped.data());
275
276 if (!img) {
278 "WindowContainer::region_to_image — TextureLoom::create_2d failed ({}x{}) for '{}'",
279 rw, rh, m_window->get_create_info().title);
280 }
281
282 return img;
283}
284
285void WindowContainer::set_region_data(const Region& /*region*/, const std::vector<DataVariant>& /*data*/)
286{
288 "WindowContainer::set_region_data — write path not yet implemented");
289}
290
291std::vector<DataVariant> WindowContainer::get_region_group_data(const RegionGroup& /*group*/) const
292{
293 std::shared_lock lock(m_data_mutex);
294 return m_processed_data;
295}
296
297std::vector<DataVariant> WindowContainer::get_segments_data(const std::vector<RegionSegment>& /*segments*/) const
298{
299 std::shared_lock lock(m_data_mutex);
300 return m_processed_data;
301}
302
303double WindowContainer::get_value_at(const std::vector<uint64_t>& coordinates) const
304{
305 if (coordinates.size() < 3 || m_processed_data.empty())
306 return 0.0;
307
308 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
309 if (!pixels)
310 return 0.0;
311
312 const uint64_t w = m_structure.get_width();
313 const uint64_t c = m_structure.get_channel_count();
314 const uint64_t idx = (coordinates[0] * w + coordinates[1]) * c + coordinates[2];
315
316 if (idx >= pixels->size())
317 return 0.0;
318
319 return static_cast<double>((*pixels)[idx]) / 255.0;
320}
321
322void WindowContainer::set_value_at(const std::vector<uint64_t>& /*coords*/, double /*value*/) { }
323
324uint64_t WindowContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
325{
326 return coordinates_to_linear(coordinates, m_structure.dimensions);
327}
328
329std::vector<uint64_t> WindowContainer::linear_index_to_coordinates(uint64_t index) const
330{
332}
333
335{
336 std::unique_lock lock(m_data_mutex);
337 const size_t sz = m_structure.get_total_elements();
338 m_processed_data.resize(1);
339 m_processed_data[0] = std::vector<uint8_t>(sz, 0U);
341}
342
345bool WindowContainer::try_lock() { return m_data_mutex.try_lock(); }
346
348{
349 if (m_processed_data.empty())
350 return nullptr;
351 const auto* v = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
352 return (v && !v->empty()) ? v->data() : nullptr;
353}
354
356{
357 std::shared_lock lock(m_data_mutex);
358 if (m_processed_data.empty())
359 return false;
360 return std::visit([](const auto& v) { return !v.empty(); }, m_processed_data[0]);
361}
362
364{
366 "WindowContainer::load_region — no-op. Register regions via add_region_group()");
367}
368
370{
372 "WindowContainer::unload_region — no-op. Remove regions via remove_region_group()");
373}
374
375bool WindowContainer::is_region_loaded(const Region& /*region*/) const
376{
377 return true;
378}
379
380// =========================================================================
381// RegionGroup management
382// =========================================================================
383
385{
386 std::lock_guard lock(m_state_mutex);
387 m_region_groups[group.name] = group;
388}
389
390const RegionGroup& WindowContainer::get_region_group(const std::string& name) const
391{
392 static const RegionGroup empty;
393 std::shared_lock lock(m_data_mutex);
394 auto it = m_region_groups.find(name);
395 return it != m_region_groups.end() ? it->second : empty;
396}
397
398std::unordered_map<std::string, RegionGroup> WindowContainer::get_all_region_groups() const
399{
400 std::shared_lock lock(m_data_mutex);
401 return m_region_groups;
402}
403
404void WindowContainer::remove_region_group(const std::string& name)
405{
406 std::lock_guard lock(m_state_mutex);
407 m_region_groups.erase(name);
408}
409
410// =========================================================================
411// SignalSourceContainer
412// =========================================================================
413
418
420{
421 ProcessingState old = m_processing_state.exchange(new_state);
422 if (old == new_state)
423 return;
424
425 std::lock_guard lock(m_state_mutex);
427 m_state_callback(shared_from_this(), new_state);
428}
429
431 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback)
432{
433 std::lock_guard lock(m_state_mutex);
434 m_state_callback = std::move(callback);
435}
436
438{
439 std::lock_guard lock(m_state_mutex);
440 m_state_callback = nullptr;
441}
442
444{
445 return m_ready_for_processing.load(std::memory_order_acquire);
446}
447
449{
450 m_ready_for_processing.store(ready, std::memory_order_release);
451}
452
454{
455 auto readback = std::make_shared<WindowAccessProcessor>();
456 readback->on_attach(shared_from_this());
457 m_default_processor = readback;
458}
459
461{
463 m_default_processor->process(shared_from_this());
464}
465
466void WindowContainer::set_default_processor(const std::shared_ptr<DataProcessor>& proc)
467{
469 m_default_processor->on_detach(shared_from_this());
470 m_default_processor = proc;
472 m_default_processor->on_attach(shared_from_this());
473}
474
475std::shared_ptr<DataProcessor> WindowContainer::get_default_processor() const
476{
477 return m_default_processor;
478}
479
480std::shared_ptr<DataProcessingChain> WindowContainer::get_processing_chain()
481{
482 return m_processing_chain;
483}
484
485void WindowContainer::set_processing_chain(const std::shared_ptr<DataProcessingChain>& chain)
486{
487 m_processing_chain = chain;
488}
489
490// =========================================================================
491// Consumer tracking
492// =========================================================================
493
494uint32_t WindowContainer::register_dimension_reader(uint32_t /*slot_index*/)
495{
497 return m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
498}
499
501{
502 if (m_registered_readers.load(std::memory_order_relaxed) > 0)
504}
505
507{
508 return m_registered_readers.load(std::memory_order_acquire) > 0;
509}
510
511void WindowContainer::mark_dimension_consumed(uint32_t /*slot_index*/, uint32_t /*reader_id*/)
512{
513 m_consumed_readers.fetch_add(1, std::memory_order_release);
514}
515
517{
518 return m_consumed_readers.load(std::memory_order_acquire)
519 >= m_registered_readers.load(std::memory_order_acquire);
520}
521
522// =========================================================================
523// Data access
524// =========================================================================
525
526std::vector<DataVariant>& WindowContainer::get_processed_data()
527{
528 return m_processed_data;
529}
530
531const std::vector<DataVariant>& WindowContainer::get_processed_data() const
532{
533 return m_processed_data;
534}
535
536const std::vector<DataVariant>& WindowContainer::get_data()
537{
538 return m_data;
539}
540
542{
544 "WindowContainer::channel_data — not meaningful for interleaved image data; returning full surface");
546}
547
552
554{
556}
557
559{
560 return m_structure.get_height();
561}
562
563std::span<const double> WindowContainer::get_frame(uint64_t frame_index) const
564{
565 std::shared_lock lock(m_data_mutex);
566
567 const uint64_t h = m_structure.get_height();
568 if (frame_index >= h || m_processed_data.empty())
569 return {};
570
571 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
572 if (!pixels || pixels->empty())
573 return {};
574
575 const uint64_t w = m_structure.get_width();
576 const uint64_t c = m_structure.get_channel_count();
577 const uint64_t row_elems = w * c;
578 const uint64_t offset = frame_index * row_elems;
579
580 if (offset + row_elems > pixels->size())
581 return {};
582
583 auto& cache = m_frame_cache;
584 cache.resize(row_elems);
585 for (uint64_t i = 0; i < row_elems; ++i)
586 cache[i] = static_cast<double>((*pixels)[offset + i]) / 255.0;
587
588 return { cache.data(), cache.size() };
589}
590
591void WindowContainer::get_frames(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
592{
593 std::shared_lock lock(m_data_mutex);
594
595 const uint64_t h = m_structure.get_height();
596 if (start_frame >= h || output.empty() || m_processed_data.empty()) {
597 std::ranges::fill(output, 0.0);
598 return;
599 }
600
601 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
602 if (!pixels || pixels->empty()) {
603 std::ranges::fill(output, 0.0);
604 return;
605 }
606
607 const uint64_t w = m_structure.get_width();
608 const uint64_t c = m_structure.get_channel_count();
609 const uint64_t row_elems = w * c;
610 const uint64_t frames_to_copy = std::min(num_frames, h - start_frame);
611 const uint64_t elems_to_copy = std::min(frames_to_copy * row_elems,
612 static_cast<uint64_t>(output.size()));
613
614 const uint64_t src_offset = start_frame * row_elems;
615 for (uint64_t i = 0; i < elems_to_copy; ++i)
616 output[i] = static_cast<double>((*pixels)[src_offset + i]) / 255.0;
617
618 if (elems_to_copy < output.size())
619 std::fill(output.begin() + elems_to_copy, output.end(), 0.0);
620}
621
622} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
uint32_t h
Definition InkPress.cpp:25
const std::vector< float > * pixels
Type-erased accessor for NDData with semantic view construction.
uint64_t coordinates_to_linear_index(const std::vector< uint64_t > &coordinates) const override
Convert coordinates to linear index based on current memory layout.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
std::atomic< uint32_t > m_registered_readers
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_callback
std::atomic< ProcessingState > m_processing_state
void set_processing_chain(const std::shared_ptr< DataProcessingChain > &chain) override
Set the processing chain for this container.
void create_default_processor() override
Create and configure a default processor for this container.
DataAccess channel_data(size_t channel_index) override
Get channel data with semantic interpretation.
bool is_region_loaded(const Region &region) const override
Always returns true.
std::span< const double > get_frame(uint64_t frame_index) const override
Get a single frame of data efficiently.
Portal::Graphics::ImageFormat get_image_format() const
Portal ImageFormat corresponding to the live swapchain surface format.
void set_memory_layout(MemoryLayout layout) override
Set the memory layout for this container.
WindowContainer(std::shared_ptr< Core::Window > window)
Construct from an existing managed window.
void unregister_dimension_reader(uint32_t dimension_index) override
Unregister a reader for a specific dimension.
std::shared_ptr< Core::VKImage > to_image() const
Upload the full surface readback to a new VKImage.
void clear() override
Clear all data in the container.
void unregister_state_change_callback() override
Unregister the state change callback, if any.
std::shared_ptr< Core::Window > m_window
uint64_t get_total_elements() const override
Get the total number of elements in the container.
const void * get_raw_data() const override
Get a raw pointer to the underlying data storage.
std::shared_ptr< Core::VKImage > region_to_image(const Region &region) const
Crop a region from the last readback and upload it as a VKImage.
std::vector< DataDimension > get_dimensions() const override
Get the dimensions describing the structure of the data.
std::atomic< uint32_t > m_consumed_readers
const std::vector< DataVariant > & get_data() override
Get a reference to the raw data stored in the container.
void load_region(const Region &region) override
No-op.
void lock() override
Acquire a lock for thread-safe access.
std::vector< DataVariant > m_processed_data
uint64_t get_num_frames() const override
Get the number of frames in the primary (temporal) dimension.
const RegionGroup & get_region_group(const std::string &name) const override
Get a region group by name.
std::unordered_map< std::string, RegionGroup > m_region_groups
void mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id) override
Mark a dimension as consumed for the current processing cycle.
uint32_t register_dimension_reader(uint32_t dimension_index) override
Register a reader for a specific dimension.
std::unordered_map< std::string, RegionGroup > get_all_region_groups() const override
Get all region groups in the container.
void unlock() override
Release a previously acquired lock.
std::vector< DataVariant > m_data
void update_processing_state(ProcessingState new_state) override
Update the processing state of the container.
void mark_ready_for_processing(bool ready) override
Mark the container as ready or not ready for processing.
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
std::shared_ptr< DataProcessingChain > m_processing_chain
std::shared_ptr< DataProcessor > m_default_processor
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
bool has_data() const override
Check if the container currently holds any data.
void remove_region_group(const std::string &name) override
Remove a region group by name.
void set_default_processor(const std::shared_ptr< DataProcessor > &processor) override
Set the default data processor for this container.
MemoryLayout get_memory_layout() const override
Get the memory layout used by this container.
bool has_active_readers() const override
Check if any dimensions currently have active readers.
std::vector< DataVariant > get_segments_data(const std::vector< RegionSegment > &segments) const override
Get data for multiple region segments efficiently.
std::vector< DataVariant > get_region_data(const Region &region) const override
Extract data for all regions across all region groups that spatially intersect region.
void set_region_data(const Region &region, const std::vector< DataVariant > &data) override
Set data for a specific region.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
void unload_region(const Region &region) override
No-op.
std::vector< DataAccess > all_channel_data() override
Get all channel data as accessors.
bool all_dimensions_consumed() const override
Check if all active dimensions have been consumed in this cycle.
double get_value_at(const std::vector< uint64_t > &coordinates) const override
Get a single value at the specified coordinates.
void get_frames(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const override
Get multiple frames efficiently.
std::vector< DataVariant > & get_processed_data() override
Get a mutable reference to the processed data buffer.
void register_state_change_callback(std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> callback) override
Register a callback to be invoked on processing state changes.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
void process_default() override
Process the container's data using the default processor.
bool is_ready_for_processing() const override
Check if the container is ready for processing.
void set_value_at(const std::vector< uint64_t > &coordinates, double value) override
Set a single value at the specified coordinates.
std::vector< uint64_t > linear_index_to_coordinates(uint64_t linear_index) const override
Convert linear index to coordinates based on current memory layout.
bool try_lock() override
Attempt to acquire a lock without blocking.
std::atomic< uint32_t > m_next_reader_id
void add_region_group(const RegionGroup &group) override
Add a named group of regions to the container.
std::shared_ptr< Core::VKImage > create_2d(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr, uint32_t mip_levels=1)
Create a 2D texture.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
ProcessingState
Represents the current processing lifecycle state of a container.
@ IDLE
Container is inactive with no data or not ready for processing.
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
@ IMAGE_COLOR
2D RGB/RGBA image
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.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:39
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
bool regions_intersect(const Region &r1, const Region &r2) noexcept
Test whether two N-dimensional regions overlap on every shared axis.
Core::GraphicsSurfaceInfo::SurfaceFormat query_surface_format(const std::shared_ptr< Core::Window > &window)
Query the actual vk::Format in use by the window's live swapchain, translated back to the MayaFlux su...
ImageFormat
User-friendly image format enum.
SurfaceFormat
Default pixel format for window surfaces (Vulkan-compatible)
static uint64_t get_height(const std::vector< DataDimension > &dimensions)
Extract height from image/video dimensions.
static uint64_t get_channel_count(const std::vector< DataDimension > &dimensions)
Extract channel count from dimensions.
static size_t get_frame_size(const std::vector< DataDimension > &dimensions)
Extract the size of non time dimensions (channel, spatial, frequency)
static ContainerDataStructure image_interleaved()
Create structure for interleaved image data.
static uint64_t get_total_elements(const std::vector< DataDimension > &dimensions)
Get total elements across all dimensions.
static uint64_t get_width(const std::vector< DataDimension > &dimensions)
Extract width from image/video dimensions.
static std::vector< DataDimension > create_dimensions(DataModality modality, const std::vector< uint64_t > &shape, MemoryLayout layout=MemoryLayout::ROW_MAJOR)
Create dimension descriptors for a data modality.
Definition NDData.cpp:108
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
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