1 use super::{ProjectionMatrix, ProjectionMatrixBuilder};
2 use crate::coord::ranged1d::Ranged;
3 use crate::coord::CoordTranslate;
4 use plotters_backend::BackendCoord;
5 
6 use std::ops::Range;
7 
8 /// A 3D cartesian coordinate system
9 #[derive(Clone)]
10 pub struct Cartesian3d<X: Ranged, Y: Ranged, Z: Ranged> {
11     pub(crate) logic_x: X,
12     pub(crate) logic_y: Y,
13     pub(crate) logic_z: Z,
14     coord_size: (i32, i32, i32),
15     projection: ProjectionMatrix,
16 }
17 
18 impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
compute_default_size(actual_x: Range<i32>, actual_y: Range<i32>) -> i3219     fn compute_default_size(actual_x: Range<i32>, actual_y: Range<i32>) -> i32 {
20         (actual_x.end - actual_x.start).min(actual_y.end - actual_y.start) * 4 / 5
21     }
create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( actual_x: Range<i32>, actual_y: Range<i32>, coord_size: (i32, i32, i32), f: F, ) -> ProjectionMatrix22     fn create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
23         actual_x: Range<i32>,
24         actual_y: Range<i32>,
25         coord_size: (i32, i32, i32),
26         f: F,
27     ) -> ProjectionMatrix {
28         let center_3d = (coord_size.0 / 2, coord_size.1 / 2, coord_size.2 / 2);
29         let center_2d = (
30             (actual_x.end + actual_x.start) / 2,
31             (actual_y.end + actual_y.start) / 2,
32         );
33         let mut pb = ProjectionMatrixBuilder::new();
34         pb.set_pivot(center_3d, center_2d);
35         f(pb)
36     }
37     /// Creates a Cartesian3d object with the given projection.
with_projection< SX: Into<X>, SY: Into<Y>, SZ: Into<Z>, F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix, >( logic_x: SX, logic_y: SY, logic_z: SZ, (actual_x, actual_y): (Range<i32>, Range<i32>), build_projection_matrix: F, ) -> Self38     pub fn with_projection<
39         SX: Into<X>,
40         SY: Into<Y>,
41         SZ: Into<Z>,
42         F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix,
43     >(
44         logic_x: SX,
45         logic_y: SY,
46         logic_z: SZ,
47         (actual_x, actual_y): (Range<i32>, Range<i32>),
48         build_projection_matrix: F,
49     ) -> Self {
50         let default_size = Self::compute_default_size(actual_x.clone(), actual_y.clone());
51         let coord_size = (default_size, default_size, default_size);
52         Self {
53             logic_x: logic_x.into(),
54             logic_y: logic_y.into(),
55             logic_z: logic_z.into(),
56             coord_size,
57             projection: Self::create_projection(
58                 actual_x,
59                 actual_y,
60                 coord_size,
61                 build_projection_matrix,
62             ),
63         }
64     }
65 
66     /// Sets the pixel sizes and projections according to the given ranges.
set_coord_pixel_range( &mut self, actual_x: Range<i32>, actual_y: Range<i32>, coord_size: (i32, i32, i32), ) -> &mut Self67     pub fn set_coord_pixel_range(
68         &mut self,
69         actual_x: Range<i32>,
70         actual_y: Range<i32>,
71         coord_size: (i32, i32, i32),
72     ) -> &mut Self {
73         self.coord_size = coord_size;
74         self.projection =
75             Self::create_projection(actual_x, actual_y, coord_size, |pb| pb.into_matrix());
76         self
77     }
78 
79     /// Set the projection matrix
set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( &mut self, actual_x: Range<i32>, actual_y: Range<i32>, f: F, ) -> &mut Self80     pub fn set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
81         &mut self,
82         actual_x: Range<i32>,
83         actual_y: Range<i32>,
84         f: F,
85     ) -> &mut Self {
86         self.projection = Self::create_projection(actual_x, actual_y, self.coord_size, f);
87         self
88     }
89 
90     /// Create a new coordinate
new<SX: Into<X>, SY: Into<Y>, SZ: Into<Z>>( logic_x: SX, logic_y: SY, logic_z: SZ, (actual_x, actual_y): (Range<i32>, Range<i32>), ) -> Self91     pub fn new<SX: Into<X>, SY: Into<Y>, SZ: Into<Z>>(
92         logic_x: SX,
93         logic_y: SY,
94         logic_z: SZ,
95         (actual_x, actual_y): (Range<i32>, Range<i32>),
96     ) -> Self {
97         Self::with_projection(logic_x, logic_y, logic_z, (actual_x, actual_y), |pb| {
98             pb.into_matrix()
99         })
100     }
101     /// Get the projection matrix
projection(&self) -> &ProjectionMatrix102     pub fn projection(&self) -> &ProjectionMatrix {
103         &self.projection
104     }
105 
106     /// Do not project, only transform the guest coordinate system
map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32)107     pub fn map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32) {
108         (
109             self.logic_x.map(x, (0, self.coord_size.0)),
110             self.logic_y.map(y, (0, self.coord_size.1)),
111             self.logic_z.map(z, (0, self.coord_size.2)),
112         )
113     }
114 
115     /// Get the depth of the projection
projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i32116     pub fn projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i32 {
117         self.projection.projected_depth(self.map_3d(x, y, z))
118     }
119 }
120 
121 impl<X: Ranged, Y: Ranged, Z: Ranged> CoordTranslate for Cartesian3d<X, Y, Z> {
122     type From = (X::ValueType, Y::ValueType, Z::ValueType);
translate(&self, coord: &Self::From) -> BackendCoord123     fn translate(&self, coord: &Self::From) -> BackendCoord {
124         let pixel_coord_3d = self.map_3d(&coord.0, &coord.1, &coord.2);
125         self.projection * pixel_coord_3d
126     }
127 
depth(&self, coord: &Self::From) -> i32128     fn depth(&self, coord: &Self::From) -> i32 {
129         self.projected_depth(&coord.0, &coord.1, &coord.2)
130     }
131 }
132