Polygon Filling

References:

  1. Andy Johnson's CS 488 Course Notes, Lecture 2 and 3
  2. Foley, Van Dam, Feiner, and Hughes, "Computer Graphics - Principles and Practice", Sections 3.5 to 3.7

Filling a Polygon

want to avoid drawing pixels twice (not a problem with frame buffers but can be with other display technologies.)

pixels within the boundary of a polygon belong to the polygon
pixels on the left and bottom edges belong to a polygon, but not the pixels on the top and right edges

Want a polygon filling routine that handles convex, concave, intersecting polygons and polygons with interior holes.

overall algorithm:

specific algorithm:

Why do it horizontally, why not vertically?

Algorithm for step 1: scan-line algorithm

as usual there is the straightforward easy way and the convoluted efficient way.

an easy way:

From above, the specific polygon filling algorithm:


efficient way:

makes use of the fact that, similar to the midpoint algorithm, once we have an intersection we incrementally compute the next intersection from the current one. Xi+1 = Xi + 1/m

Create a global Edge Table (ET)

  1. y = minimum Y value in ET (index of first non empty bucket)
  2. set Active Edge Table (AET) to be empty
  3. repeat until AET and ET are empty
    1. move edges in ET with Ymin = y into AET (new edges)
    2. sort AET on x (AET will eventually include new and old values)
    3. use list of x coordinates to fill spans
    4. remove entries in AET with ymax = y (edges you are finished with)
    5. add 1 to y (to go to the next scan line)
    6. update X values of remaining entries in AET (add 1/m to account for new y value)

Lots of nagging little details for step 3:

- if intersection is at an integer value (say 4.0)

- if intersection is at a non integer value (say 4.6) is pixel 4 interior? is pixel 5 interior?

- what if more than one vertex shares the same location? (this also handles the situation where you may end up with an odd number of edges in the AET)

- what if 2 vertices define a horizontal edge?

Example:

------     ----------------     --------------- 
| 20 | --> | 50 | 40 | -1 | --> | 40 | 40 |  0 | 
------     ----------------     ---------------- 
------     ----------------     ---------------- 
| 10 | --> | 50 | 10 |  0 | --> | 40 | 70 | -1 | 
------     ----------------     ----------------

and the horizontal line from (10,10) to (70,10) is ignored.

Now the active edge table starts out with the minimum Y in the global edge table, i.e.:

         ----------------     ---------------- 
Head --> | 50 | 10 |  0 | --> | 40 | 70 | -1 | 
         ----------------     ----------------

Using this edge table for Y=10, parity turns on at X=10 and turns off at X=70, so the pixels from 10 to 69 inclusive would be drawn. Then as Y is increased to 11 for the next line, the active edge table would adjust the X values using the 1/m values, to yield:

         ----------------     ---------------- 
Head --> | 50 | 10 |  0 | --> | 40 | 69 | -1 | 
         ----------------     ----------------

Note that the X in the first entry did not change, because that edge is vertical, but the X in the second entry was changed by 1/m, i.e. it changed from 70 to 69. There are no edges to be added or removed, so the pixels from 10 to 68 inclusive would be drawn for Y=11.

The algorithm will continue this way until Y=20, at which point two new edges get added, and so the active edge table becomes:

         ----------------     ----------------     ---------------     ---------------- 
Head --> | 50 | 10 |  0 | --> | 50 | 40 | -1 | --> | 40 | 40 |  0 |--> | 40 | 60 | -1 | 
         ----------------     ----------------     ---------------     ----------------

Traversing this line there are two parity changes at X=40, so all pixels are drawn from X=10 to 59. Then when Y is incremented to 21, the active edge table becomes:

         ----------------     ----------------     ---------------     ---------------- 
Head --> | 50 | 10 |  0 | --> | 50 | 39 | -1 | --> | 40 | 40 |  0 |--> | 40 | 59 | -1 | 
         ----------------     ----------------     ---------------     ----------------

Now pixels are drawn from 10 to 38 and from 40 to 58. This continues until Y reaches 40, at which point the rightmost two edges drop out and the active edge table becomes:

         ----------------     ---------------- 
Head --> | 50 | 10 |  0 | --> | 50 | 20 | -1 |  
         ----------------     ----------------

and so on, until Y reaches 50 and the remaining two edges are removed from the active edge table.

Why would this algorithm be much simpler if the polygon were guaranteed to be convex with no intersecting edges?

Additional Issues

The above discussion only covers the means of determining the inside versus outside pixels of a polygon defined by straight line segments.

It does not cover polygons whose borders are defined by curves such as circles, ellipses, parabolic functions, or splines. ( Of course these can all be approximated by a series of small straight lines, and given enough, the approximations can be quite good. )

We also haven't addressed what to fill the polygon with. The presence of different lighting models, textures, or bump maps may cause each pixel within the polygons to be colored differently from any of its neighbors. Those issues will come up in later lectures.