simple_term_renderer/
lib.rs

1/*
2
3    MIT License
4    
5    Copyright (c) 2022 Siandfrance
6    
7    Permission is hereby granted, free of charge, to any person obtaining a copy
8    of this software and associated documentation files (the "Software"), to deal
9    in the Software without restriction, including without limitation the rights
10    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11    copies of the Software, and to permit persons to whom the Software is
12    furnished to do so, subject to the following conditions:
13    
14    The above copyright notice and this permission notice shall be included in all
15    copies or substantial portions of the Software.
16    
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23    SOFTWARE.
24
25*/
26
27
28/* 
29    Credit to `redox-os` for the termion crate that helped with the design of this library
30    termion gitlab: https://gitlab.redox-os.org/redox-os/termion
31*/
32
33
34extern crate termios;
35extern crate image;
36extern crate palette;
37
38
39#[macro_use]
40pub mod math;
41pub mod img;
42
43pub mod rds;
44pub mod input;
45
46
47mod buffer2d;
48mod screen_buffer;
49
50
51#[cfg(test)]
52mod tests {
53
54    use crate::rds::Renderer;
55
56    use crate::math::{*};
57    use crate::img::*;
58    use crate::input::{Input, InputEvent, KeyEvent, MouseEvent};
59
60    use std::f32::consts::TAU;
61    use std::sync::{Arc, Mutex};
62    use std::time::{Duration, Instant};
63    use std::thread::sleep;
64
65
66    #[test]
67    fn renderer() {
68        // load an image and draw it on screen
69        let img = Arc::new(Mutex::new(Image::load("icon.png").unwrap()));
70
71        // get the renderer
72        let rdr = Renderer::get();
73
74
75        // draw a frame on screen
76        rdr.begin_draw();
77        rdr.draw_line((2, 7), (28, 6), Color::WHITE);
78        rdr.draw_rect((40, 15), (15, -10), Color::RED);
79        rdr.draw_rect_boundary((40, 15), (15, -10), Color::CHOCOLATE);
80        rdr.draw_ellipse_boundary((45, 25), (25, 8), Color::AQUAMARINE);
81
82        rdr.draw_ellipse_boundary((60, 30), (4, 4), Color::DEEP_PINK);
83
84        rdr.draw_rect((80, 5), (16, 8), Color::CORAL);
85        rdr.draw_whole_image_alpha(img.clone(), (80, 5), Color::BLACK);
86
87        rdr.ring_bell();
88
89        rdr.end_draw();
90        
91        
92        // wait for input and exit
93        Input::get().get_event_blocking();
94
95        // exit properly
96        Renderer::exit();
97    }
98
99
100    #[test]
101    fn input() {
102        let rdr = Renderer::get();
103        let inp = Input::get();
104        Input::enable_mouse();
105
106        let mut pos = Renderer::get_size() / 2;
107
108        loop {
109            let size = Renderer::get_size();
110
111            // manage input
112            match inp.get_event() {
113                Some(event) => {
114                    match event {
115                    InputEvent::Key(event) => match event {
116                        KeyEvent::Ctrl('c') => Renderer::exit(),
117                        KeyEvent::Up        => if pos.y >  1            {pos.y -= 1},
118                        KeyEvent::Down      => if pos.y <= size.y - 2   {pos.y += 1},
119                        KeyEvent::Left      => if pos.x >  1            {pos.x -= 1},
120                        KeyEvent::Right     => if pos.x <= size.x - 2   {pos.x += 1},
121                        _ => ()
122                    }
123                    InputEvent::Mouse(event) => match event {
124                        MouseEvent::ButtonPressed(_, mpos) | MouseEvent::Hold(_, mpos)
125                            => pos = mpos,
126                        _ => ()
127                    }
128                    _ => ()
129                }
130                }
131                None => ()
132            };
133
134            // draw on screen
135            rdr.begin_draw();
136            rdr.clear_color(Color::BLACK);
137            rdr.draw_rect_boundary(Vec2::ZERO, size - vec2!(1, 1), Color::BROWN);
138            rdr.draw_point(pos, Color::WHITE);
139            rdr.end_draw();
140        }
141    }
142
143
144    #[test]
145    fn text() {
146        let rdr = Renderer::get();
147        let inp = Input::get();
148
149        let dynamic_text = String::from("Some dynamic text !!!");
150        let dyn_text_char_count = dynamic_text.chars().count();
151
152        let mut dyn_text_pos: Vec2f = Vec2f::ZERO;
153        let mut dyn_text_speed: Vec2f = Vec2f::new(TAU * 10.0, 17.0);
154
155        let mut instant = Instant::now();
156        let max_frame_rate: f32 = 60.0;
157        let max_frame_time: f32 = 1.0 / max_frame_rate;
158
159        loop {
160            // Limit frame rate
161            let delta: f32 = instant.elapsed().as_secs_f32();
162            if delta < max_frame_time {
163                sleep(Duration::from_secs_f32(max_frame_time - delta));
164            }
165            let delta: f32 = instant.elapsed().as_secs_f32();
166            instant = Instant::now();
167            
168
169            // Handle input
170            match inp.get_event() {
171                Some(event) => {
172                    match event {
173                        InputEvent::Key(event) => match event {
174                            KeyEvent::Ctrl('c') => Renderer::exit(),
175                            _ => ()
176                        }
177                        _ => ()
178                    }
179                }
180                None => ()
181            };
182
183            // Update text values
184            let size = Vec2f::from(Renderer::get_size());
185            dyn_text_pos += dyn_text_speed * delta;
186            if dyn_text_pos.x <= 1.0 {
187                dyn_text_speed.x = dyn_text_speed.x.abs();
188            }
189            if dyn_text_pos.x >= size.x - dyn_text_char_count as f32 - 1.0 {
190                dyn_text_speed.x = -dyn_text_speed.x.abs();
191            }
192            if dyn_text_pos.y <= 1.0 {
193                dyn_text_speed.y = dyn_text_speed.y.abs();
194            }
195            if dyn_text_pos.y >= size.y - 1.0 {
196                dyn_text_speed.y = -dyn_text_speed.y.abs();
197            }
198
199
200            // Draw frame
201            rdr.begin_draw();
202            rdr.clear(Color::BLACK);
203            rdr.print_blended_text_raw(&format!("Time delta: {}", delta), (1, 15));
204
205            rdr.print_colored_text_raw(
206                &String::from("This text goes a bit off the window"),
207                (-2, 0),
208                Color::WHITE,
209                Color::BLACK
210            );
211
212            rdr.draw_rect((3, 5), (2, 6), Color::LIGHT_BLUE);
213            rdr.draw_rect((7, 3), (2, 6), Color::LIGHT_BLUE);
214            rdr.draw_rect((10, 3), (2, 6), Color::WHITE);
215            rdr.draw_rect((12, 3), (2, 6), Color::RED);
216            rdr.draw_rect((14, 3), (2, 6), Color::GREEN);
217            rdr.draw_rect((16, 3), (2, 6), Color::BLUE);
218            rdr.draw_rect((18, 3), (2, 6), Color::GRAY);
219            rdr.draw_rect((20, 3), (2, 6), Color::DARK_KHAKI);
220            rdr.draw_rect((22, 3), (2, 6), Color::CADET_BLUE);
221            rdr.draw_rect((24, 3), (2, 6), Color::PINK);
222            rdr.draw_rect((26, 3), (2, 6), Color::PURPLE);
223            rdr.draw_rect((28, 3), (2, 6), Color::GAINSBORO);
224            rdr.print_blended_text_raw(&String::from("I can draw text that will automagically change color to be readable"), (0, 4));
225
226            rdr.draw_rect((20, 20), (49, 7), Color::DARK_RED);
227            rdr.print_blended_text_raw(&dynamic_text, Vec2::from(dyn_text_pos));
228            rdr.end_draw();
229        }
230    }
231}