plotters/chart/
mesh.rs

1use std::marker::PhantomData;
2
3use super::builder::LabelAreaPosition;
4use super::context::ChartContext;
5use crate::coord::cartesian::{Cartesian2d, MeshLine};
6use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter};
7use crate::drawing::DrawingAreaErrorKind;
8use crate::style::{
9    AsRelative, Color, FontDesc, FontFamily, FontStyle, IntoTextStyle, RGBColor, ShapeStyle,
10    SizeDesc, TextStyle,
11};
12
13use plotters_backend::DrawingBackend;
14
15/// The style used to describe the mesh and axis for a secondary coordinate system.
16pub struct SecondaryMeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> {
17    style: MeshStyle<'a, 'b, X, Y, DB>,
18}
19
20impl<'a, 'b, XT, YT, X: Ranged<ValueType = XT>, Y: Ranged<ValueType = YT>, DB: DrawingBackend>
21    SecondaryMeshStyle<'a, 'b, X, Y, DB>
22where
23    X: ValueFormatter<XT>,
24    Y: ValueFormatter<YT>,
25{
26    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
35    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
44    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
53    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
60    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
67    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
74    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
81    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
88    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
96    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
103    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
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
114    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
121    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
129    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
145pub 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
173impl<'a, 'b, X, Y, XT, YT, DB> MeshStyle<'a, 'b, X, Y, DB>
174where
175    X: Ranged<ValueType = XT> + ValueFormatter<XT>,
176    Y: Ranged<ValueType = YT> + ValueFormatter<YT>,
177    DB: DrawingBackend,
178{
179    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
224impl<'a, 'b, X, Y, DB> MeshStyle<'a, 'b, X, Y, DB>
225where
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
232    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
244    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
262    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
271    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.
277    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
283    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
289    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
295    pub fn disable_y_axis(&mut self) -> &mut Self {
296        self.draw_y_axis = false;
297        self
298    }
299
300    /// Disable drawing all meshes
301    pub fn disable_mesh(&mut self) -> &mut Self {
302        self.disable_x_mesh().disable_y_mesh()
303    }
304
305    /// Disable drawing all axes
306    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
312    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
319    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
326    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
333    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
341    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
348    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
355    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
362    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
369    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
378    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
385    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
392    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
399    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
406    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
413    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
420    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
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}