feat(plot): nice-number ticks, axes, grid, traces, cursors, measurement queries

This commit is contained in:
Martino Ferrari committed 2026-09-27 22:21:22 +02:00
1 parent b4e88fe0c1
commit 93fc06f050
18 files changed
+1582

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
#include <plot/plot/measure.hpp>
#include <cmath>
namespace plot {
Measurement Measure::evaluate(const data::Series& s, u64 begin, u64 end) {
Measurement m;
// Both guards run before any query, so an empty, inverted or out-of-range
// interval returns an invalid result without touching the series.
if (end <= begin || end > s.size()) return m;
// Exact extremes and sums come from Series -- never from the pyramid's
// *_buckets forms, which answer in whole buckets only and would drop up to two
// partial edge buckets, silently returning wrong extrema and sums for any
// interval that is not bucket-aligned. Each query here is O(kBucket) worst
// case, so a measurement never scales with series length.
const Range extremes = s.reduce(begin, end);
const f64 total = s.sum(begin, end);
const f64 squared = s.sumsq(begin, end);
const f64 n = static_cast<f64>(end - begin);
const f64 step = s.dx();
m.ymin = extremes.min;
m.ymax = extremes.max;
m.mean = total / n;
m.rms = std::sqrt(squared / n);
m.integral = total * step; // left-Riemann sum: see the note in measure.hpp
if (end - begin >= 2) {
// First and last sample of the interval: a single-sample query reduces to
// that sample's value, and costs the same O(kBucket) as any other.
const f64 first = s.reduce(begin, begin + 1).min;
const f64 last = s.reduce(end - 1, end).min;
m.dy = last - first;
m.dx = static_cast<f64>(end - 1 - begin) * step;
m.slope = m.dx != 0.0 ? m.dy / m.dx : 0.0;
}
m.valid = true;
return m;
}
} // namespace plot