Skip to main content

egui_charts/studies/
values.rs

1//! Column-wise study output, with NaN for a missing line value.
2
3use super::IndicatorValue;
4
5/// One contiguous allocation per output line. Every column has [`len`](Self::len) entries.
6/// Missing and non-finite outputs are stored as NaN. Read a line without allocating with
7/// [`line`](Self::line), or export owned rows with [`to_rows`](Self::to_rows).
8#[derive(Clone, Debug, Default)]
9pub struct IndicatorValues {
10    columns: Vec<Vec<f64>>,
11    len: usize,
12}
13
14impl IndicatorValues {
15    #[cfg(feature = "studies")]
16    pub(crate) fn set_value(&mut self, line: usize, bar: usize, value: f64) {
17        assert!(bar < self.len);
18        while self.columns.len() <= line {
19            self.columns.push(vec![f64::NAN; self.len]);
20        }
21        self.columns[line][bar] = if value.is_finite() { value } else { f64::NAN };
22    }
23
24    #[cfg(feature = "studies")]
25    pub(crate) fn append_from(&mut self, other: &Self, start: usize) {
26        let start = start.min(other.len);
27        let old_len = self.len;
28        self.resize(old_len + other.len - start, IndicatorValue::None);
29        for (line, values) in other.columns.iter().enumerate() {
30            while self.columns.len() <= line {
31                self.columns.push(vec![f64::NAN; self.len]);
32            }
33            self.columns[line][old_len..].copy_from_slice(&values[start..]);
34        }
35    }
36    /// Output for `len` bars before any line has a value.
37    pub fn missing(len: usize) -> Self {
38        Self {
39            columns: Vec::new(),
40            len,
41        }
42    }
43
44    #[cfg(feature = "studies")]
45    pub(crate) fn set(&mut self, bar: usize, value: IndicatorValue) {
46        assert!(bar < self.len);
47        let values = match &value {
48            IndicatorValue::None => &[][..],
49            IndicatorValue::Single(value) => std::slice::from_ref(value),
50            IndicatorValue::Multiple(values) => values,
51        };
52        while self.columns.len() < values.len() {
53            self.columns.push(vec![f64::NAN; self.len]);
54        }
55        for (index, line) in self.columns.iter_mut().enumerate() {
56            let value = values.get(index).copied().unwrap_or(f64::NAN);
57            line[bar] = if value.is_finite() { value } else { f64::NAN };
58        }
59    }
60    /// Empty output, with columns allocated as the first values arrive.
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    /// Number of source bars, including warm-up bars.
66    pub fn len(&self) -> usize {
67        self.len
68    }
69
70    /// Whether no source bars have been calculated.
71    pub fn is_empty(&self) -> bool {
72        self.len == 0
73    }
74
75    /// Output columns in the order of the study's line names.
76    pub fn columns(&self) -> &[Vec<f64>] {
77        &self.columns
78    }
79
80    /// A complete output line, or an empty slice if that line has no values yet.
81    pub fn line(&self, line: usize) -> &[f64] {
82        self.columns.get(line).map_or(&[], Vec::as_slice)
83    }
84
85    /// A finite value at a zero-based line and source bar, or `None` when missing.
86    pub fn value(&self, line: usize, bar: usize) -> Option<f64> {
87        self.line(line).get(bar).copied().filter(|v| v.is_finite())
88    }
89
90    /// Export rows for callers that need the legacy owned row format. This allocates;
91    /// rendering and numerical consumers should read the columns directly.
92    pub fn to_rows(&self) -> Vec<IndicatorValue> {
93        (0..self.len)
94            .map(|bar| {
95                if self.columns.iter().all(|line| line[bar].is_nan()) {
96                    IndicatorValue::None
97                } else if self.columns.len() == 1 {
98                    IndicatorValue::Single(self.columns[0][bar])
99                } else {
100                    IndicatorValue::Multiple(self.columns.iter().map(|line| line[bar]).collect())
101                }
102            })
103            .collect()
104    }
105
106    /// Append one bar's line values, using NaN for missing or non-finite entries.
107    pub fn push_lines(&mut self, values: &[f64]) {
108        while self.columns.len() < values.len() {
109            self.columns.push(vec![f64::NAN; self.len]);
110        }
111        for (index, line) in self.columns.iter_mut().enumerate() {
112            let value = values.get(index).copied().unwrap_or(f64::NAN);
113            line.push(if value.is_finite() { value } else { f64::NAN });
114        }
115        self.len += 1;
116    }
117
118    pub(crate) fn push(&mut self, value: IndicatorValue) {
119        match value {
120            IndicatorValue::None => self.push_lines(&[]),
121            IndicatorValue::Single(v) => self.push_lines(&[v]),
122            IndicatorValue::Multiple(v) => self.push_lines(&v),
123        }
124    }
125
126    /// Remove all output bars and columns.
127    pub fn clear(&mut self) {
128        self.columns.clear();
129        self.len = 0;
130    }
131
132    pub(crate) fn truncate(&mut self, len: usize) {
133        self.len = self.len.min(len);
134        for line in &mut self.columns {
135            line.truncate(len);
136        }
137    }
138
139    #[cfg(feature = "studies")]
140    pub(crate) fn resize(&mut self, len: usize, value: IndicatorValue) {
141        self.truncate(len);
142        while self.len < len {
143            self.push(value.clone());
144        }
145    }
146
147    #[cfg(feature = "studies")]
148    pub(crate) fn fill(&mut self, value: IndicatorValue) {
149        let len = self.len;
150        self.clear();
151        self.resize(len, value);
152    }
153
154    pub(crate) fn extend(&mut self, values: impl IntoIterator<Item = IndicatorValue>) {
155        for value in values {
156            self.push(value);
157        }
158    }
159}
160
161impl From<Vec<IndicatorValue>> for IndicatorValues {
162    fn from(values: Vec<IndicatorValue>) -> Self {
163        let mut output = Self::new();
164        output.extend(values);
165        output
166    }
167}
168
169impl FromIterator<IndicatorValue> for IndicatorValues {
170    fn from_iter<T: IntoIterator<Item = IndicatorValue>>(iter: T) -> Self {
171        let mut output = Self::new();
172        output.extend(iter);
173        output
174    }
175}