WGT Graphics Tutorial #1
Topic: Filled Polygons
By Chris Egerter
October 7, 1994
Introduction
------------
This series of tutorials describes a method of drawing filled polygons
using 3 rendering techniques: Solid, Gouraud shading, and texture mapping.
The code in this tutorial was written in Turbo C++ but can be ported to
other graphics libraries and operating systems. I did not use the
WGT functions in this one, so the wgtgfx.c file contains a few routines
which are needed for the demo. I have decided to explain the method
used for these routines since I had to discover them on my own, and
think you can learn from the code.
1.0 - What is a Polygon?
------------------------
To start things off, we should first define what a polygon is. A
polygon is a any figure that is composed of straight lines. It can have any
number of sides, and may or may not be closed. We will be discussing closed
polygons in this article mainly because they allow you to fill the interior
with a colour. An open polygon can only have its edges drawn because there
is no difference between the inside and outside. Furthermore, we will
only discuss convex polygons. If you draw a horizontal line across the
polygon, it must cross exactly two edges at any given vertical position.
If the polygon passes this test, it is convex.
2.0 - Drawing Polygons with Horizontal Lines
--------------------------------------------
In computer graphics, the screen is comprised of an x and a y axis,
with the coordinate (0,0) being the top left corner. Each polygon consists
of a number of vertices, each of which contain an (x,y) coordinate on the
screen. Our first task is to draw a filled polygon given a set of vertices.
A simple way to fill the polygon would be to draw lines between the vertices
(making sure to connect the first to the last) and using a region fill
routine to fill in the interior. This works in most cases however it is
slow, and not very useful in a program that requires animation. The other
method, is to draw a series of horizontal lines that make up the polygon.
3.0 - The algorithm
-------------------
The reason for dealing with only convex polygons is simple. Knowing
that we can draw the polygon using horizontal lines, we can simply store
the starting and ending x coordinate of the horizontal line on each y
coordinate of the polygon. Non-convex polygons would require several
horizontal lines per y coordinate, and things get a bit more complicated.
Our basic algorithm for drawing polygons is this:
1. Calculate the starting and ending x coordinate of the horizontal line
on each y coordinate. We can do this by using a standard line algorithm
but instead of plotting pixels, store the x coordinates into an array.
Simply calculate the lines between all vertices and connect the first
and last vertex with a line to close the figure.
2. Draw each horizontal line given the row, and two columns.
As you can see here, a polygon may be drawn using two simple, well
known routines: The line (with any slope), and the horizontal line.
4.0 - Keeping Track of the Left and Right Edges
-----------------------------------------------
Let's define two arrays which will contain our horizontal line
coordinates. These routines assume you are using 320x200x256 graphics mode,
however they can be easily ported to another mode by changing the 200 to the
number of rows the mode has.
int startx[200];
int endx[200];
Before the polygon is drawn, each value in these arrays will be set
to an impossible number to indicate that no point has been found on that y
coordinate. For the following examples, I will use -16000 as the impossible
number that would never be used.
5.0 - Scan Converting the Edges of the Polygon
----------------------------------------------
Our next step is to create a routine that will calculate a line and
store the x coordinates for every y coordinate. When we store the x
coordinate, first check the startx array. If it is -16000, this is the
first point found on this row. We will then store the x coordinate into
the startx array and continue with the line. If startx is not -16000, this
means a point has already been found, and we can store the coordinate in the
endx array (since we already know each row has exactly two intersections with
the polygon).
Below is the code for this routine:
void polyline (int x1, int y1, int x2, int y2)
/* Calculates the coordinates of a line given two
vertices (x1,y1) an (x2,y2).
We will use fixed point math to speed things up.
The x coordinate is multiplied by 256 and for each row,
a constant m is added to x. This is a simplified version
of a line algorithm because we only have to store 1 x coordinate
for every y coordinate.
*/
{
int tmp,y;
long x,m;
if (y2 != y1) /* This isn't a horizontal line */
{
if (y2 < y1) /* Make sure y2 is greater than y1 */
{
tmp = y1; /* Swap the y coordinate */
y1 = y2;
y2 = tmp;
tmp = x1; /* Swap the corresponding x coordinates */
x1 = x2;
x2 = tmp;
}
x = (long)x1<<8; /* Multiply be 256 */
m = ((long)(x2 - x1)<<8) / ((long)(y2 - y1));
/* m is the fractional amount to add to the x coordinate every row.
m is equal to (delta x) / (delta y). In other words, the x coordinate
has to change by (x2 - x1) columns in (y2 - y1) rows. */
x += m; /* We ALWAYS skip the first point in every line. This is done */
y1++; /* because we do not want to store the point where two lines
meet, twice. This would result in a single point being drawn. */
for (y = y1; y <= y2; y++) /* Go through each row */
{
if ((y >= 0) & (y < 200)) /* If the coordinate is on the screen */
if (startx[y] == -16000) /* Store the first coordinate */
startx[y] = x>>8;
else
endx[y] = x>>8; /* Store the last coordinate */
x += m; /* Add our constant to x */
}
}
}
6.0 - Calling the Polyline Routine
----------------------------------
Next we need to write a routine that will go through our vertex list
and call this routine. Once that is complete, we can draw all of our
horizontal lines.
First of all, let's make a structure for our vertices.
This segment should appear at the top of your program with the startx and
endx arrays.
typedef struct
{
int x,y;
} point;
Our main polygon routine will take an array of points, call the
polyline routine between each of the points, and finally draw each
horizontal line in the array.
Below is the filled polygon main routine:
void fillpoly (point *vertexlist, int numvertex)
/* Draws a filled polygon given an array of vertices. */
{
int i;
point *curpt,*nextpt;
/* Two pointers to a vertex. These are used to connect to vertices
together in when calling the polyline routine. */
curpt = vertexlist; /* Set to the first vertex in the array */
nextpt = vertexlist + 1; /* and to the second vertex */
for (i = 0; i < 200; i++)
{
startx[i] = -16000; /* Set up our impossible values */
endx[i] = -16000;
}
for (i = 1; i < numvertex; i++)
{
polyline (curpt->x, curpt->y, nextpt->x, nextpt->y);
/* Calculate the edge of this line. */
curpt += 1; /* Go to the next line */
nextpt += 1;
}
nextpt = vertexlist; /* Now close the polygon by doing a line between
the first and last vertex. */
polyline (curpt->x, curpt->y, nextpt->x, nextpt->y);
for (i = 0; i < 200; i++) /* Now draw the horizontal line list */
if (startx[i] != -16000) /* Indicates there is a line on this row */
{
if (endx[i] == -16000)
endx[i] = startx[i]; /* In case there was only one
point found on this row */
line (startx[i], i, endx[i], i);
/* Draw a line between the two x coordinates, on the row i. */
}
}
Now we should make a small test program to use these functions:
point mypoints[10];
void main (void)
{
initialize_graphics ();
mypoints[0].x = 160;
mypoints[0].y = 30;
mypoints[1].x = 50;
mypoints[1].y = 150;
mypoints[2].x = 270;
mypoints[2].y = 180;
fillpoly (&mypoints, 3);
getch ();
close_graphics ();
}
This will draw a triangle in the middle of the screen.
You can use these routines with any graphics library. The only routines
you will need to rename are the graphics initialization and closing routines,
and the line routine.
That's are there is to it. This is a simple case however. There are
other methods to fill a polygon with something other than solid colours. The
two techniques I will discuss in the next issues are Gouraud shading and
texture mapping.
The code and test program contained in this text file has been put into
two files:
fillpoly.c - contains the original (portable) code
poly256.c - contains a demonstration in the 320x200x256 graphics mode
wgtgfx.c - Various graphics support routines
wgtgfx.h - Header file for support routines
poly256 has been compiled into poly256.exe so you can view what these
routines do instantly. To compile poly256, make a project file and
compile and link poly256.c and wgtgfx.c together.