blob: f79dbddcec1f8f30c709f29045f5b2b6777503ea [file] [log] [blame]
Jakub Kotur835fea42020-12-21 17:28:14 +01001//! 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 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
63impl Set<Axes> for Properties {
64 /// Select axes to plot against
65 ///
66 /// **Note** By default, the `BottomXLeftY` axes are used
67 fn set(&mut self, axes: Axes) -> &mut Properties {
68 self.axes = Some(axes);
69 self
70 }
71}
72
73impl Set<Color> for Properties {
74 /// Sets the fill color
75 fn set(&mut self, color: Color) -> &mut Properties {
76 self.color = Some(color);
77 self
78 }
79}
80
81impl Set<Label> for Properties {
82 /// Sets the legend label
83 fn set(&mut self, label: Label) -> &mut Properties {
84 self.label = Some(label.0);
85 self
86 }
87}
88
89impl 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]`
97 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
104pub 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
113impl<X, Y1, Y2> traits::Plot<FilledCurve<X, Y1, Y2>> for Figure
114where
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
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}