Move everything into one directory, update links
This commit is contained in:
parent
b946ff9e70
commit
4eef8b2919
680 changed files with 1638 additions and 1638 deletions
397
41-04.html
Normal file
397
41-04.html
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Those Way-Down Polygon Nomenclature Blues</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=41//-->
|
||||
<!--PAGES=766-771//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="41-03.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="42-01.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 41.4 L41-4.C</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* Color-fills an arbitrarily-shaped polygon described by VertexList.
|
||||
If the first and last points in VertexList are not the same, the path
|
||||
around the polygon is automatically closed. All vertices are offset
|
||||
by (XOffset, YOffset). Returns 1 for success, 0 if memory allocation
|
||||
failed. All C code tested with Borland C++.
|
||||
|
||||
If the polygon shape is known in advance, speedier processing may be
|
||||
enabled by specifying the shape as follows: “convex” - a rubber band
|
||||
stretched around the polygon would touch every vertex in order;
|
||||
”nonconvex” - the polygon is not self-intersecting, but need not be
|
||||
convex; “complex” - the polygon may be self-intersecting, or, indeed,
|
||||
any sort of polygon at all. Complex will work for all polygons; convex
|
||||
is fastest. Undefined results will occur if convex is specified for a
|
||||
nonconvex or complex polygon.
|
||||
|
||||
Define CONVEX_CODE_LINKED if the fast convex polygon filling code from
|
||||
the February 1991 column is linked in. Otherwise, convex polygons are
|
||||
handled by the complex polygon filling code.
|
||||
Nonconvex is handled as complex in this implementation. See text for a
|
||||
discussion of faster nonconvex handling. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#ifdef __TURBOC__
|
||||
#include <alloc.h>
|
||||
#else /* MSC */
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include “polygon.h”
|
||||
|
||||
#define SWAP(a,b) {temp = a; a = b; b = temp;}
|
||||
|
||||
struct EdgeState {
|
||||
struct EdgeState *NextEdge;
|
||||
int X;
|
||||
int StartY;
|
||||
int WholePixelXMove;
|
||||
int XDirection;
|
||||
int ErrorTerm;
|
||||
int ErrorTermAdjUp;
|
||||
int ErrorTermAdjDown;
|
||||
int Count;
|
||||
};
|
||||
|
||||
extern void DrawHorizontalLineSeg(int, int, int, int);
|
||||
extern int FillMonotoneVerticalPolygon(struct PointListHeader *,
|
||||
int, int, int);
|
||||
extern int PolygonIsMonotoneVertical(struct PointListHeader *);
|
||||
static void BuildGET(struct PointListHeader *, struct EdgeState *,
|
||||
int, int);
|
||||
static void MoveXSortedToAET(int);
|
||||
static void ScanOutAET(int, int);
|
||||
static void AdvanceAET(void);
|
||||
static void XSortAET(void);
|
||||
|
||||
/* Pointers to global edge table (GET) and active edge table (AET) */
|
||||
static struct EdgeState *GETPtr, *AETPtr;
|
||||
|
||||
int FillPolygon(struct PointListHeader * VertexList, int Color,
|
||||
int PolygonShape, int XOffset, int YOffset)
|
||||
{
|
||||
struct EdgeState *EdgeTableBuffer;
|
||||
int CurrentY;
|
||||
|
||||
#ifdef CONVEX_CODE_LINKED
|
||||
/* Pass convex polygons through to fast convex polygon filler */
|
||||
if ((PolygonShape == CONVEX) ||
|
||||
PolygonIsMonotoneVertical(VertexList))
|
||||
return(FillMonotoneVerticalPolygon(VertexList, Color, XOffset,
|
||||
YOffset));
|
||||
#endif
|
||||
|
||||
/* It takes a minimum of 3 vertices to cause any pixels to be
|
||||
drawn; reject polygons that are guaranteed to be invisible */
|
||||
if (VertexList->Length < 3)
|
||||
return(1);
|
||||
/* Get enough memory to store the entire edge table */
|
||||
if ((EdgeTableBuffer =
|
||||
(struct EdgeState *) (malloc(sizeof(struct EdgeState) *
|
||||
VertexList->Length))) == NULL)
|
||||
return(0); /* couldn’t get memory for the edge table */
|
||||
/* Build the global edge table */
|
||||
BuildGET(VertexList, EdgeTableBuffer, XOffset, YOffset);
|
||||
/* Scan down through the polygon edges, one scan line at a time,
|
||||
so long as at least one edge remains in either the GET or AET */
|
||||
AETPtr = NULL; /* initialize the active edge table to empty */
|
||||
CurrentY = GETPtr->StartY; /* start at the top polygon vertex */
|
||||
while ((GETPtr != NULL) || (AETPtr != NULL)) {
|
||||
MoveXSortedToAET(CurrentY); /* update AET for this scan line */
|
||||
ScanOutAET(CurrentY, Color); /* draw this scan line from AET */
|
||||
AdvanceAET(); /* advance AET edges 1 scan line */
|
||||
XSortAET(); /* resort on X */
|
||||
CurrentY++; /* advance to the next scan line */
|
||||
}
|
||||
/* Release the memory we’ve allocated and we’re done */
|
||||
free(EdgeTableBuffer);
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Creates a GET in the buffer pointed to by NextFreeEdgeStruc from
|
||||
the vertex list. Edge endpoints are flipped, if necessary, to
|
||||
guarantee all edges go top to bottom. The GET is sorted primarily
|
||||
by ascending Y start coordinate, and secondarily by ascending X
|
||||
start coordinate within edges with common Y coordinates. */
|
||||
static void BuildGET(struct PointListHeader * VertexList,
|
||||
struct EdgeState * NextFreeEdgeStruc, int XOffset, int YOffset)
|
||||
{
|
||||
int i, StartX, StartY, EndX, EndY, DeltaY, DeltaX, Width, temp;
|
||||
struct EdgeState *NewEdgePtr;
|
||||
struct EdgeState *FollowingEdge, **FollowingEdgeLink;
|
||||
struct Point *VertexPtr;
|
||||
|
||||
/* Scan through the vertex list and put all non-0-height edges into
|
||||
the GET, sorted by increasing Y start coordinate */
|
||||
VertexPtr = VertexList->PointPtr; /* point to the vertex list */
|
||||
GETPtr = NULL; /* initialize the global edge table to empty */
|
||||
for (i = 0; i < VertexList->Length; i++) {
|
||||
/* Calculate the edge height and width */
|
||||
StartX = VertexPtr[i].X + XOffset;
|
||||
StartY = VertexPtr[i].Y + YOffset;
|
||||
/* The edge runs from the current point to the previous one */
|
||||
if (i == 0) {
|
||||
/* Wrap back around to the end of the list */
|
||||
EndX = VertexPtr[VertexList->Length-1].X + XOffset;
|
||||
EndY = VertexPtr[VertexList->Length-1].Y + YOffset;
|
||||
} else {
|
||||
EndX = VertexPtr[i-1].X + XOffset;
|
||||
EndY = VertexPtr[i-1].Y + YOffset;
|
||||
}
|
||||
/* Make sure the edge runs top to bottom */
|
||||
if (StartY > EndY) {
|
||||
SWAP(StartX, EndX);
|
||||
SWAP(StartY, EndY);
|
||||
}
|
||||
/* Skip if this can’t ever be an active edge (has 0 height) */
|
||||
if ((DeltaY = EndY - StartY) != 0) {
|
||||
/* Allocate space for this edge’s info, and fill in the
|
||||
structure */
|
||||
NewEdgePtr = NextFreeEdgeStruc++;
|
||||
NewEdgePtr->XDirection = /* direction in which X moves */
|
||||
((DeltaX = EndX - StartX) > 0) ? 1 : -1;
|
||||
Width = abs(DeltaX);
|
||||
NewEdgePtr->X = StartX;
|
||||
NewEdgePtr->StartY = StartY;
|
||||
NewEdgePtr->Count = DeltaY;
|
||||
NewEdgePtr->ErrorTermAdjDown = DeltaY;
|
||||
if (DeltaX >= 0) /* initial error term going L->R */
|
||||
NewEdgePtr->ErrorTerm = 0;
|
||||
else /* initial error term going R->L */
|
||||
NewEdgePtr->ErrorTerm = -DeltaY + 1;
|
||||
if (DeltaY >= Width) { /* Y-major edge */
|
||||
NewEdgePtr->WholePixelXMove = 0;
|
||||
NewEdgePtr->ErrorTermAdjUp = Width;
|
||||
} else { /* X-major edge */
|
||||
NewEdgePtr->WholePixelXMove =
|
||||
(Width / DeltaY) * NewEdgePtr->XDirection;
|
||||
NewEdgePtr->ErrorTermAdjUp = Width % DeltaY;
|
||||
}
|
||||
/* Link the new edge into the GET so that the edge list is
|
||||
still sorted by Y coordinate, and by X coordinate for all
|
||||
edges with the same Y coordinate */
|
||||
FollowingEdgeLink = &GETPtr;
|
||||
for (;;) {
|
||||
FollowingEdge = *FollowingEdgeLink;
|
||||
if ((FollowingEdge == NULL) ||
|
||||
(FollowingEdge->StartY > StartY) ||
|
||||
((FollowingEdge->StartY == StartY) &&
|
||||
(FollowingEdge->X >= StartX))) {
|
||||
NewEdgePtr->NextEdge = FollowingEdge;
|
||||
*FollowingEdgeLink = NewEdgePtr;
|
||||
break;
|
||||
}
|
||||
FollowingEdgeLink = &FollowingEdge->NextEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorts all edges currently in the active edge table into ascending
|
||||
order of current X coordinates */
|
||||
static void XSortAET() {
|
||||
struct EdgeState *CurrentEdge, **CurrentEdgePtr, *TempEdge;
|
||||
int SwapOccurred;
|
||||
|
||||
/* Scan through the AET and swap any adjacent edges for which the
|
||||
second edge is at a lower current X coord than the first edge.
|
||||
Repeat until no further swapping is needed */
|
||||
if (AETPtr != NULL) {
|
||||
do {
|
||||
SwapOccurred = 0;
|
||||
CurrentEdgePtr = &AETPtr;
|
||||
while ((CurrentEdge = *CurrentEdgePtr)->NextEdge != NULL) {
|
||||
if (CurrentEdge->X > CurrentEdge->NextEdge->X) {
|
||||
/* The second edge has a lower X than the first;
|
||||
swap them in the AET */
|
||||
TempEdge = CurrentEdge->NextEdge->NextEdge;
|
||||
*CurrentEdgePtr = CurrentEdge->NextEdge;
|
||||
CurrentEdge->NextEdge->NextEdge = CurrentEdge;
|
||||
CurrentEdge->NextEdge = TempEdge;
|
||||
SwapOccurred = 1;
|
||||
}
|
||||
CurrentEdgePtr = &(*CurrentEdgePtr)->NextEdge;
|
||||
}
|
||||
} while (SwapOccurred != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advances each edge in the AET by one scan line.
|
||||
Removes edges that have been fully scanned. */
|
||||
static void AdvanceAET() {
|
||||
struct EdgeState *CurrentEdge, **CurrentEdgePtr;
|
||||
|
||||
/* Count down and remove or advance each edge in the AET */
|
||||
CurrentEdgePtr = &AETPtr;
|
||||
while ((CurrentEdge = *CurrentEdgePtr) != NULL) {
|
||||
/* Count off one scan line for this edge */
|
||||
if ((--(CurrentEdge->Count)) == 0) {
|
||||
/* This edge is finished, so remove it from the AET */
|
||||
*CurrentEdgePtr = CurrentEdge->NextEdge;
|
||||
} else {
|
||||
/* Advance the edge’s X coordinate by minimum move */
|
||||
CurrentEdge->X += CurrentEdge->WholePixelXMove;
|
||||
/* Determine whether it’s time for X to advance one extra */
|
||||
if ((CurrentEdge->ErrorTerm +=
|
||||
CurrentEdge->ErrorTermAdjUp) > 0) {
|
||||
CurrentEdge->X += CurrentEdge->XDirection;
|
||||
CurrentEdge->ErrorTerm -= CurrentEdge->ErrorTermAdjDown;
|
||||
}
|
||||
CurrentEdgePtr = &CurrentEdge->NextEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Moves all edges that start at the specified Y coordinate from the
|
||||
GET to the AET, maintaining the X sorting of the AET. */
|
||||
static void MoveXSortedToAET(int YToMove) {
|
||||
struct EdgeState *AETEdge, **AETEdgePtr, *TempEdge;
|
||||
int CurrentX;
|
||||
|
||||
/* The GET is Y sorted. Any edges that start at the desired Y
|
||||
coordinate will be first in the GET, so we’ll move edges from
|
||||
the GET to AET until the first edge left in the GET is no longer
|
||||
at the desired Y coordinate. Also, the GET is X sorted within
|
||||
each Y coordinate, so each successive edge we add to the AET is
|
||||
guaranteed to belong later in the AET than the one just added. */
|
||||
AETEdgePtr = &AETPtr;
|
||||
while ((GETPtr != NULL) && (GETPtr->StartY == YToMove)) {
|
||||
CurrentX = GETPtr->X;
|
||||
/* Link the new edge into the AET so that the AET is still
|
||||
sorted by X coordinate */
|
||||
for (;;) {
|
||||
AETEdge = *AETEdgePtr;
|
||||
if ((AETEdge == NULL) || (AETEdge->X >= CurrentX)) {
|
||||
TempEdge = GETPtr->NextEdge;
|
||||
*AETEdgePtr = GETPtr; /* link the edge into the AET */
|
||||
GETPtr->NextEdge = AETEdge;
|
||||
AETEdgePtr = &GETPtr->NextEdge;
|
||||
GETPtr = TempEdge; /* unlink the edge from the GET */
|
||||
break;
|
||||
} else {
|
||||
AETEdgePtr = &AETEdge->NextEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fills the scan line described by the current AET at the specified Y
|
||||
coordinate in the specified color, using the odd/even fill rule */
|
||||
static void ScanOutAET(int YToScan, int Color) {
|
||||
int LeftX;
|
||||
struct EdgeState *CurrentEdge;
|
||||
|
||||
/* Scan through the AET, drawing line segments as each pair of edge
|
||||
crossings is encountered. The nearest pixel on or to the right
|
||||
of left edges is drawn, and the nearest pixel to the left of but
|
||||
not on right edges is drawn */
|
||||
CurrentEdge = AETPtr;
|
||||
while (CurrentEdge != NULL) {
|
||||
LeftX = CurrentEdge->X;
|
||||
CurrentEdge = CurrentEdge->NextEdge;
|
||||
DrawHorizontalLineSeg(YToScan, LeftX, CurrentEdge->X-1, Color);
|
||||
CurrentEdge = CurrentEdge->NextEdge;
|
||||
}
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><B>LISTING 41.5 POLYGON.H</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* Header file for polygon-filling code */
|
||||
|
||||
#define CONVEX 0
|
||||
#define NONCONVEX 1
|
||||
#define COMPLEX 2
|
||||
|
||||
/* Describes a single point (used for a single vertex) */
|
||||
struct Point {
|
||||
int X; /* X coordinate */
|
||||
int Y; /* Y coordinate */
|
||||
};
|
||||
|
||||
/* Describes series of points (used to store a list of vertices that describe
|
||||
a polygon; each vertex is assumed to connect to the two adjacent vertices, and
|
||||
last vertex is assumed to connect to the first) */
|
||||
struct PointListHeader {
|
||||
int Length; /* # of points */
|
||||
struct Point * PointPtr; /* pointer to list of points */
|
||||
};
|
||||
|
||||
/* Describes beginning and ending X coordinates of a single horizontal line */
|
||||
struct HLine {
|
||||
int XStart; /* X coordinate of leftmost pixel in line */
|
||||
int XEnd; /* X coordinate of rightmost pixel in line */
|
||||
};
|
||||
|
||||
/* Describes a Length-long series of horizontal lines, all assumed to be on
|
||||
contiguous scan lines starting at YStart and proceeding downward (used to
|
||||
describe scan-converted polygon to low-level hardware-dependent drawing code) */
|
||||
struct HLineList {
|
||||
int Length; /* # of horizontal lines */
|
||||
int YStart; /* Y coordinate of topmost line */
|
||||
struct HLine * HLinePtr; /* pointer to list of horz lines */
|
||||
};
|
||||
|
||||
/* Describes a color as an RGB triple, plus one byte for other info */
|
||||
struct RGB { unsigned char Red, Green, Blue, Spare; };
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P>Is monotone-vertical polygon detection worth all this trouble? Under the right circumstances, you bet. In a situation where a great many polygons are being drawn, and the application either doesn’t know whether they’re monotone-vertical or has no way to tell the polygon filler that they are, performance can be increased considerably if most polygons are, in fact, monotone-vertical. This potential performance advantage is helped along by the surprising fact that Jim’s test for monotone-vertical status is simpler and faster than my original, nonfunctional test for convexity.
|
||||
</P>
|
||||
<P>See what accurate terminology and effective communication can do?</P><P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="41-03.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="42-01.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
Loading…
Reference in a new issue