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>, coord_size: (i32, i32, i32), f: F, ) -> ProjectionMatrix21     fn create_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
22         actual_x: Range<i32>,
23         actual_y: Range<i32>,
24         coord_size: (i32, i32, i32),
25         f: F,
26     ) -> ProjectionMatrix {
27         let center_3d = (coord_size.0 / 2, coord_size.1 / 2, coord_size.2 / 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         let coord_size = (default_size, default_size, default_size);
50         Self {
51             logic_x: logic_x.into(),
52             logic_y: logic_y.into(),
53             logic_z: logic_z.into(),
54             coord_size,
55             projection: Self::create_projection(
56                 actual_x,
57                 actual_y,
58                 coord_size,
59                 build_projection_matrix,
60             ),
61         }
62     }
63 
set_coord_pixel_range( &mut self, actual_x: Range<i32>, actual_y: Range<i32>, coord_size: (i32, i32, i32), ) -> &mut Self64     pub fn set_coord_pixel_range(
65         &mut self,
66         actual_x: Range<i32>,
67         actual_y: Range<i32>,
68         coord_size: (i32, i32, i32),
69     ) -> &mut Self {
70         self.coord_size = coord_size;
71         self.projection =
72             Self::create_projection(actual_x, actual_y, coord_size, |pb| pb.into_matrix());
73         self
74     }
75 
76     /// Set the projection matrix
set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>( &mut self, actual_x: Range<i32>, actual_y: Range<i32>, f: F, ) -> &mut Self77     pub fn set_projection<F: FnOnce(ProjectionMatrixBuilder) -> ProjectionMatrix>(
78         &mut self,
79         actual_x: Range<i32>,
80         actual_y: Range<i32>,
81         f: F,
82     ) -> &mut Self {
83         self.projection = Self::create_projection(actual_x, actual_y, self.coord_size, f);
84         self
85     }
86 
87     /// 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>), ) -> Self88     pub fn new<SX: Into<X>, SY: Into<Y>, SZ: Into<Z>>(
89         logic_x: SX,
90         logic_y: SY,
91         logic_z: SZ,
92         (actual_x, actual_y): (Range<i32>, Range<i32>),
93     ) -> Self {
94         Self::with_projection(logic_x, logic_y, logic_z, (actual_x, actual_y), |pb| {
95             pb.into_matrix()
96         })
97     }
98     /// Get the projection matrix
projection(&self) -> &ProjectionMatrix99     pub fn projection(&self) -> &ProjectionMatrix {
100         &self.projection
101     }
102 
103     /// Do not project, only transform the guest coordinate system
map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32)104     pub fn map_3d(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> (i32, i32, i32) {
105         (
106             self.logic_x.map(x, (0, self.coord_size.0)),
107             self.logic_y.map(y, (0, self.coord_size.1)),
108             self.logic_z.map(z, (0, self.coord_size.2)),
109         )
110     }
111 
112     /// Get the depth of the projection
projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i32113     pub fn projected_depth(&self, x: &X::ValueType, y: &Y::ValueType, z: &Z::ValueType) -> i32 {
114         self.projection.projected_depth(self.map_3d(x, y, z))
115     }
116 }
117 
118 impl<X: Ranged, Y: Ranged, Z: Ranged> CoordTranslate for Cartesian3d<X, Y, Z> {
119     type From = (X::ValueType, Y::ValueType, Z::ValueType);
translate(&self, coord: &Self::From) -> BackendCoord120     fn translate(&self, coord: &Self::From) -> BackendCoord {
121         let pixel_coord_3d = self.map_3d(&coord.0, &coord.1, &coord.2);
122         self.projection * pixel_coord_3d
123     }
124 
depth(&self, coord: &Self::From) -> i32125     fn depth(&self, coord: &Self::From) -> i32 {
126         self.projected_depth(&coord.0, &coord.1, &coord.2)
127     }
128 }
129