use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
#[allow(deprecated)]
use skia_safe::{Canvas, Color, Paint, PaintStyle, PathBuilder, Point, TileMode, gradient_shader};
use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
#[allow(deprecated)]
use skia_safe::gradient_shader::GradientShaderColors;
pub struct LineChart {
pub history: RefCell<VecDeque<f32>>,
colors: [Color; 2],
planner: RefCell<Option<Rc<dyn Layout>>>,
}
impl HasPlanner for LineChart {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Drawable for LineChart {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let area = self.get_planned_drawing_area(canvas);
let history = self.history.borrow();
if history.is_empty() {
return Ok(());
}
let mut line_builder = PathBuilder::new();
let mut fill_builder = PathBuilder::new();
let w = area.width();
let h = area.height();
let step_x = w / (history.len().saturating_sub(1).max(1) as f32);
fill_builder.move_to(Point::new(area.left, area.bottom));
for (i, &val) in history.iter().enumerate() {
let px = area.left + (i as f32) * step_x;
let py = area.bottom - ((val / 100.0) * h).clamp(0.0, h);
let pt = Point::new(px, py);
if i == 0 {
line_builder.move_to(pt);
} else {
line_builder.line_to(pt);
}
fill_builder.line_to(pt);
}
fill_builder.line_to(Point::new(area.right, area.bottom));
fill_builder.close();
#[allow(deprecated)]
let shader = gradient_shader::linear(
(
Point::new(area.left, area.top),
Point::new(area.right, area.top),
),
GradientShaderColors::Colors(&self.colors),
None,
TileMode::Clamp,
None,
None,
);
let mut fill_paint = Paint::default();
fill_paint.set_anti_alias(true).set_alpha_f(0.25);
if let Some(s) = &shader {
fill_paint.set_shader(s.clone());
}
let mut stroke_paint = Paint::default();
stroke_paint
.set_anti_alias(true)
.set_style(PaintStyle::Stroke)
.set_stroke_width(2.0);
if let Some(s) = &shader {
stroke_paint.set_shader(s.clone());
}
canvas.draw_path(&fill_builder.detach(), &fill_paint);
canvas.draw_path(&line_builder.detach(), &stroke_paint);
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
Plan::Fill
}
fn get_vertical_plan(&self) -> Plan {
Plan::Fill
}
}
impl LineChart {
pub fn new(history: VecDeque<f32>, colors: [Color; 2]) -> Rc<Self> {
Rc::new(Self {
history: RefCell::new(history),
colors,
planner: RefCell::new(None),
})
}
}