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