18 std::vector<std::span<const double>> series_for_mapping(
19 const Kakshya::PlotContainer& container,
20 const Series::AxisMapping& mapping)
22 std::vector<std::span<const double>> result;
23 for (
const auto& role : mapping.roles) {
25 result.insert(result.end(), s.begin(), s.end());
33 AxisRange merge_axis(std::vector<Series::AxisMapping>& mappings)
37 if (mappings.size() == 1)
38 return mappings[0].range;
40 AxisRange merged = mappings[0].range;
41 for (
size_t i = 1; i < mappings.size(); ++i) {
42 const auto& r = mappings[i].range;
43 merged.min = std::min(merged.min, r.min);
44 merged.max = std::max(merged.max, r.max);
46 merged.auto_scaling =
true;
47 if (!merged.scale_predicate && r.scale_predicate)
48 merged.scale_predicate = r.scale_predicate;
53 AxisRange merge_axis_const(
const std::vector<Series::AxisMapping>& mappings)
56 return merge_axis(copy);
62 glm::vec3 resolve_color(
63 const Series::AxisMapping& mapping,
64 const std::vector<glm::vec3>& global_palette,
67 if (!mapping.palette.empty())
72 std::vector<float> uniform_x(
size_t n,
const AxisRange& x_range)
74 std::vector<float> xs(n);
76 xs[0] = x_range.to_ndc((x_range.min + x_range.max) * 0.5F);
79 for (
size_t i = 0; i < n; ++i) {
80 const float t =
static_cast<float>(i) /
static_cast<float>(n - 1);
81 xs[i] = x_range.to_ndc(x_range.min + t * (x_range.max - x_range.min));
96 constexpr uint32_t k_stride = 60;
97 constexpr uint32_t k_off_pos = 0;
98 constexpr uint32_t k_off_color = 12;
99 constexpr uint32_t k_off_scalar = 24;
101 constexpr glm::vec3 k_default_normal { 0.F, 0.F, 1.F };
102 constexpr glm::vec3 k_default_tangent { 1.F, 0.F, 0.F };
103 constexpr glm::vec2 k_default_uv { 0.F, 0.F };
106 std::vector<uint8_t>& out,
111 const size_t base = out.size();
112 out.resize(base + k_stride, 0);
113 uint8_t* v = out.data() + base;
114 std::memcpy(v + k_off_pos, &pos, 12);
115 std::memcpy(v + k_off_color, &color, 12);
116 std::memcpy(v + k_off_scalar, &scalar, 4);
117 std::memcpy(v + 28, &k_default_uv, 8);
118 std::memcpy(v + 36, &k_default_normal, 12);
119 std::memcpy(v + 48, &k_default_tangent, 12);
130 std::vector<TickLabelsSpec> resolved;
134 resolved.reserve(
m_ticks.size());
136 for (
const auto& req :
m_ticks) {
143 range = merge_axis_const(
m_x);
146 range = merge_axis_const(
m_y);
157 .range = std::move(range),
161 .decimal_places = req.decimal_places,
162 .label_h = req.label_h,
163 .label_w = req.label_w,
164 .name_prefix = req.name_prefix,
184 .fn = [x_mappings, y_mappings, z_mappings, global_palette,
thickness](
185 const std::shared_ptr<Kakshya::PlotContainer>& container,
186 std::vector<uint8_t>& out,
191 container->process_default();
194 std::span<const double> data;
195 size_t mapping_index;
196 size_t index_within_mapping;
199 std::vector<SeriesEntry> y_entries;
200 for (
size_t mi = 0; mi < y_mappings.size(); ++mi) {
201 auto series = series_for_mapping(*container, y_mappings[mi]);
202 for (
size_t si = 0; si <
series.size(); ++si)
203 y_entries.push_back({ .data = series[si], .mapping_index = mi, .index_within_mapping = si });
205 if (y_entries.empty())
208 AxisRange x_range = merge_axis(x_mappings);
211 std::vector<std::span<const double>> all_x, all_z;
212 for (
auto& m : x_mappings) {
213 auto s = series_for_mapping(*container, m);
214 all_x.insert(all_x.end(), s.begin(), s.end());
216 for (
auto& m : z_mappings) {
217 auto s = series_for_mapping(*container, m);
218 all_z.insert(all_z.end(), s.begin(), s.end());
221 apply_auto_scale(x_range, all_x.empty() ? std::vector<std::span<const double>> { y_entries[0].data } : all_x);
226 for (
size_t ei = 0; ei < y_entries.size(); ++ei) {
227 const auto& entry = y_entries[ei];
228 const auto& ys = entry.data;
229 const size_t n = ys.size();
233 const glm::vec3 color = resolve_color(
234 y_mappings[entry.mapping_index], global_palette, entry.index_within_mapping);
236 auto y_range = y_mappings[entry.mapping_index].range;
239 const bool has_x = ei < all_x.size() && all_x[ei].size() == n;
240 const bool has_z = ei < all_z.size() && all_z[ei].size() == n;
242 const std::vector<float> xs = has_x
244 std::vector<float> v;
246 for (
double val : all_x[ei])
247 v.push_back(x_range.
to_ndc(
static_cast<float>(val)));
250 : uniform_x(n, x_range);
252 for (
size_t i = 0; i < n; ++i) {
255 y_range.to_ndc(
static_cast<float>(ys[i])),
256 has_z ? z_range.
to_ndc(
static_cast<float>(all_z[ei][i])) : 0.F },
261 if (ei + 1 < y_entries.size()) {
262 const glm::vec3 sep_pos {
264 y_range.to_ndc(
static_cast<float>(ys[n - 1])),
265 has_z ? z_range.
to_ndc(
static_cast<float>(all_z[ei][n - 1])) : 0.F,
267 write_vertex(out, sep_pos, color, 0.F);
268 write_vertex(out, sep_pos, color, 0.F);
272 .capacity_for = [](uint64_t n) {
return (n + 2) * k_stride; },
296 .fn = [x_mappings, y_mappings, z_mappings, global_palette,
point_size](
297 const std::shared_ptr<Kakshya::PlotContainer>& container,
298 std::vector<uint8_t>& out,
303 container->process_default();
305 std::vector<std::span<const double>> all_x, all_z;
306 for (
auto& m : x_mappings) {
307 auto s = series_for_mapping(*container, m);
308 all_x.insert(all_x.end(), s.begin(), s.end());
310 for (
auto& m : z_mappings) {
311 auto s = series_for_mapping(*container, m);
312 all_z.insert(all_z.end(), s.begin(), s.end());
315 AxisRange x_range = merge_axis(x_mappings);
322 size_t global_idx = 0;
323 for (
auto& y_mapping : y_mappings) {
324 auto y_series = series_for_mapping(*container, y_mapping);
325 auto y_range = y_mapping.range;
328 for (
size_t si = 0; si < y_series.size(); ++si, ++global_idx) {
329 const auto& ys = y_series[si];
330 const size_t n = ys.size();
334 const glm::vec3 color = resolve_color(y_mapping, global_palette, si);
335 const size_t x_idx = std::min(global_idx, all_x.empty() ?
size_t(0) : all_x.size() - 1);
336 const bool has_x = !all_x.empty() && all_x[x_idx].size() == n;
337 const bool has_z = global_idx < all_z.size() && all_z[global_idx].size() == n;
339 for (
size_t i = 0; i < n; ++i) {
340 const float px = has_x
341 ? x_range.
to_ndc(
static_cast<float>(all_x[x_idx][i]))
343 +
static_cast<float>(i) /
static_cast<float>(std::max<size_t>(n - 1, 1))
344 * (x_range.
max - x_range.
min));
348 y_range.to_ndc(
static_cast<float>(ys[i])),
349 has_z ? z_range.
to_ndc(
static_cast<float>(all_z[global_idx][i])) : 0.F },
356 .capacity_for = [](uint64_t n) {
return n * k_stride; },
378 .fn = [x_mappings, y_mappings, global_palette](
379 const std::shared_ptr<Kakshya::PlotContainer>& container,
380 std::vector<uint8_t>& out,
385 container->process_default();
390 for (
auto& y_mapping : y_mappings) {
391 auto y_series = series_for_mapping(*container, y_mapping);
392 if (y_series.empty())
395 auto y_range = y_mapping.range;
398 for (
size_t si = 0; si < y_series.size(); ++si) {
399 const auto&
series = y_series[si];
400 const size_t n =
series.size();
404 const glm::vec3 color = resolve_color(y_mapping, global_palette, si);
405 const float bar_w = (x_range.
max - x_range.
min) /
static_cast<float>(n);
406 const float y_base = y_range.to_ndc(0.F);
408 for (
size_t i = 0; i < n; ++i) {
409 const float x_left = x_range.
to_ndc(x_range.
min +
static_cast<float>(i) * bar_w);
410 const float x_right = x_range.
to_ndc(x_range.
min +
static_cast<float>(i + 1) * bar_w);
411 const float y_top = y_range.to_ndc(
static_cast<float>(
series[i]));
413 write_vertex(out, { x_left, y_base, 0.F }, color, 0.F);
414 write_vertex(out, { x_right, y_base, 0.F }, color, 0.F);
415 write_vertex(out, { x_left, y_top, 0.F }, color, 0.F);
416 write_vertex(out, { x_right, y_base, 0.F }, color, 0.F);
417 write_vertex(out, { x_right, y_top, 0.F }, color, 0.F);
418 write_vertex(out, { x_left, y_top, 0.F }, color, 0.F);
423 .capacity_for = [](uint64_t n) {
return n * 6 * k_stride; },
442 .fn = [x_mappings, y_mappings, global_palette, baseline_data](
443 const std::shared_ptr<Kakshya::PlotContainer>& container,
444 std::vector<uint8_t>& out,
449 container->process_default();
452 std::span<const double> data;
453 size_t mapping_index;
454 size_t index_within_mapping;
456 std::vector<SeriesEntry> y_entries;
457 for (
size_t mi = 0; mi < y_mappings.size(); ++mi) {
458 auto sv = series_for_mapping(*container, y_mappings[mi]);
459 for (
size_t si = 0; si < sv.size(); ++si)
460 y_entries.push_back({ .data = sv[si], .mapping_index = mi, .index_within_mapping = si });
462 if (y_entries.empty())
465 AxisRange x_range = merge_axis(x_mappings);
466 AxisRange y_range = merge_axis(y_mappings);
468 std::vector<std::span<const double>> all_y;
469 all_y.reserve(y_entries.size());
470 for (
const auto& e : y_entries)
471 all_y.push_back(e.data);
474 std::vector<std::span<const double>> all_x;
475 if (!x_mappings.empty()) {
476 all_x = series_for_mapping(*container, x_mappings[0]);
482 const float baseline_ndc = y_range.
to_ndc(baseline_data);
486 for (
size_t ei = 0; ei < y_entries.size(); ++ei) {
487 const auto& entry = y_entries[ei];
488 const size_t n = entry.data.size();
492 const glm::vec3 color = resolve_color(
493 y_mappings[entry.mapping_index], global_palette, entry.index_within_mapping);
495 const size_t x_idx = std::min(ei, all_x.empty() ?
size_t(0) : all_x.size() - 1);
496 const bool has_x = !all_x.empty() && all_x[x_idx].size() == n;
498 const size_t pre_size = out.size();
500 for (
size_t i = 0; i < n; ++i) {
501 const float px = has_x
502 ? x_range.
to_ndc(
static_cast<float>(all_x[x_idx][i]))
504 +
static_cast<float>(i) /
static_cast<float>(std::max<size_t>(n - 1, 1))
505 * (x_range.
max - x_range.
min));
507 const float py = y_range.
to_ndc(
static_cast<float>(entry.data[i]));
509 write_vertex(out, { px, py, 0.F }, color, 1.F);
510 write_vertex(out, { px, baseline_ndc, 0.F }, color, 0.F);
513 if (ei + 1 < y_entries.size() && out.size() > pre_size) {
514 const size_t last = out.size() - k_stride;
515 out.insert(out.end(), out.begin() +
static_cast<ptrdiff_t
>(last), out.end());
519 .capacity_for = [](uint64_t n) {
return n * 2 * k_stride + 128; },
521 ? std::optional<GeometryFn<float>> {
background(
Role
Semantic role of the dimension.