1use earcutr;
2use fontdue::{Font, FontSettings, Metrics};
3use image::{ImageBuffer, Pixel, Rgb};
4use minifb::{Key, KeyRepeat, MouseButton, MouseMode};
5use rand;
6use std::fs;
7use std::io::Read;
8use std::path::Path;
9use std::time::Duration;
10
11const DEFAULT_NAME: &str = "Rust Render 101 Sketch";
12
13pub enum StrokeMode {
14 Circle,
15 Square,
16 Custom(fn(i8) -> Vec<(i8, i8)>),
17}
18
19pub enum FontMode {
20 TimesNewRoman,
21 Arial,
22 Custom {file_path: String},
23}
24
25pub enum ShapeType {
26 Polygon,
27 LinearSpline { loops: bool },
28 CubicBezierSpline { loops: bool },
29}
30
31pub struct RgbaColor {}
32
33impl RgbaColor {
34 pub fn random_rgb_color() -> u32 {
36 let mask: u32 = !(255u32 << 24);
37
38 mask & rand::random::<u32>()
39 }
40
41 pub fn random_rgba_color() -> u32 {
44 rand::random::<u32>()
45 }
46
47 pub fn greyscale_color(g: u8) -> u32 {
49 255u32 << 24 | ((g as u32) << 16) | ((g as u32) << 8) | g as u32
50 }
51
52 pub fn rgb_color(r: u8, g: u8, b: u8) -> u32 {
54 255u32 << 24 | ((r as u32) << 16) | ((g as u32) << 8) | b as u32
55 }
56
57 pub fn argb_color(a: u8, r: u8, g: u8, b: u8) -> u32 {
59 ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | b as u32
60 }
61
62 pub fn color_alpha(color: u32) -> u8 {
64 (color >> 24) as u8
65 }
66
67 pub fn color_red(color: u32) -> u8 {
69 (color >> 16) as u8
70 }
71
72 pub fn color_green(color: u32) -> u8 {
74 (color >> 8) as u8
75 }
76
77 pub fn color_blue(color: u32) -> u8 {
79 color as u8
80 }
81
82 fn color_u32_to_4xf32(color: u32) -> (f32, f32, f32, f32) {
83 (
84 RgbaColor::color_alpha(color) as f32 / 255f32,
85 RgbaColor::color_red(color) as f32 / 255f32,
86 RgbaColor::color_green(color) as f32 / 255f32,
87 RgbaColor::color_blue(color) as f32 / 255f32,
88 )
89 }
90
91 fn color_4xf32_to_u32(color: (f32, f32, f32, f32)) -> u32 {
92 RgbaColor::argb_color(
93 (color.0 * 255f32) as u8,
94 (color.1 * 255f32) as u8,
95 (color.2 * 255f32) as u8,
96 (color.3 * 255f32) as u8,
97 )
98 }
99
100 fn alpha_compose_alpha(p_a: f32, q_a: f32) -> f32 {
101 p_a + q_a - p_a * q_a
102 }
103
104 fn alpha_compose_channel(p_a: f32, p_c: f32, q_a: f32, q_c: f32, r_a: f32) -> f32 {
105 (p_c * p_a + q_c * q_a - p_c * p_a * q_a) / r_a
106 }
107
108 fn color_alpha_compose_color(color_p: u32, color_q: u32) -> u32 {
109 let (p_a, p_r, p_g, p_b) = RgbaColor::color_u32_to_4xf32(color_p);
110 let (q_a, q_r, q_g, q_b) = RgbaColor::color_u32_to_4xf32(color_q);
111
112 let result_a = RgbaColor::alpha_compose_alpha(p_a, q_a);
113 if result_a <= 0.0001f32 {
114 return RgbaColor::argb_color(0, 0, 0, 0);
115 }
116
117 let (result_r, result_g, result_b) = (
118 RgbaColor::alpha_compose_channel(p_a, p_r, q_a, q_r, result_a),
119 RgbaColor::alpha_compose_channel(p_a, p_g, q_a, q_g, result_a),
120 RgbaColor::alpha_compose_channel(p_a, p_b, q_a, q_b, result_a),
121 );
122
123 RgbaColor::color_4xf32_to_u32((result_a, result_r, result_g, result_b))
124 }
125}
126
127pub enum EasingType {
128 Linear,
129 SmoothStep,
130 QuadIn,
131 QuadOut,
132}
133
134impl EasingType {
135 fn ease(&self, t: f32) -> f32 {
136 match self {
137 EasingType::Linear => t,
138 EasingType::SmoothStep => {
139 let t2 = t * t;
140 -2f32 * t2 * t + 3f32 * t2
141 }
142 EasingType::QuadIn => t * t,
143 EasingType::QuadOut => -t * t + 2f32 * t,
144 }
145 }
146}
147
148#[derive(Clone)]
149pub enum TransitionTarget {
150 Points { points: Vec<(i32, i32)> },
151 Point { point: (i32, i32) },
152}
153impl TransitionTarget {
154 fn interpolate(t: f32, start: &Self, end: &Self) -> Self {
155 match (start, end) {
156 (
157 TransitionTarget::Points { points: start_points },
158 TransitionTarget::Points { points: end_points },
159 ) => {
160 let new_points = start_points
161 .iter()
162 .zip(end_points.iter())
163 .map(|(&(sx, sy), &(ex, ey))| {
164 (
165 (sx as f32 * (1f32 - t) + ex as f32 * t) as i32,
166 (sy as f32 * (1f32 - t) + ey as f32 * t) as i32,
167 )
168 })
169 .collect();
170
171 TransitionTarget::Points { points: new_points }
172 }
173 (
174 TransitionTarget::Point { point: start_point },
175 TransitionTarget::Point { point: end_point },
176 ) => TransitionTarget::Point {
177 point: (
178 (start_point.0 as f32 * (1f32 - t) + end_point.0 as f32 * t) as i32,
179 (start_point.1 as f32 * (1f32 - t) + end_point.1 as f32 * t) as i32,
180 ),
181 },
182 _ => {
183 panic!("Error: 'start' and 'end' parameters must be the same TransitionTarget type!")
184 }
185 }
186 }
187}
188
189pub struct Transition {
190 duration: f32,
191 elapsed: f32,
192 easing: EasingType,
193 start_state: TransitionTarget,
194 end_state: TransitionTarget,
195 current_state: TransitionTarget,
196}
197
198impl Transition {
199 pub fn initialize(
200 easing: EasingType,
201 duration: f32,
202 start_state: TransitionTarget,
203 end_state: TransitionTarget,
204 ) -> Self {
205 let current_state = start_state.clone();
206 Transition {
207 easing,
208 duration,
209 elapsed: 0.0,
210 start_state,
211 end_state,
212 current_state,
213 }
214 }
215
216 pub fn step(&mut self, delta_time: f32) {
217 self.elapsed += delta_time;
218 let t = (self.elapsed / self.duration).clamp(0.0, 1.0);
219 let eased_t = self.easing.ease(t);
220 self.current_state =
221 TransitionTarget::interpolate(eased_t, &self.start_state, &self.end_state);
222 }
223
224 pub fn is_finished(&self) -> bool {
225 self.elapsed >= self.duration
226 }
227
228 pub fn reset(&mut self) {
229 self.elapsed = 0.0;
230 }
231
232 pub fn reset_new(&mut self, new_start: TransitionTarget, new_end: TransitionTarget) {
233 self.reset();
234
235 self.start_state = new_start;
236 self.end_state = new_end;
237 }
238
239 pub fn get_current_points(&self) -> &Vec<(i32, i32)> {
240 match &self.current_state {
241 TransitionTarget::Points { points } => { points }
242 _ => {panic!("Error: called get_points() on transition with non-points target !")}
243 }
244 }
245
246 pub fn get_start_points(&self) -> &Vec<(i32, i32)> {
247 match &self.start_state {
248 TransitionTarget::Points { points } => { points }
249 _ => {panic!("Error: called get_points() on transition with non-points target !")}
250 }
251 }
252
253 pub fn get_end_points(&self) -> &Vec<(i32, i32)> {
254 match &self.end_state {
255 TransitionTarget::Points { points } => { points }
256 _ => {panic!("Error: called get_points() on transition with non-points target !")}
257 }
258 }
259
260 pub fn get_current_point(&self) -> &(i32, i32) {
261 match &self.current_state {
262 TransitionTarget::Point { point } => { point }
263 _ => {panic!("Error: called get_point() on transition with non-point target !")}
264 }
265 }
266}
267
268
269
270pub struct Geometry {}
271
272impl Geometry {
273 pub fn random(lower: f32, upper: f32) -> f32 {
275 lower + rand::random::<f32>() * (upper - lower)
276 }
277}
278
279pub trait State : Default {}
280
281pub struct Sketch<S: State> {
282 window: minifb::Window,
283 pixels: Vec<u32>,
284
285 pub width: usize,
286 pub height: usize,
287
288 pub is_looping: bool,
289 pub frame_count: u32,
290 pub delta_time: f32,
291
292 pub mouse_x: f32,
293 pub mouse_y: f32,
294 pub mouse_is_pressed: bool,
295 pub mouse_button: MouseButton,
296
297 fill_color: Option<u32>,
298 stroke_color: Option<u32>,
299 stroke_weight: i8,
300 stroke_mode: StrokeMode,
301
302 shape_vertices: Vec<(i32, i32)>,
303 shape_holes: Vec<usize>,
304 shape_type: ShapeType,
305
306 pub draw_method: Option<fn(&mut Self)>,
307 pub setup_method: Option<fn(&mut Self)>,
308 pub mouse_pressed_method: Option<fn(&mut Self)>,
309 pub mouse_released_method: Option<fn(&mut Self)>,
310 pub key_pressed_method: Option<fn(&mut Self, Key)>,
311 pub key_released_method: Option<fn(&mut Self, Key)>,
312
313 loaded_fonts: Vec<(Font, String)>,
314 font_index: usize,
315
316 pub state: S,
317}
318
319#[allow(dead_code)]
320impl<S: State> Sketch<S> {
321 pub fn from_size(width: usize, height: usize, state: S) -> Sketch<S> {
323 let window = minifb::Window::new(DEFAULT_NAME, width, height, minifb::WindowOptions::default())
324 .unwrap_or_else(|e| {
325 panic!("Unable to open window: {}", e);
326 });
327
328 let pixels: Vec<u32> = vec![0u32; width*height];
329
330 let mut sketch = Sketch {
331 window,
332 pixels,
333 width,
334 height,
335 is_looping: true,
336 frame_count: 0,
337 delta_time: 0.0,
338 mouse_x: 0.0,
339 mouse_y: 0.0,
340 mouse_is_pressed: false,
341 mouse_button: MouseButton::Left,
342 fill_color: Some(0),
343 stroke_color: Some(0),
344 stroke_weight: 1,
345 stroke_mode: StrokeMode::Circle,
346 shape_vertices: Vec::new(),
347 shape_holes: Vec::new(),
348 shape_type: ShapeType::Polygon,
349
350 draw_method: None,
351 setup_method: None,
352 mouse_pressed_method: None,
353 mouse_released_method: None,
354 key_pressed_method: None,
355 key_released_method: None,
356
357 loaded_fonts: Vec::new(),
358 font_index: 0,
359
360 state,
361 };
362
363 println!("LOADING FONT");
364
365 let tnr_file_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("fonts/Times New Roman.ttf");
366 let tnr_file_path_str = tnr_file_path.to_str().unwrap();
367 let times_new_roman = sketch.open_ttf_file(tnr_file_path_str);
368
369 let arial_file_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("fonts/Arial.ttf");
370 let arial_file_path_str = arial_file_path.to_str().unwrap();
371 let arial = sketch.open_ttf_file(arial_file_path_str);
372
373 sketch.loaded_fonts.push((times_new_roman, tnr_file_path_str.to_string()));
374 sketch.loaded_fonts.push((arial, arial_file_path_str.to_string()));
375
376 println!("FONT DONE");
377
378 sketch
379 }
380
381 fn handle_mouse(&mut self) {
385 (self.mouse_x, self.mouse_y) = self.window.get_mouse_pos(MouseMode::Clamp).unwrap();
386
387 let temp = self.mouse_is_pressed;
388 if self.window.get_mouse_down(MouseButton::Left) {
389 self.mouse_is_pressed = true;
390 self.mouse_button = MouseButton::Left;
391 }
392 else if self.window.get_mouse_down(MouseButton::Right) {
393 self.mouse_is_pressed = true;
394 self.mouse_button = MouseButton::Right;
395 }
396 else if self.window.get_mouse_down(MouseButton::Middle) {
397 self.mouse_is_pressed = true;
398 self.mouse_button = MouseButton::Middle;
399 }
400 else {
401 if temp {
402 if let Some(mouse_released_method) = self.mouse_released_method {
403 mouse_released_method(self);
404 }
405 }
406 self.mouse_is_pressed = false;
407 }
408
409 if !temp && self.mouse_is_pressed {
410 if let Some(mouse_pressed_method) = self.mouse_pressed_method {
411 mouse_pressed_method(self);
412 }
413 }
414 }
415
416 fn handle_keys(&mut self) {
418 let keys_pressed:Vec<Key> = self.window.get_keys_pressed(KeyRepeat::No);
419
420 for key in keys_pressed {
421 if let Some(key_pressed_method) = self.key_pressed_method {
422 key_pressed_method(self, key);
423 }
424 }
425
426 let keys_released:Vec<Key> = self.window.get_keys_released();
427
428 for key in keys_released {
429 if let Some(key_released_method) = self.key_released_method {
430 key_released_method(self, key);
431 }
432 }
433 }
434
435 pub fn run(&mut self) {
437 self.setup_method.expect("Setup method was not set !")(self);
438
439 let mut now = std::time::SystemTime::now();
440
441 while self.window.is_open() {
442 if self.is_looping {
443 self.delta_time = now.elapsed().unwrap().as_secs_f32();
444 now = std::time::SystemTime::now();
445
446 self.handle_mouse();
447 self.handle_keys();
448
449 self.draw_method.expect("Draw method was not set !")(self);
450 }
451
452 self.window.update_with_buffer(&self.pixels, self.width, self.height).unwrap();
453
454 if self.is_looping {
455 self.frame_count = self.frame_count + 1;
456 }
457 }
458 }
459
460 fn open_ttf_file(&self, file_path: &str) -> Font {
461 let mut file_content = Vec::new();
462 fs::File::open(file_path).unwrap().read_to_end(&mut file_content).unwrap();
463
464 Font::from_bytes(file_content, FontSettings::default()).unwrap()
465 }
466
467 fn generate_mask(&self) -> Vec<(i8, i8)> {
469 match self.stroke_mode {
470 StrokeMode::Circle => self.generate_circular_mask(),
471 StrokeMode::Square => self.generate_square_mask(),
472 StrokeMode::Custom(mask_func) => mask_func(self.stroke_weight),
473 }
474 }
475
476 fn apply_mask_as_stroke(&mut self, x: i32, y: i32, mask: &Vec<(i8, i8)>) {
478 for (i, j) in mask {
479 let (xi, yj) = (x + *i as i32, y + *j as i32);
480 self.stroke_pixel(xi, yj);
481 }
482 }
483
484 fn bresenham_plot_line_low(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) -> Vec<(i32, i32)> {
486 let dx = x1 - x0;
487 let mut dy = y1 - y0;
488 let mut yi = 1;
489 if dy < 0 {
490 yi = -1;
491 dy = -dy;
492 }
493 let mut delta = 2 * dy - dx;
494 let mut y = y0;
495
496 let mut result: Vec<(i32, i32)> = vec![];
497
498 for x in x0..=x1 {
499 result.push((x, y));
500 if delta > 0 {
501 y += yi;
502 delta += 2 * (dy - dx);
503 }
504 else {
505 delta += 2 * dy;
506 }
507 }
508 result
509 }
510
511 fn bresenham_plot_line_high(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) -> Vec<(i32, i32)> {
513 let mut dx = x1 - x0;
514 let dy = y1 - y0;
515 let mut xi = 1;
516 if dx < 0 {
517 xi = -1;
518 dx = -dx;
519 }
520 let mut delta = 2 * dx - dy;
521 let mut x = x0;
522
523 let mut result: Vec<(i32, i32)> = vec![];
524
525 for y in y0..=y1 {
526 result.push((x, y));
527 if delta > 0 {
528 x += xi;
529 delta += 2 * (dx - dy);
530 }
531 else {
532 delta += 2 * dx;
533 }
534 }
535 result
536 }
537
538 fn bresenham_plot_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) -> Vec<(i32, i32)> {
540 let points_to_plot: Vec<(i32, i32)>;
541
542 if (y1 - y0).abs() < (x1 - x0).abs() {
543 if x0 > x1 {
544 points_to_plot = self.bresenham_plot_line_low(x1, y1, x0, y0);
545 }
546 else {
547 points_to_plot = self.bresenham_plot_line_low(x0, y0, x1, y1);
548 }
549 }
550 else {
551 if y0 > y1 {
552 points_to_plot = self.bresenham_plot_line_high(x1, y1, x0, y0);
553 }
554 else {
555 points_to_plot = self.bresenham_plot_line_high(x0, y0, x1, y1);
556 }
557 }
558 points_to_plot
559 }
560
561 fn bresenham_plot_line_mask(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, mask: Vec<(i8, i8)>) {
563 let points_to_plot = self.bresenham_plot_line(x0, y0, x1, y1);
564
565 for point in points_to_plot {
566 self.apply_mask_as_stroke(point.0, point.1, &mask);
567 }
568 }
569
570 fn plot_on_all_octants(points: &mut Vec<(i32, i32)>, xc: i32, yc: i32, x: i32, y: i32) {
572 points.push((xc + x, yc + y));
573 points.push((xc + y, yc + x));
574
575 points.push((xc - x, yc + y));
576 points.push((xc - y, yc + x));
577
578 points.push((xc + x, yc - y));
579 points.push((xc + y, yc - x));
580
581 points.push((xc - x, yc - y));
582 points.push((xc - y, yc - x));
583 }
584
585 fn bresenham_plot_circle(&mut self, xc: i32, yc: i32, r: i32) -> Vec<(i32, i32)> {
587 let mut points: Vec<(i32, i32)> = vec![];
588
589 let (mut x, mut y) = (0, r);
590 let mut d = 3 - 2 * r;
591
592 while y >= x {
593 Self::plot_on_all_octants(&mut points, xc, yc, x, y);
594 if d > 0 {
595 y -= 1;
596 d += 4 * (x - y) + 10;
597 }
598 else {
599 d += 4 * x + 6;
600 }
601 x += 1;
602 }
603 points
604 }
605
606 fn circle_stroke(&mut self, xc: i32, yc: i32, r: i32) {
608 let mask = self.generate_mask();
609 let circle = self.bresenham_plot_circle(xc, yc, r);
610 for (x, y) in circle {
611 self.apply_mask_as_stroke(x, y, &mask)
612 }
613 }
614
615 fn circle_fill(&mut self, xc: i32, yc: i32, r: i32) {
617 for xi in -r..=r {
618 for yi in -r..=r {
619 if xi*xi + yi*yi > r*r {continue;}
620 let (x, y) = (xc + xi, yc + yi);
621
622 self.fill_pixel(x, y);
623 }
624 }
625 }
626
627 fn generate_circular_mask(&self) -> Vec<(i8, i8)> {
629 let mut mask: Vec<(i8, i8)> = Vec::new();
630
631 let stroke_weight_sq = self.stroke_weight * self.stroke_weight;
632
633 for x in -self.stroke_weight..=self.stroke_weight {
634 for y in -self.stroke_weight..=self.stroke_weight {
635 if x * x + y * y <= stroke_weight_sq {
636 mask.push((x, y));
637 }
638 }
639 }
640 mask
641 }
642
643 fn generate_square_mask(&self) -> Vec<(i8, i8)> {
645 let v1 = -self.stroke_weight;
646 let v2 = self.stroke_weight;
647
648 let mut mask: Vec<(i8, i8)> = Vec::new();
649
650 for v in v1..=v2 {
651 mask.push((v1, v));
652 mask.push((v2, v));
653
654 mask.push((v, v1));
655 mask.push((v, v2));
656 }
657 mask
658 }
659
660 fn triangle_flat_top(&mut self, x0: i32, y0: i32, base: i32, x1: i32, y1: i32) {
662 let left_edge = self.bresenham_plot_line(x0, y0, x1, y1);
663 let right_edge = self.bresenham_plot_line(x0 + base, y0, x1, y1);
664
665 let mut max_right_x: Vec<i32> = vec![i32::MIN; (y1 - y0 + 1) as usize];
666 let mut min_left_x: Vec<i32> = vec![i32::MAX; (y1 - y0 + 1) as usize];
667
668 for (x, y) in right_edge {
669 let i = (y - y0) as usize;
670 if x > max_right_x[i] {
671 max_right_x[i] = x;
672 }
673 }
674 for (x, y) in left_edge {
675 let i = (y - y0) as usize;
676 if x < min_left_x[i] {
677 min_left_x[i] = x;
678 }
679 }
680
681 for i in 0..max_right_x.len() {
682 let y = i as i32 + y0;
683 for x in min_left_x[i]..=max_right_x[i] {
684 self.change_pixel(x, y, self.fill_color.unwrap());
685 }
686 }
687 }
688
689 fn triangle_flat_bottom(&mut self, x0: i32, y0: i32, base: i32, x1: i32, y1: i32) {
691 let left_edge = self.bresenham_plot_line(x1, y1, x0, y0);
692 let right_edge = self.bresenham_plot_line(x1, y1, x0 + base, y0);
693
694 let mut max_right_x: Vec<i32> = vec![i32::MIN; (y0 - y1 + 1) as usize];
695 let mut min_left_x: Vec<i32> = vec![i32::MAX; (y0 - y1 + 1) as usize];
696
697 for (x, y) in right_edge {
698 let i = (y - y1) as usize;
699 if x > max_right_x[i] {
700 max_right_x[i] = x;
701 }
702 }
703 for (x, y) in left_edge {
704 let i = (y - y1) as usize;
705 if x < min_left_x[i] {
706 min_left_x[i] = x;
707 }
708 }
709
710 for i in 0..max_right_x.len() {
711 let y = i as i32 + y1;
712 for x in min_left_x[i]..=max_right_x[i] {
713 self.change_pixel(x, y, self.fill_color.unwrap());
714 }
715 }
716 }
717
718 fn triangle_fill(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32) {
720 let mut triangle = [(x0, y0), (x1, y1), (x2, y2)];
721 triangle.sort_by(|&(_, y1), &(_, y2)| y2.cmp(&y1));
722
723 let x_project = f32::round(
724 (triangle[1].1 - triangle[2].1) as f32
725 * ((triangle[2].0 - triangle[0].0) as f32 / (triangle[2].1 - triangle[0].1) as f32)
726 + triangle[2].0 as f32
727 ) as i32;
728
729 let mut x_start = x_project;
730 let mut base = triangle[1].0 - x_project;
731 if triangle[1].0 < x_project {
732 x_start = triangle[1].0;
733 base = -base;
734 }
735
736 self.triangle_flat_bottom(x_start, triangle[1].1, base, triangle[2].0, triangle[2].1);
737 self.triangle_flat_top(x_start, triangle[1].1, base, triangle[0].0, triangle[0].1);
738 }
739
740 fn triangle_stroke(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32) {
742 self.line(x0, y0, x1, y1);
743 self.line(x0, y0, x2, y2);
744 self.line(x2, y2, x1, y1);
745 }
746
747 fn change_pixel(&mut self, x: i32, y: i32, color: u32) {
748 if x < 0 ||y < 0 ||x >= self.width as i32 || y >= self.height as i32 {return;}
749
750 let (x, y) = (x as u32, y as u32);
751
752 if RgbaColor::color_alpha(color) == 255 {
753 self.set_pixel(x, y, color);
754 }
755 else {
756 self.mix_pixel(x, y, color);
757 }
758 }
759
760 fn set_pixel(&mut self, x: u32, y: u32, color: u32) {
762 let index = x as usize + y as usize * self.width;
763 self.pixels[index] = color;
764 }
765
766 fn mix_pixel(&mut self, x: u32, y: u32, color: u32) {
767 let index = x as usize + y as usize * self.width;
768 let new_color = RgbaColor::color_alpha_compose_color(self.pixels[index], color);
769 self.pixels[index] = new_color;
770 }
771
772 fn rect_fill(&mut self, x: i32, y: i32, w: i32, h: i32) {
774 for i in x..(x+w) {
775 for j in y..(y+h) {
776 self.fill_pixel(i, j);
777 }
778 }
779 }
780
781 fn rect_stroke(&mut self, x: i32, y: i32, w: i32, h: i32) {
783 self.line(x, y, x+w, y);
784 self.line(x, y, x, y+h);
785 self.line(x, y+h, x+w, y+h);
786 self.line(x+w, y, x+w, y+h);
787 }
788
789 fn polygon_fill(&mut self) {
791 let mut coords: Vec<f64> = Vec::new();
792
793 for point in &self.shape_vertices {
794 coords.push(point.0 as f64);
795 coords.push(point.1 as f64);
796 }
797
798 let triangles = earcutr::earcut(&coords, &self.shape_holes, 2).unwrap_or_else(|e| panic!("Triangulation error: {e}"));
799
800 println!("triangles :");
801
802 for i in 0..(triangles.len() / 3) {
803 let (a, b, c) = (triangles[3*i], triangles[3*i+1], triangles[3*i+2]);
804 self.triangle_fill(
805 self.shape_vertices[a].0, self.shape_vertices[a].1,
806 self.shape_vertices[b].0, self.shape_vertices[b].1,
807 self.shape_vertices[c].0, self.shape_vertices[c].1
808 )
809 }
810 }
811
812 fn polygon_stroke(&mut self) {
814 self.linear_spline(true);
815 }
816
817 fn polygon(&mut self) {
819 if self.fill_color.is_some() {
820 self.polygon_fill();
821 }
822 if self.stroke_color.is_some() {
823 self.polygon_stroke();
824 }
825 }
826
827 fn linear_spline(&mut self, loops: bool) {
829 let mut start = 0usize;
830 let mut hole = 0usize;
831 for i in 0..self.shape_vertices.len() {
832 if !loops && (i == self.shape_vertices.len() - 1 || (hole < self.shape_holes.len() && i == self.shape_holes[hole] - 1)) {
833 continue;
834 }
835
836 let (x0, y0) = self.shape_vertices[i];
837 let (x1, y1) = self.shape_vertices[
838 if i == self.shape_vertices.len() - 1 || (hole < self.shape_holes.len() && i == self.shape_holes[hole] - 1) {start}
839 else {i + 1}
840 ];
841
842 self.line(x0, y0, x1, y1);
843
844 if hole < self.shape_holes.len() && i == self.shape_holes[hole] {
845 hole += 1;
846 start = i;
847 }
848 }
849 }
850
851 pub fn name(&mut self, name: &str) {
855 self.window.set_title(name);
856 }
857
858 pub fn key_is_down(&self, key: Key) -> bool{
860 self.window.is_key_down(key)
861 }
862
863 pub fn no_loop(&mut self) {
865 self.is_looping = false;
866 }
867
868 pub fn framerate(&mut self, fps: usize) {
870 self.window.set_target_fps(fps);
871 }
872
873 pub fn fill(&mut self, color: u32) {
875 self.fill_color = Some(color);
876 }
877
878 pub fn no_fill(&mut self) {
880 self.fill_color = None;
881 }
882
883 pub fn stroke(&mut self, color: u32) {
885 self.stroke_color = Some(color);
886 }
887
888 pub fn no_stroke(&mut self) {
890 self.stroke_color = None;
891 }
892
893 pub fn stroke_weight(&mut self, weight: i8) {
895 self.stroke_weight = weight;
896 }
897
898 pub fn stroke_mode(&mut self, mode: StrokeMode) {
900 self.stroke_mode = mode;
901 }
902
903 pub fn fill_pixel(&mut self, x: i32, y: i32) {
905 if let Some(color) = self.fill_color {
906 self.change_pixel(x, y, color);
907 }
908 }
909
910 pub fn stroke_pixel(&mut self, x: i32, y: i32) {
912 if let Some(color) = self.stroke_color {
913 self.change_pixel(x, y, color);
914 }
915 }
916
917 pub fn rect(&mut self, x: i32, y: i32, w: i32, h: i32) {
919 if self.fill_color.is_some() {
920 self.rect_fill(x, y, w, h);
921 }
922 if self.stroke_color.is_some() {
923 self.rect_stroke(x, y, w, h);
924 }
925 }
926
927 pub fn triangle(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32) {
929 if self.fill_color.is_some() {
930 self.triangle_fill(x0, y0, x1, y1, x2, y2);
931 }
932 if self.stroke_color.is_some() {
933 self.triangle_stroke(x0, y0, x1, y1, x2, y2);
934 }
935 }
936
937 pub fn circle(&mut self, x: i32, y: i32, r: i32) {
939 if self.fill_color.is_some() {
940 self.circle_fill(x, y, r);
941 }
942 if self.stroke_color.is_some() {
943 self.circle_stroke(x, y, r);
944 }
945 }
946
947 pub fn background(&mut self, color: u32) {
949 let new_frame: Vec<u32> = vec![color;self.width*self.height];
950 self.pixels = new_frame;
951 }
952
953 pub fn save(&mut self, file_path: &str) {
955 let mut image = ImageBuffer::new(self.width as u32, self.height as u32);
956
957 for x in 0..self.width as u32 {
958 for y in 0..self.height as u32 {
959 let pixel: u32 = self.pixels[x as usize + y as usize * self.width];
960 image.put_pixel(x, y, Rgb([RgbaColor::color_red(pixel), RgbaColor::color_green(pixel), RgbaColor::color_blue(pixel)]));
961 }
962 }
963
964 image.save(file_path).unwrap_or_else(|e| {
965 panic!("Unable to save screenshot : {}", e);
966 });
967 }
968
969 pub fn image(&mut self, image_buffer: ImageBuffer<Rgb<u8>, Vec<u8>>, x: i32, y: i32) {
970 for i in 0..image_buffer.width() {
971 for j in 0..image_buffer.height() {
972 let (px, py) = (x + i as i32, y + j as i32);
973 if px < 0 || py < 0 || px as usize >= self.width || py as usize >= self.height {continue;}
974
975 let (r, g, b, a) = image_buffer.get_pixel(i, j).channels4();
976 self.change_pixel(px, py, RgbaColor::argb_color(a, r, g, b));
977 }
978 }
979 }
980
981 pub fn line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) {
983 let mask = self.generate_mask();
984 self.bresenham_plot_line_mask(x0, y0, x1, y1, mask);
985 }
986
987 pub fn begin_shape(&mut self, shape_type: ShapeType) {
989 self.shape_type = shape_type;
990 self.shape_vertices.clear();
991 self.shape_holes.clear();
992 }
993
994 pub fn vertex(&mut self, x: i32, y: i32) {
996 self.shape_vertices.push((x, y));
997 }
998
999 pub fn begin_hole(&mut self) {
1001 self.shape_holes.push(self.shape_vertices.len());
1002 }
1003
1004 pub fn end_shape(&mut self) {
1006 match self.shape_type {
1007 ShapeType::Polygon => {
1008 self.polygon();
1009 }
1010 ShapeType::LinearSpline {loops} => {
1011 self.linear_spline(loops);
1012 }
1013 ShapeType::CubicBezierSpline {loops} => {
1014 todo!()
1015 }
1016 }
1017 }
1018
1019 pub fn font(&mut self, font: FontMode) {
1020 match font {
1021 FontMode::TimesNewRoman => {
1022 self.font_index = 0;
1023 }
1024 FontMode::Arial => {
1025 self.font_index = 1;
1026 }
1027 FontMode::Custom { file_path } => {
1028 let mut i = 0usize;
1029 let fonts = &self.loaded_fonts;
1030 for (_, fp) in fonts {
1031 if *fp == file_path {
1032 self.font_index = i;
1033 break;
1034 }
1035 i += 1;
1036 }
1037
1038 let new_font = self.open_ttf_file(file_path.as_str());
1039 self.loaded_fonts.push((new_font, file_path));
1040 self.font_index = self.loaded_fonts.len() - 1;
1041 }
1042 }
1043 }
1044
1045 fn render_char(&mut self, metrics: Metrics, pixels: Vec<u8>, x_start: i32, y_start : i32) {
1046 for i in 0..metrics.width {
1047 for j in 0..metrics.height {
1048 let index = j*metrics.width + i;
1049 let (px, py) = (x_start + metrics.xmin + i as i32, y_start + j as i32 - metrics.height as i32 - metrics.ymin);
1050 if px < 0 || py < 0 || px as usize >= self.width || py as usize >= self.height {continue;}
1051
1052 let color_to_mix = RgbaColor::argb_color(
1053 pixels[index],
1054 RgbaColor::color_red(self.fill_color.unwrap()),
1055 RgbaColor::color_green(self.fill_color.unwrap()),
1056 RgbaColor::color_blue(self.fill_color.unwrap()),
1057 );
1058
1059 self.mix_pixel(px as u32, py as u32, color_to_mix);
1060 }
1061 }
1062 }
1063
1064 pub fn text(&mut self, string: &str, x: i32, y: i32) {
1065 let scale = 32.0;
1066 let mut x_start = x;
1067 let mut y_start = y;
1068
1069 for char in string.chars() {
1070 let (metrics, pixels) = {
1071 let mut font = &mut self.loaded_fonts[self.font_index].0;
1072 font.rasterize(char, scale)
1073 };
1074
1075 self.render_char(metrics, pixels, x_start, y_start);
1076
1077 x_start += metrics.advance_width as i32;
1078 y_start += metrics.advance_height as i32;
1079 }
1080 }
1081}
1082
1083
1084#[cfg(test)]
1085mod tests {
1086 use super::*;
1087
1088 #[derive(Default)]
1089 struct MyState {
1090 transition: Option<Transition>,
1091 }
1092
1093 impl State for MyState {}
1094
1095 fn setup(sketch: &mut Sketch<MyState>) {
1096 println!("SETUP WAS CALLED");
1097 sketch.framerate(60);
1098 sketch.name("Example Sketch");
1099
1100 let mut start: Vec<(i32, i32)> = Vec::new();
1101 start.push((50, 50));
1102 start.push((100, 50));
1103 start.push((100, 100));
1104 start.push((150, 100));
1105
1106 let mut end: Vec<(i32, i32)> = Vec::new();
1107 end.push((450, 200));
1108 end.push((500, 200));
1109 end.push((500, 250));
1110 end.push((450, 250));
1111
1112 let transition = Transition::initialize(
1113 EasingType::SmoothStep,
1114 3f32,
1115 TransitionTarget::Points {points: start},
1116 TransitionTarget::Points {points: end},
1117 );
1118
1119 sketch.state.transition = Some(transition);
1120
1121 sketch.stroke(RgbaColor::greyscale_color(255));
1122 sketch.stroke_weight(2);
1123 sketch.stroke_mode(StrokeMode::Circle);
1124 }
1125
1126 fn draw(sketch: &mut Sketch<MyState>) {
1127 if sketch.frame_count == 0 {
1128 println!("FIRST DRAW CALL");
1129 }
1130
1131 sketch.background(RgbaColor::greyscale_color(50));
1132
1133
1134 if let Some(mut transition) = sketch.state.transition.take() {
1135 sketch.begin_shape(ShapeType::LinearSpline {loops: false});
1136 let points = transition.get_current_points();
1137 for &(x, y) in points {
1138 sketch.vertex(x, y);
1139 }
1140 sketch.end_shape();
1141
1142 if sketch.frame_count > 60 && !transition.is_finished() {
1143 transition.step(sketch.delta_time);
1144 }
1145 sketch.state.transition = Some(transition);
1146 }
1147
1148
1149 sketch.font(FontMode::TimesNewRoman);
1155 let fps = ((1f32 / sketch.delta_time * 100f32) as u32) as f32 / 100f32;
1156 sketch.text(format!("FPS : {}", fps).as_str(), 50, 50);
1157
1158
1159
1160 }
1180
1181 #[test]
1182 fn testing() {
1183 println!("START");
1184 let mut state = MyState::default();
1185 let mut sketch = Sketch::<MyState>::from_size(640, 480, state);
1186
1187 sketch.setup_method = Some(setup);
1188 sketch.draw_method = Some(draw);
1189
1190 sketch.run();
1191
1192 println!("TESTING DONE")
1193 }
1194}