This lab will show you how to create some simple line drawings in C.
Files referred to here are available in this on-line directory:
http://www.tricity.wsu.edu/~bobl/cpts121/lab06_grafic
One of those files, writeup.html, is this document in HTML. We'll download other files from this directory below.
Plug your thumbdrive into your workstation. Give it a few seconds to be recognized by Windows and then create a MinGW terminal emulator. In that emulator, create a new directory for this lab and cd to it:
$ cd /e/cpts121 $ mkdir lab06 $ cd lab06
As always, keep all your work for this lab in this directory.
grafic provides an extremely minimal graphics ability, but it is also very easy to use. It knows how to do one thing: draw lines in color on a square white "canvas" (drawing area) window.
The best way to learn to use grafic is to work through a small example:
Download the files square.c, grafic.h, and grafic.o to your lab directory.
Take a look at square.c:
#include "grafic.h" void init(const char **appTitle_pp) { *appTitle_pp = "\"grafic\" square demo"; } void redraw(void) { double p0[2], p1[2], p2[2], p3[2]; p0[0] = -0.5; p0[1] = -0.5; p1[0] = 0.5; p1[1] = -0.5; p2[0] = 0.5; p2[1] = 0.5; p3[0] = -0.5; p3[1] = 0.5; line(p0, p1); line(p1, p2); line(p2, p3); line(p3, p0); }
There are two functions here. grafic expects you to provide both of them:
init() has this prototype:
void init(const char **appTitle_pp);
In this function, you do everything you need to do before grafic creates the canvas: read data from a file, perform calculations, prompt the user for input, etc. If you like, you may assign a double-quoted string to *appTitle_pp like this:
*appTitle_pp = "Whatever title you like";
and that will be the window title. For now, don't worry about the asterisks. All in good time.
What you do here depends on your application. It may be that there's nothing for this function to do, but grafix is expecting to call it anyway, so in that case provide an empty function.
redraw() has this prototype:
void redraw(void);
This function is where you'll do most of your work. It's called by grafic whenever it's necessary to redraw window contents, and that may be often. The redraw should always start from scratch, assuming that the window is just a solid white background.
All graphics output must happen in this function. We'll say more about it below.
Note that:
Your code will be in C (in this case, square.c), so you must compile it separately:
$ gcc -Wall -c square.c
The "-c" is new and important. It's necessary for the next step, which is to link all the object files together using not the C, but the C++ compiler/linker:
$ g++ -Wall square.o grafic.o -lGdiplus -o square.exe
Now try running:
$ ./square
You should see a square black box drawn on a white background in a separate window. The drawing area in this window is referred to as the "canvas".
The canvas is in an approximately square window and shows a 2 x 2 region centered on the origin. This means
when x and y are visible.
Visibility of this viewing region (or "viewport") is preserved, even when the window is resized. It's perfectly okay to draw outside this region: What you draw just may not always be visible. 2D coordinates (x, y) (or "points" or "vertices") are specified by 2-element arrays of doubles.
All line drawing should be done in redraw().
To draw a line, call the function line(), whose prototype is:
void line(double p0[2], double p1[2]);
This draws a straight line between p0[] and p1[].
By default, the line is drawn in black, but you may change the color at any time by calling color(), whose prototype is:
void color(double r, double g, double b);
This will set the color of all subsequently-drawn lines to the color formed by the red, green, blue tuple (r, g, b). Here are some examples:
color r g b bright green 0.0 1.0 0.0 magenta 1.0 0.0 1.0 gray 0.5 0.5 0.5 dark blue 0.0 0.0 0.5
Our first graphics program will draw a regular polygon: a polygon whose sides are all the same length. The user will input an integer number of sides (3 or more) and the program will draw a polygon with that many sides on the canvas.
First, let's review the mathematics of regular polygons. A regular polygon with n sides centered on the origin has n vertices pi where
whose components are
where R is the radius of the enclosing circle.
You now know enough to create a regular polygon drawing program:
Download the file polygon_tplt.c to your lab directory.
Here it is:
#include <stdio.h> #include <math.h> /* for M_PI, sin(), and cos() */ #include "grafic.h" /* * Note our declaration of a variable outside any function. This makes * that variable "global" and accessible by all functions. Using * global variables is an alternative to passing a parameter, but is * easily abused and it should be avoided whenever possible. */ int nSides; void init(const char **appTitle_pp) { /* * set *appTitle_pp to a title string of your choice (you could * include your name, for instance) * repeat the following indefinitely: * prompt the user to enter a number of sides (hint: printf()) * read the user's answer into nSides (hint: scanf()) * if nSides is 3 or more * exit the loop (which will return from the function) */ } void redraw(void) { /* * for each of the nSides vertices i, * let p_i be the ith vertex (see math above) * let p_ip1 be the i+1st vertex * draw a line from p_i to p_ip1 (hint: line()) * * Use M_PI (from the math library) as the value of pi * and use a radius (R in the math) of 1.0. */ }
You're welcome to add color() calls to this program to vary the colors of the sides, but don't spend too much time on this.
"Doily" is an informal term for a completely-connected graph, one where every node is connected to every other node. In this part of the lab, you'll convert polygon into a program doily which is much more visually interesting.
Download the file doily_tplt.c to your lab directory and rename it to doily.c, or, since the only change from polygon.c is in redraw() (and maybe the title string in init()), you may just:
$ cp polygon.c doily.c
to copy polygon.c to doily.c and work from that.
Here is the template, which you'll need to work from in any case:
#include <stdio.h> #include <math.h> #include "grafic.h" int nSides; /* global */ void init(const char **appTitle_pp) { /* * set *appTitle_pp to a title string of your choice (you could * include your name, for instance) * repeat the following indefinitely: * prompt the user to enter a number of sides (hint: printf()) * read the user's answer into nSides (hint: scanf()) * if nSides is 3 or more * exit the loop (which will return from the function) */ } void redraw(void) { /* * declare points p_i and p_j (both are 2D double arrays) * for each of the nSides vertices i, * let p_i be the ith vertex (see math above) * for each of the higher-numbered vertices, starting at j = i + 1 * let p_j be the jth vertex * draw a line from p_i to p_j (hint: line()) * * Use M_PI (from the math library) as the value of pi and use a * radius (R in the math) of 1.0. */ }
Implement the code.
Compile and run it the same way you compiled and ran square and polygon.
Whichever way you do it, modify redraw() to nest the additional (j) vertex loop inside the original loop. Note that the inner loop always starts from j = i + 1. This is intentional: Since we always draw from a lower-indexed to a higher-indexed vertex, we never draw the same line twice.
Again, you're welcome to add color() calls to this program to vary the colors of the sides.
For the mathematically inclined, answer the following question:
Given a polygon with n vertices, how many lines are there in the corresponding doily?