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 { 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)] script(&self) -> String33 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 66 impl Set<Axes> for Properties { 67 /// Select axes to plot against 68 /// 69 /// **Note** By default, the `BottomXLeftY` axes are used set(&mut self, axes: Axes) -> &mut Properties70 fn set(&mut self, axes: Axes) -> &mut Properties { 71 self.axes = Some(axes); 72 self 73 } 74 } 75 76 impl Set<Color> for Properties { 77 /// Sets the fill color set(&mut self, color: Color) -> &mut Properties78 fn set(&mut self, color: Color) -> &mut Properties { 79 self.color = Some(color); 80 self 81 } 82 } 83 84 impl Set<Label> for Properties { 85 /// Sets the legend label set(&mut self, label: Label) -> &mut Properties86 fn set(&mut self, label: Label) -> &mut Properties { 87 self.label = Some(label.0); 88 self 89 } 90 } 91 92 impl 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]` set(&mut self, opacity: Opacity) -> &mut Properties100 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 107 pub 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 116 impl<X, Y1, Y2> traits::Plot<FilledCurve<X, Y1, Y2>> for Figure 117 where 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 plot<F>(&mut self, fc: FilledCurve<X, Y1, Y2>, configure: F) -> &mut Figure where F: FnOnce(&mut Properties) -> &mut Properties,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