Return back to the "Create a Function to Read ASCII-art from Text File" Page
/* main.rs */ mod c51; mod d51; mod extras; use c51::*; use d51::*; use extras::*; fn main() { let (speed, fly, kind, oops) = parse_opts(); draw_image(speed, fly, kind, oops); } // end of main()
/* d51.rs */ pub fn get_d51() -> Vec<Vec<String>> { let d51: Vec<Vec<String>> = vec![ vec![ /* Frame-set #0 */ // <--- The stack below of ten long boxes makes up a vector of Strings. " ==== ________ ___________ ".to_string(), // <--- This is one long box containing a String of character-containing boxes. " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // <--- And this is another. " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), // And so on.... " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\O=====O=====O=====O_/ \\_/ ".to_string(), ], vec![ /* Frame-set #1 */ // Now imagine six of those cartons in a shipping container. " ==== ________ ___________ ".to_string(), // This second vector is the second carton in that shipping container. " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|=O=====O=====O=====O |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #2 */ " ==== ________ ___________ ".to_string(), // The shipping container holding all six cartons is the "Vector of Vector of Strings". " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // This shipping container is labeled "d51" by the "let d51" line above. " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), // The "pub fn get_d51()" line above "returns" (that's the "->" symbol) this vector of " | | | H |__--------------------| [___] | ".to_string(), // vector of Strings, but unlabeled; this function only uses the label "d51" internally. " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), // The portion of the program that calls this function puts its own label on the shipping " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), // container it gets back from this function. It is free to use the same label, or a "__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ ".to_string(), // different one. " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #3 */ // This is the fourth ('cause we started counting with zero) vector of Strings in the " ==== ________ ___________ ".to_string(), // shipping container. It can be accessed as "d51[3]", as in, "Put the d51[3] carton " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // of Pringle's on this shelf, and put the other five cartons, d51[0], d51[1], d51[2], " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), // d51[4], and d51[5] in the storage closet." " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), // This fifth line of the fourth carton can be accessed as"d51[3][4]" (4 because we " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), // start counting with zero). " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #4 */ " ==== ________ ___________ ".to_string(), " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= O=====O=====O=====O|_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #5 */ " ==== ________ ___________ ".to_string(), " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\_O=====O=====O=====O/ \\_/ ".to_string(), ], ]; return d51; } // end of get_d51()
/* extras.rs */ use crate::c51::*; use crate::d51::*; use ncurses::*; use std::{thread, time::Duration}; // Use c[ommand] l[ine] a[rgument] p[arser] to get command-line arguments. use clap::Parser; // This tells clap to derive info it needs from the struct we build below, from the "#[" lines. #[derive(Parser)] /* Define the argument inputs we might expect; put them in a struct named "Args". */ struct Args { /// Move the train '[[z]ip]py', '[f]ast', '[[m]ed]ium', '[s]low' or '[c]rawl'? #[arg(short, long, default_value_t = String::from("med"))] // The above tells clap that the next line is an argument, that can be entered in as "s" or "speed". speed: String, /// Fly? #[arg(short, long, default_value_t = false)] fly: bool, /// What Kind of object? D51 | C51 | Little | Jack | Boat | Plane #[arg(short, long, default_value_t = String::from("D51"))] kind: String, /// Oops? #[arg(short, long, default_value_t = false)] oops: bool, } // end of Args definitions pub fn parse_opts() -> (u64, bool, String, bool) { // Get command-line options using Clap. let switches: Args = Args::parse(); // Set "speed". let speed: u64 = match (&switches.speed.to_uppercase()).as_str() { // The variable "switches.speed" is a String-type (as defined above in the "Args" struct). We upper-case // it, then compare it to possible matches which are string-slices (&str), which requires the ".as_str()" // conversion. We then "return" the selected value, which is assigned to "speed". "Z" | "ZIPPY" | "ZIP" | "U" | "ULTRAFAST" => 10, "F" | "FAST" => 20, "M" | "MED" | "MEDIUM" => 50, "S" | "SLOW" => 100, "C" | "CRAWL" => 400, _ => { println!("Invalid input; must be '[z]ippy/[u]ltrafast, '[f]ast', '[[m]ed]ium', '[s]low', or '[c]rawl'. You entered '{}', so the default of 'medium' was used.", switches.speed); 50 } }; // Set "kind". let kind: String = match (&switches.kind.to_uppercase()).as_str() { "D" | "D51" => String::from("D51"), // Alternative way of doing '"D51".to_string()'. "C" | "C51" => String::from("C51"), "L" | "LITTLE" => String::from("LITTLE"), "J" | "JACK" => String::from("JACK"), "B" | "BOAT" => String::from("BOAT"), "P" | "PLANE" => String::from("PLANE"), _ => { // If none of them match, then ... println!("Invalid input; run program with '--help' option for more information. You entered '{}', so the default of 'd51' was used.", switches.kind); String::from("D51") } }; return (speed, switches.fly, kind, switches.oops); } // end of parse_opts() pub fn draw_image(delay: u64, fly: bool, kind: String, oops: bool) { // Start ncurses, initializing "stdscr()". initscr(); // Don't echo keypresses to screen. noecho(); // Turn off the display of the cursor. curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); // Get the screen dimensions. let mut screen_height = 0; let mut screen_width = 0; getmaxyx(stdscr(), &mut screen_height, &mut screen_width); // Load ASCII-art into "image" variable (a vector of vectors holding Strings). let image = match kind.as_str() { // Which image are we supposed to draw? "D51" => get_d51(), // Neither image will work at this point. "C51" => get_c51(), &_ => { // If the user enters an unknown image name (say, "racecar") ... println!( "Invalid kind. You entered '{}'; the default was used instead.", kind ); get_d51() } }; let height = image[0].len() as i32; // How tall (in rows) is the image? let mut row = screen_height - height; // Put the bottom of the image at the bottom of the screen. let count_of_inner_vecs: usize = image.len(); // How many animation cel frames in this image? let length: i32 = image[0][0].len() as i32; // How long (width in screen columns/chars) is the image? let ms = Duration::from_millis(delay); // This is the "delay" converted to a format for the "sleep()" function below. let mut current_inner_vec: usize = count_of_inner_vecs - 1; // Count down from highest-numbered frame; then repeat cycle. for col in ((-length)..screen_width).rev() { // Count down from right-edge until leading edge of image drags all of image off left-edge. let current_frame = &image[current_inner_vec]; // Of the various inner_vecs, get the one we're drawing into its own variable. let mut line = row; // Keep "row" unaltered unless we're flying; use "line" within loop. for each_line in current_frame { my_mvaddstr(line, col, &each_line, screen_width, screen_height); line = line + 1; } refresh(); // Necessary to "bring to the forefront" the drawing we just did "in the back staging area". /* Pause so our eyes can see the frame. */ thread::sleep(ms); // row = screen_height - height; // Reset row back to starting row. if current_inner_vec > 0 { current_inner_vec = current_inner_vec - 1; // Prepare to display next frame. } else { // we've gotten down to last frame, so ... current_inner_vec = count_of_inner_vecs - 1; // ... start over. if fly { // And if we're flying ... row -= 1; // ... fly up one row, and ... line = row + height; // ... erase last (bottom) line drawn, using enough repeated spaces for image's length. my_mvaddstr( line, col, &" ".repeat(length as usize), screen_width, screen_height, ); // No need to refresh(); next frame will do it for us. // if row < -height { // // If we've flown completely off the top of the screen... // row = screen_height; // ... fly in from the bottom. // } } } } /* Once finished, terminate ncurses. */ endwin(); } // end of draw_image() fn my_mvaddstr(row: i32, mut col: i32, frame_line: &str, screen_width: i32, screen_height: i32) { /* This function recieves one line of an ASCII-art image, and trims away any of that line that would otherwise be displayed off-screen; then it displays the remaining portion of the line. */ let mut line = frame_line.to_string(); // An &str is immutable by nature; we need a mutable string. // Trim from left side as train moves off left edge if col < 0 { // If we've moved left of the left-edge, for _ in 0..col.abs() { // for each column beyond edge, line.remove(0); // trim the first char off the string, repeatedly. } // "line" should now have front end trimmed off. col = 0; } // Trim from right side if it extends off right edge if col + (line.len() as i32) > screen_width { let amt_to_trim = (col + (line.len() as i32)) - screen_width; for _ in 0..amt_to_trim { line.pop(); } } if (row >= 0) && (row < screen_height) { // We're within screen bounds, so it's okay to display this line. mvaddstr(row, col, &line); } } // end of my_mvaddstr()
/* c51.rs */ pub fn get_c51() -> Vec<Vec<String>> { /* This function reads a text file containing a series of ASCII-art image "frames". This gets put into a variable as one long string of UTF-8 characters (think of 'em as ASCII chars, though). This string is then broken down into separate lines, breaking at the newline/end-of-line characters, and each line is pushed onto an inner vector. When a blank line is found, we skip on to the next loop, and create a new inner vector at the same time, and then repeat the above process. Eventually, each of the image frames is put into its own inner vector, and all those inner vectors are put into an outer vector named "outer_vec". */ let mut outer_vec: Vec<Vec<String>> = Vec::new(); // Outer vec holding all the inner frame vecs. // Read the file containing the image, into a variable named "image_string". let image_string = std::fs::read_to_string("src/c51.txt").expect("Error opening file."); let mut inner_vec: usize = 0; // To keep track of which frame/inner vector we're working with. outer_vec.push(Vec::new()); // Create first frame vec in outer vec, named "frames". // Process each line of the text file that's been read into memory. for each_line in image_string.lines() { // Break-up the single String variable "image_string" at newlines. if each_line != "" { // If the line is not blank, let line_string = each_line.replace("\"", " ").to_string(); // replace quotes with spaces, & convert the &str variable to a String variable outer_vec[inner_vec].push(line_string); // Then copy that String into the current inner frame vec. } else { // Else, if the line is blank, then we're between frames, so ignore the blan line, and inner_vec += 1; // prep for a new inner frame vec, and then outer_vec.push(Vec::new()); // create that new inner frame vec, } // before looping around to the next line. } return outer_vec; } // end of get_c51()
" ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|==[]=- || || | ||=======_|__" "/~\____|___|/~\_| O=======O=======O |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ " " ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|===[]=- || || | ||=======_|__" "/~\____|___|/~\_| O=======O=======O |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ " " ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|===[]=- O=======O=======O | ||=======_|__" "/~\____|___|/~\_| || || |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ " " ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|==[]=- O=======O=======O | ||=======_|__" "/~\____|___|/~\_| || || |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ " " ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|=[]=- O=======O=======O | ||=======_|__" "/~\____|___|/~\_| || || |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ " " ___ " " _|_|_ _ __ __ ___________" " D__/ \_(_)___| |__H__| |_____I_Ii_()|_________|" " | `---' |:: `--' H `--' | |___ ___| " " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| " " || | :: H +=====+ | |:: ...| " "| | _______|_::-----------------[][]-----| | " "| /~~ || |-----/~~~~\ /[I_____I][][] --|||_______|__" "------'|oOo|=[]=- || || | ||=======_|__" "/~\____|___|/~\_| O=======O=======O |__|+-/~\_| " "\_/ \_/ \____/ \____/ \____/ \_/ "
" \ 0 / " " \|/ " " | " " / \ " "_/ \_ " " " " " " __0__ " "/ | \ " " / \ " " _\ /_ " " " " " " o " " /\ /\ " " |/ \| " " _\ /_ " " "
" ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--O========O~\-+ |__________________| |__________________| " "//// \_/ \_/ (O) (O) (O) (O) " " ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--/O========O\-+ |__________________| |__________________| " "//// \_/ \_/ (O) (O) (O) (O) " " ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--/~O========O-+ |__________________| |__________________| " "//// \_/ \_/ (O) (O) (O) (O) " " ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--/~\------/~\-+ |__________________| |__________________| " "//// \_O========O (O) (O) (O) (O) " " ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--/~\------/~\-+ |__________________| |__________________| " "//// \O========O/ (O) (O) (O) (O) " " ++ +------ ____ ____________________ " " || |+-+ | | \@@@@@@@@@@@ | ___ ___ ___ ___ | " " /---------|| | | | \@@@@@@@@@@@@@_ | |_| |_| |_| |_| | " " + ======== +-+ | | | |__________________| " " _|--/~\------/~\-+ |__________________| |__________________| " "//// O========O_/ (O) (O) (O) (O) "