Al is a DDJ contributing editor. He can be contacted at [email protected]
I often begin a column by telling you where I'm located as I write. For example, last month I was typing away on the laptop at the farm of Judy's parents in rural Pennsylvania. We usually travel in the RV (nicknamed "The Titanic" by daughter Wendy), which lets us take along the trappings of home and office. I enjoy doing this part of the column because I like going to interesting places, and I like to write about where I am and how I got there. This month is different. I'm writing again in the Titanic, this time from an RV camp south of Tampa on the west coast of Florida. We returned from the farm a couple of weeks ago and didn't think we'd be on the road again so soon. Then Floyd came to visit. Floyd, you might remember, was the biggest hurricane to approach our shores in many a year. You're reading these words months after Floyd's reign of terror. I'm writing about him as he rages in the nearby Atlantic, aimed straight at my house.
Floyd is three times the size of Andrew (which leveled Homestead, Florida, in 1992) and about the same size and on the same track as one that killed 2000 people on the east coast in the 1880s. The talking heads on the tube are referring to Floyd as the "storm of the century" and "bigger than Texas." The warning and reaction technology of today is advanced far beyond what they had a hundred years ago, so the loss in human life from a hurricane will probably never reach those dimensions again. But property loss is another matter. No technology can do much about 155 mph winds.
As with every hurricane, we Floridians have been watching the progress of Floyd cautiously since his appearance in the south Atlantic several days ago. Yesterday morning, it seemed likely that our home town had a fate in store similar to that of Homestead seven years ago. At nearby Kennedy Space Center, the Vehicle Assembly Building (the largest by-volume building in the world) houses all four Space Shuttles. The giant VAB is rated to withstand 120 mph winds. Floyd is blowing a lot harder than that. Our humble house and its contents are no match for a summer breeze that can blow Space Shuttles out of their hangar, so we packed the Titanic and got out along with most of the other east coast residents under a mandatory evacuation, the largest such exodus ever recorded. Most folks headed for shelters or motels if they could find a room, but there weren't many rooms to be found. We're lucky to have the Titanic, a hotel room on wheels, a land yacht, a prairie schooner. Traffic was heavy in places but unmarked by the usual hostility that accompanies too many cars and not enough pavement. Drivers were considerate and courteous of one another. Common plight generates common bond and draws people together.
And Now the Rest of the Story...
This tale has a happy ending. We're home again. Floyd spared us the worst, saving his vengeance for the unlucky residents of the Carolinas, New Jersey, and other points north, who are either still under water or cleaning up. We struck camp and headed home the day after I wrote the introduction to this column. The traffic coming back was not as friendly as it was when we were outbound. Out of danger and with no common enemy to bind them, my neighbors returned to their usual hostile motorist mentality, honking, yelling, and shaking fists at every minor inconvenience. Seeing the backup on I-4 that extended from Orlando to Tampa as a million-plus people tried to get home, we steered the Titanic to calmer waters, the back roads that pass through small towns and farming and ranching districts. There wasn't much competition for road space in these backwaters, and our trip home was pleasant and leisurely. As they say at the pad after every launch, it was time for Damage Assessment. Lucky on that front, too. Our only damage was a small hole that an airborne pine tree branch punched in the porch screen. Not such a big deal for the storm of the century.
Graphics and Abstraction
Last month, I introduced a class library that began as a demonstration of abstraction, inheritance, and templates in the 6th edition of a book I wrote named Teach yourself C++. This month advances that project to a higher level of support. All the code is available for download (see "Resource Center," page 5). I'll refer to the listings here by their filenames. You should download them to follow along as you read.
The class library so far uses wireframe graphics to demonstrate those concepts because graphics are visual things that lend themselves to their explanations. I built a simple Win32 framework named "Tyfc" that hosts a window onto which the graphics library can draw its pictures. I'm striving for a generic, platform-independent graphics library, so the framework serves as an abstraction layer between the graphics library and the host platform. Last month, all the framework had to provide was a list of pointers to a shape abstract base class, a way for the derived application to add shape addresses to the list, calls to a pure virtual draw function to render the shapes, and a function that the shape classes call to draw pixels at specified coordinates on the screen. The framework is implemented as tyfc.h and tyfc.cpp. This month, I've added the ability for the wireframes to display their shapes in various colors.
The Tyfc framework and the graphics library need a common layer through which to communicate. Tyfc needs to know about an abstract base Shape class that it can list and for which it can call draw functions. Tyfc also needs to know how the graphics library represents colors to translate those colors into the platform's (Win32, in this case) representation of screen colors. Shape.h provides this common layer by defining the Shape class and the Color enumeration. The graphics library is implemented as template classes that parameterize an application type that is expected to provide those things that Tyfc provides -- a window onto which graphics are drawn, a way to draw on that window, a way to register shapes to be drawn, and the notification of when to draw each shape.
Quincy 99 and Mingw32 gcc-2.95
I implemented Tyfc and the graphics library with the Mingw32 port of the egcs 1.1.2 compiler as the underlying compiler for my own Quincy 99 integrated development environment. Since then, the egcs effort has become the gcc effort and a new version, 2.95, has been released that supports more of Standard C++. Soon, gcc-2.95 will have a standard library available, too, which is something I've been waiting for, something that I need for another long-ago promised project, an ambitious undertaking named D-Flat 2000. But that's another story.
One version of gcc-2.95 is the Mingw32 port, which is built specifically to run on and compile for the Win32 platform. I ported Quincy 99 to support Mingw32 gcc-2.95.1, and that is the development environment with which Tyfc and the graphics library compile. A visit to http:// www.midifitz.com/alstevens/quincy99/ provides everything you need to download and install Quincy 99 and provides instructions for downloading and installing Mingw32 gcc-2.95.1. Or -- gratuitous plug follows -- you could buy my book when it comes out and get the whole thing on the CD-ROM.
The Graphics Template Library
Last month, the graphics library was a single header file named graphics.h that defined some template classes (or class templates, I never can remember which way you're supposed to say that) that implement the specific shapes. Since then, I've organized the specific shapes into their own header files. Graphics.h now defines the TShape<T> template class, which is a base class for the specific shape classes. TShape<T> is derived from Shape, which is not a template class, but contains only what the framework needs to know about shapes. The <T> in TShape<T> parameterizes the framework itself.
The TShape<T> class encapsulates the things that all shapes have in common -- their position on the screen and the ability to draw a pixel with a call to the framework. It will later include rotation and scaling information.
The other header files are circle.h, line.h, polyline.h, polygon.h, and rectangle.h. They define the Circle<T>, Line<T>, Polyline<T>, Polygon<T>, and Rectangle<T> template classes. Polygon<T> is derived from Polyline<T>, and Rectangle<T> is derived from Polygon<T>. Each template shape class provides the things unique to itself, its own constructors, and a virtual draw function.
Each shape class records its screen coordinate position and the information necessary to render it. For example, a circle records its radius. Its position coordinates are also the coordinates of its center. A line records its ending coordinates. Its position coordinates are also its starting coordinates. A polygon records its position and a list of vertices, which are the points where each of its lines intersect. These points are relative to the position rather than relative to the screen. Maintaining the data members relative to position will make transition (moving) and rotation simpler to add.
Circles and Lines
Last month's library implemented the circle draw function by using trigonometry. The formula for each of the 360 degrees in a circle is:
x = radius * cos(degree)
y = radius * sin(degree)
This function draws a nice enough small circle, but when the circle expands there are blank spots between some of the dots. I changed the algorithm to draw lines between the points, which closed the open holes, but the result was a raggedy looking circle. That made me look at my implementation of the line-drawing algorithm, which itself used some floating-point math. Something in the recesses of my memory recalled solving this same problem many years ago. Rather than figuring it out on my own again, I referred to Dr. Dobb's Essential Books on Graphics Programming CD-ROM, and learned from authors such as Mark Finlay and Michael Abrash how to draw a line correctly with integer math and changed lines.h accordingly. That effort made my lines look better, but the circles were still rough looking. Returning to the CD-ROM, I learned that you can draw a circle with integer math by plotting a 1/8th wedge of the circle's coordinates uniformly and then by using the symmetrical properties of the circle to draw the other seven wedges. (I used to know that. I forgot it, though. Blame it on CRS.) The code in circle.h reflects this approach, which draws a much nicer circle than my original algorithm.
A graphics library for a Win32 platform does not need to do all that shape rendering at the pixel level, however. The Win32 API includes functions that do the same thing and usually a lot faster because Win32 has access to the display drivers which often themselves implement the functions by using the display hardware. Why, then, am I going to all this trouble? Because the object of the exercise is not Win32; the object of the lesson is a portable generic graphics library and how the features of C++ contribute to this abstraction.
All that notwithstanding, you would clearly want to draw the fastest pictures possible. C++ template classes permit you to specialize their functions, and so I wrote specialize.h, which specializes the draw functions of each of the shape classes to use the features of the Win32 API to do the actual drawing. To use the optimized specializations, a program simply includes specialize.h.
When you download the code you will find two example applications. One is represented by the files testgl.cpp and testgl.prj (a Quincy 99 project file), which implement the application that I showed last month. Testgl draws one of each kind of shape in the window; this month's version uses different colors for each of the shapes. You can't see much difference in the performance of this program with and without specialize.h because the shapes are few and simple. To better demonstrate the performance change, build the patt program (patt.cpp and patt.prj) with and without specialize.h. This program draws the usual pattern of points around the circumference of a circle where each point is connected by a line to every other point. They made us draw this picture in my high school's "mechanical drawing" class by using pens and India ink. We had to keep trying until we drew one that looked right and had no ugly ink blots or smears.
More to Come
I'll be adding shape filling algorithms and line widths of greater than one in future editions of the graphics library. I'll also be adding such features as rotation, transition, and scale, and new shapes such as arcs, Bezier curves, and ellipses. With those features, the library should be capable of supporting the display rendering logic of a typical simple 2D paint program. That's not its purpose, however. The reason for this project is to explore ways to use the features of C++ to build graphical applications and ways to exploit the characteristics of graphical applications to teach C++.
Why Two, Kay?
Okay, it's the last issue of the year 1999. Y2K is upon us. I won't say it's the last issue of the millennium because that would generate tons of e-mail from date algorithm specialists and calendar junkies informing me that the new millennium doesn't begin until 2001 because there wasn't really a year 0000. To which I respond, "Says who?" Who says that measured time is relative to 1 and not to 0? Does anyone really think that in the year 0001, they had calendars with 0001 on them? And if they had, what would they have called the previous year? Wouldn't it have been 0000? Was the year before last called -0001?
I don't have any harrowing Y2K stories to tell or predictions to make. Instead I'll talk about Y1.999K and an event that changed my life and elevated my standing in the industry.
August, Y1.999K was a milestone for this column. It was the beginning of its 12th year. Why was that a milestone? Well, to understand it you must know that from my first column in August, Y1.988K, until now I haven't missed a month. And you must also know that in May, Y1.988K, Mike Swaine wrote the first of his "Programming Paradigms" columns. For 11 years since Y1.988K, Mike and I were consistent uninterrupted contributors to this magazine, each writing a column a month about things related to computer programming.
That by itself was nothing special until early Y1.993K when Miller Freeman, the publisher of the rival Computer Language magazine, purchased M&T Publishing, which published Dr. Dobb's Journal. Realizing that Computer Language magazine and Dr. Dobb's Journal addressed similar audiences, and that to continue both publications would be to compete with themselves, our new owners decided to shut down CL and continue to publish DDJ. A stroke of publishing genius. Or else DDJ got lucky and won the coin toss. Whatever. Nonetheless, that shutdown brought to an end the programming column written by our colleague, P.J. (Bill) Plauger, who had been writing his stuff since sometime before Y1.988K. Which made Bill until then the undisputed holder of the coveted, prestigious Longest Uninterrupted Column About Software Development award -- the LUCAS -- because Bill had written his "Programming On Purpose" column without interruption longer than anyone else had written a programming column (the Cal Ripkin, Jr. of programming columnists). Which made Mike first runner up. I was in an unimpressive third place not worthy of mention. But sometime after the shutdown, Mike passed Bill's track record, Bill passed the LUCAS on to Mike, and I moved up in the standings. ("Swaine's Flames," which began November, Y1.986K, is not eligible for the LUCAS. It isn't about programming. It isn't about anything. It's the Seinfeld of columns.)
I kind of liked being number two (the Sammy Sosa of programming columnists). I did not mind being in the shadow of my friend, the venerated Mike Swaine. I reconciled myself to holding the silver medal. Always a bridesmaid. The sidekick who never gets the girl. The understudy. The Vice President.
Then It all Changed
Early this year, Mike moved to Oregon to become a whole earth innkeeper and restaurateur. And, caught up in the throes of moving and setting up his business, he missed writing a column for the May Y1.999K issue. Which would have been his 11th anniversary column. Which would have started his 12th year of continuous programming columns. Which would have retained for him the LUCAS. But he missed a month, and now, by default, as of August, Y1.999K, I proudly display the LUCAS on my mantle while Mike is forevermore relegated to a dethroned second place (the Buster Douglas of programming columnists) or at least until I retire and some upstart contender like J.D. Hildebrandt passes up both of us.
So, for my acceptance speech, I'd like to take this opportunity and thank all those friends and associates who supported me through the tough times, who encouraged me to plug away in the face of insurmountable odds, who inspired me to persevere. I'd like to thank those people who made all this possible. I'd like to.
But I won't.
Anyway, by now you're tired of Y2K stories. If you need to explain next year to your nonprogrammer friends why Armageddon did not happen at the stroke of midnight on New Year's Eve as predicted, it's easy. As any programmer knows, Y2K does not occur on that date. True Y2K doesn't happen until January 1, 2048, right? Great, those of you who are still around will have to go through all this nonsense another time. And all those opportunists who exploited the Y2K scare for the past several years will get another chance to frighten business, government, and industry out of a bunch more money. They made enough this time around to carry them until the door of opportunity opens again. I don't think I'll be getting any of the action, though; I'll be 107 1/2 years old on that date and probably retired. (The George Burns of programming columnists.)
Copyright © 1999, Dr. Dobb's Journal