plotters/coord/ranged3d/
cartesian3d.rs

1use super::{ProjectionMatrix, ProjectionMatrixBuilder};
2use crate::coord::ranged1d::Ranged;
3use crate::coord::CoordTranslate;
4use plotters_backend::BackendCoord;
5
6use std::ops::Range;
7
8/// A 3D cartesian coordinate system
9pub 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    /// Set the projection matrix
77    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
88    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
99    pub fn projection(&self) -> &ProjectionMatrix {
100        &self.projection
101    }
102
103    /// Do not project, only transform the guest coordinate system
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
113    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}