Plan C - Clean Up the Image

Return to Plan C - Add the C51, Little, and Jack Images

Plan C - Clean Up the Image

The next step we want to do is to remove the quotes and to convert the single &str into a vector of String vectors, each inner String vector holding a single frame of the image. So if the image has six frames, we'll have six inner String vectors in the outer vector. Let's put these tasks in their own function, which will return a vector containing String vectors.

Create a Function to Convert the &str To A Vector of String Vectors

We need to figure out where to put our function. Since we moved the image constants to "images.rs", we no longer need the "d51.rs", "c51.rs", or "little.rs", if any of those are still around; I'm going to delete mine.

That leaves us with "main.rs", "images.rs", "parsing.rs", and "drawing.rs". We could create a new module file for our new function, or we could add it to one of our existing functions.

We could put the functionality in "parse_opts()", or at least the function in "parsing.rs"; after all, it could be argued that the purpose of "parse_opts" is to return the user's choice of image, and that an incomplete image leaves that job unfinished. On the other hand, processing the user's input is really a different task than is simply getting an input choice from the user. On the third hand, we've already processed the choice to some extent, converting "kind" to 'image".

We could put the function[ality] in "drawing.rs", arguing that prepping the image is part of drawing it. On the other hand, prepping the image is a separate task from drawing the image.

We could put the function in a new file, but that just adds to the clutter of files and "mod" and "use" statements.

We could put the function[ality] in "main.rs", but we said from the beginning we didn't want to clutter up that file.

None of these solutions are "wrong"; it's simply a design-choice. I think I'll go with the first option of putting the new function in "parsing.rs", but I'll rename "parsing.rs" to something less specific than only parsing command-line arguments, to something like "get_options.rs".

Renaming "parsing.rs" to "get_options.rs" also means we need to update the "mod" and "use" lines in "main.rs":

main.rs
/* main.rs */

mod images;
mod parsingget_options;

use parsingget_options::*;
...

Now let's create our function:

get_options.rs"
/* parsing.rs */
/* get_options */
...

   return (speed, switches.fly, kind, switches.oops, image_string);
} // end of parse_opts()


pub fn string_to_stringvecvec(image_str: &str) -> Vec<Vec<String>> {

}  // end of string_to_stringvecvec()

The first thing we'll want the new function to do is to remove the single-quotes.

Remove the Single-Quotes

We can simply remove the single-quotes, or we can replace them with spaces. If we replace them with spaces, then we won't have to worry about leaving behind a trail of image bits as the image moves across the screen, particularly in "Fly" mode, because the trailing space will overwrite any pieces that might otherwise get left behind. The only "cost" associated with doing this is that the image will have to travel two extra columns worth of space as it goes across the screen, which means the user might be looking at an extra few milliseconds of empty screen at the very first or the very last of the trip across the screen. That's a very reasonable extra cost to make sure that we don't have image bits left behind. Therefore, let's replace those single-quotes with spaces instead of simply removing them. This is a one-liner:

get_options.rs
pub fn string_to_stringvecvec(image_str: &str) -> Vec<Vec<String>> {

    // First, replace, with spaces, the quote-marks that delimit the side boundaries of the ASCII-art image.
    let image_string: String = image_str.replace("\'", " ");

}  // end of string_to_stringvecvec()

Well, that was easy. But let's test it. Temporarily, make the following changes:

get_options.rs
pub fn string_to_stringvecvec(image_str: &str)/* -> Vec<Vec<String>>*/ {

    // First, replace, with spaces, the quote-marks that delimit the side boundaries of the ASCII-art image.
    let image_string: String = image_str.replace("\'", " ");

println!("{}", image_string);

}  // end of string_to_stringvecvec()
main.rs
...
fn main() {
    let (speed, fly, kind, oops, image_string) = parse_opts(); // Get the program options from the command-line, if any.
                                                               // draw_d51(speed, fly, oops);
    string_to_stringvecvec(&image_string);
//    println!("{}", image_string);
} // end of main()

This should compile and run exactly as before our changes, except that the single-quotes are no longer shown in the display of the images.

Now to convert the several frames of the one string into several string elements in a vector.

Break Up the One String, By the Several Frames, Into Several Strings in a Vector

get_options.rs
pub fn string_to_stringvecvec(image_str: &str) /* -> Vec<Vec<String>>*/ {

    // First, replace, with spaces, the quote-marks that delimit the side boundaries of the ASCII-art image.
    let mut image_string: String = image_str.replace("\'", " ");

    // Second, remove newline at start of image.
    image_string.remove(0);

    // Then, third, convert each frame in the string to a string element, then add the element to the vector:

    // - Create an empty parent vector.
    let mut outer_vec: Vec<Vec<String>> = Vec::new();

    // - Create a counter to track which inner vector/frame we're working with.
    let mut inner_vec: usize = 0;

    // - Create the first, empty, inner vector for the first image frame.
    outer_vec.push(Vec::new()); // Create first frame vec in outer vec, named "frames".

    // - Process each line of image_string, breaking that string at newlines, into separate lines.
    for each_line in image_string.lines() {
        // If the current line is not blank...
        if each_line != "" {
            // ... then push that line into the current inner vector.
            outer_vec[inner_vec].push(each_line.to_string());
        } else {
            // Else, if the line is blank, then we're between frames, so ignore the blank line,
            // and increment the count of inner vectors, and ...
            inner_vec += 1;
            // ... create a new inner vector for the next image frame...
            outer_vec.push(Vec::new());
        } // ... before looping around to the next line.
    }

    return outer_vec;

println!("{}", image_string);
}  // end of string_to_stringvecvec()

And now...

Return the Vector

main.rs
fn main() {
    let (speed, fly, kind, oops, image_string) = parse_opts(); // Get the program options from the command-line, if any.
    // draw_d51(speed, fly, oops);
    let image_vecvec = string_to_stringvecvec(&image_string);
    // Temporary testing code 
    for each_frame in image_vecvec {
        for each_line in each_frame {
            println!("{}", each_line);
        }
    }
    //     println!("{}", image_string);
} // end of main()

Test, and all should be working.

Now it's time to Animate the Image Across the Screen.