#[derive(Debug, Clone, Copy, Default)]
pub enum MarkerShape {
#[default]
Circle,
Square,
Triangle,
Diamond,
Cross,
Plus,
}
#[derive(Debug, Clone, Copy)]
pub enum TrendLine {
Linear,
}
#[derive(Debug, Clone, Copy)]
pub struct ScatterPoint {
pub x: f64,
pub y: f64,
pub x_err: Option<(f64, f64)>, pub y_err: Option<(f64, f64)>,
}
impl From<&ScatterPoint> for (f64, f64) {
fn from(p: &ScatterPoint) -> (f64, f64) {
(p.x, p.y)
}
}
use crate::plot::band::BandPlot;
pub struct ScatterPlot {
pub data: Vec<ScatterPoint>,
pub color: String,
pub size: f64,
pub legend_label: Option<String>,
pub trend: Option<TrendLine>,
pub trend_color: String,
pub show_equation: bool,
pub show_correlation: bool,
pub trend_width: f64,
pub band: Option<BandPlot>,
pub marker: MarkerShape,
pub sizes: Option<Vec<f64>>,
pub colors: Option<Vec<String>>,
}
impl Default for ScatterPlot {
fn default() -> Self { Self::new() }
}
impl ScatterPlot {
pub fn new() -> Self {
Self {
data: vec![],
color: "black".into(),
size: 3.0,
legend_label: None,
trend: None,
trend_color: "black".into(),
show_equation: false,
show_correlation: false,
trend_width: 1.0,
band: None,
marker: MarkerShape::default(),
sizes: None,
colors: None,
}
}
pub fn with_data<T, U, I>(mut self, points: I) -> Self
where
I: IntoIterator<Item = (T, U)>,
T: Into<f64>,
U: Into<f64>,
{
self.data = points
.into_iter()
.map(|(x, y)| ScatterPoint {
x: x.into(),
y: y.into(),
x_err: None,
y_err: None,
})
.collect();
self
}
pub fn with_x_err<T, I>(mut self, errors: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<f64> + Copy,
{
for (i, err) in errors.into_iter().enumerate() {
if i < self.data.len() {
self.data[i].x_err = Some((err.into(), err.into()));
}
}
self
}
pub fn with_x_err_asymmetric<T, U, I>(mut self, errors: I) -> Self
where
I: IntoIterator<Item = (T, U)>,
T: Into<f64>,
U: Into<f64>,
{
for (i, (neg, pos)) in errors.into_iter().enumerate() {
if i < self.data.len() {
self.data[i].x_err = Some((neg.into(), pos.into()));
}
}
self
}
pub fn with_y_err<T, I>(mut self, errors: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<f64> + Copy,
{
for (i, err) in errors.into_iter().enumerate() {
if i < self.data.len() {
self.data[i].y_err = Some((err.into(), err.into()));
}
}
self
}
pub fn with_y_err_asymmetric<T, U, I>(mut self, errors: I) -> Self
where
I: IntoIterator<Item = (T, U)>,
T: Into<f64>,
U: Into<f64>,
{
for (i, (neg, pos)) in errors.into_iter().enumerate() {
if i < self.data.len() {
self.data[i].y_err = Some((neg.into(), pos.into()));
}
}
self
}
pub fn with_color<S: Into<String>>(mut self, color: S) -> Self {
self.color = color.into();
self
}
pub fn with_size(mut self, size: f64) -> Self {
self.size = size;
self
}
pub fn with_legend<S: Into<String>>(mut self, label: S) -> Self {
self.legend_label = Some(label.into());
self
}
pub fn with_trend(mut self, trend: TrendLine) -> Self {
self.trend = Some(trend);
self
}
pub fn with_trend_color<S: Into<String>>(mut self, color: S) -> Self {
self.trend_color = color.into();
self
}
pub fn with_equation(mut self) -> Self {
self.show_equation = true;
self
}
pub fn with_correlation(mut self) -> Self {
self.show_correlation = true;
self
}
pub fn with_trend_width(mut self, width: f64) -> Self {
self.trend_width = width;
self
}
pub fn with_band<T, U, I1, I2>(mut self, y_lower: I1, y_upper: I2) -> Self
where
I1: IntoIterator<Item = T>,
I2: IntoIterator<Item = U>,
T: Into<f64>,
U: Into<f64>,
{
let x: Vec<f64> = self.data.iter().map(|p| p.x).collect();
let band = BandPlot::new(x, y_lower, y_upper)
.with_color(self.color.clone());
self.band = Some(band);
self
}
pub fn with_marker(mut self, marker: MarkerShape) -> Self {
self.marker = marker;
self
}
pub fn with_sizes<T, I>(mut self, sizes: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<f64>,
{
self.sizes = Some(sizes.into_iter().map(|s| s.into()).collect());
self
}
pub fn with_colors<S, I>(mut self, colors: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.colors = Some(colors.into_iter().map(|s| s.into()).collect());
self
}
}