use crate::model::Bar;
use egui::{Color32, Painter, Rect};
pub struct SeriesRenderContext<'a> {
pub painter: &'a Painter,
pub rect: Rect,
pub start_idx: usize,
pub end_idx: usize,
pub price_min: f64,
pub price_max: f64,
pub bar_spacing: f32,
pub right_offset: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SeriesType {
Candles,
Bar,
Line,
Area,
Baseline,
Histogram,
}
#[derive(Debug, Clone)]
pub struct SeriesData {
pub time: usize,
pub open: Option<f64>,
pub high: Option<f64>,
pub low: Option<f64>,
pub close: Option<f64>,
pub value: Option<f64>,
pub volume: Option<f64>,
pub color: Option<Color32>,
}
impl SeriesData {
pub fn from_bar(bar: &Bar, index: usize) -> Self {
Self {
time: index,
open: Some(bar.open),
high: Some(bar.high),
low: Some(bar.low),
close: Some(bar.close),
value: Some(bar.close),
volume: Some(bar.volume),
color: None,
}
}
pub fn from_val(value: f64, index: usize) -> Self {
Self {
time: index,
open: None,
high: None,
low: None,
close: None,
value: Some(value),
volume: None,
color: None,
}
}
pub fn main_val(&self) -> Option<f64> {
self.value.or(self.close)
}
}
pub trait Series {
fn series_type(&self) -> SeriesType;
fn data(&self) -> &[SeriesData];
fn price_range(&self, start_idx: usize, end_idx: usize) -> Option<(f64, f64)>;
fn render(&self, ctx: &SeriesRenderContext);
fn name(&self) -> &str;
fn color(&self) -> Color32;
}
pub fn price_to_y(price: f64, price_min: f64, price_max: f64, rect: Rect) -> f32 {
let price_range = (price_max - price_min).max(1e-12);
let ratio = ((price - price_min) / price_range) as f32;
rect.max.y - ratio * rect.height()
}
pub fn idx_to_x(
global_idx: usize,
base_idx: usize,
bar_spacing: f32,
right_offset: f32,
rect: Rect,
) -> f32 {
let delta_from_right = base_idx as f32 + right_offset - global_idx as f32;
let width = rect.width();
let relative_x = width - (delta_from_right + 0.5) * bar_spacing - 1.0;
rect.min.x + relative_x
}