aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dimension_indicator.rs128
-rw-r--r--src/gui/dimension_indicator.rs141
-rw-r--r--src/gui/mod.rs2
-rw-r--r--src/main.rs2
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;