From 4c4b57dc24bc36b3091931c9dcc36f6b1894a017 Mon Sep 17 00:00:00 2001 From: Arne Dußin Date: Fri, 27 Nov 2020 22:55:00 +0100 Subject: Change to f64 as the preferred floating point number --- src/dimension_indicator.rs | 12 +++++----- src/grid.rs | 38 ++++++++----------------------- src/main.rs | 6 ++--- src/map_data.rs | 18 +++++++-------- src/math/mod.rs | 10 ++++++--- src/math/rect.rs | 42 +++++++++++++++++----------------- src/math/vec2.rs | 47 +++++++++++++++++--------------------- src/tool/deletion_tool.rs | 8 +++---- src/tool/icon_tool.rs | 30 ++++++++++++------------- src/tool/polygon_room_tool.rs | 52 +++++++++++++++++++++---------------------- src/tool/room_tool.rs | 8 +++---- src/tool/wall_tool.rs | 16 ++++++------- src/transform.rs | 52 +++++++++++++++++++++---------------------- 13 files changed, 159 insertions(+), 180 deletions(-) (limited to 'src') diff --git a/src/dimension_indicator.rs b/src/dimension_indicator.rs index 2eb3eee..cc12f0b 100644 --- a/src/dimension_indicator.rs +++ b/src/dimension_indicator.rs @@ -6,7 +6,7 @@ use raylib::ffi::Color; pub struct DimensionIndicator { /// The lines that are used to draw the Dimension Indicator. For a rectangle for instance these /// would be two. One for width and one for height. - length_lines: Vec<(Vec2, Vec2)>, + length_lines: Vec<(Vec2, Vec2)>, } impl DimensionIndicator { @@ -17,7 +17,7 @@ impl DimensionIndicator { } } - pub fn from_corner_points(corner_points: &[Vec2]) -> Self { + pub fn from_corner_points(corner_points: &[Vec2]) -> Self { let mut this = Self::new(); this.update_dimensions(corner_points); @@ -30,7 +30,7 @@ impl DimensionIndicator { /// Update the dimensions by analysing a given set of points and adjusting the internal /// (measured) dimensions. - pub fn update_dimensions(&mut self, corner_points: &[Vec2]) { + pub fn update_dimensions(&mut self, corner_points: &[Vec2]) { if corner_points.len() < 2 { warn!("Cannot discern dimensions when not at least two points are given. The dimensions were not updated."); return; @@ -85,8 +85,8 @@ impl DimensionIndicator { }; // To not have the line directly in the rect, move start and end outside a bit. - let start_px = transform.point_m_to_px(*start) + line_normal * 10.; - let end_px = transform.point_m_to_px(*end) + line_normal * 10.; + let start_px = transform.point_m_to_px(start) + line_normal * 10.; + let end_px = transform.point_m_to_px(end) + line_normal * 10.; /* Draw the indicator line, with stubs at both ends. */ let line_colour = Color { @@ -115,7 +115,7 @@ impl DimensionIndicator { * It should be placed in the middle of the line, but not into the line directly, so it * will be moved out by the normal. */ - let text_pos = transform.point_m_to_px((*end + *start) / 2.) + line_normal * 20.; + let text_pos = transform.point_m_to_px(&((*end + *start) / 2.)) + line_normal * 20.; rld.draw_text( &format!("{}m", &(*end - *start).length()), text_pos.x as i32, diff --git a/src/grid.rs b/src/grid.rs index 72a849a..c885f19 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -4,7 +4,7 @@ use raylib::drawing::RaylibDraw; use raylib::ffi::Color; /// The internal grid length which will be used to snap things to it. -pub const SNAP_SIZE: f32 = 0.5; +pub const SNAP_SIZE: f64 = 0.5; pub const LINE_COLOUR: Color = Color { r: 255, @@ -15,7 +15,7 @@ pub const LINE_COLOUR: Color = Color { /// Snap a vector to the grid with the factor being the sub-grid accuracy. For instance, 0.5 will /// snap to half a grid cell, while 2.0 would snap to every second grid cell -pub fn snap_to_grid(mut vec: Vec2, snap_fraction: f32) -> Vec2 { +pub fn snap_to_grid(mut vec: Vec2, snap_fraction: f64) -> Vec2 { vec.x = math::round(vec.x, snap_fraction); vec.y = math::round(vec.y, snap_fraction); @@ -35,37 +35,17 @@ where let translation_y_px: i32 = transform.translation_px().y as i32 % transform.pixels_per_m() as i32; - /* - let mut row = 0; - loop { - let line_y = translation_y_px + (transform.pixels_per_m() * row as f32) as i32; - if line_y > screen_height { - break; - } - rld.draw_line(0, line_y as i32, screen_width, line_y as i32, LINE_COLOUR); - row += 1; - } - - let mut column = 0; - loop { - let line_x = translation_x_px + (transform.pixels_per_m() * column as f32) as i32; - if line_x > screen_width { - break; - } - rld.draw_line(line_x as i32, 0, line_x as i32, screen_height, LINE_COLOUR); - column += 1; - } - */ - - // Draw the row lines. - let mut line_y: f32 = translation_y_px as f32; - while line_y <= screen_height as f32 { + // Draw the row lines. Add back the subpixels to the translation + let mut line_y: f64 = translation_y_px as f64 + + (transform.translation_px().y - transform.translation_px().y as i32 as f64); + while line_y <= screen_height as f64 { rld.draw_line(0, line_y as i32, screen_width, line_y as i32, LINE_COLOUR); line_y += transform.pixels_per_m(); } // Draw the column lines. - let mut line_x: f32 = translation_x_px as f32; - while line_x <= screen_width as f32 { + let mut line_x: f64 = translation_x_px as f64 + + (transform.translation_px().x - transform.translation_px().x as i32 as f64); + while line_x <= screen_width as f64 { rld.draw_line(line_x as i32, 0, line_x as i32, screen_height, LINE_COLOUR); line_x += transform.pixels_per_m(); } diff --git a/src/main.rs b/src/main.rs index f669ec6..7e04456 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,7 +73,7 @@ fn main() { // Move the canvas together with the mouse if rl.is_mouse_button_down(MouseButton::MOUSE_MIDDLE_BUTTON) { - transform.move_by_px((rl.get_mouse_position() - last_mouse_pos).into()); + transform.move_by_px(&(rl.get_mouse_position() - last_mouse_pos).into()); } let mouse_wheel_move = rl.get_mouse_wheel_move(); @@ -81,8 +81,8 @@ fn main() { // Zoom in for positive and zoom out for negative mouse wheel rotations. let scale_factor = if mouse_wheel_move > 0 { 1.2 } else { 1. / 1.2 }; transform.try_zoom( - rl.get_mouse_position().into(), - mouse_wheel_move.abs() as f32 * scale_factor, + &rl.get_mouse_position().into(), + mouse_wheel_move.abs() as f64 * scale_factor, ); } diff --git a/src/map_data.rs b/src/map_data.rs index b17d779..a6ce5a0 100644 --- a/src/map_data.rs +++ b/src/map_data.rs @@ -12,9 +12,9 @@ use std::path::Path; /// transform, or the zoom-level #[derive(Serialize, Deserialize)] pub struct MapData { - rooms: Vec>, - polygons: Vec>, - walls: Vec<(Vec2, Vec2)>, + rooms: Vec>, + polygons: Vec>, + walls: Vec<(Vec2, Vec2)>, icons: Vec, } @@ -67,24 +67,24 @@ impl MapData { file.write_all(&string.as_bytes()) } - pub fn rooms(&self) -> &Vec> { + pub fn rooms(&self) -> &Vec> { &self.rooms } - pub fn rooms_mut(&mut self) -> &mut Vec> { + pub fn rooms_mut(&mut self) -> &mut Vec> { &mut self.rooms } - pub fn polygons(&self) -> &Vec> { + pub fn polygons(&self) -> &Vec> { &self.polygons } - pub fn polygons_mut(&mut self) -> &mut Vec> { + pub fn polygons_mut(&mut self) -> &mut Vec> { &mut self.polygons } - pub fn walls(&self) -> &Vec<(Vec2, Vec2)> { + pub fn walls(&self) -> &Vec<(Vec2, Vec2)> { &self.walls } - pub fn walls_mut(&mut self) -> &mut Vec<(Vec2, Vec2)> { + pub fn walls_mut(&mut self) -> &mut Vec<(Vec2, Vec2)> { &mut self.walls } diff --git a/src/math/mod.rs b/src/math/mod.rs index b84d270..279affc 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -11,6 +11,7 @@ pub use self::triangle::*; pub use self::vec2::*; use nalgebra::Scalar; +use num_traits::Float; use std::cmp::Ordering; /// Trait that describes an area in the vector space on the field of T @@ -31,11 +32,14 @@ pub trait Surface { /// Round a floating point number to the nearest step given by the step argument. For instance, if /// the step is 0.5, then all numbers from 0.0 to 0.24999... will be 0., while all numbers from /// 0.25 to 0.74999... will be 0.5 and so on. -pub fn round(num: f32, step: f32) -> f32 { +pub fn round(num: T, step: T) -> T +where + T: Float, +{ // Only positive steps will be accepted. - assert!(step > 0.); + assert!(step > T::zero()); - let lower_bound = ((num / step) as i32) as f32 * step; + let lower_bound = (num / step).floor() * step; let upper_bound = lower_bound + step; // Compare the distances and prefer the smaller. If they are the same, prefer the upper bound. diff --git a/src/math/rect.rs b/src/math/rect.rs index 5f4e5f5..5603642 100644 --- a/src/math/rect.rs +++ b/src/math/rect.rs @@ -2,6 +2,7 @@ use super::{LineSegment, Polygon, Surface, Vec2}; //use alga::general::{Additive, Identity}; use nalgebra::{ClosedAdd, ClosedSub, RealField, Scalar}; use num_traits::identities::Zero; +use num_traits::{NumCast, ToPrimitive}; use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign}; @@ -147,16 +148,6 @@ impl Surface fo } // This is sad, but also sadly necessary :/ -impl + Scalar + Copy> Into for Rect { - fn into(self) -> raylib::ffi::Rectangle { - raylib::ffi::Rectangle { - x: self.x.into(), - y: self.y.into(), - width: self.w.into(), - height: self.h.into(), - } - } -} impl + Scalar + Copy> From for Rect { fn from(r: raylib::ffi::Rectangle) -> Self { Self { @@ -167,16 +158,6 @@ impl + Scalar + Copy> From for Rect { } } } -impl + Scalar + Copy> Into for Rect { - fn into(self) -> raylib::math::Rectangle { - raylib::math::Rectangle { - x: self.x.into(), - y: self.y.into(), - width: self.w.into(), - height: self.h.into(), - } - } -} impl + Scalar + Copy> From for Rect { fn from(r: raylib::math::Rectangle) -> Self { Self { @@ -188,6 +169,27 @@ impl + Scalar + Copy> From for Rect { } } +impl Into for Rect { + fn into(self) -> raylib::math::Rectangle { + raylib::math::Rectangle { + x: NumCast::from(self.x).expect("Unable to cast Rect into raylib Rect"), + y: NumCast::from(self.y).expect("Unable to cast Rect into raylib Rect"), + width: NumCast::from(self.w).expect("Unable to cast Rect into raylib Rect"), + height: NumCast::from(self.h).expect("Unable to cast Rect into raylib Rect"), + } + } +} +impl Into for Rect { + fn into(self) -> raylib::ffi::Rectangle { + raylib::ffi::Rectangle { + x: NumCast::from(self.x).expect("Unable to cast Rect into raylib Rect"), + y: NumCast::from(self.y).expect("Unable to cast Rect into raylib Rect"), + width: NumCast::from(self.w).expect("Unable to cast Rect into raylib Rect"), + height: NumCast::from(self.h).expect("Unable to cast Rect into raylib Rect"), + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/math/vec2.rs b/src/math/vec2.rs index cd38889..d591f1d 100644 --- a/src/math/vec2.rs +++ b/src/math/vec2.rs @@ -1,7 +1,7 @@ use crate::math::Rect; use alga::general::{ClosedAdd, ClosedSub}; use nalgebra::{RealField, Scalar}; -use num_traits::One; +use num_traits::{NumCast, One, ToPrimitive}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::convert::{From, Into}; @@ -46,18 +46,6 @@ impl Vec2 { } // This is sad, but also sadly necessary :/ -impl Into for Vec2 -where - T: Into + Scalar + Copy, -{ - fn into(self) -> raylib::ffi::Vector2 { - raylib::ffi::Vector2 { - x: self.x.into(), - y: self.y.into(), - } - } -} - impl From for Vec2 where T: From + Scalar + Copy, @@ -69,21 +57,9 @@ where } } } -impl Into for Vec2 -where - T: Into, -{ - fn into(self) -> raylib::math::Vector2 { - raylib::math::Vector2 { - x: self.x.into(), - y: self.y.into(), - } - } -} - -impl From for Vec2 +impl From for Vec2 where - T: From, + T: From + Scalar + Copy, { fn from(v: raylib::math::Vector2) -> Self { Self { @@ -93,6 +69,23 @@ where } } +impl Into for Vec2 { + fn into(self) -> raylib::ffi::Vector2 { + raylib::ffi::Vector2 { + x: NumCast::from(self.x).expect("Unable to cast Vec2 into raylib Vector"), + y: NumCast::from(self.y).expect("Unable to cast Vec2 into raylib Vector"), + } + } +} +impl Into for Vec2 { + fn into(self) -> raylib::math::Vector2 { + raylib::math::Vector2 { + x: NumCast::from(self.x).expect("Unable to cast Vec2 into raylib Vector"), + y: NumCast::from(self.y).expect("Unable to cast Vec2 into raylib Vector"), + } + } +} + // Begin mathematical operators ----------------------------------------------- // Addition diff --git a/src/tool/deletion_tool.rs b/src/tool/deletion_tool.rs index 17dd949..74a8f92 100644 --- a/src/tool/deletion_tool.rs +++ b/src/tool/deletion_tool.rs @@ -10,7 +10,7 @@ use raylib::RaylibHandle; pub struct DeletionTool { keybindings: DeletionToolKeybindings, - deletion_rect: Option<(Vec2, Vec2)>, + deletion_rect: Option<(Vec2, Vec2)>, } impl DeletionTool { @@ -22,7 +22,7 @@ impl DeletionTool { } /// Delete all map-data that is contained inside the provided rectangular space. - pub fn delete_rect(map_data: &mut MapData, rect: Rect) { + pub fn delete_rect(map_data: &mut MapData, rect: Rect) { map_data .rooms_mut() .retain(|&room| !rect.contains_rect(&room)); @@ -50,7 +50,7 @@ impl Tool for DeletionTool { transform: &Transform, mouse_blocked: bool, ) { - let mouse_pos_m = transform.point_px_to_m(rl.get_mouse_position().into()); + let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); if let Some((_, ref mut pos2)) = &mut self.deletion_rect { *pos2 = mouse_pos_m; } @@ -72,7 +72,7 @@ impl Tool for DeletionTool { fn draw(&self, _map_data: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { if let Some((pos1, pos2)) = self.deletion_rect { - let rect_px = transform.rect_m_to_px(Rect::bounding_rect(pos1, pos2)); + let rect_px = transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2)); rld.draw_rectangle_rec( rect_px, Color { diff --git a/src/tool/icon_tool.rs b/src/tool/icon_tool.rs index 4b3a1eb..e972c1c 100644 --- a/src/tool/icon_tool.rs +++ b/src/tool/icon_tool.rs @@ -19,9 +19,9 @@ pub const ICON_DIR: &str = "assets/icons"; struct IconFileInfo { /// The position the icon should be anchored in pixels. This is the Vector it will be moved by /// relative to the mouse pointer (to the left and up). - anchor: Vec2, + anchor: Vec2, /// The scale of the icon as expressed in image pixels per real meter. - pixels_per_m: f32, + pixels_per_m: f64, } #[derive(Clone, Serialize, Deserialize)] @@ -29,9 +29,9 @@ pub struct IconInfo { /// The id of the icon is the icons position in the currently loaded icon_data vector. pub icon_id: usize, /// The position of the icon on the map, given by the vector in meters. - pub position: Vec2, + pub position: Vec2, /// Rotation of the icon texture in degrees. - pub rotation: f32, + pub rotation: f64, } pub struct IconTool { @@ -132,7 +132,7 @@ impl Tool for IconTool { ) { // Update the position of the icon that should be drawn to the current mouse position. let snapped_mouse_pos_m = snap_to_grid( - transform.point_px_to_m(rl.get_mouse_position().into()), + transform.point_px_to_m(&rl.get_mouse_position().into()), SNAP_SIZE, ); self.current_icon.position = snapped_mouse_pos_m; @@ -161,14 +161,14 @@ impl Tool for IconTool { let (texture, info) = &self.icon_data[icon.icon_id]; // Round the position to whole pixels to fix rotation problems. let mut position_px = - transform.point_m_to_px(icon.position - (info.anchor / info.pixels_per_m)); - position_px.x = position_px.x as i32 as f32; - position_px.y = position_px.y as i32 as f32; + transform.point_m_to_px(&(icon.position - (info.anchor / info.pixels_per_m))); + position_px.x = position_px.x.floor(); + position_px.y = position_px.y.floor(); rld.draw_texture_ex( texture, position_px, - icon.rotation, - transform.pixels_per_m() / info.pixels_per_m, + icon.rotation as f32, + (transform.pixels_per_m() / info.pixels_per_m) as f32, Color { r: 255, g: 255, @@ -183,14 +183,14 @@ impl Tool for IconTool { let (texture, info) = &self.icon_data[self.current_icon.icon_id]; // Round the position to whole pixels to fix rotation problems. let mut position_px = transform - .point_m_to_px(self.current_icon.position - (info.anchor / info.pixels_per_m)); - position_px.x = position_px.x as i32 as f32; - position_px.y = position_px.y as i32 as f32; + .point_m_to_px(&(self.current_icon.position - (info.anchor / info.pixels_per_m))); + position_px.x = position_px.x.floor(); + position_px.y = position_px.y.floor(); rld.draw_texture_ex( texture, position_px, - self.current_icon.rotation, - transform.pixels_per_m() / info.pixels_per_m, + self.current_icon.rotation as f32, + (transform.pixels_per_m() / info.pixels_per_m) as f32, Color { r: 120, g: 200, diff --git a/src/tool/polygon_room_tool.rs b/src/tool/polygon_room_tool.rs index 42874e8..8cd2c25 100644 --- a/src/tool/polygon_room_tool.rs +++ b/src/tool/polygon_room_tool.rs @@ -11,8 +11,8 @@ use raylib::ffi::Color; use raylib::RaylibHandle; struct UnfinishedPolygon { - pub corners: Vec>, - pub working_corner: Vec2, + pub corners: Vec>, + pub working_corner: Vec2, } pub struct PolygonRoomTool { @@ -23,13 +23,13 @@ pub struct PolygonRoomTool { impl UnfinishedPolygon { // Check the validity of the already completed corners only - pub fn check_validity_completed(&self) -> Result<(), PolygonError> { + pub fn check_validity_completed(&self) -> Result<(), PolygonError> { Polygon::check_validity(&self.corners) } // Check the validity of the already completed corners, but with the working corner wedged // between the last and first corner (making it the new last corner). - pub fn check_validity_all(&self) -> Result<(), PolygonError> { + pub fn check_validity_all(&self) -> Result<(), PolygonError> { // TODO: Is this possible without changing the mutability of self and without reallocation? let mut corners = self.corners.clone(); corners.push(self.working_corner); @@ -37,7 +37,7 @@ impl UnfinishedPolygon { Polygon::check_validity(&corners) } - pub fn try_into_completed(&mut self) -> Option> { + pub fn try_into_completed(&mut self) -> Option> { match self.check_validity_completed() { Ok(()) => Some(Polygon::new_unchecked(self.corners.drain(..).collect())), Err(e) => { @@ -47,7 +47,7 @@ impl UnfinishedPolygon { } } - pub fn try_into_all(&mut self) -> Option> { + pub fn try_into_all(&mut self) -> Option> { match self.check_validity_all() { Ok(()) => { self.corners.push(self.working_corner); @@ -63,7 +63,7 @@ impl UnfinishedPolygon { } } - pub fn try_push_working(&mut self) -> Result<(), PolygonError> { + pub fn try_push_working(&mut self) -> Result<(), PolygonError> { assert!(!self.corners.is_empty()); if self.corners.len() == 1 { @@ -106,7 +106,7 @@ impl Tool for PolygonRoomTool { transform: &Transform, mouse_blocked: bool, ) { - let mouse_pos_m = transform.point_px_to_m(rl.get_mouse_position().into()); + let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); let snapped_mouse_pos_m = snap_to_grid(mouse_pos_m, SNAP_SIZE); // Update the position of the node that would be placed into the polygon next. @@ -170,11 +170,11 @@ impl Tool for PolygonRoomTool { for polygon in map.polygons() { let triangles = math::triangulate(polygon.clone()); for triangle in triangles { - let triangle: [Vec2; 3] = triangle.into(); + let triangle: [Vec2; 3] = triangle.into(); rld.draw_triangle( - transform.point_m_to_px(triangle[0]), - transform.point_m_to_px(triangle[1]), - transform.point_m_to_px(triangle[2]), + transform.point_m_to_px(&triangle[0]), + transform.point_m_to_px(&triangle[1]), + transform.point_m_to_px(&triangle[2]), Color { r: 180, g: 180, @@ -189,9 +189,9 @@ impl Tool for PolygonRoomTool { // The first corner is guaranteed to be set, so we can at least draw a line. if polygon.corners.len() == 1 { rld.draw_line_ex( - transform.point_m_to_px(polygon.corners[0]), - transform.point_m_to_px(polygon.working_corner), - transform.length_m_to_px(0.1), + transform.point_m_to_px(&polygon.corners[0]), + transform.point_m_to_px(&polygon.working_corner), + transform.length_m_to_px(0.1) as f32, Color { r: 150, g: 200, @@ -202,9 +202,9 @@ impl Tool for PolygonRoomTool { } else if polygon.corners.len() == 2 { // We have three valid corners, so we can draw a triangle. rld.draw_triangle( - transform.point_m_to_px(polygon.corners[0]), - transform.point_m_to_px(polygon.corners[1]), - transform.point_m_to_px(polygon.working_corner), + transform.point_m_to_px(&polygon.corners[0]), + transform.point_m_to_px(&polygon.corners[1]), + transform.point_m_to_px(&polygon.working_corner), Color { r: 150, g: 200, @@ -220,11 +220,11 @@ impl Tool for PolygonRoomTool { let polygon = Polygon::new_unchecked(corners); let triangles = math::triangulate(polygon); for triangle in triangles { - let triangle: [Vec2; 3] = triangle.into(); + let triangle: [Vec2; 3] = triangle.into(); rld.draw_triangle( - transform.point_m_to_px(triangle[0]), - transform.point_m_to_px(triangle[1]), - transform.point_m_to_px(triangle[2]), + transform.point_m_to_px(&triangle[0]), + transform.point_m_to_px(&triangle[1]), + transform.point_m_to_px(&triangle[2]), Color { r: 150, g: 200, @@ -237,11 +237,11 @@ impl Tool for PolygonRoomTool { let polygon = Polygon::new_unchecked(polygon.corners.clone()); let triangles = math::triangulate(polygon); for triangle in triangles { - let triangle: [Vec2; 3] = triangle.into(); + let triangle: [Vec2; 3] = triangle.into(); rld.draw_triangle( - transform.point_m_to_px(triangle[0]), - transform.point_m_to_px(triangle[1]), - transform.point_m_to_px(triangle[2]), + transform.point_m_to_px(&triangle[0]), + transform.point_m_to_px(&triangle[1]), + transform.point_m_to_px(&triangle[2]), Color { r: 150, g: 200, diff --git a/src/tool/room_tool.rs b/src/tool/room_tool.rs index c4a8b8c..6a283e3 100644 --- a/src/tool/room_tool.rs +++ b/src/tool/room_tool.rs @@ -14,7 +14,7 @@ pub struct RoomTool { keybindings: RoomToolKeybindings, /// The rectangle that is currently being drawn by the user. Once it is finished, it will be /// pushed into the room_rects. - unfinished_rect: Option<(Vec2, Vec2)>, + unfinished_rect: Option<(Vec2, Vec2)>, dimension_indicator: DimensionIndicator, } @@ -42,7 +42,7 @@ impl Tool for RoomTool { transform: &Transform, mouse_blocked: bool, ) { - let mouse_pos_m = transform.point_px_to_m(rl.get_mouse_position().into()); + let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); // Update the currently drawn rectangle, if it exists, and also its dimension indicator. if let Some((ref pos1, ref mut pos2)) = &mut self.unfinished_rect { *pos2 = snap_to_grid(mouse_pos_m, SNAP_SIZE); @@ -71,7 +71,7 @@ impl Tool for RoomTool { // Draw all finished rectangles. for &rect in map_data.rooms() { rld.draw_rectangle_rec( - transform.rect_m_to_px(rect), + transform.rect_m_to_px(&rect), Color { r: 180, g: 180, @@ -84,7 +84,7 @@ impl Tool for RoomTool { // Do the same for the unfinished rectangle if let Some((pos1, pos2)) = self.unfinished_rect { rld.draw_rectangle_rec( - transform.rect_m_to_px(Rect::bounding_rect(pos1, pos2)), + transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2)), Color { r: 150, g: 200, diff --git a/src/tool/wall_tool.rs b/src/tool/wall_tool.rs index 2cc5b5d..e6503ba 100644 --- a/src/tool/wall_tool.rs +++ b/src/tool/wall_tool.rs @@ -11,7 +11,7 @@ use raylib::RaylibHandle; pub struct WallTool { keybindings: WallToolKeybindings, - unfinished_wall: Option<(Vec2, Vec2)>, + unfinished_wall: Option<(Vec2, Vec2)>, } impl WallTool { @@ -35,7 +35,7 @@ impl Tool for WallTool { transform: &Transform, mouse_blocked: bool, ) { - let mouse_pos_m = transform.point_px_to_m(rl.get_mouse_position().into()); + let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); if let Some((_, ref mut pos2)) = &mut self.unfinished_wall { *pos2 = snap_to_grid(mouse_pos_m, SNAP_SIZE); } @@ -61,12 +61,12 @@ impl Tool for WallTool { fn draw(&self, map_data: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { for &(pos1, pos2) in map_data.walls() { - let pos1: Vector2 = transform.point_m_to_px(pos1).into(); - let pos2: Vector2 = transform.point_m_to_px(pos2).into(); + let pos1: Vector2 = transform.point_m_to_px(&pos1).into(); + let pos2: Vector2 = transform.point_m_to_px(&pos2).into(); rld.draw_line_ex( pos1, pos2, - transform.length_m_to_px(0.1), + transform.length_m_to_px(0.1) as f32, Color { r: 200, g: 120, @@ -77,12 +77,12 @@ impl Tool for WallTool { } if let Some((pos1, pos2)) = self.unfinished_wall { - let pos1: Vector2 = transform.point_m_to_px(pos1).into(); - let pos2: Vector2 = transform.point_m_to_px(pos2).into(); + let pos1: Vector2 = transform.point_m_to_px(&pos1).into(); + let pos2: Vector2 = transform.point_m_to_px(&pos2).into(); rld.draw_line_ex( pos1, pos2, - transform.length_m_to_px(0.1), + transform.length_m_to_px(0.1) as f32, Color { r: 150, g: 200, diff --git a/src/transform.rs b/src/transform.rs index bd80bc1..7c1adbf 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -5,15 +5,15 @@ use crate::math::{Rect, Vec2}; -const STANDARD_PIXELS_PER_M: f32 = 64.; -const MIN_PIXELS_PER_M: f32 = 5.; -const MAX_PIXELS_PER_M: f32 = 10_000.; +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: f32, + pixels_per_m: f64, /// The vector the entire on-screen map is moved by in pixels - translation_px: Vec2, + translation_px: Vec2, } impl Transform { @@ -27,36 +27,36 @@ impl Transform { /// Convert a point that is given in meters into the corresponding point in pixels. #[inline] - pub fn point_m_to_px(&self, point: Vec2) -> Vec2 { + pub fn point_m_to_px(&self, point: &Vec2) -> Vec2 { // 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 + (*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) -> Vec2 { + pub fn point_px_to_m(&self, point: &Vec2) -> Vec2 { // Start by subtracting the pixel translation and afterwards convert these absolute pixel // measurements into meters. - (point - self.translation_px) / self.pixels_per_m + (*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: f32) -> f32 { + 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: f32) -> f32 { + 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) -> Rect { - let left_upper = self.point_m_to_px(Vec2::new(rect.x, rect.y)); + pub fn rect_m_to_px(&self, rect: &Rect) -> Rect { + let left_upper = self.point_m_to_px(&Vec2::new(rect.x, rect.y)); Rect::new( left_upper.x, left_upper.y, @@ -67,8 +67,8 @@ impl Transform { /// Convert a rectangle which has measurements in pixels into one of meters #[inline] - pub fn rect_px_to_m(&self, rect: Rect) -> Rect { - let left_upper = self.point_px_to_m(Vec2::new(rect.x, rect.y)); + pub fn rect_px_to_m(&self, rect: &Rect) -> Rect { + let left_upper = self.point_px_to_m(&Vec2::new(rect.x, rect.y)); Rect::new( left_upper.x, left_upper.y, @@ -84,7 +84,7 @@ impl Transform { /// 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, factor: f32) -> bool { + pub fn try_zoom(&mut self, mouse_pos_px: &Vec2, 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) @@ -94,36 +94,36 @@ impl Transform { } // Save the absolute mouse position in meters for tethering later - let mouse_pos_m = self.point_px_to_m(mouse_pos_px); + 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 f32 + 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 f32 + MAX_PIXELS_PER_M as u32 as f64 } else { - desired_px_per_m as u32 as f32 + 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); + 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) { - self.translation_px += by; + pub fn move_by_px(&mut self, by: &Vec2) { + self.translation_px += *by; } - pub fn pixels_per_m(&self) -> f32 { + pub fn pixels_per_m(&self) -> f64 { self.pixels_per_m } - pub fn translation_px(&self) -> Vec2 { - self.translation_px + pub fn translation_px(&self) -> &Vec2 { + &self.translation_px } } -- cgit v1.2.3-70-g09d2 From 90db142588e1b78250dc9b266bae359cf9e22721 Mon Sep 17 00:00:00 2001 From: Arne Dußin Date: Sat, 28 Nov 2020 02:22:35 +0100 Subject: Limit grid inaccuracy to half a pixel Before, the grid was calculated accumutatively, which was pretty alright. However, with some zoom levels towards the bottom of the screen there was a discrepancy between the grid and something drawn. This should be fixed now, since this update makes the grid use the same method of calculation for every grid cell that everything else uses to calculate points. Also, sub-pixel positions are rounded for the grid, while they were floored before, which should improve accuracy. --- src/grid.rs | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/grid.rs b/src/grid.rs index c885f19..ec27fa7 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -27,26 +27,35 @@ pub fn draw_grid(rld: &mut D, screen_width: i32, screen_height: i32, transfor where D: RaylibDraw, { - /* Calculate the actual screen offset of the grid, by modulo-ing the translation of the - * transform. + /* Calculate the first whole meter that can be seen on the grid. This is the first meter that + * will be seen on screen. */ - let translation_x_px: i32 = - transform.translation_px().x as i32 % transform.pixels_per_m() as i32; - let translation_y_px: i32 = - transform.translation_px().y as i32 % transform.pixels_per_m() as i32; - - // Draw the row lines. Add back the subpixels to the translation - let mut line_y: f64 = translation_y_px as f64 - + (transform.translation_px().y - transform.translation_px().y as i32 as f64); - while line_y <= screen_height as f64 { - rld.draw_line(0, line_y as i32, screen_width, line_y as i32, LINE_COLOUR); - line_y += transform.pixels_per_m(); + let mut first_cell = *transform.translation_px() / -transform.pixels_per_m(); + first_cell.x = first_cell.x.floor(); + first_cell.y = first_cell.y.floor(); + + let mut cell = first_cell; + let mut draw_y = transform.point_m_to_px(&cell).y; + loop { + draw_y = math::round(draw_y, 1.); + rld.draw_line(0, draw_y as i32, screen_width, draw_y as i32, LINE_COLOUR); + cell.y += 1.; + draw_y = transform.point_m_to_px(&cell).y; + + if draw_y as i32 > screen_height { + break; + } } - // Draw the column lines. - let mut line_x: f64 = translation_x_px as f64 - + (transform.translation_px().x - transform.translation_px().x as i32 as f64); - while line_x <= screen_width as f64 { - rld.draw_line(line_x as i32, 0, line_x as i32, screen_height, LINE_COLOUR); - line_x += transform.pixels_per_m(); + + let mut draw_x = transform.point_m_to_px(&cell).x; + loop { + draw_x = math::round(draw_x, 1.); + rld.draw_line(draw_x as i32, 0, draw_x as i32, screen_height, LINE_COLOUR); + cell.x += 1.; + draw_x = transform.point_m_to_px(&cell).x; + + if draw_x as i32 > screen_width { + break; + } } } -- cgit v1.2.3-70-g09d2