Draw a general line |
On the following picture the red point in the center shows the start point of a general line. Whatever happens, on a 2D plane there are eight different fields based on the difference of dx and dy, where
dx := x2 - x1 dy := y2 - y1 (x1,y1) is the start point of the line (x2,y2) is the end point of the lineIf dx is zero, we draw a vertical line. If dy is zero, we draw a horizontal line. If dx=dy or dx=-dy, it's a 45 degrees line. In any other case the line is totally general. Have a look at the case when dx<dy. In this example dx=4 and dy=8. Becasue the smallest step on the screen is one pixel, for this line we have to increment the Y, and "sometimes" we have to increment the X. "Sometimes" is not a magic word, there is an exact calcualation form for finding out, when we have to increment the X. It is dy/dx, so in every second step we increment the X as well.
|
{ line steps for the 8 different directions } const Ntab : array[0..11] of integer=(-321,-320,-319,000, -001, 000, 001,000, 319, 320, 321,000); var r1,r2,r3,r4,r5 : word; { some space to store temporary data } procedure Line(x1,y1,x2,y2,color:integer); assembler; asm mov ES,SegA000 { Screensegment } mov ax,320 { Address calculation } mul y1 add ax,x1 mov di,ax mov al,Color.byte mov ES:[di],al { We show the first pixel } mov bx,0101h { sgn X (BL), sgn Y (BH), both +1 } mov dx,X2 sub dx,X1 { DX = X2-X1 } jnc @t1 neg dx { DX = abs X (where X=X2-X1) } mov bl,255 { BL = sgn X } @t1: mov si,Y2 sub si,Y1 { SI = Y2-Y1 } jnc @t2 neg si { SI = abs Y (where Y=Y2-Y1) } mov bh,255 { BH = sgn Y } @t2: mov cx,si { CX = abs Y } mov r2,bx { r2 = sgn X sgn Y, we store them } cmp dx,si { what's the main direction? (X/Y) } jnc @x_ge_y mov r1,dx { r1 = abs X } xor bl,bl { sgn X = 0 , X incremental line } jmp @t3 @x_ge_y: test dx,dx { abs X=abs Y=0 (it's only a pixel ) } jz @exit mov r1,si { r1 = abs Y } mov cx,dx { cx = abs X } xor bh,bh { sgn Y = 0 , Y incremental line } @t3: mov r3,cx { CX: number of pixels to draw } mov ax,cx shr ax,1 { AX = INT(CX/2) } { this main loop draws the line } @line_loop: add ax,r1 { increment } jc @diag { diagonal step } cmp ax,r3 jc @vhl { vertical or horizontal step } @diag: { diagonal step } sub ax,r3 mov r4,ax { save AX } mov ax,r2 { orignal sgn X, sgn Y } jmp @nextplot { go to draw } @vhl: { vertical or horizontal step } mov r4,ax { save AX } mov ax,bx { computed sgn X and sgn Y } @nextplot: { draw the pixel } mov r5,bx { save BX } inc al shl al,1 { AL = (sgn X+1)*2, can be 0,2 or 4 } inc ah shl ah,3 { AH = (sgn Y+1)*8, can be 0,8 or 16 } add al,ah xor ah,ah lea bx,Ntab { BX points to the right element of Ntab } add bx,ax add di,[bx] { so we make one step to the given direction } mov al,Color.byte mov ES:[di],al { put the pixel } mov ax,r4 { saved values back } mov bx,r5 loop @line_loop { do the next step } @exit: end; |
Do you understand? Well, I do not. :-) Yes, it's true, once I implemented, it was so clear for me, but right now it's just a mess. So don't be disappointed if you don't get it. Just use it.
Test the result
|
This is what we see on the screen:
|
Downloads: [ Gfx256 unit | Lines ]