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

81 lines
4.5 KiB
Markdown

Listing 38.2 isn't particularly interesting; it merely draws each
horizontal line in the passed-in list in the simplest possible way, one
pixel at a time. (No, that doesn't make the pixel the fundamental
primitive; in the next chapter I'll replace Listing 38.2 with a much
faster version that doesn't bother with individual pixels at all.)
Listing 38.1 is where the action is in this chapter. Our goal is to scan
out the left and right edges of each polygon so that all points inside
and no points outside the polygon are drawn, and so that all points
located exactly on the boundary are drawn only if they are not on right
or bottom edges. That's precisely what Listing 38.1 does. Here's how:
Listing 38.1 first finds the top and bottom of the polygon, then works
out from the top point to find the two ends of the top edge. If the ends
are at different locations, the top is flat, which has two implications.
First, it's easy to find the starting vertices and directions through
the vertex list for the left and right edges. (To scan-convert them
properly, we must first determine which edge is which.) Second, the top
scan line of the polygon should be drawn without the rightmost pixel,
because only the rightmost pixel of the horizontal edge that makes up
the top scan line is part of a right edge.
If, on the other hand, the ends of the top edge are at the same
location, the top is pointed. In that case, the top scan line of the
polygon isn't drawn; it's part of the right-edge line that starts at the
top vertex. (It's part of a left-edge line, too, but the right edge
overrides.) When the top isn't flat, it's more difficult to tell in
which direction through the vertex list the right and left edges go,
because both edges start at the top vertex. The solution is to compare
the slopes from the top vertex to the ends of the two lines coming out
of it in order to see which is leftmost. The calculations in Listing
38.1 involving the various deltas do this, using a rearranged form of
the slope-based equation:
(DeltaYN/DeltaXN)>(DeltaYP/DeltaXP)
Once we know where the left edge starts in the vertex list, we can
scan-convert it a line segment at a time until the bottom vertex is
reached. Each point is stored as the starting X coordinate for the
corresponding scan line in the list we'll pass to
**DrawHorizontalLineList**. The nearest X coordinate on each scan line
that's on or to the right of the left edge is selected. The last point
of each line segment making up the left edge isn't scan-converted,
producing two desirable effects. First, it avoids drawing each vertex
twice; two lines come into every vertex, but we want to scan-convert
each vertex only once. Second, not scan-converting the last point of
each line causes the bottom scan line of the polygon not to be drawn, as
required by our rules. The first scan line of the polygon is also
skipped if the top isn't flat.
Now we need to scan-convert the right edge into the ending X coordinate
fields of the line list. This is performed in the same manner as for the
left edge, except that every line in the right edge is moved one pixel
to the left before being scan-converted. Why? We want the nearest point
to the left of but not on the right edge, so that the right edge itself
isn't drawn. As it happens, drawing the nearest point on or to the right
of a line moved one pixel to the left is exactly the same as drawing the
nearest point to the left of but not on that line in its original
location. Sketch it out and you'll see what I mean.
Once the two edges are scan-converted, the whole line list is passed to
**DrawHorizontalLineList**, and the polygon is drawn.
Finis.
### Oddball Cases {#Heading7}
Listing 38.1 handles zero-length segments (multiple vertices at the same
location) by ignoring them, which will be useful down the road because
scaled-down polygons can end up with nearby vertices moved to the same
location. Horizontal line segments are fine anywhere in a polygon, too.
Basically, Listing 38.1 scan-converts between active edges (the edges
that define the extent of the polygon on each scan line) and both
horizontal and zero-length lines are non-active; neither advances to
another scan line, so they don't affect the edges being scanned.
I've limited this chapter's code to merely demonstrating the principles
of filling convex polygons, and the listings given are by no means fast.
In the next chapter, we'll spice things up by eliminating the floating
point calculations and pixel-at-a-time drawing and tossing a little
assembly language into the mix.