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
9#[derive(Clone)]
10pub 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
18impl<X: Ranged, Y: Ranged, Z: Ranged> Cartesian3d<X, Y, Z> {
19    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    }
22    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.
38    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.
67    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
80    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
91    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
102    pub fn projection(&self) -> &ProjectionMatrix {
103        &self.projection
104    }
105
106    /// Do not project, only transform the guest coordinate system
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
116    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
121impl<X: Ranged, Y: Ranged, Z: Ranged> CoordTranslate for Cartesian3d<X, Y, Z> {
122    type From = (X::ValueType, Y::ValueType, Z::ValueType);
123    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
128    fn depth(&self, coord: &Self::From) -> i32 {
129        self.projected_depth(&coord.0, &coord.1, &coord.2)
130    }
131}