aboutsummaryrefslogtreecommitdiff
path: root/src/transform.rs
diff options
context:
space:
mode:
authorArne Dußin2020-10-29 02:40:39 +0100
committerArne Dußin2020-10-29 02:40:39 +0100
commit771f94fb2383176bcd3faced3edb7daa044065eb (patch)
tree3da49330f82d2cd8ff564a4d868508f6a62d7fe5 /src/transform.rs
parent8237855202d58e28e912a83e612397b4300a3f3b (diff)
downloadgraf_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.rs130
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
+ }
+}