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}
318