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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
//! Transformation module
//!
//! Useful to turn on-screen coordinates into measurements of the "real" world the map describes
//! and the other way around.
use crate::math::{Rect, Vec2};
const STANDARD_PIXELS_PER_M: f64 = 64.;
const MIN_PIXELS_PER_M: f64 = 5.;
const MAX_PIXELS_PER_M: f64 = 10_000.;
pub struct Transform {
/// The (not necessarily natural) number of pixels per m, i.e. the current scale of the map
pixels_per_m: f64,
/// The vector the entire on-screen map is moved by in pixels
translation_px: Vec2<f64>,
}
impl Transform {
/// Create a new standard transformation for the map.
pub fn new() -> Self {
Self {
pixels_per_m: STANDARD_PIXELS_PER_M,
translation_px: Vec2::new(0., 0.),
}
}
/// Convert a point that is given in meters into the corresponding point in pixels.
#[inline]
pub fn point_m_to_px(&self, point: &Vec2<f64>) -> Vec2<f64> {
// Start by converting the absolute position in meters into the absolute position in
// pixels, then add the translation of the screen.
(*point * self.pixels_per_m) + self.translation_px
}
/// Convert an on-screen point into an absolute point with values in meters.
#[inline]
pub fn point_px_to_m(&self, point: &Vec2<f64>) -> Vec2<f64> {
// Start by subtracting the pixel translation and afterwards convert these absolute pixel
// measurements into meters.
(*point - self.translation_px) / self.pixels_per_m
}
/// Convert a length given in meters into a length in pixels
#[inline]
pub fn length_m_to_px(&self, length: f64) -> f64 {
length * self.pixels_per_m
}
/// Convert a length given in pixels into a length in meters
#[inline]
pub fn length_px_to_m(&self, length: f64) -> f64 {
length / self.pixels_per_m
}
/// Convert a rectangle which has measurements in meters into one of pixels
#[inline]
pub fn rect_m_to_px(&self, rect: &Rect<f64>) -> Rect<f64> {
let left_upper = self.point_m_to_px(&Vec2::new(rect.x, rect.y));
Rect::new(
left_upper.x,
left_upper.y,
self.length_m_to_px(rect.w),
self.length_m_to_px(rect.h),
)
}
/// Convert a rectangle which has measurements in pixels into one of meters
#[inline]
pub fn rect_px_to_m(&self, rect: &Rect<f64>) -> Rect<f64> {
let left_upper = self.point_px_to_m(&Vec2::new(rect.x, rect.y));
Rect::new(
left_upper.x,
left_upper.y,
self.length_px_to_m(rect.w),
self.length_px_to_m(rect.h),
)
}
/// Attempts to zoom the pixels per meter by the amount of factor.
///
/// # Arguments
/// `factor`: A number greater than one means zooming in, a number less than one means zooming out. What happens when you try to
/// zoom with a negative factor you'll have to figure out yourself.
/// `mouse_pos_px`: Position of the mouse cursor, this time not in meters, but in screen
/// pixels. This will be used to tether zoom on that point.
pub fn try_zoom(&mut self, mouse_pos_px: &Vec2<f64>, factor: f64) -> bool {
// Abort zooming when the scale would not be in the min-max-bounds anymore.
let desired_px_per_m = self.pixels_per_m * factor;
if (factor < 1. && desired_px_per_m <= MIN_PIXELS_PER_M)
|| (factor > 1. && desired_px_per_m >= MAX_PIXELS_PER_M)
{
return false;
}
// Save the absolute mouse position in meters for tethering later
let mouse_pos_m = self.point_px_to_m(&mouse_pos_px);
// Make sure the desired scale stays within the bounds and in whole numbers
let desired_px_per_m = if desired_px_per_m < MIN_PIXELS_PER_M {
MIN_PIXELS_PER_M as u32 as f64
} else if desired_px_per_m > MAX_PIXELS_PER_M {
MAX_PIXELS_PER_M as u32 as f64
} else {
desired_px_per_m as u32 as f64
};
/* Adjust to the desired scale and bring the map back to its desired position according to
* the mouse pointer position.
*/
self.pixels_per_m = desired_px_per_m;
self.translation_px += *mouse_pos_px - self.point_m_to_px(&mouse_pos_m);
true
}
/// Move the canvas by the vector in pixels.
pub fn move_by_px(&mut self, by: &Vec2<f64>) {
self.translation_px += *by;
}
pub fn pixels_per_m(&self) -> f64 {
self.pixels_per_m
}
pub fn translation_px(&self) -> &Vec2<f64> {
&self.translation_px
}
}
impl Default for Transform {
fn default() -> Self {
Self::new()
}
}
|