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 [SegmentedCoord coord decorator](../coord/ranged1d/trait.IntoSegmentedCoord.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 [SegmentedCoord coord decorator](../coord/ranged1d/trait.IntoSegmentedCoord.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 marks 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 /// Sets the tick mark size for a given label area position. 128 /// `value`: The new size set_tick_mark_size<S: SizeDesc>( &mut self, pos: LabelAreaPosition, value: S, ) -> &mut Self129 pub fn set_tick_mark_size<S: SizeDesc>( 130 &mut self, 131 pos: LabelAreaPosition, 132 value: S, 133 ) -> &mut Self { 134 *match pos { 135 LabelAreaPosition::Top => &mut self.style.x_tick_size[0], 136 LabelAreaPosition::Bottom => &mut self.style.x_tick_size[1], 137 LabelAreaPosition::Left => &mut self.style.y_tick_size[0], 138 LabelAreaPosition::Right => &mut self.style.y_tick_size[1], 139 } = value.in_pixels(&self.style.parent_size); 140 self 141 } 142 } 143 144 /// The struct that is used for tracking the configuration of a mesh of any chart 145 pub struct MeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> { 146 pub(super) parent_size: (u32, u32), 147 pub(super) draw_x_mesh: bool, 148 pub(super) draw_y_mesh: bool, 149 pub(super) draw_x_axis: bool, 150 pub(super) draw_y_axis: bool, 151 pub(super) x_label_offset: i32, 152 pub(super) y_label_offset: i32, 153 pub(super) x_light_lines_limit: usize, 154 pub(super) y_light_lines_limit: usize, 155 pub(super) n_x_labels: usize, 156 pub(super) n_y_labels: usize, 157 pub(super) axis_desc_style: Option<TextStyle<'b>>, 158 pub(super) x_desc: Option<String>, 159 pub(super) y_desc: Option<String>, 160 pub(super) bold_line_style: Option<ShapeStyle>, 161 pub(super) light_line_style: Option<ShapeStyle>, 162 pub(super) axis_style: Option<ShapeStyle>, 163 pub(super) x_label_style: Option<TextStyle<'b>>, 164 pub(super) y_label_style: Option<TextStyle<'b>>, 165 pub(super) format_x: Option<&'b dyn Fn(&X::ValueType) -> String>, 166 pub(super) format_y: Option<&'b dyn Fn(&Y::ValueType) -> String>, 167 pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>>, 168 pub(super) _phantom_data: PhantomData<(X, Y)>, 169 pub(super) x_tick_size: [i32; 2], 170 pub(super) y_tick_size: [i32; 2], 171 } 172 173 impl<'a, 'b, X, Y, XT, YT, DB> MeshStyle<'a, 'b, X, Y, DB> 174 where 175 X: Ranged<ValueType = XT> + ValueFormatter<XT>, 176 Y: Ranged<ValueType = YT> + ValueFormatter<YT>, 177 DB: DrawingBackend, 178 { new(chart: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self179 pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian2d<X, Y>>) -> Self { 180 let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area()); 181 182 let mut x_tick_size = [base_tick_size, base_tick_size]; 183 let mut y_tick_size = [base_tick_size, base_tick_size]; 184 185 for idx in 0..2 { 186 if chart.is_overlapping_drawing_area(chart.x_label_area[idx].as_ref()) { 187 x_tick_size[idx] = -x_tick_size[idx]; 188 } 189 if chart.is_overlapping_drawing_area(chart.y_label_area[idx].as_ref()) { 190 y_tick_size[idx] = -y_tick_size[idx]; 191 } 192 } 193 194 MeshStyle { 195 parent_size: chart.drawing_area.dim_in_pixel(), 196 axis_style: None, 197 x_label_offset: 0, 198 y_label_offset: 0, 199 draw_x_mesh: true, 200 draw_y_mesh: true, 201 draw_x_axis: true, 202 draw_y_axis: true, 203 x_light_lines_limit: 10, 204 y_light_lines_limit: 10, 205 n_x_labels: 11, 206 n_y_labels: 11, 207 bold_line_style: None, 208 light_line_style: None, 209 x_label_style: None, 210 y_label_style: None, 211 format_x: None, 212 format_y: None, 213 target: Some(chart), 214 _phantom_data: PhantomData, 215 x_desc: None, 216 y_desc: None, 217 axis_desc_style: None, 218 x_tick_size, 219 y_tick_size, 220 } 221 } 222 } 223 224 impl<'a, 'b, X, Y, DB> MeshStyle<'a, 'b, X, Y, DB> 225 where 226 X: Ranged, 227 Y: Ranged, 228 DB: DrawingBackend, 229 { 230 /// Set all the tick mark to the same size 231 /// `value`: The new size set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self232 pub fn set_all_tick_mark_size<S: SizeDesc>(&mut self, value: S) -> &mut Self { 233 let size = value.in_pixels(&self.parent_size); 234 self.x_tick_size = [size, size]; 235 self.y_tick_size = [size, size]; 236 self 237 } 238 239 /// Set the tick mark size on the axes. When this is set to negative, the axis value label will 240 /// become inward. 241 /// 242 /// - `pos`: The which label area we want to set 243 /// - `value`: The size specification set_tick_mark_size<S: SizeDesc>( &mut self, pos: LabelAreaPosition, value: S, ) -> &mut Self244 pub fn set_tick_mark_size<S: SizeDesc>( 245 &mut self, 246 pos: LabelAreaPosition, 247 value: S, 248 ) -> &mut Self { 249 *match pos { 250 LabelAreaPosition::Top => &mut self.x_tick_size[0], 251 LabelAreaPosition::Bottom => &mut self.x_tick_size[1], 252 LabelAreaPosition::Left => &mut self.y_tick_size[0], 253 LabelAreaPosition::Right => &mut self.y_tick_size[1], 254 } = value.in_pixels(&self.parent_size); 255 self 256 } 257 258 /// The offset of x labels. This is used when we want to place the label in the middle of 259 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 260 /// use case is deprecated, see [SegmentedCoord coord decorator](../coord/ranged1d/trait.IntoSegmentedCoord.html) for more details 261 /// - `value`: The offset in pixel x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self262 pub fn x_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 263 self.x_label_offset = value.in_pixels(&self.parent_size); 264 self 265 } 266 267 /// The offset of y labels. This is used when we want to place the label in the middle of 268 /// the grid. This is used to adjust label position for histograms, but since plotters 0.3, this 269 /// use case is deprecated, see [SegmentedCoord coord decorator](../coord/ranged1d/trait.IntoSegmentedCoord.html) for more details 270 /// - `value`: The offset in pixel y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self271 pub fn y_label_offset<S: SizeDesc>(&mut self, value: S) -> &mut Self { 272 self.y_label_offset = value.in_pixels(&self.parent_size); 273 self 274 } 275 276 /// Disable the mesh for the x axis. disable_x_mesh(&mut self) -> &mut Self277 pub fn disable_x_mesh(&mut self) -> &mut Self { 278 self.draw_x_mesh = false; 279 self 280 } 281 282 /// Disable the mesh for the y axis disable_y_mesh(&mut self) -> &mut Self283 pub fn disable_y_mesh(&mut self) -> &mut Self { 284 self.draw_y_mesh = false; 285 self 286 } 287 288 /// Disable drawing the X axis disable_x_axis(&mut self) -> &mut Self289 pub fn disable_x_axis(&mut self) -> &mut Self { 290 self.draw_x_axis = false; 291 self 292 } 293 294 /// Disable drawing the Y axis disable_y_axis(&mut self) -> &mut Self295 pub fn disable_y_axis(&mut self) -> &mut Self { 296 self.draw_y_axis = false; 297 self 298 } 299 300 /// Disable drawing all meshes disable_mesh(&mut self) -> &mut Self301 pub fn disable_mesh(&mut self) -> &mut Self { 302 self.disable_x_mesh().disable_y_mesh() 303 } 304 305 /// Disable drawing all axes disable_axes(&mut self) -> &mut Self306 pub fn disable_axes(&mut self) -> &mut Self { 307 self.disable_x_axis().disable_y_axis() 308 } 309 310 /// Set the style definition for the axis 311 /// - `style`: The style for the axis axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self312 pub fn axis_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 313 self.axis_style = Some(style.into()); 314 self 315 } 316 317 /// Set the maximum number of divisions for the minor grid 318 /// - `value`: Maximum desired divisions between two consecutive X labels x_max_light_lines(&mut self, value: usize) -> &mut Self319 pub fn x_max_light_lines(&mut self, value: usize) -> &mut Self { 320 self.x_light_lines_limit = value; 321 self 322 } 323 324 /// Set the maximum number of divisions for the minor grid 325 /// - `value`: Maximum desired divisions between two consecutive Y labels y_max_light_lines(&mut self, value: usize) -> &mut Self326 pub fn y_max_light_lines(&mut self, value: usize) -> &mut Self { 327 self.y_light_lines_limit = value; 328 self 329 } 330 331 /// Set the maximum number of divisions for the minor grid 332 /// - `value`: Maximum desired divisions between two consecutive labels in X and Y max_light_lines(&mut self, value: usize) -> &mut Self333 pub fn max_light_lines(&mut self, value: usize) -> &mut Self { 334 self.x_light_lines_limit = value; 335 self.y_light_lines_limit = value; 336 self 337 } 338 339 /// Set how many labels for the X axis at most 340 /// - `value`: The maximum desired number of labels in the X axis x_labels(&mut self, value: usize) -> &mut Self341 pub fn x_labels(&mut self, value: usize) -> &mut Self { 342 self.n_x_labels = value; 343 self 344 } 345 346 /// Set how many label for the Y axis at most 347 /// - `value`: The maximum desired number of labels in the Y axis y_labels(&mut self, value: usize) -> &mut Self348 pub fn y_labels(&mut self, value: usize) -> &mut Self { 349 self.n_y_labels = value; 350 self 351 } 352 353 /// Set the style for the coarse grind grid 354 /// - `style`: This is the coarse grind grid style bold_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self355 pub fn bold_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 356 self.bold_line_style = Some(style.into()); 357 self 358 } 359 360 /// Set the style for the fine grind grid 361 /// - `style`: The fine grind grid style light_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self362 pub fn light_line_style<T: Into<ShapeStyle>>(&mut self, style: T) -> &mut Self { 363 self.light_line_style = Some(style.into()); 364 self 365 } 366 367 /// Set the style of the label text 368 /// - `style`: The text style that would be applied to the labels label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self369 pub fn label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 370 let style = style.into_text_style(&self.parent_size); 371 self.x_label_style = Some(style.clone()); 372 self.y_label_style = Some(style); 373 self 374 } 375 376 /// Set the style of the label X axis text 377 /// - `style`: The text style that would be applied to the labels x_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self378 pub fn x_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 379 self.x_label_style = Some(style.into_text_style(&self.parent_size)); 380 self 381 } 382 383 /// Set the style of the label Y axis text 384 /// - `style`: The text style that would be applied to the labels y_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self385 pub fn y_label_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 386 self.y_label_style = Some(style.into_text_style(&self.parent_size)); 387 self 388 } 389 390 /// Set the formatter function for the X label text 391 /// - `fmt`: The formatter function x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self392 pub fn x_label_formatter(&mut self, fmt: &'b dyn Fn(&X::ValueType) -> String) -> &mut Self { 393 self.format_x = Some(fmt); 394 self 395 } 396 397 /// Set the formatter function for the Y label text 398 /// - `fmt`: The formatter function y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self399 pub fn y_label_formatter(&mut self, fmt: &'b dyn Fn(&Y::ValueType) -> String) -> &mut Self { 400 self.format_y = Some(fmt); 401 self 402 } 403 404 /// Set the axis description's style. If not given, use label style instead. 405 /// - `style`: The text style that would be applied to descriptions axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self406 pub fn axis_desc_style<T: IntoTextStyle<'b>>(&mut self, style: T) -> &mut Self { 407 self.axis_desc_style = Some(style.into_text_style(&self.parent_size)); 408 self 409 } 410 411 /// Set the X axis's description 412 /// - `desc`: The description of the X axis x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self413 pub fn x_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 414 self.x_desc = Some(desc.into()); 415 self 416 } 417 418 /// Set the Y axis's description 419 /// - `desc`: The description of the Y axis y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self420 pub fn y_desc<T: Into<String>>(&mut self, desc: T) -> &mut Self { 421 self.y_desc = Some(desc.into()); 422 self 423 } 424 425 /// Draw the configured mesh on the target plot draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> where X: ValueFormatter<<X as Ranged>::ValueType>, Y: ValueFormatter<<Y as Ranged>::ValueType>,426 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> 427 where 428 X: ValueFormatter<<X as Ranged>::ValueType>, 429 Y: ValueFormatter<<Y as Ranged>::ValueType>, 430 { 431 let target = self.target.take().unwrap(); 432 433 let default_mesh_color_1 = RGBColor(0, 0, 0).mix(0.2); 434 let default_mesh_color_2 = RGBColor(0, 0, 0).mix(0.1); 435 let default_axis_color = RGBColor(0, 0, 0); 436 let default_label_font = FontDesc::new( 437 FontFamily::SansSerif, 438 f64::from((12i32).percent().max(12).in_pixels(&self.parent_size)), 439 FontStyle::Normal, 440 ); 441 442 let bold_style = self 443 .bold_line_style 444 .unwrap_or_else(|| (&default_mesh_color_1).into()); 445 let light_style = self 446 .light_line_style 447 .unwrap_or_else(|| (&default_mesh_color_2).into()); 448 let axis_style = self 449 .axis_style 450 .unwrap_or_else(|| (&default_axis_color).into()); 451 452 let x_label_style = self 453 .x_label_style 454 .clone() 455 .unwrap_or_else(|| default_label_font.clone().into()); 456 457 let y_label_style = self 458 .y_label_style 459 .clone() 460 .unwrap_or_else(|| default_label_font.into()); 461 462 let axis_desc_style = self 463 .axis_desc_style 464 .clone() 465 .unwrap_or_else(|| x_label_style.clone()); 466 467 target.draw_mesh( 468 ( 469 LightPoints::new(self.n_y_labels, self.n_y_labels * self.y_light_lines_limit), 470 LightPoints::new(self.n_x_labels, self.n_x_labels * self.x_light_lines_limit), 471 ), 472 &light_style, 473 &x_label_style, 474 &y_label_style, 475 |_, _, _| None, 476 self.draw_x_mesh, 477 self.draw_y_mesh, 478 self.x_label_offset, 479 self.y_label_offset, 480 false, 481 false, 482 &axis_style, 483 &axis_desc_style, 484 self.x_desc.clone(), 485 self.y_desc.clone(), 486 self.x_tick_size, 487 self.y_tick_size, 488 )?; 489 490 target.draw_mesh( 491 (BoldPoints(self.n_y_labels), BoldPoints(self.n_x_labels)), 492 &bold_style, 493 &x_label_style, 494 &y_label_style, 495 |xr, yr, m| match m { 496 MeshLine::XMesh(_, _, v) => { 497 if self.draw_x_axis { 498 if let Some(fmt_func) = self.format_x { 499 Some(fmt_func(v)) 500 } else { 501 Some(xr.format_ext(v)) 502 } 503 } else { 504 None 505 } 506 } 507 MeshLine::YMesh(_, _, v) => { 508 if self.draw_y_axis { 509 if let Some(fmt_func) = self.format_y { 510 Some(fmt_func(v)) 511 } else { 512 Some(yr.format_ext(v)) 513 } 514 } else { 515 None 516 } 517 } 518 }, 519 self.draw_x_mesh, 520 self.draw_y_mesh, 521 self.x_label_offset, 522 self.y_label_offset, 523 self.draw_x_axis, 524 self.draw_y_axis, 525 &axis_style, 526 &axis_desc_style, 527 None, 528 None, 529 self.x_tick_size, 530 self.y_tick_size, 531 ) 532 } 533 } 534