abrash-black-book/40-05.md
2013-12-30 20:26:41 +11:00

92 lines
5.9 KiB
Markdown

An insertion sort that scans backward through the AET from the current
edge rather than forward from the start of the AET could be quite a bit
faster, because edges rarely move more than one or two positions through
the AET. However, scanning backward requires a doubly linked list,
rather than the singly linked list used in Listing 40.1. I've chosen to
use a singly linked list partly to minimize memory requirements
(double-linking requires an extra pointer field) and partly because
supporting back links would complicate the code a good bit. The main
reason, though, is that the potential rewards for the complications of
back links and insertion sorting aren't great enough; profiling a
variety of polygons reveals that less than ten percent of total time is
spent sorting the AET.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The potential 1 to 5 percent speedup gained by optimizing AET sorting just isn't worth it in any but the most demanding application—a good example of the need to keep an overall perspective when comparing the theoretical characteristics of various approaches.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
### Nonconvex Polygons {#Heading8}
Nonconvex polygons can be filled somewhat faster than complex polygons.
Because edges never cross or switch positions with other edges once
they're in the AET, the AET for a nonconvex polygon needs to be sorted
only when new edges are added. In order for this to work, though, edges
must be added to the AET in strict left-to-right order. Complications
arise when dealing with two edges that start at the same point, because
slopes must be compared to determine which edge is leftmost. This is
certainly doable, but because of space limitations and limited
performance returns, I haven't implemented this in Listing 40.1.
#### Details, Details {#Heading9}
Every so often, a programming demon that I'd thought I'd forever laid to
rest arises to haunt me once again. A minor example of this—an imp, if
you will—is the use of " = " when I mean " == ," which I've done all too
often in the past, and am sure I'll do again. That's minor deviltry,
though, compared to the considerably greater evils of one of my personal
scourges, of which I was recently reminded anew: too-close attention to
detail. Not seeing the forest for the trees. Looking low when I should
have looked high. Missing the big picture, if you catch my drift.
Thoreau said it best: "Our life is frittered away by detail....Simplify,
simplify." That quote sprang to mind when I received a letter a while
back from Anton Treuenfels of Fridley, Minnesota, thanking me for
clarifying the principles of filling adjacent convex polygons in my
ongoing writings on graphics programming. (You'll find this material in
the previous two chapters.) Anton then went on to describe his own
method for filling convex polygons.
Anton's approach had its virtues and drawbacks, foremost among the
virtues being a simplicity Thoreau would have admired. For instance, in
writing my polygon-filling code, I had spent quite some time trying to
figure out the best way to identify which edge was the left edge and
which the right, finally settling on comparing the slopes of the edges
if the top of the polygon wasn't flat, and comparing the starting points
of the edges if the top was flat. Anton simplified this tremendously by
not bothering to figure out ahead of time which was the right edge of
the polygon and which the left, instead scanning out the two edges in
whatever order he found them and letting the low-level drawing code
test, and if necessary swap, the endpoints of each horizontal line of
the fill, so that filling started at the leftmost edge. This is a little
slower than my approach (although the difference is almost surely
negligible), but it also makes quite a bit of code go away.
What that example, and others like it in Anton's letter, did was kick my
mind into a mode that it hadn't—but should have—been in when I wrote the
code, a mode in which I began to wonder, "How else can I simplify this
code?"; what you might call Occam's Razor mode. You see, I created the
convex polygon-drawing code by first writing pseudocode, then writing C
code, and finally writing assembly code, and once the pseudocode was
finished, I stopped thinking about the interactions of the various
portions of the program.
In other words, I became so absorbed in individual details that I forgot
to consider the code as a whole. That was a mistake, and an embarrassing
one for someone who constantly preaches that programmers should look at
their code from a variety of perspectives; the next chapter shows just
how much difference thinking about the big picture can make. May my
embarrassment be your enlightenment.
The point is not whether, in the final analysis, my code or Anton's code
is better; both have their advantages. The point is that I was
programming with half a deck because I was so fixated on the details of
a single type of implementation; I ended up with relatively
hard-to-write, complex code, and missed out on many potentially useful
optimizations by being so focused. It's a big world out there, and there
are many subtle approaches to any problem, so relax and keep the big
picture in mind as you implement your programs. Your code will likely be
not only better, but also simpler. And whenever you see me walking
across hot coals in this book or elsewhere when there's an easier way to
go, please, let me know!
Thanks, Anton.