// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #pragma once #include #include #include namespace plot::data { /// Series over a uniform-dx sample vector. /// /// The vector is taken by value and owned from then on, so the caller cannot /// mutate the samples behind the series' back; the store changes only through /// reset() (a whole new vector) or commit(), which rebuilds the pyramid and the /// cached bounds for whatever y_ currently holds. class ArraySeries final : public Series { public: ArraySeries(std::vector y, f64 x0, f64 dx); /// Replaces samples and grid, then rebuilds derived state (== commit()). void reset(std::vector y, f64 x0, f64 dx); /// Rebuilds the pyramid and cached bounds from y_ as it stands now. void commit(); /// Appends one sample incrementally: the pyramid folds it (`append`, amortised /// O(1)) and the cached bounds extend in O(1), NaN-aware, instead of commit() /// rebuilding the whole store. The wholesale path stays `commit()`. void push(f32 y); u64 size() const override; f64 x0() const override; f64 dx() const override; Range x_bounds() const override; Range y_bounds() const override; const MinMaxPyramid& pyramid() const override; void gather(u32 level, u64 begin, u64 end, std::vector& out_xy, GatherMode mode = GatherMode::Envelope) const override; protected: void copy_raw(u64 begin, u64 end, f32* out) const override; private: std::vector y_; f64 x0_ = 0.0; f64 dx_ = 1.0; MinMaxPyramid pyramid_; Range x_bounds_{}; Range y_bounds_{}; /// False until a finite sample has been seen: `y_bounds_` is then a /// placeholder (zero-span), not an aggregate, and the next finite sample /// seeds it. This is what makes push()'s incremental extension and commit()'s /// rebuild agree on a window that holds non-finite samples. bool y_has_finite_ = false; }; } // namespace plot::data