rust_render_101/
lib.rs

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    /// Creates random opaque rgb color
35    pub fn random_rgb_color() -> u32 {
36        let mask: u32 = !(255u32 << 24);
37
38        mask & rand::random::<u32>()
39    }
40
41    /// Creates random rgba color
42    /// (can be transparent, see RgbaColor::random_rgb_color() for opaque colors)
43    pub fn random_rgba_color() -> u32 {
44        rand::random::<u32>()
45    }
46
47    /// Creates rgba value based on greyscale u8 value
48    pub fn greyscale_color(g: u8) -> u32 {
49        255u32 << 24 | ((g as u32) << 16) | ((g as u32) << 8) | g as u32
50    }
51
52    /// Creates rgba value based on 3 rgb u8 values
53    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    /// Creates rgba value based on 4 u8 values
58    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    /// Extracts alpha channel as u8 from u32 color
63    pub fn color_alpha(color: u32) -> u8 {
64        (color >> 24) as u8
65    }
66
67    /// Extracts red channel as u8 from u32 color
68    pub fn color_red(color: u32) -> u8 {
69        (color >> 16) as u8
70    }
71
72    /// Extracts green channel as u8 from u32 color
73    pub fn color_green(color: u32) -> u8 {
74        (color >> 8) as u8
75    }
76
77    /// Extracts blue channel as u8 from u32 color
78    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    /// Creates a random f32 value between lower and upper bounds
274    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    /// Initializes a Sketch
322    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    // Private Methods
382
383    /// INTERNAL : interface between Sketch and minifb for mouse interactions
384    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    /// INTERNAL : interface between Sketch and minifb for keyboard interactions
417    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    /// main loop of the Sketch
436    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    /// Generates a mask based on current stroke mode
468    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    /// Pastes mask on Sketch at certain x,y coordinates
477    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    /// Traces a line using the low variant of the Bresenham algorithm
485    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    /// Traces a line using the high variant of the Bresenham algorithm
512    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    /// Traces a line integrating both low and high variants of the Bresenham algorithm
539    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    /// Applies mask along traced line
562    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    /// Plots a point on all octants of a space around xc,yc origin
571    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    /// Traces a circle using the Bresenham algorithm (Midpoint algorithm)
586    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    /// Applies stroke mask along traced circle
607    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    /// Fills a circular region of the sketch using a brute-force algorithm
616    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    /// Generates a circular mask with radius = stroke_weight
628    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    /// Generates a square mask with side_length = 2 * stroke_weight
644    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    /// Fills a flat triangle situated above its base
661    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    /// Fills a flat triangle situated below its base
690    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    /// Fills a triangle by separating into top and bottom parts
719    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    /// Strokes the 3 sides of a triangle
741    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    /// Changes the color of the pixel at x,y
761    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    /// Fills the inside of a rectangle at x,y with side lengths w,h
773    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    /// Strokes the 4 sides of a rectangle at x,y with side lengths w,h
782    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    /// Triangulates and fills current constructed polygon
790    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    /// Strokes all edges of the current constructed polygon
813    fn polygon_stroke(&mut self) {
814        self.linear_spline(true);
815    }
816
817    /// Draws a polygon based on current shape construction
818    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    /// Draws a linear spline based on the current shape construction, holes separate different chains
828    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    // Public Methods
852
853    /// Changes the name of the window
854    pub fn name(&mut self, name: &str) {
855        self.window.set_title(name);
856    }
857
858    /// Checks if the key: Key is currently pressed
859    pub fn key_is_down(&self, key: Key) -> bool{
860        self.window.is_key_down(key)
861    }
862
863    /// Stops the animation loop
864    pub fn no_loop(&mut self) {
865        self.is_looping = false;
866    }
867
868    /// Sets the framerate limit of the window
869    pub fn framerate(&mut self, fps: usize) {
870        self.window.set_target_fps(fps);
871    }
872
873    /// Sets the current fill color
874    pub fn fill(&mut self, color: u32) {
875        self.fill_color = Some(color);
876    }
877
878    /// Removes current fill color, drawn shapes will be hollow
879    pub fn no_fill(&mut self) {
880        self.fill_color = None;
881    }
882
883    /// Sets the current stroke color
884    pub fn stroke(&mut self, color: u32) {
885        self.stroke_color = Some(color);
886    }
887
888    /// Removes the current stroke color, drawn shapes will not have an outline
889    pub fn no_stroke(&mut self) {
890        self.stroke_color = None;
891    }
892
893    /// Sets the thickness of the outline
894    pub fn stroke_weight(&mut self, weight: i8) {
895        self.stroke_weight = weight;
896    }
897
898    /// Changes the current stroke mode, see StrokeMode
899    pub fn stroke_mode(&mut self, mode: StrokeMode) {
900        self.stroke_mode = mode;
901    }
902
903    /// Applies current fill color to pixel at x,y
904    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    /// Applies current stroke color to pixel at x,y
911    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    /// Draws a rectangle at x,y with side lengths w,h
918    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    /// Draws a triangle between points x0,y0 x1,y1 and x2,y2
928    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    /// Draws a circle at x,y with radius r
938    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    /// Fills the window with given color
948    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    /// Saves a png screenshot of the window
954    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    /// Draws a line between points x0,y0 and x1,y1
982    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    /// Indicates the start of a shape construction
988    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    /// Add a vertex to current shape construction
995    pub fn vertex(&mut self, x: i32, y: i32) {
996        self.shape_vertices.push((x, y));
997    }
998
999    /// Indicate start of a hole within the current shape construction
1000    pub fn begin_hole(&mut self) {
1001        self.shape_holes.push(self.shape_vertices.len());
1002    }
1003
1004    /// Indicate the end of the current shape construction and render constructed shape
1005    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.fill(RgbaColor::argb_color(255, 255, 20, 20));
1150        // sketch.rect(100, 100, 200, 50);
1151        //
1152        // sketch.fill(RgbaColor::greyscale_color(255));
1153        //
1154        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        // sketch.stroke(RgbaColor::greyscale_color(255));
1161        // sketch.stroke_weight(3);
1162        // sketch.fill(0);
1163        //
1164        // sketch.begin_shape(ShapeType::Polygon);
1165        // {
1166        //     for i in 0..10 {
1167        //         let angle: f32 = std::f32::consts::PI / 5f32  * i as f32;
1168        //         let (x, y) = (320f32 + 200f32 * angle.cos(), 240f32 + 200f32 * angle.sin());
1169        //         sketch.vertex(x as i32, y as i32);
1170        //     }
1171        //     sketch.begin_hole();
1172        //     for i in 0..4 {
1173        //         let angle: f32 = std::f32::consts::PI / 2f32  * i as f32;
1174        //         let (x, y) = (320f32 + 100f32 * angle.cos(), 240f32 + 100f32 * angle.sin());
1175        //         sketch.vertex(x as i32, y as i32);
1176        //     }
1177        // }
1178        // sketch.end_shape();
1179    }
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}