plotpy/fill_between.rs
1use super::{vector_to_array, AsVector, GraphMaker};
2use num_traits::Num;
3use std::fmt::Write;
4
5/// Fills the area between two curves
6///
7/// # Examples
8///
9/// ```
10/// use plotpy::{Curve, FillBetween, Plot, StrError, linspace};
11///
12/// fn main() -> Result<(), StrError> {
13/// // data and curve
14/// let x = linspace(-1.0, 2.0, 21);
15/// let y: Vec<_> = x.iter().map(|&x| x * x).collect();
16/// let mut curve = Curve::new();
17/// curve.set_line_color("black").draw(&x, &y);
18///
19/// // draw area between curve and x-axis
20/// // (note that we have to use "y1" as variable name for the curve)
21/// let mut fb = FillBetween::new();
22/// fb.set_where("y1>=0.5").set_extra("alpha=0.5").draw(&x, &y, None);
23///
24/// // add curve and fb to plot
25/// let mut plot = Plot::new();
26/// plot.add(&curve).add(&fb);
27///
28/// // save figure
29/// plot.save("/tmp/plotpy/doc_tests/doc_fill_between.svg")?;
30/// Ok(())
31/// }
32/// ```
33///
34/// 
35pub struct FillBetween {
36 where_condition: String,
37 facecolor: String,
38 interpolate: bool,
39 extra: String,
40 buffer: String,
41}
42
43impl FillBetween {
44 /// Allocates a new instance
45 pub fn new() -> Self {
46 FillBetween {
47 where_condition: String::new(),
48 facecolor: String::new(),
49 interpolate: false,
50 extra: String::new(),
51 buffer: String::new(),
52 }
53 }
54
55 /// Draws the filled area between two curves
56 ///
57 /// * `x` - x values
58 /// * `y1` - y values of the first curve
59 /// * `y2` - optional y values of the second curve. If None, fills area between y1 and x-axis
60 ///
61 pub fn draw<'a, T, U>(&mut self, x: &'a T, y1: &'a T, y2: Option<&'a T>)
62 where
63 T: AsVector<'a, U>,
64 U: 'a + std::fmt::Display + Num,
65 {
66 let opt = self.options();
67 vector_to_array(&mut self.buffer, "x", x);
68 vector_to_array(&mut self.buffer, "y1", y1);
69 match y2 {
70 Some(y2) => {
71 vector_to_array(&mut self.buffer, "y2", y2);
72 write!(&mut self.buffer, "plt.fill_between(x,y1,y2{})\n", &opt).unwrap();
73 }
74 None => {
75 write!(&mut self.buffer, "plt.fill_between(x,y1{})\n", &opt).unwrap();
76 }
77 }
78 }
79
80 /// Sets the condition to select the area to be filled.
81 ///
82 /// For example: "y2>=y1" or "y2<=y1"
83 ///
84 /// **WARNING:** `condition` must use `y1` and `y2` as variable names for the two curves.
85 pub fn set_where(&mut self, condition: &str) -> &mut Self {
86 self.where_condition = condition.to_string();
87 self
88 }
89
90 /// Sets the face color of the filled area.
91 pub fn set_facecolor(&mut self, color: &str) -> &mut Self {
92 self.facecolor = color.to_string();
93 self
94 }
95
96 /// Calculates the actual intersection point and extend the filled region up to this point.
97 ///
98 /// From <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html>:
99 ///
100 /// "This option is only relevant if where is used and the two curves are crossing each other. Semantically,
101 /// `where` is often used for y1 > y2 or similar. By default, the nodes of the polygon defining the filled
102 /// region will only be placed at the positions in the x array. Such a polygon cannot describe the above
103 /// semantics close to the intersection. The x-sections containing the intersection are simply clipped."
104 ///
105 /// Default is false.
106 pub fn set_interpolate(&mut self, interpolate: bool) -> &mut Self {
107 self.interpolate = interpolate;
108 self
109 }
110
111 /// Fills the area between two curves
112 ///
113 /// **WARNING:** `where_condition` must use `y1` and `y2` as variable names for the two curves.
114 /// For example:
115 ///
116 /// ```text
117 /// curve.fill_between(x, y1, y2, "y2>=y1", "#ffaabb", true, "");
118 /// curve.fill_between(x, y1, y2, "y2>=y1", "#ffaabb", true, "");
119 /// curve.fill_between(x, y1, y2b, "y2<=y1", "#c1e3ff", true, "");
120 /// ```
121 ///
122 /// **Note:** This method does not use the options of the Curve object.
123 ///
124 /// See more options in <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html>
125 pub fn set_extra(&mut self, extra: &str) -> &mut Self {
126 self.extra = extra.to_string();
127 self
128 }
129
130 /// Returns the options
131 fn options(&self) -> String {
132 let mut opt = String::new();
133 if self.facecolor != "" {
134 write!(&mut opt, ",facecolor='{}'", self.facecolor).unwrap();
135 }
136 if self.where_condition != "" {
137 write!(&mut opt, ",where={}", self.where_condition).unwrap();
138 }
139 if self.interpolate {
140 write!(&mut opt, ",interpolate=True").unwrap();
141 }
142 if self.extra != "" {
143 write!(&mut opt, ",{}", self.extra).unwrap();
144 }
145 opt
146 }
147}
148
149impl GraphMaker for FillBetween {
150 fn get_buffer<'a>(&'a self) -> &'a String {
151 &self.buffer
152 }
153 fn clear_buffer(&mut self) {
154 self.buffer.clear();
155 }
156}
157
158////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
159
160#[cfg(test)]
161mod tests {
162 use super::FillBetween;
163
164 #[test]
165 fn new_works() {
166 let fill_between = FillBetween::new();
167 assert_eq!(fill_between.where_condition, "");
168 assert_eq!(fill_between.facecolor, "");
169 assert_eq!(fill_between.interpolate, false);
170 assert_eq!(fill_between.extra, "");
171 assert_eq!(fill_between.buffer.len(), 0);
172 }
173}