ember_rs/
ember.rs

1use minifb::{Window, WindowOptions, MouseMode, MouseButton};
2pub use minifb::Key;
3
4use crate::text::FONT_DATA;
5
6pub struct Ember {
7    buffer: Vec<u32>,
8    pub width: i32,
9    pub height: i32,
10
11    window: Window
12}
13
14pub struct MouseInfo {
15    pub position: Option<(f32, f32)>,
16    pub left_button: bool,
17    pub right_button: bool,
18    pub middle_button: bool,
19    pub wheel: Option<(f32, f32)>
20}
21
22impl Ember {
23    pub fn new(title: &str, width: i32, height: i32, fps: f32) -> Self {
24        let mut window = Window::new(
25            title,
26            width as usize,
27            height as usize,
28            WindowOptions::default(),
29        ).unwrap_or_else(|e| {
30            panic!("{}", e);
31        });
32
33        if fps > 0.0 {
34            let fps: f32 = 1.0 / (fps / 1_000_000.0);
35            let fps: u64 = fps as u64;
36    
37            window.limit_update_rate(Some(std::time::Duration::from_micros(fps)));
38        }
39
40        let buffer: Vec<u32> = vec![0; (width * height) as usize];
41
42        Self { buffer, width, height, window }
43    }
44
45    pub fn update(&mut self) {
46        self.window
47            .update_with_buffer(&self.buffer[..], self.width as usize, self.height as usize)
48            .unwrap();
49    }
50
51    // Using the 'checked_...' methods for safer arithmetic operations.
52    pub fn set_pixel(&mut self, x: i32, y: i32, color: u32) {
53        if let Some(index) = (y as usize).checked_mul(self.width as usize).and_then(|i| i.checked_add(x as usize)) {
54            if index < self.buffer.len() {
55                self.buffer[index] = color;
56            }
57        }
58    }
59
60    pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: u32) {
61        let dx = (x2 - x1).abs();
62        let dy = (y2 - y1).abs();
63        let sx = if x1 < x2 { 1 } else { -1 };
64        let sy = if y1 < y2 { 1 } else { -1 };
65        let mut err = if dx > dy { dx } else { -dy } / 2;
66        let mut err2;
67    
68        let mut x = x1;
69        let mut y = y1;
70        loop {
71            self.set_pixel(x, y, color);
72            if x == x2 && y == y2 {
73                break;
74            }
75            err2 = err;
76            if err2 > -dx {
77                err -= dy;
78                x += sx;
79            }
80            if err2 < dy {
81                err += dx;
82                y += sy;
83            }
84        }
85    }
86
87    pub fn draw_line_width(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, width: i32, color: u32) {
88        let dx = (x1 - x0).abs();
89        let dy = (y1 - y0).abs();
90        let sx = if x0 < x1 { 1 } else { -1 };
91        let sy = if y0 < y1 { 1 } else { -1 };
92        let mut err = dx - dy;
93    
94        let mut x = x0;
95        let mut y = y0;
96    
97        let half_width = width / 2;
98    
99        while x != x1 || y != y1 {
100            // Draw a square around the point (x, y) with a side length of `width`
101            for i in -half_width..=half_width {
102                for j in -half_width..=half_width {
103                    self.set_pixel(
104                        x + i, 
105                        y + j, 
106                        color
107                    );
108                }
109            }
110    
111            let err2 = 2 * err;
112    
113            if err2 > -dy {
114                err -= dy;
115                x += sx;
116            }
117            if err2 < dx {
118                err += dx;
119                y += sy;
120            }
121        }
122    }
123
124    pub fn draw_rectangle(&mut self, x: i32, y: i32, width: i32, height: i32, color: u32) {
125        let x2 = x + width;
126        let y2 = y + height;
127
128        self.draw_line(x, y, x2, y, color);
129        self.draw_line(x2, y, x2, y2, color);
130        self.draw_line(x2, y2, x, y2, color);
131        self.draw_line(x, y2, x, y, color);
132    }
133
134    pub fn draw_rectangle_fill(&mut self, x: i32, y: i32, width: i32, height: i32, color: u32) {
135        for row in y..y + height {
136            self.draw_line(x, row, x + width, row, color);
137        }
138    }
139
140    pub fn draw_circle(&mut self, x0: i32, y0: i32, radius: i32, color: u32) {
141        let mut x = radius;
142        let mut y = 0;
143        let mut err = 0;
144    
145        while x >= y {
146            self.set_pixel(x0 + x, y0 + y, color);
147            self.set_pixel(x0 + y, y0 + x, color);
148            self.set_pixel(x0 - y, y0 + x, color);
149            self.set_pixel(x0 - x, y0 + y, color);
150            self.set_pixel(x0 - x, y0 - y, color);
151            self.set_pixel(x0 - y, y0 - x, color);
152            self.set_pixel(x0 + y, y0 - x, color);
153            self.set_pixel(x0 + x, y0 - y, color);
154    
155            y += 1;
156            err += 1 + 2 * y;
157            if 2 * (err - x) + 1 > 0 {
158                x -= 1;
159                err += 1 - 2 * x;
160            }
161        }
162    }
163
164    pub fn draw_circle_fill(&mut self, x0: i32, y0: i32, radius: i32, color: u32) {
165        let mut x = 0;
166        let mut y = radius;
167        let mut dp = 1 - radius;
168    
169        while x <= y {
170            self.draw_line(x0 - x, y0 - y, x0 + x, y0 - y, color);
171            self.draw_line(x0 - x, y0 + y, x0 + x, y0 + y, color);
172            self.draw_line(x0 - y, y0 - x, x0 + y, y0 - x, color);
173            self.draw_line(x0 - y, y0 + x, x0 + y, y0 + x, color);
174    
175            if dp < 0 {
176                dp = dp + 2 * x + 3;
177            } else {
178                dp = dp + 2 * (x - y) + 5;
179                y -= 1;
180            }
181            x += 1;
182        }
183    }
184
185    pub fn draw_text(&mut self, text: &str, x: i32, y: i32, scale: i32, color: u32) {
186        let mut current_x = x;
187    
188        for ch in text.to_uppercase().chars() {
189            let ascii = ch as usize;
190            if (ascii >= 65 && ascii <= 90) || (ascii >= 48 && ascii <= 57) || ch == '.'  || ch == ':' {
191                let font_index = if ascii >= 65 { 
192                    ascii - 65 
193                } else if ch == '.' {
194                    26 + 10 // skip alph + nums
195                } else if ch == ':' {
196                    26 + 10 + 1 // skip alph + nums + dot
197                } else { 
198                    ascii - 48 + 26 
199                };
200
201                let character = &FONT_DATA[font_index];
202    
203                for i in 0..8 {
204                    let row = character[i];
205                    for j in 0..8 {
206                        if row & (1 << (7 - j)) != 0 {
207                            for s_y in 0..scale {
208                                for s_x in 0..scale {
209                                    self.set_pixel(current_x + j * scale + s_x, y + i as i32 * scale + s_y, color);
210                                }
211                            }
212                        }
213                    }
214                }
215            }
216    
217            current_x += 8 * scale;
218        }
219    }
220
221    pub fn get_keys(&mut self) -> Vec<Key> {
222        self.window.get_keys()
223    }
224
225    pub fn process_keys<F>(&mut self, f: F)
226    where
227        F: FnMut(&Key)
228    {
229        self.window.get_keys().iter().for_each(f);
230    }
231
232    pub fn get_mouse_info(&self) -> MouseInfo {
233        let position = self.window.get_mouse_pos(MouseMode::Clamp).map(|(x, y)| (x as f32, y as f32));
234        let left_button = self.window.get_mouse_down(MouseButton::Left);
235        let right_button = self.window.get_mouse_down(MouseButton::Right);
236        let middle_button = self.window.get_mouse_down(MouseButton::Middle);
237        let wheel = self.window.get_scroll_wheel().map(|(x, y)| (x as f32, y as f32));
238
239        MouseInfo {
240            position,
241            left_button,
242            right_button,
243            middle_button,
244            wheel
245        }
246    }
247
248    pub fn should_close(&mut self) -> bool {
249        !self.window.is_open() || self.window.is_key_down(Key::Escape)
250    }
251
252    pub fn clear(&mut self) {
253        for pixel in self.buffer.iter_mut() {
254            *pixel = 0;
255        }
256    }
257
258    pub fn clear_color(&mut self, color: u32) {
259        for pixel in self.buffer.iter_mut() {
260            *pixel = color;
261        }
262    }
263}