1use super::{ProjectionMatrix, ProjectionMatrixBuilder};
2use crate::coord::ranged1d::Ranged;
3use crate::coord::CoordTranslate;
4use plotters_backend::BackendCoord;
5
6use std::ops::Range;
7
8pub 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
17impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
18 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 }
21 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 }
36 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
64 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 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 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 pub fn projection(&self) -> &ProjectionMatrix {
100 &self.projection
101 }
102
103 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 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
118impl<X: Ranged, Y: Ranged, Z: Ranged> CoordTranslate for Cartesian3d<X, Y, Z> {
119 type From = (X::ValueType, Y::ValueType, Z::ValueType);
120 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
125 fn depth(&self, coord: &Self::From) -> i32 {
126 self.projected_depth(&coord.0, &coord.1, &coord.2)
127 }
128}