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