1 use crate::element::Polygon; 2 use crate::style::{colors::BLUE, Color, ShapeStyle}; 3 use std::marker::PhantomData; 4 5 /// Any type that describe a surface orientation 6 pub trait Direction<X, Y, Z> { 7 /// The type for the first input argument 8 type Input1Type; 9 /// The type for the second input argument 10 type Input2Type; 11 /// The output of the surface function 12 type OutputType; 13 14 /// The function that maps a point on surface into the coordinate system make_coord( free_vars: (Self::Input1Type, Self::Input2Type), result: Self::OutputType, ) -> (X, Y, Z)15 fn make_coord( 16 free_vars: (Self::Input1Type, Self::Input2Type), 17 result: Self::OutputType, 18 ) -> (X, Y, Z); 19 } 20 21 macro_rules! define_panel_descriptor { 22 ($name: ident, $var1: ident, $var2: ident, $out: ident, ($first: ident, $second:ident) -> $result: ident = $output: expr) => { 23 #[allow(clippy::upper_case_acronyms)] 24 pub struct $name; 25 impl<X, Y, Z> Direction<X, Y, Z> for $name { 26 type Input1Type = $var1; 27 type Input2Type = $var2; 28 type OutputType = $out; 29 fn make_coord( 30 ($first, $second): (Self::Input1Type, Self::Input2Type), 31 $result: Self::OutputType, 32 ) -> (X, Y, Z) { 33 $output 34 } 35 } 36 }; 37 } 38 39 define_panel_descriptor!(XOY, X, Y, Z, (x, y) -> z = (x,y,z)); 40 define_panel_descriptor!(XOZ, X, Z, Y, (x, z) -> y = (x,y,z)); 41 define_panel_descriptor!(YOZ, Y, Z, X, (y, z) -> x = (x,y,z)); 42 43 enum StyleConfig<'a, T> { 44 Fixed(ShapeStyle), 45 Function(&'a dyn Fn(&T) -> ShapeStyle), 46 } 47 48 impl<T> StyleConfig<'_, T> { get_style(&self, v: &T) -> ShapeStyle49 fn get_style(&self, v: &T) -> ShapeStyle { 50 match self { 51 StyleConfig::Fixed(s) => *s, 52 StyleConfig::Function(f) => f(v), 53 } 54 } 55 } 56 57 /** 58 Represents functions of two variables. 59 60 # Examples 61 62 ``` 63 use plotters::prelude::*; 64 let drawing_area = SVGBackend::new("surface_series_xoz.svg", (640, 480)).into_drawing_area(); 65 drawing_area.fill(&WHITE).unwrap(); 66 let mut chart_context = ChartBuilder::on(&drawing_area) 67 .margin(10) 68 .build_cartesian_3d(-3.0..3.0f64, -3.0..3.0f64, -3.0..3.0f64) 69 .unwrap(); 70 chart_context.configure_axes().draw().unwrap(); 71 let axis_title_style = ("sans-serif", 20, &BLACK).into_text_style(&drawing_area); 72 chart_context.draw_series([("x", (3., -3., -3.)), ("y", (-3., 3., -3.)), ("z", (-3., -3., 3.))] 73 .map(|(label, position)| Text::new(label, position, &axis_title_style))).unwrap(); 74 chart_context.draw_series(SurfaceSeries::xoz( 75 (-30..30).map(|v| v as f64 / 10.0), 76 (-30..30).map(|v| v as f64 / 10.0), 77 |x:f64,z:f64|(0.7 * (x * x + z * z)).cos()).style(&BLUE.mix(0.5)) 78 ).unwrap(); 79 ``` 80 81 The code above with [`SurfaceSeries::xoy()`] produces a surface that depends on x and y and 82 points in the z direction: 83 84  85 86 The code above with [`SurfaceSeries::xoz()`] produces a surface that depends on x and z and 87 points in the y direction: 88 89  90 91 The code above with [`SurfaceSeries::yoz()`] produces a surface that depends on y and z and 92 points in the x direction: 93 94  95 */ 96 pub struct SurfaceSeries<'a, X, Y, Z, D, SurfaceFunc> 97 where 98 D: Direction<X, Y, Z>, 99 SurfaceFunc: Fn(D::Input1Type, D::Input2Type) -> D::OutputType, 100 { 101 free_var_1: Vec<D::Input1Type>, 102 free_var_2: Vec<D::Input2Type>, 103 surface_f: SurfaceFunc, 104 style: StyleConfig<'a, D::OutputType>, 105 vidx_1: usize, 106 vidx_2: usize, 107 _phantom: PhantomData<(X, Y, Z, D)>, 108 } 109 110 impl<'a, X, Y, Z, D, SurfaceFunc> SurfaceSeries<'a, X, Y, Z, D, SurfaceFunc> 111 where 112 D: Direction<X, Y, Z>, 113 SurfaceFunc: Fn(D::Input1Type, D::Input2Type) -> D::OutputType, 114 { 115 /// Create a new surface series, the surface orientation is determined by D new<IterA: Iterator<Item = D::Input1Type>, IterB: Iterator<Item = D::Input2Type>>( first_iter: IterA, second_iter: IterB, func: SurfaceFunc, ) -> Self116 pub fn new<IterA: Iterator<Item = D::Input1Type>, IterB: Iterator<Item = D::Input2Type>>( 117 first_iter: IterA, 118 second_iter: IterB, 119 func: SurfaceFunc, 120 ) -> Self { 121 Self { 122 free_var_1: first_iter.collect(), 123 free_var_2: second_iter.collect(), 124 surface_f: func, 125 style: StyleConfig::Fixed(BLUE.mix(0.4).filled()), 126 vidx_1: 0, 127 vidx_2: 0, 128 _phantom: PhantomData, 129 } 130 } 131 132 /** 133 Sets the style as a function of the value of the dependent coordinate of the surface. 134 135 # Examples 136 137 ``` 138 use plotters::prelude::*; 139 let drawing_area = SVGBackend::new("surface_series_style_func.svg", (640, 480)).into_drawing_area(); 140 drawing_area.fill(&WHITE).unwrap(); 141 let mut chart_context = ChartBuilder::on(&drawing_area) 142 .margin(10) 143 .build_cartesian_3d(-3.0..3.0f64, -3.0..3.0f64, -3.0..3.0f64) 144 .unwrap(); 145 chart_context.configure_axes().draw().unwrap(); 146 let axis_title_style = ("sans-serif", 20, &BLACK).into_text_style(&drawing_area); 147 chart_context.draw_series([("x", (3., -3., -3.)), ("y", (-3., 3., -3.)), ("z", (-3., -3., 3.))] 148 .map(|(label, position)| Text::new(label, position, &axis_title_style))).unwrap(); 149 chart_context.draw_series(SurfaceSeries::xoz( 150 (-30..30).map(|v| v as f64 / 10.0), 151 (-30..30).map(|v| v as f64 / 10.0), 152 |x:f64,z:f64|(0.4 * (x * x + z * z)).cos()).style_func( 153 &|y| HSLColor(0.6666, y + 0.5, 0.5).mix(0.8).filled() 154 ) 155 ).unwrap(); 156 ``` 157 158 The resulting style varies from gray to blue according to the value of y: 159 160  161 */ style_func<F: Fn(&D::OutputType) -> ShapeStyle>(mut self, f: &'a F) -> Self162 pub fn style_func<F: Fn(&D::OutputType) -> ShapeStyle>(mut self, f: &'a F) -> Self { 163 self.style = StyleConfig::Function(f); 164 self 165 } 166 167 /// Sets the style of the plot. See [`SurfaceSeries`] for more information and examples. style<S: Into<ShapeStyle>>(mut self, s: S) -> Self168 pub fn style<S: Into<ShapeStyle>>(mut self, s: S) -> Self { 169 self.style = StyleConfig::Fixed(s.into()); 170 self 171 } 172 } 173 174 macro_rules! impl_constructor { 175 ($dir: ty, $name: ident) => { 176 impl<'a, X, Y, Z, SurfaceFunc> SurfaceSeries<'a, X, Y, Z, $dir, SurfaceFunc> 177 where 178 SurfaceFunc: Fn( 179 <$dir as Direction<X, Y, Z>>::Input1Type, 180 <$dir as Direction<X, Y, Z>>::Input2Type, 181 ) -> <$dir as Direction<X, Y, Z>>::OutputType, 182 { 183 /// Implements the constructor. See [`SurfaceSeries`] for more information and examples. 184 pub fn $name<IterA, IterB>(a: IterA, b: IterB, f: SurfaceFunc) -> Self 185 where 186 IterA: Iterator<Item = <$dir as Direction<X, Y, Z>>::Input1Type>, 187 IterB: Iterator<Item = <$dir as Direction<X, Y, Z>>::Input2Type>, 188 { 189 Self::new(a, b, f) 190 } 191 } 192 }; 193 } 194 195 impl_constructor!(XOY, xoy); 196 impl_constructor!(XOZ, xoz); 197 impl_constructor!(YOZ, yoz); 198 impl<'a, X, Y, Z, D, SurfaceFunc> Iterator for SurfaceSeries<'a, X, Y, Z, D, SurfaceFunc> 199 where 200 D: Direction<X, Y, Z>, 201 D::Input1Type: Clone, 202 D::Input2Type: Clone, 203 SurfaceFunc: Fn(D::Input1Type, D::Input2Type) -> D::OutputType, 204 { 205 type Item = Polygon<(X, Y, Z)>; next(&mut self) -> Option<Self::Item>206 fn next(&mut self) -> Option<Self::Item> { 207 let (b0, b1) = if let (Some(b0), Some(b1)) = ( 208 self.free_var_2.get(self.vidx_2), 209 self.free_var_2.get(self.vidx_2 + 1), 210 ) { 211 self.vidx_2 += 1; 212 (b0, b1) 213 } else { 214 self.vidx_1 += 1; 215 self.vidx_2 = 1; 216 if let (Some(b0), Some(b1)) = (self.free_var_2.get(0), self.free_var_2.get(1)) { 217 (b0, b1) 218 } else { 219 return None; 220 } 221 }; 222 223 match ( 224 self.free_var_1.get(self.vidx_1), 225 self.free_var_1.get(self.vidx_1 + 1), 226 ) { 227 (Some(a0), Some(a1)) => { 228 let value = (self.surface_f)(a0.clone(), b0.clone()); 229 let style = self.style.get_style(&value); 230 let vert = vec![ 231 D::make_coord((a0.clone(), b0.clone()), value), 232 D::make_coord( 233 (a0.clone(), b1.clone()), 234 (self.surface_f)(a0.clone(), b1.clone()), 235 ), 236 D::make_coord( 237 (a1.clone(), b1.clone()), 238 (self.surface_f)(a1.clone(), b1.clone()), 239 ), 240 D::make_coord( 241 (a1.clone(), b0.clone()), 242 (self.surface_f)(a1.clone(), b0.clone()), 243 ), 244 ]; 245 Some(Polygon::new(vert, style)) 246 } 247 _ => None, 248 } 249 } 250 } 251