1use super::graphics::{Color, PixelMode, Sprite};
2use super::vector2::{Vi2d, Vu2d};
3
4use crate::graphics::DrawSpriteTrait;
5
6macro_rules! impl_trait {
7 ($trait:ident) => {
8 impl<T: SmartDrawingTrait> $trait for T {}
9 };
10}
11
12pub trait SmartDrawingTrait: DrawSpriteTrait {
15 fn get_size(&self) -> Vu2d;
17 fn get_textsheet(&self) -> &'static Sprite;
19 fn clear(&mut self, col: Color);
21 fn draw<P: Into<Vi2d>>(&mut self, pos: P, col: Color);
24 fn get_pixel<P: Into<Vi2d>>(&self, pos: P) -> Option<Color>;
26 fn get_pixel_mode(&self) -> PixelMode;
28 fn set_pixel_mode(&mut self, mode: PixelMode);
30 fn get_blend_factor(&self) -> f32;
33 fn set_blend_factor(&mut self, f: f32);
36}
37
38pub trait DottedShapeTrait: SmartDrawingTrait {
39 fn draw_line_dotted<P: Into<Vi2d>>(&mut self, p1: P, p2: P, col: Color, mut pattern: u32) {
41 let mut rol = || {
42 pattern = (pattern << 1) | (pattern >> 31);
43 pattern & 1 > 0
44 };
45 let p1: Vi2d = p1.into();
46 let p2: Vi2d = p2.into();
47 if p1.x == p2.x {
48 for y in if p2.y > p1.y {
50 p1.y..=p2.y
51 } else {
52 p2.y..=p1.y
53 } {
54 if rol() {
55 self.draw((p1.x, y), col);
56 }
57 }
58 } else if p1.y == p2.y {
59 for x in if p2.x > p1.x {
61 p1.x..=p2.x
62 } else {
63 p2.x..=p1.x
64 } {
65 if rol() {
66 self.draw((x, p1.y), col);
67 }
68 }
69 } else {
70 let (mut x0, mut y0) = (p1.x as i32, p1.y as i32);
71 let (mut x1, mut y1) = (p2.x as i32, p2.y as i32);
72 if (y1 - y0).abs() < (x1 - x0).abs() {
73 if x0 > x1 {
74 std::mem::swap(&mut x0, &mut x1);
75 std::mem::swap(&mut y0, &mut y1);
76 }
77 let dx = x1 - x0;
78 let mut dy = y1 - y0;
79 let mut yi = 1;
80 if dy < 0 {
81 yi = -1;
82 dy = -dy;
83 }
84 let mut d = 2 * dy - dx;
85 let mut y = y0;
86
87 for x in x0..x1 {
88 if x >= 0 && y >= 0 && rol() {
89 self.draw((x, y), col);
90 }
91 if d > 0 {
92 y += yi;
93 d -= 2 * dx;
94 }
95 d += 2 * dy;
96 }
97 } else {
98 if y0 > y1 {
99 std::mem::swap(&mut x0, &mut x1);
100 std::mem::swap(&mut y0, &mut y1);
101 }
102 let mut dx = x1 - x0;
103 let dy = y1 - y0;
104 let mut xi = 1;
105 if dx < 0 {
106 xi = -1;
107 dx = -dx;
108 }
109 let mut d = 2 * dx - dy;
110 let mut x = x0;
111
112 for y in y0..=y1 {
113 if x >= 0 && y >= 0 && rol() {
114 self.draw((x, y), col);
115 }
116 if d > 0 {
117 x += xi;
118 d -= 2 * dy;
119 }
120 d += 2 * dx;
121 }
122 }
123 }
124 }
125
126 fn draw_rect_dotted<P: Into<Vi2d>>(&mut self, pos: P, size: P, col: Color, pattern: u32) {
130 let Vi2d { x, y } = pos.into();
131 let Vi2d { x: w, y: h } = size.into() - Vi2d { x: 1, y: 1 };
132 self.draw_line_dotted((x, y), (x + w, y), col, pattern);
133 self.draw_line_dotted((x + w, y), (x + w, y + h), col, pattern);
134 self.draw_line_dotted((x + w, y + h), (x, y + h), col, pattern);
135 self.draw_line_dotted((x, y + h), (x, y), col, pattern);
136 }
137
138 fn draw_triangle_dotted<P: Into<Vi2d>>(
141 &mut self,
142 pts1: P,
143 pts2: P,
144 pts3: P,
145 col: Color,
146 pattern: u32,
147 ) {
148 let pts1: Vi2d = pts1.into();
149 let pts2: Vi2d = pts2.into();
150 let pts3: Vi2d = pts3.into();
151 self.draw_line_dotted(pts1, pts2, col, pattern);
152 self.draw_line_dotted(pts1, pts3, col, pattern);
153 self.draw_line_dotted(pts2, pts3, col, pattern);
154 }
155}
156
157pub trait ShapesTrait: SmartDrawingTrait {
160 fn draw_text<P: Into<Vi2d>>(&mut self, pos: P, scale: u32, col: Color, text: &str) {
167 #![allow(
168 clippy::cast_precision_loss,
169 clippy::cast_possible_wrap,
170 clippy::cast_sign_loss
171 )]
172 let Vi2d { x, y } = pos.into();
173 let scale: i32 = scale.try_into().unwrap();
174 let mut sx = 0;
175 let mut sy = 0;
176 for chr in text.chars() {
177 if chr == '\n' {
178 sx = 0;
179 sy += 8 * scale;
180 } else {
181 if !chr.is_ascii() {
182 continue;
183 }
184 let ox: i32 = ((chr as u32 - 32) % 16) as i32;
185 let oy: i32 = ((chr as u32 - 32) / 16) as i32;
186 if scale > 1 {
187 for i in 0..8i32 {
188 for j in 0..8i32 {
189 if self
190 .get_textsheet()
191 .get_pixel((i + ox * 8) as u32, (j + oy * 8) as u32)
192 .r
193 > 0
194 {
195 for is in 0..=(scale as i32) {
196 for js in 0..=(scale as i32) {
197 self.draw(
198 (x + sx + (i * scale) + is, y + sy + (j * scale) + js),
199 col,
200 );
201 }
202 }
203 }
204 }
205 }
206 } else {
207 for i in 0..8i32 {
208 for j in 0..8i32 {
209 if self
210 .get_textsheet()
211 .get_pixel((i + ox * 8) as u32, (j + oy * 8) as u32)
212 .r
213 > 0
214 {
215 self.draw((x + sx + i, y + sy + j), col);
216 }
217 }
218 }
219 }
220 }
221 sx += 8 * scale;
222 }
223 }
224
225 fn draw_line<P: Into<Vi2d>>(&mut self, p1: P, p2: P, col: Color) {
228 let p1: Vi2d = p1.into();
269 let p2: Vi2d = p2.into();
270 if p1.x == p2.x {
271 for y in if p2.y > p1.y {
273 p1.y..=p2.y
274 } else {
275 p2.y..=p1.y
276 } {
277 self.draw((p1.x, y), col);
278 }
279 } else if p1.y == p2.y {
280 for x in if p2.x > p1.x {
282 p1.x..=p2.x
283 } else {
284 p2.x..=p1.x
285 } {
286 self.draw((x, p1.y), col);
287 }
288 } else {
289 let (mut x0, mut y0) = (p1.x as i32, p1.y as i32);
290 let (mut x1, mut y1) = (p2.x as i32, p2.y as i32);
291 if (y1 - y0).abs() < (x1 - x0).abs() {
292 if x0 > x1 {
293 std::mem::swap(&mut x0, &mut x1);
294 std::mem::swap(&mut y0, &mut y1);
295 }
296 let dx = x1 - x0;
297 let mut dy = y1 - y0;
298 let mut yi = 1;
299 if dy < 0 {
300 yi = -1;
301 dy = -dy;
302 }
303 let mut d = 2 * dy - dx;
304 let mut y = y0;
305
306 for x in x0..x1 {
307 if x >= 0 && y >= 0 {
308 self.draw((x, y), col);
309 }
310 if d > 0 {
311 y += yi;
312 d -= 2 * dx;
313 }
314 d += 2 * dy;
315 }
316 } else {
317 if y0 > y1 {
318 std::mem::swap(&mut x0, &mut x1);
319 std::mem::swap(&mut y0, &mut y1);
320 }
321 let mut dx = x1 - x0;
322 let dy = y1 - y0;
323 let mut xi = 1;
324 if dx < 0 {
325 xi = -1;
326 dx = -dx;
327 }
328 let mut d = 2 * dx - dy;
329 let mut x = x0;
330
331 for y in y0..=y1 {
332 if x >= 0 && y >= 0 {
333 self.draw((x, y), col);
334 }
335 if d > 0 {
336 x += xi;
337 d -= 2 * dy;
338 }
339 d += 2 * dx;
340 }
341 }
342 }
343 }
344
345 fn draw_rect<P: Into<Vi2d>>(&mut self, pos: P, size: P, col: Color) {
348 let Vi2d { x, y } = pos.into();
349 let Vi2d { x: w, y: h } = size.into() - Vi2d { x: 1, y: 1 };
350 self.draw_line((x, y), (x + w, y), col);
351 self.draw_line((x + w, y), (x + w, y + h), col);
352 self.draw_line((x + w, y + h), (x, y + h), col);
353 self.draw_line((x, y + h), (x, y), col);
354 }
355
356 fn fill_rect<P: Into<Vi2d>>(&mut self, pos: P, size: P, col: Color) {
359 let Vi2d { x, y } = pos.into();
360 let Vi2d { x: w, y: h } = size.into();
361 for nx in x..(x + w) {
362 self.draw_line((nx, y), (nx, y + h), col);
363 }
364 }
365
366 fn draw_circle<P: Into<Vi2d>>(&mut self, pos: P, r: u32, col: Color) {
368 let Vi2d { x, y } = pos.into();
369 let r_i32: i32 = r.try_into().unwrap();
370 let x = x as i32;
371 let y = y as i32;
372 let mut x0: i32 = 0;
373 let mut y0: i32 = r_i32;
374 let mut d: i32 = 3i32 - 2i32 * r_i32;
375 if r == 0 {
376 return;
377 }
378 while y0 >= x0 {
379 self.draw(((x + x0), (y - y0)), col);
380 self.draw(((x + y0), (y - x0)), col);
381 self.draw(((x + y0), (y + x0)), col);
382 self.draw(((x + x0), (y + y0)), col);
383
384 self.draw(((x - x0), (y + y0)), col);
385 self.draw(((x - y0), (y + x0)), col);
386 self.draw(((x - y0), (y - x0)), col);
387 self.draw(((x - x0), (y - y0)), col);
388
389 x0 += 1;
390 if d < 0 {
391 d += 4 * x0 + 6;
392 } else {
393 y0 -= 1;
394 d += 4 * (x0 - y0) + 10;
395 }
396 }
397 }
398
399 fn fill_circle<P: Into<Vi2d>>(&mut self, pos: P, r: u32, col: Color) {
401 let Vi2d { x, y } = pos.into();
402 let r_i32: i32 = r.try_into().unwrap();
403 let x = x as i32;
404 let y = y as i32;
405 let mut x0: i32 = 0;
406 let mut y0: i32 = r_i32;
407 let mut d: i32 = 3 - 2 * r_i32;
408 if r == 0 {
409 return;
410 }
411 while y0 >= x0 {
412 self.draw_line((x - x0, y - y0), (x + x0, y - y0), col);
413 self.draw_line((x - y0, y - x0), (x + y0, y - x0), col);
414 self.draw_line((x - x0, y + y0), (x + x0, y + y0), col);
415 self.draw_line((x - y0, y + x0), (x + y0, y + x0), col);
416 x0 += 1;
417 if d < 0 {
418 d += 4 * x0 + 6;
419 } else {
420 y0 -= 1;
421 d += 4 * (x0 - y0) + 10;
422 }
423 }
424 }
425
426 fn draw_triangle<P: Into<Vi2d>>(&mut self, pts1: P, pts2: P, pts3: P, col: Color) {
428 let pts1: Vi2d = pts1.into();
429 let pts2: Vi2d = pts2.into();
430 let pts3: Vi2d = pts3.into();
431 self.draw_line(pts1, pts2, col);
432 self.draw_line(pts1, pts3, col);
433 self.draw_line(pts2, pts3, col);
434 }
435
436 fn fill_triangle<P: Into<Vi2d>>(&mut self, pts1: P, pts2: P, pts3: P, col: Color) {
438 #![allow(clippy::cast_precision_loss)]
439 use std::cmp::{max, min};
440
441 let pts1: Vi2d = pts1.into();
442 let pts2: Vi2d = pts2.into();
443 let pts3: Vi2d = pts3.into();
444 self.draw_triangle(pts1, pts2, pts3, col);
445
446 let pts1 = (pts1.x as i32, pts1.y as i32);
447 let pts2 = (pts2.x as i32, pts2.y as i32);
448 let pts3 = (pts3.x as i32, pts3.y as i32);
449 let centroid = (
450 ((pts1.0 + pts2.0 + pts3.0) as f32 / 3f32),
451 ((pts1.1 + pts2.1 + pts3.1) as f32 / 3f32),
452 );
453 let lines = (
454 (
455 pts1.1 - pts2.1,
456 pts2.0 - pts1.0,
457 (pts1.1 - pts2.1) * pts2.0 + (pts2.0 - pts1.0) * pts2.1,
458 ),
459 (
460 pts3.1 - pts1.1,
461 pts1.0 - pts3.0,
462 (pts3.1 - pts1.1) * pts1.0 + (pts1.0 - pts3.0) * pts1.1,
463 ),
464 (
465 pts2.1 - pts3.1,
466 pts3.0 - pts2.0,
467 (pts2.1 - pts3.1) * pts3.0 + (pts3.0 - pts2.0) * pts3.1,
468 ),
469 );
470 let iterx = {
472 let x1 = min(min(pts1.0, pts2.0), pts3.0);
473 let x2 = max(max(pts1.0, pts2.0), pts3.0);
474 if x1 > x2 {
475 x2..=x1
476 } else {
477 x1..=x2
478 }
479 };
480 let itery = {
481 let y1 = min(min(pts1.1, pts2.1), pts3.1);
482 let y2 = max(max(pts1.1, pts2.1), pts3.1);
483
484 if y1 > y2 {
485 y2..=y1
486 } else {
487 y1..=y2
488 }
489 };
490 let l_mul = (
491 if (lines.0).0 as f32 * centroid.0 + (lines.0).1 as f32 * centroid.1
492 - (lines.0).2 as f32
493 >= 0f32
494 {
495 1
496 } else {
497 -1
498 },
499 if (lines.1).0 as f32 * centroid.0 + (lines.1).1 as f32 * centroid.1
500 - (lines.1).2 as f32
501 >= 0f32
502 {
503 1
504 } else {
505 -1
506 },
507 if (lines.2).0 as f32 * centroid.0 + (lines.2).1 as f32 * centroid.1
508 - (lines.2).2 as f32
509 >= 0f32
510 {
511 1
512 } else {
513 -1
514 },
515 );
516
517 for x in iterx {
518 for y in itery.clone() {
519 if ((lines.0).0 * x + (lines.0).1 * y - (lines.0).2) * l_mul.0 >= 0
520 && ((lines.1).0 * x + (lines.1).1 * y - (lines.1).2) * l_mul.1 >= 0
521 && ((lines.2).0 * x + (lines.2).1 * y - (lines.2).2) * l_mul.2 >= 0
522 {
523 self.draw((x, y), col);
524 }
525 }
526 }
527 }
528}
529pub trait SpriteTrait: SmartDrawingTrait {
531 fn draw_sprite<P: Into<Vi2d>>(
536 &mut self,
537 pos: P,
538 scale: u32,
539 sprite: &Sprite,
540 flip: (bool, bool),
541 ) {
542 #![allow(
543 clippy::cast_precision_loss,
544 clippy::cast_possible_wrap,
545 clippy::cast_sign_loss
546 )]
547 let Vi2d { x, y } = pos.into();
548 let (mut fxs, mut fxm) = (0i32, 1i32);
549 let (mut fys, mut fym) = (0i32, 1i32);
550 let mut fx: i32;
551 let mut fy: i32;
552 if flip.0 {
553 fxs = sprite.width() as i32 - 1;
554 fxm = -1;
555 }
556 if flip.1 {
557 fys = sprite.height() as i32 - 1;
558 fym = -1;
559 }
560 if scale > 1 {
561 fx = fxs;
562 for i in 0..(sprite.width() as i32) {
563 fy = fys;
564 for j in 0..(sprite.height() as i32) {
565 for is in 0..(scale as i32) {
566 for js in 0..(scale as i32) {
567 self.draw(
568 (x + i * (scale as i32) + is, y + j * (scale as i32) + js),
569 sprite.get_pixel(fx as u32, fy as u32),
570 );
571 }
572 }
573 fy += fym;
574 }
575 fx += fxm;
576 }
577 } else {
578 fx = fxs;
579 for i in 0..(sprite.width() as i32) {
580 fy = fys;
581 for j in 0..(sprite.height() as i32) {
582 self.draw((x + i, y + j), sprite.get_pixel(fx as u32, fy as u32));
583 fy += fym;
584 }
585 fx += fxm;
586 }
587 }
588 }
589 fn draw_partial_sprite<P: Into<Vi2d>>(
595 &mut self,
596 coords: P,
597 sprite: &Sprite,
598 o: P,
599 size: P,
600 scale: u32,
601 flip: (bool, bool),
602 ) {
603 #![allow(
604 clippy::cast_precision_loss,
605 clippy::cast_possible_wrap,
606 clippy::cast_sign_loss
607 )]
608 let Vi2d { x, y } = coords.into();
609 let Vi2d { x: ox, y: oy } = o.into();
610 let Vi2d { x: w, y: h } = size.into();
611
612 let (mut fxs, mut fxm) = (0i32, 1i32);
613 let (mut fys, mut fym) = (0i32, 1i32);
614 let mut fx: i32;
615 let mut fy: i32;
616 if flip.0 {
617 fxs = w as i32 - 1;
618 fxm = -1;
619 }
620 if flip.1 {
621 fys = h as i32 - 1;
622 fym = -1;
623 }
624
625 if scale > 1 {
626 fx = fxs;
627 for i in 0..w {
628 fy = fys;
629 for j in 0..h {
630 for is in 0..(scale as i32) {
631 for js in 0..(scale as i32) {
632 self.draw(
633 (x + i * (scale as i32) + is, y + j * (scale as i32) + js),
634 sprite.get_pixel((fx + ox) as u32, (fy + oy) as u32),
635 );
636 }
637 }
638 fy += fym;
639 fx += fxm;
640 }
641 }
642 } else {
643 fx = fxs;
644 for i in 0..w {
645 fy = fys;
646 for j in 0..h {
647 self.draw(
648 (x + i, y + j),
649 sprite.get_pixel(fx as u32 + ox as u32, fy as u32 + oy as u32),
650 );
651 fy += fym;
652 }
653 fx += fxm;
654 }
655 }
656 }
657}
658
659impl_trait!(SpriteTrait);
660impl_trait!(ShapesTrait);
661impl_trait!(DottedShapeTrait);