viewport/
lib.rs

1#![deny(missing_docs)]
2
3//! A library for storing viewport information
4
5extern crate float;
6
7use float::{Float, FromPrimitive};
8
9/// Stores viewport information.
10#[derive(Debug, Copy, Clone, PartialEq)]
11pub struct Viewport {
12    /// Viewport in pixels.
13    /// ```[x, y, width, height]``` where ```(x, y)``` is lower left corner.
14    pub rect: [i32; 4],
15    /// The size of frame buffer in pixels.
16    pub draw_size: [u32; 2],
17    /// The size of window in points.
18    pub window_size: [f64; 2],
19}
20
21impl Viewport {
22    /// Computes absolute transform for 2D graphics,
23    /// which uses a row major 2x3 matrix.
24    ////
25    /// The origin is in the upper left corner of the viewport rectangle.
26    /// The x-axis points to the right, and the y-axis points down.
27    /// The units are in points (window coordinates).
28    ///
29    /// It is assumed that the underlying coordinate system is normalized
30    /// with the origin in the center, such that ```(-1.0, 1.0)``` in the
31    /// underlying coordinate system corresponds to the
32    /// upper left corner of the viewport.
33    #[must_use]
34    pub fn abs_transform<T: Float>(&self) -> [[T; 3]; 2] {
35        let (dw, dh) = (f64::from(self.draw_size[0]), f64::from(self.draw_size[1]));
36        let sx = 2.0 * (dw / self.window_size[0]) / f64::from(self.rect[2]);
37        let sy = -2.0 * (dh / self.window_size[1]) / f64::from(self.rect[3]);
38        let f = |x| FromPrimitive::from_f64(x);
39        [
40            [f(sx), f(0.0), f(-1.0)],
41            [f(0.0), f(sy), f(1.0)]
42        ]
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn abs() {
52        let transform = |mat: [[f64; 3]; 2], (px, py)| -> (f64, f64) {
53            (mat[0][0] * px + mat[0][1] * py + mat[0][2],
54            mat[1][0] * px + mat[1][1] * py + mat[1][2])
55         };
56
57        let viewport = Viewport {
58            rect: [0, 0, 100, 200],
59            draw_size: [100, 200],
60            window_size: [100.0, 200.0],
61        };
62        let abs = viewport.abs_transform();
63        assert_eq!(abs, [
64                [0.02, 0.0, -1.0],
65                [0.0, -0.01, 1.0]
66            ]);
67        assert_eq!(transform(abs, (0.0, 0.0)), (-1.0, 1.0));
68        assert_eq!(transform(abs, (50.0, 100.0)), (0.0, 0.0));
69        assert_eq!(transform(abs, (100.0, 200.0)), (1.0, -1.0));
70
71        let viewport = Viewport {
72            rect: [10, 10, 80, 80],
73            draw_size: [100, 100],
74            window_size: [50.0, 50.0],
75        };
76        let abs = viewport.abs_transform();
77        assert_eq!(abs, [
78                [0.05, 0.0, -1.0],
79                [0.0, -0.05, 1.0]
80            ]);
81        assert_eq!(transform(abs, (0.0, 0.0)), (-1.0, 1.0));
82        // The viewport size is 80 pixels, 40 points, therefore center is 20.
83        assert_eq!(transform(abs, (20.0, 20.0)), (0.0, 0.0));
84        assert_eq!(transform(abs, (40.0, 40.0)), (1.0, -1.0));
85    }
86}