[+] Translate functions

This commit is contained in:
Azalea Gui
2023-03-08 09:48:09 -05:00
parent 731b4b965e
commit a1578fd2ce
+250
View File
@@ -45,9 +45,259 @@ struct AsciiArt {
credit: String,
}
impl AsciiArt {
fn new(art: &str, credit: &str) -> Self {
// Trim empty line breaks from the art and calculate the height and width
let art = art.trim_matches('\n');
let h = art.lines().count();
let w = art.lines().map(|l| l.len()).max().unwrap_or(0);
Self {
art: art.to_string(),
h: h as u16,
w: w as u16,
credit: credit.to_string(),
}
}
}
#[derive(Clone, PartialEq, Eq)]
struct Pixel<'a> {
color: &'a str,
char: char,
}
fn snow_rand_velocity() -> (f32, f32) {
let mut rng = rand::thread_rng();
let vx = rng.gen_range(-SNOW_X_RAND..SNOW_X_RAND) * SNOW_SPEED;
let vy = rng.gen_range(1.0..2.0) * SNOW_SPEED;
(vx, vy)
}
fn create_snow(width: u16, height: u16) -> Vec<SnowParticle<'static>> {
let count: u16 = ((width * height) as f32 * SNOW_DENSITY) as u16;
let mut snow = Vec::with_capacity(count as usize);
let mut rng = rand::thread_rng();
for _ in 0..count {
let x = rng.gen_range(0.0..width as f32);
let y = rng.gen_range(0.0..height as f32);
let (vx, vy) = snow_rand_velocity();
let color = COLORS_STR[rng.gen_range(0..COLORS_STR.len())];
snow.push(SnowParticle { x, y, vx, vy, color });
}
snow
}
struct Main {
asc_cat: AsciiArt,
asc_tree: AsciiArt,
asc_house: AsciiArt,
width: u16,
height: u16,
x: u16,
buf: Vec<Vec<Option<Pixel<'static>>>>,
last_buf: Vec<Vec<Option<Pixel<'static>>>>,
last_update: Instant,
snow: Vec<SnowParticle<'static>>,
}
impl Main {
fn new() -> Self {
// Initialize the ascii art
let asc_cat = AsciiArt::new(
r#"
/\_/\
( | | )
> < "#, "Azalea");
let asc_tree = AsciiArt::new(
r#"
%%%,%%%%%%%
,'%% \\-*%%%%%%%
;%%%%%*% _%%%%"
,%%% \(_.*%%%%.
% *%%, ,%%%%*( '
%^ ,*%%% )\|,%%*%,_
*% \/ #).-"*%%*
_.) ,/ *%,
/)#(
/ \ "#, "b'ger from ascii.co.uk/art/tree");
let asc_house = AsciiArt::new(
r#"
_
,--l l--------,
/ /^/ /^/ / \
/_.--.___.--._/ \
| ,--, ,--, | ,|
,%| '--'._.'--' |,o%o
.*%|_,%%_| |_%%,_|#%%%*"#, "Modified from hjw from ascii.co.uk/art/house");
// Get the terminal size
let (width, height) = termion::terminal_size().unwrap();
// Initialize the buffers
let buf = vec![vec![None; width as usize]; height as usize];
let last_buf = buf.clone();
// Place cat x in the middle of the screen
let x = (width - asc_cat.w) / 2;
// Create snow particles
let snow = create_snow(width, height);
Self {
asc_cat, asc_tree, asc_house,
width, height, x,
buf, last_buf,
last_update: Instant::now(),
snow,
}
}
/// Update snow particles
fn update_snow(&mut self, dt: f32) {
// Loop through all snow particles
for p in &mut self.snow {
// Update the snow particle position
p.x += p.vx * dt;
p.y += p.vy * dt;
// If the snow particle is out of x bounds, wrap it around
if p.x < 0.0 {
p.x += self.width as f32;
} else if p.x > self.width as f32 {
p.x -= self.width as f32;
}
// If the snow particle is out of y bounds, reset it
if p.y > self.height as f32 {
let (vx, vy) = snow_rand_velocity();
p.vx = vx;
p.vy = vy;
p.y = 0.0;
}
// Draw the snow particle in the buffer
let x = p.x.round() as u16;
let y = p.y.round() as u16;
if x < self.width && y < self.height {
self.buf[y as usize][x as usize] = Some(Pixel { color: p.color, char: '*' });
}
}
}
/// Draw the buffer to the screen, diffing it with the last buffer, and only drawing the changed pixels
fn draw_buf(&mut self) -> Result<String> {
// Create a buffer string
let mut buf_str = String::with_capacity((self.width * self.height) as usize);
// Keep the last color
let mut last_color: &str = "";
// Keep the current cursor
let mut cursor = (0, 0);
let mut ensure_cursor = |x: u16, y: u16, a: u16, buf_str: &mut String|
if cursor != (x, y) {
// Go to the pixel position
buf_str.push_str(&Goto(x + 1, y + 1).to_string());
cursor = (x + a, y);
};
// Loop through all pixels in the buffer
for y in 0..self.height {
for x in 0..self.width {
// Get the pixel
let p = &self.buf[y as usize][x as usize];
// Get the last pixel
let last_p = &self.last_buf[y as usize][x as usize];
// If color changed and isn't the same as last color, update the color prefix
if let Some(p) = p && (last_p.is_none() || p.color != last_p.as_ref().unwrap().color) && p.color != last_color {
ensure_cursor(x, y, 0, &mut buf_str);
// Set the color
buf_str.push_str(p.color);
last_color = p.color;
}
// If the char changed, update the char
if let Some(p) = p && (last_p.is_none() || p.char != last_p.as_ref().unwrap().char) {
ensure_cursor(x, y, 1, &mut buf_str);
// Set the char
buf_str.push(p.char);
}
// If the pixel is empty but the last pixel wasn't, clear the pixel
if last_p.is_some() {
if p.is_none() {
ensure_cursor(x, y, 1, &mut buf_str);
// Clear the pixel
buf_str.push(' ');
}
// Clear the last pixel
self.last_buf[y as usize][x as usize] = None;
}
}
}
// Since last_buf is cleared, we can swap it with buf
mem::swap(&mut self.buf, &mut self.last_buf);
// Reset the color
buf_str.push_str(&Fg(termion::color::Reset).to_string());
// Flush the buffer
print!("{}", Goto(1, self.height as u16 + 1));
print!("{}", termion::cursor::Show);
io::stdout().flush()?;
Ok(buf_str)
}
fn start_loop(&mut self) {
// Clear the screen
print!("{}", termion::clear::All);
// Start the loop
loop {
// Get the current time
let now = Instant::now();
// Calculate the delta time
let dt = (now - self.last_update).as_secs_f32();
self.last_update = now;
// Update the snow
self.update_snow(dt);
// Draw the buffer, time it, and print it
let start = Instant::now();
let txt = self.draw_buf().unwrap();
let end = Instant::now();
let draw_time = (end - start).as_secs_f32();
print!("Draw time: {:.2}ms", draw_time * 1000.0);
print!("{}", txt);
// Set cursor to the bottom of the screen
print!("\x1b[9999;9999H");
// Sleep for 1/20th of a second
thread::sleep(Duration::from_millis(1000 / 20));
}
}
}
fn main() {
pretty_env_logger::init();
println!("Hello, world!");
// Create the Main object
let mut main = Main::new();
main.start_loop();
}