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