Operating system: Windows8.1
Graphics: Nivida gtx965m
Development tools: Unity2017.3 | Nativec
Recently when learning Unity tilemap Brush Custom brush function, see its linear brush Linebrush is implemented using Bresenham algorithm, so take this opportunity to record the learning process here, and finally give a complete implementation.
Introduction
The Bresenham is a rasterized linear algorithm, or a straight-line simulation through the like. For example, the pixel dots are shown to simulate a red line.
Given two beginnings P1 (x1, y1) | P2 (x2, y2), how to draw a connection between two points. Assuming that the slope is constrained here, then the algorithm's process is as follows:
- Draws the starting point (x1, y1) .
- Draw the next point, the x-coordinate plus 1, to determine whether to the end, if the algorithm is completed. Otherwise, find the next point, the point is to be drawn is not the right neighbor, or the upper right adjacency point.
- Draws a point.
- Jump back to the second step.
- End.
The exact process of the algorithm is to select the point with the smallest difference in the y-coordinate of the intersection of the line each time the point is plotted, as shown in:
So the question focuses on how to find the nearest point, logically every timexare incremented1,ythen increase1or no increase. Specifically, the assumption has been drawn to theD1Point, then the nextxAdd1, but chooseD2Still isuPoint, intuitively, you know.D2With the target line andx + 1The intersection of the line is close, that is, the difference between the ordinate is small. Other words(x + 1, y + 1)Point ordinate difference is greater than0.5。 So chooseD2, and other points are executed according to the rule.
The
Basic Bresenham
Assume (x, y) to draw the starting point, the general visual idea is to ask m = dy/dx ( y Increments of ), and then incrementally x , set a new point to x1 = x + j , then y1 = round (y + j * m) . As you can see, this process involves a lot of floating-point arithmetic, which is inefficient (especially in embedded applications, where the DSP can do 2 multiplication in one cycle, and one floating point is hundreds of cycles).
Let's take a look at bresenham Algorithm, 1, (x, y +ε) The next point is (x, y + ε+ m) , here ε The is a cumulative error. As you can see, when Ε+m < 0.5 , Draw (x + 1, y) Point, otherwise draw (x + 1, y + 1) Point. After each draw, ε will be updated to the new value:
ε=ε+ m , if (ε+ m) <0.5 (or represented as 2 * (ε+ m) < 1 )
ε=ε+ m–1, other cases
Multiply the above formula by DX and ε* DX with the new symbol ξ , which can be
ξ=ξ+ dy, if 2 * (ξ+ dy) < DX
ξ=ξ+ DY–DX, other cases
As you can see, the operation has all become integers. The following is the pseudo-code for the algorithm:
Ξ←0,y←y1
For x←x1 to X2 do
Plot Point at (x, y)
If (2 (ξ+ dy) < DX)
ξ←ξ+ dy
Else
Y←y + 1,ξ←ξ+ DY–DX
End If
End for
handing multiple slopes
In practical applications, we will find that when dy > dx or the right side of the case, the desired result is not obtained, this is because we only consider dx > dy, and (x, y) the increment is positive. After analysis, there are 8 different partitioning scenarios to consider, as shown in:
Of course, if the 8 cases are enumerated directly in the algorithm, then the duplicated code will be very bloated, so in the design of the algorithm must fully consider the generality of the above-mentioned situations. For example, the right side X positive and negative partition is only the relationship between the Y -axis mirroring, in the implementation of the specific set of the correct increment direction.
Implementation
The following is a C language-based implementation:
voidDraw_line (intX1,intY1,intX2,inty2) { intDX = x2-X1; intdy = y2-Y1; intUX = (dx >0) <<1) -1; intUy = ((Dy >0) <<1) -1; intx = x1, y =Y1, EPS; EPS=0; DX = ABS (DX); DY =abs (DY); if(DX >dy) { for(x = x1; x! = x2; x + =UX) {printf ("x =%d y =%d\n", x, y); EPS+=dy; if(EPS <<1) >=dx) {y+ = Uy; EPS-=DX; } } } Else { for(y = y1; Y! = y2; y + =Uy) {printf ("x =%d y =%d\n", x, y); EPS+=DX; if(EPS <<1) >=dy) {x+ = UX; EPS-=dy; } } }}
The test data were Green line segments, starting point ( 10,10) and Blue Line, starting point ( 1, ten) end point (10,1).
The test data is a brown line, a start point ( 2,10) end point ( 4,1) , and a start point ( 6,9) endpoint ( 10,1) .
By populating the table with the test data that the program runs, you can visually see the connection path of the segment between two points.
Based on Unity
The Unity official example has given a C # implementation based on the following code:
// http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html Public StaticIenumerable<vector2int>getpointsonline (vector2int p1, vector2int p2) {intx0 =p1.x; intY0 =p1.y; intX1 =p2.x; intY1 =p2.y; BOOLSteep = Math.Abs (y1-y0) > Math.Abs (x1-x0); if(steep) {intT; T= x0;//swap x0 and y0x0 =y0; Y0=T; T= x1;//swap x1 and y1X1 =Y1; Y1=T; } if(X0 >x1) { intT; T= x0;//swap x0 and X1x0 =X1; X1=T; T= y0;//swap y0 and Y1Y0 =Y1; Y1=T; } intDX = x1-x0; intDY = Math.Abs (Y1-y0); intError = DX/2; intYstep = (y0 < y1)?1: -1; inty =y0; for(intx = x0; x <= x1; X + +) { yield return NewVector2int (Steep y:x), (steep?x:y)); Error= Error-dy; if(Error <0) {y+=Ystep; Error+=DX; } } yield Break; }
Finally, the two previous sets of data are brought into unity to verify the consistency of the results:
You can see that the data results from the previous program algorithm output are consistent with the C # implementation results that unity uses.
Summary
Resources:
http://blog.csdn.net/jinbing_peng/article/details/44797993
Https://www.cnblogs.com/gamesky/archive/2012/08/21/2648623.html
Http://www.cnblogs.com/pheye/archive/2010/08/14/1799803.html
[Algorithmstaff] Bresenham Fast Linear algorithm