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