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