1//! Filled curve plots
2
3use std::borrow::Cow;
4use std::iter::IntoIterator;
5
6use crate::data::Matrix;
7use crate::traits::{self, Data, Set};
8use crate::{Axes, Color, Default, Display, Figure, Label, Opacity, Plot, Script};
9
10/// Properties common to filled curve plots
11pub struct Properties {
12 axes: Option<Axes>,
13 color: Option<Color>,
14 label: Option<Cow<'static, str>>,
15 opacity: Option<f64>,
16}
17
18impl Default for Properties {
19 fn default() -> Properties {
20 Properties {
21 axes: None,
22 color: None,
23 label: None,
24 opacity: None,
25 }
26 }
27}
28
29impl Script for Properties {
30 // Allow clippy::format_push_string even with older versions of rust (<1.62) which
31 // don't have it defined.
32 #[allow(clippy::all)]
33 fn script(&self) -> String {
34 let mut script = if let Some(axes) = self.axes {
35 format!("axes {} ", axes.display())
36 } else {
37 String::new()
38 };
39 script.push_str("with filledcurves ");
40
41 script.push_str("fillstyle ");
42
43 if let Some(opacity) = self.opacity {
44 script.push_str(&format!("solid {} ", opacity))
45 }
46
47 // TODO border shoulde be configurable
48 script.push_str("noborder ");
49
50 if let Some(color) = self.color {
51 script.push_str(&format!("lc rgb '{}' ", color.display()));
52 }
53
54 if let Some(ref label) = self.label {
55 script.push_str("title '");
56 script.push_str(label);
57 script.push('\'')
58 } else {
59 script.push_str("notitle")
60 }
61
62 script
63 }
64}
65
66impl Set<Axes> for Properties {
67 /// Select axes to plot against
68 ///
69 /// **Note** By default, the `BottomXLeftY` axes are used
70 fn set(&mut self, axes: Axes) -> &mut Properties {
71 self.axes = Some(axes);
72 self
73 }
74}
75
76impl Set<Color> for Properties {
77 /// Sets the fill color
78 fn set(&mut self, color: Color) -> &mut Properties {
79 self.color = Some(color);
80 self
81 }
82}
83
84impl Set<Label> for Properties {
85 /// Sets the legend label
86 fn set(&mut self, label: Label) -> &mut Properties {
87 self.label = Some(label.0);
88 self
89 }
90}
91
92impl Set<Opacity> for Properties {
93 /// Changes the opacity of the fill color
94 ///
95 /// **Note** By default, the fill color is totally opaque (`opacity = 1.0`)
96 ///
97 /// # Panics
98 ///
99 /// Panics if `opacity` is outside the range `[0, 1]`
100 fn set(&mut self, opacity: Opacity) -> &mut Properties {
101 self.opacity = Some(opacity.0);
102 self
103 }
104}
105
106/// Fills the area between two curves
107pub struct FilledCurve<X, Y1, Y2> {
108 /// X coordinate of the data points of both curves
109 pub x: X,
110 /// Y coordinate of the data points of the first curve
111 pub y1: Y1,
112 /// Y coordinate of the data points of the second curve
113 pub y2: Y2,
114}
115
116impl<X, Y1, Y2> traits::Plot<FilledCurve<X, Y1, Y2>> for Figure
117where
118 X: IntoIterator,
119 X::Item: Data,
120 Y1: IntoIterator,
121 Y1::Item: Data,
122 Y2: IntoIterator,
123 Y2::Item: Data,
124{
125 type Properties = Properties;
126
127 fn plot<F>(&mut self, fc: FilledCurve<X, Y1, Y2>, configure: F) -> &mut Figure
128 where
129 F: FnOnce(&mut Properties) -> &mut Properties,
130 {
131 let FilledCurve { x, y1, y2 } = fc;
132
133 let mut props = Default::default();
134 configure(&mut props);
135
136 let (x_factor, y_factor) =
137 crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY));
138
139 let data = Matrix::new(izip!(x, y1, y2), (x_factor, y_factor, y_factor));
140 self.plots.push(Plot::new(data, &props));
141 self
142 }
143}
144