UMBC CMSC 202, Computer Science II, Spring 1999,
Sections 0101, 0102, 0103, 0104
Project 3: Recursion
Due: Friday April 9, 1999
See Project Notes
You are to write a C++ program to implement an algorithm for flood-filling a region of
a surface. The purpose of flood-fill is to fill an arbitrary bounded space on the screen
with a given color (which, for us, will be black).
To run a flood-fill algorithm, you need a grid of pixels (simply dots on a screen)
each of which is either on or off, and a location within the grid,
from which to flood-fill. The pixel at the start position should be off. The flood-fill
algorithm sets the start position on, and all of its horizontal and vertical neighbors
that are off, and all of their horizontal and vertical neighbors that are off, etc. This
'flooding' stops when a pixel that is already on is reached. You can think of the
pixels that are initially on as walls, and of the start location as a hose pumping out
water. The water fills as much area as it can, but it is stopped by the walls.
As an example, suppose that we were to flood-fill Figure 1 below on the
left, starting from the pixel marked with a diamond. The result is shown in
Figure 2. Notice that the flooding does not proceed diagonally; only the horizontal
and vertical neighbors of a flooded cell that are not already on will be flooded:
Assignment
You are to implement a flood-fill algorithm. You will write a program that reads in a
'screen' (i.e. a two dimensional array of pixels) from a file, asks for a point
on that screen from which to commence flood-filling, runs the flood-fill
algorithm with that point as the starting point, and writes out the resulting screen to another
file. After completing the flood-fill and writing the new file, the program allows the user
to view a version of the picture on the screen, using user-selected ASCII characters for
pixels.
As stated above, this assignment is intended to give you experience using recursion.
Therefore, your program may not use any of C++'s iteration constructs (do, while,
for and goto); all repetition must be accomplished using recursion.
This applies to every repetitive operation in the program, even those which
involve I/O.
Example
Here is an example of how your program should work (text entered by the user is shown in
italics):
umbc8[22]% hw3
What is the name of the input PBM file? example.pbm
This screen is 5 pixels wide by 4 pixels high.
Do you want to see the input [n]: y
Please enter characters for '1' and '0': * -
****-
*--*
**--*
*****
Please enter the x and y coordinates of the start pixel: a 5
You must enter two integers; 'a 5' is not valid.
Please enter the x and y coordinates of the start pixel: 5 a
You must enter two integers; '5 a' is not valid.
Please enter the x and y coordinates of the start pixel: 8 3
The first integer must lie between 0 and 4, inclusive.
Please enter the x and y coordinates of the start pixel: 0 5
The second integer must lie between 0 and 3, inclusive.
Please enter the x and y coordinates of the start pixel: 0 0
That pixel is already set.
Please enter the x and y coordinates of the start pixel: 2 1
What is the name of the output file? outexample.pbm
Output written to file outexample.pbm
Do you want to see the output [n]: y
Please enter characters for '1' and '0': @ +
@@@@+
@@@@@
@@@@@
@@@@@
umbc8[23]% cat outexample.pbm
P1
5 4
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
umbc8[23]%
Note that the pixels are numbered from the upper left-hand corner of the screen, which is
0,0. Your program should verify that the selected start pixel is indeed off, and print a
warning message if it is not.
Error Checking of Input
Your program should be bulletproofed by checking for certain errors, and if they occur,
to respond appropriately. The following errors should result in an appropriate error
message printed to cerr and program termination:
- The input PBM file cannot be opened for reading.
- The output PBM file cannot be opened for writing.
- The screen represented by the input PBM file exceeds the maximum allowed size (which
we will set at 70 pixels wide by 100 pixels high).
The following errors should cause a warning message to be printed to cerr, but
should not cause program termination:
- The start pixel is already on
- The start pixel is not a legal location on this screen.
- An input item is not correct e.g. expected a y or an n but got
something else; expected an integer but got a non--integer, etc.
Background Information
The PBM Format
We will be using a file format called PBM, which stands for Portable Bit Map. A PBM file has
three components. The first item in the file is the pair of characters 'P1
';
this serves to identify the file as a PBM file (For those of you interested, the 2 character
pair at the start of a file is known in unix as the file's magic number).
The second item in the file is a pair of integers that indicate the size of the screen: first
the width, then the height (i.e., first the number of pixels in each row, then the
number of rows). The remaining lines of the file contain one digit for each pixel in the image.
A '1' means that the pixel is on, while a '0' means that the pixel is off. Here
is the sample file used above in PBM format:
P1
5 4
1 1 1 1 0
1 00 0 1
11 00 11 111 1
Note that neither the rows nor the bits in those rows are required to be separated by
whitespace. In particular, a newline is not required after each row (of the image).
Complete details about the PBM format may be obtained by reading the man page ('man pbm').
Two other points should be made about the PBM format. First, characters from a # to the
end of the line are supposed to be ignored in a PBM file; however, we will not allow such
comments in our PBM files. Second, no line is supposed to be over 70 characters long. We will
ignore this requirement; however, if you decide you want to use the output of your program as
the input to other programs that manipulate PBM files, you might want to observe it.
Resources
There are a couple of tools that you might find useful when working on this assignment.
The 'xv' program can display pictures that are stored in a variety of formats, including
PBM. Because the standard X-windows manager imposes a minimum window width, very small PBM
pictures will be stretched when they are displayed by xv. You can unstretch
such pictures by changing the size of the xv window (for instance, by dragging the
lower right-hand corner of the window with the mouse).
Another tool you might find useful is the 'bitmap' program. Bitmap allows you to
draw a picture on a bitmap screen and flood-fill it. You can use bitmap to experiment
with flood-filling, to verify that your program produces correct results, and to create
new bitmaps for use as input to your program. Unfortunately, bitmap does not
save its results in PBM format. It uses instead an alternative format called XBM.
You can convert a PBM file into an XBMfile using the 'pbmtoxbm' program:
umbc8[24]% pbmtoxbm myfile.pbm > myfile.xbm
Converting a file from XBM to PBM format is slightly more complicated. First, you must
use the 'xbmtopbm' program to convert from XBM format to a packed version of the
PBM format. Then, you use the 'pnmnoraw' program to convert the packed PBM file into
an ordinary PBM file such as those with which we are familiar:
umbc8[25]% xbmtopbm myfile.xbm > myfile.junk
umbc8[26]% pnmnoraw myfile.junk > myfile.pbm
umbc8[27]% rm myfile.junk
What to code
You should implement a class for portable bitmap files. Use this class declaration as a
starter:
class PBM {
private:
int _rows; // number of rows in the image
int _cols; // number of columns in the image
int ** _bits; // the pixel settings for the image
// (will become 2 dimensional array)
public:
PBM (); // default constructor
PBM (int rows, int columns); // alternate constructor
~PBM (); // destructor
PBM (const PBM &); // copy constructor
PBM& operator= (const PBM &); // overloaded assignment operator
// Get status of an individual pixel
// Parameters: row and column position of the pixel
// Returns: 1 for on, 0 for off, -1 for outside of grid
int GetPixelStatus (int row, int column);
// Floodfill a region from specified starting position
// Parameters: row and column position of the start pixel
void FloodFill(int row, int column);
// Read PBM from a file
// Parameter: reference to file input stream
// Returns: true for success, false if the file is not
// in valid PBM format
int Load (ifstream& istr);
// Write PBM to a file
// Parameter: reference to file output stream
void Save (ofstream& ostr);
};
You may add other functions and data members to the class as needed.
Your source files should be named PBM.H
, PBM.C
,
and Proj3.C
(contains main()
). You may create
additional source files as needed.
You must also write and submit a makefile that runs with the
single command make.
Hints
The flood-fill task itself is a natural for ordinary recursion.
You should use tail recursion to read and write the files. Refer to the recursion handout for
more information about tail recursion. You should write and test the code to read the
input file before attempting to write the rest of the program. If you are having great
difficulty writing the necessary code directly, you may try implementing the code
using iteration statements, then translating your code into recursive code. Don't submit
the iterative code, though, as you will get no credit for it.
Submission
As usual, you must submit all of your .C and .H files,
together with your makefile.
The makefile must correctly compile your homework upon issue of the make command
with no arguments. The executable created by
the make command must be named Proj3. Your Makefile will be used to compile
your submitted code, so be sure it works!
Make sure that you adhere to the I/O specifications given in this handout
(i.e., the order of inputs to your program) The precise wording of the
program's prompts
to the user is unimportant. If you do not adhere to these specifications,
the automatic
portions of the grading process will fail, and you will receive a poor grade.
Checklist
You should check to make sure that you have fulfilled each of these requirements:
- Did not use any iteration constructs (while, for, etc).
- Wrote in modular form, using implementation and interface files.
- All header files are guarded
- Submitted a working makefile.
- Submitted all source files needed to compile the program.
- All error messages are written to
cerr
.
- All other messages are written to
cout
.
- "ASCII version" of PBM files are written to
cout
.
- Program terminates, with error message on the following conditions:
- Can't open a PBM file (in or out).
- The input file is not in PBM format (no "magic Number", number of pixels
is different from rows * columns, or has other than 1's or 0's for pixel values)
- Input PBM file exceeds the maximum screen size.
- Program prints error message, but does not terminate on the following conditions:
- User input is not correct format.
- Start pixel is already on.
- Start pixel is "off the screen."
- Tested the program thoroughly.
- Followed all style guidelines.