Revision: | 1 |
---|---|
Due Date: | 3/9/17 |
Arrays have an even more important use than storing simulation data (as with laplace): images. Gray scale images can be represented by two-dimensional arrays of ints. In this homework, we'll use an array to fill a "height field" array by sampling a function on a fixed grid and build a tool to output the array as a image. This is a simple case of "visualization": using graphics to present large quantities of data in a useful and intuitive form.
In order to create an image, your program needs to output a file in one of the many acceptable image formats. To keep things simple, we'll create grayscale ("black and white") images. The easiest grayscale format to create is called Portable Gray Map or "PGM".
PGM image files are human-readable ASCII files which start with a few "header" lines followed by the image data. Here is a very small example, a hemisphere with a black background:
P2 7 7 255 0 0 0 0 0 0 0 0 84 169 190 169 85 0 0 169 224 240 224 170 0 0 190 240 255 240 190 0 0 169 224 240 224 170 0 0 85 170 190 170 85 0 0 0 0 0 0 0 0
Here's how the format works:
The first line is always "P2".
The second line is the (integer) width (call it w), followed by a space, followed by the (integer) height (call it h). In this homework, these will always be N, the width and height of our array.
The third line is the maximum gray value: the one that corresponds to white pixels. In most cases (including this homework), this is always "255".
The rest of the file consists of the image: w × h integers, separated by spaces, to indicate pixel values between 0 (black) and 255 (white). It starts with the top row, with successive values going from left to right, just the way the example shows.
Although PGM isn't picky about line breaks, it's convenient for debugging to output a newline at the end of each row.
Except where noted, add no additional spaces or other output. (The format is very picky!)
All image files need a tool to display them. If you're working in our lab, that tool is Paint.NET. As it happens, it was developed by WSU CptS (YAY!) students in Pullman (yay!).
In the lab, we've enabled Paint.NET to be invoked from the MinGW command line as:
$ paintdotnet {image_file_name}
If you're working on your Windows laptop, you can download a free copy of Paint.NET at
http://www.getpaint.net/download
If you have the gimp or inkscape programs installed, they can display PGM as well. There may be others.
On Linux machines, gimp and inkscape are also freely available. There's also eog (Eye of Gnome), which starts up faster.
On Macs, open should work. I think.
Your assignment is to create a program plotz (as in "plot z(x,y)"). Here's the template:
#include <stdio.h> #include <math.h> #define N 7 void getExtrema(double z[N][N], double *zMin_p, double *zMax_p) /* return the minimum and maximum values in a 2D double array */ { /* * input * z[][] -- an N x N array of double values * * output * *zMin_p - the minimum value in z[][] * *zMax_p - the maximum value in z[][] * * set (*zMin_p) and (*zMax_p) to z[0][0] * for all rows i * for all columns j * if z[i][j] is less than *zMin_p, * set *zMin_p to z[i][j] * else z[i][j] is greater than *zMax_p, * set *zMax_p to z[i][j] */ } void printPgm(double z[N][N]) /* print a double array on stdout as a PGM file with automatic scaling */ { /* * input * z[][] -- an N x N array of double values * * This function first finds the maximum and minimum values in * z[][], zMin and zMax, with a call to getExtrema(). It then * prints out the values of the array as a PGM file. The PGM file * format is as follows: * * line # contains * ------ -------- * 1 "P2" * 2 "w h" where "w" is the width of the image and "h" is the * height. In this case, both values are N. * 3 "255" * 4 scaled (see below) values of z[0][...] * 5 scaled values of z[1][...] * ... ... * N+3 scaled values of z[N-1][...] * * Before printing, each value of z[][] is scaled and converted to * an int "pixelValue" between 0 and 255 (inclusively) in a way * that will not cause overflow by the following: * * if (zMin == zMax) { * pixelValue = 128; * } else { * pixelValue = 255 * (z[i][j] - zMin) / (zMax - zMin) * } * * "pixelValue" is the value you print. The "if" takes care of the * case where the z[][] values are all identical: the image will * be a uniform gray. Except for the first line, the output is all * integers. */ } void sampleFunction(double (*f_p)(double x, double y), double z[N][N]) /* sample an N x N grid over the square [-1,1] x [-1,1] */ { /* * input: * f -- pointer to a function that will be called over the grid * * output: * z -- output N x N array of values of f evaluated over * [ -1, 1 ] x [ -1, 1 ]. z[0][0] corresponds to the * upper left corner (x, y) = (-1, 1). * * This function evaluates an N x N grid that fits into a 2 x 2 * square centered on the origin. * * pseudocode: * * let dx = 2.0 / (N - 1) * let dy = 2.0 / (N - 1) * for i between 0 and N-1, inclusive * y = 1.0 - i * dy * for j between 0 and N-1, inclusive * x = j * dx - 1.0 * z[i][j] = f(x, y) */ } double hemisphere(double x, double y) /* return the height of a unit hemisphere or 0 if (x,y) lies outside */ { double rSqrd = 1.0 - x*x - y*y; if (rSqrd > 0) return sqrt(rSqrd); return 0.0; } double ripple(double x, double y) /* return a radial, exponentially-damped cosine, or "ripple" */ { double r = sqrt(x*x + y*y); return exp(-2*r) * cos(20*r); } /* * Add your own function here. */ int main(void) { /* * Windows won't let us declare local variables this large unless * we make them static. */ static double z[N][N]; /* * We are making use of C's concept of a "function pointer": * passing the name of a function to be called later. */ sampleFunction(ripple, z); /* try "ripple()" and/or your own function */ printPgm(z); return 0; }
If you like, you may download this at:
http://www.tricity.wsu.edu/~bobl/cpts121/hw04_plotz/plotz_tplt.c
If you have "wget", you can do this on the command line:
$ wget http://www.tricity.wsu.edu/~bobl/cpts121/hw04_plotz/plotz_tplt.c
Rename this file it to "plotz.c":
$ mv plotz_tplt.c plotz.c
then fill in the bodies of the three functions indicated.
The value of N in the above (7) is very small to make the program easy to debug. You should see the same values as in the example on page 1. When you do, increase N to, say, 512 and run the program like this:
$ plotz >image.pgm
This will save the results to a file "image.pgm". (This is an example of "output redirection".)
Now invoke the display tool on that file:
$ paintdotnet image.pgm
to see the result.
At this point, go back and add a new function and pass it to sampleFunction() to produce an image that looks interesting to you. Be creative!
Finally, turn in plotz.c via Angel.
Once your plotz is working, for 10 points of extra credit:
You now have a program that can visualize the output of your Laplace solver! Change the boundary conditions to see some different results.
Turn in plot_laplace2d.c via Angel.