Kamis, 14 April 2011

The common conventions will be used:

    * that pixel coordinates increase in the right and down directions (e.g. that the pixel at (1,1) is directly above the pixel at (1,2)), and
    * that the pixel centers have integer coordinates.

The endpoints of the line are the pixels at (x0, y0) and (x1, y1), where the first coordinate of the pair is the column and the second is the row.

The algorithm will be initially presented only for the octant in which the segment goes down and to the right (x0‚â§x1 and y0‚â§y1), and its horizontal projection x1 ‚àí x0 is longer than the vertical projection y1 ‚àí y0 (the line has a slope whose absolute value is less than 1 and greater than 0.) In this octant, for each column x between x0 and x1, there is exactly one row y (computed by the algorithm) containing a pixel of the line, while each row between y0 and y1 may contain multiple rasterized pixels.

Bresenham's algorithm chooses the integer y corresponding to the pixel center that is closest to the ideal (fractional) y for the same x; on successive columns y can remain the same or increase by 1. The general equation of the line through the endpoints is given by:

    \frac{y - y_0}{y_1-y_0} = \frac{x-x_0}{x_1-x_0}.

Since we know the column, x, the pixel's row, y, is given by rounding this quantity to the nearest integer:

    y = \frac{y_1-y_0}{x_1-x_0} (x-x_0) + y_0.

The slope (y1 ‚àí y0) / (x1 ‚àí x0) depends on the endpoint coordinates only and can be precomputed, and the ideal y for successive integer values of x can be computed starting from y0 and repeatedly adding the slope.

In practice, the algorithm can track, instead of possibly large y values, a small error value between ‚àí0.5 and 0.5: the vertical distance between the rounded and the exact y values for the current x. Each time x is increased, the error is increased by the slope; if it exceeds 0.5, the rasterization y is increased by 1 (the line continues on the next lower row of the raster) and the error is decremented by 1.0.

In the following pseudocode sample plot(x,y) plots a point and abs returns absolute value:

 function line(x0, x1, y0, y1)
     int deltax := x1 - x0
     int deltay := y1 - y0
     real error := 0
     real deltaerr := abs (deltay / deltax)    // Assume deltax != 0 (line is not vertical),
           // note that this division needs to be done in a way that preserves the fractional part
     int y := y0
     for x from x0 to x1
         plot(x,y)
         error := error + deltaerr
         if error ‚â• 0.5 then
             y := y + 1
             error := error - 1.0

[edit] Generalization

The version above only handles lines that descend to the right.The first case is allowing us to draw lines that still slope downwards but head in the opposite direction. This is a simple matter of swapping the initial points if x0 > x1. Trickier is determining how to draw lines that go up. To do this, we check if y0 ‚â• y1; if so, we step y by -1 instead of 1. Lastly, we still need to generalize the algorithm to drawing lines in all directions. Up until now we have only been able to draw lines with a slope less than one. To be able to draw lines with a steeper slope, we take advantage of the fact that a steep line can be reflected across the line y=x to obtain a line with a small slope. The effect is to switch the x and y variables throughout, including switching the parameters to plot. The code looks like this:

 function line(x0, x1, y0, y1)
     boolean steep := abs(y1 - y0) > abs(x1 - x0)
     if steep then
         swap(x0, y0)
         swap(x1, y1)
     if x0 > x1 then
         swap(x0, x1)
         swap(y0, y1)
     int deltax := x1 - x0
     int deltay := abs(y1 - y0)
     real error := 0
     real deltaerr := deltay / deltax
     int ystep
     int y := y0
     if y0 < y1 then ystep := 1 else ystep := -1
     for x from x0 to x1
         if steep then plot(y,x) else plot(x,y)
         error := error + deltaerr
         if error ‚â• 0.5 then
             y := y + ystep
             error := error - 1.0

The function now handles all lines and implements the complete Bresenham's algorithm.
[edit] Optimization

The problem with this approach is that computers operate relatively slowly on fractional numbers like error and deltaerr; moreover, errors can accumulate over many floating-point additions. Working with integers will be both faster and more accurate. The trick we use is to multiply all the fractional numbers above by deltax, which enables us to express them as integers. The only problem remaining is the constant 0.5—to deal with this, we change the initialization of the variable error, and invert it for an additional small optimization. The new program looks like this:

 function line(x0, x1, y0, y1)
     boolean steep := abs(y1 - y0) > abs(x1 - x0)
     if steep then
         swap(x0, y0)
         swap(x1, y1)
     if x0 > x1 then
         swap(x0, x1)
         swap(y0, y1)
     int deltax := x1 - x0
     int deltay := abs(y1 - y0)
     int error := deltax / 2
     int ystep
     int y := y0
     if y0 < y1 then ystep := 1 else ystep := -1
     for x from x0 to x1
         if steep then plot(y,x) else plot(x,y)
         error := error - deltay
         if error < 0 then
             y := y + ystep
             error := error + deltax

Remark: If you need to control the points in order of appearance (for example to print several consecutive dashed lines) you will have to simplify this code by skipping the 2nd swap:

function line(x0, x1, y0, y1)
     boolean steep := abs(y1 - y0) > abs(x1 - x0)
     if steep then
         swap(x0, y0)
         swap(x1, y1)
     int deltax := abs(x1 - x0)
     int deltay := abs(y1 - y0)
     int error := deltax / 2
     int ystep
     int y := y0
 
     int inc REM added
     if x0 < x1 then inc := 1 else inc := -1 REM added

     if y0 < y1 then ystep := 1 else ystep := -1
     for x from x0 to x1 with increment inc REM changed
         if steep then plot(y,x) else plot(x,y)
         REM increment here a variable to control the progress of the line drawing
         error := error - deltay
         if error < 0 then
             y := y + ystep
             error := error + deltax

[edit] Simplification

It is further possible to eliminate the swaps in the initialisation by considering the error calculation for both directions simultaneously:

 function line(x0, y0, x1, y1)
   dx := abs(x1-x0)
   dy := abs(y1-y0)
   if x0 < x1 then sx := 1 else sx := -1
   if y0 < y1 then sy := 1 else sy := -1
   err := dx-dy

   loop
     setPixel(x0,y0)
     if x0 = x1 and y0 = y1 exit loop
     e2 := 2*err
     if e2 > -dy then
       err := err - dy
       x0 := x0 + sx
     end if
     if e2 <  dx then
       err := err + dx
       y0 := y0 + sy
     end if
   end loop

Tidak ada komentar:

Posting Komentar