use vello_cpu::kurbo::{Point, Rect};
use crate::{
layout::{DataCoordinateSystem, GridLayoutInfo, LayoutOutput},
model::ChartModel,
visual::Color,
};
#[derive(Debug, Clone)]
pub struct SeriesContext<'a> {
pub series_index: usize,
pub grid_index: usize,
pub resolved: &'a ChartModel,
pub layout: &'a LayoutOutput,
pub grid_info: &'a GridLayoutInfo,
pub coord: &'a DataCoordinateSystem,
}
impl<'a> SeriesContext<'a> {
pub fn new(
series_index: usize,
grid_index: usize,
resolved: &'a ChartModel,
layout: &'a LayoutOutput,
grid_info: &'a GridLayoutInfo,
) -> Self {
Self {
series_index,
grid_index,
resolved,
layout,
grid_info,
coord: &grid_info.data_coord,
}
}
pub fn try_new(
series_index: usize,
grid_index: usize,
resolved: &'a ChartModel,
layout: &'a LayoutOutput,
) -> Option<Self> {
let grid_info = layout.grids.iter().find(|g| g.grid_index == grid_index)?;
Some(Self::new(
series_index,
grid_index,
resolved,
layout,
grid_info,
))
}
pub fn plot_bounds(&self) -> Rect {
self.grid_info.grid_inner_bbox
}
pub fn get_series_color(&self, item_color: Option<Color>) -> Color {
item_color.unwrap_or_else(|| {
self.resolved
.colors
.get(self.series_index % self.resolved.colors.len())
.copied()
.unwrap_or_else(|| Color::new(0, 0, 0))
})
}
pub fn get_series_color_with_opacity(&self, item_color: Option<Color>, opacity: f64) -> Color {
let mut color = self.get_series_color(item_color);
color.a = (color.a as f64 * opacity).clamp(0.0, 255.0) as u8;
color
}
pub fn get_polar_center(&self) -> Point {
let bounds = self.plot_bounds();
Point::new(
bounds.x0 + bounds.width() * 0.5,
bounds.y0 + bounds.height() * 0.5,
)
}
pub fn get_polar_max_radius(&self) -> f64 {
let bounds = self.plot_bounds();
bounds.width().min(bounds.height()) * 0.5
}
pub fn category_width(&self) -> f64 {
self.coord.category_width()
}
pub fn data_to_pixel(&self, x: f64, y: f64, y_axis_index: usize) -> Point {
self.coord.data_to_pixel(x, y, y_axis_index)
}
}
pub trait SeriesRenderer {
fn is_empty(&self) -> bool;
fn grid_index(&self) -> usize;
fn series_index(&self) -> usize;
fn render(&self, ctx: &SeriesContext) -> Vec<crate::visual::VisualElement>;
}
pub trait CartesianRenderer: SeriesRenderer {
fn y_axis_index(&self) -> usize;
fn is_data_empty(&self) -> bool;
fn render_cartesian(&self, ctx: &SeriesContext) -> Vec<crate::visual::VisualElement>;
}
pub trait PolarRenderer: SeriesRenderer {
fn center_percent(&self) -> (f64, f64) {
(50.0, 50.0) }
fn radius_percent(&self) -> (f64, f64) {
(0.0, 75.0) }
fn render_polar(
&self,
ctx: &SeriesContext,
center: Point,
max_radius: f64,
) -> Vec<crate::visual::VisualElement>;
}