1 use std::cmp::Ordering;
2 
3 use plotters_backend::DrawingBackend;
4 
5 use crate::chart::ChartContext;
6 use crate::coord::{
7     cartesian::Cartesian3d,
8     ranged1d::{KeyPointHint, Ranged},
9     CoordTranslate,
10 };
11 use crate::drawing::DrawingAreaErrorKind;
12 use crate::element::{EmptyElement, PathElement, Polygon, Text};
13 use crate::style::{
14     text_anchor::{HPos, Pos, VPos},
15     ShapeStyle, TextStyle,
16 };
17 
18 use super::Coord3D;
19 
20 pub(crate) struct KeyPoints3d<X: Ranged, Y: Ranged, Z: Ranged> {
21     pub(crate) x_points: Vec<X::ValueType>,
22     pub(crate) y_points: Vec<Y::ValueType>,
23     pub(crate) z_points: Vec<Z::ValueType>,
24 }
25 
26 impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
27 where
28     DB: DrawingBackend,
29     X::ValueType: Clone,
30     Y::ValueType: Clone,
31     Z::ValueType: Clone,
32 {
get_key_points<XH: KeyPointHint, YH: KeyPointHint, ZH: KeyPointHint>( &self, x_hint: XH, y_hint: YH, z_hint: ZH, ) -> KeyPoints3d<X, Y, Z>33     pub(crate) fn get_key_points<XH: KeyPointHint, YH: KeyPointHint, ZH: KeyPointHint>(
34         &self,
35         x_hint: XH,
36         y_hint: YH,
37         z_hint: ZH,
38     ) -> KeyPoints3d<X, Y, Z> {
39         let coord = self.plotting_area().as_coord_spec();
40         let x_points = coord.logic_x.key_points(x_hint);
41         let y_points = coord.logic_y.key_points(y_hint);
42         let z_points = coord.logic_z.key_points(z_hint);
43         KeyPoints3d {
44             x_points,
45             y_points,
46             z_points,
47         }
48     }
49     #[allow(clippy::type_complexity)]
draw_axis_ticks( &mut self, axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], labels: &[( [Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3], String, )], tick_size: i32, style: ShapeStyle, font: TextStyle, ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>50     pub(crate) fn draw_axis_ticks(
51         &mut self,
52         axis: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2],
53         labels: &[(
54             [Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3],
55             String,
56         )],
57         tick_size: i32,
58         style: ShapeStyle,
59         font: TextStyle,
60     ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> {
61         let coord = self.plotting_area().as_coord_spec();
62         let begin = coord.translate(&Coord3D::build_coord([
63             &axis[0][0],
64             &axis[0][1],
65             &axis[0][2],
66         ]));
67         let end = coord.translate(&Coord3D::build_coord([
68             &axis[1][0],
69             &axis[1][1],
70             &axis[1][2],
71         ]));
72         let axis_dir = (end.0 - begin.0, end.1 - begin.1);
73         let (x_range, y_range) = self.plotting_area().get_pixel_range();
74         let x_mid = (x_range.start + x_range.end) / 2;
75         let y_mid = (y_range.start + y_range.end) / 2;
76 
77         let x_dir = if begin.0 < x_mid {
78             (-tick_size, 0)
79         } else {
80             (tick_size, 0)
81         };
82 
83         let y_dir = if begin.1 < y_mid {
84             (0, -tick_size)
85         } else {
86             (0, tick_size)
87         };
88 
89         let x_score = (x_dir.0 * axis_dir.0 + x_dir.1 * axis_dir.1).abs();
90         let y_score = (y_dir.0 * axis_dir.0 + y_dir.1 * axis_dir.1).abs();
91 
92         let dir = if x_score < y_score { x_dir } else { y_dir };
93 
94         for (pos, text) in labels {
95             let logic_pos = Coord3D::build_coord([&pos[0], &pos[1], &pos[2]]);
96             let mut font = font.clone();
97 
98             match dir.0.cmp(&0) {
99                 Ordering::Less => font.pos = Pos::new(HPos::Right, VPos::Center),
100                 Ordering::Greater => font.pos = Pos::new(HPos::Left, VPos::Center),
101                 _ => (),
102             }
103 
104             match dir.1.cmp(&0) {
105                 Ordering::Less => font.pos = Pos::new(HPos::Center, VPos::Bottom),
106                 Ordering::Greater => font.pos = Pos::new(HPos::Center, VPos::Top),
107                 _ => (),
108             }
109 
110             let element = EmptyElement::at(logic_pos)
111                 + PathElement::new(vec![(0, 0), dir], style)
112                 + Text::new(text.to_string(), (dir.0 * 2, dir.1 * 2), font);
113             self.plotting_area().draw(&element)?;
114         }
115         Ok(())
116     }
117     #[allow(clippy::type_complexity)]
draw_axis( &mut self, idx: usize, panels: &[[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], style: ShapeStyle, ) -> Result< [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], DrawingAreaErrorKind<DB::ErrorType>, >118     pub(crate) fn draw_axis(
119         &mut self,
120         idx: usize,
121         panels: &[[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3],
122         style: ShapeStyle,
123     ) -> Result<
124         [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2],
125         DrawingAreaErrorKind<DB::ErrorType>,
126     > {
127         let coord = self.plotting_area().as_coord_spec();
128         let x_range = coord.logic_x.range();
129         let y_range = coord.logic_y.range();
130         let z_range = coord.logic_z.range();
131 
132         let ranges: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 2]; 3] = [
133             [Coord3D::X(x_range.start), Coord3D::X(x_range.end)],
134             [Coord3D::Y(y_range.start), Coord3D::Y(y_range.end)],
135             [Coord3D::Z(z_range.start), Coord3D::Z(z_range.end)],
136         ];
137 
138         let (start, end) = {
139             let mut start = [&ranges[0][0], &ranges[1][0], &ranges[2][0]];
140             let mut end = [&ranges[0][1], &ranges[1][1], &ranges[2][1]];
141 
142             let mut plan = vec![];
143 
144             for i in 0..3 {
145                 if i == idx {
146                     continue;
147                 }
148                 start[i] = &panels[i][0][i];
149                 end[i] = &panels[i][0][i];
150                 for j in 0..3 {
151                     if i != idx && i != j && j != idx {
152                         for k in 0..2 {
153                             start[j] = &panels[i][k][j];
154                             end[j] = &panels[i][k][j];
155                             plan.push((start, end));
156                         }
157                     }
158                 }
159             }
160             plan.into_iter()
161                 .min_by_key(|&(s, e)| {
162                     let d = coord.projected_depth(s[0].get_x(), s[1].get_y(), s[2].get_z());
163                     let d = d + coord.projected_depth(e[0].get_x(), e[1].get_y(), e[2].get_z());
164                     let (_, y1) = coord.translate(&Coord3D::build_coord(s));
165                     let (_, y2) = coord.translate(&Coord3D::build_coord(e));
166                     let y = y1 + y2;
167                     (d, y)
168                 })
169                 .unwrap()
170         };
171 
172         self.plotting_area().draw(&PathElement::new(
173             vec![Coord3D::build_coord(start), Coord3D::build_coord(end)],
174             style,
175         ))?;
176 
177         Ok([
178             [start[0].clone(), start[1].clone(), start[2].clone()],
179             [end[0].clone(), end[1].clone(), end[2].clone()],
180         ])
181     }
182 
183     #[allow(clippy::type_complexity)]
draw_axis_panels( &mut self, bold_points: &KeyPoints3d<X, Y, Z>, light_points: &KeyPoints3d<X, Y, Z>, panel_style: ShapeStyle, bold_grid_style: ShapeStyle, light_grid_style: ShapeStyle, ) -> Result< [[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3], DrawingAreaErrorKind<DB::ErrorType>, >184     pub(crate) fn draw_axis_panels(
185         &mut self,
186         bold_points: &KeyPoints3d<X, Y, Z>,
187         light_points: &KeyPoints3d<X, Y, Z>,
188         panel_style: ShapeStyle,
189         bold_grid_style: ShapeStyle,
190         light_grid_style: ShapeStyle,
191     ) -> Result<
192         [[[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2]; 3],
193         DrawingAreaErrorKind<DB::ErrorType>,
194     > {
195         let mut r_iter = (0..3).map(|idx| {
196             self.draw_axis_panel(
197                 idx,
198                 bold_points,
199                 light_points,
200                 panel_style,
201                 bold_grid_style,
202                 light_grid_style,
203             )
204         });
205         Ok([
206             r_iter.next().unwrap()?,
207             r_iter.next().unwrap()?,
208             r_iter.next().unwrap()?,
209         ])
210     }
211     #[allow(clippy::type_complexity)]
draw_axis_panel( &mut self, idx: usize, bold_points: &KeyPoints3d<X, Y, Z>, light_points: &KeyPoints3d<X, Y, Z>, panel_style: ShapeStyle, bold_grid_style: ShapeStyle, light_grid_style: ShapeStyle, ) -> Result< [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2], DrawingAreaErrorKind<DB::ErrorType>, >212     fn draw_axis_panel(
213         &mut self,
214         idx: usize,
215         bold_points: &KeyPoints3d<X, Y, Z>,
216         light_points: &KeyPoints3d<X, Y, Z>,
217         panel_style: ShapeStyle,
218         bold_grid_style: ShapeStyle,
219         light_grid_style: ShapeStyle,
220     ) -> Result<
221         [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 3]; 2],
222         DrawingAreaErrorKind<DB::ErrorType>,
223     > {
224         let coord = self.plotting_area().as_coord_spec();
225         let x_range = coord.logic_x.range();
226         let y_range = coord.logic_y.range();
227         let z_range = coord.logic_z.range();
228 
229         let ranges: [[Coord3D<X::ValueType, Y::ValueType, Z::ValueType>; 2]; 3] = [
230             [Coord3D::X(x_range.start), Coord3D::X(x_range.end)],
231             [Coord3D::Y(y_range.start), Coord3D::Y(y_range.end)],
232             [Coord3D::Z(z_range.start), Coord3D::Z(z_range.end)],
233         ];
234 
235         let (mut panel, start, end) = {
236             let vert_a = [&ranges[0][0], &ranges[1][0], &ranges[2][0]];
237             let mut vert_b = [&ranges[0][1], &ranges[1][1], &ranges[2][1]];
238             let mut vert_c = vert_a;
239             let vert_d = vert_b;
240 
241             vert_b[idx] = &ranges[idx][0];
242             vert_c[idx] = &ranges[idx][1];
243 
244             let (vert_a, vert_b) =
245                 if coord.projected_depth(vert_a[0].get_x(), vert_a[1].get_y(), vert_a[2].get_z())
246                     >= coord.projected_depth(
247                         vert_c[0].get_x(),
248                         vert_c[1].get_y(),
249                         vert_c[2].get_z(),
250                     )
251                 {
252                     (vert_a, vert_b)
253                 } else {
254                     (vert_c, vert_d)
255                 };
256 
257             let mut m = vert_a;
258             m[(idx + 1) % 3] = vert_b[(idx + 1) % 3];
259             let mut n = vert_a;
260             n[(idx + 2) % 3] = vert_b[(idx + 2) % 3];
261 
262             (
263                 vec![
264                     Coord3D::build_coord(vert_a),
265                     Coord3D::build_coord(m),
266                     Coord3D::build_coord(vert_b),
267                     Coord3D::build_coord(n),
268                 ],
269                 vert_a,
270                 vert_b,
271             )
272         };
273         self.plotting_area()
274             .draw(&Polygon::new(panel.clone(), panel_style))?;
275         panel.push(panel[0].clone());
276         self.plotting_area()
277             .draw(&PathElement::new(panel, bold_grid_style))?;
278 
279         for (kps, style) in vec![
280             (light_points, light_grid_style),
281             (bold_points, bold_grid_style),
282         ]
283         .into_iter()
284         {
285             for idx in (0..3).filter(|&i| i != idx) {
286                 let kps: Vec<_> = match idx {
287                     0 => kps.x_points.iter().map(|x| Coord3D::X(x.clone())).collect(),
288                     1 => kps.y_points.iter().map(|y| Coord3D::Y(y.clone())).collect(),
289                     _ => kps.z_points.iter().map(|z| Coord3D::Z(z.clone())).collect(),
290                 };
291                 for kp in kps.iter() {
292                     let mut kp_start = start;
293                     let mut kp_end = end;
294                     kp_start[idx] = kp;
295                     kp_end[idx] = kp;
296                     self.plotting_area().draw(&PathElement::new(
297                         vec![Coord3D::build_coord(kp_start), Coord3D::build_coord(kp_end)],
298                         style,
299                     ))?;
300                 }
301             }
302         }
303 
304         Ok([
305             [start[0].clone(), start[1].clone(), start[2].clone()],
306             [end[0].clone(), end[1].clone(), end[2].clone()],
307         ])
308     }
309 }
310