diff options
| author | Arne Dußin | 2020-12-16 18:01:25 +0100 |
|---|---|---|
| committer | Arne Dußin | 2020-12-16 18:01:25 +0100 |
| commit | c073618d9773216ab4a2dcd7944589f38b1df751 (patch) | |
| tree | e57b1ea8f99d9eb4bd3e500b6b56fb7879cabc40 /src | |
| parent | 82d11b7d3e15d8175accf7579db1fbe528fc6583 (diff) | |
| download | graf_karto-c073618d9773216ab4a2dcd7944589f38b1df751.tar.gz graf_karto-c073618d9773216ab4a2dcd7944589f38b1df751.zip | |
Add new dimension indicator
Diffstat (limited to 'src')
| -rw-r--r-- | src/dimension_indicator.rs | 128 | ||||
| -rw-r--r-- | src/gui/dimension_indicator.rs | 141 | ||||
| -rw-r--r-- | src/gui/mod.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 2 |
4 files changed, 98 insertions, 175 deletions
diff --git a/src/dimension_indicator.rs b/src/dimension_indicator.rs deleted file mode 100644 index cc12f0b..0000000 --- a/src/dimension_indicator.rs +++ /dev/null @@ -1,128 +0,0 @@ -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<f64>, Vec2<f64>)>, -} - -impl DimensionIndicator { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { - length_lines: Vec::new(), - } - } - - pub fn from_corner_points(corner_points: &[Vec2<f64>]) -> Self { - let mut this = Self::new(); - this.update_dimensions(corner_points); - - this - } - - pub fn clear_dimensions(&mut self) { - self.length_lines.clear(); - } - - /// 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<f64>]) { - 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.length() - }; - - // 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).length()), - text_pos.x as i32, - text_pos.y as i32, - 20, - line_colour, - ); - } - } -} diff --git a/src/gui/dimension_indicator.rs b/src/gui/dimension_indicator.rs index cc12f0b..3d8313d 100644 --- a/src/gui/dimension_indicator.rs +++ b/src/gui/dimension_indicator.rs @@ -1,69 +1,118 @@ -use crate::math::Vec2; +use crate::math::{self, Vec2, Rect}; use crate::transform::Transform; use raylib::drawing::RaylibDraw; -use raylib::ffi::Color; +use raylib::RaylibHandle; +use raylib::ffi::{KeyboardKey, Color}; +use crate::map::Map; +use std::mem; +/// A state the [DimensionIndicator] is currently in. This determines the behaviour of it and what +/// inputs it might be waiting for. +enum State { + /// In this state, the indicator is not trying to read any keyboard input, but will instead watch + /// for any changes to the dimensions from a different source, updating its display. + Watching, + /// In this state, the indicator will capture keyboard input and attempt to set the dimensions + /// according to whatever was entered. If the dimensions cannot be set, the indicator will use + /// the last valid dimensions. + Ruling { + dim_x: String, + dim_y: String, + editing_x: bool + } +} + +/// Used to render the horizontal and vertical dimensions of whatever is selected on the map and, if +/// the user so desires edit them directly by entering values into it. 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<f64>, Vec2<f64>)>, + /// The [State] the dimension indicator is currently in. + state: State, + /// The last dimensions that were valid. + dimensions: Rect<f64> +} + +impl Default for State { + fn default() -> Self { + Self::Watching + } } impl DimensionIndicator { - #[allow(clippy::new_without_default)] + /// Create a new dimension indicator. While it is possible to have multiple instances, this is + /// not generally recommended, since they will need to be managed carefully or otherwise steal + /// keystrokes from each other. pub fn new() -> Self { Self { - length_lines: Vec::new(), + state: State::default(), + dimensions: Rect::new(0., 0., 0., 0.); } } - pub fn from_corner_points(corner_points: &[Vec2<f64>]) -> Self { - let mut this = Self::new(); - this.update_dimensions(corner_points); - - this + /// Update whatever is selected on the map according to the dimension indicator rules and rulers. + pub fn update(&mut self, map: &mut Map, rl: &mut RaylibHandle) { + match &self.state { + &State::Watching => self.update_watching(map, rl), + &State::Ruling{ .. } => self.update_ruling(map, rl), + }; } - pub fn clear_dimensions(&mut self) { - self.length_lines.clear(); - } + fn update_watching(&mut self, map: &Map, rl: &RaylibHandle) { + let mut min: Vec2<f64> = Vec2::default(); + let mut max: Vec2<f64> = Vec2::default(); - /// 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<f64>]) { - if corner_points.len() < 2 { - warn!("Cannot discern dimensions when not at least two points are given. The dimensions were not updated."); - return; + /* Try to find selected items. If no items exist, the dimension indicator is set to its + * default, otherwise it is adjusted to the size of the combined selection. + */ + let mut selection_exists = false; + for e in map.elements() { + if e.selected() { + let element_bounds = e.bounding_rect(); + if selection_exists { + // Adjust the currently detected selection size. + min.x = math::partial_min(min.x, element_bounds.x); + min.y = math::partial_min(min.y, element_bounds.y); + max.x = math::partial_max(max.x, element_bounds.x + element_bounds.w); + max.y = math::partial_max(max.y, element_bounds.y + element_bounds.h); + } + else { + // No selection size detected yet. Set now. + min.x = element_bounds.x; + min.y = element_bounds.y; + max.x = element_bounds.x + element_bounds.w; + max.y = element_bounds.y + element_bounds.h; + } + } } - // 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; - } + // Set the current selection limits, if any. + self.dimensions = if selection_exists { + Rect::bounding_rect(min, max) + } + else { + Rect::new(0., 0., 0., 0.) + }; - if point.y < min.y { - min.y = point.y; - } - if point.y > max.y { - max.y = point.y; - } + // Check if the user wants to change into editing mode, which the user can only do if there + // is a selection to begin with. + if selection_exists && rl.is_key_pressed(KeyboardKey::KEY_TAB) { + self.state = State::Ruling { + dim_x: self.dimensions.w.to_string(), + dim_y: self.dimensions.h.to_string(), + editing_x: true + }; } + } + + fn update_ruling(&mut self, map: &mut Map, rl: &mut RaylibHandle) { + /* Capture the current key press and interpret it, if it exists. Otherwise, there is nothing + * to update. + */ + let key = match rl.get_key_pressed() { + Some(key) => key, + None => return, + }; + - // 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) { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index a4a000b..0351ab3 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,3 +1,5 @@ pub mod tool_sidebar; +pub mod dimension_indicator; pub use self::tool_sidebar::*; +pub use self::dimension_indicator::*;
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8ddc587..7957f14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![warn(missing_docs)] #[macro_use] extern crate log; @@ -6,7 +7,6 @@ extern crate log; pub mod button; pub mod colours; pub mod config; -pub mod dimension_indicator; pub mod editor; pub mod grid; pub mod gui; |
