diff options
| author | Arne Dußin | 2020-10-29 02:40:39 +0100 |
|---|---|---|
| committer | Arne Dußin | 2020-10-29 02:40:39 +0100 |
| commit | 771f94fb2383176bcd3faced3edb7daa044065eb (patch) | |
| tree | 3da49330f82d2cd8ff564a4d868508f6a62d7fe5 /src/transform.rs | |
| parent | 8237855202d58e28e912a83e612397b4300a3f3b (diff) | |
| download | graf_karto-771f94fb2383176bcd3faced3edb7daa044065eb.tar.gz graf_karto-771f94fb2383176bcd3faced3edb7daa044065eb.zip | |
Make rectangles zoom together with the grid
Diffstat (limited to 'src/transform.rs')
| -rw-r--r-- | src/transform.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/transform.rs b/src/transform.rs new file mode 100644 index 0000000..ef94029 --- /dev/null +++ b/src/transform.rs @@ -0,0 +1,130 @@ +//! Transformation module +//! +//! Useful to turn on-screen coordinates into the measurements in the "real" world the map +//! describes and the other way around. + +use piston_window::math; + +const STANDARD_PIXELS_PER_M: f64 = 64.; +const MIN_PIXELS_PER_M: f64 = 0.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: [f64; 2], +} + +impl Transform { + /// Create a new standard transformation for the map. + pub fn new() -> Self { + Self { + pixels_per_m: STANDARD_PIXELS_PER_M, + translation_px: [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: [f64; 2]) -> [f64; 2] { + // Start by converting the absolute position in meters into the absolute position in + // pixels, then add the translation of the screen. + math::add( + math::mul_scalar(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: [f64; 2]) -> [f64; 2] { + // Start by subtracting the pixel translation and afterwards convert these absolute pixel + // measurements into meters. + math::mul_scalar( + math::sub(point, self.translation_px), + 1. / 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: [f64; 4]) -> [f64; 4] { + let left_upper = self.point_m_to_px([rect[0], rect[1]]); + [ + left_upper[0], + left_upper[1], + self.length_m_to_px(rect[2]), + self.length_m_to_px(rect[3]), + ] + } + + /// Convert a rectangle which has measurements in pixels into one of meters + #[inline] + pub fn rect_px_to_m(&self, rect: [f64; 4]) -> [f64; 4] { + let left_upper = self.point_px_to_m([rect[0], rect[1]]); + [ + left_upper[0], + left_upper[1], + self.length_px_to_m(rect[2]), + self.length_px_to_m(rect[3]), + ] + } + + /* Helper function to make sure the standard zoom factor is always exact. This helps + * normalising zoom levels even when the user zooms in and out extremely often, assuming they + * pass the standard zoom factor. + */ + fn normalise_zoom(&mut self) { + if self.pixels_per_m > STANDARD_PIXELS_PER_M - 5. + && self.pixels_per_m < STANDARD_PIXELS_PER_M + 5. + { + self.pixels_per_m = STANDARD_PIXELS_PER_M; + } + } + + /// Attempts to zoom in a step and return true. + /// If the maximum zoom is reached, this function changes nothing and returns false. + pub fn try_zoom_in(&mut self) -> bool { + // TODO: Zoom in on mouse pointer position + if self.pixels_per_m < MAX_PIXELS_PER_M { + self.pixels_per_m *= 1.2; + self.normalise_zoom(); + true + } else { + false + } + } + + /// Attempts to zoom out a step and return true. + /// If the minimum zoom is reached, this function changes nothing and returns false. + pub fn try_zoom_out(&mut self) -> bool { + // TODO: Zoom out at mouse pointer position + if self.pixels_per_m > MIN_PIXELS_PER_M { + self.pixels_per_m /= 1.2; + self.normalise_zoom(); + true + } else { + false + } + } + + pub fn pixels_per_m(&self) -> f64 { + self.pixels_per_m + } + pub fn translation_px(&self) -> [f64; 2] { + self.translation_px + } +} |
