mod bar;
mod line;
mod stack;
mod use_data;
mod use_y;
pub use bar::{Bar, BarPlacement, BAR_GAP, BAR_GAP_INNER};
pub use line::{
Interpolation, Line, Marker, MarkerShape, Step, DIVERGING_GRADIENT, LINEAR_GRADIENT,
};
pub use stack::{Stack, STACK_COLOUR_SCHEME};
pub use use_data::{RenderData, UseData};
pub use use_y::{Snippet, UseY};
use crate::{
colours::{Colour, ColourScheme},
Tick,
};
use leptos::prelude::*;
use std::sync::Arc;
pub const SERIES_COLOUR_SCHEME: [Colour; 10] = [
Colour::from_rgb(0x12, 0xA5, 0xED), Colour::from_rgb(0xF5, 0x32, 0x5B), Colour::from_rgb(0x71, 0xc6, 0x14), Colour::from_rgb(0xFF, 0x84, 0x00), Colour::from_rgb(0x7b, 0x4d, 0xff), Colour::from_rgb(0xdb, 0x4c, 0xb2), Colour::from_rgb(0x92, 0xb4, 0x2c), Colour::from_rgb(0xFF, 0xCA, 0x00), Colour::from_rgb(0x22, 0xd2, 0xba), Colour::from_rgb(0xea, 0x60, 0xdf), ];
type GetX<T, X> = Arc<dyn Fn(&T) -> X + Send + Sync>;
type GetY<T, Y> = Arc<dyn GetYValue<T, Y>>;
trait GetYValue<T, Y>: Send + Sync {
fn value(&self, t: &T) -> Y;
fn stacked_value(&self, t: &T) -> Y;
}
#[derive(Clone)]
#[non_exhaustive]
pub struct Series<T: Send + Sync + 'static, X: Tick, Y: Tick> {
get_x: GetX<T, X>,
series: Vec<Arc<dyn ApplyUseSeries<T, Y> + Send + Sync>>,
pub min_x: RwSignal<Option<X>>,
pub max_x: RwSignal<Option<X>>,
pub min_y: RwSignal<Option<Y>>,
pub max_y: RwSignal<Option<Y>>,
pub colours: RwSignal<ColourScheme>,
}
trait ApplyUseSeries<T, Y> {
fn apply_use_series(self: Arc<Self>, _: &mut SeriesAcc<T, Y>);
}
trait IntoUseLine<T, Y> {
fn into_use_line(self, id: usize, colour: Memo<Colour>) -> (UseY, GetY<T, Y>);
}
trait IntoUseBar<T, Y> {
fn into_use_bar(self, id: usize, group_id: usize, colour: Memo<Colour>) -> (UseY, GetY<T, Y>);
}
struct SeriesAcc<T, Y> {
colour_id: usize,
colours: RwSignal<ColourScheme>,
next_id: usize,
next_group_id: usize,
lines: Vec<(UseY, GetY<T, Y>)>,
}
impl<T: Send + Sync, X: Tick, Y: Tick> Series<T, X, Y> {
pub fn new(get_x: impl Fn(&T) -> X + Send + Sync + 'static) -> Self {
Self {
get_x: Arc::new(get_x),
min_x: RwSignal::default(),
max_x: RwSignal::default(),
min_y: RwSignal::default(),
max_y: RwSignal::default(),
colours: RwSignal::new(SERIES_COLOUR_SCHEME.into()),
series: Vec::new(),
}
}
pub fn with_colours(self, colours: impl Into<ColourScheme>) -> Self {
self.colours.set(colours.into());
self
}
pub fn with_min_x(self, max_x: impl Into<Option<X>>) -> Self {
self.min_x.set(max_x.into());
self
}
pub fn with_max_x(self, max_x: impl Into<Option<X>>) -> Self {
self.max_x.set(max_x.into());
self
}
pub fn with_min_y(self, min_y: impl Into<Option<Y>>) -> Self {
self.min_y.set(min_y.into());
self
}
pub fn with_max_y(self, max_y: impl Into<Option<Y>>) -> Self {
self.max_y.set(max_y.into());
self
}
pub fn with_x_range(self, min_x: impl Into<Option<X>>, max_x: impl Into<Option<X>>) -> Self {
self.with_min_x(min_x).with_max_x(max_x)
}
pub fn with_y_range(self, min_y: impl Into<Option<Y>>, max_y: impl Into<Option<Y>>) -> Self {
self.with_min_y(min_y).with_max_y(max_y)
}
pub fn line(mut self, line: impl Into<Line<T, Y>>) -> Self {
self.series.push(Arc::new(line.into()));
self
}
pub fn lines(mut self, lines: impl IntoIterator<Item = impl Into<Line<T, Y>>>) -> Self {
for line in lines {
self = self.line(line.into());
}
self
}
pub fn bar(mut self, bar: impl Into<Bar<T, Y>>) -> Self {
self.series.push(Arc::new(bar.into()));
self
}
pub fn bars(mut self, bars: impl IntoIterator<Item = impl Into<Bar<T, Y>>>) -> Self {
for bar in bars {
self = self.bar(bar.into());
}
self
}
pub fn len(&self) -> usize {
self.series.len()
}
pub fn is_empty(&self) -> bool {
self.series.is_empty()
}
fn to_use_lines(&self) -> Vec<(UseY, GetY<T, Y>)> {
let mut series = SeriesAcc::new(self.colours);
for seq in self.series.clone() {
seq.apply_use_series(&mut series);
}
series.lines
}
}
impl<T: Send + Sync, X: Tick> Series<T, X, f64> {
pub fn stack(mut self, stack: impl Into<Stack<T, f64>>) -> Self {
self.series.push(Arc::new(stack.into()));
self
}
}
impl<T, Y> SeriesAcc<T, Y> {
fn new(colours: RwSignal<ColourScheme>) -> Self {
Self {
colour_id: 0,
colours,
next_id: 0,
next_group_id: 0,
lines: Vec::new(),
}
}
fn next_colour(&mut self) -> Memo<Colour> {
let id = self.colour_id;
self.colour_id += 1;
let colours = self.colours;
Memo::new(move |_| colours.get().by_index(id))
}
fn push_line(&mut self, colour: Memo<Colour>, line: impl IntoUseLine<T, Y>) -> GetY<T, Y> {
let id = self.next_id;
self.next_id += 1;
let (line, get_y) = line.into_use_line(id, colour);
self.lines.push((line, get_y.clone()));
get_y
}
fn push_bar(&mut self, colour: Memo<Colour>, bar: impl IntoUseBar<T, Y>) -> GetY<T, Y> {
let id = self.next_id;
let group_id = self.next_group_id;
self.next_id += 1;
self.next_group_id += 1;
let (bar, get_y) = bar.into_use_bar(id, group_id, colour);
self.lines.push((bar, get_y.clone()));
get_y
}
}