1 use std::marker::PhantomData; 2 3 use super::builder::LabelAreaPosition; 4 use super::context::ChartContext; 5 use crate::coord::cartesian::{Cartesian2d, MeshLine}; 6 use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter}; 7 use crate::drawing::DrawingAreaErrorKind; 8 use crate::style::{ 9 AsRelative, Color, FontDesc, FontFamily, FontStyle, IntoTextStyle, RGBColor, ShapeStyle, 10 SizeDesc, TextStyle, 11 }; 12 13 use plotters_backend::DrawingBackend; 14 15 /// The style used to describe the mesh and axis for a secondary coordinate system. 16 pub struct SecondaryMeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> { 17 style: MeshStyle<'a, 'b, X, Y, DB>, 18 } 19 20 impl<'a, 'b, XT, YT, X: Ranged<ValueType = XT>, Y: Ranged<ValueType = YT>, DB: DrawingBackend> 21 SecondaryMeshStyle<'a, 'b, X, Y, DB> 22 where 23 X: ValueFormatter<XT>, 24 Y: ValueFormatter<YT>, 25 { new(target: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self26 pub(super) fn new(target: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self { 27 let mut style = target.configure_mesh(); 28 style.draw_x_mesh = false; 29 style.draw_y_mesh = false; 30 Self { style } 31 } 32 33 /// Set the style definition for the axis 34 /// - `style`: The style for the axis axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self35 pub fn axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 36 self.style.axis_style(style); 37 self 38 } 39 40 /// The offset of x labels. This is used when we want to place the label in the middle of 41 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 42 /// use case is deprecated, see [CentricDiscreteRanged coord decorator](../coord/trait.IntoCentric.html) for more details 43 /// - `value`: The offset in pixel x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self44 pub fn x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 45 self.style.x_label_offset(value); 46 self 47 } 48 49 /// The offset of y labels. This is used when we want to place the label in the middle of 50 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 51 /// use case is deprecated, see [CentricDiscreteRanged coord decorator](../coord/trait.IntoCentric.html) for more details 52 /// - `value`: The offset in pixel y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self53 pub fn y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 54 self.style.y_label_offset(value); 55 self 56 } 57 58 /// Set how many labels for the X axis at most 59 /// - `value`: The maximum desired number of labels in the X axis x_labels(&mut self, value: usize) -> &mut Self60 pub fn x_labels(&mut self, value: usize) -> &mut Self { 61 self.style.x_labels(value); 62 self 63 } 64 65 /// Set how many label for the Y axis at most 66 /// - `value`: The maximum desired number of labels in the Y axis y_labels(&mut self, value: usize) -> &mut Self67 pub fn y_labels(&mut self, value: usize) -> &mut Self { 68 self.style.y_labels(value); 69 self 70 } 71 72 /// Set the formatter function for the X label text 73 /// - `fmt`: The formatter function x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self74 pub fn x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self { 75 self.style.x_label_formatter(fmt); 76 self 77 } 78 79 /// Set the formatter function for the Y label text 80 /// - `fmt`: The formatter function y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self81 pub fn y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self { 82 self.style.y_label_formatter(fmt); 83 self 84 } 85 86 /// Set the axis description's style. If not given, use label style instead. 87 /// - `style`: The text style that would be applied to descriptions axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self88 pub fn axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 89 self.style 90 .axis_desc_style(style.into_text_style(&self.style.parent_size)); 91 self 92 } 93 94 /// Set the X axis's description 95 /// - `desc`: The description of the X axis x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self96 pub fn x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 97 self.style.x_desc(desc); 98 self 99 } 100 101 /// Set the Y axis's description 102 /// - `desc`: The description of the Y axis y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self103 pub fn y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 104 self.style.y_desc(desc); 105 self 106 } 107 108 /// Draw the axes for the secondary coordinate system draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>109 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> { 110 self.style.draw() 111 } 112 113 /// Set the label style for the secondary axis label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self114 pub fn label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 115 self.style.label_style(style); 116 self 117 } 118 119 /// Set all the tick mark to the same size 120 /// `value`: The new size set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self121 pub fn set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self { 122 let size = value.in_pixels(&self.style.parent_size); 123 self.style.x_tick_size = [size, size]; 124 self.style.y_tick_size = [size, size]; 125 self 126 } 127 set_tick_mark_size<S: SizeDesc>( &mut self, pos: LabelAreaPosition, value: S, ) -> &mut Self128 pub fn set_tick_mark_size<S: SizeDesc>( 129 &mut self, 130 pos: LabelAreaPosition, 131 value: S, 132 ) -> &mut Self { 133 *match pos { 134 LabelAreaPosition::Top => &mut self.style.x_tick_size[0], 135 LabelAreaPosition::Bottom => &mut self.style.x_tick_size[1], 136 LabelAreaPosition::Left => &mut self.style.y_tick_size[0], 137 LabelAreaPosition::Right => &mut self.style.y_tick_size[1], 138 } = value.in_pixels(&self.style.parent_size); 139 self 140 } 141 } 142 143 /// The struct that is used for tracking the configuration of a mesh of any chart 144 pub struct MeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> { 145 pub(super) parent_size: (u32, u32), 146 pub(super) draw_x_mesh: bool, 147 pub(super) draw_y_mesh: bool, 148 pub(super) draw_x_axis: bool, 149 pub(super) draw_y_axis: bool, 150 pub(super) x_label_offset: i32, 151 pub(super) y_label_offset: i32, 152 pub(super) n_x_labels: usize, 153 pub(super) n_y_labels: usize, 154 pub(super) axis_desc_style: Option<TextStyle<'b>>, 155 pub(super) x_desc: Option<String>, 156 pub(super) y_desc: Option<String>, 157 pub(super) bold_line_style: Option<ShapeStyle>, 158 pub(super) light_line_style: Option<ShapeStyle>, 159 pub(super) axis_style: Option<ShapeStyle>, 160 pub(super) x_label_style: Option<TextStyle<'b>>, 161 pub(super) y_label_style: Option<TextStyle<'b>>, 162 pub(super) format_x: &'b dyn Fn(&X::ValueType) -> String, 163 pub(super) format_y: &'b dyn Fn(&Y::ValueType) -> String, 164 pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>>, 165 pub(super) _phantom_data: PhantomData<(X, Y)>, 166 pub(super) x_tick_size: [i32; 2], 167 pub(super) y_tick_size: [i32; 2], 168 } 169 170 impl<'a, 'b, X, Y, XT, YT, DB> MeshStyle<'a, 'b, X, Y, DB> 171 where 172 X: Ranged<ValueType = XT> + ValueFormatter<XT>, 173 Y: Ranged<ValueType = YT> + ValueFormatter<YT>, 174 DB: DrawingBackend, 175 { new(chart: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self176 pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self { 177 let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area()); 178 179 let mut x_tick_size = [base_tick_size, base_tick_size]; 180 let mut y_tick_size = [base_tick_size, base_tick_size]; 181 182 for idx in 0..2 { 183 if chart.is_overlapping_drawing_area(chart.x_label_area[idx].as_ref()) { 184 x_tick_size[idx] = -x_tick_size[idx]; 185 } 186 if chart.is_overlapping_drawing_area(chart.y_label_area[idx].as_ref()) { 187 y_tick_size[idx] = -y_tick_size[idx]; 188 } 189 } 190 191 MeshStyle { 192 parent_size: chart.drawing_area.dim_in_pixel(), 193 axis_style: None, 194 x_label_offset: 0, 195 y_label_offset: 0, 196 draw_x_mesh: true, 197 draw_y_mesh: true, 198 draw_x_axis: true, 199 draw_y_axis: true, 200 n_x_labels: 10, 201 n_y_labels: 10, 202 bold_line_style: None, 203 light_line_style: None, 204 x_label_style: None, 205 y_label_style: None, 206 format_x: &X::format, 207 format_y: &Y::format, 208 target: Some(chart), 209 _phantom_data: PhantomData, 210 x_desc: None, 211 y_desc: None, 212 axis_desc_style: None, 213 x_tick_size, 214 y_tick_size, 215 } 216 } 217 } 218 219 impl<'a, 'b, X, Y, DB> MeshStyle<'a, 'b, X, Y, DB> 220 where 221 X: Ranged, 222 Y: Ranged, 223 DB: DrawingBackend, 224 { 225 /// Set all the tick mark to the same size 226 /// `value`: The new size set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self227 pub fn set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self { 228 let size = value.in_pixels(&self.parent_size); 229 self.x_tick_size = [size, size]; 230 self.y_tick_size = [size, size]; 231 self 232 } 233 234 /// Set the tick mark size on the axes. When this is set to negative, the axis value label will 235 /// become inward. 236 /// 237 /// - `pos`: The which label area we want to set 238 /// - `value`: The size specification set_tick_mark_size<S: SizeDesc>( &mut self, pos: LabelAreaPosition, value: S, ) -> &mut Self239 pub fn set_tick_mark_size<S: SizeDesc>( 240 &mut self, 241 pos: LabelAreaPosition, 242 value: S, 243 ) -> &mut Self { 244 *match pos { 245 LabelAreaPosition::Top => &mut self.x_tick_size[0], 246 LabelAreaPosition::Bottom => &mut self.x_tick_size[1], 247 LabelAreaPosition::Left => &mut self.y_tick_size[0], 248 LabelAreaPosition::Right => &mut self.y_tick_size[1], 249 } = value.in_pixels(&self.parent_size); 250 self 251 } 252 253 /// The offset of x labels. This is used when we want to place the label in the middle of 254 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 255 /// use case is deprecated, see [CentricDiscreteRanged coord decorator](../coord/trait.IntoCentric.html) for more details 256 /// - `value`: The offset in pixel x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self257 pub fn x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 258 self.x_label_offset = value.in_pixels(&self.parent_size); 259 self 260 } 261 262 /// The offset of y labels. This is used when we want to place the label in the middle of 263 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 264 /// use case is deprecated, see [CentricDiscreteRanged coord decorator](../coord/trait.IntoCentric.html) for more details 265 /// - `value`: The offset in pixel y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self266 pub fn y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 267 self.y_label_offset = value.in_pixels(&self.parent_size); 268 self 269 } 270 271 /// Disable the mesh for the x axis. disable_x_mesh(&mut self) -> &mut Self272 pub fn disable_x_mesh(&mut self) -> &mut Self { 273 self.draw_x_mesh = false; 274 self 275 } 276 277 /// Disable the mesh for the y axis disable_y_mesh(&mut self) -> &mut Self278 pub fn disable_y_mesh(&mut self) -> &mut Self { 279 self.draw_y_mesh = false; 280 self 281 } 282 283 /// Disable drawing the X axis disable_x_axis(&mut self) -> &mut Self284 pub fn disable_x_axis(&mut self) -> &mut Self { 285 self.draw_x_axis = false; 286 self 287 } 288 289 /// Disable drawing the Y axis disable_y_axis(&mut self) -> &mut Self290 pub fn disable_y_axis(&mut self) -> &mut Self { 291 self.draw_y_axis = false; 292 self 293 } 294 295 /// Disable drawing all meshes disable_mesh(&mut self) -> &mut Self296 pub fn disable_mesh(&mut self) -> &mut Self { 297 self.disable_x_mesh().disable_y_mesh() 298 } 299 300 /// Disable drawing all axes disable_axes(&mut self) -> &mut Self301 pub fn disable_axes(&mut self) -> &mut Self { 302 self.disable_x_axis().disable_y_axis() 303 } 304 305 /// Set the style definition for the axis 306 /// - `style`: The style for the axis axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self307 pub fn axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 308 self.axis_style = Some(style.into()); 309 self 310 } 311 /// Set how many labels for the X axis at most 312 /// - `value`: The maximum desired number of labels in the X axis x_labels(&mut self, value: usize) -> &mut Self313 pub fn x_labels(&mut self, value: usize) -> &mut Self { 314 self.n_x_labels = value; 315 self 316 } 317 318 /// Set how many label for the Y axis at most 319 /// - `value`: The maximum desired number of labels in the Y axis y_labels(&mut self, value: usize) -> &mut Self320 pub fn y_labels(&mut self, value: usize) -> &mut Self { 321 self.n_y_labels = value; 322 self 323 } 324 325 /// Set the style for the coarse grind grid 326 /// - `style`: This is the coarse grind grid style bold_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self327 pub fn bold_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 328 self.bold_line_style = Some(style.into()); 329 self 330 } 331 332 /// Set the style for the fine grind grid 333 /// - `style`: The fine grind grid style light_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self334 pub fn light_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 335 self.light_line_style = Some(style.into()); 336 self 337 } 338 339 /// Set the style of the label text 340 /// - `style`: The text style that would be applied to the labels label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self341 pub fn label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 342 let style = style.into_text_style(&self.parent_size); 343 self.x_label_style = Some(style.clone()); 344 self.y_label_style = Some(style); 345 self 346 } 347 348 /// Set the style of the label X axis text 349 /// - `style`: The text style that would be applied to the labels x_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self350 pub fn x_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 351 self.x_label_style = Some(style.into_text_style(&self.parent_size)); 352 self 353 } 354 355 /// Set the style of the label Y axis text 356 /// - `style`: The text style that would be applied to the labels y_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self357 pub fn y_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 358 self.y_label_style = Some(style.into_text_style(&self.parent_size)); 359 self 360 } 361 362 /// Set the formatter function for the X label text 363 /// - `fmt`: The formatter function x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self364 pub fn x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self { 365 self.format_x = fmt; 366 self 367 } 368 369 /// Set the formatter function for the Y label text 370 /// - `fmt`: The formatter function y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self371 pub fn y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self { 372 self.format_y = fmt; 373 self 374 } 375 376 /// Set the axis description's style. If not given, use label style instead. 377 /// - `style`: The text style that would be applied to descriptions axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self378 pub fn axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 379 self.axis_desc_style = Some(style.into_text_style(&self.parent_size)); 380 self 381 } 382 383 /// Set the X axis's description 384 /// - `desc`: The description of the X axis x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self385 pub fn x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 386 self.x_desc = Some(desc.into()); 387 self 388 } 389 390 /// Set the Y axis's description 391 /// - `desc`: The description of the Y axis y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self392 pub fn y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 393 self.y_desc = Some(desc.into()); 394 self 395 } 396 397 /// Draw the configured mesh on the target plot draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>398 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> { 399 let mut target = None; 400 std::mem::swap(&mut target, &mut self.target); 401 let target = target.unwrap(); 402 403 let default_mesh_color_1 = RGBColor(0, 0, 0).mix(0.2); 404 let default_mesh_color_2 = RGBColor(0, 0, 0).mix(0.1); 405 let default_axis_color = RGBColor(0, 0, 0); 406 let default_label_font = FontDesc::new( 407 FontFamily::SansSerif, 408 f64::from((12i32).percent().max(12).in_pixels(&self.parent_size)), 409 FontStyle::Normal, 410 ); 411 412 let bold_style = self 413 .bold_line_style 414 .clone() 415 .unwrap_or_else(|| (&default_mesh_color_1).into()); 416 let light_style = self 417 .light_line_style 418 .clone() 419 .unwrap_or_else(|| (&default_mesh_color_2).into()); 420 let axis_style = self 421 .axis_style 422 .clone() 423 .unwrap_or_else(|| (&default_axis_color).into()); 424 425 let x_label_style = self 426 .x_label_style 427 .clone() 428 .unwrap_or_else(|| default_label_font.clone().into()); 429 430 let y_label_style = self 431 .y_label_style 432 .clone() 433 .unwrap_or_else(|| default_label_font.into()); 434 435 let axis_desc_style = self 436 .axis_desc_style 437 .clone() 438 .unwrap_or_else(|| x_label_style.clone()); 439 440 target.draw_mesh( 441 ( 442 LightPoints::new(self.n_y_labels, self.n_y_labels * 10), 443 LightPoints::new(self.n_x_labels, self.n_x_labels * 10), 444 ), 445 &light_style, 446 &x_label_style, 447 &y_label_style, 448 |_| None, 449 self.draw_x_mesh, 450 self.draw_y_mesh, 451 self.x_label_offset, 452 self.y_label_offset, 453 false, 454 false, 455 &axis_style, 456 &axis_desc_style, 457 self.x_desc.clone(), 458 self.y_desc.clone(), 459 self.x_tick_size, 460 self.y_tick_size, 461 )?; 462 463 target.draw_mesh( 464 (BoldPoints(self.n_y_labels), BoldPoints(self.n_x_labels)), 465 &bold_style, 466 &x_label_style, 467 &y_label_style, 468 |m| match m { 469 MeshLine::XMesh(_, _, v) => Some((self.format_x)(v)), 470 MeshLine::YMesh(_, _, v) => Some((self.format_y)(v)), 471 }, 472 self.draw_x_mesh, 473 self.draw_y_mesh, 474 self.x_label_offset, 475 self.y_label_offset, 476 self.draw_x_axis, 477 self.draw_y_axis, 478 &axis_style, 479 &axis_desc_style, 480 None, 481 None, 482 self.x_tick_size, 483 self.y_tick_size, 484 ) 485 } 486 } 487