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
16pub 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 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 pub fn x_labels(&mut self, n: usize) -> &mut Self {
49 self.n_labels[0] = n;
50 self
51 }
52
53 pub fn y_labels(&mut self, n: usize) -> &mut Self {
55 self.n_labels[1] = n;
56 self
57 }
58
59 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}