1 use std::marker::PhantomData; 2 3 use super::ChartContext; 4 use crate::coord::cartesian::Cartesian3d; 5 use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter}; 6 use crate::style::colors::{BLACK, TRANSPARENT}; 7 use crate::style::Color; 8 use crate::style::{AsRelative, ShapeStyle, SizeDesc, TextStyle}; 9 10 use super::Coord3D; 11 12 use crate::drawing::DrawingAreaErrorKind; 13 14 use plotters_backend::DrawingBackend; 15 16 /** 17 Implements 3D plot axes configurations. 18 19 The best way to use this struct is by way of the [`configure_axes()`] function. 20 See [`ChartContext::configure_axes()`] for more information and examples. 21 */ 22 pub struct Axes3dStyle<'a, 'b, X: Ranged, Y: Ranged, Z: Ranged, DB: DrawingBackend> { 23 pub(super) parent_size: (u32, u32), 24 pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>>, 25 pub(super) tick_size: i32, 26 pub(super) light_lines_limit: [usize; 3], 27 pub(super) n_labels: [usize; 3], 28 pub(super) bold_line_style: ShapeStyle, 29 pub(super) light_line_style: ShapeStyle, 30 pub(super) axis_panel_style: ShapeStyle, 31 pub(super) axis_style: ShapeStyle, 32 pub(super) label_style: TextStyle<'b>, 33 pub(super) format_x: &'b dyn Fn(&X::ValueType) -> String, 34 pub(super) format_y: &'b dyn Fn(&Y::ValueType) -> String, 35 pub(super) format_z: &'b dyn Fn(&Z::ValueType) -> String, 36 _phantom: PhantomData<&'a (X, Y, Z)>, 37 } 38 39 impl<'a, 'b, X, Y, Z, XT, YT, ZT, DB> Axes3dStyle<'a, 'b, X, Y, Z, DB> 40 where 41 X: Ranged<ValueType = XT> + ValueFormatter<XT>, 42 Y: Ranged<ValueType = YT> + ValueFormatter<YT>, 43 Z: Ranged<ValueType = ZT> + ValueFormatter<ZT>, 44 DB: DrawingBackend, 45 { 46 /** 47 Set the size of the tick marks. 48 49 - `value` Desired tick mark size, in pixels. 50 51 See [`ChartContext::configure_axes()`] for more information and examples. 52 */ tick_size<Size: SizeDesc>(&mut self, size: Size) -> &mut Self53 pub fn tick_size<Size: SizeDesc>(&mut self, size: Size) -> &mut Self { 54 let actual_size = size.in_pixels(&self.parent_size); 55 self.tick_size = actual_size; 56 self 57 } 58 59 /** 60 Set the maximum number of divisions for the minor grid in the X axis. 61 62 - `value`: Maximum desired divisions between two consecutive X labels. 63 64 See [`ChartContext::configure_axes()`] for more information and examples. 65 */ x_max_light_lines(&mut self, value: usize) -> &mut Self66 pub fn x_max_light_lines(&mut self, value: usize) -> &mut Self { 67 self.light_lines_limit[0] = value; 68 self 69 } 70 71 /** 72 Set the maximum number of divisions for the minor grid in the Y axis. 73 74 - `value`: Maximum desired divisions between two consecutive Y labels. 75 76 See [`ChartContext::configure_axes()`] for more information and examples. 77 */ y_max_light_lines(&mut self, value: usize) -> &mut Self78 pub fn y_max_light_lines(&mut self, value: usize) -> &mut Self { 79 self.light_lines_limit[1] = value; 80 self 81 } 82 83 /** 84 Set the maximum number of divisions for the minor grid in the Z axis. 85 86 - `value`: Maximum desired divisions between two consecutive Z labels. 87 88 See [`ChartContext::configure_axes()`] for more information and examples. 89 */ z_max_light_lines(&mut self, value: usize) -> &mut Self90 pub fn z_max_light_lines(&mut self, value: usize) -> &mut Self { 91 self.light_lines_limit[2] = value; 92 self 93 } 94 95 /** 96 Set the maximum number of divisions for the minor grid. 97 98 - `value`: Maximum desired divisions between two consecutive labels in X, Y, and Z. 99 100 See [`ChartContext::configure_axes()`] for more information and examples. 101 */ max_light_lines(&mut self, value: usize) -> &mut Self102 pub fn max_light_lines(&mut self, value: usize) -> &mut Self { 103 self.light_lines_limit[0] = value; 104 self.light_lines_limit[1] = value; 105 self.light_lines_limit[2] = value; 106 self 107 } 108 109 /** 110 Set the number of labels on the X axes. 111 112 See [`ChartContext::configure_axes()`] for more information and examples. 113 */ x_labels(&mut self, n: usize) -> &mut Self114 pub fn x_labels(&mut self, n: usize) -> &mut Self { 115 self.n_labels[0] = n; 116 self 117 } 118 119 /** 120 Set the number of labels on the Y axes. 121 122 See [`ChartContext::configure_axes()`] for more information and examples. 123 */ y_labels(&mut self, n: usize) -> &mut Self124 pub fn y_labels(&mut self, n: usize) -> &mut Self { 125 self.n_labels[1] = n; 126 self 127 } 128 129 /** 130 Set the number of labels on the Z axes. 131 132 See [`ChartContext::configure_axes()`] for more information and examples. 133 */ z_labels(&mut self, n: usize) -> &mut Self134 pub fn z_labels(&mut self, n: usize) -> &mut Self { 135 self.n_labels[2] = n; 136 self 137 } 138 139 /** 140 Sets the style of the panels in the background. 141 142 See [`ChartContext::configure_axes()`] for more information and examples. 143 */ axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self144 pub fn axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self { 145 self.axis_panel_style = style.into(); 146 self 147 } 148 149 /** 150 Sets the style of the major grid lines. 151 152 See [`ChartContext::configure_axes()`] for more information and examples. 153 */ bold_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self154 pub fn bold_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self { 155 self.bold_line_style = style.into(); 156 self 157 } 158 159 /** 160 Sets the style of the minor grid lines. 161 162 See [`ChartContext::configure_axes()`] for more information and examples. 163 */ light_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self164 pub fn light_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self { 165 self.light_line_style = style.into(); 166 self 167 } 168 169 /** 170 Sets the text style of the axis labels. 171 172 See [`ChartContext::configure_axes()`] for more information and examples. 173 */ label_style<S: Into<TextStyle<'b>>>(&mut self, style: S) -> &mut Self174 pub fn label_style<S: Into<TextStyle<'b>>>(&mut self, style: S) -> &mut Self { 175 self.label_style = style.into(); 176 self 177 } 178 179 /** 180 Specifies the string format of the X axis labels. 181 182 See [`ChartContext::configure_axes()`] for more information and examples. 183 */ x_formatter<F: Fn(&X::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self184 pub fn x_formatter<F: Fn(&X::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self { 185 self.format_x = f; 186 self 187 } 188 189 /** 190 Specifies the string format of the Y axis labels. 191 192 See [`ChartContext::configure_axes()`] for more information and examples. 193 */ y_formatter<F: Fn(&Y::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self194 pub fn y_formatter<F: Fn(&Y::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self { 195 self.format_y = f; 196 self 197 } 198 199 /** 200 Specifies the string format of the Z axis labels. 201 202 See [`ChartContext::configure_axes()`] for more information and examples. 203 */ z_formatter<F: Fn(&Z::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self204 pub fn z_formatter<F: Fn(&Z::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self { 205 self.format_z = f; 206 self 207 } 208 209 /** 210 Constructs a new configuration object and defines the defaults. 211 212 This is used internally by Plotters and should probably not be included in user code. 213 See [`ChartContext::configure_axes()`] for more information and examples. 214 */ new(chart: &'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>) -> Self215 pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>) -> Self { 216 let parent_size = chart.drawing_area.dim_in_pixel(); 217 let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area()); 218 let tick_size = base_tick_size; 219 Self { 220 parent_size, 221 tick_size, 222 light_lines_limit: [10, 10, 10], 223 n_labels: [10, 10, 10], 224 bold_line_style: Into::<ShapeStyle>::into(&BLACK.mix(0.2)), 225 light_line_style: Into::<ShapeStyle>::into(&TRANSPARENT), 226 axis_panel_style: Into::<ShapeStyle>::into(&BLACK.mix(0.1)), 227 axis_style: Into::<ShapeStyle>::into(&BLACK.mix(0.8)), 228 label_style: ("sans-serif", (12).percent().max(12).in_pixels(&parent_size)).into(), 229 format_x: &X::format, 230 format_y: &Y::format, 231 format_z: &Z::format, 232 _phantom: PhantomData, 233 target: Some(chart), 234 } 235 } 236 draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> where XT: Clone, YT: Clone, ZT: Clone,237 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> 238 where 239 XT: Clone, 240 YT: Clone, 241 ZT: Clone, 242 { 243 let chart = self.target.take().unwrap(); 244 let kps_bold = chart.get_key_points( 245 BoldPoints(self.n_labels[0]), 246 BoldPoints(self.n_labels[1]), 247 BoldPoints(self.n_labels[2]), 248 ); 249 let kps_light = chart.get_key_points( 250 LightPoints::new( 251 self.n_labels[0], 252 self.n_labels[0] * self.light_lines_limit[0], 253 ), 254 LightPoints::new( 255 self.n_labels[1], 256 self.n_labels[1] * self.light_lines_limit[1], 257 ), 258 LightPoints::new( 259 self.n_labels[2], 260 self.n_labels[2] * self.light_lines_limit[2], 261 ), 262 ); 263 264 let panels = chart.draw_axis_panels( 265 &kps_bold, 266 &kps_light, 267 self.axis_panel_style, 268 self.bold_line_style, 269 self.light_line_style, 270 )?; 271 272 for i in 0..3 { 273 let axis = chart.draw_axis(i, &panels, self.axis_style)?; 274 let labels: Vec<_> = match i { 275 0 => kps_bold 276 .x_points 277 .iter() 278 .map(|x| { 279 let x_text = (self.format_x)(x); 280 let mut p = axis[0].clone(); 281 p[0] = Coord3D::X(x.clone()); 282 (p, x_text) 283 }) 284 .collect(), 285 1 => kps_bold 286 .y_points 287 .iter() 288 .map(|y| { 289 let y_text = (self.format_y)(y); 290 let mut p = axis[0].clone(); 291 p[1] = Coord3D::Y(y.clone()); 292 (p, y_text) 293 }) 294 .collect(), 295 _ => kps_bold 296 .z_points 297 .iter() 298 .map(|z| { 299 let z_text = (self.format_z)(z); 300 let mut p = axis[0].clone(); 301 p[2] = Coord3D::Z(z.clone()); 302 (p, z_text) 303 }) 304 .collect(), 305 }; 306 chart.draw_axis_ticks( 307 axis, 308 &labels[..], 309 self.tick_size, 310 self.axis_style, 311 self.label_style.clone(), 312 )?; 313 } 314 315 Ok(()) 316 } 317 } 318