MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PlotContainer.cpp
Go to the documentation of this file.
1#include "PlotContainer.hpp"
2
5
7
8namespace MayaFlux::Kakshya {
9
10namespace {
11 constexpr auto C = Journal::Component::Kakshya;
13}
14
22
23// =========================================================================
24// ensure_processor
25// =========================================================================
26
28{
29 if (!m_processor)
31 return *std::static_pointer_cast<PlotProcessor>(m_processor);
32}
33
34// =========================================================================
35// Series management
36// =========================================================================
37
38uint32_t PlotContainer::add_series(std::string name, uint64_t count,
39 DataDimension::Role role, DataModality modality)
40{
41 m_data.emplace_back(std::vector<double>(count, 0.0));
42 m_processed_data.emplace_back(std::vector<double>(count, 0.0));
43 m_structure.dimensions.emplace_back(std::move(name), count, 1, role);
45
46 const auto idx = static_cast<uint32_t>(m_structure.dimensions.size() - 1);
47 MF_INFO(C, X, "PlotContainer: added series '{}' at index {} ({} samples, role={}, modality={})",
48 m_structure.dimensions[idx].name, idx, count,
49 static_cast<int>(role),
50 modality_to_string(modality));
51
52 return idx;
53}
54
55void PlotContainer::write_series(uint32_t index, std::span<const double> samples)
56{
57 if (index >= m_data.size()) {
58 MF_ERROR(C, X, "PlotContainer::write_series: index {} out of range", index);
59 return;
60 }
61
62 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
63 if (!vec) {
64 MF_ERROR(C, X, "PlotContainer::write_series: series {} has unexpected variant type", index);
65 return;
66 }
67
68 const uint64_t n = std::min<uint64_t>(samples.size(), vec->size());
69 std::copy_n(samples.begin(), n, vec->begin());
70}
71
72void PlotContainer::write_sample(uint32_t index, uint64_t sample_index, double value)
73{
74 if (index >= m_data.size()) {
75 MF_ERROR(C, X, "PlotContainer::write_sample: series index {} out of range", index);
76 return;
77 }
78
79 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
80 if (!vec || sample_index >= vec->size()) {
81 MF_ERROR(C, X, "PlotContainer::write_sample: sample index {} out of range in series {}", sample_index, index);
82 return;
83 }
84
85 (*vec)[sample_index] = value;
86}
87
88void PlotContainer::resize_series(uint32_t index, uint64_t count)
89{
90 if (index >= m_data.size()) {
91 MF_ERROR(C, X, "PlotContainer::resize_series: index {} out of range", index);
92 return;
93 }
94
95 auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
96 if (!vec)
97 return;
98
99 vec->resize(count, 0.0);
100
101 auto* pv = std::get_if<std::vector<double>>(&m_processed_data[index]);
102 if (pv)
103 pv->resize(count, 0.0);
104
105 m_structure.dimensions[index].size = count;
106}
107
109{
110 return static_cast<uint32_t>(m_structure.dimensions.size());
111}
112
113const std::string& PlotContainer::series_name(uint32_t index) const
114{
115 static const std::string empty;
116 return index < m_structure.dimensions.size()
117 ? m_structure.dimensions[index].name
118 : empty;
119}
120
121uint64_t PlotContainer::series_size(uint32_t index) const
122{
123 if (index >= m_data.size())
124 return 0;
125 const auto* vec = std::get_if<std::vector<double>>(&m_data[index]);
126 return vec ? static_cast<uint64_t>(vec->size()) : 0;
127}
128
130{
131 return index < m_structure.dimensions.size()
132 ? m_structure.dimensions[index].role
134}
135
136// =========================================================================
137// Source binding
138// =========================================================================
139
140void PlotContainer::bind(uint32_t series_index, std::shared_ptr<Nodes::Node> node)
141{
142 auto& p = ensure_processor();
143 p.bind_node(series_index, std::move(node));
144 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
147}
148
149void PlotContainer::bind(uint32_t series_index,
150 std::shared_ptr<Buffers::AudioBuffer> buffer)
151{
152 auto& p = ensure_processor();
153 p.bind_audio_buffer(series_index, std::move(buffer));
154 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
157}
158
159void PlotContainer::bind(uint32_t series_index,
160 std::shared_ptr<Nodes::Network::NodeNetwork> network)
161{
162 auto& p = ensure_processor();
163 p.bind_network(series_index, std::move(network));
164 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
167}
168
169void PlotContainer::bind(uint32_t series_index,
170 std::function<void(std::vector<double>&)> fn)
171{
172 auto& p = ensure_processor();
173 p.bind_callable(series_index, std::move(fn));
174 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
177}
178
179void PlotContainer::set_raw(uint32_t series_index, std::vector<double> data)
180{
181 auto& p = ensure_processor();
182 p.set_raw(series_index, std::move(data));
183 p.set_series_semantics(series_index, m_structure.dimensions[series_index].role,
186}
187
188void PlotContainer::unbind(uint32_t series_index)
189{
190 if (m_processor)
191 std::static_pointer_cast<PlotProcessor>(m_processor)->unbind(series_index);
192}
193
194// =========================================================================
195// NDDataContainer
196// =========================================================================
197
198std::vector<DataDimension> PlotContainer::get_dimensions() const
199{
200 return m_structure.dimensions;
201}
202
204{
205 uint64_t total = 0;
206 for (const auto& v : m_data) {
207 const auto* vec = std::get_if<std::vector<double>>(&v);
208 if (vec)
209 total += vec->size();
210 }
211 return total;
212}
213
215{
216 return static_cast<uint64_t>(m_data.size());
217}
218
219std::vector<DataVariant> PlotContainer::get_region_data(const Region& region) const
220{
221 if (region.start_coordinates.empty() || region.end_coordinates.empty())
222 return {};
223
224 const uint64_t series_idx = region.start_coordinates[0];
225 if (series_idx >= m_data.size())
226 return {};
227
228 const auto* src = std::get_if<std::vector<double>>(&m_data[series_idx]);
229 if (!src || src->empty())
230 return {};
231
232 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
233 const uint64_t e = (region.end_coordinates.size() > 1)
234 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(src->size() - 1))
235 : static_cast<uint64_t>(src->size() - 1);
236
237 if (s > e)
238 return {};
239
240 std::vector<double> slice(src->begin() + static_cast<ptrdiff_t>(s),
241 src->begin() + static_cast<ptrdiff_t>(e + 1));
242 return { DataVariant(std::move(slice)) };
243}
244
245void PlotContainer::set_region_data(const Region& region, const std::vector<DataVariant>& data)
246{
247 if (data.empty() || region.start_coordinates.empty())
248 return;
249
250 const uint64_t series_idx = region.start_coordinates[0];
251 if (series_idx >= m_data.size())
252 return;
253
254 auto* dst = std::get_if<std::vector<double>>(&m_data[series_idx]);
255 if (!dst)
256 return;
257
258 const auto* src = std::get_if<std::vector<double>>(&data[0]);
259 if (!src || src->empty())
260 return;
261
262 const uint64_t s = (region.start_coordinates.size() > 1) ? region.start_coordinates[1] : 0;
263 const uint64_t e = (region.end_coordinates.size() > 1)
264 ? std::min(region.end_coordinates[1], static_cast<uint64_t>(dst->size() - 1))
265 : static_cast<uint64_t>(dst->size() - 1);
266
267 if (s > e)
268 return;
269
270 const uint64_t n = std::min<uint64_t>(src->size(), e - s + 1);
271 std::copy_n(src->begin(), n, dst->begin() + static_cast<ptrdiff_t>(s));
272}
273
274std::vector<DataVariant> PlotContainer::get_region_group_data(const RegionGroup& group) const
275{
276 std::vector<DataVariant> result;
277 for (const auto& r : group.regions) {
278 auto slice = get_region_data(r);
279 for (auto& v : slice)
280 result.push_back(std::move(v));
281 }
282 return result;
283}
284
285std::vector<DataVariant> PlotContainer::get_segments_data(const std::vector<RegionSegment>&) const
286{
287 return m_processed_data;
288}
289
290std::span<const double> PlotContainer::get_frame(uint64_t frame_index) const
291{
292 if (frame_index >= m_data.size())
293 return {};
294 const auto* vec = std::get_if<std::vector<double>>(&m_data[frame_index]);
295 if (!vec || vec->empty())
296 return {};
297 return { vec->data(), vec->size() };
298}
299
300void PlotContainer::get_frames(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
301{
302 size_t out = 0;
303 for (uint64_t i = start_frame; i < start_frame + num_frames && i < m_data.size(); ++i) {
304 const auto* vec = std::get_if<std::vector<double>>(&m_data[i]);
305 if (!vec)
306 continue;
307 for (double v : *vec) {
308 if (out >= output.size())
309 return;
310 output[out++] = v;
311 }
312 }
313}
314
315double PlotContainer::get_value_at(const std::vector<uint64_t>& coordinates) const
316{
317 if (coordinates.size() < 2 || coordinates[0] >= m_data.size())
318 return 0.0;
319 const auto* vec = std::get_if<std::vector<double>>(&m_data[coordinates[0]]);
320 if (!vec || coordinates[1] >= vec->size())
321 return 0.0;
322 return (*vec)[coordinates[1]];
323}
324
325void PlotContainer::set_value_at(const std::vector<uint64_t>& coordinates, double value)
326{
327 if (coordinates.size() < 2 || coordinates[0] >= m_data.size())
328 return;
329 auto* vec = std::get_if<std::vector<double>>(&m_data[coordinates[0]]);
330 if (!vec || coordinates[1] >= vec->size())
331 return;
332 (*vec)[coordinates[1]] = value;
333}
334
335uint64_t PlotContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
336{
337 if (coordinates.size() < 2)
338 return 0;
339 return coordinates[1];
340}
341
342std::vector<uint64_t> PlotContainer::linear_index_to_coordinates(uint64_t linear) const
343{
344 return { 0, linear };
345}
346
354
356{
357 if (m_data.empty())
358 return nullptr;
359 const auto* vec = std::get_if<std::vector<double>>(&m_data[0]);
360 return (vec && !vec->empty()) ? vec->data() : nullptr;
361}
362
364{
365 return !m_data.empty();
366}
367
369{
370 if (index >= m_data.size()) {
371 static DataVariant empty = std::vector<double> {};
372 static std::vector<DataDimension> empty_dims;
373 return { empty, empty_dims, DataModality::TENSOR_ND };
374 }
375 return { m_data[index], { m_structure.dimensions[index] }, DataModality::TENSOR_ND };
376}
377
378std::vector<DataAccess> PlotContainer::all_channel_data()
379{
380 std::vector<DataAccess> result;
381 result.reserve(m_data.size());
382 for (size_t i = 0; i < m_data.size(); ++i) {
383 result.emplace_back(m_data[i],
384 std::vector<DataDimension> { m_structure.dimensions[i] },
386 }
387
388 return result;
389}
390
392{
393 m_region_groups[group.name] = group;
394}
395
396const RegionGroup& PlotContainer::get_region_group(const std::string& name) const
397{
398 static const RegionGroup empty;
399 auto it = m_region_groups.find(name);
400 return it != m_region_groups.end() ? it->second : empty;
401}
402
403std::unordered_map<std::string, RegionGroup> PlotContainer::get_all_region_groups() const
404{
405 return m_region_groups;
406}
407
408void PlotContainer::remove_region_group(const std::string& name)
409{
410 m_region_groups.erase(name);
411}
412
413// =========================================================================
414// SignalSourceContainer
415// =========================================================================
416
418{
419 return m_processing_state.load(std::memory_order_acquire);
420}
421
423{
424 m_processing_state.store(state, std::memory_order_release);
425 if (m_state_cb)
426 m_state_cb(shared_from_this(), state);
427}
428
430 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb)
431{
432 m_state_cb = std::move(cb);
433}
434
439
441{
442 const auto s = get_processing_state();
444}
445
450
452{
453 set_default_processor(std::make_shared<PlotProcessor>());
454}
455
464
465void PlotContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
466{
467 if (m_processor)
468 m_processor->on_detach(shared_from_this());
469 m_processor = processor;
470 if (m_processor)
471 m_processor->on_attach(shared_from_this());
472}
473
474std::shared_ptr<DataProcessor> PlotContainer::get_default_processor() const
475{
476 return m_processor;
477}
478
479} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:37
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::span< const double > get_frame(uint64_t frame_index) const override
Get a single frame of data efficiently.
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.
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::vector< DataVariant > m_data
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.
PlotContainer()
Construct an empty container.
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_cb
std::atomic< ProcessingState > m_processing_state
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
void set_value_at(const std::vector< uint64_t > &coordinates, double value) override
Set a single value at the specified coordinates.
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.
const RegionGroup & get_region_group(const std::string &name) const override
Get a region group by name.
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.
void create_default_processor() override
Create and configure a default processor for this container.
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 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.
void get_frames(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const override
Get multiple frames efficiently.
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.
double get_value_at(const std::vector< uint64_t > &coordinates) const override
Get a single value at the specified coordinates.
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:...
@ 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.
@ 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.
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:76
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:81
@ 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:83
Container structure for consistent dimension ordering.
Role
Semantic role of the dimension.
Definition NDData.hpp:150
@ 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