mold2d/
viewport.rs

1use collision::center_point;
2use context::Window;
3use sdl2::rect::Rect;
4
5/// Calculates the origin coordinate for the viewport
6/// given the center coordinate, the canvas coordinate, and the map coordinate
7fn calc_viewport_point(center_coord: f64, window_coord: f64, map_coord: f64) -> f64 {
8    let half = window_coord / 2.0;
9
10    ((center_coord - half).max(0.0))
11        .min((map_coord - window_coord).min((center_coord - half).abs()))
12}
13
14/// Constrains coordinates from an open world into the current window view
15/// This allows for scrolling for levels larger than the current screen
16#[derive(Clone)]
17pub struct Viewport {
18    /// The x value of the center coordinate of the viewport
19    pub x: i32,
20    /// The y value of the center coordinate of the viewport
21    pub y: i32,
22    /// Width and height of the window
23    pub window_dimensions: (i32, i32),
24    /// Width and height of the map
25    pub map_dimensions: (i32, i32),
26}
27
28impl Viewport {
29    pub fn new(window: &Window, map_dimensions: (i32, i32)) -> Viewport {
30        Viewport {
31            x: 0,
32            y: 0,
33            window_dimensions: (window.width as i32, window.height as i32),
34            map_dimensions: map_dimensions,
35        }
36    }
37
38    pub fn set_position(&mut self, new_center: (i32, i32)) {
39        let new_x = calc_viewport_point(new_center.0 as f64,
40                                        self.window_dimensions.0 as f64,
41                                        self.map_dimensions.0 as f64);
42        let new_y = calc_viewport_point(new_center.1 as f64,
43                                        self.window_dimensions.1 as f64,
44                                        self.map_dimensions.1 as f64);
45
46        self.x = new_x as i32;
47        self.y = new_y as i32;
48    }
49
50    /// Returns true if the point is inside the viewport, false otherwise
51    pub fn in_viewport(&self, point: (i32, i32)) -> bool {
52        let margin = 32;
53
54        let (v_min_x, v_max_x) = (self.x - margin, self.x + self.window_dimensions.0);
55        let (v_min_y, v_max_y) = (self.y - margin, self.y + self.window_dimensions.1);
56
57        point.0 >= v_min_x && point.0 <= v_max_x && point.1 >= v_min_y && point.1 <= v_max_y
58    }
59
60    /// Returns the point in the game relative to the viewpoint
61    pub fn relative_point(&self, map_point: (i32, i32)) -> (i32, i32) {
62        (map_point.0 - self.x, map_point.1 - self.y)
63    }
64
65    /// Returns a rectangle in viewport coordinates or None if not in viewport
66    pub fn constrain_to_viewport(&self, rect: &Rect) -> Option<Rect> {
67        let rect_points = [(rect.x(), rect.y()),
68                           (rect.x() + (rect.width() as i32), rect.y()),
69                           (rect.x(), rect.y() + (rect.height() as i32)),
70                           (rect.x() + (rect.width() as i32), rect.y() + (rect.height() as i32))];
71
72        let mut in_viewport = false;
73        for point in rect_points.iter() {
74            if self.in_viewport(*point) {
75                in_viewport = true;
76                break;
77            }
78        }
79
80        if in_viewport {
81            let center = center_point(rect);
82            let (x, y) = self.relative_point((center.0 as i32, center.1 as i32));
83            Some(Rect::new_unwrap(x, y, rect.width(), rect.height()))
84        } else {
85            None
86        }
87    }
88}