aboutsummaryrefslogtreecommitdiff
path: root/src/gui/dimension_indicator.rs
diff options
context:
space:
mode:
authorArne Dußin2020-12-20 03:58:46 +0100
committerArne Dußin2020-12-20 16:41:52 +0100
commit48f321a80970ebeb8374072b1d2e0a4d297aa348 (patch)
tree8f6dbffef7ed507ae118919917fd69a7a9528c39 /src/gui/dimension_indicator.rs
parentc073618d9773216ab4a2dcd7944589f38b1df751 (diff)
downloadgraf_karto-48f321a80970ebeb8374072b1d2e0a4d297aa348.tar.gz
graf_karto-48f321a80970ebeb8374072b1d2e0a4d297aa348.zip
Add dimensional indicator with scaling
Diffstat (limited to 'src/gui/dimension_indicator.rs')
-rw-r--r--src/gui/dimension_indicator.rs189
1 files changed, 153 insertions, 36 deletions
diff --git a/src/gui/dimension_indicator.rs b/src/gui/dimension_indicator.rs
index 3d8313d..aa00f67 100644
--- a/src/gui/dimension_indicator.rs
+++ b/src/gui/dimension_indicator.rs
@@ -1,10 +1,11 @@
-use crate::math::{self, Vec2, Rect};
+use crate::colours::DEFAULT_COLOURS;
+use crate::map::Map;
+use crate::math::{self, Rect, Vec2};
use crate::transform::Transform;
+use nalgebra::{Matrix3, Vector2};
use raylib::drawing::RaylibDraw;
+use raylib::ffi::{Color, KeyboardKey};
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.
@@ -18,8 +19,8 @@ enum State {
Ruling {
dim_x: String,
dim_y: String,
- editing_x: bool
- }
+ editing_x: bool,
+ },
}
/// Used to render the horizontal and vertical dimensions of whatever is selected on the map and, if
@@ -28,7 +29,7 @@ pub struct DimensionIndicator {
/// The [State] the dimension indicator is currently in.
state: State,
/// The last dimensions that were valid.
- dimensions: Rect<f64>
+ bounds: Rect<f64>,
}
impl Default for State {
@@ -44,7 +45,7 @@ impl DimensionIndicator {
pub fn new() -> Self {
Self {
state: State::default(),
- dimensions: Rect::new(0., 0., 0., 0.);
+ bounds: Rect::new(0., 0., 0., 0.),
}
}
@@ -52,7 +53,7 @@ impl DimensionIndicator {
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),
+ &State::Ruling { .. } => self.update_ruling(map, rl),
};
}
@@ -73,22 +74,21 @@ impl DimensionIndicator {
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 {
+ } 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;
}
+ selection_exists = true;
}
}
// Set the current selection limits, if any.
- self.dimensions = if selection_exists {
+ self.bounds = if selection_exists {
Rect::bounding_rect(min, max)
- }
- else {
+ } else {
Rect::new(0., 0., 0., 0.)
};
@@ -96,28 +96,151 @@ impl DimensionIndicator {
// 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
+ dim_x: self.bounds.w.to_string(),
+ dim_y: self.bounds.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,
+ // Get the currently edited dimension for processing.
+ let (edited_dim, editing_x) = match &mut self.state {
+ State::Watching => panic!("Called ruler update when in watching state"),
+ State::Ruling {
+ dim_x,
+ dim_y,
+ editing_x,
+ } => {
+ if *editing_x {
+ (dim_x, editing_x)
+ } else {
+ (dim_y, editing_x)
+ }
+ }
};
+ // Switch the currently edited dimension when pressing tab.
+ if rl.is_key_pressed(KeyboardKey::KEY_TAB) {
+ *editing_x = !*editing_x;
+ return;
+ }
+ // Finish editing mode on enter.
+ if rl.is_key_pressed(KeyboardKey::KEY_ENTER) {
+ self.state = State::Watching;
+ return;
+ }
+
+ // Marker to see if the dimensions will have to be checked for an update.
+ let mut dimension_changed = false;
+ // Delete the last character of the dimension on backspace.
+ if rl.is_key_pressed(KeyboardKey::KEY_BACKSPACE) {
+ edited_dim.pop();
+ dimension_changed = true;
+ }
+ /* Capture the current key and try to add it to the string of the current dimension,
+ * if possible.
+ */
+ else if let Some(key) = rl.get_key_pressed() {
+ match key {
+ // Add a decimal point to the dimension if possible.
+ KeyboardKey::KEY_PERIOD => {
+ if !edited_dim.contains('.') {
+ edited_dim.push('.');
+ }
+ // Nothing changed here, since there is an implicit .0 at the end.
+ }
+ // Handle the entered key if it is a number to append it to the currently edited dimension.
+ _ => {
+ if key as u16 >= KeyboardKey::KEY_ZERO as u16
+ && key as u16 <= KeyboardKey::KEY_NINE as u16
+ {
+ edited_dim.push(key as u8 as char);
+ dimension_changed = true;
+ }
+ }
+ };
+ }
+
+ if dimension_changed {
+ /* Try to parse the dimension from the currently edited string. If it
+ * is valid, change the dimensions of the currently selected items. If
+ * not, ignore the change and wait for a valid dimension.
+ */
+ if let Ok(dim) = edited_dim.parse::<f64>() {
+ let new_bounds = if *editing_x {
+ Rect::new(self.bounds.x, self.bounds.y, dim, self.bounds.h)
+ } else {
+ Rect::new(self.bounds.x, self.bounds.y, self.bounds.h, dim)
+ };
+ self.set_bounds(map, new_bounds);
+ }
+ }
+ }
+
+ /// Set the selection boundaries to the given bounds. Tries to transform the
+ /// currently selected items in the map so they fit inside of the new bounding box.
+ ///
+ /// # Panics
+ /// If the `bounds` have a negative value for width or height, the dimensions
+ /// cannot be set and the function will panic.
+ pub fn set_bounds(&mut self, map: &mut Map, bounds: Rect<f64>) {
+ if bounds.w <= 0. || bounds.h <= 0. {
+ panic!("Cannot set dimensions of elements to zero.");
+ }
+
+ // If the bounds are the same as before, there is nothing to do.
+ if self.bounds == bounds {
+ return;
+ }
+
+ /* Create a matrix to transform from the current rectangle bounds into the
+ * new bounds. Internally, this is a three-step process. First, we
+ * translate the points currently in the bounding box to the origin
+ * (0, 0) origin vector of the map, then scale and finally move it to the
+ * origin of the new rectangle. This needs to be applied to all vertices
+ * of all elements that can be scaled.
+ */
+ let scale = Vector2::new(bounds.w / self.bounds.w, bounds.h / self.bounds.h);
+ let transform = Matrix3::new_translation(&Vector2::new(-self.bounds.x, -self.bounds.y))
+ .append_nonuniform_scaling(&scale)
+ .append_translation(&Vector2::new(bounds.x, bounds.y));
+
+ for element in map.elements_mut() {
+ if element.selected() {
+ if let Some(transformable) = element.as_non_rigid_mut() {
+ transformable.apply_matrix(&transform);
+ }
+ }
+ }
+
+ self.bounds = bounds;
}
pub fn draw(&self, rld: &mut impl RaylibDraw, transform: &Transform) {
- // Draw all the dimension lines.
- for (start, end) in &self.length_lines {
+ /* Ignore a selection that has no non-null dimensions, since this usually
+ * indicates that there is nothing to be scaled.
+ */
+ if self.bounds.w == 0. && self.bounds.h == 0. {
+ return;
+ }
+
+ let (dim_str_width, dim_str_height) = match &self.state {
+ State::Watching => (self.bounds.w.to_string(), self.bounds.h.to_string()),
+ State::Ruling { dim_x, dim_y, .. } => (dim_x.clone(), dim_y.clone()),
+ };
+
+ // Draw the horizontal dimension at the bottom and the vertical dimension to the right.
+ // Use the valid dimensions, but show the edited dimensions in the String (should they differ)
+ let top_right = Vec2::new(self.bounds.x + self.bounds.w, self.bounds.y);
+ let bot_left = Vec2::new(self.bounds.x, self.bounds.y + self.bounds.h);
+ let bot_right = Vec2::new(self.bounds.x + self.bounds.w, self.bounds.y + self.bounds.h);
+ let dimensions = [
+ (bot_left, bot_right, dim_str_width),
+ (bot_right, top_right, dim_str_height),
+ ];
+ for (start, end, dim_str) in &dimensions {
// Don't draw anything if the length is zero.
if start == end {
continue;
@@ -138,27 +261,21 @@ impl DimensionIndicator {
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,
+ DEFAULT_COLOURS.dimension_indicators,
);
rld.draw_line_ex(
end_px - line_normal * 5.,
end_px + line_normal * 5.,
2.,
- line_colour,
+ DEFAULT_COLOURS.dimension_indicators,
);
// Then the actual indicator line.
- rld.draw_line_ex(start_px, end_px, 2., line_colour);
+ rld.draw_line_ex(start_px, end_px, 2., DEFAULT_COLOURS.dimension_indicators);
/* 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
@@ -166,11 +283,11 @@ impl DimensionIndicator {
*/
let text_pos = transform.point_m_to_px(&((*end + *start) / 2.)) + line_normal * 20.;
rld.draw_text(
- &format!("{}m", &(*end - *start).length()),
+ &format!("{}m", dim_str),
text_pos.x as i32,
text_pos.y as i32,
20,
- line_colour,
+ DEFAULT_COLOURS.dimension_text,
);
}
}