MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PlotContainer.cpp
Go to the documentation of this file.
1#include "PlotContainer.hpp"
2
6
8
9namespace MayaFlux::Kakshya {
10
11namespace {
12 constexpr auto C = Journal::Component::Kakshya;
14
15 bool series_in_range(size_t count, uint32_t index, const char* operation)
16 {
17 if (index < count)
18 return true;
19 MF_ERROR(C, X, "PlotContainer::{}: series index {} out of range", operation, index);
20 return false;
21 }
22}
23
32
33// =========================================================================
34// ensure_processor
35// =========================================================================
36
38{
39 if (!m_processor)
41 return *std::static_pointer_cast<PlotProcessor>(m_processor);
42}
43
44// =========================================================================
45// Series management
46// =========================================================================
47
48uint32_t PlotContainer::add_series(std::string name, uint64_t count,
49 DataDimension::Role role, DataModality modality)
50{
51 m_data.emplace_back(std::vector<double>(count, 0.0));
52 m_processed_data.emplace_back(std::vector<double>(count, 0.0));
53
55
56 m_structure.dimensions.emplace_back(std::move(name), count, 1, role);
58
59 const auto idx = static_cast<uint32_t>(m_structure.dimensions.size() - 1);
60 MF_INFO(C, X, "PlotContainer: added series '{}' at index {} ({} samples, role={}, modality={})",
61 m_structure.dimensions[idx].name, idx, count,
62 static_cast<int>(role),
63 modality_to_string(modality));
64
65 return idx;
66}
67
68void PlotContainer::write_series(uint32_t index, std::span<const double> samples)
69{
70 if (index >= m_data.size()) {
71 MF_ERROR(C, X, "PlotContainer::write_series: index {} out of range", index);
72 return;
73 }
74
75 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
76 if (!vec) {
77 MF_ERROR(C, X, "PlotContainer::write_series: series {} has unexpected variant type", index);
78 return;
79 }
80
81 const uint64_t n = std::min<uint64_t>(samples.size(), vec->size());
83 std::copy_n(samples.begin(), n, vec->begin());
84}
85
86void PlotContainer::write_sample(uint32_t index, uint64_t sample_index, double value)
87{
88 if (index >= m_data.size()) {
89 MF_ERROR(C, X, "PlotContainer::write_sample: series index {} out of range", index);
90 return;
91 }
92
93 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
94 if (!vec || sample_index >= vec->size()) {
95 MF_ERROR(C, X, "PlotContainer::write_sample: sample index {} out of range in series {}", sample_index, index);
96 return;
97 }
98
100 (*vec)[sample_index] = value;
101}
102
104{
105 if (index >= m_data.size()) {
106 MF_ERROR(C, X, "PlotContainer::resize_series: index {} out of range", index);
107 return;
108 }
109
110 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
111 if (!vec)
112 return;
113
114 {
116 vec->resize(count, 0.0);
117 }
118
119 auto* pv = std::get_if<std::vector<double>>(&m_processed_data[index]);
120 if (pv)
121 pv->resize(count, 0.0);
122
124}
125
127{
128 return static_cast<uint32_t>(m_structure.dimensions.size());
129}
130
131const std::string& PlotContainer::series_name(uint32_t index) const
132{
133 static const std::string empty;
134 return index < m_structure.dimensions.size()
136 : empty;
137}
138
139uint64_t PlotContainer::series_size(uint32_t index) const
140{
141 if (index >= m_data.size())
142 return 0;
143 const auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
144 return vec ? static_cast<uint64_t>(vec->size()) : 0;
145}
146
153
154// =========================================================================
155// Source binding
156// =========================================================================
157
158void PlotContainer::bind(uint32_t series_index, std::shared_ptr<Nodes::Node> node)
159{
160 if (!series_in_range(m_structure.dimensions.size(), series_index, "bind"))
161 return;
162 auto& p = ensure_processor();
163 p.bind_node(series_index, std::move(node));
164 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
167}
168
169void PlotContainer::bind(uint32_t series_index,
170 std::shared_ptr<Buffers::AudioBuffer> buffer)
171{
172 if (!series_in_range(m_structure.dimensions.size(), series_index, "bind"))
173 return;
174 auto& p = ensure_processor();
175 p.bind_audio_buffer(series_index, std::move(buffer));
176 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
179}
180
181void PlotContainer::bind(uint32_t series_index,
182 std::shared_ptr<Nodes::Network::NodeNetwork> network)
183{
184 if (!series_in_range(m_structure.dimensions.size(), series_index, "bind"))
185 return;
186 auto& p = ensure_processor();
187 p.bind_network(series_index, std::move(network));
188 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
191}
192
193void PlotContainer::bind(uint32_t series_index,
194 std::function<void(std::vector<double>&)> fn)
195{
196 if (!series_in_range(m_structure.dimensions.size(), series_index, "bind"))
197 return;
198 auto& p = ensure_processor();
199 p.bind_callable(series_index, std::move(fn));
200 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
203}
204
205void PlotContainer::set_raw(uint32_t series_index, std::vector<double> data)
206{
207 if (!series_in_range(m_structure.dimensions.size(), series_index, "set_raw"))
208 return;
209 auto& p = ensure_processor();
210 p.set_raw(series_index, std::move(data));
211 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
214}
215
216void PlotContainer::unbind(uint32_t series_index)
217{
218 if (m_processor)
219 std::static_pointer_cast<PlotProcessor>(m_processor)->unbind(series_index);
220}
221
222// =========================================================================
223// NDDataContainer
224// =========================================================================
225
226std::vector<DataDimension> PlotContainer::get_dimensions() const
227{
228 return m_structure.dimensions;
229}
230
232{
233 uint64_t total = 0;
234 for (const auto& v : m_data) {
235 const auto* vec = std::get_if<std::vector<double>>(&v);
236 if (vec)
237 total += vec->size();
238 }
239 return total;
240}
241
243{
244 return static_cast<uint64_t>(m_data.size());
245}
246
247std::vector<DataVariant> PlotContainer::get_region_data(const Region& region) const
248{
249 if (region.start_coordinates.empty() || region.end_coordinates.empty())
250 return {};
251
252 const auto series_idx = static_cast<size_t>(region.start_coordinates[0]);
253 if (series_idx >= m_data.size())
254 return {};
255
256 std::optional<std::vector<DataVariant>> result;
257 seqlock_read_void(m_series_locks[series_idx], 8, [&] {
258 const auto* src = std::get_if<std::vector<double>>(&m_data[series_idx]);
259 if (!src || src->empty())
260 return;
261 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
262 const uint64_t e = (region.end_coordinates.size() > 1)
263 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(src->size() - 1))
264 : static_cast<uint64_t>(src->size() - 1);
265 if (s > e)
266 return;
267 std::vector<double> slice(src->begin() + static_cast<ptrdiff_t>(s),
268 src->begin() + static_cast<ptrdiff_t>(e + 1));
269 result = { DataVariant(std::move(slice)) };
270 });
271
272 return result.value_or(std::vector<DataVariant> {});
273}
274
275void PlotContainer::set_region_data(const Region& region, const std::vector<DataVariant>& data)
276{
277 if (data.empty() || region.start_coordinates.empty())
278 return;
279
280 const auto series_idx = static_cast<size_t>(region.start_coordinates[0]);
281 if (series_idx >= m_data.size())
282 return;
283
284 const auto* src = std::get_if<std::vector<double>>(&data[0]);
285 if (!src || src->empty())
286 return;
287
289 auto* dst = std::get_if<std::vector<double>>(&m_data[series_idx]);
290 if (!dst || dst->empty())
291 return;
292
293 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
294 const uint64_t e = (region.end_coordinates.size() > 1)
295 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(dst->size() - 1))
296 : static_cast<uint64_t>(dst->size() - 1);
297 if (s > e)
298 return;
299
300 const uint64_t n = std::min<uint64_t>(src->size(), e - s + 1);
301 std::copy_n(src->begin(), n, dst->begin() + static_cast<ptrdiff_t>(s));
302}
303
304std::vector<DataVariant> PlotContainer::get_region_group_data(const RegionGroup& group) const
305{
306 std::vector<DataVariant> result;
307 for (const auto& r : group.regions) {
308 auto slice = get_region_data(r);
309 for (auto& v : slice)
310 result.push_back(std::move(v));
311 }
312 return result;
313}
314
315std::vector<DataVariant> PlotContainer::get_segments_data(const std::vector<RegionSegment>&) const
316{
317 return m_processed_data;
318}
319
320uint64_t PlotContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
321{
322 if (coordinates.size() < 2)
323 return 0;
324 return coordinates[1];
325}
326
327std::vector<uint64_t> PlotContainer::linear_index_to_coordinates(uint64_t linear) const
328{
329 return { 0, linear };
330}
331
339
341{
342 if (m_data.empty())
343 return nullptr;
344 const auto* vec = std::get_if<std::vector<double>>(&m_data[0]);
345 return (vec && !vec->empty()) ? vec->data() : nullptr;
346}
347
349{
350 return !m_data.empty();
351}
352
354{
355 if (index >= m_data.size()) {
356 static DataVariant empty = std::vector<double> {};
357 static std::vector<DataDimension> empty_dims;
358 return { empty, empty_dims, DataModality::TENSOR_ND };
359 }
361}
362
363std::vector<DataAccess> PlotContainer::all_channel_data()
364{
365 std::vector<DataAccess> result;
366 result.reserve(m_data.size());
367 for (size_t i = 0; i < m_data.size(); ++i) {
368 result.emplace_back(m_data[i],
369 std::vector<DataDimension> { m_structure.dimensions[i] },
371 }
372
373 return result;
374}
375
381
383{
384 static const RegionGroup empty;
385 std::optional<RegionGroup> result;
386 seqlock_read_void(m_region_lock, 8, [&] {
387 auto it = m_region_groups.find(name);
388 result = (it != m_region_groups.end()) ? it->second : empty;
389 });
390 return result.value_or(empty);
391}
392
393std::unordered_map<std::string, RegionGroup> PlotContainer::get_all_region_groups() const
394{
395 std::optional<std::unordered_map<std::string, RegionGroup>> result;
396 seqlock_read_void(m_region_lock, 8, [&] {
397 result = m_region_groups;
398 });
399 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
400}
401
407
408// =========================================================================
409// SignalSourceContainer
410// =========================================================================
411
413{
414 return m_processing_state.load(std::memory_order_acquire);
415}
416
418{
419 ProcessingState prev = m_processing_state.exchange(state);
420 if (prev == state)
421 return;
422
423 seqlock_read_void(m_cb_lock, 8, [&] {
424 if (m_state_cb)
425 m_state_cb(shared_from_this(), state);
426 });
427}
428
430 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb)
431{
433 m_state_cb = std::move(cb);
434}
435
441
443{
444 const auto s = get_processing_state();
446}
447
452
454{
455 set_default_processor(std::make_shared<PlotProcessor>());
456}
457
466
467void PlotContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
468{
469 if (m_processor)
470 m_processor->on_detach(shared_from_this());
471 m_processor = processor;
472 if (m_processor)
473 m_processor->on_attach(shared_from_this());
474}
475
476std::shared_ptr<DataProcessor> PlotContainer::get_default_processor() const
477{
478 return m_processor;
479}
480
481std::shared_ptr<DataProcessingChain> PlotContainer::get_processing_chain()
482{
483 if (!m_chain)
484 m_chain = std::make_shared<DataProcessingChain>();
485 return m_chain;
486}
487
488auto PlotContainer::get_frame_typed(uint64_t frame_index) const -> std::span<const double>
489{
490 if (frame_index >= m_data.size())
491 return {};
492
493 std::span<const double> result;
494 seqlock_read_void(m_series_locks[frame_index], 8, [&] {
495 const auto* vec = std::get_if<std::vector<double>>(&m_data[frame_index]);
496 if (!vec || vec->empty())
497 return;
498 result = { vec->data(), vec->size() };
499 });
500 return result;
501}
502
503void PlotContainer::get_frames_typed(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
504{
505 size_t out = 0;
506 for (uint64_t i = start_frame; i < start_frame + num_frames && i < m_data.size(); ++i) {
507 seqlock_read_void(m_series_locks[i], 8, [&] {
508 const auto* vec = std::get_if<std::vector<double>>(&m_data[i]);
509 if (!vec)
510 return;
511 for (double v : *vec) {
512 if (out >= output.size())
513 return;
514 output[out++] = v;
515 }
516 });
517 }
518}
519
520auto PlotContainer::get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant
521{
522 return { get_frame_typed(frame_index) };
523}
524
526 void* output,
527 size_t count,
528 uint64_t start_frame,
529 uint64_t num_frames,
530 const std::type_info& type) const
531{
532 if (type != typeid(double)) {
533 error<std::runtime_error>(
536 std::source_location::current(),
537 "PlotContainer only supports double");
538 }
539
540 get_frames_typed(std::span<double>(static_cast<double*>(output), count), start_frame, num_frames);
541}
542
544 const std::vector<uint64_t>& coords,
545 void* out,
546 const std::type_info& type) const
547{
548 if (type != typeid(double) || coords.size() < 2 || coords[0] >= m_data.size())
549 return;
550
551 const auto idx = static_cast<size_t>(coords[0]);
552
553 seqlock_read_void(m_series_locks[idx], 8, [&] {
554 const auto* vec = std::get_if<std::vector<double>>(&m_data[idx]);
555 if (!vec || coords[1] >= vec->size())
556 return;
557 *static_cast<double*>(out) = (*vec)[coords[1]];
558 });
559}
560
562 const std::vector<uint64_t>& coords,
563 const void* in,
564 const std::type_info& type)
565{
566 if (type != typeid(double) || coords.size() < 2 || coords[0] >= m_data.size())
567 return;
568
569 const auto idx = static_cast<size_t>(coords[0]);
570
572 auto* vec = std::get_if<std::vector<double>>(&m_data[idx]);
573 if (!vec || coords[1] >= vec->size())
574 return;
575
576 (*vec)[coords[1]] = *static_cast<const double*>(in);
577}
578
579} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:43
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t * dst
const uint32_t * src
uint32_t total
std::vector< float > * out
glm::vec3 value
std::vector< glm::ivec3 > coords
Definition VDBWriter.cpp:25
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
std::string name
Definition VKDevice.cpp:123
uint32_t index
Definition VKDevice.cpp:122
size_t count
Type-erased accessor for NDData with semantic view construction.
void bind(uint32_t series_index, std::shared_ptr< Nodes::Node > node)
Bind a series to a Node.
std::unordered_map< std::string, RegionGroup > get_all_region_groups() const override
Get all region groups in the container.
PlotProcessor & ensure_processor()
Return the PlotProcessor, creating and attaching it if absent.
void get_frames_impl(void *output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info &type) const override
Implementation-specific method to retrieve multiple frames.
std::vector< DataAccess > all_channel_data() override
Get all channel data as accessors.
void unbind(uint32_t series_index)
Remove the source binding for a series.
std::shared_ptr< DataProcessingChain > m_chain
std::vector< DataVariant > m_data
auto get_frame_typed(uint64_t frame_index) const -> std::span< const double >
void mark_ready_for_processing(bool ready) override
Mark the container as ready or not ready for processing.
void remove_region_group(const std::string &name) override
Remove a region group by name.
auto get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant override
Implementation-specific method to retrieve a frame span.
PlotContainer()
Construct an empty container.
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_cb
void get_frames_typed(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const
std::atomic< ProcessingState > m_processing_state
RegionGroup get_region_group(const std::string &name) const override
Get a region group by name.
void write_series(uint32_t index, std::span< const double > samples)
Write the full sample buffer for a series.
uint32_t add_series(std::string name, uint64_t count, DataDimension::Role role=DataDimension::Role::CUSTOM, DataModality modality=DataModality::TENSOR_ND)
Add a named series with a given capacity, zero-initialised.
std::unordered_map< std::string, RegionGroup > m_region_groups
std::vector< DataDimension > get_dimensions() const override
Get the dimensions describing the structure of the data.
bool has_data() const override
Check if the container currently holds any data.
void write_sample(uint32_t index, uint64_t sample_index, double value)
Write a single sample within a series.
std::vector< DataVariant > get_region_data(const Region &region) const override
Extract a sample range from a single series.
std::vector< DataVariant > m_processed_data
ContainerDataStructure m_structure
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
const void * get_raw_data() const override
Get a raw pointer to the underlying data storage.
uint64_t coordinates_to_linear_index(const std::vector< uint64_t > &coordinates) const override
Convert coordinates to linear index based on current memory layout.
const std::string & series_name(uint32_t index) const
Return the name of a series.
void resize_series(uint32_t index, uint64_t count)
Resize a series.
uint64_t series_size(uint32_t index) const
Return the sample count of a series.
void process_default() override
Process the container's data using the default processor.
uint32_t series_count() const
Return the number of series.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
void create_default_processor() override
Create and configure a default processor for this container.
void set_value_impl(const std::vector< uint64_t > &coords, const void *in, const std::type_info &type) override
Type-erased single-element write.
DataAccess channel_data(size_t index) override
Get channel data with semantic interpretation.
void set_default_processor(const std::shared_ptr< DataProcessor > &processor) override
Set the default data processor for this container.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
void get_value_impl(const std::vector< uint64_t > &coords, void *out, const std::type_info &type) const override
Type-erased single-element read.
Memory::SeqlockArray m_series_locks
void clear() override
Clear all data in the container.
uint64_t get_total_elements() const override
Get the total number of elements in the container.
void update_processing_state(ProcessingState state) override
Update the processing state of the container.
std::vector< uint64_t > linear_index_to_coordinates(uint64_t linear_index) const override
Convert linear index to coordinates based on current memory layout.
std::vector< DataVariant > get_segments_data(const std::vector< RegionSegment > &segments) const override
Get data for multiple region segments efficiently.
void add_region_group(const RegionGroup &group) override
Add a named group of regions to the container.
void unregister_state_change_callback() override
Unregister the state change callback, if any.
std::shared_ptr< DataProcessor > m_processor
void set_raw(uint32_t series_index, std::vector< double > data)
Push raw sample data into a series.
bool is_ready_for_processing() const override
Check if the container is ready for processing.
void register_state_change_callback(std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> cb) override
Register a callback to be invoked on processing state changes.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
DataDimension::Role series_role(uint32_t index) const
Return the role of a series.
void set_region_data(const Region &region, const std::vector< DataVariant > &data) override
Write a sample range back into a series.
uint64_t get_num_frames() const override
Get the number of frames in the primary (temporal) dimension.
DataProcessor that acquires per-series data from heterogeneous sources and writes into PlotContainer:...
void resize(size_t n)
Replace the slot array with n default-constructed Seqlock instances.
Definition SeqLock.hpp:339
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:155
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
ProcessingState
Represents the current processing lifecycle state of a container.
@ READY
Container has data loaded and is ready for processing.
@ IDLE
Container is inactive with no data or not ready for processing.
@ PROCESSING
Container is actively being processed.
@ PROCESSED
Container has completed processing and results are available.
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
Definition NDData.hpp:625
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:70
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:132
@ TENSOR_ND
N-dimensional tensor.
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
@ PLANAR
Separate DataVariant per logical unit (LLL...RRR for stereo)
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:102
Container structure for consistent dimension ordering.
Role
Semantic role of the dimension.
Definition NDData.hpp:204
@ CUSTOM
User-defined or application-specific.
std::vector< Region > regions
Collection of regions belonging to this group.
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: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