1use super::*;
2
3use std::path::Path;
4
5pub(crate) fn iteration_times_figure(
6 title: Option<&str>,
7 path: &Path,
8 formatter: &dyn ValueFormatter,
9 measurements: &MeasurementData<'_>,
10 size: Option<(u32, u32)>,
11) {
12 let data = &measurements.avg_times;
13 let max_avg_time = data.max();
14 let mut scaled_y: Vec<_> = data.iter().map(|(f, _)| f).collect();
15 let unit = formatter.scale_values(max_avg_time, &mut scaled_y);
16 let scaled_y = Sample::new(&scaled_y);
17
18 let size = size.unwrap_or(SIZE);
19 let root_area = SVGBackend::new(path, size).into_drawing_area();
20
21 let mut cb = ChartBuilder::on(&root_area);
22 if let Some(title) = title {
23 cb.caption(title, (DEFAULT_FONT, 20));
24 }
25
26 let x_range = (1.0)..((data.len() + 1) as f64);
27 let y_range = plotters::data::fitting_range(scaled_y.iter());
28
29 let mut chart = cb
30 .margin((5).percent())
31 .set_label_area_size(LabelAreaPosition::Left, (5).percent_width().min(60))
32 .set_label_area_size(LabelAreaPosition::Bottom, (5).percent_height().min(40))
33 .build_cartesian_2d(x_range, y_range)
34 .unwrap();
35
36 chart
37 .configure_mesh()
38 .y_desc(format!("Average Iteration Time ({})", unit))
39 .x_label_formatter(&|x| pretty_print_float(*x, true))
40 .light_line_style(TRANSPARENT)
41 .draw()
42 .unwrap();
43
44 chart
45 .draw_series(
46 (1..=data.len())
47 .zip(scaled_y.iter())
48 .map(|(x, y)| Circle::new((x as f64, *y), POINT_SIZE, DARK_BLUE.filled())),
49 )
50 .unwrap()
51 .label("Sample")
52 .legend(|(x, y)| Circle::new((x + 10, y), POINT_SIZE, DARK_BLUE.filled()));
53
54 if title.is_some() {
55 chart
56 .configure_series_labels()
57 .position(SeriesLabelPosition::UpperLeft)
58 .draw()
59 .unwrap();
60 }
61}
62
63pub(crate) fn iteration_times_comparison_figure(
64 title: Option<&str>,
65 path: &Path,
66 formatter: &dyn ValueFormatter,
67 measurements: &MeasurementData<'_>,
68 comparison: &ComparisonData,
69 size: Option<(u32, u32)>,
70) {
71 let current_data = &measurements.avg_times;
72 let base_data = &comparison.base_avg_times;
73
74 let mut all_data: Vec<f64> = current_data.iter().map(|(f, _)| f).collect();
75 all_data.extend_from_slice(base_data);
76
77 let typical_value = Sample::new(&all_data).max();
78 let unit = formatter.scale_values(typical_value, &mut all_data);
79
80 let (scaled_current_y, scaled_base_y) = all_data.split_at(current_data.len());
81 let scaled_current_y = Sample::new(scaled_current_y);
82 let scaled_base_y = Sample::new(scaled_base_y);
83
84 let size = size.unwrap_or(SIZE);
85 let root_area = SVGBackend::new(path, size).into_drawing_area();
86
87 let mut cb = ChartBuilder::on(&root_area);
88 if let Some(title) = title {
89 cb.caption(title, (DEFAULT_FONT, 20));
90 }
91
92 let max_samples = current_data.len().max(base_data.len()) as f64;
93
94 let y_range = plotters::data::fitting_range(all_data.iter());
95
96 let mut chart = cb
97 .margin((5).percent())
98 .set_label_area_size(LabelAreaPosition::Left, (5).percent_width().min(60))
99 .set_label_area_size(LabelAreaPosition::Bottom, (5).percent_height().min(40))
100 .build_cartesian_2d(0.0..max_samples, y_range)
101 .unwrap();
102
103 chart
104 .configure_mesh()
105 .y_desc(format!("Average Iteration Time ({})", unit))
106 .x_label_formatter(&|x| pretty_print_float(*x, true))
107 .light_line_style(TRANSPARENT)
108 .draw()
109 .unwrap();
110
111 chart
112 .draw_series(
113 (1..=current_data.len())
114 .zip(scaled_current_y.iter())
115 .map(|(x, y)| Circle::new((x as f64, *y), POINT_SIZE, DARK_BLUE.filled())),
116 )
117 .unwrap()
118 .label("Current")
119 .legend(|(x, y)| Circle::new((x + 10, y), POINT_SIZE, DARK_BLUE.filled()));
120
121 chart
122 .draw_series(
123 (1..=base_data.len())
124 .zip(scaled_base_y.iter())
125 .map(|(x, y)| Circle::new((x as f64, *y), POINT_SIZE, DARK_RED.filled())),
126 )
127 .unwrap()
128 .label("Base")
129 .legend(|(x, y)| Circle::new((x + 10, y), POINT_SIZE, DARK_RED.filled()));
130
131 if title.is_some() {
132 chart
133 .configure_series_labels()
134 .position(SeriesLabelPosition::UpperLeft)
135 .draw()
136 .unwrap();
137 }
138}
139