use crate::{Color, FontName, PltError};
use std::{array, fmt, f64, iter};
#[derive(Clone, Debug)]
pub struct Subplot<'a> {
pub(crate) format: SubplotFormat,
pub(crate) plot_order: Vec<PlotType>,
pub(crate) plot_infos: Vec<PlotInfo<'a>>,
pub(crate) fill_infos: Vec<FillInfo<'a>>,
pub(crate) title: String,
pub(crate) xaxis: AxisBuf,
pub(crate) yaxis: AxisBuf,
pub(crate) secondary_xaxis: AxisBuf,
pub(crate) secondary_yaxis: AxisBuf,
}
impl<'a> Subplot<'a> {
pub fn builder() -> SubplotBuilder<'a> {
SubplotBuilder { desc: SubplotDescriptor::default() }
}
pub fn plotter<'b>(&'b mut self) -> Plotter<'a, 'b> {
Plotter {
subplot: self,
desc: PlotDescriptor::default(),
}
}
pub fn filler<'b>(&'b mut self) -> Filler<'a, 'b> {
Filler {
subplot: self,
desc: FillDescriptor::default(),
}
}
pub fn plot<Xs: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
&mut self,
xs: Xs,
ys: Ys,
) -> Result<(), PltError> {
let plotter = Plotter {
subplot: self,
desc: PlotDescriptor::default(),
};
plotter.plot(xs, ys)
}
pub fn plot_owned<Xs: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
&mut self,
xs: Xs,
ys: Ys,
) -> Result<(), PltError> {
let plotter = Plotter {
subplot: self,
desc: PlotDescriptor::default(),
};
plotter.plot_owned(xs, ys)
}
pub fn step<Xs: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
&mut self,
steps: Xs,
ys: Ys,
) -> Result<(), PltError> {
let plotter = Plotter {
subplot: self,
desc: PlotDescriptor::default(),
};
plotter.step(steps, ys)
}
pub fn step_owned<Xs: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
&mut self,
steps: Xs,
ys: Ys,
) -> Result<(), PltError> {
let plotter = Plotter {
subplot: self,
desc: PlotDescriptor::default(),
};
plotter.step_owned(steps, ys)
}
pub fn fill_between<
Xs: Into<ndarray::ArrayView1<'a, f64>>,
Y1s: Into<ndarray::ArrayView1<'a, f64>>,
Y2s: Into<ndarray::ArrayView1<'a, f64>>,
>(
&mut self,
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Result<(), PltError> {
let filler = Filler {
subplot: self,
desc: FillDescriptor::default(),
};
filler.fill_between(xs, y1s, y2s)
}
pub fn fill_between_owned<
Xs: Into<ndarray::Array1<f64>>,
Y1s: Into<ndarray::Array1<f64>>,
Y2s: Into<ndarray::Array1<f64>>,
>(
&mut self,
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Result<(), PltError> {
let filler = Filler {
subplot: self,
desc: FillDescriptor::default(),
};
filler.fill_between_owned(xs, y1s, y2s)
}
pub fn format(&self) -> &SubplotFormat {
&self.format
}
}
impl<'a> Subplot<'a> {
pub(crate) fn new(desc: &SubplotDescriptor) -> Self {
Self {
format: desc.format.clone(),
plot_order: vec![],
plot_infos: vec![],
fill_infos: vec![],
title: desc.title.to_string(),
xaxis: desc.xaxis.to_buf(),
yaxis: desc.yaxis.to_buf(),
secondary_xaxis: desc.secondary_xaxis.to_buf(),
secondary_yaxis: desc.secondary_yaxis.to_buf(),
}
}
}
impl<'a> Subplot<'a> {
fn plot_desc<D: SeriesData + Clone + 'a>(
&mut self,
desc: PlotDescriptor,
data: D,
) {
let line = if desc.line {
Some(desc.line_format)
} else {
None
};
let marker = if desc.marker {
Some(desc.marker_format)
} else {
None
};
let xaxis = match desc.xaxis {
AxisType::X => &mut self.xaxis,
AxisType::Y => &mut self.yaxis,
AxisType::SecondaryX => &mut self.secondary_xaxis,
AxisType::SecondaryY => &mut self.secondary_yaxis,
};
match xaxis.limit_policy {
Limits::Auto => {
xaxis.span = if let Some((xmin, xmax)) = xaxis.span {
Some((f64::min(xmin, data.xmin()), f64::max(xmax, data.xmax())))
} else {
Some((data.xmin(), data.xmax()))
};
let (xmin, xmax) = xaxis.span.unwrap();
let extent = xmax - xmin;
xaxis.limits = Some((xmin - 0.05 * extent, xmax + 0.05 * extent));
},
Limits::Manual { min: _, max: _ } => {},
};
let yaxis = match desc.yaxis {
AxisType::X => &mut self.xaxis,
AxisType::Y => &mut self.yaxis,
AxisType::SecondaryX => &mut self.secondary_xaxis,
AxisType::SecondaryY => &mut self.secondary_yaxis,
};
match yaxis.limit_policy {
Limits::Auto => {
yaxis.span = if let Some((ymin, ymax)) = yaxis.span {
Some((f64::min(ymin, data.ymin()), f64::max(ymax, data.ymax())))
} else {
Some((data.ymin(), data.ymax()))
};
let (ymin, ymax) = yaxis.span.unwrap();
let extent = ymax - ymin;
yaxis.limits = Some((ymin - 0.05 * extent, ymax + 0.05 * extent));
},
Limits::Manual { min: _, max: _ } => {},
};
self.plot_infos.push(PlotInfo {
label: desc.label.to_string(),
data: Box::new(data),
line,
marker,
xaxis: desc.xaxis,
yaxis: desc.yaxis,
pixel_perfect: desc.pixel_perfect,
});
self.plot_order.push(PlotType::Series);
}
fn fill_between_desc<D: FillData + 'a>(
&mut self,
desc: FillDescriptor,
data: D,
) {
let xaxis = match desc.xaxis {
AxisType::X => &mut self.xaxis,
AxisType::Y => &mut self.yaxis,
AxisType::SecondaryX => &mut self.secondary_xaxis,
AxisType::SecondaryY => &mut self.secondary_yaxis,
};
match xaxis.limit_policy {
Limits::Auto => {
xaxis.span = if let Some((xmin, xmax)) = xaxis.span {
Some((f64::min(xmin, data.xmin()), f64::max(xmax, data.xmax())))
} else {
Some((data.xmin(), data.xmax()))
};
let (xmin, xmax) = xaxis.span.unwrap();
let extent = xmax - xmin;
xaxis.limits = Some((xmin - 0.05 * extent, xmax + 0.05 * extent));
},
Limits::Manual { min: _, max: _ } => {},
};
let yaxis = match desc.yaxis {
AxisType::X => &mut self.xaxis,
AxisType::Y => &mut self.yaxis,
AxisType::SecondaryX => &mut self.secondary_xaxis,
AxisType::SecondaryY => &mut self.secondary_yaxis,
};
match yaxis.limit_policy {
Limits::Auto => {
yaxis.span = if let Some((ymin, ymax)) = yaxis.span {
Some((f64::min(ymin, data.ymin()), f64::max(ymax, data.ymax())))
} else {
Some((data.ymin(), data.ymax()))
};
let (ymin, ymax) = yaxis.span.unwrap();
let extent = ymax - ymin;
yaxis.limits = Some((ymin - 0.05 * extent, ymax + 0.05 * extent));
},
Limits::Manual { min: _, max: _ } => {},
};
self.fill_infos.push(FillInfo {
label: desc.label.to_string(),
data: Box::new(data),
color_override: desc.color_override,
xaxis: desc.xaxis,
yaxis: desc.yaxis,
});
self.plot_order.push(PlotType::Fill);
}
}
pub struct SubplotBuilder<'a> {
desc: SubplotDescriptor<'a>,
}
impl<'a> SubplotBuilder<'a> {
pub fn build(self) -> Subplot<'a> {
Subplot::new(&self.desc)
}
pub fn title(mut self, title: &'a str) -> Self {
self.desc.title = title;
self
}
pub fn format(mut self, format: SubplotFormat) -> Self {
self.desc.format = format;
self
}
pub fn label(mut self, axes: Axes, label: &'a str) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.label = label;
}
self
}
pub fn limits(mut self, axes: Axes, limits: Limits) -> Self {
let axes = self.axes(axes);
for axis in axes {
if let Limits::Manual { min, max } = limits {
axis.limits = Some((min, max));
axis.span = Some((min, max));
}
axis.limit_policy = limits;
}
self
}
pub fn grid(mut self, axes: Axes, grid: Grid) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.grid = grid;
}
self
}
pub fn major_tick_marks(mut self, axes: Axes, spacing: TickSpacing) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.major_tick_marks = spacing.clone();
}
self
}
pub fn major_tick_labels(mut self, axes: Axes, labels: TickLabels) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.major_tick_labels = labels.clone();
}
self
}
pub fn minor_tick_marks(mut self, axes: Axes, spacing: TickSpacing) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.minor_tick_marks = spacing.clone();
}
self
}
pub fn minor_tick_labels(mut self, axes: Axes, labels: TickLabels) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.minor_tick_labels = labels.clone();
}
self
}
pub fn visible(mut self, axes: Axes, visible: bool) -> Self {
let axes = self.axes(axes);
for axis in axes {
axis.visible = visible;
}
self
}
}
impl<'a> SubplotBuilder<'a> {
fn axes<'b>(&'b mut self, axes: Axes) -> Vec<&'b mut AxisDescriptor<&'a str>> {
match axes {
Axes::X => vec![&mut self.desc.xaxis],
Axes::Y => vec![&mut self.desc.yaxis],
Axes::SecondaryX => vec![&mut self.desc.secondary_xaxis],
Axes::SecondaryY => vec![&mut self.desc.secondary_yaxis],
Axes::BothX => vec![
&mut self.desc.xaxis,
&mut self.desc.secondary_xaxis,
],
Axes::BothY => vec![
&mut self.desc.yaxis,
&mut self.desc.secondary_yaxis,
],
Axes::BothPrimary => vec![
&mut self.desc.xaxis,
&mut self.desc.yaxis,
],
Axes::BothSecondary => vec![
&mut self.desc.secondary_xaxis,
&mut self.desc.secondary_yaxis,
],
Axes::All => vec![
&mut self.desc.xaxis,
&mut self.desc.yaxis,
&mut self.desc.secondary_xaxis,
&mut self.desc.secondary_yaxis,
],
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Axes {
X,
Y,
SecondaryX,
SecondaryY,
BothX,
BothY,
BothPrimary,
BothSecondary,
All,
}
#[derive(Clone, Debug)]
pub struct SubplotFormat {
pub default_marker_color: Color,
pub default_fill_color: Color,
pub plot_color: Color,
pub line_width: u32,
pub line_color: Color,
pub grid_color: Color,
pub font_name: FontName,
pub font_size: f32,
pub text_color: Color,
pub tick_length: u32,
pub tick_direction: TickDirection,
pub override_minor_tick_length: Option<u32>,
pub color_cycle: Vec<Color>,
}
impl SubplotFormat {
pub fn dark() -> Self {
let line_color = Color { r: 0.659, g: 0.600, b: 0.518, a: 1.0 };
let color_cycle = vec![
Color { r: 0.271, g: 0.522, b: 0.533, a: 1.0 }, Color { r: 0.839, g: 0.365, b: 0.055, a: 1.0 }, Color { r: 0.596, g: 0.592, b: 0.102, a: 1.0 }, Color { r: 0.694, g: 0.384, b: 0.525, a: 1.0 }, Color { r: 0.800, g: 0.141, b: 0.114, a: 1.0 }, ];
Self {
default_marker_color: line_color,
default_fill_color: Color { r: 1.0, g: 0.0, b: 0.0, a: 0.5 },
plot_color: Color { r: 0.157, g: 0.157, b: 0.157, a: 1.0 },
grid_color: Color { r: 0.250, g: 0.250, b: 0.250, a: 1.0 },
line_width: 2,
line_color,
font_name: FontName::default(),
font_size: 20.0,
text_color: line_color,
tick_length: 8,
tick_direction: TickDirection::Inner,
override_minor_tick_length: None,
color_cycle,
}
}
}
impl Default for SubplotFormat {
fn default() -> Self {
let color_cycle = vec![
Color { r: 0.271, g: 0.522, b: 0.533, a: 1.0 }, Color { r: 0.839, g: 0.365, b: 0.055, a: 1.0 }, Color { r: 0.596, g: 0.592, b: 0.102, a: 1.0 }, Color { r: 0.694, g: 0.384, b: 0.525, a: 1.0 }, Color { r: 0.800, g: 0.141, b: 0.114, a: 1.0 }, ];
Self {
default_marker_color: Color::BLACK,
default_fill_color: Color { r: 1.0, g: 0.0, b: 0.0, a: 0.5 },
plot_color: Color::TRANSPARENT,
line_width: 2,
line_color: Color::BLACK,
grid_color: Color { r: 0.750, g: 0.750, b: 0.750, a: 1.0 },
font_name: FontName::default(),
font_size: 20.0,
text_color: Color::BLACK,
tick_length: 8,
tick_direction: TickDirection::Inner,
override_minor_tick_length: None,
color_cycle,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum TickDirection {
Inner,
Outer,
Both,
}
#[derive(Clone, Debug)]
pub enum TickSpacing {
On,
Auto,
None,
Count(u16),
Manual(Vec<f64>),
}
#[derive(Clone, Debug)]
pub enum TickLabels {
On,
Auto,
None,
Manual(Vec<String>),
}
#[derive(Copy, Clone, Debug)]
pub enum Grid {
Major,
Full,
None,
}
#[derive(Copy, Clone, Debug)]
pub enum Limits {
Auto,
Manual { min: f64, max: f64 },
}
pub struct Plotter<'a, 'b> {
subplot: &'b mut Subplot<'a>,
desc: PlotDescriptor,
}
impl<'a, 'b> Plotter<'a, 'b> {
pub fn plot<Xs: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
self,
xs: Xs,
ys: Ys,
) -> Result<(), PltError> {
let xdata = xs.into();
let ydata = ys.into();
if xdata.len() != ydata.len() {
return Err(PltError::InvalidData(
"Data is not correctly sized. x-data and y-data should be same length".to_owned()
));
} else if xdata.iter().any(|x| x.is_nan()) {
return Err(PltError::InvalidData("x-data has NaN value".to_owned()));
} else if ydata.iter().any(|y| y.is_nan()) {
return Err(PltError::InvalidData("y-data has NaN value".to_owned()));
}
let data = PlotData::new(xdata, ydata);
self.subplot.plot_desc(self.desc, data);
Ok(())
}
pub fn plot_owned<Xs: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
self,
xs: Xs,
ys: Ys,
) -> Result<(), PltError> {
let xdata = xs.into();
let ydata = ys.into();
if xdata.len() != ydata.len() {
return Err(PltError::InvalidData(
"Data is not correctly sized. x-data and y-data should be same length".to_owned()
));
} else if xdata.iter().any(|x| x.is_nan()) {
return Err(PltError::InvalidData("x-data has NaN value".to_owned()));
} else if ydata.iter().any(|y| y.is_nan()) {
return Err(PltError::InvalidData("y-data has NaN value".to_owned()));
}
let data = PlotDataOwned::new(xdata, ydata);
self.subplot.plot_desc(self.desc, data);
Ok(())
}
pub fn step<Xs: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
mut self,
steps: Xs,
ys: Ys,
) -> Result<(), PltError> {
let step_data = steps.into();
let ydata = ys.into();
if step_data.len() != ydata.len() + 1 {
return Err(PltError::InvalidData(
"Data is not correctly sized. There should be one more step than y-value".to_owned()
));
} else if step_data.iter().any(|step| step.is_nan()) {
return Err(PltError::InvalidData("step-data has NaN value".to_owned()));
} else if ydata.iter().any(|y| y.is_nan()) {
return Err(PltError::InvalidData("y-data has NaN value".to_owned()));
}
self.desc.pixel_perfect = true;
let data = StepData::new(step_data, ydata);
self.subplot.plot_desc(self.desc, data);
Ok(())
}
pub fn step_owned<Xs: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
mut self,
steps: Xs,
ys: Ys,
) -> Result<(), PltError> {
let step_data = steps.into();
let ydata = ys.into();
if step_data.len() != ydata.len() + 1 {
return Err(PltError::InvalidData(
"Data is not correctly sized. There should be one more step than y-value".to_owned()
));
} else if step_data.iter().any(|step| step.is_nan()) {
return Err(PltError::InvalidData("step-data has NaN value".to_owned()));
} else if ydata.iter().any(|y| y.is_nan()) {
return Err(PltError::InvalidData("y-data has NaN value".to_owned()));
}
self.desc.pixel_perfect = true;
let data = StepDataOwned::new(step_data, ydata);
self.subplot.plot_desc(self.desc, data);
Ok(())
}
pub fn use_secondary_xaxis(mut self) -> Self {
self.desc.xaxis = AxisType::SecondaryX;
self
}
pub fn use_secondary_yaxis(mut self) -> Self {
self.desc.yaxis = AxisType::SecondaryY;
self
}
pub fn label<S: AsRef<str>>(mut self, label: S) -> Self {
self.desc.label = label.as_ref().to_string();
self
}
pub fn line(mut self, line_style: Option<LineStyle>) -> Self {
if let Some(line_style) = line_style {
self.desc.line = true;
self.desc.line_format.style = line_style;
} else {
self.desc.line = false;
}
self
}
pub fn line_width(mut self, width: u32) -> Self {
self.desc.line_format.width = width;
self
}
pub fn line_color(mut self, color: Color) -> Self {
self.desc.line_format.color_override = Some(color);
self
}
pub fn marker(mut self, marker_style: Option<MarkerStyle>) -> Self {
if let Some(marker_style) = marker_style {
self.desc.marker = true;
self.desc.marker_format.style = marker_style;
} else {
self.desc.marker = false;
}
self
}
pub fn marker_size(mut self, size: u32) -> Self {
self.desc.marker_format.size = size;
self
}
pub fn marker_color(mut self, color: Color) -> Self {
self.desc.marker_format.color_override = Some(color);
self
}
pub fn marker_outline(mut self, on: bool) -> Self {
self.desc.marker_format.outline = on;
self
}
pub fn marker_outline_color(mut self, color: Color) -> Self {
self.desc.marker_format.outline_format.color_override = Some(color);
self
}
pub fn marker_outline_width(mut self, width: u32) -> Self {
self.desc.marker_format.outline_format.width = width;
self
}
pub fn marker_outline_style(mut self, line_style: LineStyle) -> Self {
self.desc.marker_format.outline_format.style = line_style;
self
}
}
pub struct Filler<'a, 'b> {
subplot: &'b mut Subplot<'a>,
desc: FillDescriptor,
}
impl<'a, 'b> Filler<'a, 'b> {
pub fn fill_between<
Xs: Into<ndarray::ArrayView1<'a, f64>>,
Y1s: Into<ndarray::ArrayView1<'a, f64>>,
Y2s: Into<ndarray::ArrayView1<'a, f64>>,
>(
self,
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Result<(), PltError> {
let data = FillBetweenData::new(xs, y1s, y2s);
self.subplot.fill_between_desc(self.desc, data);
Ok(())
}
pub fn fill_between_owned<
Xs: Into<ndarray::Array1<f64>>,
Y1s: Into<ndarray::Array1<f64>>,
Y2s: Into<ndarray::Array1<f64>>,
>(
self,
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Result<(), PltError> {
let data = FillBetweenDataOwned::new(xs, y1s, y2s);
self.subplot.fill_between_desc(FillDescriptor::default(), data);
Ok(())
}
pub fn use_secondary_xaxis(mut self) -> Self {
self.desc.xaxis = AxisType::SecondaryX;
self
}
pub fn use_secondary_yaxis(mut self) -> Self {
self.desc.yaxis = AxisType::SecondaryY;
self
}
pub fn label<S: AsRef<str>>(mut self, label: S) -> Self {
self.desc.label = label.as_ref().to_string();
self
}
pub fn color(mut self, color: Color) -> Self {
self.desc.color_override = Some(color);
self
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
pub enum LineStyle {
Solid,
Dashed,
ShortDashed,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
pub enum MarkerStyle {
Circle,
Square,
}
#[derive(Clone, Debug)]
pub(crate) struct SubplotDescriptor<'a> {
pub format: SubplotFormat,
pub title: &'a str,
pub xaxis: AxisDescriptor<&'a str>,
pub yaxis: AxisDescriptor<&'a str>,
pub secondary_xaxis: AxisDescriptor<&'a str>,
pub secondary_yaxis: AxisDescriptor<&'a str>,
}
impl Default for SubplotDescriptor<'_> {
fn default() -> Self {
Self {
format: SubplotFormat::default(),
title: "",
xaxis: AxisDescriptor {
label: "",
major_tick_marks: TickSpacing::On,
major_tick_labels: TickLabels::Auto,
minor_tick_marks: TickSpacing::On,
minor_tick_labels: TickLabels::None,
grid: Grid::None,
limit_policy: Limits::Auto,
limits: None,
span: None,
visible: true,
},
yaxis: AxisDescriptor {
label: "",
major_tick_marks: TickSpacing::On,
major_tick_labels: TickLabels::Auto,
minor_tick_marks: TickSpacing::On,
minor_tick_labels: TickLabels::None,
grid: Grid::None,
limit_policy: Limits::Auto,
limits: None,
span: None,
visible: true,
},
secondary_xaxis: AxisDescriptor {
label: "",
major_tick_marks: TickSpacing::On,
major_tick_labels: TickLabels::Auto,
minor_tick_marks: TickSpacing::On,
minor_tick_labels: TickLabels::None,
grid: Grid::None,
limit_policy: Limits::Auto,
limits: None,
span: None,
visible: true,
},
secondary_yaxis: AxisDescriptor {
label: "",
major_tick_marks: TickSpacing::On,
major_tick_labels: TickLabels::Auto,
minor_tick_marks: TickSpacing::On,
minor_tick_labels: TickLabels::None,
grid: Grid::None,
limit_policy: Limits::Auto,
limits: None,
span: None,
visible: true,
},
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum PlotType {
Series,
Fill,
}
#[derive(Clone, Debug)]
pub(crate) struct PlotDescriptor {
pub label: String,
pub line: bool,
pub marker: bool,
pub line_format: Line,
pub marker_format: Marker,
pub xaxis: AxisType,
pub yaxis: AxisType,
pub pixel_perfect: bool,
}
impl Default for PlotDescriptor {
fn default() -> Self {
Self {
label: String::new(),
line: true,
marker: false,
line_format: Line::default(),
marker_format: Marker::default(),
xaxis: AxisType::X,
yaxis: AxisType::Y,
pixel_perfect: false,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct FillDescriptor {
pub label: String,
pub color_override: Option<Color>,
pub xaxis: AxisType,
pub yaxis: AxisType,
}
impl Default for FillDescriptor {
fn default() -> Self {
Self {
label: String::new(),
color_override: None,
xaxis: AxisType::X,
yaxis: AxisType::Y,
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct Line {
pub style: LineStyle,
pub width: u32,
pub color_override: Option<Color>,
}
impl Default for Line {
fn default() -> Self {
Self {
style: LineStyle::Solid,
width: 3,
color_override: None,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Marker {
pub style: MarkerStyle,
pub size: u32,
pub color_override: Option<Color>,
pub outline: bool,
pub outline_format: Line,
}
impl Default for Marker {
fn default() -> Self {
Self {
style: MarkerStyle::Circle,
size: 3,
color_override: None,
outline: false,
outline_format: Line {
width: 2,
..Default::default()
},
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct AxisDescriptor<S: AsRef<str>> {
pub label: S,
pub major_tick_marks: TickSpacing,
pub major_tick_labels: TickLabels,
pub minor_tick_marks: TickSpacing,
pub minor_tick_labels: TickLabels,
pub grid: Grid,
pub limit_policy: Limits,
pub limits: Option<(f64, f64)>,
pub span: Option<(f64, f64)>,
pub visible: bool,
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub(crate) enum AxisType {
X,
Y,
SecondaryX,
SecondaryY,
}
impl AxisType {
pub(crate) fn iter() -> array::IntoIter<Self, 4> {
[Self::X, Self::Y, Self::SecondaryX, Self::SecondaryY].into_iter()
}
}
pub(crate) type AxisBuf = AxisDescriptor<String>;
impl<S: AsRef<str>> AxisDescriptor<S> {
fn to_buf(&self) -> AxisBuf {
AxisBuf {
label: self.label.as_ref().to_string(),
major_tick_marks: self.major_tick_marks.clone(),
major_tick_labels: self.major_tick_labels.clone(),
minor_tick_marks: self.minor_tick_marks.clone(),
minor_tick_labels: self.minor_tick_labels.clone(),
grid: self.grid,
limit_policy: self.limit_policy,
limits: self.limits,
span: self.span,
visible: self.visible,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct PlotInfo<'a> {
#[allow(dead_code)]
pub label: String,
pub data: Box<dyn SeriesData + 'a>,
pub line: Option<Line>,
pub marker: Option<Marker>,
pub xaxis: AxisType,
pub yaxis: AxisType,
pub pixel_perfect: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct FillInfo<'a> {
#[allow(dead_code)]
pub label: String,
pub data: Box<dyn FillData + 'a>,
pub color_override: Option<Color>,
pub xaxis: AxisType,
pub yaxis: AxisType,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct PlotData<'a> {
xdata: ndarray::ArrayView1<'a, f64>,
ydata: ndarray::ArrayView1<'a, f64>,
}
impl Default for PlotData<'_> {
fn default() -> Self {
Self {
xdata: ndarray::ArrayView1::<f64>::from(&[]),
ydata: ndarray::ArrayView1::<f64>::from(&[]),
}
}
}
impl SeriesData for PlotData<'_> {
fn data<'b>(&'b self) -> Box<dyn Iterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.ydata.iter().cloned(),
))
}
fn xmin(&self) -> f64 {
self.xdata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.xdata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
self.ydata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn ymax(&self) -> f64 {
self.ydata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
}
impl<'a> PlotData<'a> {
pub fn new<Xs: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
xs: Xs,
ys: Ys,
) -> Self {
let xdata = xs.into();
let ydata = ys.into();
Self { xdata, ydata }
}
}
#[derive(Clone, Debug)]
pub(crate) struct PlotDataOwned {
xdata: ndarray::Array1<f64>,
ydata: ndarray::Array1<f64>,
}
impl Default for PlotDataOwned {
fn default() -> Self {
Self {
xdata: ndarray::Array1::<f64>::default(0),
ydata: ndarray::Array1::<f64>::default(0),
}
}
}
impl SeriesData for PlotDataOwned {
fn data(&self) -> Box<dyn Iterator<Item = (f64, f64)> + '_> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.ydata.iter().cloned(),
))
}
fn xmin(&self) -> f64 {
self.xdata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.xdata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
self.ydata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn ymax(&self) -> f64 {
self.ydata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
}
impl PlotDataOwned {
pub fn new<Xs: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
xs: Xs,
ys: Ys,
) -> Self {
let xdata = xs.into();
let ydata = ys.into();
Self { xdata, ydata }
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct StepData<'a> {
edges: ndarray::ArrayView1<'a, f64>,
ydata: ndarray::ArrayView1<'a, f64>,
}
impl Default for StepData<'_> {
fn default() -> Self {
Self {
edges: ndarray::ArrayView1::<f64>::from(&[]),
ydata: ndarray::ArrayView1::<f64>::from(&[]),
}
}
}
impl SeriesData for StepData<'_> {
fn data<'b>(&'b self) -> Box<dyn Iterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.edges.windows(2).into_iter().flatten().cloned(),
self.ydata.iter().flat_map(|y| [y, y]).cloned(),
))
}
fn xmin(&self) -> f64 {
self.edges.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.edges.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
self.ydata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn ymax(&self) -> f64 {
self.ydata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
}
impl<'a> StepData<'a> {
pub fn new<Es: Into<ndarray::ArrayView1<'a, f64>>, Ys: Into<ndarray::ArrayView1<'a, f64>>>(
edges: Es,
ys: Ys,
) -> Self {
let edges = edges.into();
let ydata = ys.into();
Self { edges, ydata }
}
}
#[derive(Clone, Debug)]
pub(crate) struct StepDataOwned {
edges: ndarray::Array1<f64>,
ydata: ndarray::Array1<f64>,
}
impl Default for StepDataOwned {
fn default() -> Self {
Self {
edges: ndarray::Array1::<f64>::default(0),
ydata: ndarray::Array1::<f64>::default(0),
}
}
}
impl SeriesData for StepDataOwned {
fn data(&self) -> Box<dyn Iterator<Item = (f64, f64)> + '_> {
Box::new(iter::zip(
self.edges.windows(2).into_iter().flatten().cloned(),
self.ydata.iter().flat_map(|y| [y, y]).cloned(),
))
}
fn xmin(&self) -> f64 {
self.edges.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.edges.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
self.ydata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn ymax(&self) -> f64 {
self.ydata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
}
impl StepDataOwned {
pub fn new<Es: Into<ndarray::Array1<f64>>, Ys: Into<ndarray::Array1<f64>>>(
edges: Es,
ys: Ys,
) -> Self {
let edges = edges.into();
let ydata = ys.into();
Self { edges, ydata }
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct FillBetweenData<'a> {
xdata: ndarray::ArrayView1<'a, f64>,
y1_data: ndarray::ArrayView1<'a, f64>,
y2_data: ndarray::ArrayView1<'a, f64>,
}
impl Default for FillBetweenData<'_> {
fn default() -> Self {
Self {
xdata: ndarray::ArrayView1::<f64>::from(&[]),
y1_data: ndarray::ArrayView1::<f64>::from(&[]),
y2_data: ndarray::ArrayView1::<f64>::from(&[]),
}
}
}
impl FillData for FillBetweenData<'_> {
fn curve1<'b>(&'b self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.y1_data.iter().cloned(),
))
}
fn curve2<'b>(&'b self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.y2_data.iter().cloned(),
))
}
fn xmin(&self) -> f64 {
self.xdata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.xdata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
f64::min(
self.y1_data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
self.y2_data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
)
}
fn ymax(&self) -> f64 {
f64::max(
self.y1_data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
self.y2_data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
)
}
}
impl<'a> FillBetweenData<'a> {
pub fn new<
Xs: Into<ndarray::ArrayView1<'a, f64>>,
Y1s: Into<ndarray::ArrayView1<'a, f64>>,
Y2s: Into<ndarray::ArrayView1<'a, f64>>,
>(
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Self {
let xdata = xs.into();
let y1_data = y1s.into();
let y2_data = y2s.into();
Self { xdata, y1_data, y2_data }
}
}
#[derive(Clone, Debug)]
pub(crate) struct FillBetweenDataOwned {
xdata: ndarray::Array1<f64>,
y1_data: ndarray::Array1<f64>,
y2_data: ndarray::Array1<f64>,
}
impl Default for FillBetweenDataOwned {
fn default() -> Self {
Self {
xdata: ndarray::Array1::<f64>::default(0),
y1_data: ndarray::Array1::<f64>::default(0),
y2_data: ndarray::Array1::<f64>::default(0),
}
}
}
impl FillData for FillBetweenDataOwned {
fn curve1<'b>(&'b self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.y1_data.iter().cloned(),
))
}
fn curve2<'b>(&'b self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'b> {
Box::new(iter::zip(
self.xdata.iter().cloned(),
self.y2_data.iter().cloned(),
))
}
fn xmin(&self) -> f64 {
self.xdata.iter().fold(f64::INFINITY, |a, &b| a.min(b))
}
fn xmax(&self) -> f64 {
self.xdata.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
}
fn ymin(&self) -> f64 {
f64::min(
self.y1_data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
self.y2_data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
)
}
fn ymax(&self) -> f64 {
f64::max(
self.y1_data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
self.y2_data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
)
}
}
impl FillBetweenDataOwned {
pub fn new<
Xs: Into<ndarray::Array1<f64>>,
Y1s: Into<ndarray::Array1<f64>>,
Y2s: Into<ndarray::Array1<f64>>,
>(
xs: Xs,
y1s: Y1s,
y2s: Y2s,
) -> Self {
let xdata = xs.into();
let y1_data = y1s.into();
let y2_data = y2s.into();
Self { xdata, y1_data, y2_data }
}
}
pub(crate) trait SeriesData: dyn_clone::DynClone + fmt::Debug {
fn data<'a>(&'a self) -> Box<dyn Iterator<Item = (f64, f64)> + 'a>;
fn xmin(&self) -> f64;
fn xmax(&self) -> f64;
fn ymin(&self) -> f64;
fn ymax(&self) -> f64;
}
dyn_clone::clone_trait_object!(SeriesData);
pub(crate) trait FillData: dyn_clone::DynClone + fmt::Debug {
fn curve1<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'a>;
fn curve2<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = (f64, f64)> + 'a>;
fn xmin(&self) -> f64;
fn xmax(&self) -> f64;
fn ymin(&self) -> f64;
fn ymax(&self) -> f64;
}
dyn_clone::clone_trait_object!(FillData);