This one is easy. Just copy the second frame of the Jack image and paste it in as a fourth frame. This will increase the "resolution" of Jumping Jack.
If Image Flies off Top Edge of Screen, Bring It Back in from the Bottom
Technically, the "0" in the "if" statement is a "magic number", something we'd normally want to avoid, but in this case, the comment explains what it means, and to make a variable or constant just for the top of the screen, just to avoid the use of a magic number, seems a little ... overboard. I guess we could say something like if (row + height) < (screen_height - screen_height), but that seems a little nutzoid also. Sometimes it just makes sense to "break the rules".
Add Coalcar
The original "sl" program has the coalcar attached to both the C51 and the D51 trains, without any option to not have it attached. Later we'll give the user the option to not include the coalcar, but for now, we'll just add it. The easiest way to add the coalcar (if we don't give the user to not add it), is to add it to the ASCII-art, like this:
But like the original "sl" program in the C-language, we'll keep the coalcar a completely separate ASCII-art drawing, which we can stitch onto the back-end of an image.
So let's put the coalcar in our "images.rs" file as a separate art-work, like so:
If a main image does not have a tail image attached, we can specify that in the "Set 'kind'" match statement with an empty string.
The following temporary configuration of "main()" should print out the coalcar image in single-quotes, if you provide either the D51 or the C51 image as the option ($ cargo run or $ cargo run -- kind=c51, etc).
If that worked, we're ready to move on.
Just like we did with the "image_string" variable, containing the main body of the image we've been animating across the screen, we can send the coalcar image to the "string_to_stringvecvec()" function to convert it from a String to a vector of String vectors. Unlike the main image, our outer vector won't contain several inner vectors; it'll only contain one inner vector. But the two images, the main body, and the tailing coalcar, will be configured similarly, as a vector of String vector[s]. Then we'll be able to massage the two images into one image, before animating that one image across the screen.
So let's convert the coalcar image, and then animate it by itself across the screen to make sure it seems to be working as we expect.
And ... it does!
Now let's handle the situation where there's not a tailstring:
In other words, if the tailstring is not an empty string (remember, we set "tail" to "" for the JACK option in "parse_opts()"), that means there is a tail, and so the two commands in the "if" clause are executed. But if that tailstring is an empty string, then that means there is not a tail, so we won't do those two commands in the "if clause", and since there's nothing left for the program to do, the program ends without doing anything. (We'll soon have it draw the main image without the tail in the latter case, but this is just for testing/development purposes.) You can see the different behavior by first running:
$ cargo run -- --kind=d51
and then running:
$ cargo run -- --kind=jack
The first one will animate the coalcar across the screen; the second one will do nothing.
Now we're ready to stitch the tail to the main body of the image.
Since we've already co-opted the "get_options.rs" file to hold some of our pre-processing routines, we might as well put our new function in this same file.
Just for testing the basic strucure, we can have the function return either of the vectors.
And in "main()":
When we feed the "image_vecvec" vector to our function, we lose ownership of that value, and when the function returns a similar value, we are binding that value to the same "image_vecvec" variable name we were using earlier, but we can only do that if we first declare that binding as mutable. That's why we had to add the "mut" to our initial declaration of "image_vecvec".
Ideally, the main body of the image, stored initially in "image_vecvec", is sent, along with the tail image, stored in "tail_vecvec", to the function "stitch_tail_to_body()", which returns a new value consisting of the main body image stitched together with the tail image, which new value is placed into the variable named "image_vecvec". So whereas "image_vecvec" starts out as just the body image, it comes back as the body + tail image.
If you give the option of "D51" or "C51" as the image to draw (or give no image, which will default to the D51), you should see the coalcar travel across the screen if this test succeeds. But if you specify an image that does not have a tail ("jack" is the only such image we currently have working), you should see that image animate across the screen. In a few minutes, once we have the function working, we'll have it working so that if you specify the D51 or C51, not only will the tailing coalcar animate across the screen, but it will be tailing the main body of the D51 or C51.
On to the next step. We need to make sure both the main image and the tail image are the same height. We could write program code to do this, but it would get pretty complicated, and it's easier just to inform the ASCII-artist to draw his art with the same number of lines for his "tail" as for his main "body".
Now back to the function to stitch the tail to the main body.
When I ran the program with cargo run -- --kind=d51, I discovered several problems:
One problem is that the wheels of the coalcar "shimmied". When I went back and looked at the "images.rs" file for the "D51" train, I realized that there was an extra space after the closing single-quote on the bottom line of about half the D51 frames. The easiest way to see this is to highlight all the frames in your text editor, which will let you visually see the extra space, as in the image below. We should remove those extra spaces, because inconsistent data could lead to unforeseen behavior. I originally left the extra space in, because I kind of liked the shimmy, but a few steps farther on, it introduces a small "blip" in the animation of the D51+trailer set.
Another problem is that selecting the C51 image causes the program to crash. Again, looking more closely at the "images.rs" file, I see that whereas the D51 and the COALCAR images both have 10 lines, the C51 image has 11. Oops. To fix this, we can either not include the COALCAR tail on the C51 image, or we can add a blank line at the tops of both the D51 and COALCAR images. Your choice. I added the blank lines.
A third problem is that there is an unwanted space between the D51/C51 and the COALCAR in the animated image. There are several ways this could be fixed; or we can just ignore it. I fixed mine by changing one line in the "string_to_stringvecvec()" function from:
let mut image_string: String = image_str.replace("\'", " ");
to
let mut image_string: String = image_str.replace("\'", "");
so that instead of replacing all the single-quotes with spaces, we're simply replacing them with nothing, effectively removing all the single-quotes. (This has unforeseen repercussions, which we'll discuss shortly.)
However, this leaves a trail behind when in "Fly" mode, because we no longer have that trailing space that deletes any leftovers from the previous frame (which is visible with a command like $ cargo run -- --kind=c51 -f). So we'd still like to have a trailing space at the end of the image. Sometimes that image will have a tail, such as with the D51 having the COALCAR tail, and sometimes it won't. So instead of adding a space to either the main body or to the tail, let's add it to the finished stitched-together product. And instead of adding it to the whole image prior to displaying the whole image, we can just add it to each line as each line is displayed, over in the "mymvaddstring()" function, like so:
That took care of the leftovers, but when drawing the C51, the program now crashes near the end of the animation, and the top of the coalcar looks broken. Something's obviously wrong.
These problems bring to light a problem that has existed all along, but which I'm only now noticing. The ASCII-artwork for the C51 train implements single-quotes as part of its art. When we removed all the single-quotes above, this also removed parts of the actual ASCII-artwork, which caused some of the lines of the C51 to be shorter than the others, which has led to the crashing at the end of the animation, and which causes the coalcar, and the C51 itself, now that I look at it more closely, to be misaligned.
The fix for this is to not remove all the single-quotes, but only the single-quotes at the beginning and the end of each line.
The first line above removes all single-quotes that follow immediately after a newline, and the second line removes all single-quotes immediately before a newline.
btw, it's at this point that the aforementioned "blip in the animation of the D51" shows up if we had left the shimmy in the D51 art.
A final issue, very minor, is that the compiler complains that we never use the "each_line" variable in our "for" loop. (It also complains that we're not using "kind" or "oops", but we plan to use those, so we'll just ignore those warnings for now.) This is easy enough to fix; we just do what the compiler suggests:
This should have the coalcar working with the D51 and C51 trains. In just a bit, we'll make the coalcar an option that can be turned off. But first, let's Add Some More Drawings.