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 pub struct Cartesian3d<X: Ranged, Y: Ranged, Z: Ranged> {
10     pub(crate) logic_x: X,
11     pub(crate) logic_y: Y,
12     pub(crate) logic_z: Z,
13     coord_size: (i32, i32, i32),
14     projection: ProjectionMatrix,
15 }
16 
17 impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
compute_default_size(actual_x: Range<i32>, actual_y: Range<i32>) -> i3218     fn compute_default_size(actual_x: Range<i32>, actual_y: Range<i32>) -> i32 {
19         (actual_x.end - actual_x.start).min(actual_y.end - actual_y.start) * 4 / 5
20     }
create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( actual_x: Range<i32>, actual_y: Range<i32>, f: F, ) -> ProjectionMatrix21     fn create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
22         actual_x: Range<i32>,
23         actual_y: Range<i32>,
24         f: F,
25     ) -> ProjectionMatrix {
26         let default_size = Self::compute_default_size(actual_x.clone(), actual_y.clone());
27         let center_3d = (default_size / 2, default_size / 2, default_size / 2);
28         let center_2d = (
29             (actual_x.end + actual_x.start) / 2,
30             (actual_y.end + actual_y.start) / 2,
31         );
32         let mut pb = ProjectionMatrixBuilder::new();
33         pb.set_pivot(center_3d, center_2d);
34         f(pb)
35     }
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, ) -> Self36     pub fn with_projection<
37         SX: Into<X>,
38         SY: Into<Y>,
39         SZ: Into<Z>,
40         F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix,
41     >(
42         logic_x: SX,
43         logic_y: SY,
44         logic_z: SZ,
45         (actual_x, actual_y): (Range<i32>, Range<i32>),
46         build_projection_matrix: F,
47     ) -> Self {
48         let default_size = Self::compute_default_size(actual_x.clone(), actual_y.clone());
49         Self {
50             logic_x: logic_x.into(),
51             logic_y: logic_y.into(),
52             logic_z: logic_z.into(),
53             coord_size: (default_size, default_size, default_size),
54             projection: Self::create_projection(actual_x, actual_y, build_projection_matrix),
55         }
56     }
57     /// Set the projection matrix
set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( &mut self, actual_x: Range<i32>, actual_y: Range<i32>, f: F, ) -> &mut Self58     pub fn set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
59         &mut self,
60         actual_x: Range<i32>,
61         actual_y: Range<i32>,
62         f: F,
63     ) -> &mut Self {
64         self.projection = Self::create_projection(actual_x, actual_y, f);
65         self
66     }
67 
68     /// 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>), ) -> Self69     pub fn new<SX: Into<X>, SY: Into<Y>, SZ: Into<Z>>(
70         logic_x: SX,
71         logic_y: SY,
72         logic_z: SZ,
73         (actual_x, actual_y): (Range<i32>, Range<i32>),
74     ) -> Self {
75         Self::with_projection(logic_x, logic_y, logic_z, (actual_x, actual_y), |pb| {
76             pb.into_matrix()
77         })
78     }
79     /// Get the projection matrix
projection(&self) -> &ProjectionMatrix80     pub fn projection(&self) -> &ProjectionMatrix {
81         &self.projection
82     }
83 
84     /// Do not project, only transform the guest coordinate system
map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32)85     pub fn map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32) {
86         (
87             self.logic_x.map(x, (0, self.coord_size.0)),
88             self.logic_y.map(y, (0, self.coord_size.1)),
89             self.logic_z.map(z, (0, self.coord_size.2)),
90         )
91     }
92 
93     /// Get the depth of the projection
projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i3294     pub fn projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i32 {
95         self.projection.projected_depth(self.map_3d(x, y, z))
96     }
97 }
98 
99 impl<X: Ranged, Y: Ranged, Z: Ranged> CoordTranslate for Cartesian3d<X, Y, Z> {
100     type From = (X::ValueType, Y::ValueType, Z::ValueType);
translate(&self, coord: &Self::From) -> BackendCoord101     fn translate(&self, coord: &Self::From) -> BackendCoord {
102         let pixel_coord_3d = self.map_3d(&coord.0, &coord.1, &coord.2);
103         self.projection * pixel_coord_3d
104     }
105 }
106