use piet::{kurbo::Circle, Color, RenderContext};
use polars::prelude::DataFrame;
use crate::{
cartesian::camera2::Camera2,
data::{ConstOrColumn, ConstOrColumnTrait},
primitive::{Marker, MarkerShape},
};
use super::BoundingBox;
pub struct Scatter {
pub df: DataFrame,
pub x: ConstOrColumn<f64>,
pub y: ConstOrColumn<f64>,
pub marker: MarkerOpts,
}
impl Scatter {
pub fn render<C: RenderContext>(&self, ctx: &mut C, camera: &Camera2) {
let n = self.df.height();
for i in 0..n {
let marker = self.marker.get(&self.df, i);
let pos = (self.x.get(&self.df, i), self.y.get(&self.df, i));
let pos = camera.map(pos);
match marker.shape {
MarkerShape::Circle => {
if marker.filled {
ctx.fill(Circle::new(pos, marker.size), &marker.color);
} else {
ctx.stroke(
Circle::new(pos, marker.size),
&marker.color,
marker.stroke_thickness,
);
}
}
MarkerShape::Char(_ch) => todo!(),
}
}
}
pub fn bounding_box(&self) -> Option<BoundingBox> {
let x_range = self.x.min_max(&self.df)?;
let y_range = self.y.min_max(&self.df)?;
Some(BoundingBox {
min_x: x_range.0,
max_x: x_range.1,
min_y: y_range.0,
max_y: y_range.1,
})
}
}
pub struct MarkerOpts {
pub size: ConstOrColumn<f64>,
pub color: ConstOrColumn<Color>,
pub shape: ConstOrColumn<MarkerShape>,
}
impl Default for MarkerOpts {
fn default() -> Self {
Self {
size: ConstOrColumn::Const(5.0),
color: ConstOrColumn::Const(Color::GREEN),
shape: ConstOrColumn::Const(MarkerShape::Circle),
}
}
}
impl MarkerOpts {
pub fn get(&self, df: &DataFrame, idx: usize) -> Marker {
Marker {
size: self.size.get(df, idx),
color: self.color.get(df, idx),
shape: self.shape.get(df, idx),
filled: true,
stroke_thickness: 2.0,
}
}
}