diff options
Diffstat (limited to 'src/map')
| -rw-r--r-- | src/map/data.rs | 6 | ||||
| -rw-r--r-- | src/map/icon.rs | 8 | ||||
| -rw-r--r-- | src/map/icon_renderer.rs | 14 | ||||
| -rw-r--r-- | src/map/mappable.rs | 14 | ||||
| -rw-r--r-- | src/map/mod.rs | 29 | ||||
| -rw-r--r-- | src/map/polygon_room.rs | 34 | ||||
| -rw-r--r-- | src/map/rect_room.rs | 23 | ||||
| -rw-r--r-- | src/map/wall.rs | 36 |
8 files changed, 120 insertions, 44 deletions
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 b348c4b..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::scaleable::Scaleable; 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,7 +22,14 @@ pub trait Mappable { /// Get the rectangle that contains the mappable object in its entirety without excess. fn bounding_rect(&self) -> Rect<f64>; - fn as_scaleable(&self) -> Option<&dyn Scaleable> { + /// 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 a209a7c..fd4122e 100644 --- a/src/map/polygon_room.rs +++ b/src/map/polygon_room.rs @@ -1,12 +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, Vec2}; -use crate::scaleable::Scaleable; +use crate::math::{self, Polygon, Rect, Triangle}; use crate::transform::Transform; +use crate::transformable::NonRigidTransformable; +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. @@ -15,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(), @@ -23,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()); } @@ -56,20 +65,21 @@ impl Mappable for PolygonRoom { Rect::bounding_rect_n(&self.data.corners()) } - fn as_scaleable(&self) -> Option<&dyn Scaleable> { - Some(self as &dyn Scaleable) + fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { + Some(self as &dyn NonRigidTransformable) } -} -impl Scaleable for PolygonRoom { - fn scale(&mut self, by: &Vec2<f64>) { - if by.x < 0. || by.y < 0. { - panic!("Cannot set dimensions with negative size"); - } + fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { + Some(self as &mut dyn NonRigidTransformable) + } +} +impl NonRigidTransformable for PolygonRoom { + fn apply_matrix(&mut self, matrix: &Matrix3<f64>) { for corner in self.data.corners_mut() { - corner.x *= by.x; - corner.y *= by.y; + *corner = matrix + .transform_point(&Point2::new(corner.x, corner.y)) + .into(); } self.retriangulate(); diff --git a/src/map/rect_room.rs b/src/map/rect_room.rs index 5008c63..6ed3ed6 100644 --- a/src/map/rect_room.rs +++ b/src/map/rect_room.rs @@ -1,14 +1,17 @@ +//! Deprecated simple rectangular room type. + use crate::colours::DEFAULT_COLOURS; use crate::map::Mappable; -use crate::math::{Rect, Vec2}; -use crate::scaleable::Scaleable; +use crate::math::Rect; use crate::transform::Transform; 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, @@ -16,6 +19,7 @@ pub struct RectRoom { } impl RectRoom { + #[allow(missing_docs)] pub fn from_data(data: RectRoomData) -> Self { RectRoom { data, @@ -47,21 +51,6 @@ impl Mappable for RectRoom { fn bounding_rect(&self) -> Rect<f64> { self.data.clone() } - - fn as_scaleable(&self) -> Option<&dyn Scaleable> { - Some(self as &dyn Scaleable) - } -} - -impl Scaleable for RectRoom { - fn scale(&mut self, by: &Vec2<f64>) { - if by.x < 0. || by.y < 0. { - panic!("Cannot set dimensions with negative size"); - } - - self.data.x *= by.x; - self.data.y *= by.y; - } } impl Deref for RectRoom { diff --git a/src/map/wall.rs b/src/map/wall.rs index 22393bb..f1748bb 100644 --- a/src/map/wall.rs +++ b/src/map/wall.rs @@ -1,13 +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::scaleable::Scaleable; use crate::transform::Transform; +use crate::transformable::NonRigidTransformable; +use nalgebra::Matrix3; use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; use std::ops::{Deref, DerefMut}; +/// 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, @@ -16,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, @@ -25,6 +36,7 @@ impl Wall { } } + /// Get the internal data for serialisation pub fn data(&self) -> &WallData { &self.data } @@ -81,18 +93,20 @@ impl Mappable for Wall { fn bounding_rect(&self) -> Rect<f64> { Rect::bounding_rect(self.data.start, self.data.end) } -} -impl Scaleable for Wall { - fn scale(&mut self, by: &Vec2<f64>) { - if by.x <= 0. || by.y <= 0. { - panic!("Cannot set dimensions with negative size"); - } + fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { + Some(self as &dyn NonRigidTransformable) + } + + fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { + Some(self as &mut dyn NonRigidTransformable) + } +} - self.data.start.x *= by.x; - self.data.start.y *= by.y; - self.data.end.x *= by.x; - self.data.end.y *= by.y; +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(); } } |
