diff options
Diffstat (limited to 'src')
36 files changed, 410 insertions, 34 deletions
diff --git a/src/button.rs b/src/button.rs index 89ce9a5..846377e 100644 --- a/src/button.rs +++ b/src/button.rs @@ -11,10 +11,13 @@ use std::mem; /// user has free reign over what key they use for what purpose. #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Button { + /// Button on the mouse with internal mouse button representation of raylib. Mouse(MouseButton), + /// Keyboard button with internal keyboard key representation of raylib. Keyboard(KeyboardKey), } +#[allow(missing_docs)] #[repr(u32)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum MouseButton { @@ -23,6 +26,7 @@ pub enum MouseButton { Middle = 2, } +#[allow(missing_docs)] #[repr(u32)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum KeyboardKey { @@ -134,6 +138,9 @@ pub enum KeyboardKey { } impl Button { + /// Check if this button is pressed. If `mouse_blocked` is true, mouse buttons are ignored which + /// is useful when an element has captured the mouse, but other elements are still queried in the + /// background. pub fn is_pressed(self, rl: &RaylibHandle, mouse_blocked: bool) -> bool { match self { Self::Mouse(button) => !mouse_blocked && rl.is_mouse_button_pressed(button.into()), diff --git a/src/colours.rs b/src/colours.rs index bafb53c..4a3b799 100644 --- a/src/colours.rs +++ b/src/colours.rs @@ -1,20 +1,40 @@ +//! The colour definitions used for items drawn in graf karto. + use raylib::ffi::Color; +/// Contains the default colours used throughout, if nothing else is set. pub const DEFAULT_COLOURS: Colours = Colours::default(); +/// All the different colours that may be used for different elements of the program. Contains one +/// entry for each colourable component. pub struct Colours { + /// Colour the rectangle used for the deletion tool is filled with. pub deletion_rect: Color, + /// The colour of the outline of the deletion tool rectangle. pub deletion_rect_outline: Color, + /// The colour that is used for filling the selection tool's rectangle. pub selection_rect: Color, + /// Colour of the selection tool rectangle outline. pub selection_rect_outline: Color, + /// Colour of the rooms that are currently not selected. pub room_normal: Color, + /// The Colour the rooms should be tinted in when they have been selected. pub room_selected: Color, + /// Colour of the walls when they are not selected. pub wall_normal: Color, + /// Colour of the walls when they have been selected. pub wall_selected: Color, + /// Colour of the icons when they are not selected. pub icon_normal: Color, + /// Colour of the icons when they are selected. pub icon_selected: Color, + /// Colour used to draw the rulers (the ruling lines) of the dimension indicator. pub dimension_indicators: Color, + /// Colour of the text used to display the size of the dimension indicators dimensions. pub dimension_text: Color, + /// The colour used for drawing the lines of the grid which divides the map into chunks of evenly + /// spaced cells. + pub grid_lines: Color, } impl Colours { @@ -95,6 +115,12 @@ impl Colours { b: 200, a: 255, }, + grid_lines: Color { + r: 255, + g: 255, + b: 255, + a: 75, + }, } } } diff --git a/src/config.rs b/src/config.rs index 2a1e5ed..b5abb1e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +//! Home of the user configuratable content of graf karto, like keybindings and (TODO) colours etc. + use crate::button::*; use ron::de::from_reader; use ron::ser::{to_string_pretty, PrettyConfig}; @@ -6,6 +8,8 @@ use std::fs::File; use std::io::{self, Write}; use std::path::Path; +/// All configuration parameters the user can set are contained in this struct. +#[allow(missing_docs)] #[derive(Deserialize, Serialize)] pub struct Config { pub tool_activation_keys: ToolActivationKeys, @@ -13,7 +17,10 @@ pub struct Config { pub icon_keys: IconToolKeys, } +#[allow(missing_docs)] #[derive(Deserialize, Serialize)] +/// The keys used to activate the individual tools. These keystrokes will not be sent to the tools, +/// but instead will be handled by the editor where the tools are registered. pub struct ToolActivationKeys { pub deletion: Button, pub icon: Button, @@ -24,21 +31,38 @@ pub struct ToolActivationKeys { } #[derive(Deserialize, Serialize)] +/// Keys that are useful to most tools. These are packaged so that not every tool has the same n keys +/// and then some more. pub struct ToolGeneralKeys { + /// Keybinding to, where applicable, place a single node (usually a vertex) for the tool in + /// question. pub place_single: Button, + /// Finish up whatever one is doing with the current tool, without removing information. pub finish: Button, + /// Abort whatever one is doing with the current tool which means the last atomic action will not + /// be pushed into the map items. pub abort: Button, } #[derive(Clone, Serialize, Deserialize)] +/// Key bindings that are individually interesting to the icon tool. pub struct IconToolKeys { + /// Key to change to the next icon of the icon list. pub next: Button, + /// Key to change to the previous icon of the icon list. pub previous: Button, + /// Rotate the working icon clockwise by a certain amount (currently 45 degrees) pub rotate_clockwise: Button, + /// Rotate the working icon counterclockwise by a certain amount (currently 45 degrees) pub rotate_counterclockwise: Button, } impl Config { + /// Try to parse a configuration from the file located at path. + /// + /// # Errors + /// If the file is not found or can not be read or parsed for a different reason, an IO-Error is + /// returned. pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Config> { let file = File::open(&path)?; match from_reader(file) { @@ -47,6 +71,10 @@ impl Config { } } + /// Try to write the configuration to the file at path. If the file exists, it will be overwritten. + /// + /// # Errors + /// If the file can not be written, for example for lack of permissions, an IO-Error is returned. pub fn write_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { let mut file = File::create(&path)?; diff --git a/src/editor.rs b/src/editor.rs index 87a8db4..d541fb6 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,3 +1,10 @@ +//! Element creation base +//! +//! The actual editor environment sits here. This especially means all tools that require low-level +//! access to the data of items currently being created. While this may be subject to change, there is +//! currently a difference between things that are being created (inside the editor) and things that +//! are part of the environment (the map). + use crate::button::{Button, MouseButton}; use crate::config::Config; use crate::grid::{snap_to_grid, SNAP_SIZE}; @@ -8,6 +15,7 @@ use raylib::core::drawing::RaylibDrawHandle; use raylib::{RaylibHandle, RaylibThread}; use std::collections::HashMap; +/// The editor containing all tools and currently the map of the stuff that has been created. pub struct Editor { map: Map, /// HashMap that matches the ToolType with its proper activation key and of course the tool @@ -18,6 +26,8 @@ pub struct Editor { } impl Editor { + /// Create a new editor with all tools necessary. There should be only one editor per program + /// instance. pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread, config: Config) -> Self { let map = Map::new(rl, rlt); @@ -74,7 +84,8 @@ impl Editor { } } - /// Get the currently active tool. + /// Get the currently active tool. Since the every tool exists only once, it is entirely indexable + /// by its type, which is what is actually returned. pub fn active(&self) -> ToolType { self.active } @@ -93,6 +104,8 @@ impl Editor { } } + /// Update the internal editor data where necessary and handle selecting different tools, aswell + /// as updating the currently active tool. Should be called once every frame. pub fn update(&mut self, rl: &mut RaylibHandle, transform: &Transform, mouse_blocked: bool) { // Handle keybindings for tool change for (&tool_type, (_, activation_key)) in self.tools.iter() { @@ -175,15 +188,19 @@ impl Editor { } } + /// Draw all tools and in case of the active tool also what is currently being edited by it, if + /// that exists. pub fn draw_tools(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { for (tool, _) in self.tools.values() { tool.draw(rld, transform); } } + /// Get the world containing all finished elements. pub fn map(&self) -> &Map { &self.map } + /// Get the world containing all finished elements mutably. pub fn map_mut(&mut self) -> &mut Map { &mut self.map } diff --git a/src/grid.rs b/src/grid.rs index ec27fa7..d1c4b15 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,18 +1,13 @@ +//! The grid used to divide the map into evenly sized chunks. + +use crate::colours::DEFAULT_COLOURS; use crate::math::{self, Vec2}; use crate::transform::Transform; use raylib::drawing::RaylibDraw; -use raylib::ffi::Color; /// The internal grid length which will be used to snap things to it. pub const SNAP_SIZE: f64 = 0.5; -pub const LINE_COLOUR: Color = Color { - r: 255, - g: 255, - b: 255, - a: 75, -}; - /// Snap a vector to the grid with the factor being the sub-grid accuracy. For instance, 0.5 will /// snap to half a grid cell, while 2.0 would snap to every second grid cell pub fn snap_to_grid(mut vec: Vec2<f64>, snap_fraction: f64) -> Vec2<f64> { @@ -38,7 +33,13 @@ where let mut draw_y = transform.point_m_to_px(&cell).y; loop { draw_y = math::round(draw_y, 1.); - rld.draw_line(0, draw_y as i32, screen_width, draw_y as i32, LINE_COLOUR); + rld.draw_line( + 0, + draw_y as i32, + screen_width, + draw_y as i32, + DEFAULT_COLOURS.grid_lines, + ); cell.y += 1.; draw_y = transform.point_m_to_px(&cell).y; @@ -50,7 +51,13 @@ where let mut draw_x = transform.point_m_to_px(&cell).x; loop { draw_x = math::round(draw_x, 1.); - rld.draw_line(draw_x as i32, 0, draw_x as i32, screen_height, LINE_COLOUR); + rld.draw_line( + draw_x as i32, + 0, + draw_x as i32, + screen_height, + DEFAULT_COLOURS.grid_lines, + ); cell.x += 1.; draw_x = transform.point_m_to_px(&cell).x; diff --git a/src/gui/dimension_indicator.rs b/src/gui/dimension_indicator.rs index aa00f67..e8848fe 100644 --- a/src/gui/dimension_indicator.rs +++ b/src/gui/dimension_indicator.rs @@ -1,10 +1,13 @@ +//! An interface element that shows the size of the selected map items and provides a means to +//! manually change the size of them in a precise manner should need be. + 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::ffi::KeyboardKey; use raylib::RaylibHandle; /// A state the [DimensionIndicator] is currently in. This determines the behaviour of it and what @@ -218,6 +221,7 @@ impl DimensionIndicator { self.bounds = bounds; } + /// Draw the dimensions detected on the current selection. pub fn draw(&self, rld: &mut impl RaylibDraw, transform: &Transform) { /* Ignore a selection that has no non-null dimensions, since this usually * indicates that there is nothing to be scaled. diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 032d430..a94122e 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,3 +1,12 @@ +//! General graphical user interfaces +//! +//! This mod does not contain all graphical content on screen, but all user interfaces that is drawn +//! that is not contained in a different category. This means all interface elements where it does not +//! make sense to bind it to any other part of the program, for instance a tool or type of element. +//! It also does *not* contain anything that does anything that is not triggered by the user, which +//! means everything is called top-down from this module. A function in this module should not be +//! called from any point in the program except the main loop, where the user input is polled. + pub mod dimension_indicator; pub mod tool_sidebar; diff --git a/src/gui/tool_sidebar.rs b/src/gui/tool_sidebar.rs index 7674c47..e6b8867 100644 --- a/src/gui/tool_sidebar.rs +++ b/src/gui/tool_sidebar.rs @@ -1,3 +1,8 @@ +//! The sidebar showing all tools available to the user. This toolbar handles changing the active tool +//! based on the mouse input and (TODO!) keyboard inputs. +// TODO: Currently, the keyboard shortcuts for tools are handled by the editor, but a lot speaks for +// them being handled by the ToolSidebar instead. + use crate::math::{Rect, Surface, Vec2}; use crate::tool::ToolType; use crate::Editor; @@ -6,13 +11,16 @@ use raylib::rgui::RaylibDrawGui; use raylib::{RaylibHandle, RaylibThread}; use std::mem; +/// The file containing textures for all buttons describing the tools. pub const BUTTON_FILE: &str = "assets/button/tool_buttons.png"; +/// Sidebar that renders and handles input for the tool activation buttons. pub struct ToolSidebar { button_texture: Texture2D, } impl ToolSidebar { + /// Create a new tool sidebar. There should be only one sidebar per program instance. pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { let button_texture = rl .load_texture(rlt, BUTTON_FILE) @@ -31,6 +39,9 @@ impl ToolSidebar { Self::panel_rect(screen_height).contains_point(&mouse_pos) } + /// Draw the tool buttons and encasing panel. Because of the way raylib works, this also handles + /// clicking on tool buttons, which may be changed in the future, should a different gui be + /// chosen. pub fn draw(&self, screen_height: u16, rld: &mut impl RaylibDrawGui, editor: &mut Editor) { rld.gui_panel(Self::panel_rect(screen_height)); diff --git a/src/main.rs b/src/main.rs index 6e54c59..8c1d63e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,20 @@ +//! # Graf Karto cartographer +//! +//! ### What is it exactly +//! Graf Karto is a table top role playing game (TTRPG) map creation tool that is optimised for real +//! time map interaction. +//! +//! ### Motivation +//! While there are certainly many TTRPG map creation tools for single user and multi user available +//! online and on the market, we felt that most of them lack features or are to unwieldy to seriously +//! consider for real time dungeon drawing, say for instance when drawing a map for an old school +//! revival style game. This is why Graf Karto is optimised for speed above pretty graphical features. +//! The aim is for the user not to have to think much about how they are going to achieve what they are +//! doing and how they are going to make it pretty, but should just be able to *do* it, to have time to +//! worry about other things. This does not mean that all maps created should visually be equivalent to +//! a steaming pile of hot garbage, but if the visuals should try to get in the way, usability and speed +//! takes precedence. + #![allow(dead_code)] #![warn(missing_docs)] @@ -25,7 +42,9 @@ use std::ffi::CString; use std::io; use transform::Transform; +/// Location of the file containing the style used for the raylib user interface. pub const GUI_STYLE: &str = "assets/style/cyber.rgs"; +/// Location of the graf karto configuration options file. pub const CONFIG_FILE: &str = "config.ron"; fn main() { diff --git a/src/map/data.rs b/src/map/data.rs index f978081..1031d3c 100644 --- a/src/map/data.rs +++ b/src/map/data.rs @@ -1,3 +1,5 @@ +//! Module containing the raw map data version of the map. + use super::{IconData, PolygonRoomData, RectRoomData, WallData}; use ron::de::from_reader; use ron::ser::{to_string_pretty, PrettyConfig}; @@ -18,6 +20,7 @@ pub struct MapData { } impl MapData { + /// Create a serialisable map data type from the data elements contained in a map. pub fn new( rect_rooms: Vec<RectRoomData>, polygon_rooms: Vec<PolygonRoomData>, @@ -32,6 +35,7 @@ impl MapData { } } + /// Load the map data from a file. Fails if the file does not exist or cannot be correctly parsed. pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Self> { let file = File::open(&path)?; let data: Self = match from_reader(file) { @@ -44,6 +48,8 @@ impl MapData { Ok(data) } + /// Write the map data to the file located at `path`. If the file already exists, it will be + /// overwritten. If the write fails, an IO-Error is returned. pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { let mut file = File::create(&path)?; diff --git a/src/map/icon.rs b/src/map/icon.rs index f623c98..2e45486 100644 --- a/src/map/icon.rs +++ b/src/map/icon.rs @@ -1,3 +1,6 @@ +//! Icons are map elements that have a specific size and cannot be stretched. They are usually used +//! as markers for specific places in the world. + use super::icon_renderer::IconRenderer; use crate::colours::DEFAULT_COLOURS; use crate::map::Mappable; @@ -8,6 +11,7 @@ use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; use std::rc::Rc; +/// The icon data necessary to create an Icon again. #[derive(Clone, Serialize, Deserialize)] pub struct IconData { /// The id of the icon is the icons position in the currently loaded icon_data vector. @@ -18,6 +22,7 @@ pub struct IconData { pub rotation: f64, } +/// Describes an icon in the world and can be drawn. #[derive(Clone)] pub struct Icon { data: IconData, @@ -26,6 +31,8 @@ pub struct Icon { } impl Icon { + /// Create a new icon. Requires the icon renderer that is used to render this icon, as well as all + /// the information necessary to describe the icon itself. pub fn new(id: usize, position: Vec2<f64>, rotation: f64, renderer: Rc<IconRenderer>) -> Self { Self::from_data( IconData { @@ -37,6 +44,7 @@ impl Icon { ) } + /// Like `new()`, but with the icon data bundled into the icon data type. pub fn from_data(data: IconData, renderer: Rc<IconRenderer>) -> Self { Self { data, diff --git a/src/map/icon_renderer.rs b/src/map/icon_renderer.rs index fb81e24..eef053d 100644 --- a/src/map/icon_renderer.rs +++ b/src/map/icon_renderer.rs @@ -1,3 +1,6 @@ +//! Since the same icon may be used very often on a map, it becomes impracticalto let every icon have +//! it's own texture data saved, which is why a texture manager type of struct is used for it instead. + use crate::math::Vec2; use raylib::core::texture::Texture2D; use raylib::{RaylibHandle, RaylibThread}; @@ -5,6 +8,7 @@ use ron::de::from_reader; use serde::Deserialize; use std::fs::{self, File}; +/// The directory containing all files related to icons. pub const ICON_DIR: &str = "assets/icons"; #[derive(Deserialize)] @@ -16,11 +20,15 @@ pub(super) struct IconFileInfo { pub pixels_per_m: f64, } +/// Manager for all icon texture or rendering data. pub struct IconRenderer { texture_data: Vec<(Texture2D, IconFileInfo)>, } impl IconRenderer { + /// Create a new icon renderer. This loads all textures and information for icons that is needed + /// to draw them to the screen. Usually, there should be only one IconRenderer, or at least one + /// renderer per directory and program instance. 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 @@ -67,10 +75,16 @@ impl IconRenderer { Self { texture_data } } + /// Get the data needed to render an icon of type `icon_id`. + /// + /// # Panics + /// If the `icon_id` does not describe a valid icon (is out of bounds), there is no data to be + /// accessed and the function panics. pub(super) fn get(&self, icon_id: usize) -> &(Texture2D, IconFileInfo) { &self.texture_data[icon_id] } + /// The number of icons registered in this icon-renderer. pub fn num_icons(&self) -> usize { self.texture_data.len() } diff --git a/src/map/mappable.rs b/src/map/mappable.rs index 323361a..7978f50 100644 --- a/src/map/mappable.rs +++ b/src/map/mappable.rs @@ -1,11 +1,12 @@ //! Something that's mappable can be placed on the map and drawn at a specific position. It has a -//! dimension on the map and may be scaleable +//! dimension on the map and may be transformable in various ways. use crate::math::Rect; use crate::transform::Transform; use crate::transformable::NonRigidTransformable; use raylib::drawing::RaylibDrawHandle; +/// Anything that can be added to the map or world must implement this trait. pub trait Mappable { /// Draw this to `rld` with the transform. An item that cannot be drawn is not mappable, so /// this must always be implemented. @@ -21,10 +22,13 @@ pub trait Mappable { /// Get the rectangle that contains the mappable object in its entirety without excess. fn bounding_rect(&self) -> Rect<f64>; + /// If this mappable item can be transformed in a non-rigid way, a dynamic reference is returned, + /// otherwise none. fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { None } + /// The same as `as_non_rigid`, but mutably. fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { None } diff --git a/src/map/mod.rs b/src/map/mod.rs index ff03474..88a7e6c 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -1,3 +1,16 @@ +//! The map contains all the items that make up the world. +//! +//! There are two main structs to look out for, the first being [Map]. This is the interaction point +//! for most parts of the program. It contains the actual elements that are drawn on the screen. and +//! can be changed by the user. +//! The second is [MapData] and it contains the data that can be saved/loaded and distributed. Every +//! map item has an internal item that it can be dereferenced to and can be used to construct this +//! exact item in the same world elsewhere or at a different time. This is often different from the +//! item that is being drawn. An example would be the [PolygonRoom], which contains a triangulated +//! version of itself, so it can be drawn without always having to compute the triangles every frame. +//! It's data type however [PolygonRoomData] contains only the raw polygon data, not the triangulated +//! version, since that is enough to create the same [PolygonRoom] again. + pub mod data; pub mod icon; pub mod icon_renderer; @@ -19,6 +32,7 @@ use raylib::drawing::RaylibDrawHandle; use raylib::{RaylibHandle, RaylibThread}; use std::rc::Rc; +/// The map containing all map elements that are seen on the screen. pub struct Map { rect_rooms: Vec<RectRoom>, polygon_rooms: Vec<PolygonRoom>, @@ -28,6 +42,7 @@ pub struct Map { } impl Map { + /// Create a new, empty map/world. pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { Self { rect_rooms: Vec::new(), @@ -38,14 +53,20 @@ impl Map { } } + /// Add a rectangularly shaped room to the world. Since the polygon room tool was added, and + /// afaik rects are polygon rooms, this will be phased out in favour of only having polygon + /// rooms, which will then become just normal rooms. pub fn push_rect_room(&mut self, room_data: RectRoomData) { self.rect_rooms.push(RectRoom::from_data(room_data)); } + /// Add a room to the map. Currently, holes are not supported in the polygon, but this might + /// change later. pub fn push_polygon_room(&mut self, room_data: PolygonRoomData) { self.polygon_rooms.push(PolygonRoom::from_data(room_data)); } + /// Add a wall to the world. pub fn push_wall(&mut self, wall_data: WallData) { /* Check for intersections with any wall that was arleady created so the wall ends can be * rendered properly. @@ -70,21 +91,25 @@ impl Map { .push(Wall::from_data(wall_data, start_intersects, end_intersects)); } + /// Add an icon to the world. pub fn push_icon(&mut self, icon: Icon) { self.icons.push(icon); } + /// Draw all elements of the map to the screen. This should be called after the background of the + /// map has already been drawn. pub fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { for element in self.elements() { element.draw(rld, transform); } } + /// Get the icon-renderer that is currently used to render the icons. pub fn icon_renderer(&self) -> Rc<IconRenderer> { self.icon_renderer.clone() } - /// Retain all map elements that fulfill the given predicate. + /// Retain all map elements that fulfill the given predicate, removing everything else. pub fn retain<F>(&mut self, mut f: F) where F: FnMut(&dyn Mappable) -> bool, @@ -107,6 +132,8 @@ impl Map { .chain(self.icons.iter().map(|i| i as &dyn Mappable)) } + /// Iterator over all elements, but the individual elements can be mutated. It is however + /// impossible to add or remove elements in this way. For that, use the dedicated functions. pub fn elements_mut(&mut self) -> impl Iterator<Item = &mut dyn Mappable> { self.rect_rooms .iter_mut() diff --git a/src/map/polygon_room.rs b/src/map/polygon_room.rs index 65eeaab..fd4122e 100644 --- a/src/map/polygon_room.rs +++ b/src/map/polygon_room.rs @@ -1,13 +1,18 @@ +//! Polygon rooms are the standard rooms in graf karto. They can take the form of anything that a +//! [Polygon](crate::math::Polygon) can have. + use super::Mappable; use crate::colours::DEFAULT_COLOURS; use crate::math::{self, Polygon, Rect, Triangle}; use crate::transform::Transform; use crate::transformable::NonRigidTransformable; -use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; use nalgebra::{Matrix3, Point2}; +use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; +/// Data type for the Polygon room. pub type PolygonRoomData = Polygon<f64>; +/// A polygon room, which can be placed and modified in the world. pub struct PolygonRoom { data: PolygonRoomData, // The polygon shape, but in triangles, so the polygon does not have to be triangulated every frame. @@ -16,6 +21,7 @@ pub struct PolygonRoom { } impl PolygonRoom { + /// Create a room from the given polygon data. pub fn from_data(data: PolygonRoomData) -> Self { Self { data: data.clone(), @@ -24,6 +30,8 @@ impl PolygonRoom { } } + // When the internal polygon changes, it must be retriangulated to be drawn on the screen + // properly, so this function must be called any time that happens. fn retriangulate(&mut self) { self.triangulated = math::triangulate(self.data.clone()); } diff --git a/src/map/rect_room.rs b/src/map/rect_room.rs index ee184fb..6ed3ed6 100644 --- a/src/map/rect_room.rs +++ b/src/map/rect_room.rs @@ -1,3 +1,5 @@ +//! Deprecated simple rectangular room type. + use crate::colours::DEFAULT_COLOURS; use crate::map::Mappable; use crate::math::Rect; @@ -6,8 +8,10 @@ use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; use serde::Serialize; use std::ops::{Deref, DerefMut}; +#[allow(missing_docs)] pub type RectRoomData = Rect<f64>; +#[allow(missing_docs)] #[derive(Serialize)] pub struct RectRoom { data: RectRoomData, @@ -15,6 +19,7 @@ pub struct RectRoom { } impl RectRoom { + #[allow(missing_docs)] pub fn from_data(data: RectRoomData) -> Self { RectRoom { data, diff --git a/src/map/wall.rs b/src/map/wall.rs index d18096b..f1748bb 100644 --- a/src/map/wall.rs +++ b/src/map/wall.rs @@ -1,14 +1,23 @@ +//! Walls, solid barriers that are generally unscaleable. +//! +//! This interpretation is generally up to the GM to decide, but generally speaking, a wall cannot be +//! crossed by a player. If special conditions apply (for instance, when the player wants to scale the +//! wall), a check is necessary. If a check is not necessary, then maybe you were not thinking about +//! a wall. + use super::Mappable; use crate::colours::DEFAULT_COLOURS; use crate::math::{LineSegment, Rect, Vec2}; use crate::transform::Transform; use crate::transformable::NonRigidTransformable; +use nalgebra::Matrix3; use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; use std::ops::{Deref, DerefMut}; -use nalgebra::{Matrix3, Point2}; +/// The data which defines a wall segment completely for serialisation. pub type WallData = LineSegment<f64>; +/// A solid wall a player cannot go through, except if special conditions apply. pub struct Wall { data: WallData, selected: bool, @@ -17,6 +26,7 @@ pub struct Wall { } impl Wall { + /// Create a new wall from the deserialised data and information known from internal sources. pub fn from_data(data: WallData, round_start: bool, round_end: bool) -> Self { Self { data, @@ -26,6 +36,7 @@ impl Wall { } } + /// Get the internal data for serialisation pub fn data(&self) -> &WallData { &self.data } @@ -94,12 +105,8 @@ impl Mappable for Wall { impl NonRigidTransformable for Wall { fn apply_matrix(&mut self, matrix: &Matrix3<f64>) { - self.data.start = matrix - .transform_point(&self.data.start.into()) - .into(); - self.data.end = matrix - .transform_point(&self.data.end.into()) - .into(); + self.data.start = matrix.transform_point(&self.data.start.into()).into(); + self.data.end = matrix.transform_point(&self.data.end.into()).into(); } } diff --git a/src/math/line_segment.rs b/src/math/line_segment.rs index b496787..204cf0c 100644 --- a/src/math/line_segment.rs +++ b/src/math/line_segment.rs @@ -1,3 +1,6 @@ +//! A line segment is like a line, but with a start and an end, with the line only being between +//! those two. + use super::{Rect, Surface, TripletOrientation, Vec2}; use alga::general::{ClosedDiv, ClosedMul, ClosedSub}; use nalgebra::{RealField, Scalar}; @@ -5,6 +8,7 @@ use num_traits::Zero; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +#[allow(missing_docs)] #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LineSegment<T: Scalar + Copy> { pub start: Vec2<T>, @@ -12,6 +16,7 @@ pub struct LineSegment<T: Scalar + Copy> { } impl<T: Scalar + Copy> LineSegment<T> { + /// Create a new line segment from `start` to `end` pub fn new(start: Vec2<T>, end: Vec2<T>) -> Self { Self { start, end } } @@ -92,6 +97,9 @@ impl<T: Scalar + Copy> LineSegment<T> { false } + /// Try to find the the point where the two line segments intersect. If they do not intersect, + /// `None` is returned. If the lines are parallel and intersect (at least part of a line is on + /// a part of the other line), inside that region is returned. pub fn intersection(line_a: &LineSegment<T>, line_b: &LineSegment<T>) -> Option<Vec2<T>> where T: ClosedSub + ClosedMul + ClosedDiv + Zero + RealField, @@ -104,10 +112,26 @@ impl<T: Scalar + Copy> LineSegment<T> { let dby = line_b.start.y - line_b.end.y; // Calculate determinant to see, if the lines are parallel or not - // TODO: Collinearity check? let d = (dax * dby) - (day * dbx); if d == T::zero() { - None + // The two line segments are parallel, check if one may be on the other. + if super::triplet_orientation(line_a.start, line_a.end, line_b.start) == TripletOrientation::Collinear + && super::triplet_orientation(line_a.start, line_a.end, line_b.end) == TripletOrientation::Collinear + { + if line_a.contains_collinear(line_b.start) { + Some(line_b.start) + } else if line_a.contains_collinear(line_b.end) { + Some(line_b.end) + } else if line_b.contains_collinear(line_a.start) { + Some(line_a.start) + } else if line_b.contains_collinear(line_a.end) { + Some(line_a.end) + } else { + None + } + } else { + None + } } else { let ad = (line_a.start.x * line_a.end.y) - (line_a.start.y * line_a.end.x); let bd = (line_b.start.x * line_b.end.y) - (line_b.start.y * line_b.end.x); @@ -190,4 +214,27 @@ mod test { assert!(!segment.contains_collinear(Vec2::new(3., 3.))); assert!(!segment.contains_collinear(Vec2::new(-1., 0.))); } + + #[test] + fn line_intersection() { + let segment_a = LineSegment::new(Vec2::new(-1., -1.), Vec2::new(1., 1.)); + let segment_b = LineSegment::new(Vec2::new(1., -1.), Vec2::new(-1., 1.)); + let segment_c = LineSegment::new(Vec2::new(1., -1.), Vec2::new(2., 2.)); + + assert!(LineSegment::intersect(&segment_a, &segment_b)); + assert!(LineSegment::intersect(&segment_b, &segment_c)); + assert!(!LineSegment::intersect(&segment_a, &segment_c)); + assert!(LineSegment::intersect(&segment_a, &segment_a)); + + assert_eq!( + LineSegment::intersection(&segment_a, &segment_b), + Some(Vec2::new(0., 0.)) + ); + assert_eq!( + LineSegment::intersection(&segment_b, &segment_c), + Some(Vec2::new(1., -1.)) + ); + assert_eq!(LineSegment::intersection(&segment_a, &segment_c), None); + assert!(LineSegment::intersection(&segment_a, &segment_a).is_some()); + } } diff --git a/src/math/mod.rs b/src/math/mod.rs index 829a3c5..c9c1c6e 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,3 +1,5 @@ +//! Useful mathematical operations in graphical contexts. + pub mod line_segment; pub mod polygon; pub mod rect; diff --git a/src/math/polygon/mod.rs b/src/math/polygon/mod.rs index 98b1570..1577f63 100644 --- a/src/math/polygon/mod.rs +++ b/src/math/polygon/mod.rs @@ -15,6 +15,7 @@ use std::ops::Neg; use thiserror::Error; /// Describes errors that can happen when handling polygons, especially on creation. +#[allow(missing_docs)] #[derive(Debug, Error)] pub enum PolygonError<T: Scalar + Copy> { /// Since the polygon is not allowed to be complex, self intersection is an error. @@ -28,6 +29,7 @@ pub enum PolygonError<T: Scalar + Copy> { EdgeNotFound(LineSegment<T>), } +/// Describes a non-complex (non-self-intersecting) polygon, currently without holes. #[derive(Clone, Debug, Deserialize, Serialize)] // TODO: Support polygons with holes pub struct Polygon<T: Scalar + Copy> { diff --git a/src/math/polygon/polygon_graph.rs b/src/math/polygon/polygon_graph.rs index 5a730b0..fd373dd 100644 --- a/src/math/polygon/polygon_graph.rs +++ b/src/math/polygon/polygon_graph.rs @@ -1,3 +1,8 @@ +//! Polygon graphs are used for a more general approach than polygons. +//! +//! They are not polygons, but often describe a polygon and make some algorithms on polygons faster +//! or possible, which may not be practical on the polygon data alone. + use super::Polygon; use crate::math::{self, LineSegment, Vec2}; use nalgebra::{RealField, Scalar}; @@ -16,7 +21,7 @@ struct EdgeIterator<'a, T: Scalar + Copy> { /// An undirected graph, that is optimised for polygon edge operations. Since edges of a polygon /// are an undirected graph, this structure also holds both directions. This makes it rather space -/// inefficient, but makes edge operations rather swift. ß +/// inefficient, but makes edge operations rather swift. #[derive(Debug)] pub struct PolygonGraph<T: Scalar + Copy + PartialOrd> { /// The nodes of the graph, together with their adjacency list. diff --git a/src/math/rect.rs b/src/math/rect.rs index b571644..6f993d1 100644 --- a/src/math/rect.rs +++ b/src/math/rect.rs @@ -1,3 +1,5 @@ +//! Rectangles where the sides are parallel to the x and y-axes. + use super::{LineSegment, Polygon, Surface, Vec2}; //use alga::general::{Additive, Identity}; use nalgebra::{ClosedAdd, ClosedSub, RealField, Scalar}; @@ -20,6 +22,8 @@ pub struct Rect<T: Scalar + Copy> { } impl<T: Scalar + Copy> Rect<T> { + /// Create a new Rectangle from the internal values, where it might be nicer to use a function + /// instead of setting the values directly. pub fn new(x: T, y: T, w: T, h: T) -> Self { Self { x, y, w, h } } diff --git a/src/math/surface.rs b/src/math/surface.rs index da265d8..ab1c703 100644 --- a/src/math/surface.rs +++ b/src/math/surface.rs @@ -1,3 +1,5 @@ +//! Surfaces, which are areas at a certain position in a vector space. + use super::{LineSegment, Polygon, Rect, Vec2}; use nalgebra::Scalar; diff --git a/src/math/triangle.rs b/src/math/triangle.rs index 35bdcec..b5c1bda 100644 --- a/src/math/triangle.rs +++ b/src/math/triangle.rs @@ -1,3 +1,5 @@ +//! Triangles. Nothing more, nothing less. + use super::{LineSegment, Vec2}; use alga::general::{ClosedMul, ClosedSub}; use nalgebra::{RealField, Scalar}; diff --git a/src/math/vec2.rs b/src/math/vec2.rs index b9e3215..b5706a0 100644 --- a/src/math/vec2.rs +++ b/src/math/vec2.rs @@ -1,5 +1,8 @@ +//! Two-dimensional vectors and useful operations on them. + use crate::math::Rect; use alga::general::{ClosedAdd, ClosedSub}; +use nalgebra::Point2; use nalgebra::{RealField, Scalar}; use num_traits::{NumCast, One, ToPrimitive}; use serde::{Deserialize, Serialize}; @@ -7,8 +10,9 @@ use std::cmp::Ordering; use std::convert::{From, Into}; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; use std::{fmt, mem}; -use nalgebra::Point2; +/// Describes a vector, which may be a point or a directed length, depending on the interpretation. +#[allow(missing_docs)] #[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Eq, Hash)] pub struct Vec2<T: Scalar + Copy> { pub x: T, @@ -16,10 +20,12 @@ pub struct Vec2<T: Scalar + Copy> { } impl<T: Scalar + Copy> Vec2<T> { + /// Create a new vector from its internal values. pub fn new(x: T, y: T) -> Self { Self { x, y } } + /// Finds the euclidian length and returns it. pub fn length(&self) -> T where T: RealField, @@ -27,6 +33,9 @@ impl<T: Scalar + Copy> Vec2<T> { (self.x * self.x + self.y * self.y).sqrt() } + /// Consumes the vector and returns a vector that is rotated by Pi/2 in clockwise direction. + /// This is a special case of rotation and the function is faster than using the nonspecific + /// rotation function. pub fn rotated_90_clockwise(mut self) -> Vec2<T> where T: One + Neg<Output = T> + MulAssign, @@ -36,6 +45,9 @@ impl<T: Scalar + Copy> Vec2<T> { self } + /// Consumes the vector and returns a vector that is rotated by Pi/2 in counterclockwise direction. + /// This is a special case of rotation and the function is faster than using the nonspecific + /// rotation function. pub fn rotated_90_counterclockwise(mut self) -> Vec2<T> where T: One + Neg<Output = T> + MulAssign, diff --git a/src/svg/mod.rs b/src/svg/mod.rs index fffc00b..af066f1 100644 --- a/src/svg/mod.rs +++ b/src/svg/mod.rs @@ -13,10 +13,10 @@ use style::Style; use svgtypes::{Path as SVGPath, PathSegment}; use xmltree::{Element, XMLNode}; -// Find the first XML-Node with the given name. With depth, the maximum depth the -// algorithm will search to can be set. If it is set to `None`, the algorithm will search the -// entire sub-tree. Returns `None` if no such child can be found. Returns itself, in case the root -// node is already of the name given. +/// Find the first XML-Node with the given name. With depth, the maximum depth the +/// algorithm will search to can be set. If it is set to `None`, the algorithm will search the +/// entire sub-tree. Returns `None` if no such child can be found. Returns itself, in case the root +/// node is already of the name given. pub fn find_first_node(root: Element, name: &str, depth: Option<usize>) -> Option<Element> { // The abort condition of this recursive function. If the element itself is of the required // name, return it. @@ -68,7 +68,10 @@ pub fn read_svg_file<P: AsRef<Path>>(file: P) -> io::Result<Element> { } } +/// Trait that indicates a struct is capable of drawing SVG-elements. pub trait DrawSVG { + /// Draw the elements given by `svg_data` and all its children to the implementor, with the + /// specified scale and translated by `position_px`. fn draw_svg(&mut self, svg_data: &Element, pixels_per_m: f32, position_px: Vec2<f32>); } diff --git a/src/svg/style.rs b/src/svg/style.rs index 78b800d..7a0110e 100644 --- a/src/svg/style.rs +++ b/src/svg/style.rs @@ -1,3 +1,8 @@ +//! Style options for SVG components. +//! +//! For information on SVG styles, pleas see the SVG documentation. +// TODO: There should be a lib available that can be integrated into the program + use raylib::ffi::Color; use std::str::FromStr; @@ -32,6 +37,7 @@ pub fn colour_from_html(html: &str) -> Option<Color> { /// The style of the end of the stroke. /// See [stroke-line-cap property](https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty) /// in the SVG Documentation. +#[allow(missing_docs)] pub enum StrokeLineCap { Butt, Round, @@ -41,6 +47,7 @@ pub enum StrokeLineCap { /// The style of the joining corners of the stroke. /// See [stroke-line-join property](https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty) /// in the SVG Documentation +#[allow(missing_docs)] pub enum StrokeLineJoin { Miter, Round, @@ -48,6 +55,7 @@ pub enum StrokeLineJoin { } /// The style of a path drawing instruction. +#[allow(missing_docs)] pub struct Style { pub fill: Option<Color>, pub stroke: Color, diff --git a/src/tool/deletion_tool.rs b/src/tool/deletion_tool.rs index cd38f6c..afd916a 100644 --- a/src/tool/deletion_tool.rs +++ b/src/tool/deletion_tool.rs @@ -1,3 +1,11 @@ +//! A meta tool for selecting parts of a map and removing them in a single operation. +//! +//! The user can draw a rectangle, which currently must have it's side parallel to the x and y-axes +//! of the world. With the first node placement, the mode is started, while the second placement would +//! finish the process and delete all elements that are *completely* contained in the rectangle +//! (partially contained items are not deleted) or abort it, in which case the selection is removed +//! and nothing is deleted. + use super::Tool; use crate::colours::DEFAULT_COLOURS; use crate::map::Map; @@ -5,11 +13,14 @@ use crate::math::{Rect, Surface, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; +/// The deletion tool itself. pub struct DeletionTool { deletion_rect: Option<(Vec2<f64>, Vec2<f64>)>, } impl DeletionTool { + /// Create a new deletion tool, there should only be one deletion tool and it should be created + /// by the editor. pub fn new() -> Self { Self { deletion_rect: None, diff --git a/src/tool/icon_tool.rs b/src/tool/icon_tool.rs index 09b0ac1..c9e671e 100644 --- a/src/tool/icon_tool.rs +++ b/src/tool/icon_tool.rs @@ -1,3 +1,6 @@ +//! Tool for creating icons. For explanation of icons, please see +//! [the icon module](crate::map::icon). + use crate::button::Button; use crate::config::IconToolKeys; use crate::map::icon_renderer::IconRenderer; @@ -8,10 +11,8 @@ use crate::transform::Transform; use raylib::core::drawing::RaylibDrawHandle; use std::rc::Rc; -pub const ICON_DIR: &str = "assets/icons"; - +/// The icon tool itself. pub struct IconTool { - // TODO: support svg keybindings: IconToolKeys, /// Saves whether the IconTool is the currently active tool or not. active: bool, @@ -22,6 +23,8 @@ pub struct IconTool { } impl IconTool { + /// Create a new icon tool that renders icons with the provided icon renderer. There should only + /// be one instance of the tool for the program, which should be created in the editor. pub fn new(keybindings: IconToolKeys, renderer: Rc<IconRenderer>) -> Self { Self { keybindings, diff --git a/src/tool/mod.rs b/src/tool/mod.rs index aeabf19..70534ac 100644 --- a/src/tool/mod.rs +++ b/src/tool/mod.rs @@ -1,3 +1,11 @@ +//! Tools, which are user interfaces that must be specifically selected in order to do something. +//! +//! As stated, a tool is not simply everything that helps a user do something, think of it more as a +//! mode which must be elected by the user to perform a task on a specific object type or a class of +//! objects. If instead the operation is defined by the state of the program, it is not a tool, since +//! the user didn't explicitly ask for this function to be performed, but it is rather an option +//! that's inherent to the situation the user finds themselves in. + pub mod deletion_tool; pub mod icon_tool; pub mod polygon_room_tool; @@ -20,16 +28,31 @@ use raylib::core::drawing::RaylibDrawHandle; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[repr(u8)] +/// The types of tools available in graf karto. For information about the tool itself, please see the +/// referenced Tool's documentation. pub enum ToolType { + /// See [super::RectRoomTool] for information on this tool. RectRoomTool, + /// See [super::PolygonRoomTool] for information on this tool. PolygonRoomTool, + /// See [super::WallTool] for information on this tool. WallTool, + /// See [super::IconTool] for information on this tool. IconTool, + /// See [super::DeletionTool] for information on this tool. DeletionTool, + /// See [super::SelectionTool] for information on this tool. SelectionTool, + /// Not a real tool but used to know how many tools are available. New tools must be added + /// above this variant. + // TODO: Since we now use a hash map in the editor, check if this is still necessary at all. NumTools, } +/// Base trait for tools. A tool is something that performs a specific action on one or more types of +/// elements. It must be selected in order to be active. For this reason, the selection tool is a +/// tool (it must be selected from the toolbox), but the dimension indicator for instance is not, +/// since it is automatically updated when applicable. pub trait Tool { /// Code that needs to be called when this Tool is activated or reactivated goes here. fn activate(&mut self) {} diff --git a/src/tool/polygon_room_tool.rs b/src/tool/polygon_room_tool.rs index de6714d..33daaf5 100644 --- a/src/tool/polygon_room_tool.rs +++ b/src/tool/polygon_room_tool.rs @@ -1,3 +1,5 @@ +//! Tool to create rooms in the shape of generic polygons. + use super::Tool; use crate::colours::DEFAULT_COLOURS; use crate::map::Map; @@ -10,6 +12,7 @@ struct UnfinishedPolygon { pub working_corner: Vec2<f64>, } +/// The tool itself. pub struct PolygonRoomTool { unfinished_polygon: Option<UnfinishedPolygon>, } @@ -76,6 +79,8 @@ impl UnfinishedPolygon { } impl PolygonRoomTool { + /// Create a new polygon room tool. There should be only one instance and it should be created + /// in the editor. pub fn new() -> Self { Self { unfinished_polygon: None, diff --git a/src/tool/rect_room_tool.rs b/src/tool/rect_room_tool.rs index 29173cd..7cb85bb 100644 --- a/src/tool/rect_room_tool.rs +++ b/src/tool/rect_room_tool.rs @@ -1,3 +1,7 @@ +//! The rectangle room tool is a specialised tool to create rooms of rectangular shape and with the +//! sides of the room parallel to the x and y-axes. This is often useful, when a quick room creation +//! is necessary and the shape of the room does not have to be very special. + use super::Tool; use crate::colours::DEFAULT_COLOURS; use crate::map::Map; @@ -5,6 +9,7 @@ use crate::math::{Rect, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; +/// The tool to create simple, rectangular rooms. 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. @@ -12,7 +17,8 @@ pub struct RectRoomTool { } impl RectRoomTool { - /// Create a new room tool where no rooms have been drawn yet. + /// Create a new room tool where no rooms have been drawn yet. Should be created only once per + /// program instance and by the editor. pub fn new() -> Self { Self { unfinished_rect: None, diff --git a/src/tool/selection_tool.rs b/src/tool/selection_tool.rs index 30f91bf..c790734 100644 --- a/src/tool/selection_tool.rs +++ b/src/tool/selection_tool.rs @@ -1,3 +1,11 @@ +//! Selection of items on the map. +//! +//! When selecting items on the map, the editor goes into a different mode than when editing a +//! specific kind of item. Actions that are available for specific types of items become +//! unavailable, while other actions that make use of the properties to a wide range of items +//! become available instead. +//! For this reason, the selection tool can be thought of as a kind of meta tool over tools. + use super::Tool; use crate::colours::DEFAULT_COLOURS; use crate::map::Map; @@ -5,11 +13,14 @@ use crate::math::{Rect, Surface, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; +/// The selection tool makes it possible to select any item on the map when activated. pub struct SelectionTool { selection_rect: Option<(Vec2<f64>, Vec2<f64>)>, } impl SelectionTool { + /// Create a new selection tool. There should be only one such tool per program instance and it + /// should be created in the editor. pub fn new() -> Self { Self { selection_rect: None, diff --git a/src/tool/wall_tool.rs b/src/tool/wall_tool.rs index b958799..123171c 100644 --- a/src/tool/wall_tool.rs +++ b/src/tool/wall_tool.rs @@ -1,3 +1,6 @@ +//! Tool to create walls. For information about walls, see also +//! [the wall module](crate::map::wall). + use super::Tool; use crate::map::Map; use crate::math::{LineSegment, Vec2}; @@ -5,11 +8,14 @@ use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; use raylib::ffi::{Color, Vector2}; +/// The wall tool to create solid barriers a player usually cannot cross. pub struct WallTool { unfinished_wall: Option<LineSegment<f64>>, } impl WallTool { + /// Create a new wall tool. There should only be one wall tool per program instance, which should + /// be created inside of the editor. pub fn new() -> Self { Self { unfinished_wall: None, diff --git a/src/transform.rs b/src/transform.rs index 7c1adbf..147956c 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -9,6 +9,8 @@ const STANDARD_PIXELS_PER_M: f64 = 64.; const MIN_PIXELS_PER_M: f64 = 5.; const MAX_PIXELS_PER_M: f64 = 10_000.; +/// A rigid 2D transformation. Since the translation must often be accessed directly and so far there +/// was no huge need for fancy transformation, this currently does not use any matrix transformations. pub struct Transform { /// The (not necessarily natural) number of pixels per m, i.e. the current scale of the map pixels_per_m: f64, @@ -119,9 +121,15 @@ impl Transform { self.translation_px += *by; } + /// Get the current scale with the number of pixels (as a real number) that makes up a meter of + /// actual map space. pub fn pixels_per_m(&self) -> f64 { self.pixels_per_m } + /// Get the current translation of the map on the screen. This is purely in pixels, rather than + /// meters, since a translation in meters does not make sense, because all map items have an + /// absolute position and the translation is merely used to see where on the screen anything + /// should be drawn. pub fn translation_px(&self) -> &Vec2<f64> { &self.translation_px } diff --git a/src/transformable.rs b/src/transformable.rs index c181bd6..b8212f9 100644 --- a/src/transformable.rs +++ b/src/transformable.rs @@ -1,3 +1,12 @@ +//! Traits for items that can be modified using a variety of matrix transformations. +//! +//! Items in the world may be resizable or rotateable etc. in a variety of different ways. This +//! module contains the traits that are used to interact with such objects in a way, that ensures the +//! item is not transformed illegaly. For example, an item may be scaleable by a factor, but that does +//! not necessarily mean that it can be scaled by different factors in x and y direction (stretched). +//! If need be, this module contains the different types of transformations and transformation +//! restrictions to make these features available and restricting them reliably. + use nalgebra::Matrix3; /// Trait for things that can be stretched and rotated etc. as one pleases without |
