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

129 lines
8 KiB
Markdown

Chapter 41\
Those Way-Down Polygon Nomenclature Blues {#Heading1}
------------------------------------------
### Names Do Matter when You Conceptualize a Data Structure {#Heading2}
After I wrote the columns on polygons in *Dr. Dobb's Journal* that
became Chapters 38-40, long-time reader Bill Huber wrote to take me to
task—and a well-deserved kick in the fanny it was, I might add—for my
use of non-standard polygon terminology in those columns. Unix's
X-Window System (XWS) defines three categories of polygons: complex,
nonconvex, and convex. These three categories, each a specialized subset
of the preceding category, not-so-coincidentally map quite nicely to
three increasingly fast polygon filling techniques. Therefore, I used
the XWS names to describe the sorts of polygons that can be drawn with
each of the polygon filling techniques.
The problem is that those names don't accurately describe all the sorts
of polygons that the techniques are capable of drawing. Convex polygons
are those for which no interior angle is greater than 180 degrees. The
"convex" drawing approach described in the previous few chapters
actually handles a number of polygons that are not convex; in fact, it
can draw any polygon through which no horizontal line can be drawn that
intersects the boundary more than twice. (In other words, the boundary
reverses the Y direction exactly twice, disregarding polygons that have
degenerated into horizontal lines, which I'm going to ignore.)
Bill was kind enough to send me the pages out of *Computational
Geometry, An Introduction* (Springer-Verlag, 1988) that describe the
correct terminology; such polygons are, in fact, "monotone with respect
to a vertical line" (which unfortunately makes a rather long
**\#define** variable). Actually, to be a tad more precise, I'd call
them "monotone with respect to a vertical line and simple," where
"simple" means "not self-intersecting." Similarly, the polygon type I
called "nonconvex" is actually "simple," and I suppose what I called
"complex" should be referred to as "nonsimple," or maybe just "none of
the above."
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This may seem like nit-picking, but actually, it isn't; what it's really about is the tremendous importance of having a shared language. In one of his books, Richard Feynman describes having developed his own mathematical framework, complete with his own notation and terminology, in high school. When he got to college and started working with other people who were at his level, he suddenly understood that people can't share ideas effectively unless they speak the same language; otherwise, they waste a great deal of time on misunderstandings and explanation.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Or, as Bill Huber put it, "You are free to adopt your own terminology
when it suits your purposes well. But you risk losing or confusing those
who could be among your most astute readers—those who already have been
trained in the same or a related field." Ditto. Likewise. *D'accord*.
And *mea culpa* ; I shall endeavor to watch my language in the future.
### Nomenclature in Action {#Heading3}
Just to show you how much difference proper description and interchange
of ideas can make, consider the case of identifying convex polygons.
When I was writing about polygons in my column in *DDJ*, a nonfunctional
method for identifying such polygons—checking for exactly two X
direction changes and two Y direction changes around the perimeter of
the polygon—crept into the column by accident. That method, as I noted
in a later column, does not work. (That's why you won't find it in this
book.) Still, a fast method of checking for convex polygons would be
highly desirable, because such polygons can be drawn with the fast code
from Chapter 39, rather than the relatively slow, general-purpose code
from Chapter 40.
Now consider Bill's point that we're not limited to drawing convex
polygons in our "convex fill" code, but can actually handle any simple
polygon that's monotone with respect to a vertical line. Additionally,
consider Anton Treuenfels's point, made back in Chapter 40, that life
gets simpler if we stop worrying about which edge of a polygon is the
left edge and which is the right, and instead just scan out each raster
line starting at whichever edge is left-most. Now, what do we have?
What we have is an approach passed along by Jim Kent, of Autodesk
Animator fame. If we modify the low-level code to check which edge is
left-most on each scan line and start drawing there, as just described,
then we can handle any polygon that's monotone with respect to a
vertical line regardless of whether the edges cross. (I'll call this
"monotone-vertical" from now on; if anyone wants to correct that
terminology, jump right in.) In other words, we can then handle
nonsimple polygons that are monotone-vertical; self-intersection is no
longer a problem. We just scan around the polygon's perimeter looking
for exactly two direction reversals along the Y axis only, and if that
proves to be the case, we can handle the polygon at high speed. Figure
41.1 shows polygons that can be drawn by a monotone-vertical capable
filler; Figure 41.2 shows some that cannot. Listing 41.1 shows code to
test whether a polygon is appropriately monotone.
**LISTING 41.1 L41-1.C**
/* Returns 1 if polygon described by passed-in vertex list is monotone with
respect to a vertical line, 0 otherwise. Doesn't matter if polygon is simple
(non-self-intersecting) or not. Tested with Borland C++ in small model. */
#include "polygon.h"
#define SIGNUM(a) ((a>0)?1:((a<0)?-1:0))
int PolygonIsMonotoneVertical(struct PointListHeader * VertexList)
{
int i, Length, DeltaYSign, PreviousDeltaYSign;
int NumYReversals = 0;
struct Point *VertexPtr = VertexList->PointPtr;
/* Three or fewer points can't make a non-vertical-monotone polygon */
if ((Length=VertexList->Length) < 4) return(1);
/* Scan to the first non-horizontal edge */
PreviousDeltaYSign = SIGNUM(VertexPtr[Length-1].Y - VertexPtr[0].Y);
i = 0;
while ((PreviousDeltaYSign == 0) && (i < (Length-1))) {
PreviousDeltaYSign = SIGNUM(VertexPtr[i].Y - VertexPtr[i+1].Y);
i++;
}
if (i == (Length-1)) return(1); /* polygon is a flat line */
/* Now count Y reversals. Might miss one reversal, at the last vertex, but
because reversal counts must be even, being off by one isn't a problem */
do {
if ((DeltaYSign = SIGNUM(VertexPtr[i].Y - VertexPtr[i+1].Y))
!= 0) {
if (DeltaYSign != PreviousDeltaYSign) {
/* Switched Y direction; not vertical-monotone if
reversed Y direction as many as three times */
if (++NumYReversals > 2) return(0);
PreviousDeltaYSign = DeltaYSign;
}
}
} while (i++ < (Length-1));
return(1); /* it's a vertical-monotone polygon */
}