use crate::{
drawable::{Bound, Drawable},
primitive::config::Config,
};
use tiny_skia::{Paint, PathBuilder, Pixmap, Point, Stroke, Transform};
pub enum StairStyle {
TraceX,
TraceY,
Histogram,
}
pub struct Stair {
name: String,
stair_style: StairStyle,
x: Vec<f32>,
y: Vec<f32>,
config: Config,
}
impl Stair {
pub fn new(name: String, config: Config) -> Self {
Self {
name,
x: Vec::new(),
y: Vec::new(),
config,
stair_style: StairStyle::TraceX,
}
}
fn _add_data(&mut self, x: &[f32], y: &[f32]) {
self.x.extend_from_slice(x);
self.y.extend_from_slice(y);
}
pub fn set_style(&mut self, style: StairStyle) {
self.stair_style = style;
}
pub fn set_data(&mut self, x: &[f32], y: &[f32]) {
self.x.clear();
self.y.clear();
self._add_data(x, y);
}
}
impl Drawable for Stair {
fn draw(&self, pixmap: &mut Pixmap, ts: &Transform) {
if self.x.len() < 2 {
return;
}
let mut pb = PathBuilder::new();
let mut p0 = Point::from_xy(self.x[0], self.y[0]);
ts.map_point(&mut p0);
pb.move_to(p0.x, p0.y);
for i in 0..self.x.len() - 1 {
match self.stair_style {
StairStyle::TraceX => {
let mut p_corner = Point::from_xy(self.x[i + 1], self.y[i]);
ts.map_point(&mut p_corner);
pb.line_to(p_corner.x, p_corner.y);
let mut p_next = Point::from_xy(self.x[i + 1], self.y[i + 1]);
ts.map_point(&mut p_next);
pb.line_to(p_next.x, p_next.y);
}
StairStyle::TraceY => {
let mut p_corner = Point::from_xy(self.x[i], self.y[i + 1]);
ts.map_point(&mut p_corner);
pb.line_to(p_corner.x, p_corner.y);
let mut p_next = Point::from_xy(self.x[i + 1], self.y[i + 1]);
ts.map_point(&mut p_next);
pb.line_to(p_next.x, p_next.y);
}
StairStyle::Histogram => {
let mid_x = (self.x[i] + self.x[i + 1]) / 2.0;
let mut p1 = Point::from_xy(mid_x, self.y[i]);
ts.map_point(&mut p1);
pb.line_to(p1.x, p1.y);
let mut p2 = Point::from_xy(mid_x, self.y[i + 1]);
ts.map_point(&mut p2);
pb.line_to(p2.x, p2.y);
let mut p_next = Point::from_xy(self.x[i + 1], self.y[i + 1]);
ts.map_point(&mut p_next);
pb.line_to(p_next.x, p_next.y);
}
}
}
if let Some(path) = pb.finish() {
let mut paint = Paint::default();
let [r, g, b, a] = self.config.color;
paint.set_color_rgba8(r, g, b, a);
paint.anti_alias = true;
let stroke = Stroke {
width: self.config.stroke_width, ..Default::default()
};
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
}
}
fn bound(&self) -> Option<crate::drawable::Bound> {
if self.x.is_empty() || self.y.is_empty() {
return None;
}
let x_min = self.x.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let x_max = self.x.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
let y_min = self.y.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let y_max = self.y.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
Some(Bound {
x_min,
x_max,
y_min,
y_max,
})
}
fn name(&self) -> String {
self.name.clone()
}
fn get_color(&self) -> [u8; 4] {
self.config.color
}
fn set_color(&mut self, color: [u8; 4]) {
self.config.color = color;
}
}