gpui_base/plot/shape/
stack.rs1#[derive(Clone, Debug)]
5#[non_exhaustive]
6pub struct StackPoint<T> {
7 pub y0: f32,
9 pub y1: f32,
11 pub data: T,
13}
14
15#[derive(Clone, Debug)]
17#[non_exhaustive]
18pub struct StackSeries<T> {
19 pub key: String,
21 pub index: usize,
23 pub points: Vec<StackPoint<T>>,
25}
26
27#[allow(clippy::type_complexity)]
28pub struct Stack<T> {
29 data: Vec<T>,
30 keys: Vec<String>,
31 value: Box<dyn Fn(&T, &str) -> Option<f32>>,
32}
33
34impl<T: Clone> Default for Stack<T> {
35 fn default() -> Self {
36 Self {
37 data: Vec::new(),
38 keys: Vec::new(),
39 value: Box::new(|_, _| None),
40 }
41 }
42}
43
44impl<T: Clone> Stack<T> {
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn data<I>(mut self, data: I) -> Self
51 where
52 I: IntoIterator<Item = T>,
53 {
54 self.data = data.into_iter().collect();
55 self
56 }
57
58 pub fn keys<I, S>(mut self, keys: I) -> Self
60 where
61 I: IntoIterator<Item = S>,
62 S: Into<String>,
63 {
64 self.keys = keys.into_iter().map(|s| s.into()).collect();
65 self
66 }
67
68 pub fn value<F>(mut self, value: F) -> Self
70 where
71 F: Fn(&T, &str) -> Option<f32> + 'static,
72 {
73 self.value = Box::new(value);
74 self
75 }
76
77 pub fn series(&self) -> Vec<StackSeries<T>> {
79 if self.data.is_empty() || self.keys.is_empty() {
80 return Vec::new();
81 }
82
83 let n = self.data.len(); let m = self.keys.len(); let mut matrix: Vec<Vec<f32>> = Vec::with_capacity(m);
88 for key in &self.keys {
89 let mut series_values = Vec::with_capacity(n);
90 for datum in &self.data {
91 let value = (self.value)(datum, key).unwrap_or(0.0);
92 series_values.push(value);
93 }
94 matrix.push(series_values);
95 }
96
97 let order: Vec<usize> = (0..m).collect();
99
100 let mut stacks: Vec<Vec<(f32, f32)>> = vec![vec![(0.0, 0.0); n]; m];
102
103 for j in 0..n {
105 let mut y0 = 0.0;
106 for &i in &order {
107 let y1 = y0 + matrix[i][j];
108 stacks[i][j] = (y0, y1);
109 y0 = y1;
110 }
111 }
112
113 let mut result = Vec::with_capacity(m);
115 for (i, key) in self.keys.iter().enumerate() {
116 let points = self
117 .data
118 .iter()
119 .enumerate()
120 .map(|(j, datum)| StackPoint {
121 y0: stacks[i][j].0,
122 y1: stacks[i][j].1,
123 data: datum.clone(),
124 })
125 .collect();
126
127 result.push(StackSeries {
128 key: key.clone(),
129 index: i,
130 points,
131 });
132 }
133
134 result
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[derive(Clone, Debug)]
143 struct SalesData {
144 #[allow(dead_code)]
145 date: String,
146 apples: f32,
147 bananas: f32,
148 cherries: f32,
149 }
150
151 #[test]
152 fn test_basic_stack() {
153 let data = vec![
154 SalesData {
155 date: "Jan".to_string(),
156 apples: 10.0,
157 bananas: 20.0,
158 cherries: 30.0,
159 },
160 SalesData {
161 date: "Feb".to_string(),
162 apples: 15.0,
163 bananas: 25.0,
164 cherries: 35.0,
165 },
166 ];
167
168 let stack = Stack::new()
169 .data(data)
170 .keys(vec!["apples", "bananas", "cherries"])
171 .value(|d, key| match key {
172 "apples" => Some(d.apples),
173 "bananas" => Some(d.bananas),
174 "cherries" => Some(d.cherries),
175 _ => None,
176 });
177
178 let series = stack.series();
179
180 assert_eq!(series.len(), 3);
181 assert_eq!(series[0].key, "apples");
182 assert_eq!(series[0].points[0].y0, 0.0);
183 assert_eq!(series[0].points[0].y1, 10.0);
184
185 assert_eq!(series[1].key, "bananas");
186 assert_eq!(series[1].points[0].y0, 10.0);
187 assert_eq!(series[1].points[0].y1, 30.0);
188
189 assert_eq!(series[2].key, "cherries");
190 assert_eq!(series[2].points[0].y0, 30.0);
191 assert_eq!(series[2].points[0].y1, 60.0);
192 }
193}