1use std::collections::VecDeque;
2
3use ratatui::symbols;
4use ratatui::symbols::line::*;
5use ratatui::text::Span;
6use ratatui::style::{
7 Modifier,
8 Color,
9 Style,
10};
11use ratatui::widgets::{
12 Axis,
13 Block,
14 BorderType,
15 BarChart,
16 GraphType,
17 Dataset,
18 Chart,
19 LineGauge,
20 Borders,
21};
22
23use ndhistogram::{
25 Histogram,
27 Hist1D,
28};
29use ndhistogram::axis::{
30 Uniform,
31};
32
33pub const LG_LINE_HORIZONTAL : &str = "░";
34pub const LG_LINE: Set = Set {
35 vertical : THICK_VERTICAL,
36 horizontal : LG_LINE_HORIZONTAL,
38 top_right : THICK_TOP_RIGHT,
39 top_left : THICK_TOP_LEFT,
40 bottom_right : THICK_BOTTOM_RIGHT,
41 bottom_left : THICK_BOTTOM_LEFT,
42 vertical_left : THICK_VERTICAL_LEFT,
43 vertical_right : THICK_VERTICAL_RIGHT,
44 horizontal_down : THICK_HORIZONTAL_DOWN,
45 horizontal_up : THICK_HORIZONTAL_UP,
46 cross : THICK_CROSS,
47};
48
49use crate::colors::{
53 ColorTheme,
54};
55
56
57pub fn clean_data<'a>(histo : &'a Hist1D<Uniform<f32>>,
90 labels : &'a Vec<String>,
91 clean_from : usize) -> Vec<(&'a str,u64)> {
92 let mut max_pop_bin = 0;
93 let mut vec_index = 0;
94 let mut bins = Vec::<(u64, u64)>::new();
95 for bin in histo.iter() {
96 let bin_value = *bin.value as u64;
97 bins.push((bin.index as u64, bin_value));
98 if bin_value > 0 && bin.index > clean_from {
102 max_pop_bin = vec_index;
103 }
104 vec_index += 1;
105 }
106 bins.retain(|&(x,_)| x <= max_pop_bin);
107 let mut clean_data = Vec::<(&str, u64)>::new();
108 for n in bins.iter() {
109 clean_data.push((&labels[n.0 as usize], n.1));
110 }
111 clean_data
112}
113
114pub fn create_labels(histo : &Hist1D<Uniform<f32>>) -> Vec<String> {
117 let mut labels = Vec::<String>::new();
118 for bin in histo.iter() {
119 match bin.bin.start() {
120 None => {
121 labels.push(String::from("x"));
122 },
123 Some(value) => {
124 labels.push(format!("{}", value as u64));
125 }
126 }
127 }
128 labels
129}
130
131pub fn prep_data<'a>(histo : &'a Hist1D<Uniform<f32>>,
138 labels : &'a Vec<String>,
139 spacing : usize,
140 remove_uf : bool) -> Vec<(&'a str,u64)> {
141 let mut data = Vec::<(&str, u64)>::new();
142 for (k,bin) in histo.iter().enumerate() {
143 if k == 0 && remove_uf {
144 continue;
145 }
146 if k == 1 && remove_uf {
147 data.push((&labels[k], *bin.value as u64));
148 continue;
149 }
150 if k % spacing != 0 {
152 data.push(("-", *bin.value as u64));
153 } else {
154 data.push((&labels[k], *bin.value as u64));
155 }
156 }
157 data
158}
159
160pub fn histogram<'a>(hist_data : Vec<(&'a str, u64)>,
161 title : String,
162 bar_width : u16,
163 bar_gap : u16,
164 theme : &ColorTheme) -> BarChart<'a> {
165 let chart = BarChart::default()
171 .block(Block::default().title(title).borders(Borders::ALL))
172 .data(hist_data.as_slice())
173 .bar_width(bar_width)
174 .bar_gap(bar_gap)
175 .bar_style(theme.style())
177 .value_style(
178 theme.style()
179 .add_modifier(Modifier::BOLD),
182 )
183 .style(theme.background());
184 chart
185}
186
187pub fn timeseries<'a>(data : &'a mut VecDeque<(f64,f64)>,
188 ds_name : String,
189 xlabel : String,
190 theme : &'a ColorTheme) -> Chart<'a> {
191 let x_only : Vec::<f64> = data.iter().map(|z| z.0).collect();
192 let t_min : u64;
194 let mut t_max : u64;
195 if x_only.len() == 0 {
196 t_min = 0;
197 t_max = 0;
198 } else {
199 t_min = x_only[0] as u64;
200 t_max = x_only[x_only.len() -1] as u64;
201 }
202 t_max += (0.05*t_max as f64).round() as u64;
203 let t_spacing = (t_max - t_min)/10;
204 let mut t_labels = Vec::<Span>::new();
205 for k in 0..10 {
206 let _label = format!("{}", (t_min + t_spacing * k as u64));
207 t_labels.push(Span::from(_label));
208 }
209
210 let y_only : Vec::<f64> = data.iter().map(|z| z.1).collect();
211 let mut y_min = f64::MAX;
212 let mut y_max = f64::MIN;
213 if y_only.len() == 0 {
214 y_max = 0.0;
215 y_min = 0.0;
216 }
217 for y in y_only {
218 if y < y_min {
219 y_min = y;
220 }
221 if y > y_max {
222 y_max = y;
223 }
224 }
225 y_max += f64::abs(y_max)*0.05;
226 y_min -= f64::abs(y_min)*0.05;
227 let y_spacing = f64::abs(y_max - y_min)/5.0;
228 let mut y_labels = Vec::<Span>::new() ;
229 let mut precision = 0u8;
230 if f64::abs(y_max - y_min) <= 10.0 {
231 precision = 1;
232 }
233 if f64::abs(y_max - y_min) <= 1.0 {
234 precision = 2;
235 }
236 for k in 0..5 {
237 match precision {
238 0 => {
239 let _label = format!("{}", (y_min + y_spacing * k as f64).round() as i64);
240 y_labels.push(Span::from(_label));
241 },
242 1 => {
243 let _label = format!("{:.1}", (y_min + y_spacing * k as f64));
244 y_labels.push(Span::from(_label));
245 },
246 2 => {
247 let _label = format!("{:.2}", (y_min + y_spacing * k as f64));
248 y_labels.push(Span::from(_label));
249 },
250 _ => (),
251 }
252 }
253
254 let dataset = vec![Dataset::default()
255 .name(ds_name)
256 .marker(symbols::Marker::Braille)
257 .graph_type(GraphType::Line)
258 .style(theme.style())
259 .data(data.make_contiguous())];
260 let chart = Chart::new(dataset)
261 .block(
262 Block::default()
263 .borders(Borders::ALL)
264 .style(theme.style())
265 .title(xlabel )
266 .border_type(BorderType::Rounded),
267 )
268 .x_axis(Axis::default()
269 .title(Span::styled("MET [s]", Style::default().fg(Color::White)))
270 .style(theme.style())
271 .bounds([t_min as f64, t_max as f64])
272 .labels(t_labels.clone())
274 )
275 .y_axis(Axis::default()
276 .style(theme.style())
278 .bounds([y_min as f64, y_max as f64])
279 .labels(y_labels.clone())
281 )
282 .style(theme.style());
283 chart
284}
285
286
287pub fn gauge(title : String,
289 label : String,
290 ratio : f64,
291 theme : &ColorTheme) -> LineGauge {
292 let gauge = LineGauge::default()
293 .block(
294 Block::default()
295 .borders(Borders::ALL)
296 .style(theme.style())
297 .title(title)
298 .border_type(BorderType::Rounded)
299 )
300 .filled_style(
301 Style::default()
302 .fg(theme.hc)
303 .bg(theme.bg1)
304 .add_modifier(Modifier::BOLD)
305 )
306 .label(label)
308 .line_set(LG_LINE)
310 .ratio(ratio);
312 gauge
313}
314