graphics/
context.rs

1//! Transformation context
2
3use crate::{
4    math::{abs_transform, get_scale, identity, Matrix2d, Scalar, Vec2d},
5    DrawState, Viewport,
6};
7
8/// Drawing 2d context.
9#[derive(Copy, Clone)]
10pub struct Context {
11    /// Viewport information.
12    pub viewport: Option<Viewport>,
13    /// View transformation.
14    pub view: Matrix2d,
15    /// Current transformation.
16    pub transform: Matrix2d,
17    /// Current draw state settings.
18    pub draw_state: DrawState,
19}
20
21impl Context {
22    /// Creates a new drawing context.
23    #[inline(always)]
24    pub fn new() -> Context {
25        Context {
26            view: identity(),
27            transform: identity(),
28            draw_state: Default::default(),
29            viewport: None,
30        }
31    }
32
33    /// Creates a new context with absolute transform in point coordinates.
34    ///
35    /// This function assumes the default coordinate system
36    /// being centered with x axis pointing to the right
37    /// and y axis pointing up.
38    ///
39    /// Returns a drawing context
40    /// with origin in the upper left corner
41    /// and x axis pointing to the right
42    /// and y axis pointing down.
43    #[inline(always)]
44    pub fn new_viewport(viewport: Viewport) -> Context {
45        let mat = viewport.abs_transform();
46        Context {
47            view: mat,
48            transform: mat,
49            draw_state: Default::default(),
50            viewport: Some(viewport),
51        }
52    }
53
54    /// Creates a new drawing context in absolute coordinates.
55    ///
56    /// This function assumes the default coordinate system
57    /// being centered with x axis pointing to the right
58    /// and y axis pointing up.
59    ///
60    /// Returns a drawing context
61    /// with origin in the upper left corner
62    /// and x axis pointing to the right
63    /// and y axis pointing down.
64    #[inline(always)]
65    pub fn new_abs(w: Scalar, h: Scalar) -> Context {
66        let mat = abs_transform(w, h);
67        Context {
68            view: mat,
69            transform: mat,
70            draw_state: Default::default(),
71            viewport: None,
72        }
73    }
74
75    /// Moves the current transform to the view coordinate system.
76    ///
77    /// This is usually [0.0, 0.0] in the upper left corner
78    /// with the x axis pointing to the right
79    /// and the y axis pointing down.
80    #[inline(always)]
81    pub fn view(mut self) -> Self {
82        self.transform = self.view;
83        self
84    }
85
86    /// Moves the current transform to the default coordinate system.
87    ///
88    /// This is usually [0.0, 0.0] in the center
89    /// with the x axis pointing to the right
90    /// and the y axis pointing up.
91    #[inline(always)]
92    pub fn reset(mut self) -> Self {
93        self.transform = identity();
94        self
95    }
96
97    /// Stores the current transform as new view.
98    #[inline(always)]
99    pub fn store_view(mut self) -> Self {
100        self.view = self.transform;
101        self
102    }
103
104    /// Computes the current view size.
105    #[inline(always)]
106    pub fn get_view_size(&self) -> Vec2d {
107        let scale = get_scale(self.view);
108        [2.0 / scale[0], 2.0 / scale[1]]
109    }
110}
111
112impl Default for Context {
113    fn default() -> Context {
114        Context::new()
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use super::Context;
121
122    #[test]
123    fn test_context() {
124        use crate::Transformed;
125
126        let c = Context::new();
127        {
128            let d = c.trans(20.0, 40.0);
129            let d = d.trans(10.0, 10.0);
130            let transform = d.transform;
131            assert_eq!(transform[0][2], 30.0);
132            assert_eq!(transform[1][2], 50.0);
133        }
134
135        let transform = c.transform;
136        assert_eq!(transform[0][2], 0.0);
137        assert_eq!(transform[1][2], 0.0);
138
139        let c = c.rot_deg(90.0);
140        let transform = c.transform;
141        assert!((transform[0][0] - 0.0).abs() < 0.00001);
142        assert!((transform[0][1] + 1.0).abs() < 0.00001);
143    }
144
145    #[test]
146    fn test_scale() {
147        use crate::Transformed;
148
149        let c = Context::new();
150        let c = c.scale(2.0, 3.0);
151        let transform = c.transform;
152        assert!((transform[0][0] - 2.0).abs() < 0.00001);
153        assert!((transform[1][1] - 3.0).abs() < 0.00001);
154    }
155}