plotters/chart/
axes3d.rs

1use std::marker::PhantomData;
2
3use super::ChartContext;
4use crate::coord::cartesian::Cartesian3d;
5use crate::coord::ranged1d::{BoldPoints, LightPoints, Ranged, ValueFormatter};
6use crate::style::colors::{BLACK, TRANSPARENT};
7use crate::style::Color;
8use crate::style::{AsRelative, ShapeStyle, SizeDesc, TextStyle};
9
10use super::Coord3D;
11
12use crate::drawing::DrawingAreaErrorKind;
13
14use plotters_backend::DrawingBackend;
15
16/**
17Implements 3D plot axes configurations.
18
19The best way to use this struct is by way of the [`configure_axes()`] function.
20See [`ChartContext::configure_axes()`] for more information and examples.
21*/
22pub 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
39impl<'a, 'b, X, Y, Z, XT, YT, ZT, DB> Axes3dStyle<'a, 'b, X, Y, Z, DB>
40where
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    */
53    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    */
66    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    */
78    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    */
90    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    */
102    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    */
114    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    */
124    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    */
134    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    */
144    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    */
154    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    */
164    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    */
174    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    */
184    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    */
194    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    */
204    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    */
215    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
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}