Lab 8 - Using Arrays for graphics

Remember the Refrigerator Art project you did way back when? For most of you, that artwork was constructed by hand coding a bunch of calls to the shape functions provided in the Graphics library. (A few of you wrote smarter programs to draw your pictures, but basically, you ended up with a set of simple calls to functions with data you provided in your code.

Array driven artwork

For this lab, we will draw a simple robot. However, instead of going about this problem the way we did earlier, we will set up a data array with all the information we need. I will show you basically how to do this, you will need to come up with the numbers that build your robot.

Here is an image of a robot toy to give you an idea what we want to construct:

../../_images/SimpleRobot.png

Figuring our the data

As before, you will need to sketch your robot and figure out the approximate values you need for parameters to the shape drawing routines you will need to use. We will record those numbers in a big array of integer numbers, and trick our program into fetching the data needed for each function call from the array.

Note

This will not be so bad, just follow my lead!

Building the drawing program

To create our drawing, we need to write down (on paper, not code) the sequence of shapes we want to draw, and for each shape, write down the numbers we will pass in as parameters on the appropriate function call line. Next, we will assign a simple number for each shape. Something like this:

  • 0 = circle
  • 2 = triangle
  • 3 = rectangle

Now, we can write out our data as a set of numbers, line so:

0, 50, 50, 25
1, 200, 125, 100, 275, 200, 375
9

Note

That last number is bigger than any number we use for a shape.

This set of numbers should draw a circle at x = 50, y = 50, with radius = 25, followed by a triangle drawn from x1 = 200, y1 = 125, and so on.

We will record all of these numbers , in order, in a simple data array, then write code to pull these numbers back out as needed to generate our drawing!

The main program code

You will need to create a loop that works through the entire data array. Your loop will need to be coded so it checks the shape value, then pulls out the numbers needed for the right function call.

We will use a new C++ statement to control the actual drawing. Here is the code we need to generate our drawing:

int data[12] = {
    0, 50, 50, 25,
    1, 200, 125, 100, 275, 200, 375,
    9
};


void drawScene(void) {

    int index = 0;
    int shape_type;
    int done = 0;

    clearWindow();
    setColor(YELLOW);
    index = 0;
    for(int i=0; i<2;i++) {
        shape_type = data[index];     // this should be the shape code
        index = index + 1;      // now index point to the first parameter
        switch(shape_type) {
            case 0: // circle
                drawFilledCircle(
                   data[index], data[index+1], data[index+2]);
                index = index + 3;
                break;
            case 1: // triangle
                drawFilledTriangle(
                    data[index], data[index+1], 
                    data[index+2], data[index+3], 
                    data[index+4], data[index+5]);
                index = index + 6;
                break;
            default: // done with shape loop
                done = 1;
        }
        if (done == 1) break;
    }
    glEnd();
    glFlush();
    glutSwapBuffers();
    glEnd();
}

The switch statement

There is a new statement in this code, the switch statement. Basically, this statement is a replacement for what would be a complicated nested if-then-else statement, It works like this:

Suppose you have an integer variable that can hold one of a defined set of values and you want to process different code depending on the actual value in that variable.

The switch(shape_type) part of this statement examines the variable (shape_type in this example). What comes after the switch line is a C++ block, which is a series of things inside curly braces. In this statement, each possible value we want to check is identified by a case value: line. In the example above, I have set up simple numbers for each possible shape I want to draw (well not all of them, this is just an example).

After each case value: line, we place a series of statements (indented) that will be processed only if the value of the variable matches the value on the case line.

For our program, the last line in this set of statements should be a break line. The break “breaks” us out of the switch statement, and we continue processing on the statement following the close curly bracket. In special cases, programmers leave the break out, but what happens if beyond this course, so make sure you remember to put this statement in as shown.

If the value in shape_type does not match any of the values listed on the set of case lines you include, we can do something by default. We just include a final default: line, followed by the code we want to process. In this set of code, we do not need a final break, since this chunk of code comes at the end of the switch statement.

Phew, sounds complicated, but it is not too bad, and can be handy. Like everything in programming, you only use statements if you feel that they will help you get your job done. IN this case, it is just what we need to control our drawing!

How this code works

The code shown above is a bit tricky, but it gets the job done! I figured this out a million years ago when I was trying to draw things in a language called FORTRAN! (Wait!, I am not really that old!)

The main for-loop uses the counter to track the shapes we want to draw. In the example data set, there are two shapes, plus another number that will stop the loop, in case I mess up the data somehow! I set up another variable named index that I will use to point to the first number for a shape in the data array. Initially, this variable is initialized to zero (remember, the first number in the array, which is at data[0]). Since the number in the data array is a zero in our example, that means we will draw a circle, and we need three additional numbers. Those numbers will be at:

data[0] = 0 - draw a circle
data[1] = 50 - the x coordinate
data[2] = 50 - the y coordinate
data[3] = 25 - the circle radius

The case 0: line is where the code for drawing the circle needs to go. Notice that I am using the index variable to send the values from the data array into the drawing function.

What happens at after the call to the drawing function is key to getting this all to work. For the next shape we want to draw, the shape number is in data[4]. If we add 4 to Index, then index will be “pointing” to the next shape number in the array. In data[index] (data[4] now), we find our next shape number, The parameters for that shape are passed in as parameters exactly as we did for the circle.

Each case block needs to add something to the index variable, but exactly what that something is depends on the shape being drawn. For the circle, it was 3, for the triangle, it will be 6 (can you see why).

I am not going to explain this code further here. You have enough background to figure out what is going on!

Writing your code

You should start up your lab project by making sure this code works. Then add comments to the code to show you do understand what is going on. Once you have that done, add code to complete your robot (changing the data, of course!)

Make sure you build this as a glut project and add the two files as usual.

Have fun with this, and let me know if you have any problems getting things going!