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