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.