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