1 use crate::chart::{axes3d::Axes3dStyle, ChartContext};
2 use crate::coord::{
3     cartesian::Cartesian3d,
4     ranged1d::{Ranged, ValueFormatter},
5     ranged3d::{ProjectionMatrix, ProjectionMatrixBuilder},
6 };
7 use plotters_backend::DrawingBackend;
8 
9 mod draw_impl;
10 
11 #[derive(Clone, Debug)]
12 pub(crate) enum Coord3D<X, Y, Z> {
13     X(X),
14     Y(Y),
15     Z(Z),
16 }
17 
18 impl<X, Y, Z> Coord3D<X, Y, Z> {
get_x(&self) -> &X19     fn get_x(&self) -> &X {
20         match self {
21             Coord3D::X(ret) => ret,
22             _ => panic!("Invalid call!"),
23         }
24     }
get_y(&self) -> &Y25     fn get_y(&self) -> &Y {
26         match self {
27             Coord3D::Y(ret) => ret,
28             _ => panic!("Invalid call!"),
29         }
30     }
get_z(&self) -> &Z31     fn get_z(&self) -> &Z {
32         match self {
33             Coord3D::Z(ret) => ret,
34             _ => panic!("Invalid call!"),
35         }
36     }
37 
build_coord([x, y, z]: [&Self; 3]) -> (X, Y, Z) where X: Clone, Y: Clone, Z: Clone,38     fn build_coord([x, y, z]: [&Self; 3]) -> (X, Y, Z)
39     where
40         X: Clone,
41         Y: Clone,
42         Z: Clone,
43     {
44         (x.get_x().clone(), y.get_y().clone(), z.get_z().clone())
45     }
46 }
47 
48 impl<'a, DB, X, Y, Z, XT, YT, ZT> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
49 where
50     DB: DrawingBackend,
51     X: Ranged<ValueType = XT> + ValueFormatter<XT>,
52     Y: Ranged<ValueType = YT> + ValueFormatter<YT>,
53     Z: Ranged<ValueType = ZT> + ValueFormatter<ZT>,
54 {
55     /**
56     Create an axis configuration object, to set line styles, labels, sizes, etc.
57 
58     Default values for axis configuration are set by function `Axes3dStyle::new()`.
59 
60     # Example
61 
62     ```
63     use plotters::prelude::*;
64     let drawing_area = SVGBackend::new("configure_axes.svg", (300, 200)).into_drawing_area();
65     drawing_area.fill(&WHITE).unwrap();
66     let mut chart_builder = ChartBuilder::on(&drawing_area);
67     let mut chart_context = chart_builder.margin_bottom(30).build_cartesian_3d(0.0..4.0, 0.0..3.0, 0.0..2.7).unwrap();
68     chart_context.configure_axes().tick_size(8).x_labels(4).y_labels(3).z_labels(2)
69         .max_light_lines(5).axis_panel_style(GREEN.mix(0.1)).bold_grid_style(BLUE.mix(0.3))
70         .light_grid_style(BLUE.mix(0.2)).label_style(("Calibri", 10))
71         .x_formatter(&|x| format!("x={x}")).draw().unwrap();
72     ```
73 
74     The resulting chart reflects the customizations specified through `configure_axes()`:
75 
76     ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@4c3cef4/apidoc/configure_axes.svg)
77 
78     All these customizations are `Axes3dStyle` methods.
79 
80     In the chart, `tick_size(8)` produces tick marks 8 pixels long. You can use
81     `(5u32).percent().max(5).in_pixels(chart.plotting_area()` to tell Plotters to calculate the tick mark
82     size as a percentage of the dimensions of the figure. See [`crate::style::RelativeSize`] and
83     [`crate::style::SizeDesc`] for more information.
84 
85     `x_labels(4)` specifies a maximum of 4
86     tick marks and labels in the X axis. `max_light_lines(5)` specifies a maximum of 5 minor grid lines
87     between any two tick marks. `axis_panel_style(GREEN.mix(0.1))` specifies the style of the panels in
88     the background, a light green color. `bold_grid_style(BLUE.mix(0.3))` and `light_grid_style(BLUE.mix(0.2))`
89     specify the style of the major and minor grid lines, respectively. `label_style()` specifies the text
90     style of the axis labels, and `x_formatter(|x| format!("x={x}"))` specifies the string format of the X
91     axis labels.
92 
93     # See also
94 
95     [`ChartContext::configure_mesh()`], a similar function for 2D plots
96     */
configure_axes(&mut self) -> Axes3dStyle<'a, '_, X, Y, Z, DB>97     pub fn configure_axes(&mut self) -> Axes3dStyle<'a, '_, X, Y, Z, DB> {
98         Axes3dStyle::new(self)
99     }
100 }
101 
102 impl<'a, DB, X: Ranged, Y: Ranged, Z: Ranged> ChartContext<'a, DB, Cartesian3d<X, Y, Z>>
103 where
104     DB: DrawingBackend,
105 {
106     /// Override the 3D projection matrix. This function allows to override the default projection
107     /// matrix.
108     /// - `pf`: A function that takes the default projection matrix configuration and returns the
109     /// projection matrix. This function will allow you to adjust the pitch, yaw angle and the
110     /// centeral point of the projection, etc. You can also build a projection matrix which is not
111     /// relies on the default configuration as well.
with_projection<P: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( &mut self, pf: P, ) -> &mut Self112     pub fn with_projection<P: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
113         &mut self,
114         pf: P,
115     ) -> &mut Self {
116         let (actual_x, actual_y) = self.drawing_area.get_pixel_range();
117         self.drawing_area
118             .as_coord_spec_mut()
119             .set_projection(actual_x, actual_y, pf);
120         self
121     }
122     /// Sets the 3d coordinate pixel range.
set_3d_pixel_range(&mut self, size: (i32, i32, i32)) -> &mut Self123     pub fn set_3d_pixel_range(&mut self, size: (i32, i32, i32)) -> &mut Self {
124         let (actual_x, actual_y) = self.drawing_area.get_pixel_range();
125         self.drawing_area
126             .as_coord_spec_mut()
127             .set_coord_pixel_range(actual_x, actual_y, size);
128         self
129     }
130 }
131