Coverage Report

Created: 2025-11-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/criterion-plot-0.5.0/src/lib.rs
Line
Count
Source
1
//! [Criterion]'s plotting library.
2
//!
3
//! [Criterion]: https://github.com/bheisler/criterion.rs
4
//!
5
//! **WARNING** This library is criterion's implementation detail and there no plans to stabilize
6
//! it. In other words, the API may break at any time without notice.
7
//!
8
//! # Examples
9
//!
10
//! - Simple "curves" (based on [`simple.dem`](http://gnuplot.sourceforge.net/demo/simple.html))
11
//!
12
//! ![Plot](curve.svg)
13
//!
14
//! ```
15
//! # use std::fs;
16
//! # use std::path::Path;
17
//! use itertools_num::linspace;
18
//! use criterion_plot::prelude::*;
19
//!
20
//! # if let Err(_) = criterion_plot::version() {
21
//! #     return;
22
//! # }
23
//! let ref xs = linspace::<f64>(-10., 10., 51).collect::<Vec<_>>();
24
//!
25
//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
26
//! # assert_eq!(Some(String::new()),
27
//! Figure::new()
28
//! #   .set(Font("Helvetica"))
29
//! #   .set(FontSize(12.))
30
//! #   .set(Output(Path::new("target/doc/criterion_plot/curve.svg")))
31
//! #   .set(Size(1280, 720))
32
//!     .configure(Key, |k| {
33
//!         k.set(Boxed::Yes)
34
//!          .set(Position::Inside(Vertical::Top, Horizontal::Left))
35
//!     })
36
//!     .plot(LinesPoints {
37
//!               x: xs,
38
//!               y: xs.iter().map(|x| x.sin()),
39
//!           },
40
//!           |lp| {
41
//!               lp.set(Color::DarkViolet)
42
//!                 .set(Label("sin(x)"))
43
//!                 .set(LineType::Dash)
44
//!                 .set(PointSize(1.5))
45
//!                 .set(PointType::Circle)
46
//!           })
47
//!     .plot(Steps {
48
//!               x: xs,
49
//!               y: xs.iter().map(|x| x.atan()),
50
//!           },
51
//!           |s| {
52
//!               s.set(Color::Rgb(0, 158, 115))
53
//!                .set(Label("atan(x)"))
54
//!                .set(LineWidth(2.))
55
//!           })
56
//!     .plot(Impulses {
57
//!               x: xs,
58
//!               y: xs.iter().map(|x| x.atan().cos()),
59
//!           },
60
//!           |i| {
61
//!               i.set(Color::Rgb(86, 180, 233))
62
//!                .set(Label("cos(atan(x))"))
63
//!           })
64
//!     .draw()  // (rest of the chain has been omitted)
65
//! #   .ok()
66
//! #   .and_then(|gnuplot| {
67
//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
68
//! #   }));
69
//! ```
70
//!
71
//! - error bars (based on
72
//! [Julia plotting tutorial](https://plot.ly/julia/error-bars/#Colored-and-Styled-Error-Bars))
73
//!
74
//! ![Plot](error_bar.svg)
75
//!
76
//! ```
77
//! # use std::fs;
78
//! # use std::path::Path;
79
//! use std::f64::consts::PI;
80
//!
81
//! use itertools_num::linspace;
82
//! use rand::Rng;
83
//! use criterion_plot::prelude::*;
84
//!
85
//! fn sinc(mut x: f64) -> f64 {
86
//!     if x == 0. {
87
//!         1.
88
//!     } else {
89
//!         x *= PI;
90
//!         x.sin() / x
91
//!     }
92
//! }
93
//!
94
//! # if let Err(_) = criterion_plot::version() {
95
//! #     return;
96
//! # }
97
//! let ref xs_ = linspace::<f64>(-4., 4., 101).collect::<Vec<_>>();
98
//!
99
//! // Fake some data
100
//! let ref mut rng = rand::thread_rng();
101
//! let xs = linspace::<f64>(-4., 4., 13).skip(1).take(11);
102
//! let ys = xs.map(|x| sinc(x) + 0.05 * rng.gen::<f64>() - 0.025).collect::<Vec<_>>();
103
//! let y_low = ys.iter().map(|&y| y - 0.025 - 0.075 * rng.gen::<f64>()).collect::<Vec<_>>();
104
//! let y_high = ys.iter().map(|&y| y + 0.025 + 0.075 * rng.gen::<f64>()).collect::<Vec<_>>();
105
//! let xs = linspace::<f64>(-4., 4., 13).skip(1).take(11);
106
//! let xs = xs.map(|x| x + 0.2 * rng.gen::<f64>() - 0.1);
107
//!
108
//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
109
//! # assert_eq!(Some(String::new()),
110
//! Figure::new()
111
//! #   .set(Font("Helvetica"))
112
//! #   .set(FontSize(12.))
113
//! #   .set(Output(Path::new("target/doc/criterion_plot/error_bar.svg")))
114
//! #   .set(Size(1280, 720))
115
//!     .configure(Axis::BottomX, |a| {
116
//!         a.set(TicLabels {
117
//!             labels: &["-π", "0", "π"],
118
//!             positions: &[-PI, 0., PI],
119
//!         })
120
//!     })
121
//!     .configure(Key,
122
//!                |k| k.set(Position::Outside(Vertical::Top, Horizontal::Right)))
123
//!     .plot(Lines {
124
//!               x: xs_,
125
//!               y: xs_.iter().cloned().map(sinc),
126
//!           },
127
//!           |l| {
128
//!               l.set(Color::Rgb(0, 158, 115))
129
//!                .set(Label("sinc(x)"))
130
//!                .set(LineWidth(2.))
131
//!           })
132
//!     .plot(YErrorBars {
133
//!               x: xs,
134
//!               y: &ys,
135
//!               y_low: &y_low,
136
//!               y_high: &y_high,
137
//!           },
138
//!           |eb| {
139
//!               eb.set(Color::DarkViolet)
140
//!                 .set(LineWidth(2.))
141
//!                 .set(PointType::FilledCircle)
142
//!                 .set(Label("measured"))
143
//!           })
144
//!     .draw()  // (rest of the chain has been omitted)
145
//! #   .ok()
146
//! #   .and_then(|gnuplot| {
147
//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
148
//! #   }));
149
//! ```
150
//!
151
//! - Candlesticks (based on
152
//! [`candlesticks.dem`](http://gnuplot.sourceforge.net/demo/candlesticks.html))
153
//!
154
//! ![Plot](candlesticks.svg)
155
//!
156
//! ```
157
//! # use std::fs;
158
//! # use std::path::Path;
159
//! use criterion_plot::prelude::*;
160
//! use rand::Rng;
161
//!
162
//! # if let Err(_) = criterion_plot::version() {
163
//! #     return;
164
//! # }
165
//! let xs = 1..11;
166
//!
167
//! // Fake some data
168
//! let mut rng = rand::thread_rng();
169
//! let bh = xs.clone().map(|_| 5f64 + 2.5 * rng.gen::<f64>()).collect::<Vec<_>>();
170
//! let bm = xs.clone().map(|_| 2.5f64 + 2.5 * rng.gen::<f64>()).collect::<Vec<_>>();
171
//! let wh = bh.iter().map(|&y| y + (10. - y) * rng.gen::<f64>()).collect::<Vec<_>>();
172
//! let wm = bm.iter().map(|&y| y * rng.gen::<f64>()).collect::<Vec<_>>();
173
//! let m = bm.iter().zip(bh.iter()).map(|(&l, &h)| (h - l) * rng.gen::<f64>() + l)
174
//!     .collect::<Vec<_>>();
175
//!
176
//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
177
//! # assert_eq!(Some(String::new()),
178
//! Figure::new()
179
//! #   .set(Font("Helvetica"))
180
//! #   .set(FontSize(12.))
181
//! #   .set(Output(Path::new("target/doc/criterion_plot/candlesticks.svg")))
182
//! #   .set(Size(1280, 720))
183
//!     .set(BoxWidth(0.2))
184
//!     .configure(Axis::BottomX, |a| a.set(Range::Limits(0., 11.)))
185
//!     .plot(Candlesticks {
186
//!               x: xs.clone(),
187
//!               whisker_min: &wm,
188
//!               box_min: &bm,
189
//!               box_high: &bh,
190
//!               whisker_high: &wh,
191
//!           },
192
//!           |cs| {
193
//!               cs.set(Color::Rgb(86, 180, 233))
194
//!                 .set(Label("Quartiles"))
195
//!                 .set(LineWidth(2.))
196
//!           })
197
//!     // trick to plot the median
198
//!     .plot(Candlesticks {
199
//!               x: xs,
200
//!               whisker_min: &m,
201
//!               box_min: &m,
202
//!               box_high: &m,
203
//!               whisker_high: &m,
204
//!           },
205
//!           |cs| {
206
//!               cs.set(Color::Black)
207
//!                 .set(LineWidth(2.))
208
//!           })
209
//!     .draw()  // (rest of the chain has been omitted)
210
//! #   .ok()
211
//! #   .and_then(|gnuplot| {
212
//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
213
//! #   }));
214
//! ```
215
//!
216
//! - Multiaxis (based on [`multiaxis.dem`](http://gnuplot.sourceforge.net/demo/multiaxis.html))
217
//!
218
//! ![Plot](multiaxis.svg)
219
//!
220
//! ```
221
//! # use std::fs;
222
//! # use std::path::Path;
223
//! use std::f64::consts::PI;
224
//!
225
//! use itertools_num::linspace;
226
//! use num_complex::Complex;
227
//! use criterion_plot::prelude::*;
228
//!
229
//! fn tf(x: f64) -> Complex<f64> {
230
//!     Complex::new(0., x) / Complex::new(10., x) / Complex::new(1., x / 10_000.)
231
//! }
232
//!
233
//! # if let Err(_) = criterion_plot::version() {
234
//! #     return;
235
//! # }
236
//! let (start, end): (f64, f64) = (1.1, 90_000.);
237
//! let ref xs = linspace(start.ln(), end.ln(), 101).map(|x| x.exp()).collect::<Vec<_>>();
238
//! let phase = xs.iter().map(|&x| tf(x).arg() * 180. / PI);
239
//! let magnitude = xs.iter().map(|&x| tf(x).norm());
240
//!
241
//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
242
//! # assert_eq!(Some(String::new()),
243
//! Figure::new().
244
//! #   set(Font("Helvetica")).
245
//! #   set(FontSize(12.)).
246
//! #   set(Output(Path::new("target/doc/criterion_plot/multiaxis.svg"))).
247
//! #   set(Size(1280, 720)).
248
//!     set(Title("Frequency response")).
249
//!     configure(Axis::BottomX, |a| a.
250
//!         configure(Grid::Major, |g| g.
251
//!             show()).
252
//!         set(Label("Angular frequency (rad/s)")).
253
//!         set(Range::Limits(start, end)).
254
//!         set(Scale::Logarithmic)).
255
//!     configure(Axis::LeftY, |a| a.
256
//!         set(Label("Gain")).
257
//!         set(Scale::Logarithmic)).
258
//!     configure(Axis::RightY, |a| a.
259
//!         configure(Grid::Major, |g| g.
260
//!             show()).
261
//!         set(Label("Phase shift (°)"))).
262
//!     configure(Key, |k| k.
263
//!         set(Position::Inside(Vertical::Top, Horizontal::Center)).
264
//!         set(Title(" "))).
265
//!     plot(Lines {
266
//!         x: xs,
267
//!         y: magnitude,
268
//!     }, |l| l.
269
//!         set(Color::DarkViolet).
270
//!         set(Label("Magnitude")).
271
//!         set(LineWidth(2.))).
272
//!     plot(Lines {
273
//!         x: xs,
274
//!         y: phase,
275
//!     }, |l| l.
276
//!         set(Axes::BottomXRightY).
277
//!         set(Color::Rgb(0, 158, 115)).
278
//!         set(Label("Phase")).
279
//!         set(LineWidth(2.))).
280
//!     draw().  // (rest of the chain has been omitted)
281
//! #   ok().and_then(|gnuplot| {
282
//! #       gnuplot.wait_with_output().ok().and_then(|p| {
283
//! #           String::from_utf8(p.stderr).ok()
284
//! #       })
285
//! #   }));
286
//! ```
287
//! - Filled curves (based on
288
//! [`transparent.dem`](http://gnuplot.sourceforge.net/demo/transparent.html))
289
//!
290
//! ![Plot](filled_curve.svg)
291
//!
292
//! ```
293
//! # use std::fs;
294
//! # use std::path::Path;
295
//! use std::f64::consts::PI;
296
//! use std::iter;
297
//!
298
//! use itertools_num::linspace;
299
//! use criterion_plot::prelude::*;
300
//!
301
//! # if let Err(_) = criterion_plot::version() {
302
//! #     return;
303
//! # }
304
//! let (start, end) = (-5., 5.);
305
//! let ref xs = linspace(start, end, 101).collect::<Vec<_>>();
306
//! let zeros = iter::repeat(0);
307
//!
308
//! fn gaussian(x: f64, mu: f64, sigma: f64) -> f64 {
309
//!     1. / (((x - mu).powi(2) / 2. / sigma.powi(2)).exp() * sigma * (2. * PI).sqrt())
310
//! }
311
//!
312
//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
313
//! # assert_eq!(Some(String::new()),
314
//! Figure::new()
315
//! #   .set(Font("Helvetica"))
316
//! #   .set(FontSize(12.))
317
//! #   .set(Output(Path::new("target/doc/criterion_plot/filled_curve.svg")))
318
//! #   .set(Size(1280, 720))
319
//!     .set(Title("Transparent filled curve"))
320
//!     .configure(Axis::BottomX, |a| a.set(Range::Limits(start, end)))
321
//!     .configure(Axis::LeftY, |a| a.set(Range::Limits(0., 1.)))
322
//!     .configure(Key, |k| {
323
//!         k.set(Justification::Left)
324
//!          .set(Order::SampleText)
325
//!          .set(Position::Inside(Vertical::Top, Horizontal::Left))
326
//!          .set(Title("Gaussian Distribution"))
327
//!     })
328
//!     .plot(FilledCurve {
329
//!               x: xs,
330
//!               y1: xs.iter().map(|&x| gaussian(x, 0.5, 0.5)),
331
//!               y2: zeros.clone(),
332
//!           },
333
//!           |fc| {
334
//!               fc.set(Color::ForestGreen)
335
//!                 .set(Label("μ = 0.5 σ = 0.5"))
336
//!           })
337
//!     .plot(FilledCurve {
338
//!               x: xs,
339
//!               y1: xs.iter().map(|&x| gaussian(x, 2.0, 1.0)),
340
//!               y2: zeros.clone(),
341
//!           },
342
//!           |fc| {
343
//!               fc.set(Color::Gold)
344
//!                 .set(Label("μ = 2.0 σ = 1.0"))
345
//!                 .set(Opacity(0.5))
346
//!           })
347
//!     .plot(FilledCurve {
348
//!               x: xs,
349
//!               y1: xs.iter().map(|&x| gaussian(x, -1.0, 2.0)),
350
//!               y2: zeros,
351
//!           },
352
//!           |fc| {
353
//!               fc.set(Color::Red)
354
//!                 .set(Label("μ = -1.0 σ = 2.0"))
355
//!                 .set(Opacity(0.5))
356
//!           })
357
//!     .draw()
358
//!     .ok()
359
//!     .and_then(|gnuplot| {
360
//!         gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
361
//!     }));
362
//! ```
363
364
#![deny(missing_docs)]
365
#![deny(warnings)]
366
#![deny(bare_trait_objects)]
367
// This lint has lots of false positives ATM, see
368
// https://github.com/Manishearth/rust-clippy/issues/761
369
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
370
// False positives with images
371
#![cfg_attr(feature = "cargo-clippy", allow(clippy::doc_markdown))]
372
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
373
374
extern crate cast;
375
#[macro_use]
376
extern crate itertools;
377
378
use std::borrow::Cow;
379
use std::fmt;
380
use std::fs::File;
381
use std::io;
382
use std::num::ParseIntError;
383
use std::path::Path;
384
use std::process::{Child, Command};
385
use std::str;
386
387
use crate::data::Matrix;
388
use crate::traits::{Configure, Set};
389
390
mod data;
391
mod display;
392
mod map;
393
394
pub mod axis;
395
pub mod candlestick;
396
pub mod curve;
397
pub mod errorbar;
398
pub mod filledcurve;
399
pub mod grid;
400
pub mod key;
401
pub mod prelude;
402
pub mod proxy;
403
pub mod traits;
404
405
/// Plot container
406
#[derive(Clone)]
407
pub struct Figure {
408
    alpha: Option<f64>,
409
    axes: map::axis::Map<axis::Properties>,
410
    box_width: Option<f64>,
411
    font: Option<Cow<'static, str>>,
412
    font_size: Option<f64>,
413
    key: Option<key::Properties>,
414
    output: Cow<'static, Path>,
415
    plots: Vec<Plot>,
416
    size: Option<(usize, usize)>,
417
    terminal: Terminal,
418
    tics: map::axis::Map<String>,
419
    title: Option<Cow<'static, str>>,
420
}
421
422
impl Figure {
423
    /// Creates an empty figure
424
0
    pub fn new() -> Figure {
425
0
        Figure {
426
0
            alpha: None,
427
0
            axes: map::axis::Map::new(),
428
0
            box_width: None,
429
0
            font: None,
430
0
            font_size: None,
431
0
            key: None,
432
0
            output: Cow::Borrowed(Path::new("output.plot")),
433
0
            plots: Vec::new(),
434
0
            size: None,
435
0
            terminal: Terminal::Svg,
436
0
            tics: map::axis::Map::new(),
437
0
            title: None,
438
0
        }
439
0
    }
440
441
    // Allow clippy::format_push_string even with older versions of rust (<1.62) which
442
    // don't have it defined.
443
    #[allow(clippy::all)]
444
0
    fn script(&self) -> Vec<u8> {
445
0
        let mut s = String::new();
446
447
0
        s.push_str(&format!(
448
0
            "set output '{}'\n",
449
0
            self.output.display().to_string().replace('\'', "''")
450
0
        ));
451
452
0
        if let Some(width) = self.box_width {
453
0
            s.push_str(&format!("set boxwidth {}\n", width))
454
0
        }
455
456
0
        if let Some(ref title) = self.title {
457
0
            s.push_str(&format!("set title '{}'\n", title))
458
0
        }
459
460
0
        for axis in self.axes.iter() {
461
0
            s.push_str(&axis.script());
462
0
        }
463
464
0
        for (_, script) in self.tics.iter() {
465
0
            s.push_str(script);
466
0
        }
467
468
0
        if let Some(ref key) = self.key {
469
0
            s.push_str(&key.script())
470
0
        }
471
472
0
        if let Some(alpha) = self.alpha {
473
0
            s.push_str(&format!("set style fill transparent solid {}\n", alpha))
474
0
        }
475
476
0
        s.push_str(&format!("set terminal {} dashed", self.terminal.display()));
477
478
0
        if let Some((width, height)) = self.size {
479
0
            s.push_str(&format!(" size {}, {}", width, height))
480
0
        }
481
482
0
        if let Some(ref name) = self.font {
483
0
            if let Some(size) = self.font_size {
484
0
                s.push_str(&format!(" font '{},{}'", name, size))
485
            } else {
486
0
                s.push_str(&format!(" font '{}'", name))
487
            }
488
0
        }
489
490
        // TODO This removes the crossbars from the ends of error bars, but should be configurable
491
0
        s.push_str("\nunset bars\n");
492
493
0
        let mut is_first_plot = true;
494
0
        for plot in &self.plots {
495
0
            let data = plot.data();
496
497
0
            if data.bytes().is_empty() {
498
0
                continue;
499
0
            }
500
501
0
            if is_first_plot {
502
0
                s.push_str("plot ");
503
0
                is_first_plot = false;
504
0
            } else {
505
0
                s.push_str(", ");
506
0
            }
507
508
0
            s.push_str(&format!(
509
0
                "'-' binary endian=little record={} format='%float64' using ",
510
0
                data.nrows()
511
0
            ));
512
513
0
            let mut is_first_col = true;
514
0
            for col in 0..data.ncols() {
515
0
                if is_first_col {
516
0
                    is_first_col = false;
517
0
                } else {
518
0
                    s.push(':');
519
0
                }
520
0
                s.push_str(&(col + 1).to_string());
521
            }
522
0
            s.push(' ');
523
524
0
            s.push_str(plot.script());
525
        }
526
527
0
        let mut buffer = s.into_bytes();
528
0
        let mut is_first = true;
529
0
        for plot in &self.plots {
530
0
            if is_first {
531
0
                is_first = false;
532
0
                buffer.push(b'\n');
533
0
            }
534
0
            buffer.extend_from_slice(plot.data().bytes());
535
        }
536
537
0
        buffer
538
0
    }
539
540
    /// Spawns a drawing child process
541
    ///
542
    /// NOTE: stderr, stdin, and stdout are piped
543
0
    pub fn draw(&mut self) -> io::Result<Child> {
544
        use std::process::Stdio;
545
546
0
        let mut gnuplot = Command::new("gnuplot")
547
0
            .stderr(Stdio::piped())
548
0
            .stdin(Stdio::piped())
549
0
            .stdout(Stdio::piped())
550
0
            .spawn()?;
551
0
        self.dump(gnuplot.stdin.as_mut().unwrap())?;
552
0
        Ok(gnuplot)
553
0
    }
554
555
    /// Dumps the script required to produce the figure into `sink`
556
0
    pub fn dump<W>(&mut self, sink: &mut W) -> io::Result<&mut Figure>
557
0
    where
558
0
        W: io::Write,
559
    {
560
0
        sink.write_all(&self.script())?;
561
0
        Ok(self)
562
0
    }
563
564
    /// Saves the script required to produce the figure to `path`
565
0
    pub fn save(&self, path: &Path) -> io::Result<&Figure> {
566
        use std::io::Write;
567
568
0
        File::create(path)?.write_all(&self.script())?;
569
0
        Ok(self)
570
0
    }
571
}
572
573
impl Configure<Axis> for Figure {
574
    type Properties = axis::Properties;
575
576
    /// Configures an axis
577
0
    fn configure<F>(&mut self, axis: Axis, configure: F) -> &mut Figure
578
0
    where
579
0
        F: FnOnce(&mut axis::Properties) -> &mut axis::Properties,
580
    {
581
0
        if self.axes.contains_key(axis) {
582
0
            configure(self.axes.get_mut(axis).unwrap());
583
0
        } else {
584
0
            let mut properties = Default::default();
585
0
            configure(&mut properties);
586
0
            self.axes.insert(axis, properties);
587
0
        }
588
0
        self
589
0
    }
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::regression::regression_figure::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::regression::regression_figure::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::regression::regression_comparison_figure::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::regression::regression_comparison_figure::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::distributions::abs_distribution::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::distributions::abs_distribution::{closure#4}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::distributions::rel_distribution::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::distributions::rel_distribution::{closure#5}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_figure::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_figure::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_comparison_figure::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_comparison_figure::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_comparison_figure::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_comparison_figure::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_comparison_figure::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_small::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_small::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::t_test::t_test::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::t_test::t_test::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::summary::line_comparison::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::summary::line_comparison::{closure#1}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::summary::violin::{closure#4}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<criterion::plot::gnuplot_backend::summary::violin::{closure#5}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Axis>>::configure::<_>
590
}
591
592
impl Configure<Key> for Figure {
593
    type Properties = key::Properties;
594
595
    /// Configures the key (legend)
596
0
    fn configure<F>(&mut self, _: Key, configure: F) -> &mut Figure
597
0
    where
598
0
        F: FnOnce(&mut key::Properties) -> &mut key::Properties,
599
    {
600
0
        if self.key.is_some() {
601
0
            configure(self.key.as_mut().unwrap());
602
0
        } else {
603
0
            let mut key = Default::default();
604
0
            configure(&mut key);
605
0
            self.key = Some(key);
606
0
        }
607
0
        self
608
0
    }
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::regression::regression::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::regression::regression_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::regression::regression_comparison_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::regression::regression_comparison_figure::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::distributions::abs_distribution::{closure#5}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::distributions::rel_distribution::{closure#4}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_comparison_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::iteration_times::iteration_times_comparison_figure::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_comparison_small::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_comparison_figure::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf::{closure#4}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::pdf::pdf_small::{closure#3}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::t_test::t_test::{closure#2}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<criterion::plot::gnuplot_backend::summary::line_comparison::{closure#0}>
Unexecuted instantiation: <criterion_plot::Figure as criterion_plot::traits::Configure<criterion_plot::Key>>::configure::<_>
609
}
610
611
impl Set<BoxWidth> for Figure {
612
    /// Changes the box width of all the box related plots (bars, candlesticks, etc)
613
    ///
614
    /// **Note** The default value is 0
615
    ///
616
    /// # Panics
617
    ///
618
    /// Panics if `width` is a negative value
619
0
    fn set(&mut self, width: BoxWidth) -> &mut Figure {
620
0
        let width = width.0;
621
622
0
        assert!(width >= 0.);
623
624
0
        self.box_width = Some(width);
625
0
        self
626
0
    }
627
}
628
629
impl Set<Font> for Figure {
630
    /// Changes the font
631
0
    fn set(&mut self, font: Font) -> &mut Figure {
632
0
        self.font = Some(font.0);
633
0
        self
634
0
    }
635
}
636
637
impl Set<FontSize> for Figure {
638
    /// Changes the size of the font
639
    ///
640
    /// # Panics
641
    ///
642
    /// Panics if `size` is a non-positive value
643
0
    fn set(&mut self, size: FontSize) -> &mut Figure {
644
0
        let size = size.0;
645
646
0
        assert!(size >= 0.);
647
648
0
        self.font_size = Some(size);
649
0
        self
650
0
    }
651
}
652
653
impl Set<Output> for Figure {
654
    /// Changes the output file
655
    ///
656
    /// **Note** The default output file is `output.plot`
657
0
    fn set(&mut self, output: Output) -> &mut Figure {
658
0
        self.output = output.0;
659
0
        self
660
0
    }
661
}
662
663
impl Set<Size> for Figure {
664
    /// Changes the figure size
665
0
    fn set(&mut self, size: Size) -> &mut Figure {
666
0
        self.size = Some((size.0, size.1));
667
0
        self
668
0
    }
669
}
670
671
impl Set<Terminal> for Figure {
672
    /// Changes the output terminal
673
    ///
674
    /// **Note** By default, the terminal is set to `Svg`
675
0
    fn set(&mut self, terminal: Terminal) -> &mut Figure {
676
0
        self.terminal = terminal;
677
0
        self
678
0
    }
679
}
680
681
impl Set<Title> for Figure {
682
    /// Sets the title
683
0
    fn set(&mut self, title: Title) -> &mut Figure {
684
0
        self.title = Some(title.0);
685
0
        self
686
0
    }
687
}
688
689
impl Default for Figure {
690
0
    fn default() -> Self {
691
0
        Self::new()
692
0
    }
693
}
694
695
/// Box width for box-related plots: bars, candlesticks, etc
696
#[derive(Clone, Copy)]
697
pub struct BoxWidth(pub f64);
698
699
/// A font name
700
pub struct Font(Cow<'static, str>);
701
702
/// The size of a font
703
#[derive(Clone, Copy)]
704
pub struct FontSize(pub f64);
705
706
/// The key or legend
707
#[derive(Clone, Copy)]
708
pub struct Key;
709
710
/// Plot label
711
pub struct Label(Cow<'static, str>);
712
713
/// Width of the lines
714
#[derive(Clone, Copy)]
715
pub struct LineWidth(pub f64);
716
717
/// Fill color opacity
718
#[derive(Clone, Copy)]
719
pub struct Opacity(pub f64);
720
721
/// Output file path
722
pub struct Output(Cow<'static, Path>);
723
724
/// Size of the points
725
#[derive(Clone, Copy)]
726
pub struct PointSize(pub f64);
727
728
/// Axis range
729
#[derive(Clone, Copy)]
730
pub enum Range {
731
    /// Autoscale the axis
732
    Auto,
733
    /// Set the limits of the axis
734
    Limits(f64, f64),
735
}
736
737
/// Figure size
738
#[derive(Clone, Copy)]
739
pub struct Size(pub usize, pub usize);
740
741
/// Labels attached to the tics of an axis
742
pub struct TicLabels<P, L> {
743
    /// Labels to attach to the tics
744
    pub labels: L,
745
    /// Position of the tics on the axis
746
    pub positions: P,
747
}
748
749
/// Figure title
750
pub struct Title(Cow<'static, str>);
751
752
/// A pair of axes that define a coordinate system
753
#[allow(missing_docs)]
754
#[derive(Clone, Copy)]
755
pub enum Axes {
756
    BottomXLeftY,
757
    BottomXRightY,
758
    TopXLeftY,
759
    TopXRightY,
760
}
761
762
/// A coordinate axis
763
#[derive(Clone, Copy)]
764
pub enum Axis {
765
    /// X axis on the bottom side of the figure
766
    BottomX,
767
    /// Y axis on the left side of the figure
768
    LeftY,
769
    /// Y axis on the right side of the figure
770
    RightY,
771
    /// X axis on the top side of the figure
772
    TopX,
773
}
774
775
impl Axis {
776
0
    fn next(self) -> Option<Axis> {
777
        use crate::Axis::*;
778
779
0
        match self {
780
0
            BottomX => Some(LeftY),
781
0
            LeftY => Some(RightY),
782
0
            RightY => Some(TopX),
783
0
            TopX => None,
784
        }
785
0
    }
786
}
787
788
/// Color
789
#[allow(missing_docs)]
790
#[derive(Clone, Copy)]
791
pub enum Color {
792
    Black,
793
    Blue,
794
    Cyan,
795
    DarkViolet,
796
    ForestGreen,
797
    Gold,
798
    Gray,
799
    Green,
800
    Magenta,
801
    Red,
802
    /// Custom RGB color
803
    Rgb(u8, u8, u8),
804
    White,
805
    Yellow,
806
}
807
808
/// Grid line
809
#[derive(Clone, Copy)]
810
pub enum Grid {
811
    /// Major gridlines
812
    Major,
813
    /// Minor gridlines
814
    Minor,
815
}
816
817
impl Grid {
818
0
    fn next(self) -> Option<Grid> {
819
        use crate::Grid::*;
820
821
0
        match self {
822
0
            Major => Some(Minor),
823
0
            Minor => None,
824
        }
825
0
    }
826
}
827
828
/// Line type
829
#[allow(missing_docs)]
830
#[derive(Clone, Copy)]
831
pub enum LineType {
832
    Dash,
833
    Dot,
834
    DotDash,
835
    DotDotDash,
836
    /// Line made of minimally sized dots
837
    SmallDot,
838
    Solid,
839
}
840
841
/// Point type
842
#[allow(missing_docs)]
843
#[derive(Clone, Copy)]
844
pub enum PointType {
845
    Circle,
846
    FilledCircle,
847
    FilledSquare,
848
    FilledTriangle,
849
    Plus,
850
    Square,
851
    Star,
852
    Triangle,
853
    X,
854
}
855
856
/// Axis scale
857
#[allow(missing_docs)]
858
#[derive(Clone, Copy)]
859
pub enum Scale {
860
    Linear,
861
    Logarithmic,
862
}
863
864
/// Axis scale factor
865
#[allow(missing_docs)]
866
#[derive(Clone, Copy)]
867
pub struct ScaleFactor(pub f64);
868
869
/// Output terminal
870
#[allow(missing_docs)]
871
#[derive(Clone, Copy)]
872
pub enum Terminal {
873
    Svg,
874
}
875
876
/// Not public version of `std::default::Default`, used to not leak default constructors into the
877
/// public API
878
trait Default {
879
    /// Creates `Properties` with default configuration
880
    fn default() -> Self;
881
}
882
883
/// Enums that can produce gnuplot code
884
trait Display<S> {
885
    /// Translates the enum in gnuplot code
886
    fn display(&self) -> S;
887
}
888
889
/// Curve variant of Default
890
trait CurveDefault<S> {
891
    /// Creates `curve::Properties` with default configuration
892
    fn default(s: S) -> Self;
893
}
894
895
/// Error bar variant of Default
896
trait ErrorBarDefault<S> {
897
    /// Creates `errorbar::Properties` with default configuration
898
    fn default(s: S) -> Self;
899
}
900
901
/// Structs that can produce gnuplot code
902
trait Script {
903
    /// Translates some configuration struct into gnuplot code
904
    fn script(&self) -> String;
905
}
906
907
#[derive(Clone)]
908
struct Plot {
909
    data: Matrix,
910
    script: String,
911
}
912
913
impl Plot {
914
0
    fn new<S>(data: Matrix, script: &S) -> Plot
915
0
    where
916
0
        S: Script,
917
    {
918
0
        Plot {
919
0
            data,
920
0
            script: script.script(),
921
0
        }
922
0
    }
Unexecuted instantiation: <criterion_plot::Plot>::new::<criterion_plot::filledcurve::Properties>
Unexecuted instantiation: <criterion_plot::Plot>::new::<criterion_plot::curve::Properties>
Unexecuted instantiation: <criterion_plot::Plot>::new::<_>
923
924
0
    fn data(&self) -> &Matrix {
925
0
        &self.data
926
0
    }
927
928
0
    fn script(&self) -> &str {
929
0
        &self.script
930
0
    }
931
}
932
933
/// Possible errors when parsing gnuplot's version string
934
#[derive(Debug)]
935
pub enum VersionError {
936
    /// The `gnuplot` command couldn't be executed
937
    Exec(io::Error),
938
    /// The `gnuplot` command returned an error message
939
    Error(String),
940
    /// The `gnuplot` command returned invalid utf-8
941
    OutputError,
942
    /// The `gnuplot` command returned an unparseable string
943
    ParseError(String),
944
}
945
impl fmt::Display for VersionError {
946
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
947
0
        match self {
948
0
            VersionError::Exec(err) => write!(f, "`gnuplot --version` failed: {}", err),
949
0
            VersionError::Error(msg) => {
950
0
                write!(f, "`gnuplot --version` failed with error message:\n{}", msg)
951
            }
952
0
            VersionError::OutputError => write!(f, "`gnuplot --version` returned invalid utf-8"),
953
0
            VersionError::ParseError(msg) => write!(
954
0
                f,
955
0
                "`gnuplot --version` returned an unparseable version string: {}",
956
                msg
957
            ),
958
        }
959
0
    }
960
}
961
impl ::std::error::Error for VersionError {
962
0
    fn description(&self) -> &str {
963
0
        match self {
964
0
            VersionError::Exec(_) => "Execution Error",
965
0
            VersionError::Error(_) => "Other Error",
966
0
            VersionError::OutputError => "Output Error",
967
0
            VersionError::ParseError(_) => "Parse Error",
968
        }
969
0
    }
970
971
0
    fn cause(&self) -> Option<&dyn ::std::error::Error> {
972
0
        match self {
973
0
            VersionError::Exec(err) => Some(err),
974
0
            _ => None,
975
        }
976
0
    }
977
}
978
979
/// Structure representing a gnuplot version number.
980
pub struct Version {
981
    /// The major version number
982
    pub major: usize,
983
    /// The minor version number
984
    pub minor: usize,
985
    /// The patch level
986
    pub patch: String,
987
}
988
989
/// Returns `gnuplot` version
990
0
pub fn version() -> Result<Version, VersionError> {
991
0
    let command_output = Command::new("gnuplot")
992
0
        .arg("--version")
993
0
        .output()
994
0
        .map_err(VersionError::Exec)?;
995
0
    if !command_output.status.success() {
996
0
        let error =
997
0
            String::from_utf8(command_output.stderr).map_err(|_| VersionError::OutputError)?;
998
0
        return Err(VersionError::Error(error));
999
0
    }
1000
1001
0
    let output = String::from_utf8(command_output.stdout).map_err(|_| VersionError::OutputError)?;
1002
1003
0
    parse_version(&output).map_err(|_| VersionError::ParseError(output.clone()))
1004
0
}
1005
1006
0
fn parse_version(version_str: &str) -> Result<Version, Option<ParseIntError>> {
1007
0
    let mut words = version_str.split_whitespace().skip(1);
1008
0
    let mut version = words.next().ok_or(None)?.split('.');
1009
0
    let major = version.next().ok_or(None)?.parse()?;
1010
0
    let minor = version.next().ok_or(None)?.parse()?;
1011
0
    let patchlevel = words.nth(1).ok_or(None)?.to_owned();
1012
1013
0
    Ok(Version {
1014
0
        major,
1015
0
        minor,
1016
0
        patch: patchlevel,
1017
0
    })
1018
0
}
1019
1020
0
fn scale_factor(map: &map::axis::Map<axis::Properties>, axes: Axes) -> (f64, f64) {
1021
    use crate::Axes::*;
1022
    use crate::Axis::*;
1023
1024
0
    match axes {
1025
0
        BottomXLeftY => (
1026
0
            map.get(BottomX).map_or(1., ScaleFactorTrait::scale_factor),
1027
0
            map.get(LeftY).map_or(1., ScaleFactorTrait::scale_factor),
1028
0
        ),
1029
0
        BottomXRightY => (
1030
0
            map.get(BottomX).map_or(1., ScaleFactorTrait::scale_factor),
1031
0
            map.get(RightY).map_or(1., ScaleFactorTrait::scale_factor),
1032
0
        ),
1033
0
        TopXLeftY => (
1034
0
            map.get(TopX).map_or(1., ScaleFactorTrait::scale_factor),
1035
0
            map.get(LeftY).map_or(1., ScaleFactorTrait::scale_factor),
1036
0
        ),
1037
0
        TopXRightY => (
1038
0
            map.get(TopX).map_or(1., ScaleFactorTrait::scale_factor),
1039
0
            map.get(RightY).map_or(1., ScaleFactorTrait::scale_factor),
1040
0
        ),
1041
    }
1042
0
}
1043
1044
// XXX :-1: to intra-crate privacy rules
1045
/// Private
1046
trait ScaleFactorTrait {
1047
    /// Private
1048
    fn scale_factor(&self) -> f64;
1049
}
1050
1051
#[cfg(test)]
1052
mod test {
1053
    #[test]
1054
    fn version() {
1055
        if let Ok(version) = super::version() {
1056
            assert!(version.major >= 4);
1057
        } else {
1058
            println!("Gnuplot not installed.");
1059
        }
1060
    }
1061
1062
    #[test]
1063
    fn test_parse_version_on_valid_string() {
1064
        let string = "gnuplot 5.0 patchlevel 7";
1065
        let version = super::parse_version(&string).unwrap();
1066
        assert_eq!(5, version.major);
1067
        assert_eq!(0, version.minor);
1068
        assert_eq!("7", &version.patch);
1069
    }
1070
1071
    #[test]
1072
    fn test_parse_gentoo_version() {
1073
        let string = "gnuplot 5.2 patchlevel 5a (Gentoo revision r0)";
1074
        let version = super::parse_version(&string).unwrap();
1075
        assert_eq!(5, version.major);
1076
        assert_eq!(2, version.minor);
1077
        assert_eq!("5a", &version.patch);
1078
    }
1079
1080
    #[test]
1081
    fn test_parse_version_returns_error_on_invalid_strings() {
1082
        let strings = [
1083
            "",
1084
            "foobar",
1085
            "gnuplot 50 patchlevel 7",
1086
            "gnuplot 5.0 patchlevel",
1087
            "gnuplot foo.bar patchlevel 7",
1088
        ];
1089
        for string in &strings {
1090
            assert!(super::parse_version(string).is_err());
1091
        }
1092
    }
1093
}