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/// The configurations about the 3D plot's axes
17pub struct Axes3dStyle<'a, 'b, X: Ranged, Y: Ranged, Z: Ranged, DB: DrawingBackend> {
18    pub(super) parent_size: (u32, u32),
19    pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>>,
20    pub(super) tick_size: i32,
21    pub(super) n_labels: [usize; 3],
22    pub(super) bold_line_style: ShapeStyle,
23    pub(super) light_line_style: ShapeStyle,
24    pub(super) axis_panel_style: ShapeStyle,
25    pub(super) axis_style: ShapeStyle,
26    pub(super) label_style: TextStyle<'b>,
27    pub(super) format_x: &'b dyn Fn(&X::ValueType) -> String,
28    pub(super) format_y: &'b dyn Fn(&Y::ValueType) -> String,
29    pub(super) format_z: &'b dyn Fn(&Z::ValueType) -> String,
30    _phantom: PhantomData<&'a (X, Y, Z)>,
31}
32
33impl<'a, 'b, X, Y, Z, XT, YT, ZT, DB> Axes3dStyle<'a, 'b, X, Y, Z, DB>
34where
35    X: Ranged<ValueType = XT> + ValueFormatter<XT>,
36    Y: Ranged<ValueType = YT> + ValueFormatter<YT>,
37    Z: Ranged<ValueType = ZT> + ValueFormatter<ZT>,
38    DB: DrawingBackend,
39{
40    /// Set the size of the tick mark
41    pub fn tick_size<Size: SizeDesc>(&mut self, size: Size) -> &mut Self {
42        let actual_size = size.in_pixels(&self.parent_size);
43        self.tick_size = actual_size;
44        self
45    }
46
47    /// Set the number of labels on the X axes
48    pub fn x_labels(&mut self, n: usize) -> &mut Self {
49        self.n_labels[0] = n;
50        self
51    }
52
53    /// Set the number of labels on the Y axes
54    pub fn y_labels(&mut self, n: usize) -> &mut Self {
55        self.n_labels[1] = n;
56        self
57    }
58
59    /// Set the number of labels on the Z axes
60    pub fn z_labels(&mut self, n: usize) -> &mut Self {
61        self.n_labels[2] = n;
62        self
63    }
64
65    pub fn axis_panel_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
66        self.axis_panel_style = style.into();
67        self
68    }
69
70    pub fn bold_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
71        self.bold_line_style = style.into();
72        self
73    }
74
75    pub fn light_grid_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
76        self.light_line_style = style.into();
77        self
78    }
79
80    pub fn label_style<S: Into<TextStyle<'b>>>(&mut self, style: S) -> &mut Self {
81        self.label_style = style.into();
82        self
83    }
84
85    pub fn x_formatter<F: Fn(&X::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
86        self.format_x = f;
87        self
88    }
89
90    pub fn y_formatter<F: Fn(&Y::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
91        self.format_y = f;
92        self
93    }
94
95    pub fn z_formatter<F: Fn(&Z::ValueType) -> String>(&mut self, f: &'b F) -> &mut Self {
96        self.format_z = f;
97        self
98    }
99
100    pub(crate) fn new(chart: &'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>) -> Self {
101        let parent_size = chart.drawing_area.dim_in_pixel();
102        let base_tick_size = (5u32).percent().max(5).in_pixels(chart.plotting_area());
103        let tick_size = base_tick_size;
104        Self {
105            parent_size,
106            tick_size,
107            n_labels: [10, 10, 10],
108            bold_line_style: Into::<ShapeStyle>::into(&BLACK.mix(0.2)),
109            light_line_style: Into::<ShapeStyle>::into(&TRANSPARENT),
110            axis_panel_style: Into::<ShapeStyle>::into(&BLACK.mix(0.1)),
111            axis_style: Into::<ShapeStyle>::into(&BLACK.mix(0.8)),
112            label_style: ("sans-serf", (12).percent().max(12).in_pixels(&parent_size)).into(),
113            format_x: &X::format,
114            format_y: &Y::format,
115            format_z: &Z::format,
116            _phantom: PhantomData,
117            target: Some(chart),
118        }
119    }
120    pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>
121    where
122        XT: Clone,
123        YT: Clone,
124        ZT: Clone,
125    {
126        let chart = self.target.take().unwrap();
127        let kps_bold = chart.get_key_points(
128            BoldPoints(self.n_labels[0]),
129            BoldPoints(self.n_labels[1]),
130            BoldPoints(self.n_labels[2]),
131        );
132        let kps_light = chart.get_key_points(
133            LightPoints::new(self.n_labels[0], self.n_labels[0] * 10),
134            LightPoints::new(self.n_labels[1], self.n_labels[1] * 10),
135            LightPoints::new(self.n_labels[2], self.n_labels[2] * 10),
136        );
137
138        let panels = chart.draw_axis_panels(
139            &kps_bold,
140            &kps_light,
141            self.axis_panel_style.clone(),
142            self.bold_line_style.clone(),
143            self.light_line_style.clone(),
144        )?;
145
146        for i in 0..3 {
147            let axis = chart.draw_axis(i, &panels, self.axis_style.clone())?;
148            let labels: Vec<_> = match i {
149                0 => kps_bold
150                    .x_points
151                    .iter()
152                    .map(|x| {
153                        let x_text = (self.format_x)(x);
154                        let mut p = axis[0].clone();
155                        p[0] = Coord3D::X(x.clone());
156                        (p, x_text)
157                    })
158                    .collect(),
159                1 => kps_bold
160                    .y_points
161                    .iter()
162                    .map(|y| {
163                        let y_text = (self.format_y)(y);
164                        let mut p = axis[0].clone();
165                        p[1] = Coord3D::Y(y.clone());
166                        (p, y_text)
167                    })
168                    .collect(),
169                _ => kps_bold
170                    .z_points
171                    .iter()
172                    .map(|z| {
173                        let z_text = (self.format_z)(z);
174                        let mut p = axis[0].clone();
175                        p[2] = Coord3D::Z(z.clone());
176                        (p, z_text)
177                    })
178                    .collect(),
179            };
180            chart.draw_axis_ticks(
181                axis,
182                &labels[..],
183                self.tick_size,
184                self.axis_style.clone(),
185                self.label_style.clone(),
186            )?;
187        }
188
189        Ok(())
190    }
191}