MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SeriesBuilder.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "PlotSpec.hpp"
4
6
7class WaveformBuilder;
8class ScatterBuilder;
9class BarsBuilder;
10class FilledWaveformBuilder;
11
12// =============================================================================
13// Series
14// =============================================================================
15
16/**
17 * @struct SeriesSpec
18 * @brief Result of a Series terminal. Bundles a geometry function
19 * with the topology and capacity arithmetic implied by the encoding.
20 *
21 * Passed to Plot::place() so callers never supply topology or buffer
22 * capacity manually. The raw GeometryFn<shared_ptr<PlotContainer>> is
23 * still accessible via .fn for callers that need it directly.
24 */
25struct SeriesSpec {
28 std::function<size_t(uint64_t sample_count)> capacity_for;
29 std::optional<GeometryFn<float>> background_fn;
30
31 /**
32 * @brief Optional plot/data region in NDC used by plot adornments.
33 *
34 * The current data geometry still maps through its own GeometryFn. This
35 * bounds value is for labels, ticks, legends, and other construction
36 * performed by Plot::place().
37 */
38 std::optional<Kinesis::AABB2D> plot_bounds;
39
40 /**
41 * @brief Explicit text labels to materialize when the plot is placed.
42 */
43 std::vector<LabelSpec> labels;
44
45 /**
46 * @brief Concrete tick label generation specs resolved at done() time.
47 */
48 std::vector<TickLabelsSpec> tick_labels;
49
50 /**
51 * @brief Optional legend to materialize when the plot is placed.
52 */
53 std::optional<LegendSpec> legend;
54};
55
56/**
57 * @class Series
58 * @brief Convenience constructor for GeometryFn<shared_ptr<PlotContainer>>.
59 *
60 * Accumulates axis role mappings and palette, then produces a geometry
61 * function via an encoding terminal (.as_waveform(), .as_scatter(), .as_bars()).
62 *
63 * This is an illustrative convenience surface over the GeometryFn contract,
64 * not the primary or idiomatic path. The raw lambda is always preferred when
65 * the builder cannot express what you need:
66 *
67 * @code
68 * // raw — full power, always valid
69 * GeometryFn<std::shared_ptr<Kakshya::PlotContainer>> geom =
70 * [](auto c, auto& out, auto& el) { ... };
71 *
72 * // builder — convenience for common cases
73 * auto geom = Plot::series()
74 * .y(Role::SPATIAL_Y, AxisRange{}.auto_scale(), { 0.2F, 0.8F, 1.0F })
75 * .color({ 0.8F, 0.4F, 0.1F }) // fallback for unmapped series
76 * .as_waveform()
77 * .done();
78 * @endcode
79 *
80 * Multiple .x(), .y(), .z() calls accumulate independently. Each call appends
81 * a role-to-axis mapping with its own optional palette. The full PlotContainer
82 * is always passed to the generated function — no data is filtered or intercepted.
83 *
84 * Color resolution order at geometry time:
85 * 1. Per-mapping palette (cycled across series within that mapping).
86 * 2. Global palette set via .color() (cycled across all series).
87 * 3. White.
88 *
89 * When the builder cannot express what you need, write the raw lambda.
90 * It receives the same PlotContainer.
91 */
92class MAYAFLUX_API Series {
93public:
95
96 /**
97 * @struct AxisMapping
98 * @brief One or more roles mapped to a shared AxisRange and optional palette.
99 *
100 * palette cycles across series within this mapping. When empty, the
101 * global Series palette is used as fallback.
102 */
103 struct AxisMapping {
104 std::vector<Role> roles;
106 std::vector<glm::vec3> palette;
107 };
108
109 enum class TickAxis : uint8_t {
110 X,
111 Y,
112 Explicit,
113 };
114
115 /**
116 * @brief Tick request collected fluently and resolved into TickLabelsSpec
117 * by the terminal builder once axis mappings are known.
118 */
119 struct TickRequest {
120 TickAxis axis { TickAxis::Explicit };
121 std::optional<AxisRange> range;
122 uint32_t count { 2 };
123 TickEdge edge { TickEdge::Bottom };
124 glm::vec4 color { 0.65F, 0.65F, 0.65F, 1.F };
125 uint8_t decimal_places { 2 };
126 float label_h { 0.055F };
127 float label_w { 0.12F };
128 std::string name_prefix { "tick" };
129 };
130
131 // =========================================================================
132 // Axis configuration — all additive, multiple calls accumulate
133 // =========================================================================
134
135 /**
136 * @brief Map a single role to the X axis.
137 */
138 Series& x(Role role, AxisRange range = {})
139 {
140 m_x.push_back({ .roles = { role }, .range = std::move(range), .palette = {} });
141 return *this;
142 }
143
144 /**
145 * @brief Map a single role to the X axis with per-mapping colors.
146 */
147 Series& x(Role role, AxisRange range, std::initializer_list<glm::vec3> palette)
148 {
149 m_x.push_back({ .roles = { role }, .range = std::move(range), .palette = palette });
150 return *this;
151 }
152
153 /**
154 * @brief Map multiple roles to the X axis with a shared range.
155 */
156 Series& x(std::vector<Role> roles, AxisRange range = {})
157 {
158 m_x.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = {} });
159 return *this;
160 }
161
162 /**
163 * @brief Map multiple roles to the X axis with a shared range and per-mapping colors.
164 */
165 Series& x(std::vector<Role> roles, AxisRange range, std::initializer_list<glm::vec3> palette)
166 {
167 m_x.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = palette });
168 return *this;
169 }
170
171 /**
172 * @brief Map a single role to the Y axis.
173 */
174 Series& y(Role role, AxisRange range = {})
175 {
176 m_y.push_back({ .roles = { role }, .range = std::move(range), .palette = {} });
177 return *this;
178 }
179
180 /**
181 * @brief Map a single role to the Y axis with per-mapping colors.
182 */
183 Series& y(Role role, AxisRange range, std::initializer_list<glm::vec3> palette)
184 {
185 m_y.push_back({ .roles = { role }, .range = std::move(range), .palette = palette });
186 return *this;
187 }
188
189 /**
190 * @brief Map multiple roles to the Y axis with a shared range.
191 */
192 Series& y(std::vector<Role> roles, AxisRange range = {})
193 {
194 m_y.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = {} });
195 return *this;
196 }
197
198 /**
199 * @brief Map multiple roles to the Y axis with a shared range and per-mapping colors.
200 */
201 Series& y(std::vector<Role> roles, AxisRange range, std::initializer_list<glm::vec3> palette)
202 {
203 m_y.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = palette });
204 return *this;
205 }
206
207 /**
208 * @brief Map a single role to the Z axis.
209 */
210 Series& z(Role role, AxisRange range = {})
211 {
212 m_z.push_back({ .roles = { role }, .range = std::move(range), .palette = {} });
213 return *this;
214 }
215
216 /**
217 * @brief Map a single role to the Z axis with per-mapping colors.
218 */
219 Series& z(Role role, AxisRange range, std::initializer_list<glm::vec3> palette)
220 {
221 m_z.push_back({ .roles = { role }, .range = std::move(range), .palette = palette });
222 return *this;
223 }
224
225 /**
226 * @brief Map multiple roles to the Z axis with a shared range.
227 */
228 Series& z(std::vector<Role> roles, AxisRange range = {})
229 {
230 m_z.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = {} });
231 return *this;
232 }
233
234 /**
235 * @brief Map multiple roles to the Z axis with a shared range and per-mapping colors.
236 */
237 Series& z(std::vector<Role> roles, AxisRange range, std::initializer_list<glm::vec3> palette)
238 {
239 m_z.push_back({ .roles = std::move(roles), .range = std::move(range), .palette = palette });
240 return *this;
241 }
242
243 // =========================================================================
244 // Global palette — fallback when a mapping carries no per-mapping colors
245 // =========================================================================
246
247 /**
248 * @brief Append colors to the global palette.
249 *
250 * Used as fallback when a series belongs to a mapping with no per-mapping
251 * palette. Cycled across all such series by index. Multiple calls append.
252 */
253 Series& color(std::initializer_list<glm::vec3> colors)
254 {
255 m_palette.insert(m_palette.end(), colors.begin(), colors.end());
256 return *this;
257 }
258
259 Series& color(glm::vec3 c)
260 {
261 m_palette.push_back(c);
262 return *this;
263 }
264
265 /**
266 * @brief Set a solid-color background quad for this plot area.
267 *
268 * When set, Forma::plot() creates a background element behind the
269 * plot geometry. Has no effect when using Plot::place() directly.
270 *
271 * @param bounds NDC bounds of the background quad.
272 * @param color Fill color. Defaults to a dark near-black.
273 */
274 Series& background(Kinesis::AABB2D bounds, glm::vec3 color = glm::vec3(0.08F))
275 {
276 m_background_bounds = bounds;
277 m_background_color = color;
278 m_has_background = true;
279 return *this;
280 }
281
282 // =========================================================================
283 // Plot adornments
284 // =========================================================================
285
286 /**
287 * @brief Set the logical plot/data bounds used by labels, tick labels,
288 * legends, and future plot adornments.
289 *
290 * This does not remap the generated data geometry by itself. It defines
291 * the NDC area around which Plot::place() should construct adornments.
292 */
294 {
295 m_plot_bounds = bounds;
296 return *this;
297 }
298
299 /**
300 * @brief Add a construction-free text label to this plot.
301 */
303 std::string text,
304 Kinesis::AABB2D bounds,
305 glm::vec4 color = { 0.85F, 0.85F, 0.85F, 1.F },
306 std::string name = {})
307 {
308 m_labels.push_back(plot_label(std::move(text), bounds, color, std::move(name)));
309 return *this;
310 }
311
312 /**
313 * @brief Add an already-built label spec.
314 */
316 {
317 m_labels.push_back(std::move(spec));
318 return *this;
319 }
320
321 /**
322 * @brief Request X-axis tick labels. The effective range is resolved
323 * from X mappings at done() time unless @p range is supplied.
324 */
326 uint32_t count,
327 TickEdge edge = TickEdge::Bottom,
328 uint8_t decimal_places = 2,
329 glm::vec4 color = { 0.65F, 0.65F, 0.65F, 1.F })
330 {
331 m_ticks.push_back(TickRequest {
332 .axis = TickAxis::X,
333 .range = std::nullopt,
334 .count = count,
335 .edge = edge,
336 .color = color,
337 .decimal_places = decimal_places,
338 .name_prefix = "x_tick",
339 });
340 return *this;
341 }
342
343 /**
344 * @brief Request X-axis tick labels with an explicit display range.
345 */
347 AxisRange range,
348 uint32_t count,
349 TickEdge edge = TickEdge::Bottom,
350 uint8_t decimal_places = 2,
351 glm::vec4 color = { 0.65F, 0.65F, 0.65F, 1.F })
352 {
353 m_ticks.push_back(TickRequest {
354 .axis = TickAxis::X,
355 .range = std::move(range),
356 .count = count,
357 .edge = edge,
358 .color = color,
359 .decimal_places = decimal_places,
360 .name_prefix = "x_tick",
361 });
362 return *this;
363 }
364
365 /**
366 * @brief Request Y-axis tick labels. The effective range is resolved
367 * from Y mappings at done() time unless @p range is supplied.
368 */
370 uint32_t count,
371 TickEdge edge = TickEdge::Left,
372 uint8_t decimal_places = 2,
373 glm::vec4 color = { 0.65F, 0.65F, 0.65F, 1.F })
374 {
375 m_ticks.push_back(TickRequest {
376 .axis = TickAxis::Y,
377 .range = std::nullopt,
378 .count = count,
379 .edge = edge,
380 .color = color,
381 .decimal_places = decimal_places,
382 .name_prefix = "y_tick",
383 });
384 return *this;
385 }
386
387 /**
388 * @brief Request Y-axis tick labels with an explicit display range.
389 */
391 AxisRange range,
392 uint32_t count,
393 TickEdge edge = TickEdge::Left,
394 uint8_t decimal_places = 2,
395 glm::vec4 color = { 0.65F, 0.65F, 0.65F, 1.F })
396 {
397 m_ticks.push_back(TickRequest {
398 .axis = TickAxis::Y,
399 .range = std::move(range),
400 .count = count,
401 .edge = edge,
402 .color = color,
403 .decimal_places = decimal_places,
404 .name_prefix = "y_tick",
405 });
406 return *this;
407 }
408
409 /**
410 * @brief Request tick labels on an arbitrary edge with an explicit range.
411 */
413 TickEdge edge,
414 AxisRange range,
415 uint32_t count,
416 uint8_t decimal_places = 2,
417 glm::vec4 color = { 0.65F, 0.65F, 0.65F, 1.F })
418 {
419 m_ticks.push_back(TickRequest {
420 .axis = TickAxis::Explicit,
421 .range = std::move(range),
422 .count = count,
423 .edge = edge,
424 .color = color,
425 .decimal_places = decimal_places,
426 .name_prefix = "tick",
427 });
428 return *this;
429 }
430
431 /**
432 * @brief Request an automatic legend. Entries may be resolved later from
433 * the PlotContainer by Plot::place().
434 */
435 Series& legend(glm::vec2 origin)
436 {
437 m_legend = LegendSpec {
438 .origin = origin,
439 .entries = {},
440 };
441 return *this;
442 }
443
444 /**
445 * @brief Set a manual legend.
446 */
447 Series& legend(glm::vec2 origin, std::initializer_list<LegendEntry> entries)
448 {
449 LegendSpec spec {
450 .origin = origin,
451 .entries = entries,
452 };
453 m_legend = std::move(spec);
454 return *this;
455 }
456
457 /**
458 * @brief Set a manual legend from a pre-built spec.
459 */
461 {
462 m_legend = std::move(spec);
463 return *this;
464 }
465
466 // =========================================================================
467 // Encoding terminals
468 // =========================================================================
469
470 [[nodiscard]] WaveformBuilder as_waveform() const;
471 [[nodiscard]] ScatterBuilder as_scatter() const;
472 [[nodiscard]] BarsBuilder as_bars() const;
473 [[nodiscard]] FilledWaveformBuilder as_filled_waveform() const;
474
475 // =========================================================================
476 // State accessors — used by encoding terminals in .cpp
477 // =========================================================================
478
479 [[nodiscard]] const std::vector<AxisMapping>& x_mappings() const { return m_x; }
480 [[nodiscard]] const std::vector<AxisMapping>& y_mappings() const { return m_y; }
481 [[nodiscard]] const std::vector<AxisMapping>& z_mappings() const { return m_z; }
482 [[nodiscard]] const std::vector<glm::vec3>& palette() const { return m_palette; }
483 [[nodiscard]] bool has_background() const { return m_has_background; }
484 [[nodiscard]] Kinesis::AABB2D background_bounds() const { return m_background_bounds; }
485 [[nodiscard]] glm::vec3 background_color() const { return m_background_color; }
486
487 [[nodiscard]] const std::optional<Kinesis::AABB2D>& plot_bounds() const { return m_plot_bounds; }
488 [[nodiscard]] const std::vector<LabelSpec>& labels() const { return m_labels; }
489 [[nodiscard]] const std::optional<LegendSpec>& legend_spec() const { return m_legend; }
490
491 /**
492 * @brief Resolve pending fluent tick requests into concrete tick label specs.
493 */
494 [[nodiscard]] std::vector<TickLabelsSpec> resolved_tick_labels() const;
495
496private:
497 std::vector<AxisMapping> m_x;
498 std::vector<AxisMapping> m_y;
499 std::vector<AxisMapping> m_z;
500 std::vector<glm::vec3> m_palette;
501 glm::vec3 m_background_color {};
502 bool m_has_background {};
503 Kinesis::AABB2D m_background_bounds {};
504
505 std::optional<Kinesis::AABB2D> m_plot_bounds;
506 std::vector<LabelSpec> m_labels;
507 std::vector<TickRequest> m_ticks;
508 std::optional<LegendSpec> m_legend;
509};
510
511// =============================================================================
512// Encoding builders
513// =============================================================================
514
515/**
516 * @class WaveformBuilder
517 * @brief Terminal builder for LINE_STRIP waveform encoding.
518 *
519 * Reads all Y-axis mappings as line series. X-axis mappings provide
520 * explicit sample positions; when absent, positions are distributed
521 * uniformly. Z-axis mappings map to vertex Z when present.
522 *
523 * N Y-series in the container = N LINE_STRIP runs, one per series,
524 * separated by degenerate vertices, sharing one FormaBuffer.
525 * Colors are resolved per series: per-mapping palette first, global
526 * palette as fallback, white if both are empty.
527 *
528 * Roles not consumed by this encoding remain in the container and are
529 * available to any other GeometryFn produced from the same Series.
530 *
531 * .done() produces a GeometryFn<shared_ptr<PlotContainer>>.
532 */
533class MAYAFLUX_API WaveformBuilder {
534public:
536 : m_state(std::move(state))
537 {
538 }
539
540 /** @brief Line thickness in pixels. Default 1.5. */
542 {
543 m_thickness = t;
544 return *this;
545 }
546
547 [[nodiscard]] SeriesSpec done() const;
548
549private:
551 float m_thickness { 1.5F };
552};
553
554/**
555 * @class ScatterBuilder
556 * @brief Terminal builder for POINT_LIST scatter encoding.
557 *
558 * Pairs X and Y axis mappings into (x[i], y[i]) point series. Z mappings
559 * map to vertex Z. N X/Y pairs = N point clouds in one FormaBuffer.
560 * Colors resolved per series: per-mapping palette first, global fallback.
561 *
562 * .done() produces a GeometryFn<shared_ptr<PlotContainer>>.
563 */
564class MAYAFLUX_API ScatterBuilder {
565public:
567 : m_state(std::move(state))
568 {
569 }
570
571 /** @brief Point size in pixels. Default 4. */
573 {
574 m_point_size = s;
575 return *this;
576 }
577
578 [[nodiscard]] SeriesSpec done() const;
579
580private:
582 float m_point_size { 4.F };
583};
584
585/**
586 * @class BarsBuilder
587 * @brief Terminal builder for TRIANGLE_LIST bar chart encoding.
588 *
589 * X mappings provide bar positions; absent = uniform tiling.
590 * Y mappings provide bar heights. N Y-series = N bar sets.
591 * Colors resolved per series: per-mapping palette first, global fallback.
592 * Z is unused by this encoding.
593 *
594 * .done() produces a GeometryFn<shared_ptr<PlotContainer>>.
595 */
596class MAYAFLUX_API BarsBuilder {
597public:
599 : m_state(std::move(state))
600 {
601 }
602
603 [[nodiscard]] SeriesSpec done() const;
604
605private:
607};
608
609/**
610 * @class FilledWaveformBuilder
611 * @brief Terminal builder for filled waveform encoding (TRIANGLE_STRIP).
612 *
613 * For each Y-series, produces a TRIANGLE_STRIP quad strip between the signal
614 * value and a baseline Y (default: y_range.to_ndc(0), clamped to bounds).
615 * Each sample pair contributes two vertices: one at the signal, one at
616 * the baseline. The strip is continuous within a series; separate series
617 * are separated by degenerate vertices.
618 *
619 * Shares all Y-axis, X-axis, palette, and auto-scale machinery with
620 * WaveformBuilder. Use when the filled area under the signal carries
621 * meaning: envelope display, energy readout, spectral band fill.
622 *
623 * .done() produces a SeriesSpec with TRIANGLE_STRIP topology.
624 */
625class MAYAFLUX_API FilledWaveformBuilder {
626public:
628 : m_state(std::move(state))
629 {
630 }
631
632 /**
633 * @brief Override the baseline Y value in data coordinates.
634 *
635 * Defaults to 0.0 (maps to y_range.to_ndc(0)). Set to the range minimum
636 * to always fill downward, or to the range maximum to always fill upward.
637 */
639 {
640 m_baseline = y;
641 return *this;
642 }
643
644 [[nodiscard]] SeriesSpec done() const;
645
646private:
648 float m_baseline { 0.F };
649};
650
651inline WaveformBuilder Series::as_waveform() const { return { *this }; }
652inline ScatterBuilder Series::as_scatter() const { return { *this }; }
653inline BarsBuilder Series::as_bars() const { return { *this }; }
654inline FilledWaveformBuilder Series::as_filled_waveform() const { return { *this }; }
655
656} // namespace MayaFlux::Portal::Forma::Plot
size_t count
Terminal builder for TRIANGLE_LIST bar chart encoding.
FilledWaveformBuilder & baseline(float y)
Override the baseline Y value in data coordinates.
Terminal builder for filled waveform encoding (TRIANGLE_STRIP).
ScatterBuilder & point_size(float s)
Point size in pixels.
Terminal builder for POINT_LIST scatter encoding.
const std::vector< LabelSpec > & labels() const
std::optional< LegendSpec > m_legend
Series & y(Role role, AxisRange range={})
Map a single role to the Y axis.
Kinesis::AABB2D background_bounds() const
const std::vector< AxisMapping > & z_mappings() const
Series & ticks(TickEdge edge, AxisRange range, uint32_t count, uint8_t decimal_places=2, glm::vec4 color={ 0.65F, 0.65F, 0.65F, 1.F })
Request tick labels on an arbitrary edge with an explicit range.
Series & legend(glm::vec2 origin, std::initializer_list< LegendEntry > entries)
Set a manual legend.
const std::vector< AxisMapping > & x_mappings() const
const std::optional< LegendSpec > & legend_spec() const
Series & label(std::string text, Kinesis::AABB2D bounds, glm::vec4 color={ 0.85F, 0.85F, 0.85F, 1.F }, std::string name={})
Add a construction-free text label to this plot.
Series & color(std::initializer_list< glm::vec3 > colors)
Append colors to the global palette.
Series & y(Role role, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map a single role to the Y axis with per-mapping colors.
Series & legend(LegendSpec spec)
Set a manual legend from a pre-built spec.
Series & z(Role role, AxisRange range={})
Map a single role to the Z axis.
const std::optional< Kinesis::AABB2D > & plot_bounds() const
const std::vector< glm::vec3 > & palette() const
Series & bounds(Kinesis::AABB2D bounds)
Set the logical plot/data bounds used by labels, tick labels, legends, and future plot adornments.
Series & legend(glm::vec2 origin)
Request an automatic legend.
Series & background(Kinesis::AABB2D bounds, glm::vec3 color=glm::vec3(0.08F))
Set a solid-color background quad for this plot area.
Series & x(Role role, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map a single role to the X axis with per-mapping colors.
Series & x(std::vector< Role > roles, AxisRange range={})
Map multiple roles to the X axis with a shared range.
std::optional< Kinesis::AABB2D > m_plot_bounds
Series & y_ticks(uint32_t count, TickEdge edge=TickEdge::Left, uint8_t decimal_places=2, glm::vec4 color={ 0.65F, 0.65F, 0.65F, 1.F })
Request Y-axis tick labels.
std::vector< TickRequest > m_ticks
Series & x(Role role, AxisRange range={})
Map a single role to the X axis.
const std::vector< AxisMapping > & y_mappings() const
FilledWaveformBuilder as_filled_waveform() const
Series & z(std::vector< Role > roles, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map multiple roles to the Z axis with a shared range and per-mapping colors.
Series & y(std::vector< Role > roles, AxisRange range={})
Map multiple roles to the Y axis with a shared range.
Series & x(std::vector< Role > roles, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map multiple roles to the X axis with a shared range and per-mapping colors.
Series & label(LabelSpec spec)
Add an already-built label spec.
Series & y_ticks(AxisRange range, uint32_t count, TickEdge edge=TickEdge::Left, uint8_t decimal_places=2, glm::vec4 color={ 0.65F, 0.65F, 0.65F, 1.F })
Request Y-axis tick labels with an explicit display range.
Series & z(std::vector< Role > roles, AxisRange range={})
Map multiple roles to the Z axis with a shared range.
Series & z(Role role, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map a single role to the Z axis with per-mapping colors.
Series & x_ticks(uint32_t count, TickEdge edge=TickEdge::Bottom, uint8_t decimal_places=2, glm::vec4 color={ 0.65F, 0.65F, 0.65F, 1.F })
Request X-axis tick labels.
Series & x_ticks(AxisRange range, uint32_t count, TickEdge edge=TickEdge::Bottom, uint8_t decimal_places=2, glm::vec4 color={ 0.65F, 0.65F, 0.65F, 1.F })
Request X-axis tick labels with an explicit display range.
Series & y(std::vector< Role > roles, AxisRange range, std::initializer_list< glm::vec3 > palette)
Map multiple roles to the Y axis with a shared range and per-mapping colors.
Convenience constructor for GeometryFn<shared_ptr<PlotContainer>>.
WaveformBuilder & thickness(float t)
Line thickness in pixels.
Terminal builder for LINE_STRIP waveform encoding.
TickEdge
Edge along which tick labels are placed.
Definition PlotSpec.hpp:21
std::function< void(T value, std::vector< uint8_t > &out_bytes, Element &element)> GeometryFn
Geometry function signature.
Definition Mapped.hpp:64
PrimitiveTopology
Vertex assembly primitive topology.
Role
Semantic role of the dimension.
Definition NDData.hpp:150
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:21
Scalar domain extent for one plot axis.
Definition AxisRange.hpp:22
Construction-free text label description.
Definition PlotSpec.hpp:35
Construction-free vertical legend configuration.
Definition PlotSpec.hpp:107
std::optional< LegendSpec > legend
Optional legend to materialize when the plot is placed.
std::optional< GeometryFn< float > > background_fn
std::vector< TickLabelsSpec > tick_labels
Concrete tick label generation specs resolved at done() time.
std::optional< Kinesis::AABB2D > plot_bounds
Optional plot/data region in NDC used by plot adornments.
Forma::GeometryFn< std::shared_ptr< Kakshya::PlotContainer > > fn
std::vector< LabelSpec > labels
Explicit text labels to materialize when the plot is placed.
std::function< size_t(uint64_t sample_count)> capacity_for
One or more roles mapped to a shared AxisRange and optional palette.
Tick request collected fluently and resolved into TickLabelsSpec by the terminal builder once axis ma...