Skip to main content

egui_charts/studies/
plots.rs

1//! Bands between study outputs or fixed levels, and shapes stored per shape instead of per bar.
2
3use egui::Color32;
4
5/// One boundary of a study band, in the study's value scale.
6#[derive(Clone, Copy, Debug)]
7pub enum FillBoundary {
8    /// A zero-based output line, including its offset and visibility.
9    Plot(usize),
10    /// A constant level across the pane.
11    Level(f64),
12}
13
14/// A band between two plots or levels.
15#[derive(Clone, Copy, Debug)]
16pub struct StudyFill {
17    /// First boundary.
18    pub first: FillBoundary,
19    /// Second boundary.
20    pub second: FillBoundary,
21    /// Colour when the first value is at least the second.
22    pub color: Color32,
23    /// Optional colour when the first value is below the second.
24    pub inverted_color: Option<Color32>,
25}
26
27/// One OHLCV candle spanning an exclusive range of source bar indices.
28#[derive(Clone, Debug)]
29pub struct StudyCandle {
30    /// Loaded source bars covered by this candle; gaps do not invent positions.
31    pub bars: std::ops::Range<usize>,
32    /// Aggregated period, stamped with its opening calendar label.
33    pub bar: crate::model::Bar,
34    /// Output whose colour, stroke width and visibility apply to this candle.
35    pub plot: usize,
36}
37
38/// A price band between two source bars, stored once instead of as a value on every bar.
39#[derive(Clone, Debug, PartialEq)]
40pub struct StudyZone {
41    /// Source bars the band spans, from the centre of the first to the centre of the last, or
42    /// from edge to edge when its study's zones cover whole bars.
43    pub bars: std::ops::Range<usize>,
44    /// Upper price.
45    pub top: f64,
46    /// Lower price.
47    pub bottom: f64,
48    /// Output whose colour, stroke width and visibility apply to this zone: its edges in that
49    /// colour and its band at 15% opacity.
50    pub plot: usize,
51}
52
53/// Where the text of a [`StudyLabel`] sits relative to its point.
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55#[non_exhaustive]
56pub enum LabelPlacement {
57    /// Above the point, starting at it and extending right.
58    AboveRight,
59    /// Above the point, ending at it and extending left.
60    AboveLeft,
61    /// Centred above the point.
62    Above,
63    /// Centred below the point.
64    Below,
65}
66
67/// Text beside a study value, stored once per label instead of per bar.
68///
69/// The text is written when the study calculates, so drawing a frame formats nothing.
70#[derive(Clone, Debug, PartialEq)]
71pub struct StudyLabel {
72    /// Source bar the text is attached to; may lie past the last bar, in future whitespace.
73    pub bar: usize,
74    /// Price the text is attached to, in the study's value scale.
75    pub price: f64,
76    /// The text; a line break starts a new line.
77    pub text: String,
78    /// Where the text sits relative to `bar` and `price`.
79    pub placement: LabelPlacement,
80    /// Output whose colour and visibility apply to the text. The text is drawn opaque.
81    pub plot: usize,
82}
83
84#[cfg(feature = "drawings")]
85pub(crate) fn drawing_prices(
86    study: &dyn super::Indicator,
87    bars: std::ops::Range<usize>,
88) -> impl Iterator<Item = f64> + '_ {
89    study
90        .drawings()
91        .iter()
92        .filter_map(move |drawing| {
93            if !study.plot_style(0).visible || !drawing.visible || bars.is_empty() {
94                return None;
95            }
96            let levels = drawing.fib_levels();
97            let extent = if levels.is_some() {
98                &drawing.chart_points[..2]
99            } else {
100                &drawing.chart_points
101            };
102            let first = extent.iter().map(|p| p.bar_idx).reduce(f32::min)?;
103            let last = extent.iter().map(|p| p.bar_idx).reduce(f32::max)?;
104            let left = drawing.extend_left
105                || levels
106                    .as_ref()
107                    .is_some_and(|(c, _)| c.extend_left || c.show_background);
108            let right = drawing.extend_right
109                || drawing.tool_type == crate::drawings::DrawingToolType::Pitchfork
110                || levels
111                    .as_ref()
112                    .is_some_and(|(c, _)| c.extend_right || c.show_background);
113            if (!left && first >= bars.end as f32) || (!right && last < bars.start as f32) {
114                return None;
115            }
116            Some(
117                drawing
118                    .chart_points
119                    .iter()
120                    .map(|p| p.price)
121                    .chain(levels.into_iter().flat_map(|(_, prices)| prices)),
122            )
123        })
124        .flatten()
125        .filter(|price| price.is_finite())
126}