From 625d00bb0e9a374e7ecf4b91fde599307199f3b6 Mon Sep 17 00:00:00 2001 From: Arne Dußin Date: Fri, 6 Nov 2020 00:53:46 +0100 Subject: Add icon tool --- src/tool/icon_tool.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tool/mod.rs | 3 ++ src/tool/room_tool.rs | 14 ++--- src/tool/wall_tool.rs | 14 ++--- 4 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 src/tool/icon_tool.rs (limited to 'src/tool') diff --git a/src/tool/icon_tool.rs b/src/tool/icon_tool.rs new file mode 100644 index 0000000..0ff1dc2 --- /dev/null +++ b/src/tool/icon_tool.rs @@ -0,0 +1,142 @@ +use crate::grid::snap_to_grid; +use crate::map_data::MapData; +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, KeyboardKey, MouseButton}; +use raylib::{RaylibHandle, RaylibThread}; +use ron::de::from_reader; +use serde::Deserialize; +use std::fs::{self, File}; + +pub const ICON_DIR: &'static str = "assets/icons"; + +#[derive(Deserialize)] +struct IconInfo { + /// 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, + /// The scale of the icon as expressed in image pixels per real meter. + pixels_per_m: f32, +} + +pub struct IconTool { + // TODO: support svg + /// 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, IconInfo)>, + /// The currently active icon, defined by its position in the Vec. + active_icon: usize, + current_icon_pos: Option>, +} + +impl IconTool { + pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> 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: IconInfo = + from_reader(ron).expect("Could not parse icon info from reader."); + + icon_data.push((texture, icon_info)); + } + + Self { + icon_data, + active_icon: 0, + current_icon_pos: None, + } + } +} + +impl Tool for IconTool { + fn active_update(&mut self, map: &mut MapData, rl: &RaylibHandle, transform: &Transform) { + // Put the currently active icon to where it would be placed. + self.current_icon_pos = Some(snap_to_grid( + transform.point_px_to_m(rl.get_mouse_position().into()), + 0.5, + )); + + // Activate the next icon when pressing the icon tool key. + if rl.is_key_pressed(KeyboardKey::KEY_I) { + self.active_icon = (self.active_icon + 1) % self.icon_data.len(); + } + + // Handle placing the icon on the map + if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { + map.icons_mut() + .push((self.active_icon, self.current_icon_pos.unwrap())); + } + } + + fn draw(&self, map: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { + // Draw all icons that have been placed on the map. + for (icon, pos) in map.icons() { + let (texture, info) = &self.icon_data[*icon]; + rld.draw_texture_ex( + texture, + transform.point_m_to_px(*pos - (info.anchor / info.pixels_per_m)), + 0., + transform.pixels_per_m() / info.pixels_per_m, + Color { + r: 255, + g: 255, + b: 255, + a: 255, + }, + ); + } + + // Draw the icon that would be placed + if let Some(current_icon_pos) = self.current_icon_pos { + let (texture, info) = &self.icon_data[self.active_icon]; + rld.draw_texture_ex( + texture, + transform.point_m_to_px(current_icon_pos - (info.anchor / info.pixels_per_m)), + 0., + transform.pixels_per_m() / info.pixels_per_m, + Color { + r: 120, + g: 200, + b: 120, + a: 255, + }, + ); + } + } +} diff --git a/src/tool/mod.rs b/src/tool/mod.rs index 73654d9..a3d5964 100644 --- a/src/tool/mod.rs +++ b/src/tool/mod.rs @@ -1,8 +1,10 @@ pub mod deletion_tool; +pub mod icon_tool; pub mod room_tool; pub mod wall_tool; pub use deletion_tool::DeletionTool; +pub use icon_tool::IconTool; pub use room_tool::RoomTool; pub use wall_tool::WallTool; @@ -16,6 +18,7 @@ use raylib::RaylibHandle; pub enum ToolType { RoomTool, WallTool, + IconTool, DeletionTool, NumTools, } diff --git a/src/tool/room_tool.rs b/src/tool/room_tool.rs index 0321ff3..09126c9 100644 --- a/src/tool/room_tool.rs +++ b/src/tool/room_tool.rs @@ -1,6 +1,7 @@ use super::Tool; +use crate::grid::snap_to_grid; use crate::map_data::MapData; -use crate::math::{self, Rect, Vec2}; +use crate::math::{Rect, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; use raylib::ffi::{Color, MouseButton}; @@ -26,11 +27,7 @@ impl Tool for RoomTool { 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 { - let snapped_mouse_pos = Vec2::new( - math::round(mouse_pos_m.x, 0.5), - math::round(mouse_pos_m.y, 0.5), - ); - *pos2 = snapped_mouse_pos; + *pos2 = snap_to_grid(mouse_pos_m, 0.5); } // Start or finish drawing the currently unfinished rectangle @@ -39,10 +36,7 @@ impl Tool for RoomTool { map_data.rooms_mut().push(Rect::bounding_rect(pos1, pos2)); self.unfinished_rect = None; } else { - let snapped_mouse_pos = Vec2::new( - math::round(mouse_pos_m.x, 0.5), - math::round(mouse_pos_m.y, 0.5), - ); + let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); self.unfinished_rect = Some((snapped_mouse_pos, snapped_mouse_pos)) } } diff --git a/src/tool/wall_tool.rs b/src/tool/wall_tool.rs index d5760e6..5eda8e0 100644 --- a/src/tool/wall_tool.rs +++ b/src/tool/wall_tool.rs @@ -1,6 +1,7 @@ use super::Tool; +use crate::grid::snap_to_grid; use crate::map_data::MapData; -use crate::math::{self, Vec2}; +use crate::math::Vec2; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; use raylib::ffi::{Color, MouseButton, Vector2}; @@ -22,11 +23,7 @@ impl Tool for WallTool { 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()); if let Some((_, ref mut pos2)) = &mut self.unfinished_wall { - let snapped_mouse_pos = Vec2::new( - math::round(mouse_pos_m.x, 0.5), - math::round(mouse_pos_m.y, 0.5), - ); - *pos2 = snapped_mouse_pos; + *pos2 = snap_to_grid(mouse_pos_m, 0.5); } if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { @@ -34,10 +31,7 @@ impl Tool for WallTool { map_data.walls_mut().push((pos1, pos2)); self.unfinished_wall = Some((pos2, pos2)); } else { - let snapped_mouse_pos = Vec2::new( - math::round(mouse_pos_m.x, 0.5), - math::round(mouse_pos_m.y, 0.5), - ); + let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); self.unfinished_wall = Some((snapped_mouse_pos, snapped_mouse_pos)) } } -- cgit v1.2.3-70-g09d2