aboutsummaryrefslogtreecommitdiff
path: root/src/client/map/icon_texture_manager.rs
blob: c6b7feacfbb173f7d2ca763d5e767dfde1ef5b5f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! 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";

#[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<f64>,
    /// 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");
            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.");

            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()
    }
}