use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, RwLock};
use crate::datasource::{AppendError, AppendOnlyData, SeriesStore};
use crate::geom::Point;
use crate::render::{LineStyle, MarkerStyle};
use crate::view::Viewport;
static SERIES_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SeriesId(u64);
impl SeriesId {
fn next() -> Self {
Self(SERIES_ID_COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Debug, Clone)]
pub enum SeriesKind {
Line(LineStyle),
Scatter(MarkerStyle),
}
#[derive(Debug)]
pub struct Series {
id: SeriesId,
name: String,
kind: SeriesKind,
data: Arc<RwLock<SeriesStore>>,
visible: bool,
}
impl Series {
pub fn line(name: impl Into<String>) -> Self {
Self {
id: SeriesId::next(),
name: name.into(),
kind: SeriesKind::Line(LineStyle::default()),
data: Arc::new(RwLock::new(SeriesStore::indexed())),
visible: true,
}
}
pub fn scatter(name: impl Into<String>) -> Self {
Self {
id: SeriesId::next(),
name: name.into(),
kind: SeriesKind::Scatter(MarkerStyle::default()),
data: Arc::new(RwLock::new(SeriesStore::indexed())),
visible: true,
}
}
pub(crate) fn with_data(
name: impl Into<String>,
data: AppendOnlyData,
kind: SeriesKind,
) -> Self {
Self {
id: SeriesId::next(),
name: name.into(),
kind,
data: Arc::new(RwLock::new(SeriesStore::with_base_chunk(data, 64))),
visible: true,
}
}
pub fn from_iter_y<I, T>(name: impl Into<String>, iter: I, kind: SeriesKind) -> Self
where
I: IntoIterator<Item = T>,
T: Into<f64>,
{
let data = AppendOnlyData::from_iter_y(iter);
Self::with_data(name, data, kind)
}
pub fn from_iter_points<I>(name: impl Into<String>, iter: I, kind: SeriesKind) -> Self
where
I: IntoIterator<Item = Point>,
{
let data = AppendOnlyData::from_iter_points(iter);
Self::with_data(name, data, kind)
}
pub fn from_explicit_callback(
name: impl Into<String>,
function: impl Fn(f64) -> f64,
x_range: crate::view::Range,
points: usize,
kind: SeriesKind,
) -> Self {
let data = AppendOnlyData::from_explicit_callback(function, x_range, points);
Self::with_data(name, data, kind)
}
pub fn id(&self) -> SeriesId {
self.id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &SeriesKind {
&self.kind
}
pub fn with_kind(mut self, kind: SeriesKind) -> Self {
self.kind = kind;
self
}
pub fn share(&self) -> Self {
Self {
id: SeriesId::next(),
name: self.name.clone(),
kind: self.kind.clone(),
data: Arc::clone(&self.data),
visible: self.visible,
}
}
pub(crate) fn with_store<R>(&self, f: impl FnOnce(&SeriesStore) -> R) -> R {
let data = self.data.read().expect("series data lock");
f(&data)
}
pub fn push_y(&mut self, y: f64) -> Result<usize, AppendError> {
self.with_store_mut(|data| data.push_y(y))
}
pub fn extend_y<I, T>(&mut self, values: I) -> Result<usize, AppendError>
where
I: IntoIterator<Item = T>,
T: Into<f64>,
{
self.with_store_mut(|data| data.extend_y(values))
}
pub fn push_point(&mut self, point: Point) -> Result<usize, AppendError> {
self.with_store_mut(|data| data.push_point(point))
}
pub fn extend_points<I>(&mut self, points: I) -> Result<usize, AppendError>
where
I: IntoIterator<Item = Point>,
{
self.with_store_mut(|data| data.extend_points(points))
}
pub fn bounds(&self) -> Option<Viewport> {
self.with_store(SeriesStore::bounds)
}
pub fn generation(&self) -> u64 {
self.with_store(SeriesStore::generation)
}
pub fn is_visible(&self) -> bool {
self.visible
}
pub fn set_visible(&mut self, visible: bool) {
self.visible = visible;
}
fn with_store_mut<R>(&self, f: impl FnOnce(&mut SeriesStore) -> R) -> R {
let mut data = self.data.write().expect("series data lock");
f(&mut data)
}
}
impl Clone for Series {
fn clone(&self) -> Self {
let data = self.data.read().expect("series data lock").clone();
Self {
id: self.id,
name: self.name.clone(),
kind: self.kind.clone(),
data: Arc::new(RwLock::new(data)),
visible: self.visible,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn share_observes_appends_from_source() {
let mut source = Series::line("shared");
let mut shared = source.share();
let _ = source.extend_y([1.0, 2.0, 3.0]);
assert_eq!(shared.generation(), 3);
let _ = shared.push_y(4.0);
assert_eq!(source.generation(), 4);
assert_eq!(source.bounds(), shared.bounds());
}
#[test]
fn clone_is_independent_copy() {
let mut source = Series::line("sensor");
let mut cloned = source.clone();
let _ = source.push_y(1.0);
assert_eq!(source.generation(), 1);
assert_eq!(cloned.generation(), 0);
let _ = cloned.push_y(2.0);
assert_eq!(source.generation(), 1);
assert_eq!(cloned.generation(), 1);
}
}