blob: f79dbddcec1f8f30c709f29045f5b2b6777503ea [file] [log] [blame]
//! Filled curve plots
use std::borrow::Cow;
use std::iter::IntoIterator;
use crate::data::Matrix;
use crate::traits::{self, Data, Set};
use crate::{Axes, Color, Default, Display, Figure, Label, Opacity, Plot, Script};
/// Properties common to filled curve plots
pub struct Properties {
axes: Option<Axes>,
color: Option<Color>,
label: Option<Cow<'static, str>>,
opacity: Option<f64>,
}
impl Default for Properties {
fn default() -> Properties {
Properties {
axes: None,
color: None,
label: None,
opacity: None,
}
}
}
impl Script for Properties {
fn script(&self) -> String {
let mut script = if let Some(axes) = self.axes {
format!("axes {} ", axes.display())
} else {
String::new()
};
script.push_str("with filledcurves ");
script.push_str("fillstyle ");
if let Some(opacity) = self.opacity {
script.push_str(&format!("solid {} ", opacity))
}
// TODO border shoulde be configurable
script.push_str("noborder ");
if let Some(color) = self.color {
script.push_str(&format!("lc rgb '{}' ", color.display()));
}
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<Axes> for Properties {
/// Select axes to plot against
///
/// **Note** By default, the `BottomXLeftY` axes are used
fn set(&mut self, axes: Axes) -> &mut Properties {
self.axes = Some(axes);
self
}
}
impl Set<Color> for Properties {
/// Sets the fill color
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<Opacity> for Properties {
/// Changes the opacity of the fill color
///
/// **Note** By default, the fill color is totally opaque (`opacity = 1.0`)
///
/// # Panics
///
/// Panics if `opacity` is outside the range `[0, 1]`
fn set(&mut self, opacity: Opacity) -> &mut Properties {
self.opacity = Some(opacity.0);
self
}
}
/// Fills the area between two curves
pub struct FilledCurve<X, Y1, Y2> {
/// X coordinate of the data points of both curves
pub x: X,
/// Y coordinate of the data points of the first curve
pub y1: Y1,
/// Y coordinate of the data points of the second curve
pub y2: Y2,
}
impl<X, Y1, Y2> traits::Plot<FilledCurve<X, Y1, Y2>> for Figure
where
X: IntoIterator,
X::Item: Data,
Y1: IntoIterator,
Y1::Item: Data,
Y2: IntoIterator,
Y2::Item: Data,
{
type Properties = Properties;
fn plot<F>(&mut self, fc: FilledCurve<X, Y1, Y2>, configure: F) -> &mut Figure
where
F: FnOnce(&mut Properties) -> &mut Properties,
{
let FilledCurve { x, y1, y2 } = fc;
let mut props = Default::default();
configure(&mut props);
let (x_factor, y_factor) =
crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY));
let data = Matrix::new(izip!(x, y1, y2), (x_factor, y_factor, y_factor));
self.plots.push(Plot::new(data, &props));
self
}
}