Files
plotter/include/plot/plot/measure.hpp
T

59 lines
2.3 KiB
C++

#pragma once
#include <plot/core/types.hpp>
#include <plot/data/series.hpp>
namespace plot {
/// Interval statistics over the half-open sample range [begin, end).
///
/// `dx` is the x distance between the first and the last sample of the interval
/// (0 for a single-sample interval), `dy` the corresponding y difference,
/// `slope` = `dy / dx`, `mean` and `rms` over the `end - begin` samples, `ymin`
/// / `ymax` the exact extremes, and `integral` the area under the samples.
///
/// `integral` is deliberately a LEFT-RIEMANN sum, `sum(begin, end) * dx`: the
/// series' samples are taken as the value at the left edge of each step. A
/// trapezoid rule would be equally defensible and compute a different number for
/// every reported interval, so this choice is part of the result's contract, not
/// an implementation detail. Do not "fix" it without changing every documented
/// value and every test that pins one.
///
/// `valid` is false (and every field zero) for an interval that is empty,
/// inverted or out of range; such a call never reads the series.
struct Measurement {
f64 dx = 0.0;
f64 dy = 0.0;
f64 slope = 0.0;
f64 rms = 0.0;
f64 mean = 0.0;
f64 ymin = 0.0;
f64 ymax = 0.0;
f64 integral = 0.0;
bool valid = false;
};
/// The cursor pair a measure spans, as the plot stored it. `valid` is false for
/// an id that names no measure and for a measure whose cursors are not both
/// registered, so a caller can tell "no band to shade" from "a band between
/// cursors 0 and 0". The renderer uses it to place the band a measurement's
/// readout belongs to.
struct MeasureSpan {
u32 cursor_a = 0;
u32 cursor_b = 0;
bool valid = false;
};
/// Stateless interval measurement over a data::Series.
struct Measure {
/// Evaluates [begin, end). Every quantity comes from `Series::reduce`/`sum`/
/// `sumsq` -- never from the pyramid's `*_buckets` forms, which exclude partial
/// edge buckets and would silently return wrong extrema and sums for a range
/// that is not bucket-aligned. Each of those queries is O(kBucket) worst case
/// (bucket-aligned interior from the pyramid, at most two partial edge buckets
/// read raw), so a measurement never scans the series and its cost does not
/// grow with series length.
static Measurement evaluate(const data::Series& s, u64 begin, u64 end);
};
} // namespace plot