1use std::{process, ops::Range};
52fn from_u8_rgb(r: u8, g: u8, b: u8) -> u32 {
53 let (r, g, b) = (r as u32, g as u32, b as u32);
54 (r << 16) | (g << 8) | b
55}
56
57type Unit = Result<(), WinErr>;
58
59macro_rules! unit {
60 () => {
61 Ok(())
62 };
63}
64#[macro_export] macro_rules! window {
66 ($width:expr, $height:expr, $name:tt) => {
67 WindowContainer::new($width, $height, $name, "FFFFFF")
68 };
69 (?$width:expr, $height:expr, $name:tt) => {
70 WindowContainer::new($width, $height, $name, "FFFFFF").unwrap()
71 };
72
73 ($width:expr, $height:expr, $name:tt, $color:tt) => {
74 WindowContainer::new($width, $height, $name, $color)
75 };
76 (?$width:expr, $height:expr, $name:tt, $color:tt) => {
77 WindowContainer::new($width, $height, $name, $color).unwrap()
78 };
79
80}
81
82pub fn hex_to_rgb(code:String) -> Result<u32, WinErr> {
84 if code.len() != 6 {return Err(WinErr::InvalidHexCode(code))}
85
86 let (r, gb) = code.split_at(2);
87 let (g, b) = gb.split_at(2);
88
89 let out = from_u8_rgb(
90 from_hex8(r.to_string())? as u8,
91 from_hex8(g.to_string())? as u8,
92 from_hex8(b.to_string())? as u8
93 );
94
95 Ok(out)
96}
97
98
99
100fn hex2num_code(c: char) -> Result<u8, WinErr> {
101 c.to_digit(16)
102 .map(|n| n as u8)
103 .ok_or(WinErr::InvalidHexChar(c))
104}
105pub fn from_hex8(input:String) -> Result<u32, WinErr> {
107 if input.len() > 8 {return Err(WinErr::InvalidHexCode(input))}
108
109 let mut output = 0_u32;
110
111 let iterator = input.chars().rev().enumerate();
112
113 for (i, c) in iterator {
114 let val = hex2num_code(c)? as u32;
115 output += val * 16_u32.pow(i as u32);
116 }
117
118 Ok(output)
119}
120fn num2hex_code(num: u8) -> Result<char, WinErr> {
121 match num {
122 0..=9 => Ok((num + 48) as char),
123 10..=15 => Ok((num + 55) as char),
124 _ => Err(WinErr::InvalidNumCode(num))
125 }
126}
127pub fn to_hex6(num: u32) -> Result<String, WinErr> {
129 if num > 16777215 {return Err(WinErr::OverflowNum(num))}
130
131 let mut hex_string = String::new();
132
133 for i in (0..6).rev() {
134 let shift = i * 4;
135 let hex_digit = ((num >> shift) & 0xF) as u8;
136 let hex_char = num2hex_code(hex_digit)?;
137
138 hex_string.push(hex_char);
139 }
140
141 Ok(hex_string)
142}
143pub fn to_hex2(num: u8) -> Result<String, WinErr> {
145 let mut hex_string = String::new();
146
147 for i in (0..2).rev() {
148 let shift = i * 4;
149 let hex_digit = (num >> shift) & 0xF;
150 let hex_char = num2hex_code(hex_digit)?;
151
152 hex_string.push(hex_char);
153 }
154
155 Ok(hex_string)
156}
157
158
159
160
161fn dist(p1:(usize, usize), p2:(usize, usize)) -> f64 {
162 let fp1 = (p1.0 as f64, p1.1 as f64);
163 let fp2 = (p2.0 as f64, p2.1 as f64);
164 ((fp2.0 - fp1.0).powi(2) + (fp2.1 - fp1.1).powi(2)).sqrt()
165
166}
167
168fn tupf64(tup:(usize, usize)) -> (f64, f64) {
169 (tup.0 as f64, tup.1 as f64)
170}
171
172fn perpendicular_line(slope: f64, midpoint: (f64, f64), length: f64) -> ((f64, f64), (f64, f64)) {
173 let m_perp = -1.0 / slope;
174 let dx = length / (2.0 * (1.0 + m_perp.powi(2)).sqrt());
175 let dy = m_perp * dx;
176 let start = (midpoint.0 - dx, midpoint.1 - dy);
177 let end = (midpoint.0 + dx, midpoint.1 + dy);
178 (start, end)
179}
180
181pub struct WindowContainer {
183 buffer:Vec<u32>,
184 window:minifb::Window,
185
186 width:usize,
187 height:usize,
188 pub bg_color:u32,
189
190 length:usize,
191
192}
193
194impl WindowContainer {
195 pub fn new(width:usize, height:usize, name:&str, color:&str) -> Result<Self, WinErr> {
197 let bg_color = hex_to_rgb(color.to_string())?;
198 if bg_color > 16777215 {return Err(WinErr::InvalidRGBValue(bg_color))}
199
200 let buffer = vec![bg_color; width * height];
201 let window = minifb::Window::new(name, width, height, minifb::WindowOptions::default()).unwrap();
202
203 let length = buffer.len();
204
205 Ok(WindowContainer {buffer, window, width, height, bg_color, length})
206 }
207
208 pub fn update(&mut self) -> Unit {
217 if self.window.is_key_down(minifb::Key::Escape) {process::exit(1)}
218
219
220 self.window.update_with_buffer(&self.buffer, self.width, self.height)?;
221 unit!()
222 }
223 pub fn clear(&mut self) {
236 self.buffer = self.buffer.iter().map(|_| self.bg_color).collect();
237 }
238
239 pub fn get(&self, pos:(usize, usize)) -> Result<String, WinErr> {
242 if pos.0 >= self.width {return Err(WinErr::InvalidPos(pos))}
243 if pos.1 >= self.height {return Err(WinErr::InvalidPos(pos))}
244
245 let i = (pos.1 * self.width) + pos.0;
246 let val = self.buffer[i];
247
248 to_hex6(val)
249 }
250 pub fn set(&mut self, pos:(usize, usize), val:&str) -> Result<(), WinErr> {
252 if pos.0 >= self.width {return Err(WinErr::InvalidPos(pos))}
253 if pos.1 >= self.height {return Err(WinErr::InvalidPos(pos))}
254
255 let i = (pos.1 * self.width) + pos.0;
256 self.buffer[i] = hex_to_rgb(val.to_string())?;
257
258 unit!()
259 }
260
261 pub fn iter(&self) -> std::vec::IntoIter<(String, (usize, usize))> {
280 let mut table:Vec<(String, (usize, usize))> = vec![];
281
282 for i in 0..self.length {
283 let string = to_hex6(self.buffer[i]).unwrap();
284
285 table.push((string, self.nth_to_pos(i)));
286 }
287
288 table.into_iter()
289 }
290
291
292 fn pos_to_nth(&self, pos:(usize, usize)) -> usize {
293 (pos.1 * self.width) + pos.0
294 }
295 fn nth_to_pos(&self, i:usize) -> (usize, usize) {
296 (i / self.width, i % self.width)
297 }
298
299 pub fn circle(&mut self, pos:(usize, usize), r:usize, color:&str) -> Unit {
302 if pos.0 >= self.width {return Err(WinErr::InvalidPos(pos))}
303 if pos.1 >= self.height {return Err(WinErr::InvalidPos(pos))}
304
305 let y_range = pos.1-r..pos.1+r;
306
307 let mut range_table: Vec<Range<usize>> = vec![];
308
309 for i in y_range {
310 range_table.push((pos.0+(self.width*i))-r..(pos.0+(self.width*i))+r)
311 }
312
313 for range in range_table {
314 for i in range {
315 let loc = self.nth_to_pos(i);
316
317 if dist(loc, pos) < r as f64 {self.buffer[i] = hex_to_rgb(color.to_string())?}
318 }
319 }
320
321
322 unit!()
323 }
324 pub fn line(&mut self, p1:(usize, usize), p2:(usize, usize), t:f64, color:&str) -> Unit {
326 let fp1 = tupf64(p1);
327 let fp2 = tupf64(p2);
328
329 let normal = (fp2.0-fp1.0, fp2.1-fp1.1);
331
332 let m = (fp2.1 - fp1.1) / (fp2.0 - fp1.0);
333 let p_line = perpendicular_line(m, fp1, t);
334 let p_normal = (p_line.1.0 - p_line.0.0, p_line.1.1 - p_line.0.1);
335
336 let step = (1.0/dist(p1, p2)) * 0.9999;
338 let p_step = 0.5/t;
339 let mut t = 0.0;
340 while t <= 1.0 {
342 let point = ((normal.0*t)+fp1.0, (normal.1*t)+fp1.1);
343 let usize_p = (point.0 as usize, point.1 as usize);
344
345 let loc = self.pos_to_nth(usize_p);
346
347
348 if usize_p.0 < self.width && usize_p.1 < self.height {
349 self.buffer[loc] = hex_to_rgb(color.to_string())?;
350 }
351
352 let p_line = perpendicular_line(m, point, t);
353
354 let mut j = 0.0;
355 while j <= 1.0 {
356 let p_point = ((p_normal.0*j) + p_line.0.0, (p_normal.1*j) + p_line.0.1);
357 let usize_p_point = (p_point.0 as usize, p_point.1 as usize);
358
359 let p_loc = self.pos_to_nth(usize_p_point);
360
361 if usize_p_point.0 < self.width && usize_p_point.1 < self.height {
362 self.buffer[p_loc] = hex_to_rgb(color.to_string())?;
363 }
364
365 j += p_step;
366 }
367
368 t += step;
369 }
370
371
372 unit!()
373 }
374
375}
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 #[test]
408 fn it_works() {
409 let mut window = window!(?255, 255, "Window", "FF00FF");
411 println!("{:?}", from_hex8(String::from("FFFFFF")));
412 loop {
413 window.update();
414 }
415 }
416 #[test]
417 fn uv_map() -> Unit {
418 let mut window = window!(?500, 500, "Window", "FFFFFF");
419
420 for (_val, pos) in window.iter() {
421 let r = to_hex2(pos.0 as u8).unwrap();
422 let g = to_hex2(pos.1 as u8).unwrap();
423 let string = format!("{r}{g}00");
424 window.set(pos, &string)?;
426 }
427
428 loop {
429 window.update();
430 }
431
432 unit!()
433 }
434
435 #[test]
436 fn shapes() -> Unit {
437 let mut window = WindowContainer::new(1000, 1000, "Window", "FFFFFF").unwrap();
438
439 window.circle((100, 100), 50, "CC00FF");
440
441 window.line((800, 1000), (800, 0), 50.0, "FF0000");
442 window.line((200, 200), (150, 230), 10.0, "0000FF");
443 loop {
446 window.update();
447 }
448
449 unit!()
450 }
451}
452
453#[derive(Debug)]
455pub enum WinErr {
456 MinifbError(minifb::Error),
457
458 InvalidHexCode(String),
459 InvalidHexChar(char),
460 InvalidNumCode(u8),
461 OverflowNum(u32),
462 InvalidRGBValue(u32),
463
464
465 InvalidIndex(usize),
466 InvalidPos((usize, usize)),
467}
468
469impl From<minifb::Error> for WinErr {
470 fn from(cause:minifb::Error) -> Self {
471 WinErr::MinifbError(cause)
472 }
473}
474