Jakub Kotur | 835fea4 | 2020-12-21 17:28:14 +0100 | [diff] [blame] | 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 { |
| 19 | 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 { |
| 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 | |
| 63 | impl 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 | |
| 73 | impl 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 | |
| 81 | impl 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 | |
| 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]` |
| 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 |
| 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 | |
| 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 | } |