Skip to main content

gpui_component/chart/
mod.rs

1mod area_chart;
2mod bar_chart;
3mod candlestick_chart;
4mod line_chart;
5mod pie_chart;
6mod radar_chart;
7mod sankey_chart;
8
9pub use area_chart::AreaChart;
10pub use bar_chart::BarChart;
11pub use candlestick_chart::CandlestickChart;
12pub use line_chart::LineChart;
13pub use pie_chart::PieChart;
14pub use radar_chart::{RadarChart, RadarLabel};
15pub use sankey_chart::{SankeyChart, SankeyLabel};
16
17use std::hash::Hash;
18
19use gpui::{App, Hsla, Pixels, SharedString, TextAlign, px};
20use gpui_base::Spring;
21
22use crate::{
23    ActiveTheme,
24    plot::{
25        AxisText,
26        scale::{Scale, ScaleBand, ScalePoint},
27    },
28};
29
30/// The spring a chart's pointer — the crosshair, highlight band or hover dot —
31/// follows the hovered datum with.
32///
33/// A pointer chases the cursor across neighbouring data, so it has to arrive
34/// well within the time the cursor takes to reach the next datum: ECharts moves
35/// its axis pointer over 200 ms on an exponential ease-out, which is most of
36/// the way there in the first third. The fast tier as a critically damped
37/// response lands in the same place, and the tolerance is sub-pixel so the
38/// spring rests once nothing visible moves.
39pub(crate) fn pointer_spring(cx: &App) -> Spring {
40    Spring::new(cx.theme().motion_tokens().duration_fast).with_epsilon(0.1)
41}
42
43/// The size of the dot marking the hovered data point.
44pub(crate) const HOVER_DOT_SIZE: Pixels = px(8.);
45
46/// The ring behind the hovered dot at full focus.
47const HOVER_HALO_SIZE: f32 = 20.;
48
49/// The ring behind a hovered dot, growing out of the dot as the hover fades in.
50pub(crate) fn hover_halo_size(focus: f32) -> Pixels {
51    px(HOVER_HALO_SIZE * focus)
52}
53
54/// Build x-axis labels for point-based scales (`LineChart`, `AreaChart`).
55///
56/// Point scales place items at evenly spaced positions. The first label is
57/// left-aligned, the last is right-aligned, and the rest are centered.
58pub(crate) fn build_point_x_labels<T, X>(
59    data: &[T],
60    x_fn: &dyn Fn(&T) -> X,
61    x_scale: &ScalePoint<X>,
62    tick_margin: usize,
63    color: Hsla,
64) -> Vec<AxisText>
65where
66    X: PartialEq + Into<SharedString>,
67{
68    let data_len = data.len();
69    data.iter()
70        .enumerate()
71        .filter_map(|(i, d)| {
72            if (i + 1) % tick_margin != 0 {
73                return None;
74            }
75            x_scale.tick(&x_fn(d)).map(|x_tick| {
76                let align = match i {
77                    0 if data_len == 1 => TextAlign::Center,
78                    0 => TextAlign::Left,
79                    i if i == data_len - 1 => TextAlign::Right,
80                    _ => TextAlign::Center,
81                };
82                // Call x_fn again to get an owned value for the label text.
83                AxisText::new(x_fn(d).into(), x_tick, color).align(align)
84            })
85        })
86        .collect()
87}
88
89/// Build axis labels for band-based scales (`BarChart`, `CandlestickChart`).
90///
91/// Band scales place items in evenly sized bands. The returned `tick`
92/// coordinate is the centre of each band along the band axis; the caller
93/// decides whether to feed the result to `PlotAxis::x_label` (vertical
94/// charts) or `PlotAxis::y_label` (horizontal charts).
95pub(crate) fn build_band_labels<T, X>(
96    data: &[T],
97    x_fn: &dyn Fn(&T) -> X,
98    x_scale: &ScaleBand<X>,
99    band_width: f32,
100    tick_margin: usize,
101    color: Hsla,
102) -> Vec<AxisText>
103where
104    X: Eq + Hash + Into<SharedString>,
105{
106    data.iter()
107        .enumerate()
108        .filter_map(|(i, d)| {
109            if (i + 1) % tick_margin != 0 {
110                return None;
111            }
112            x_scale.tick(&x_fn(d)).map(|x_tick| {
113                // Call x_fn again to get an owned value for the label text.
114                AxisText::new(x_fn(d).into(), x_tick + band_width / 2., color)
115                    .align(TextAlign::Center)
116            })
117        })
118        .collect()
119}