blob: 7efd23e3dcc2deba62f4d8a105711a55870dc22c [file] [log] [blame]
//! Error bar plots
use std::borrow::Cow;
use std::iter::IntoIterator;
use crate::data::Matrix;
use crate::traits::{self, Data, Set};
use crate::{
Color, Display, ErrorBarDefault, Figure, Label, LineType, LineWidth, Plot, PointSize,
PointType, Script,
};
/// Properties common to error bar plots
pub struct Properties {
color: Option<Color>,
label: Option<Cow<'static, str>>,
line_type: LineType,
linewidth: Option<f64>,
point_size: Option<f64>,
point_type: Option<PointType>,
style: Style,
}
impl ErrorBarDefault<Style> for Properties {
fn default(style: Style) -> Properties {
Properties {
color: None,
label: None,
line_type: LineType::Solid,
linewidth: None,
point_type: None,
point_size: None,
style,
}
}
}
impl Script for Properties {
fn script(&self) -> String {
let mut script = format!("with {} ", self.style.display());
script.push_str(&format!("lt {} ", self.line_type.display()));
if let Some(lw) = self.linewidth {
script.push_str(&format!("lw {} ", lw))
}
if let Some(color) = self.color {
script.push_str(&format!("lc rgb '{}' ", color.display()))
}
if let Some(pt) = self.point_type {
script.push_str(&format!("pt {} ", pt.display()))
}
if let Some(ps) = self.point_size {
script.push_str(&format!("ps {} ", ps))
}
if let Some(ref label) = self.label {
script.push_str("title '");
script.push_str(label);
script.push('\'')
} else {
script.push_str("notitle")
}
script
}
}
impl Set<Color> for Properties {
/// Changes the color of the error bars
fn set(&mut self, color: Color) -> &mut Properties {
self.color = Some(color);
self
}
}
impl Set<Label> for Properties {
/// Sets the legend label
fn set(&mut self, label: Label) -> &mut Properties {
self.label = Some(label.0);
self
}
}
impl Set<LineType> for Properties {
/// Change the line type
///
/// **Note** By default `Solid` lines are used
fn set(&mut self, lt: LineType) -> &mut Properties {
self.line_type = lt;
self
}
}
impl Set<LineWidth> for Properties {
/// Changes the linewidth
///
/// # Panics
///
/// Panics if `lw` is a non-positive value
fn set(&mut self, lw: LineWidth) -> &mut Properties {
let lw = lw.0;
assert!(lw > 0.);
self.linewidth = Some(lw);
self
}
}
impl Set<PointSize> for Properties {
/// Changes the size of the points
///
/// # Panics
///
/// Panics if `size` is a non-positive value
fn set(&mut self, ps: PointSize) -> &mut Properties {
let ps = ps.0;
assert!(ps > 0.);
self.point_size = Some(ps);
self
}
}
impl Set<PointType> for Properties {
/// Changes the point type
fn set(&mut self, pt: PointType) -> &mut Properties {
self.point_type = Some(pt);
self
}
}
#[derive(Clone, Copy)]
enum Style {
XErrorBars,
XErrorLines,
YErrorBars,
YErrorLines,
}
impl Display<&'static str> for Style {
fn display(&self) -> &'static str {
match *self {
Style::XErrorBars => "xerrorbars",
Style::XErrorLines => "xerrorlines",
Style::YErrorBars => "yerrorbars",
Style::YErrorLines => "yerrorlines",
}
}
}
/// Asymmetric error bar plots
pub enum ErrorBar<X, Y, L, H> {
/// Horizontal error bars
XErrorBars {
/// X coordinate of the data points
x: X,
/// Y coordinate of the data points
y: Y,
/// X coordinate of the left end of the error bar
x_low: L,
/// Y coordinate of the right end of the error bar
x_high: H,
},
/// Horizontal error bars, where each point is joined by a line
XErrorLines {
/// X coordinate of the data points
x: X,
/// Y coordinate of the data points
y: Y,
/// X coordinate of the left end of the error bar
x_low: L,
/// Y coordinate of the right end of the error bar
x_high: H,
},
/// Vertical error bars
YErrorBars {
/// X coordinate of the data points
x: X,
/// Y coordinate of the data points
y: Y,
/// Y coordinate of the bottom of the error bar
y_low: L,
/// Y coordinate of the top of the error bar
y_high: H,
},
/// Vertical error bars, where each point is joined by a line
YErrorLines {
/// X coordinate of the data points
x: X,
/// Y coordinate of the data points
y: Y,
/// Y coordinate of the bottom of the error bar
y_low: L,
/// Y coordinate of the top of the error bar
y_high: H,
},
}
impl<X, Y, L, H> ErrorBar<X, Y, L, H> {
fn style(&self) -> Style {
match *self {
ErrorBar::XErrorBars { .. } => Style::XErrorBars,
ErrorBar::XErrorLines { .. } => Style::XErrorLines,
ErrorBar::YErrorBars { .. } => Style::YErrorBars,
ErrorBar::YErrorLines { .. } => Style::YErrorLines,
}
}
}
impl<X, Y, L, H> traits::Plot<ErrorBar<X, Y, L, H>> for Figure
where
H: IntoIterator,
H::Item: Data,
L: IntoIterator,
L::Item: Data,
X: IntoIterator,
X::Item: Data,
Y: IntoIterator,
Y::Item: Data,
{
type Properties = Properties;
fn plot<F>(&mut self, e: ErrorBar<X, Y, L, H>, configure: F) -> &mut Figure
where
F: FnOnce(&mut Properties) -> &mut Properties,
{
let (x_factor, y_factor) = crate::scale_factor(&self.axes, crate::Axes::BottomXLeftY);
let style = e.style();
let (x, y, length, height, e_factor) = match e {
ErrorBar::XErrorBars {
x,
y,
x_low,
x_high,
}
| ErrorBar::XErrorLines {
x,
y,
x_low,
x_high,
} => (x, y, x_low, x_high, x_factor),
ErrorBar::YErrorBars {
x,
y,
y_low,
y_high,
}
| ErrorBar::YErrorLines {
x,
y,
y_low,
y_high,
} => (x, y, y_low, y_high, y_factor),
};
let data = Matrix::new(
izip!(x, y, length, height),
(x_factor, y_factor, e_factor, e_factor),
);
self.plots.push(Plot::new(
data,
configure(&mut ErrorBarDefault::default(style)),
));
self
}
}
// TODO XY error bar
// pub struct XyErrorBar<X, Y, XL, XH, YL, YH> {
// x: X,
// y: Y,
// x_low: XL,
// x_high: XH,
// y_low: YL,
// y_high: YH,
// }
// TODO Symmetric error bars
// pub enum SymmetricErrorBar {
// XSymmetricErrorBar { x: X, y: Y, x_delta: D },
// XSymmetricErrorLines { x: X, y: Y, x_delta: D },
// YSymmetricErrorBar { x: X, y: Y, y_delta: D },
// YSymmetricErrorLines { x: X, y: Y, y_delta: D },
// }