use crate::data::{IntoSeries, Series};
use crate::render::Color;
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Area<'a> {
pub(crate) x: Option<Series<'a>>,
pub(crate) low: Option<Series<'a>>,
pub(crate) high: Series<'a>,
pub(crate) horizontal: bool,
pub(crate) color: Option<Color>,
pub(crate) label: Option<String>,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none", default)
)]
pub(crate) opacity: Option<f64>,
}
impl<'a> Area<'a> {
pub fn y(values: impl IntoSeries<'a>) -> Area<'a> {
Area {
x: None,
low: None,
high: values.into_series(),
horizontal: false,
color: None,
label: None,
opacity: None,
}
}
pub fn xy(x: impl IntoSeries<'a>, y: impl IntoSeries<'a>) -> Area<'a> {
let x = x.into_series();
let y = y.into_series();
let area = Area {
x: Some(x),
low: None,
high: y,
horizontal: false,
color: None,
label: None,
opacity: None,
};
area.validate()
.expect("Area::xy requires series of equal length");
area
}
pub fn between(
x: impl IntoSeries<'a>,
low: impl IntoSeries<'a>,
high: impl IntoSeries<'a>,
) -> Area<'a> {
let x = x.into_series();
let low = low.into_series();
let high = high.into_series();
let area = Area {
x: Some(x),
low: Some(low),
high,
horizontal: false,
color: None,
label: None,
opacity: None,
};
area.validate()
.expect("Area::between requires series of equal length");
area
}
pub fn horizontal(
y: impl IntoSeries<'a>,
x_low: impl IntoSeries<'a>,
x_high: impl IntoSeries<'a>,
) -> Area<'a> {
let y = y.into_series();
let x_low = x_low.into_series();
let x_high = x_high.into_series();
let area = Area {
x: Some(y),
low: Some(x_low),
high: x_high,
horizontal: true,
color: None,
label: None,
opacity: None,
};
area.validate()
.expect("Area::horizontal requires series of equal length");
area
}
#[must_use]
pub fn color(mut self, color: Color) -> Area<'a> {
self.color = Some(color);
self
}
#[must_use]
pub fn opacity(mut self, opacity: f64) -> Area<'a> {
self.opacity = Some(opacity);
self.validate()
.expect("Area::opacity requires a value in (0, 1]");
self
}
#[must_use]
pub fn label(mut self, label: impl Into<String>) -> Area<'a> {
self.label = Some(label.into());
self
}
pub(crate) fn validate(&self) -> crate::Result<()> {
if let Some(x) = &self.x {
super::pair("Area: x and high", x.len(), self.high.len())?;
}
if let Some(low) = &self.low {
super::pair("Area: low and high", low.len(), self.high.len())?;
}
if let Some(opacity) = self.opacity
&& !(opacity.is_finite() && 0.0 < opacity && opacity <= 1.0)
{
return Err(crate::Error::InvalidParameter {
detail: "Area opacity must be finite and in (0, 1]",
});
}
Ok(())
}
pub fn into_owned(self) -> Area<'static> {
Area {
x: self.x.map(Series::into_owned),
low: self.low.map(Series::into_owned),
high: self.high.into_owned(),
horizontal: self.horizontal,
color: self.color,
label: self.label,
opacity: self.opacity,
}
}
}
impl std::fmt::Debug for Area<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Area")
.field("points", &self.high.len())
.field("banded", &self.low.is_some())
.field("color", &self.color)
.finish()
}
}
#[cfg(test)]
#[path = "tests/area_tests.rs"]
mod tests;