EG2069 - Course Notes and Guides for Programming in C
Week 3


Plotting Graphs

Before you start this shorter exercise this week, please ensure that you have finished week 2.

Using a copy of table.c (written last week) as a guide, copy it into graph.c and modify the declaration of z so that it is a two dimensional array containing enough rows and columns into which the values of the function are to be stored. You will also need to introduce two new integer variables to control the loops. The template program. Here's a rough guide below:
 
#include <stdio.h>
#include <math.h>

#define ROWS 20
#define COLUMNS 60

main()
{
  double x, y, z[ROWS][COLUMNS];
  int r,c;

  for (r = 0; r < ROWS; r++)
  {
    for (c = 0; c < COLUMNS; c++)
    {
      x = ???????;
      y = ???????;
      z[r][c] = cos(2.0*x) * sin(y);
    }
  }
}

The text in red shows some of the modifications. Notice the #define lines near the top of the program and their usage down below to set the declaration of z. Because the loops now count in terms of r and c in steps of 1 from 0 upto the number of rows and columns (minus 1) in the z array, some mapping of row and column numbers onto x and y values will be required. You will therefore need extra variables stating the x and y limits over which z is to be calculated.

Assuming suitable double declarations for xstart, xfinish (and therefore ystart, yfinish), the following fragment of C code will request values from the user of your program:
 
printf("Enter starting and ending values for x: ");
scanf("%lf %lf",&xstart,&xfinish);
 

Within the body of the innermost loop, the calculation of x (and similarly y) will look something like:
 
x = xstart + r * (xfinish - xstart) / ROWS;
 

Finish off the program by printing out the values of the z array in the same way as last week's table.c program.

The program you have hopefully just written (graph.c) performs the following steps:

1. Ask the user for the range of x to be considered and input these values into variables.
2. Ask the user for the range of y to be considered and input these values.
3. Calculate the z array using a nested loop, printing out the calculations as they are generated.

Notice that step 1 (and 2) can be split into two sub-steps as follows:
1a. Ask the user to type two numbers for the x range.
1b. Input the numbers into two variables xstart, xfinish.

Modify step 3 (the loop within a loop part of the program) so that as well as printing the values of
the elements of z, your program finds the maximum and minimum values of the function calculation.
You will need two extra variables zmax and zmin (declared as double) which should be initialised before being involved in any calculations: zmax should initially be a very large negative number (say -10000.0), zmin a very large positive number.

Every time a z[r][c] value is calculated, your program will have to ask if its value is larger than zmax. If this is so, then the value of zmax should be replaced with the z value.
 
if (z[r][c] > zmax)
  zmax = z[r][c];
 

You will need something similar to the above for the calculation of zmin too.
Use a printf statement after the outermost loop to display the values of zmax and zmin along with
suitable explanatory text.

After testing your program (graph.c) you should notice that by the time it prints out the maximum and minimum values of the function values calculated, it has accumulated the function's values in the array z. Armed with this information, your program can be augmented so that it prints out a density plot or contour plot of your table of function values. This is an old trick of using a series of single characters to replace a range of values. The characters are chosen so that they appear to be increasing in darkness (or lightness) when printed out in a square. For example:
 
..........
..++++++..
.++++++++.
.++****++.
.++*##*++.
.++****++.
.++++++++.
..++++++..
..........

Immediately after the code to print out zmax and zmin, begin another two nested loops in terms of r and c with exactly the same control code (r = 0; r < ROWS; r++) etc.. as the loops used to calculate the z array.

Instead of using a printf statement to print out the value of z[r][c] as in the previous loop, reduce the value of
z[r][c] into an integer number between 0 and 4 (you already have the maximum values for z). The expression
will look something like:
 
((int) ((z[r][c] - zmin) / (zmax - zmin) * 4))

The int in brackets forces the calculation to produce an integer result rather than a double floating point value) and use a switch statement (you've not met this before) in preference to a series of if statements to decide on what character to print for each value between 0 and 4.
 
switch ( ((int) ((z[r][c] - zmin) / (zmax - zmin) * 4)) )
{
  case 0:
    printf(" ");
    break;
  case 1:
    printf(".");
    break;
  case 2:
    printf("+");
    break;
  case 3:
    printf("*");
    break;
  case 4:
    printf("#");
    break;
}
 

Apart from getting the brackets right, there's little to worry about a switch statement which is just a more convenient way of handling more than one or two conditions. Following the calculation of
the expression in the brackets after switch, the program jumps to the case matching the value calculated.
In the above code, one of the printf statements will be executed. The following break causes the
program to jump out of the switch statement to the line following the closing curly bracket. It is important
that these break statements not be omitted.

Remember to use printf to force a newline at end of each inner loop (the column loop in terms of c)
 
printf("\n");

When you have successfully compiled your program, try running it with different x,y ranges. Also consider
modifying the value of ROWS and COLUMNS and even the function cos(2.0*x) * sin(y) to generate more interesting contour plots.

Remember to print your fully functional copy of graph.c and paste it into your log book.
 


Back to main page
Author : Keith Halewood / Helge Nareid Current contact: Gorry Fairhurst G.Fairhurst@eng.abdn.ac.uk