The breshenham algorithm draws lines quickly, but it is not very fine. generally, the integer line can be drawn only on the integer coordinate, which leads to ugly serrations. in a book by Michael Abrash, I saw a good reverse Line Painting and decided to improve it with non-integer coordinates.
A Wu line not only looks better than a normal line, but also produces better animation. A straight line simply jumps from a position to the next position. however, a Wu line is very leisurely drifting to the next position.
How does it work?
Let's look at what a reverse line actually means. What should an appropriate line look like? How rough is a straight line? What should the tail end be like? A straight line is a one-dimensional object with an infinite length. it has no thickness. the tail end of a line segment does not look like anything. It is just the tail end. such a line cannot be painted on a pixel-based display device.
In computer graphics, it is generally assumed that the line segment is a thick pixel. this means that it can be painted. this also means that he is fine enough to ignore what the end should look like, because it is smaller than a pixel and cannot be painted.
When designing a new Wu line, we must make similar assumptions. because this new algorithm can process non-integer coordinate endpoints, what can be done for a line smaller than one pixel long?
Assume that
. Assume that pixels are in the center of their coordinates.
A straight line is a pixel thick
The shape of the end of a straight line is not important.
It can be better if:
. Two parallel segments with endpoints connected, one of which cannot be distinguished and one of which is a long segment
. The brightness/transparency of a straight line can be defined.
What is the final algorithm?
The final algorithm is quite easy to implement, but it seems too slow in practical application. but it is not really necessary, because its effect is almost the same as that of another faster method. anyway, I will clarify some basic things.
Imagine that you can zoom in the pixels of the screen on which we draw a line. The ideal draw line algorithm will calculate the exact area covered by each line to increase
Add the brightness of those pixels.
Such an algorithm is easy to write. In a higher resolution, redraw the relevant part of the screen, draw the line above it, and calculate the covered pixel or
Calculating it presicely.
However, this requires some accuracy. Let's look at the next method.
A more sensible Algorithm
This algorithm draws a pair of pixels across a straight line. A main cycle draws a pair of pixels along the length of the line, and the pixel pairs at the endpoint are calculated separately.
Pixel pair:
Again, a close-up of the screen draws an almost horizontal line.
This almost horizontal line goes through the vertical pixel column. Each time the pixel is crossed, the X coordinate is an integer, but the Y coordinate is not an integer.
A closer look, a single cross point:
In each crossing point, we will consider the pixel pairs of the Cross-ride straight line. The brightness of the two pixels should be 1, and the brightness of the center is the brightness of the ideal straight line.
The brightness of a pixel on a straight line must be proportional to (1-A), and the brightness of a pixel on the line must be proportional to (1-B). That is to say, the pixel closer to a straight line should be brighter.
Draw such a pixel pair along the straight line length, then you basically get a reverse line.
Draw an endpoint
The last thing is to draw the endpoint. this is not easy to handle. I still haven't handled them completely correctly. here is a method I use now. he is not ideal, but very similar. you will notice that when the straight line is almost perpendicular to the horizontal line, it has a careful failure, and the reverse process is also. you will notice this when moving slowly over 45 degrees.
Let's take a closer look at the endpoint:
We can see an endpoint of a straight line, represented by red points. Blue points represent the center point of the pixel. You can see that the vertex is not in the center point of the pixel.
Calculate the correct brightness of two pixels at the linear endpoint:
. Extend the straight line (backward or forward) to the nearest integer x coordinate (P ). calculate this pixel pair in the same way as the brightness of a common pixel pair on a straight line. then, because the straight line only covers a small part of the pixel (I), the straight line will reduce its brightness. so the brightness should be multiplied by I. therefore, when the entire straight line moves to the right, I will decrease, so that this pixel is dimmed smoothly.
Special Cases
What will happen to a straight line with a length less than one pixel? Because its length is too small to be accurate, you can draw it as needed. this is my own method. I stretch the line to a pixel and then reduce their brightness. so a very short straight line looks very dark. When it gets longer, it will become brighter. When it is a pixel, it will be normally drawn at its position.
Finally, some pseudocode
Some functions required for calculating the number of points in a Wu straight line:
Function trunc (X)
Return integer part of X
End of Function
Function frac (X)
Return fractional part of X
End of Function
Function invfrac (X)
Return 1-(fractional part of X)
End of Function
Wu linear program:
Procedure wuline (fixpt X1, fixpt Y1, fixpt X2, fixpt Y2)
Variable declerations:
Fixpt variables:
Grad, XD, YD, length, XM, ym
Xgap, ygap, xend, yend, XF, YF
Brigheness1, brigheness2
Integer Variables:
X, Y, ix1, ix2, iy1, iy2
Byte variables:
C1, C2
Code starts here:
Width and height of the line
XD = (x2-x1)
YD = (y2-y1)
If ABS (xd)> ABS (YD) then check line gradient
Horizontal (ISH) lines
If x1> X2 then if line is back to front
Swap X1 and X2 then swap it round
Swap Y1 and Y2
XD = (x2-x1)
And recalc XD & YD
YD = (y2-y1)
End if
Grad = YD/XD
Gradient of the line
End Point 1
-----------
Xend = trunc (X1 +. 5)
Find nearest integer x-Coordinate
Yend = Y1 + grad * (xend-x1)
And corresponding y value
Xgap = invfrac (X1 +. 5)
Distance I
Ix1
= Int (xend)
Calc screen coordinates
Iy1
= Int (yend)
Brightness1 = invfrac (yend) * xgap
Calc the intensity of the other
Brightness2 =
Frac (yend) * xgap
End Point pixel pair.
C1 = byte (brightness1 * maxpixelvalue) calc pixel values
C2 = byte (brightness2 * maxpixelvalue)
Drawpixel (ix1, iy1), C1
Draw the pair of pixels
Drawpixel (ix1, iy1 + 1), C2
YF = yend + grad
Calc first y-intersection
Main Loop
End Point 2
-----------
Xend = trunc (X2 +. 5)
Find nearest integer x-Coordinate
Yend = y2 + grad * (xend-x2)
And corresponding y value
Xgap = invfrac (x2-. 5)
Distance I
Ix2
= Int (xend)
Calc screen coordinates
Iy2
= Int (yend)
Brightness1 = invfrac (yend) * xgap
Calc the intensity of the first
Brightness2 =
Frac (yend) * xgap
End Point pixel pair.
C1 = byte (brightness1 * maxpixelvalue) calc pixel values
C2 = byte (brightness2 * maxpixelvalue)
Drawpixel (ix2, iy2), C1 draw the pair of pixels
Drawpixel (ix2, iy2 + 1), C2
Main Loop
---------
Loop X from (ix1 + 1) to (ix2-1) Main Loop
Brightness1 = invfrac (YF)
Calc pixel brightnesses
Brightness2 =
Frac (YF)
C1 = byte (brightness1 * maxpixelvalue) calc pixel values
C2 = byte (brightness2 * maxpixelvalue)
Drawpixel (x, INT (YF), C1 draw the pair of pixels
Drawpixel (x, INT (YF) + 1), C2
YF = YF + grad update the Y-Coordinate
End of X loop end of Loop
Else
Vertical (ISH) lines
Handle the vertical (ISH) lines in
Same way as the horizontal (ISH) ones
But swap the roles of X and Y
End if
End of procedure
Here is a more detailed description of the algorithm mentioned above. For convenience and speed, I used a fixed number of points here. In this case, it is obviously very convenient.
First, I have defined some functions.
Last
It turns out to work and looks amazing.
This program draws a Newton's cradle in two resolutions to demonstrate that Wu's straight lines can draw small things and still look OK. You can see how smooth they move.