#![warn(missing_docs)]
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::scene::geometry::{GeometryId, next_geometry_id};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Sample {
pub x: f64,
pub y: f64,
}
impl Sample {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
impl From<(f64, f64)> for Sample {
fn from((x, y): (f64, f64)) -> Self {
Self { x, y }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct SeriesBounds {
pub x: Option<(f64, f64)>,
pub y: Option<(f64, f64)>,
}
impl SeriesBounds {
pub fn of(samples: &[Sample]) -> Self {
let mut x: Option<(f64, f64)> = None;
let mut y: Option<(f64, f64)> = None;
for s in samples {
if s.x.is_finite() {
x = Some(match x {
Some((lo, hi)) => (lo.min(s.x), hi.max(s.x)),
None => (s.x, s.x),
});
}
if s.y.is_finite() {
y = Some(match y {
Some((lo, hi)) => (lo.min(s.y), hi.max(s.y)),
None => (s.y, s.y),
});
}
}
Self { x, y }
}
pub fn union(self, other: SeriesBounds) -> SeriesBounds {
SeriesBounds {
x: union_axis(self.x, other.x),
y: union_axis(self.y, other.y),
}
}
}
fn union_axis(a: Option<(f64, f64)>, b: Option<(f64, f64)>) -> Option<(f64, f64)> {
match (a, b) {
(Some((alo, ahi)), Some((blo, bhi))) => Some((alo.min(blo), ahi.max(bhi))),
(some, None) | (None, some) => some,
}
}
#[derive(Debug)]
struct SeriesStore {
id: GeometryId,
samples: Mutex<Arc<Vec<Sample>>>,
bounds: Mutex<SeriesBounds>,
rev: AtomicU64,
}
#[derive(Clone, Debug)]
pub struct SeriesHandle {
inner: Arc<SeriesStore>,
}
impl SeriesHandle {
pub fn new(samples: impl Into<Vec<Sample>>) -> Self {
let samples = samples.into();
let bounds = SeriesBounds::of(&samples);
Self {
inner: Arc::new(SeriesStore {
id: next_geometry_id(),
samples: Mutex::new(Arc::new(samples)),
bounds: Mutex::new(bounds),
rev: AtomicU64::new(0),
}),
}
}
pub fn empty() -> Self {
Self::new(Vec::new())
}
pub fn set(&self, samples: impl Into<Vec<Sample>>) {
let samples = samples.into();
*self.inner.bounds.lock().unwrap() = SeriesBounds::of(&samples);
*self.inner.samples.lock().unwrap() = Arc::new(samples);
self.inner.rev.fetch_add(1, Ordering::Relaxed);
}
pub fn append(&self, more: &[Sample]) {
if more.is_empty() {
return;
}
{
let mut guard = self.inner.samples.lock().unwrap();
let mut v = guard.as_ref().clone();
v.extend_from_slice(more);
*guard = Arc::new(v);
}
{
let mut b = self.inner.bounds.lock().unwrap();
*b = b.union(SeriesBounds::of(more));
}
self.inner.rev.fetch_add(1, Ordering::Relaxed);
}
pub fn id(&self) -> GeometryId {
self.inner.id
}
pub fn revision(&self) -> u64 {
self.inner.rev.load(Ordering::Relaxed)
}
pub fn bounds(&self) -> SeriesBounds {
*self.inner.bounds.lock().unwrap()
}
pub fn len(&self) -> usize {
self.inner.samples.lock().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn snapshot(&self) -> (Arc<Vec<Sample>>, u64) {
let rev = self.inner.rev.load(Ordering::Relaxed);
(Arc::clone(&self.inner.samples.lock().unwrap()), rev)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bounds_ignore_non_finite() {
let b = SeriesBounds::of(&[
Sample::new(1.0, 10.0),
Sample::new(f64::NAN, 5.0),
Sample::new(3.0, f64::INFINITY),
]);
assert_eq!(b.x, Some((1.0, 3.0)));
assert_eq!(b.y, Some((5.0, 10.0)));
}
#[test]
fn empty_bounds_are_none() {
let b = SeriesBounds::of(&[]);
assert_eq!(b.x, None);
assert_eq!(b.y, None);
}
#[test]
fn set_bumps_revision_and_recomputes_bounds() {
let h = SeriesHandle::new(vec![Sample::new(0.0, 0.0)]);
assert_eq!(h.revision(), 0);
assert_eq!(h.bounds().x, Some((0.0, 0.0)));
h.set(vec![Sample::new(-1.0, 2.0), Sample::new(4.0, 9.0)]);
assert_eq!(h.revision(), 1);
assert_eq!(h.bounds().x, Some((-1.0, 4.0)));
assert_eq!(h.bounds().y, Some((2.0, 9.0)));
}
#[test]
fn append_grows_and_unions_bounds() {
let h = SeriesHandle::new(vec![Sample::new(0.0, 0.0)]);
h.append(&[Sample::new(5.0, -3.0)]);
assert_eq!(h.len(), 2);
assert_eq!(h.revision(), 1);
assert_eq!(h.bounds().x, Some((0.0, 5.0)));
assert_eq!(h.bounds().y, Some((-3.0, 0.0)));
h.append(&[]);
assert_eq!(h.revision(), 1);
}
#[test]
fn clones_share_storage_and_id() {
let a = SeriesHandle::new(vec![Sample::new(0.0, 0.0)]);
let b = a.clone();
assert_eq!(a.id(), b.id());
b.set(vec![Sample::new(1.0, 1.0), Sample::new(2.0, 2.0)]);
assert_eq!(a.len(), 2);
assert_eq!(a.revision(), 1);
}
#[test]
fn distinct_handles_have_distinct_ids() {
let a = SeriesHandle::new(Vec::new());
let b = SeriesHandle::new(Vec::new());
assert_ne!(a.id(), b.id());
}
}