1use crate::alignment::Alignment;
2use crate::color::{Color, ColorMode};
3use crate::format::NumberFormatter;
4use crate::interval::{Interval, draw_labeled_intervals};
5use crate::lines::Lines;
6use crate::skip_value::SkipValue;
7use hmath::Ratio;
8use std::collections::HashSet;
9use std::sync::Arc;
10
11mod merge;
12mod setters;
13
14pub use merge::*;
15
16#[derive(Clone)]
17pub struct Graph {
18 data: GraphData,
19
20 title: Option<String>,
21 big_title: bool,
22 title_color: Option<Color>,
23
24 plot_width: usize,
25 plot_height: usize,
26
27 block_width: Option<usize>,
28
29 x_label_margin: usize,
30 y_label_margin: usize,
31
32 x_axis_label: Option<String>,
33 y_axis_label: Option<String>,
34
35 labeled_intervals: Vec<Interval>,
36
37 y_min: Option<Ratio>,
38 y_max: Option<Ratio>,
39
40 pretty_y: Option<Ratio>,
41
42 y_label_formatter: Arc<dyn NumberFormatter>,
43
44 skip_value: SkipValue,
46 skip_skip_range: Option<(Option<Ratio>, Option<Ratio>)>,
47
48 horizontal_break: Option<(usize, usize)>,
50
51 paddings: [usize; 4],
52
53 color_mode: ColorMode,
54 primary_color: Option<Color>,
55}
56
57#[derive(Debug, PartialEq, Clone)]
58enum GraphData {
59 Data1D (Vec<(String, Ratio)>),
60 Data2D {
61 data: Vec<(usize, usize, u16)>,
62 x_labels: Vec<Option<String>>,
63 y_labels: Vec<Option<String>>,
64 },
65 None,
66}
67
68impl GraphData {
69 pub fn unwrap_1d(&self) -> &Vec<(String, Ratio)> {
70 if let GraphData::Data1D(v) = self {
71 v
72 } else {
73 panic!("Unable to unwrap 1d data from {self:?}")
74 }
75 }
76
77 pub fn unwrap_2d(&self) -> ( &Vec<(usize, usize, u16)>, &Vec<Option<String>>, &Vec<Option<String>> ) {
78 if let GraphData::Data2D { data, x_labels, y_labels } = self {
79 (&data, &x_labels, &y_labels)
80 } else {
81 panic!("Unable to unwrap 2d data from {self:?}")
82 }
83 }
84
85 pub fn len(&self) -> usize {
86 match self {
87 GraphData::Data1D(data) => data.len(),
88 GraphData::Data2D { data, .. } => data.len(),
89 GraphData::None => 0,
90 }
91 }
92
93 pub fn is_empty(&self) -> bool {
94 match self {
95 GraphData::Data1D(data) => data.is_empty(),
96 GraphData::Data2D { data, .. } => data.is_empty(),
97 GraphData::None => true,
98 }
99 }
100}
101
102impl Graph {
103 pub fn new(plot_width: usize, plot_height: usize) -> Self {
104 Graph {
105 plot_width,
106 plot_height,
107 ..Default::default()
108 }
109 }
110
111 pub fn draw(&self) -> String {
113 match &self.data {
114 GraphData::Data1D(_) => self.draw_1d_graph(),
115 GraphData::Data2D { .. } => self.draw_2d_graph(),
116 GraphData::None => panic!("Cannot draw a graph without any data"),
117 }
118 }
119
120 pub(crate) fn get_actual_plot_width(&self) -> usize {
121 match &self.data {
122 GraphData::Data1D(_) => match self.block_width {
123 Some(w) => w * self.data.len(),
124 _ => self.plot_width,
125 },
126 _ => self.plot_width,
127 }
128 }
129
130 pub fn is_valid(&self) -> bool {
135 (match (&self.y_min, &self.y_max) { (Some(n), Some(m)) if n.gt_rat(&m) => false,
137 _ => true
138 }) && match &self.data {
139 GraphData::Data1D(v) => v.len() > 0,
140 GraphData::Data2D { data, x_labels, y_labels } if x_labels.len() > 0 && y_labels.len() > 0 => {
141 let mut x_max = 0;
142 let mut y_max = 0;
143
144 for (x, y, _) in data.iter() {
145
146 if *x > x_max {
147 x_max = *x;
148 }
149
150 if *y > y_max {
151 y_max = *y;
152 }
153
154 }
155
156 x_labels.len() >= x_max && y_labels.len() >= y_max && x_labels.len() == self.plot_width && y_labels.len() == self.plot_height
157 },
158 _ => false,
159 } && {
160 self.labeled_intervals.iter().all(|i| i.is_valid())
161 } && {
162 true
164 }
165 }
166
167 fn draw_1d_graph(&self) -> String {
168 let mut data = self.data.unwrap_1d().clone();
169
170 let plot_width = self.get_actual_plot_width();
171
172 if data.len() > plot_width * 2 {
173 data = pick_meaningful_values(&data, plot_width);
174 }
175
176 let (data_min, data_max, max_diff) = get_min_max_diff(&data, self.plot_height);
177 let (mut y_min, mut y_max) = unwrap_y_min_max(&self.y_min, &self.y_max, &data_min, &data_max);
178
179 let mut ratio_of_subgraphs = (3, 3);
180
181 let mut skip_range = match &self.skip_value {
182 _ if self.plot_height <= 18 => None,
183 SkipValue::None => None,
184 SkipValue::Automatic => {
185 if !max_diff.is_zero() && y_max.sub_rat(&y_min).div_rat(&max_diff).lt_i32(3) {
186 let (y_min_, from, to, y_max_, ratio_of_subgraphs_) = get_where_to_skip(data.clone());
187 ratio_of_subgraphs = ratio_of_subgraphs_;
188
189 if self.y_min.is_none() {
191 y_min = y_min_;
192 }
193
194 if self.y_max.is_none() {
195 y_max = y_max_;
196 }
197
198 let respect_skip_skip_range = if let Some((skip_skip_from, skip_skip_to)) = &self.skip_skip_range {
199 match (skip_skip_from, skip_skip_to) {
200 (Some(skip_skip_from), Some(skip_skip_to)) => if skip_skip_from.lt_rat(&from) && skip_skip_to.gt_rat(&to) {
201 None
202 } else {
203 Some((from, to))
204 },
205 (None, Some(skip_skip_to)) => if skip_skip_to.gt_rat(&to) {
206 None
207 } else {
208 Some((from, to))
209 },
210 (Some(skip_skip_from), None) => if skip_skip_from.lt_rat(&from) {
211 None
212 } else {
213 Some((from, to))
214 },
215 (None, None) => None,
217 }
218 } else {
219 Some((from, to))
220 };
221
222 if let Some((from, to)) = respect_skip_skip_range {
223 if y_min.lt_rat(&from) && to.lt_rat(&y_max) {
225 Some((from, to))
226 }
227
228 else {
229 None
230 }
231 }
232
233 else {
234 None
235 }
236 }
237
238 else {
239 None
240 }
241 },
242 SkipValue::Manual { from, to } => {
243 let mut values_below_skip_range = HashSet::new();
244 let mut values_above_skip_range = HashSet::new();
245
246 for (_, n) in data.iter() {
247
248 if n.lt_rat(from) {
249 values_below_skip_range.insert(n.clone());
250 }
251
252 else if n.gt_rat(to) {
253 values_above_skip_range.insert(n.clone());
254 }
255
256 }
257
258 if values_below_skip_range.len() * 2 > values_above_skip_range.len() * 3 {
259 ratio_of_subgraphs = (4, 2);
260 }
261
262 else if values_above_skip_range.len() * 2 > values_below_skip_range.len() * 3 {
263 ratio_of_subgraphs = (2, 4);
264 }
265
266 Some((from.clone(), to.clone()))
267 },
268 };
269
270 if let Some((from, to)) = &skip_range {
271 if from.lt_rat(&y_min) || to.gt_rat(&y_max) {
272 skip_range = None;
273 }
274 }
275
276 let y_labels_len;
277
278 let mut plot = match &skip_range {
279 None => {
280 let (y_min, y_max) = prettify_y_labels(
281 &y_min,
282 &y_max,
283 self.plot_height,
284 self.pretty_y.as_ref().map(|n| (self.y_min.is_none(), self.y_max.is_none(), n.clone()))
285 );
286
287 let mut plot = plot_1d(
288 &data,
289 plot_width,
290 self.plot_height,
291 &y_min,
292 &y_max,
293 false, self.primary_color.clone(),
295 );
296 plot = plot.add_border([false, true, true, false]);
297
298 let y_labels = draw_y_labels_1d_plot(
299 &y_min,
300 &y_max,
301 self.plot_height,
302 self.y_label_margin,
303 &self.y_label_formatter,
304 );
305 y_labels_len = y_labels.get_width();
306
307 y_labels.merge_horizontally(&plot, Alignment::First)
308 },
309 Some((from, to)) => {
310 let (mut height1, mut height2) = (
311 self.plot_height * ratio_of_subgraphs.0 / 6,
312 self.plot_height * ratio_of_subgraphs.1 / 6,
313 );
314
315 if height1 > height2 {
318 height1 += self.plot_height - height1 - height2;
319 height2 -= 1;
320 }
321
322 else {
323 height2 += self.plot_height - height1 - height2;
324 height1 -= 1;
325 }
326
327 let (plot1_y_min, plot1_y_max) = prettify_y_labels(
330 &y_min,
331 &from,
332 height1,
333 self.pretty_y.as_ref().map(|n| (self.y_min.is_none(), self.skip_value.is_automatic(), n.clone()))
334 );
335
336 let mut plot1 = plot_1d(
337 &data,
338 plot_width,
339 height1,
340 &plot1_y_min,
341 &plot1_y_max,
342 true, self.primary_color.clone(),
344 );
345 plot1 = plot1.add_border([false, true, true, false]);
346
347 let (plot2_y_min, plot2_y_max) = prettify_y_labels(
348 &to,
349 &y_max,
350 height2,
351 self.pretty_y.as_ref().map(|n| (self.skip_value.is_automatic(), self.y_max.is_none(), n.clone()))
352 );
353
354 let mut plot2 = plot_1d(
355 &data,
356 plot_width,
357 height2,
358 &plot2_y_min,
359 &plot2_y_max,
360 false, self.primary_color.clone(),
362 );
363 plot2 = plot2.add_border([false, false, true, false]);
364
365 let mut y_labels1 = draw_y_labels_1d_plot(
366 &plot1_y_min,
367 &plot1_y_max,
368 height1,
369 self.y_label_margin,
370 &self.y_label_formatter,
371 );
372 let mut y_labels2 = draw_y_labels_1d_plot(
373 &plot2_y_min,
374 &plot2_y_max,
375 height2,
376 self.y_label_margin,
377 &self.y_label_formatter,
378 );
379
380 if y_labels1.get_width() < y_labels2.get_width() {
381 y_labels1 = y_labels1.add_padding([0, 0, y_labels2.get_width() - y_labels1.get_width(), 0]);
382 }
383
384 else if y_labels2.get_width() < y_labels1.get_width() {
385 y_labels2 = y_labels2.add_padding([0, 0, y_labels1.get_width() - y_labels2.get_width(), 0]);
386 }
387
388 y_labels_len = y_labels1.get_width();
389 plot1 = y_labels1.merge_horizontally(&plot1, Alignment::First);
390 plot2 = y_labels2.merge_horizontally(&plot2, Alignment::First);
391
392 let mut horizontal_line = Lines::from_string(&"~".repeat(plot1.get_width()), Alignment::First, &ColorMode::None);
393 horizontal_line.set_color_all(self.primary_color.clone());
394
395 plot1 = horizontal_line.merge_vertically(&plot1, Alignment::First);
396
397 plot2.merge_vertically(&plot1, Alignment::First)
398 },
399 };
400
401 let x_labels = draw_x_labels(&data, plot_width, self.x_label_margin);
402 plot = plot.merge_vertically(&x_labels, Alignment::Last);
403
404 if !self.labeled_intervals.is_empty() {
405 let arrows = draw_labeled_intervals(&self.labeled_intervals, plot_width);
406 plot = plot.merge_vertically(&arrows, Alignment::Last);
407 }
408
409 if let Some((from, to)) = self.horizontal_break {
410 let plot_width = plot.get_width();
411 let plot_height = plot.get_height();
412 let left_half = plot.crop(0, 0, y_labels_len + from, plot_height);
413 let right_half = plot.crop(y_labels_len + to, 0, plot_width - y_labels_len - to, plot_height);
414 let break_line = draw_vertial_line(plot_height, self.primary_color.clone());
415
416 plot = left_half.merge_horizontally(&break_line, Alignment::Last);
417 plot = plot.merge_horizontally(&right_half, Alignment::Last);
418 }
419
420 if let Some(xal) = &self.x_axis_label {
421 let mut xal = Lines::from_string(xal, Alignment::First, &ColorMode::None);
422 xal = xal.add_padding([self.plot_height, 0, 0, 0]);
423 plot = plot.merge_horizontally(&xal, Alignment::First);
424 }
425
426 if let Some(yal) = &self.y_axis_label {
427 let yal = Lines::from_string(yal, Alignment::First, &ColorMode::None);
428 plot = yal.merge_vertically(&plot, Alignment::First);
429 }
430
431 if let Some(t) = &self.title {
432 let title = draw_title(t, self.big_title, self.title_color.clone());
433 plot = title.merge_vertically(&plot, Alignment::Center);
434 }
435
436 plot = plot.add_padding(self.paddings);
437
438 plot.to_string(&self.color_mode)
439 }
440
441 fn draw_2d_graph(&self) -> String {
442 let (
443 data, x_labels, y_labels
444 ) = self.data.unwrap_2d();
445
446 let mut plot = plot_2d(&data, self.plot_width, self.plot_height);
447 plot = plot.add_border([false, true, true, false]);
448
449 let x_labels = draw_x_labels(
450 &x_labels.iter().map(
451 |s| (
452 match s { Some(s) => s.to_string(), _ => String::new() },
453 ()
454 )
455 ).collect(),
456 self.plot_width,
457 self.x_label_margin
458 );
459 plot = plot.merge_vertically(&x_labels, Alignment::Last);
460
461 let y_labels = draw_y_labels_2d_plot(y_labels);
462 plot = y_labels.merge_horizontally(&plot, Alignment::First);
463
464 if let Some(xal) = &self.x_axis_label {
465 let mut xal = Lines::from_string(xal, Alignment::First, &ColorMode::None);
466 xal = xal.add_padding([self.plot_height, 0, 0, 0]);
467 plot = plot.merge_horizontally(&xal, Alignment::First);
468 }
469
470 if let Some(yal) = &self.y_axis_label {
471 let yal = Lines::from_string(yal, Alignment::First, &ColorMode::None);
472 plot = yal.merge_vertically(&plot, Alignment::First);
473 }
474
475 if let Some(t) = &self.title {
476 let title = draw_title(t, self.big_title, self.title_color.clone());
477 plot = title.merge_vertically(&plot, Alignment::Center);
478 }
479
480 plot = plot.add_padding(self.paddings);
481
482 plot.to_string(&self.color_mode)
483 }
484
485 fn adjust_all_labeled_intervals(&mut self) {
486 let plot_width = self.get_actual_plot_width();
487 let data_len = self.data.len();
488
489 if !self.data.is_empty() {
490 self.labeled_intervals.iter_mut().for_each(
491 |i| i.adjust_coordinate(plot_width, data_len)
492 );
493 }
494 }
495}
496
497fn pick_meaningful_values(data: &Vec<(String, Ratio)>, width: usize) -> Vec<(String, Ratio)> {
498 let half_width = width / 2;
500
501 let mut last_ind = 0;
502 let mut result = Vec::with_capacity(width);
503
504 for i in 0..half_width {
505 let curr_ind = (i + 1) * data.len() / half_width;
506 let mut min_ind = 0;
507 let mut min_val = &data[last_ind].1;
508 let mut max_ind = 0;
509 let mut max_val = &data[last_ind].1;
510
511 for (ind, (_, val)) in data[last_ind..curr_ind].iter().enumerate() {
512 if val.gt_rat(&max_val) {
513 max_ind = ind;
514 max_val = val;
515 }
516
517 else if val.lt_rat(&min_val) {
518 min_ind = ind;
519 min_val = val;
520 }
521 }
522
523 if min_ind < max_ind {
524 result.push(data[last_ind + min_ind].clone());
525 result.push(data[last_ind + max_ind].clone());
526 }
527
528 else {
529 result.push(data[last_ind + max_ind].clone());
530 result.push(data[last_ind + min_ind].clone());
531 }
532
533 last_ind = curr_ind;
534 }
535
536 result
537}
538
539fn get_where_to_skip(mut data: Vec<(String, Ratio)>) -> (Ratio, Ratio, Ratio, Ratio, (usize, usize)) {
540 let mut curr_max_diff = Ratio::zero();
541 let mut curr_max_diff_ind = 0;
542 data.sort_unstable_by_key(|(_, n)| n.clone());
543
544 for i in 0..(data.len() - 1) {
545 let curr_diff = data[i + 1].1.sub_rat(&data[i].1);
546
547 if curr_diff.gt_rat(&curr_max_diff) {
548 curr_max_diff = curr_diff;
549 curr_max_diff_ind = i;
550 }
551
552 }
553
554 let mut padding1 = data[curr_max_diff_ind].1.sub_rat(&data[0].1).div_i32(16);
555 let mut padding2 = data[data.len() - 1].1.sub_rat(&data[curr_max_diff_ind + 1].1).div_i32(16);
556
557 if padding1.is_zero() {
558 padding1 = data[curr_max_diff_ind + 1].1.sub_rat(&data[curr_max_diff_ind].1).div_i32(16);
559 }
560
561 if padding2.is_zero() {
562 padding2 = data[curr_max_diff_ind + 1].1.sub_rat(&data[curr_max_diff_ind].1).div_i32(16);
563 }
564
565 let mut ratio_of_subgraphs = (3, 3);
566 let values_below_skip_range = data[0..(curr_max_diff_ind + 1)].iter().map(|(_, n)| n).collect::<HashSet<&Ratio>>();
567 let values_above_skip_range = data[(curr_max_diff_ind + 1)..].iter().map(|(_, n)| n).collect::<HashSet<&Ratio>>();
568
569 if values_below_skip_range.len() * 2 > values_above_skip_range.len() * 3 {
570 ratio_of_subgraphs = (4, 2);
571 }
572
573 else if values_above_skip_range.len() * 2 > values_below_skip_range.len() * 3 {
574 ratio_of_subgraphs = (2, 4);
575 }
576
577 (
578 data[0].1.sub_rat(&padding1),
579 data[curr_max_diff_ind].1.add_rat(&padding1),
580 data[curr_max_diff_ind + 1].1.sub_rat(&padding2),
581 data[data.len() - 1].1.add_rat(&padding2),
582 ratio_of_subgraphs,
583 )
584}
585
586fn unwrap_y_min_max(self_y_min: &Option<Ratio>, self_y_max: &Option<Ratio>, data_min: &Ratio, data_max: &Ratio) -> (Ratio, Ratio) {
587 match (&self_y_min, &self_y_max) {
588 (Some(n), Some(m)) => (n.clone(), m.clone()),
589 (Some(n), None) => if n.lt_rat(&data_max) {
590 (n.clone(), data_max.clone())
591 } else {
592 (n.clone(), n.add_i32(1))
593 },
594 (None, Some(n)) => if n.gt_rat(&data_min) {
595 (data_min.clone(), n.clone())
596 } else {
597 (n.sub_i32(1), n.clone())
598 },
599 (None, None) => (data_min.clone(), data_max.clone()),
600 }
601}
602
603fn get_min_max_diff(v: &Vec<(String, Ratio)>, height: usize) -> (Ratio, Ratio, Ratio) { if v.len() == 0 {
605 return (Ratio::zero(), Ratio::one(), Ratio::zero());
606 }
607
608 let mut data = v.iter().map(|(_, n)| n.clone()).collect::<Vec<Ratio>>();
609 data.sort_unstable();
610
611 let curr_min = &data[0];
612 let curr_max = &data[data.len() - 1];
613 let mut max_diff = Ratio::zero();
614
615 for i in 0..(data.len() - 1) {
616 let diff = data[i + 1].sub_rat(&data[i]);
617
618 if diff.gt_rat(&max_diff) {
619 max_diff = diff;
620 }
621 }
622
623 let mut diff = curr_max.sub_rat(curr_min).div_i32(16);
624
625 if diff.is_zero() {
626 diff = Ratio::from_i32(height as i32).div_i32(4);
627 }
628
629 let min = curr_min.sub_rat(&diff);
630 let max = curr_max.add_rat(&diff);
631
632 (min, max, max_diff)
633}
634
635fn draw_title(title: &str, big_title: bool, title_color: Option<Color>) -> Lines {
636 let mut result = if big_title {
637 Lines::from_string(&asciibox::render_string(title, asciibox::RenderOption::default()), Alignment::First, &ColorMode::None)
638 }
639
640 else {
641 Lines::from_string(title, Alignment::Center, &ColorMode::None)
642 };
643
644 result.set_color_all(title_color);
645
646 result
647}
648
649fn draw_y_labels_2d_plot(y_labels: &Vec<Option<String>>) -> Lines {
651 Lines::from_string(
652 &y_labels.iter().map(
653 |s| match s {
654 Some(s) => s.replace("\n", " "),
655 _ => String::new(),
656 }
657 ).collect::<Vec<String>>().join("\n"),
658 Alignment::Last,
659 &ColorMode::None,
660 )
661}
662
663fn draw_y_labels_1d_plot(
665 y_min: &Ratio,
666 y_max: &Ratio,
667 height: usize,
668 margin: usize,
669 formatter: &Arc<dyn NumberFormatter>,
670) -> Lines {
671 let mut labels = Vec::with_capacity(height);
672 let y_diff = y_max.sub_rat(y_min);
673 let y_label_step = y_diff.div_i32(height as i32);
674 let mut curr_max_width = 0;
675
676 for y in 0..height {
677 if margin > 1 && y % margin != 0 {
678 labels.push(String::new());
679 continue;
680 }
681
682 let curr_y = y_max.sub_rat(&y_label_step.mul_i32(y as i32));
683 let curr_label = formatter.f(&curr_y);
684
685 if curr_label.len() > curr_max_width {
686 curr_max_width = curr_label.len();
687 }
688
689 labels.push(curr_label);
690 }
691
692 Lines::from_string(&labels.join("\n"), Alignment::Last, &ColorMode::None)
693}
694
695fn draw_x_labels<T>(data: &Vec<(String, T)>, width: usize, margin: usize) -> Lines {
697 let mut result = Lines::new(width, 2);
698
699 let mut first_line_filled = 0;
700 let mut second_line_filled = 0;
701 let mut on_first_line = false;
702 let mut last_ind = usize::MAX;
703
704 for x in 0..width {
705 let data_ind = x * data.len() / width;
706
707 if on_first_line && x < first_line_filled || !on_first_line && x < second_line_filled || data_ind == last_ind {
708 continue;
709 }
710
711 let curr_label = &data[data_ind].0;
712 let y_ind = on_first_line as usize;
713
714 if curr_label.len() + x >= width {
715 on_first_line = !on_first_line;
716 continue;
717 }
718
719 for (lab_ind, c) in curr_label.chars().enumerate() {
720 let c = if c == '\n' { ' ' as u16 } else { c as u16 };
721
722 result.set(x + lab_ind, y_ind, c);
723 }
724
725 last_ind = data_ind;
726
727 if on_first_line {
728 first_line_filled = x + curr_label.len() + margin;
729 }
730
731 else {
732 second_line_filled = x + curr_label.len() + margin;
733 }
734
735 on_first_line = !on_first_line;
736 }
737
738 result
739}
740
741fn plot_2d(data: &Vec<(usize, usize, u16)>, width: usize, height: usize) -> Lines {
743 let mut result = Lines::new(width, height);
744
745 for (x, y, c) in data.iter() {
746 if *c == '\n' as u16 {
747 result.set(*x, *y, 32);
748 }
749
750 else {
751 result.set(*x, *y, *c);
752 }
753 }
754
755 result
756}
757
758fn plot_1d(data: &Vec<(String, Ratio)>, width: usize, height: usize, y_min: &Ratio, y_max: &Ratio, no_overflow_char: bool, overflow_char_color: Option<Color>) -> Lines {
760 let mut result = Lines::new(width, height);
761 let y_diff = y_max.sub_rat(&y_min);
762
763 for x in 0..width {
764 let data_ind = x * data.len() / width;
765 let data_val = &data[data_ind].1;
766 let mut overflow = false;
767
768 let mut y_start = match y_max.sub_rat(data_val).div_rat(&y_diff).mul_i32(height as i32).mul_i32(4).truncate_bi().to_i32() {
770 Ok(n) if n < 0 => {
771 overflow = true;
772 0
773 },
774 Ok(n) => n as usize,
775 Err(_) => usize::MAX,
776 };
777
778 let block_type = y_start % 4;
779 y_start /= 4;
780
781 if y_start + 1 > height {
782 continue;
783 }
784
785 for y in y_start..height {
786 result.set(x, y, '█' as u16);
787 }
788
789 if overflow && !no_overflow_char {
790 result.set(x, 0, '^' as u16);
791 result.set_color(x, 0, overflow_char_color.clone());
792 }
793
794 else {
795 result.set(x, y_start, [
796 '█' as u16,
797 '▆' as u16,
798 '▄' as u16,
799 '▂' as u16,
800 ][block_type])
801 }
802
803 }
804
805 result
806}
807
808fn prettify_y_labels(old_y_min: &Ratio, old_y_max: &Ratio, height: usize, pretty_y_label_info: Option<(bool, bool, Ratio)>) -> (Ratio, Ratio) {
814 if let Some((y_min_movable, y_max_movable, interval)) = pretty_y_label_info {
815 if !y_max_movable || (!y_min_movable && !old_y_min.div_rat(&interval).is_integer()) {
816 (old_y_min.clone(), old_y_max.clone())
817 }
818
819 else {
820 let curr_interval = old_y_max.sub_rat(old_y_min).div_i32(height as i32);
821
822 let should_be_multiple_of_16 = curr_interval.div_rat(&interval).mul_i32(16).round_bi();
827
828 if let Ok(n) = should_be_multiple_of_16.to_i32() {
829 if n < 15 || (17 < n && n < 30)
830 || (34 < n && n < 45)
831 || (51 < n && n < 60)
832 || (68 < n && n < 75)
833 || (85 < n && n < 90)
834 || (102 < n && n < 105)
835 {
836 return (old_y_min.clone(), old_y_max.clone());
837 }
838
839 }
840
841 let new_y_min = old_y_min.div_rat(&interval).round().mul_rat(&interval);
844 let y_diff = old_y_max.sub_rat(&new_y_min);
845 let new_y_diff = y_diff.div_rat(&interval).div_i32(height as i32).round().mul_rat(&interval).mul_i32(height as i32);
846 let new_y_max = new_y_min.add_rat(&new_y_diff);
847
848 (new_y_min, new_y_max)
849 }
850
851 }
852
853 else {
854 (old_y_min.clone(), old_y_max.clone())
855 }
856}
857
858fn draw_vertial_line(height: usize, color: Option<Color>) -> Lines {
859 let mut result = Lines::new(2, height);
860 result.set_color_all(color);
861
862 for i in 0..height {
863 if i & 1 == 0 {
864 result.set(0, i, ')' as u16);
865 result.set(1, i, ')' as u16);
866 }
867
868 else {
869 result.set(0, i, '(' as u16);
870 result.set(1, i, '(' as u16);
871 }
872 }
873
874 result
875}
876
877use std::fmt;
878
879impl fmt::Display for Graph {
880 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
881 write!(fmt, "{}", self.draw())
882 }
883}