MayaFlux 0.5.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
24
25// =========================================================================
26// ensure_processor
27// =========================================================================
28
30{
31 if (!m_processor)
33 return *std::static_pointer_cast<PlotProcessor>(m_processor);
34}
35
36// =========================================================================
37// Series management
38// =========================================================================
39
40uint32_t PlotContainer::add_series(std::string name, uint64_t count,
41 DataDimension::Role role, DataModality modality)
42{
43 m_data.emplace_back(std::vector<double>(count, 0.0));
44 m_processed_data.emplace_back(std::vector<double>(count, 0.0));
45
47
48 m_structure.dimensions.emplace_back(std::move(name), count, 1, role);
50
51 const auto idx = static_cast<uint32_t>(m_structure.dimensions.size() - 1);
52 MF_INFO(C, X, "PlotContainer: added series '{}' at index {} ({} samples, role={}, modality={})",
53 m_structure.dimensions[idx].name, idx, count,
54 static_cast<int>(role),
55 modality_to_string(modality));
56
57 return idx;
58}
59
60void PlotContainer::write_series(uint32_t index, std::span<const double> samples)
61{
62 if (index >= m_data.size()) {
63 MF_ERROR(C, X, "PlotContainer::write_series: index {} out of range", index);
64 return;
65 }
66
67 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
68 if (!vec) {
69 MF_ERROR(C, X, "PlotContainer::write_series: series {} has unexpected variant type", index);
70 return;
71 }
72
73 const uint64_t n = std::min<uint64_t>(samples.size(), vec->size());
75 std::copy_n(samples.begin(), n, vec->begin());
76}
77
78void PlotContainer::write_sample(uint32_t index, uint64_t sample_index, double value)
79{
80 if (index >= m_data.size()) {
81 MF_ERROR(C, X, "PlotContainer::write_sample: series index {} out of range", index);
82 return;
83 }
84
85 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
86 if (!vec || sample_index >= vec->size()) {
87 MF_ERROR(C, X, "PlotContainer::write_sample: sample index {} out of range in series {}", sample_index, index);
88 return;
89 }
90
92 (*vec)[sample_index] = value;
93}
94
95void PlotContainer::resize_series(uint32_t index, uint64_t count)
96{
97 if (index >= m_data.size()) {
98 MF_ERROR(C, X, "PlotContainer::resize_series: index {} out of range", index);
99 return;
100 }
101
102 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
103 if (!vec)
104 return;
105
106 vec->resize(count, 0.0);
107
108 auto* pv = std::get_if<std::vector<double>>(&m_processed_data[index]);
109 if (pv)
110 pv->resize(count, 0.0);
111
112 m_structure.dimensions[index].size = count;
113}
114
116{
117 return static_cast<uint32_t>(m_structure.dimensions.size());
118}
119
120const std::string& PlotContainer::series_name(uint32_t index) const
121{
122 static const std::string empty;
123 return index < m_structure.dimensions.size()
124 ? m_structure.dimensions[index].name
125 : empty;
126}
127
128uint64_t PlotContainer::series_size(uint32_t index) const
129{
130 if (index >= m_data.size())
131 return 0;
132 const auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
133 return vec ? static_cast<uint64_t>(vec->size()) : 0;
134}
135
137{
138 return index < m_structure.dimensions.size()
139 ? m_structure.dimensions[index].role
141}
142
143// =========================================================================
144// Source binding
145// =========================================================================
146
147void PlotContainer::bind(uint32_t series_index, std::shared_ptr<Nodes::Node> node)
148{
149 auto& p = ensure_processor();
150 p.bind_node(series_index, std::move(node));
151 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
154}
155
156void PlotContainer::bind(uint32_t series_index,
157 std::shared_ptr<Buffers::AudioBuffer> buffer)
158{
159 auto& p = ensure_processor();
160 p.bind_audio_buffer(series_index, std::move(buffer));
161 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
164}
165
166void PlotContainer::bind(uint32_t series_index,
167 std::shared_ptr<Nodes::Network::NodeNetwork> network)
168{
169 auto& p = ensure_processor();
170 p.bind_network(series_index, std::move(network));
171 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
174}
175
176void PlotContainer::bind(uint32_t series_index,
177 std::function<void(std::vector<double>&)> fn)
178{
179 auto& p = ensure_processor();
180 p.bind_callable(series_index, std::move(fn));
181 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
184}
185
186void PlotContainer::set_raw(uint32_t series_index, std::vector<double> data)
187{
188 auto& p = ensure_processor();
189 p.set_raw(series_index, std::move(data));
190 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
193}
194
195void PlotContainer::unbind(uint32_t series_index)
196{
197 if (m_processor)
198 std::static_pointer_cast<PlotProcessor>(m_processor)->unbind(series_index);
199}
200
201// =========================================================================
202// NDDataContainer
203// =========================================================================
204
205std::vector<DataDimension> PlotContainer::get_dimensions() const
206{
207 return m_structure.dimensions;
208}
209
211{
212 uint64_t total = 0;
213 for (const auto& v : m_data) {
214 const auto* vec = std::get_if<std::vector<double>>(&v);
215 if (vec)
216 total += vec->size();
217 }
218 return total;
219}
220
222{
223 return static_cast<uint64_t>(m_data.size());
224}
225
226std::vector<DataVariant> PlotContainer::get_region_data(const Region& region) const
227{
228 if (region.start_coordinates.empty() || region.end_coordinates.empty())
229 return {};
230
231 const auto series_idx = static_cast<size_t>(region.start_coordinates[0]);
232 if (series_idx >= m_data.size())
233 return {};
234
235 std::optional<std::vector<DataVariant>> result;
236 seqlock_read_void(m_series_locks[series_idx], 8, [&] {
237 const auto* src = std::get_if<std::vector<double>>(&m_data[series_idx]);
238 if (!src || src->empty())
239 return;
240 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
241 const uint64_t e = (region.end_coordinates.size() > 1)
242 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(src->size() - 1))
243 : static_cast<uint64_t>(src->size() - 1);
244 if (s > e)
245 return;
246 std::vector<double> slice(src->begin() + static_cast<ptrdiff_t>(s),
247 src->begin() + static_cast<ptrdiff_t>(e + 1));
248 result = { DataVariant(std::move(slice)) };
249 });
250
251 return result.value_or(std::vector<DataVariant> {});
252}
253
254void PlotContainer::set_region_data(const Region& region, const std::vector<DataVariant>& data)
255{
256 if (data.empty() || region.start_coordinates.empty())
257 return;
258
259 const auto series_idx = static_cast<size_t>(region.start_coordinates[0]);
260 if (series_idx >= m_data.size())
261 return;
262
263 const auto* src = std::get_if<std::vector<double>>(&data[0]);
264 if (!src || src->empty())
265 return;
266
268 auto* dst = std::get_if<std::vector<double>>(&m_data[series_idx]);
269 if (!dst)
270 return;
271
272 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
273 const uint64_t e = (region.end_coordinates.size() > 1)
274 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(dst->size() - 1))
275 : static_cast<uint64_t>(dst->size() - 1);
276 if (s > e)
277 return;
278
279 const uint64_t n = std::min<uint64_t>(src->size(), e - s + 1);
280 std::copy_n(src->begin(), n, dst->begin() + static_cast<ptrdiff_t>(s));
281}
282
283std::vector<DataVariant> PlotContainer::get_region_group_data(const RegionGroup& group) const
284{
285 std::vector<DataVariant> result;
286 for (const auto& r : group.regions) {
287 auto slice = get_region_data(r);
288 for (auto& v : slice)
289 result.push_back(std::move(v));
290 }
291 return result;
292}
293
294std::vector<DataVariant> PlotContainer::get_segments_data(const std::vector<RegionSegment>&) const
295{
296 return m_processed_data;
297}
298
299uint64_t PlotContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
300{
301 if (coordinates.size() < 2)
302 return 0;
303 return coordinates[1];
304}
305
306std::vector<uint64_t> PlotContainer::linear_index_to_coordinates(uint64_t linear) const
307{
308 return { 0, linear };
309}
310
318
320{
321 if (m_data.empty())
322 return nullptr;
323 const auto* vec = std::get_if<std::vector<double>>(&m_data[0]);
324 return (vec && !vec->empty()) ? vec->data() : nullptr;
325}
326
328{
329 return !m_data.empty();
330}
331
333{
334 if (index >= m_data.size()) {
335 static DataVariant empty = std::vector<double> {};
336 static std::vector<DataDimension> empty_dims;
337 return { empty, empty_dims, DataModality::TENSOR_ND };
338 }
339 return { m_data[index], { m_structure.dimensions[index] }, DataModality::TENSOR_ND };
340}
341
342std::vector<DataAccess> PlotContainer::all_channel_data()
343{
344 std::vector<DataAccess> result;
345 result.reserve(m_data.size());
346 for (size_t i = 0; i < m_data.size(); ++i) {
347 result.emplace_back(m_data[i],
348 std::vector<DataDimension> { m_structure.dimensions[i] },
350 }
351
352 return result;
353}
354
360
361RegionGroup PlotContainer::get_region_group(const std::string& name) const
362{
363 static const RegionGroup empty;
364 std::optional<RegionGroup> result;
365 seqlock_read_void(m_region_lock, 8, [&] {
366 auto it = m_region_groups.find(name);
367 result = (it != m_region_groups.end()) ? it->second : empty;
368 });
369 return result.value_or(empty);
370}
371
372std::unordered_map<std::string, RegionGroup> PlotContainer::get_all_region_groups() const
373{
374 std::optional<std::unordered_map<std::string, RegionGroup>> result;
375 seqlock_read_void(m_region_lock, 8, [&] {
376 result = m_region_groups;
377 });
378 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
379}
380
381void PlotContainer::remove_region_group(const std::string& name)
382{
384 m_region_groups.erase(name);
385}
386
387// =========================================================================
388// SignalSourceContainer
389// =========================================================================
390
392{
393 return m_processing_state.load(std::memory_order_acquire);
394}
395
397{
398 ProcessingState prev = m_processing_state.exchange(state);
399 if (prev == state)
400 return;
401
402 seqlock_read_void(m_cb_lock, 8, [&] {
403 if (m_state_cb)
404 m_state_cb(shared_from_this(), state);
405 });
406}
407
409 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb)
410{
412 m_state_cb = std::move(cb);
413}
414
420
422{
423 const auto s = get_processing_state();
425}
426
431
433{
434 set_default_processor(std::make_shared<PlotProcessor>());
435}
436
445
446void PlotContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
447{
448 if (m_processor)
449 m_processor->on_detach(shared_from_this());
450 m_processor = processor;
451 if (m_processor)
452 m_processor->on_attach(shared_from_this());
453}
454
455std::shared_ptr<DataProcessor> PlotContainer::get_default_processor() const
456{
457 return m_processor;
458}
459
460std::shared_ptr<DataProcessingChain> PlotContainer::get_processing_chain()
461{
462 if (!m_chain)
463 m_chain = std::make_shared<DataProcessingChain>();
464 return m_chain;
465}
466
467auto PlotContainer::get_frame_typed(uint64_t frame_index) const -> std::span<const double>
468{
469 if (frame_index >= m_data.size())
470 return {};
471
472 std::span<const double> result;
473 seqlock_read_void(m_series_locks[frame_index], 8, [&] {
474 const auto* vec = std::get_if<std::vector<double>>(&m_data[frame_index]);
475 if (!vec || vec->empty())
476 return;
477 result = { vec->data(), vec->size() };
478 });
479 return result;
480}
481
482void PlotContainer::get_frames_typed(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
483{
484 size_t out = 0;
485 for (uint64_t i = start_frame; i < start_frame + num_frames && i < m_data.size(); ++i) {
486 seqlock_read_void(m_series_locks[i], 8, [&] {
487 const auto* vec = std::get_if<std::vector<double>>(&m_data[i]);
488 if (!vec)
489 return;
490 for (double v : *vec) {
491 if (out >= output.size())
492 return;
493 output[out++] = v;
494 }
495 });
496 }
497}
498
499auto PlotContainer::get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant
500{
501 return { get_frame_typed(frame_index) };
502}
503
505 void* output,
506 size_t count,
507 uint64_t start_frame,
508 uint64_t num_frames,
509 const std::type_info& type) const
510{
511 if (type != typeid(double)) {
512 error<std::runtime_error>(
515 std::source_location::current(),
516 "PlotContainer only supports double");
517 }
518
519 get_frames_typed(std::span<double>(static_cast<double*>(output), count), start_frame, num_frames);
520}
521
523 const std::vector<uint64_t>& coords,
524 void* out,
525 const std::type_info& type) const
526{
527 if (type != typeid(double) || coords.size() < 2 || coords[0] >= m_data.size())
528 return;
529
530 const auto idx = static_cast<size_t>(coords[0]);
531
532 seqlock_read_void(m_series_locks[idx], 8, [&] {
533 const auto* vec = std::get_if<std::vector<double>>(&m_data[idx]);
534 if (!vec || coords[1] >= vec->size())
535 return;
536 *static_cast<double*>(out) = (*vec)[coords[1]];
537 });
538}
539
541 const std::vector<uint64_t>& coords,
542 const void* in,
543 const std::type_info& type)
544{
545 if (type != typeid(double) || coords.size() < 2 || coords[0] >= m_data.size())
546 return;
547
548 const auto idx = static_cast<size_t>(coords[0]);
549
551 auto* vec = std::get_if<std::vector<double>>(&m_data[idx]);
552 if (!vec || coords[1] >= vec->size())
553 return;
554
555 (*vec)[coords[1]] = *static_cast<const double*>(in);
556}
557
558} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:39
size_t count
float value
std::shared_ptr< Core::VKImage > output
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:293
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:136
@ 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:592
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:102
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:164
@ 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:111
Container structure for consistent dimension ordering.
Role
Semantic role of the dimension.
Definition NDData.hpp:234
@ 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