[+] Translate functions
This commit is contained in:
@@ -45,9 +45,259 @@ struct AsciiArt {
|
|||||||
credit: String,
|
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)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
struct Pixel<'a> {
|
struct Pixel<'a> {
|
||||||
color: &'a str,
|
color: &'a str,
|
||||||
char: char,
|
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();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user