//! 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}; 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"; /// Default amount of pixels per meter, which is used, when an icon does not have /// an associated ron-file, which sets them explicitly. pub const ICON_DEFAULT_PPM: f64 = 256.; #[derive(Deserialize)] pub(super) 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). pub anchor: Vec2, /// The scale of the icon as expressed in image pixels per real meter. pub pixels_per_m: f64, } /// Manager for all icon texture or rendering data. pub struct IconTextureManager { texture_data: Vec<(Texture2D, IconFileInfo)>, } impl IconTextureManager { /// Create a new icon manager. This loads all textures and information for icons that is needed /// to draw them to the screen. Usually, there should be only one IconTextureManager, or at least one /// manager 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 * 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 texture_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"); /* Try to read the ron file containing information about this icon. * If no ron-file is found, the anchor is set to the middle of the * texture, and the default pixels per meter value is used. */ let icon_info = if let Ok(ron) = File::open(file) { from_reader(ron).expect("Could not parse icon info from reader.") } else { IconFileInfo { anchor: Vec2::new(texture.width as f64, texture.height as f64), pixels_per_m: ICON_DEFAULT_PPM, } }; texture_data.push((texture, icon_info)); } Self { texture_data } } /// Get the textures 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 texture manager. pub fn num_icons(&self) -> usize { self.texture_data.len() } }