plotters/chart/context/cartesian3d/
draw_impl.rs

1use std::cmp::Ordering;
2
3use plotters_backend::DrawingBackend;
4
5use crate::chart::ChartContext;
6use crate::coord::{
7    cartesian::Cartesian3d,
8    ranged1d::{KeyPointHint, Ranged},
9    CoordTranslate,
10};
11use crate::drawing::DrawingAreaErrorKind;
12use crate::element::{EmptyElement, PathElement, Polygon, Text};
13use crate::style::{
14    text_anchor::{HPos, Pos, VPos},
15    ShapeStyle, TextStyle,
16};
17
18use super::Coord3D;
19
20pub(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
26impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
27where
28    DB: DrawingBackend,
29    X::ValueType: Clone,
30    Y::ValueType: Clone,
31    Z::ValueType: Clone,
32{
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)]
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)]
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)]
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)]
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}