The repository had no licence at all, so every source was all-rights-reserved by default. Add the verbatim MIT text at LICENSE (Copyright (c) 2026 Martino Ferrari) and a two-line SPDX header to each of the 113 first-party sources -- 48 include/plot, 26 src, 10 adapters, 5 examples, 21 tests, 3 tools -- so the grant travels with each file rather than relying on the root document alone. Scripts keep their shebang on line 1; the SPDX lines follow it. Genuinely vendored code is untouched: third_party/stb_truetype.h carries no header of ours. src/render/embedded_mono_font.cpp is generated, so tools/fonts/gen_atlas.py emit() now writes the two lines into its header comment and the file was regenerated -- gen_atlas.py --check passes and the regeneration adds only those comment lines to the previously committed file. README's provenance section now states MIT and links LICENSE. No code, test or public API change.
62 lines
2.4 KiB
C++
62 lines
2.4 KiB
C++
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
|
|
|
#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
|