148 lines
7 KiB
Markdown
148 lines
7 KiB
Markdown
---
|
|
title: Michael Abrash's Graphics Programming Black Book, Special Edition
|
|
author: Michael Abrash
|
|
date: '1997-07-01'
|
|
isbn: '1576101746'
|
|
publisher: The Coriolis Group
|
|
category: 'Web and Software Development: Game Development,Web and Software Development:
|
|
Graphics and Multimedia Development'
|
|
chapter: '41'
|
|
pages: 757-761
|
|
---
|
|
|
|
## 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."
|
|
|
|
> 
|
|
> 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**
|
|
|
|
```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 */
|
|
}
|
|
```
|