use gpui::{fill, point, px, App, Bounds, Hsla, PaintQuad, Pixels, Point, Window}; use crate::plot::{ label::{PlotLabel, Text, TEXT_GAP, TEXT_HEIGHT}, origin_point, }; #[allow(clippy::type_complexity)] pub struct Bar { data: Vec, x: Box Option>, band_width: f32, y0: f32, y1: Box Option>, fill: Box Hsla>, label: Option) -> Vec>>, } impl Default for Bar { fn default() -> Self { Self { data: Vec::new(), x: Box::new(|_| None), band_width: 0., y0: 0., y1: Box::new(|_| None), fill: Box::new(|_| gpui::black()), label: None, } } } impl Bar { pub fn new() -> Self { Self::default() } /// Set the data of the Bar. pub fn data(mut self, data: I) -> Self where I: IntoIterator, { self.data = data.into_iter().collect(); self } /// Set the x of the Bar. pub fn x(mut self, x: F) -> Self where F: Fn(&T) -> Option + 'static, { self.x = Box::new(x); self } /// Set the band width of the Bar. pub fn band_width(mut self, band_width: f32) -> Self { self.band_width = band_width; self } /// Set the y0 of the Bar. pub fn y0(mut self, y0: f32) -> Self { self.y0 = y0; self } /// Set the y1 of the Bar. pub fn y1(mut self, y: F) -> Self where F: Fn(&T) -> Option + 'static, { self.y1 = Box::new(y); self } /// Set the fill color of the Bar. pub fn fill(mut self, fill: F) -> Self where F: Fn(&T) -> C + 'static, C: Into, { self.fill = Box::new(move |v| fill(v).into()); self } /// Set the label of the Bar. pub fn label(mut self, label: F) -> Self where F: Fn(&T, Point) -> Vec + 'static, { self.label = Some(Box::new(label)); self } fn path(&self, bounds: &Bounds) -> (Vec, PlotLabel) { let origin = bounds.origin; let mut graph = vec![]; let mut labels = vec![]; for v in &self.data { let x_tick = (self.x)(v); let y_tick = (self.y1)(v); if let (Some(x_tick), Some(y_tick)) = (x_tick, y_tick) { let is_negative = y_tick > self.y0; let (p1, p2) = if is_negative { ( origin_point(px(x_tick), px(self.y0), origin), origin_point(px(x_tick + self.band_width), px(y_tick), origin), ) } else { ( origin_point(px(x_tick), px(y_tick), origin), origin_point(px(x_tick + self.band_width), px(self.y0), origin), ) }; let color = (self.fill)(v); graph.push(fill(Bounds::from_corners(p1, p2), color)); if let Some(label) = &self.label { labels.extend(label( v, point( px(x_tick + self.band_width / 2.), if is_negative { px(y_tick + TEXT_GAP) } else { px(y_tick - TEXT_HEIGHT) }, ), )); } } } (graph, PlotLabel::new(labels)) } /// Paint the Bar. pub fn paint(&self, bounds: &Bounds, window: &mut Window, cx: &mut App) { let (graph, labels) = self.path(bounds); for quad in graph { window.paint_quad(quad); } labels.paint(bounds, window, cx); } }