diff options
Diffstat (limited to 'src/tool')
| -rw-r--r-- | src/tool/deletion_tool.rs | 91 | ||||
| -rw-r--r-- | src/tool/icon_tool.rs | 197 | ||||
| -rw-r--r-- | src/tool/mod.rs | 49 | ||||
| -rw-r--r-- | src/tool/polygon_room_tool.rs | 167 | ||||
| -rw-r--r-- | src/tool/rect_room_tool.rs | 73 | ||||
| -rw-r--r-- | src/tool/room_tool.rs | 103 | ||||
| -rw-r--r-- | src/tool/selection_tool.rs | 63 | ||||
| -rw-r--r-- | src/tool/wall_tool.rs | 113 |
8 files changed, 299 insertions, 557 deletions
diff --git a/src/tool/deletion_tool.rs b/src/tool/deletion_tool.rs index 74a8f92..5ff3e6a 100644 --- a/src/tool/deletion_tool.rs +++ b/src/tool/deletion_tool.rs @@ -1,41 +1,25 @@ use super::Tool; -use crate::button::Button; -use crate::config::{DeletionToolKeybindings, ToolKeybindings}; -use crate::map_data::MapData; +use crate::map::Map; use crate::math::{Rect, Surface, Vec2}; use crate::transform::Transform; +use crate::colours::DEFAULT_COLOURS; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use raylib::ffi::Color; -use raylib::RaylibHandle; pub struct DeletionTool { - keybindings: DeletionToolKeybindings, deletion_rect: Option<(Vec2<f64>, Vec2<f64>)>, } impl DeletionTool { - pub fn new(keybindings: DeletionToolKeybindings) -> Self { + pub fn new() -> Self { Self { - keybindings, deletion_rect: None, } } +} - /// Delete all map-data that is contained inside the provided rectangular space. - pub fn delete_rect(map_data: &mut MapData, rect: Rect<f64>) { - map_data - .rooms_mut() - .retain(|&room| !rect.contains_rect(&room)); - map_data - .walls_mut() - .retain(|&(pos1, pos2)| !rect.contains_point(&pos1) || !rect.contains_point(&pos2)); - map_data - .icons_mut() - .retain(|icon| !rect.contains_point(&icon.position)); - map_data - .polygons_mut() - .retain(|polygon| !rect.contains_polygon(&polygon)); - } +fn delete_rect((pos1, pos2): (&Vec2<f64>, &Vec2<f64>), map: &mut Map) { + let bounds = Rect::bounding_rect(*pos1, *pos2); + map.retain(|e| !bounds.contains_rect(&e.bounding_rect())); } impl Tool for DeletionTool { @@ -43,59 +27,44 @@ impl Tool for DeletionTool { self.deletion_rect = None; } - fn active_update( - &mut self, - map_data: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ) { - let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { if let Some((_, ref mut pos2)) = &mut self.deletion_rect { - *pos2 = mouse_pos_m; - } - - if self.keybindings.do_delete.is_pressed(rl, mouse_blocked) && self.deletion_rect.is_some() - { - let (pos1, pos2) = self.deletion_rect.take().unwrap(); - Self::delete_rect(map_data, Rect::bounding_rect(pos1, pos2)); - } else if self - .keybindings - .start_selection - .is_pressed(rl, mouse_blocked) - { - self.deletion_rect = Some((mouse_pos_m, mouse_pos_m)) - } else if self.keybindings.abort_deletion.is_pressed(rl, false) { - self.deletion_rect = None; + *pos2 = *mouse_pos_m; } } - fn draw(&self, _map_data: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { + fn draw(&self, 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)); rld.draw_rectangle_rec( rect_px, - Color { - r: 200, - g: 150, - b: 150, - a: 50, - }, + DEFAULT_COLOURS.deletion_rect ); rld.draw_rectangle_lines_ex( rect_px, 4, - Color { - r: 200, - g: 150, - b: 150, - a: 150, - }, + DEFAULT_COLOURS.deletion_rect_outline ); } } - fn activation_key(&self) -> Button { - self.keybindings.activation_key() + fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2<f64>) { + if let Some((pos1, pos2)) = self.deletion_rect { + delete_rect((&pos1, &pos2), map); + self.deletion_rect = None; + } else { + self.deletion_rect = Some((*mouse_pos_m, *mouse_pos_m)); + } + } + + fn finish(&mut self, map: &mut Map) { + if let Some((pos1, pos2)) = self.deletion_rect { + delete_rect((&pos1, &pos2), map); + self.deletion_rect = None; + } + } + + fn abort(&mut self) { + self.deletion_rect = None; } } diff --git a/src/tool/icon_tool.rs b/src/tool/icon_tool.rs index e972c1c..09b0ac1 100644 --- a/src/tool/icon_tool.rs +++ b/src/tool/icon_tool.rs @@ -1,115 +1,33 @@ use crate::button::Button; -use crate::config::{IconToolKeybindings, ToolKeybindings}; -use crate::grid::{snap_to_grid, SNAP_SIZE}; -use crate::map_data::MapData; +use crate::config::IconToolKeys; +use crate::map::icon_renderer::IconRenderer; +use crate::map::{Icon, Map, Mappable}; use crate::math::Vec2; use crate::tool::Tool; use crate::transform::Transform; -use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use raylib::core::texture::Texture2D; -use raylib::ffi::Color; -use raylib::{RaylibHandle, RaylibThread}; -use ron::de::from_reader; -use serde::{Deserialize, Serialize}; -use std::fs::{self, File}; +use raylib::core::drawing::RaylibDrawHandle; +use std::rc::Rc; pub const ICON_DIR: &str = "assets/icons"; -#[derive(Deserialize)] -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<f64>, - /// The scale of the icon as expressed in image pixels per real meter. - pixels_per_m: f64, -} - -#[derive(Clone, Serialize, Deserialize)] -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<f64>, - /// Rotation of the icon texture in degrees. - pub rotation: f64, -} - pub struct IconTool { // TODO: support svg - keybindings: IconToolKeybindings, - /// The icon data, containing the image texture and the info for that image texture like the - /// scale the image actually has. - icon_data: Vec<(Texture2D, IconFileInfo)>, + keybindings: IconToolKeys, /// Saves whether the IconTool is the currently active tool or not. active: bool, /// The information of the icon that should be placed / is currently being placed, if it /// exists. - current_icon: IconInfo, -} - -impl Default for IconInfo { - fn default() -> Self { - Self { - icon_id: 0, - position: Vec2::new(0., 0.), - rotation: 0., - } - } + current_icon: Icon, + renderer: Rc<IconRenderer>, } impl IconTool { - pub fn new( - rl: &mut RaylibHandle, - rlt: &RaylibThread, - keybindings: IconToolKeybindings, - ) -> Self { - /* Read all available icons from the icon directory. SVGs do not need any special scale - * file, but pixel-based file formats require a RON-file declaring what the scale of the - * picture is right beside them. - */ - let mut image_files = Vec::new(); - for entry in fs::read_dir(ICON_DIR).expect("Could not open icon directory") { - let entry = entry.expect("Failed to read file from icon directory"); - - // Ignore the RON-files for now and put the image files into the vec - if entry - .path() - .extension() - .expect("Entry does not have a file extension") - != "ron" - { - image_files.push(entry); - } - } - - // Read the RON-files where it is necessary. - let mut icon_data = Vec::with_capacity(image_files.len()); - for file in image_files { - // TODO: Handle svg - - let texture = rl - .load_texture( - rlt, - file.path() - .to_str() - .expect("Unable to convert path to string."), - ) - .expect("Could not read image file"); - - let mut file = file.path(); - file.set_extension("ron"); - let ron = File::open(file).expect("Could not read ron file for icon information."); - let icon_info: IconFileInfo = - from_reader(ron).expect("Could not parse icon info from reader."); - - icon_data.push((texture, icon_info)); - } - + pub fn new(keybindings: IconToolKeys, renderer: Rc<IconRenderer>) -> Self { Self { keybindings, - icon_data, active: false, - current_icon: IconInfo::default(), + current_icon: Icon::new(0, Vec2::default(), 0., renderer.clone()), + renderer, } } } @@ -123,85 +41,30 @@ impl Tool for IconTool { self.active = false; } - fn active_update( - &mut self, - map: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ) { - // 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()), - SNAP_SIZE, - ); - self.current_icon.position = snapped_mouse_pos_m; - - // Unwrap the current icon, since it is now definitely set, as we are in the active update. - if self.keybindings.next.is_pressed(rl, mouse_blocked) { - self.current_icon.icon_id = (self.current_icon.icon_id + 1) % self.icon_data.len(); - } - if self - .keybindings - .rotate_clockwise - .is_pressed(rl, mouse_blocked) - { - self.current_icon.rotation += 45.; - } - - // Handle placing the icon on the map - if self.keybindings.place.is_pressed(rl, mouse_blocked) { - map.icons_mut().push(self.current_icon.clone()); - } + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { + self.current_icon.position = *mouse_pos_m; } - fn draw(&self, map: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { - // Draw all icons that have been placed on the map. - for icon in map.icons() { - 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.floor(); - position_px.y = position_px.y.floor(); - rld.draw_texture_ex( - texture, - position_px, - icon.rotation as f32, - (transform.pixels_per_m() / info.pixels_per_m) as f32, - Color { - r: 255, - g: 255, - b: 255, - a: 255, - }, - ); - } - - // Draw the icon that would be placed + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { if self.active { - 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.floor(); - position_px.y = position_px.y.floor(); - rld.draw_texture_ex( - texture, - position_px, - self.current_icon.rotation as f32, - (transform.pixels_per_m() / info.pixels_per_m) as f32, - Color { - r: 120, - g: 200, - b: 120, - a: 255, - }, - ); + self.current_icon.draw(rld, transform); } } - fn activation_key(&self) -> Button { - self.keybindings.activation_key() + fn place_single(&mut self, map: &mut Map, _mouse_pos_m: &Vec2<f64>) { + map.push_icon(self.current_icon.clone()); + } + + fn on_button_pressed(&mut self, _map: &mut Map, button: Button) { + if button == self.keybindings.next { + self.current_icon.id = (self.current_icon.id + 1) % self.renderer.num_icons(); + } else if button == self.keybindings.previous { + self.current_icon.id = + (self.current_icon.id + self.renderer.num_icons() - 1) % self.renderer.num_icons(); + } else if button == self.keybindings.rotate_clockwise { + self.current_icon.rotation += 45.; + } else if button == self.keybindings.rotate_counterclockwise { + self.current_icon.rotation -= 45.; + } } } diff --git a/src/tool/mod.rs b/src/tool/mod.rs index f503d2b..aeabf19 100644 --- a/src/tool/mod.rs +++ b/src/tool/mod.rs @@ -1,46 +1,63 @@ pub mod deletion_tool; pub mod icon_tool; pub mod polygon_room_tool; -pub mod room_tool; +pub mod rect_room_tool; +pub mod selection_tool; pub mod wall_tool; pub use deletion_tool::DeletionTool; pub use icon_tool::IconTool; pub use polygon_room_tool::PolygonRoomTool; -pub use room_tool::RoomTool; +pub use rect_room_tool::RectRoomTool; +pub use selection_tool::SelectionTool; pub use wall_tool::WallTool; use crate::button::Button; -use crate::map_data::MapData; +use crate::map::Map; +use crate::math::Vec2; use crate::transform::Transform; use raylib::core::drawing::RaylibDrawHandle; -use raylib::RaylibHandle; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[repr(u8)] pub enum ToolType { - RoomTool, + RectRoomTool, PolygonRoomTool, WallTool, IconTool, DeletionTool, + SelectionTool, NumTools, } pub trait Tool { + /// Code that needs to be called when this Tool is activated or reactivated goes here. fn activate(&mut self) {} + /// Cleanup that needs to be done when the user switches from this tool to something else goes here. fn deactivate(&mut self) {} - fn update(&mut self, _map: &MapData, _rl: &RaylibHandle, _transform: &Transform) {} - fn active_update( - &mut self, - map: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ); + /// Called on each frame when this tool is active. + fn update(&mut self, map: &Map, mouse_pos_m: &Vec2<f64>); - fn draw(&self, _map: &MapData, _rld: &mut RaylibDrawHandle, _transform: &Transform) {} + /// Draw the contents of this tool. + // TODO: Maybe make this tool mappable? This might make it easier to make things resizable while + // it's still being drawn. + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform); - fn activation_key(&self) -> Button; + /// Generic keybinding. + /// Code to place a single node for this tool. + fn place_single(&mut self, _map: &mut Map, _mouse_pos_m: &Vec2<f64>) {} + + /// Generic keybinding. + /// Code to finish whatever one is doing with this tool currently and trying to apply the + /// changes to the map data. + fn finish(&mut self, _map: &mut Map) {} + + /// Generic keybinding. + /// Stop whatever one is doing with this tool and do not apply any changes to the map data. + fn abort(&mut self) {} + + /// If there are any additional keybindings that need to be handled by this tool, these can be + /// handled here. + fn on_button_pressed(&mut self, _map: &mut Map, _button: Button) {} } diff --git a/src/tool/polygon_room_tool.rs b/src/tool/polygon_room_tool.rs index 8cd2c25..1b079d2 100644 --- a/src/tool/polygon_room_tool.rs +++ b/src/tool/polygon_room_tool.rs @@ -1,14 +1,9 @@ use super::Tool; -use crate::button::Button; -use crate::config::{PolygonRoomToolKeybindings, ToolKeybindings}; -use crate::dimension_indicator::DimensionIndicator; -use crate::grid::{snap_to_grid, SNAP_SIZE}; -use crate::map_data::MapData; +use crate::map::Map; use crate::math::{self, Polygon, PolygonError, Vec2}; use crate::transform::Transform; +use crate::colours::DEFAULT_COLOURS; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use raylib::ffi::Color; -use raylib::RaylibHandle; struct UnfinishedPolygon { pub corners: Vec<Vec2<f64>>, @@ -16,9 +11,7 @@ struct UnfinishedPolygon { } pub struct PolygonRoomTool { - keybindings: PolygonRoomToolKeybindings, unfinished_polygon: Option<UnfinishedPolygon>, - dimension_indicator: DimensionIndicator, } impl UnfinishedPolygon { @@ -83,108 +76,26 @@ impl UnfinishedPolygon { } impl PolygonRoomTool { - pub fn new(keybindings: PolygonRoomToolKeybindings) -> Self { + pub fn new() -> Self { Self { - keybindings, unfinished_polygon: None, - dimension_indicator: DimensionIndicator::new(), } } } impl Tool for PolygonRoomTool { - fn activate(&mut self) {} - fn deactivate(&mut self) { self.unfinished_polygon = None; } - fn active_update( - &mut self, - map: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ) { - 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); - + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { // Update the position of the node that would be placed into the polygon next. if let Some(ref mut polygon) = &mut self.unfinished_polygon { - polygon.working_corner = snapped_mouse_pos_m; - - polygon.corners.push(polygon.working_corner); - self.dimension_indicator.update_dimensions(&polygon.corners); - polygon.working_corner = polygon.corners.pop().unwrap(); - } - - /* Check if the finishing keybinding has been pressed. If so, try to turn the part of the - * polygon that is already completed into a proper polygon and push it into the map data. - */ - if self.keybindings.finish.is_pressed(rl, mouse_blocked) { - if let Some(ref mut polygon) = self.unfinished_polygon { - if let Some(polygon) = polygon.try_into_completed() { - self.dimension_indicator.clear_dimensions(); - map.polygons_mut().push(polygon); - self.unfinished_polygon = None; - } - } - } - - /* Handle placing a new corner of the polygon. If the corner is placed on the first node, - * the polygon will be created. - */ - if self.keybindings.place_node.is_pressed(rl, mouse_blocked) { - if let Some(ref mut polygon) = &mut self.unfinished_polygon { - if polygon.working_corner == polygon.corners[0] { - /* The working corner will be ignored, since it would double the vertex at the - * polygon starting position. - */ - if let Some(polygon) = polygon.try_into_completed() { - self.dimension_indicator.clear_dimensions(); - map.polygons_mut().push(polygon); - self.unfinished_polygon = None; - } - } else { - // Check if we can add the corner to the polygon without ruining it. - if let Err(e) = polygon.try_push_working() { - error!("Cannot add corner to polygon: {}", e); - } - } - } else { - // Start a new unfinished polygon - self.unfinished_polygon = Some(UnfinishedPolygon { - corners: vec![snapped_mouse_pos_m], - working_corner: snapped_mouse_pos_m, - }); - } - } - - if self.keybindings.abort.is_pressed(rl, false) { - self.unfinished_polygon = None; + polygon.working_corner = *mouse_pos_m; } } - fn draw(&self, map: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { - // TODO: Buffer triangles so the polygons don't always have to be retriangulated. - for polygon in map.polygons() { - let triangles = math::triangulate(polygon.clone()); - for triangle in triangles { - let triangle: [Vec2<f64>; 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]), - Color { - r: 180, - g: 180, - b: 180, - a: 255, - }, - ) - } - } - + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { if let Some(polygon) = &self.unfinished_polygon { // The first corner is guaranteed to be set, so we can at least draw a line. if polygon.corners.len() == 1 { @@ -192,12 +103,7 @@ impl Tool for PolygonRoomTool { 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, - b: 150, - a: 255, - }, + DEFAULT_COLOURS.room_selected ); } else if polygon.corners.len() == 2 { // We have three valid corners, so we can draw a triangle. @@ -205,12 +111,7 @@ impl Tool for PolygonRoomTool { 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, - b: 150, - a: 255, - }, + DEFAULT_COLOURS.room_selected ) } else { // A proper polygon can be drawn. @@ -225,12 +126,7 @@ impl Tool for PolygonRoomTool { 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, - b: 150, - a: 255, - }, + DEFAULT_COLOURS.room_selected ) } } else if polygon.check_validity_completed().is_ok() { @@ -242,22 +138,49 @@ impl Tool for PolygonRoomTool { 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, - b: 150, - a: 255, - }, + DEFAULT_COLOURS.room_selected ) } } } + } + } + + fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2<f64>) { + if let Some(ref mut polygon) = &mut self.unfinished_polygon { + if polygon.working_corner == polygon.corners[0] { + /* The working corner will be ignored, since it would double the vertex at the + * polygon starting position. + */ + if let Some(polygon) = polygon.try_into_completed() { + map.push_polygon_room(polygon); + self.unfinished_polygon = None; + } + } else { + // Check if we can add the corner to the polygon without ruining it. + if let Err(e) = polygon.try_push_working() { + error!("Cannot add corner to polygon: {}", e); + } + } + } else { + // Start a new unfinished polygon + self.unfinished_polygon = Some(UnfinishedPolygon { + corners: vec![*mouse_pos_m], + working_corner: *mouse_pos_m, + }); + } + } - self.dimension_indicator.draw(rld, transform); + fn finish(&mut self, map: &mut Map) { + if let Some(ref mut polygon) = self.unfinished_polygon { + if let Some(polygon) = polygon.try_into_completed() { + map.push_polygon_room(polygon); + self.unfinished_polygon = None; + } } } - fn activation_key(&self) -> Button { - self.keybindings.activation_key() + fn abort(&mut self) { + self.unfinished_polygon = None; } } diff --git a/src/tool/rect_room_tool.rs b/src/tool/rect_room_tool.rs new file mode 100644 index 0000000..dfda495 --- /dev/null +++ b/src/tool/rect_room_tool.rs @@ -0,0 +1,73 @@ +use super::Tool; +use crate::map::Map; +use crate::math::{Rect, Vec2}; +use crate::transform::Transform; +use crate::colours::DEFAULT_COLOURS; +use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; + +pub struct RectRoomTool { + /// 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<f64>, Vec2<f64>)>, +} + +impl RectRoomTool { + /// Create a new room tool where no rooms have been drawn yet. + pub fn new() -> Self { + Self { + unfinished_rect: None, + } + } +} + +impl Tool for RectRoomTool { + fn deactivate(&mut self) { + self.unfinished_rect = None; + } + + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { + if let Some((_, ref mut pos2)) = &mut self.unfinished_rect { + *pos2 = *mouse_pos_m; + } + } + + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { + if let Some((pos1, pos2)) = self.unfinished_rect { + rld.draw_rectangle_rec( + transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2)), + DEFAULT_COLOURS.room_selected + ); + } + } + + fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2<f64>) { + // Try to finish the rectangle if it has been started. + if let Some((pos1, pos2)) = self.unfinished_rect { + if pos1 == pos2 { + warn!("Cannot place rectangle with start and endpoint being the same"); + return; + } + + map.push_rect_room(Rect::bounding_rect(pos1, pos2)); + self.unfinished_rect = None; + } else { + self.unfinished_rect = Some((*mouse_pos_m, *mouse_pos_m)); + } + } + + fn finish(&mut self, map: &mut Map) { + if let Some((pos1, pos2)) = self.unfinished_rect { + if pos1 == pos2 { + warn!("Cannot place rectangle with start and endpoint being the same"); + return; + } + + map.push_rect_room(Rect::bounding_rect(pos1, pos2)); + self.unfinished_rect = None; + } + } + + fn abort(&mut self) { + self.unfinished_rect = None; + } +} diff --git a/src/tool/room_tool.rs b/src/tool/room_tool.rs deleted file mode 100644 index 6a283e3..0000000 --- a/src/tool/room_tool.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::Tool; -use crate::button::Button; -use crate::config::{RoomToolKeybindings, ToolKeybindings}; -use crate::dimension_indicator::DimensionIndicator; -use crate::grid::{snap_to_grid, SNAP_SIZE}; -use crate::map_data::MapData; -use crate::math::{Rect, Vec2}; -use crate::transform::Transform; -use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use raylib::ffi::Color; -use raylib::RaylibHandle; - -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<f64>, Vec2<f64>)>, - dimension_indicator: DimensionIndicator, -} - -impl RoomTool { - /// Create a new room tool where no rooms have been drawn yet. - pub fn new(keybindings: RoomToolKeybindings) -> Self { - Self { - keybindings, - unfinished_rect: None, - dimension_indicator: DimensionIndicator::new(), - } - } -} - -impl Tool for RoomTool { - fn deactivate(&mut self) { - self.unfinished_rect = None; - self.dimension_indicator.clear_dimensions(); - } - - fn active_update( - &mut self, - map_data: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ) { - 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); - - self.dimension_indicator.update_dimensions(&[*pos1, *pos2]); - } - - // Start or finish drawing the currently unfinished rectangle - if self.keybindings.finish_draw.is_pressed(rl, mouse_blocked) - && self.unfinished_rect.is_some() - { - let (pos1, pos2) = self.unfinished_rect.take().unwrap(); - self.dimension_indicator.clear_dimensions(); - map_data.rooms_mut().push(Rect::bounding_rect(pos1, pos2)); - } else if self.keybindings.start_draw.is_pressed(rl, mouse_blocked) { - let snapped_mouse_pos = snap_to_grid(mouse_pos_m, SNAP_SIZE); - self.unfinished_rect = Some((snapped_mouse_pos, snapped_mouse_pos)) - } - - if self.keybindings.abort_draw.is_pressed(rl, false) { - self.unfinished_rect = None; - } - } - - fn draw(&self, map_data: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { - // Draw all finished rectangles. - for &rect in map_data.rooms() { - rld.draw_rectangle_rec( - transform.rect_m_to_px(&rect), - Color { - r: 180, - g: 180, - b: 180, - a: 255, - }, - ); - } - - // 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)), - Color { - r: 150, - g: 200, - b: 150, - a: 255, - }, - ); - - self.dimension_indicator.draw(rld, transform); - } - } - - fn activation_key(&self) -> Button { - self.keybindings.activation_key() - } -} diff --git a/src/tool/selection_tool.rs b/src/tool/selection_tool.rs new file mode 100644 index 0000000..49efba9 --- /dev/null +++ b/src/tool/selection_tool.rs @@ -0,0 +1,63 @@ +use super::Tool; +use crate::map::Map; +use crate::math::{Rect, Surface, Vec2}; +use crate::transform::Transform; +use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; +use crate::colours::DEFAULT_COLOURS; + +pub struct SelectionTool { + selection_rect: Option<(Vec2<f64>, Vec2<f64>)>, +} + +impl SelectionTool { + pub fn new() -> Self { + Self { + selection_rect: None, + } + } +} + +impl Tool for SelectionTool { + fn deactivate(&mut self) { + self.selection_rect = None; + } + + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { + if let Some((_, ref mut pos2)) = &mut self.selection_rect { + *pos2 = *mouse_pos_m; + } + } + + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { + if let Some((pos1, pos2)) = self.selection_rect { + let rect_px = transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2)); + rld.draw_rectangle_rec( + rect_px, + DEFAULT_COLOURS.selection_rect + ); + rld.draw_rectangle_lines_ex( + rect_px, + 4, + DEFAULT_COLOURS.selection_rect_outline + ); + } + } + + fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2<f64>) { + if let Some((pos1, pos2)) = self.selection_rect { + // Select all items on the map that are inside of the selection rectangle + let bounds = Rect::bounding_rect(pos1, pos2); + for element in map.elements_mut() { + // TODO: Make it possible to do this additively by custom keybinding. + element.set_selected(bounds.contains_rect(&element.bounding_rect())); + } + self.selection_rect = None; + } else { + self.selection_rect = Some((*mouse_pos_m, *mouse_pos_m)); + } + } + + fn abort(&mut self) { + self.selection_rect = None; + } +} diff --git a/src/tool/wall_tool.rs b/src/tool/wall_tool.rs index d86d0af..b958799 100644 --- a/src/tool/wall_tool.rs +++ b/src/tool/wall_tool.rs @@ -1,23 +1,17 @@ use super::Tool; -use crate::button::Button; -use crate::config::{ToolKeybindings, WallToolKeybindings}; -use crate::grid::{snap_to_grid, SNAP_SIZE}; -use crate::map_data::MapData; -use crate::math::Vec2; +use crate::map::Map; +use crate::math::{LineSegment, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; use raylib::ffi::{Color, Vector2}; -use raylib::RaylibHandle; pub struct WallTool { - keybindings: WallToolKeybindings, - unfinished_wall: Option<(Vec2<f64>, Vec2<f64>)>, + unfinished_wall: Option<LineSegment<f64>>, } impl WallTool { - pub fn new(keybindings: WallToolKeybindings) -> Self { + pub fn new() -> Self { Self { - keybindings, unfinished_wall: None, } } @@ -28,86 +22,19 @@ impl Tool for WallTool { self.unfinished_wall = None; } - fn active_update( - &mut self, - map_data: &mut MapData, - rl: &RaylibHandle, - transform: &Transform, - mouse_blocked: bool, - ) { - 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); - } - - if self - .keybindings - .finish_segment - .is_pressed(rl, mouse_blocked) - && self.unfinished_wall.is_some() - { - let (pos1, pos2) = self.unfinished_wall.unwrap(); - map_data.walls_mut().push((pos1, pos2)); - self.unfinished_wall = Some((pos2, pos2)); - } else if self.keybindings.start_wall.is_pressed(rl, mouse_blocked) { - let snapped_mouse_pos = snap_to_grid(mouse_pos_m, SNAP_SIZE); - self.unfinished_wall = Some((snapped_mouse_pos, snapped_mouse_pos)) - } - - if self.keybindings.abort_segment.is_pressed(rl, false) { - self.unfinished_wall = None; + fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) { + if let Some(ref mut wall) = &mut self.unfinished_wall { + wall.end = *mouse_pos_m; } } - fn draw(&self, map_data: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { - for &(pos1, pos2) in map_data.walls() { - let pos1_px = transform.point_m_to_px(&pos1); - let pos2_px = transform.point_m_to_px(&pos2); - rld.draw_line_ex( - pos1_px, - pos2_px, - transform.length_m_to_px(0.1) as f32, - Color { - r: 200, - g: 120, - b: 120, - a: 255, - }, - ); - - /* Find walls that end/start at the start or end of this wall and draw part of a circle - * to join these two walls more nicely. - */ - for &(other1, other2) in map_data.walls() { - // Ignore the line segment if it's the same wall - if pos1 == other1 && pos2 == other2 { - continue; - } - - // TODO: Only draw segments when introducing transparency. - for pos in [pos1, pos2].iter() { - if *pos == other1 || *pos == other2 { - rld.draw_circle_v( - transform.point_m_to_px(&pos), - transform.length_m_to_px(0.05) as f32, - Color { - r: 200, - g: 120, - b: 120, - a: 255, - }, - ); - } - } - } - } - - 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(); + fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { + if let Some(ref wall) = self.unfinished_wall { + let start: Vector2 = transform.point_m_to_px(&wall.start).into(); + let end: Vector2 = transform.point_m_to_px(&wall.end).into(); rld.draw_line_ex( - pos1, - pos2, + start, + end, transform.length_m_to_px(0.1) as f32, Color { r: 150, @@ -119,7 +46,17 @@ impl Tool for WallTool { } } - fn activation_key(&self) -> Button { - self.keybindings.activation_key() + fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2<f64>) { + if let Some(wall) = self.unfinished_wall.take() { + // Continue with the next wall straight away. + self.unfinished_wall = Some(LineSegment::new(wall.end, wall.end)); + map.push_wall(wall); + } else { + self.unfinished_wall = Some(LineSegment::new(*mouse_pos_m, *mouse_pos_m)); + } + } + + fn abort(&mut self) { + self.unfinished_wall = None; } } |
