tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198635 Definiciones

⎇ d3649bfd1a80153652c2a028315369bb4b184024 crates/modules/ui_engine/widgets/lapaloma-financial/src/element.rs Rust · 176 líneas · 5.2 KB Historia Quién tocó qué crudo

feat(lapaloma-financial): OHLC + candlesticks con preserva-volatilidad d1ce4c897 · sergio ·

1//! `LapalomaCandlestickElement` — Element GPUI para charts OHLC.
2//!
3//! Reusa `lapaloma_cartesian::axis::paint_axes` para los ejes y el
4//! `WindowCanvas` adapter de `lapaloma-render` para el output.
5//! El render mismo de las velas lo hace `paint_candlesticks` que
6//! es agnóstico de gpui — facilita futuros backends (SVG, wgpu).
7//!
8//! Sin cache pan-blit en v0.1: las velas se redibujan cada frame.
9//! Para hasta ~500 bars on-screen son sub-millisecond; con
10//! aggregation razonable (1 bar por columna de pixel) eso cubre
11//! cualquier caso humano. Si se necesita más, se replica el
12//! patrón de `LapalomaChartElement::with_cache`.
13
14use std::panic;
15
16use gpui::{
17    App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
18    Pixels, Style, Window,
19};
20
21use lapaloma_cartesian::axis::{self, AxisStyle};
22use lapaloma_cartesian::{ChartViewport, CoordinateSystem};
23use lapaloma_render::{Canvas, Color, Rect, WindowCanvas};
24
25use crate::candlestick::{paint_candlesticks, CandlestickStyle};
26use crate::ohlc_buffer::OhlcBuffer;
27
28const TARGET_TICKS_X: usize = 8;
29const TARGET_TICKS_Y: usize = 6;
30
31pub struct LapalomaCandlestickElement {
32    pub data: OhlcBuffer,
33    pub viewport: ChartViewport,
34    pub style: CandlestickStyle,
35    pub background: Option<Color>,
36    pub axis_color: Color,
37    pub axis_style: AxisStyle,
38    pub margin_bottom: f32,
39    pub margin_left: f32,
40    pub margin_top: f32,
41    pub margin_right: f32,
42}
43
44impl LapalomaCandlestickElement {
45    pub fn new(data: OhlcBuffer, viewport: ChartViewport) -> Self {
46        Self {
47            data,
48            viewport,
49            style: CandlestickStyle::default(),
50            background: None,
51            axis_color: Color::rgba(0.6, 0.6, 0.65, 0.8),
52            axis_style: AxisStyle::default(),
53            margin_bottom: 24.0,
54            margin_left: 48.0,
55            margin_top: 8.0,
56            margin_right: 8.0,
57        }
58    }
59
60    pub fn background(mut self, color: Color) -> Self {
61        self.background = Some(color);
62        self
63    }
64    pub fn axis_color(mut self, color: Color) -> Self {
65        self.axis_color = color;
66        self
67    }
68    pub fn style(mut self, style: CandlestickStyle) -> Self {
69        self.style = style;
70        self
71    }
72    pub fn margins(mut self, top: f32, right: f32, bottom: f32, left: f32) -> Self {
73        self.margin_top = top;
74        self.margin_right = right;
75        self.margin_bottom = bottom;
76        self.margin_left = left;
77        self
78    }
79
80    fn plot_rect(&self, bounds: Rect) -> Rect {
81        Rect::new(
82            bounds.x + self.margin_left,
83            bounds.y + self.margin_top,
84            (bounds.w - self.margin_left - self.margin_right).max(1.0),
85            (bounds.h - self.margin_top - self.margin_bottom).max(1.0),
86        )
87    }
88}
89
90impl IntoElement for LapalomaCandlestickElement {
91    type Element = Self;
92    fn into_element(self) -> Self::Element {
93        self
94    }
95}
96
97impl Element for LapalomaCandlestickElement {
98    type RequestLayoutState = ();
99    type PrepaintState = ();
100
101    fn id(&self) -> Option<ElementId> {
102        None
103    }
104    fn source_location(&self) -> Option<&'static panic::Location<'static>> {
105        None
106    }
107
108    fn request_layout(
109        &mut self,
110        _id: Option<&GlobalElementId>,
111        _inspector_id: Option<&InspectorElementId>,
112        window: &mut Window,
113        cx: &mut App,
114    ) -> (LayoutId, Self::RequestLayoutState) {
115        let mut style = Style::default();
116        style.size.width = gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0));
117        style.size.height = gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0));
118        let id = window.request_layout(style, [], cx);
119        (id, ())
120    }
121
122    fn prepaint(
123        &mut self,
124        _id: Option<&GlobalElementId>,
125        _inspector_id: Option<&InspectorElementId>,
126        _bounds: Bounds<Pixels>,
127        _request_layout: &mut Self::RequestLayoutState,
128        _window: &mut Window,
129        _cx: &mut App,
130    ) -> Self::PrepaintState {
131    }
132
133    fn paint(
134        &mut self,
135        _id: Option<&GlobalElementId>,
136        _inspector_id: Option<&InspectorElementId>,
137        bounds: Bounds<Pixels>,
138        _request_layout: &mut Self::RequestLayoutState,
139        _prepaint: &mut Self::PrepaintState,
140        window: &mut Window,
141        _cx: &mut App,
142    ) {
143        let ox: f32 = bounds.origin.x.into();
144        let oy: f32 = bounds.origin.y.into();
145        let w: f32 = bounds.size.width.into();
146        let h: f32 = bounds.size.height.into();
147        let outer = Rect::new(ox, oy, w, h);
148        let plot = self.plot_rect(outer);
149
150        let cs = CoordinateSystem::new(self.viewport, plot);
151        let mut canvas = WindowCanvas::new(window);
152
153        if let Some(bg) = self.background {
154            canvas.fill_rect(outer, bg);
155        }
156
157        axis::paint_axes(
158            &mut canvas,
159            &cs,
160            &self.viewport,
161            self.axis_color,
162            self.axis_style,
163            TARGET_TICKS_X,
164            TARGET_TICKS_Y,
165        );
166
167        paint_candlesticks(&mut canvas, &cs, &self.data, self.style);
168    }
169}
170
171pub fn lapaloma_candlestick(
172    data: OhlcBuffer,
173    viewport: ChartViewport,
174) -> LapalomaCandlestickElement {
175    LapalomaCandlestickElement::new(data, viewport)
176}