diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dimension_indicator.rs | 123 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/math/vec2.rs | 23 | ||||
| -rw-r--r-- | src/tool/room_tool.rs | 11 |
4 files changed, 159 insertions, 4 deletions
diff --git a/src/dimension_indicator.rs b/src/dimension_indicator.rs new file mode 100644 index 0000000..a08d22c --- /dev/null +++ b/src/dimension_indicator.rs @@ -0,0 +1,123 @@ +use crate::math::Vec2; +use crate::transform::Transform; +use raylib::drawing::RaylibDraw; +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<f32>, Vec2<f32>)>, +} + +impl DimensionIndicator { + pub fn new() -> Self { + Self { + length_lines: Vec::new(), + } + } + + pub fn from_corner_points(corner_points: &Vec<Vec2<f32>>) -> Self { + let mut this = Self::new(); + this.update_dimensions(corner_points); + + this + } + + /// 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<f32>]) { + if corner_points.len() < 2 { + warn!("Cannot discern dimensions when not at least two points are given. The dimensions were not updated."); + return; + } + + // Discern the bounding box for the given corner points. + let mut min = corner_points[0]; + let mut max = corner_points[0]; + for point in corner_points.iter().skip(1) { + if point.x < min.x { + min.x = point.x; + } + if point.x > max.x { + max.x = point.x; + } + + if point.y < min.y { + min.y = point.y; + } + if point.y > max.y { + max.y = point.y; + } + } + + // For now, only use the width and height vectors. + // TODO: Change to a more sophisticated approach. + self.length_lines.clear(); + // Horizontal dimensions, left to right. + self.length_lines + .push((Vec2::new(min.x, max.y), Vec2::new(max.x, max.y))); + // Vertical dimensions, bottom to top. + self.length_lines + .push((Vec2::new(max.x, max.y), Vec2::new(max.x, min.y))); + } + + pub fn draw(&self, rld: &mut impl RaylibDraw, transform: &Transform) { + // Draw all the dimension lines. + for (start, end) in &self.length_lines { + // Don't draw anything if the length is zero. + if start == end { + continue; + } + + /* Get the vector that is perpendicular and points right/down from the line, assuming + * the lines prefer left as start over right and bottom over top. + */ + let line_normal = { + // Start with the direction of the line vector. + let dir = *start - *end; + // Calculate perpendicular vec and normalise. + dir.rotated_90_clockwise() / dir.len() + }; + + // 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.; + + /* Draw the indicator line, with stubs at both ends. */ + let line_colour = Color { + r: 200, + g: 200, + b: 200, + a: 255, + }; + // First the two stubs. + rld.draw_line_ex( + start_px - line_normal * 5., + start_px + line_normal * 5., + 2., + line_colour, + ); + rld.draw_line_ex( + end_px - line_normal * 5., + end_px + line_normal * 5., + 2., + line_colour, + ); + // Then the actual indicator line. + rld.draw_line_ex(start_px, end_px, 2., line_colour); + + /* Draw the indicator text showing how long this line is in meters. + * 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.; + rld.draw_text( + &format!("{}m", &(*end - *start).len()), + text_pos.x as i32, + text_pos.y as i32, + 20, + line_colour, + ); + } + } +} diff --git a/src/main.rs b/src/main.rs index a0d8a13..e40a40e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,9 @@ +#[macro_use] +extern crate log; + pub mod button; pub mod config; +pub mod dimension_indicator; pub mod editor; pub mod grid; pub mod map_data; @@ -17,6 +21,8 @@ use transform::Transform; pub const CONFIG_FILE: &str = "config.ron"; fn main() { + pretty_env_logger::init(); + let (mut rl, thread) = raylib::init().resizable().title("Hello there!").build(); rl.set_target_fps(120); rl.set_exit_key(None); diff --git a/src/math/vec2.rs b/src/math/vec2.rs index ea549d7..7834ffc 100644 --- a/src/math/vec2.rs +++ b/src/math/vec2.rs @@ -1,11 +1,12 @@ use crate::math::Rect; use alga::general::{ClosedAdd, ClosedSub}; use nalgebra::{RealField, Scalar}; +use num_traits::One; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::convert::{From, Into}; -use std::fmt; -use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::{fmt, mem}; #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Serialize, Deserialize, Eq)] pub struct Vec2<T: Scalar + Copy> { @@ -24,6 +25,24 @@ impl<T: Scalar + Copy> Vec2<T> { { (self.x * self.x + self.y * self.y).sqrt() } + + pub fn rotated_90_clockwise(mut self) -> Vec2<T> + where + T: One + Neg<Output = T> + MulAssign, + { + mem::swap(&mut self.x, &mut self.y); + self.y *= -T::one(); + self + } + + pub fn rotated_90_counterclockwise(mut self) -> Vec2<T> + where + T: One + Neg<Output = T> + MulAssign, + { + mem::swap(&mut self.x, &mut self.y); + self.x *= -T::one(); + self + } } // This is sad, but also sadly necessary :/ diff --git a/src/tool/room_tool.rs b/src/tool/room_tool.rs index 1bfb225..52748fc 100644 --- a/src/tool/room_tool.rs +++ b/src/tool/room_tool.rs @@ -1,6 +1,7 @@ use super::Tool; use crate::button::Button; use crate::config::{RoomToolKeybindings, ToolKeybindings}; +use crate::dimension_indicator::DimensionIndicator; use crate::grid::snap_to_grid; use crate::map_data::MapData; use crate::math::{Rect, Vec2}; @@ -14,6 +15,7 @@ pub struct RoomTool { /// 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<f32>, Vec2<f32>)>, + dimension_indicator: DimensionIndicator, } impl RoomTool { @@ -22,6 +24,7 @@ impl RoomTool { Self { keybindings, unfinished_rect: None, + dimension_indicator: DimensionIndicator::new(), } } } @@ -29,9 +32,11 @@ impl RoomTool { impl Tool for RoomTool { fn active_update(&mut self, map_data: &mut MapData, rl: &RaylibHandle, transform: &Transform) { let mouse_pos_m = transform.point_px_to_m(rl.get_mouse_position().into()); - // Update the currently drawn rectangle, if it exists - if let Some((_, ref mut pos2)) = &mut self.unfinished_rect { + // 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, 0.5); + + self.dimension_indicator.update_dimensions(&[*pos1, *pos2]); } // Start or finish drawing the currently unfinished rectangle @@ -73,6 +78,8 @@ impl Tool for RoomTool { a: 255, }, ); + + self.dimension_indicator.draw(rld, transform); } } |
