Tuesday 1 October 2013

Polygon Filling

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:

    moving from bottom to top up the polygon
      starting at a left edge, fill pixels in spans until you come to a right edge

specific algorithm:
    moving from bottom to top up the polygon
    1. find intersections of the current scan line with all edges of polygon
    2. sort intersections by increasing x coordinate
    3. moving through list of increasing x intersections
        parity bit = even (0)
        each intersection inverts the parity bit
        draw pixels when parity is odd (1)
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:

  • given that each line segment can be described using x = y/m + B
  • each scan line covering the polygon has a unique integer Y value from ymin to ymax
  • plugging each of these Y values into the equation gives the corresponding fractional X value
  • these values could then be sorted and stored
From above, the specific polygon filling algorithm:
    moving from bottom to top up the polygon
    1. find intersections of the scan line with all edges of polygon
    2. sort intersections by increasing x coordinate
    3. moving through list of increasing x intersections
        parity bit = even (0)
        each intersection inverts the parity bit
        draw pixels when parity is odd (1)

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)

  • contains all edges sorted by Ymin
  • array of buckets - one bucket for each scan line in the polygon
  • each bucket has a list of entries stored in increasing X coordinate order (where X is the X value at the Ymin of the line)
  • each entry contains Ymax, Xmin (again X at Ymin), 1/m
  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 we are at a left edge then that pixel is interior
    if we are at a right edge then that pixel is not interior
- if intersection is at a non integer value (say 4.6) is pixel 4 interior? is pixel 5 interior?
    if we are outside the polygon (parity is even) then we round up (pixel 5 is interior)
    if we are inside the polygon (parity is odd) then we round down (pixel 4 is 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)
    bottom vertex of a line segment counts in parity calculation
    top vertex of a line segment does not count in parity calculation
- what if 2 vertices define a horizontal edge?
    imperfect, but easy, solution is to ignore this edge.
Example:

    So the global ET starts out being ...
------     ----------------     --------------- 
| 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.

No comments:

Post a Comment