Fractions, division + Bresenham lines

Share your advanced PureBasic knowledge/code with the community.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Fractions, division + Bresenham lines

Post by BackupUser »

Code updated for 5.20+

Restored from previous forum. Originally posted by MrVainSCL.

This time i have a small introduction how does the bresenham draw line algorythm works and how to speed it up :wink:

Introduction
Many years while figuring out how to draw lines using 68000 I had the chance to quickly look at that great bible, Computer Graphics Principles and Practice. At the time I didn't really understand all those pages of maths concerning the Bresenham line algorithm, so came up with a far, far simpler explaination. As far as I know, everyone still uses Bresenham's descriptions to explain how it works. In this article, I'll explain it from a different point of view.

NOTE: This article focuses on the hidden divisions of the Bresenham algorithm.

Lines and division
Let's skip over the dodgy looking ASCII and normal line definitions and start with two end-points (x1,y1) and (x2,y2). We'll consider how to draw a line from (10,20) to (99,39) which gives us a horizontal length of 90 pixels and vertical length of 20 pixels. After dividing the horizontal length (major axis) by the vertical length (minor axis) we get 90/20 = 4.5

In short we need to draw the entire line by using a number of 4.5 horizontal pixel spans.

Fractions and Totals
But we can't draw 0.5 of a pixel, so we must either draw 4 or 5 pixels. To deal with the above problem of 4.5 pixels we seperate the integer part, use that to know how big the span should be and then add the fractional part to a total. Once that total becomes greater or equal to 1.0 we draw an extra pixel.

Think of it this way:

Instead of trying to draw 4.5 pixels then a second 4.5 pixels (ie. 9 pixels), we could draw 4 pixels then 4+1 pixels and still reach our 9.

The very clever part in the Bresenham algorithm is the way it performs 2 divisions using just ADD and SUBTRACT. As we'll see in a short while there is one (very obvious) division for the Integer part and one (slightly hidden) division to obtain the fraction part.

Division through subtraction
As you all know multiplication can be performed using repeated additions (4n = n + n + n + n). Also division can be done through repeated subtraction.

Code: Select all

      num = 90
      divisor = 20

      count = 0
      remainder = num
again:
      count = count + 1
      remainder = num - divisor
      if remainder >= divisor then goto again

Running the above code should give you count=4 and remainder=10 which is exactly right.

Code: Select all


      mov     bx, 90          ; num = 90
      mov     cx, 20          ; divisor = 20
init:
      mov     ax, 0           ; count = 0
      mov     dx, bx          ; remainder = num
again:
      inc     ax              ; count + 1
      sub     dx, cx          ; remainder - divisor
      cmp     dx, cx          ; is remainder >= divisor ?
      jae     again
Integer division and spans
Okay, there has been nothing ground breaking so far. The previous code performs an integer division just like the DIV and IDIV instructions and gives us the remainder too (remainder = num MOD divisor).

It seems like we can modify it to perform a line drawing algorithm. We plot a pixel inside the above 'span' loop and then re-initialise the remainder and count variables and then loop back to draw the required number of spans (in our example case, 20 of ~4 pixel spans).

Code: Select all

example:

      mov     bp, 20          ; number of spans
span:
      mov     bx, 90          ; num = 90
      mov     cx, 20          ; divisor = 20
init:
      mov     ax, 0           ; count = 0
      mov     dx, bx          ; remainder = num
again:
      mov     ES:[di], 5      ; plot pixel, color 5
      inc     di              ; x + 1
      inc     ax              ; count + 1
      sub     dx, cx          ; remainder - divisor
      cmp     dx, cx          ; is remainder >= divisor ?
      jae     again

      add     di, 320         ; y + 1
      dec     bp
      jnz     span            ; another span ?
BUT, we have forgotten about the fraction part.

Performing the above example code will only give us 20 spans of 4 pixels (80 pixels), but we wanted to draw 20 spans of 4.5 pixels (90 pixels!!).

Using remainders to store fractions
You may think we need to use some more variables (or registers) to store the fraction to handle that 0.5 part, but we can simply use the remainder to do this.

Code: Select all

      90 / 20 = 4.5           integer part 4
                              fractional part 0.5

      int (90/20) = 4         integer part
        90 mod 20 = 10        remainder
Look closely at that remainder value and the divisor (20).


10 / 20 = 0.5 fractional part !

remainder / divisor = fractional part

which is:
(num MOD divisor) / divisor = fractional part
If we modify the repeated-subtraction for division so that instead of initialising 'remainder = num' we add to the remainder and do this 'remainder = remainder + num' then that 10 remainder will soon cause an extra iteration of that 'again' loop which will draw that extra pixel.

This means we draw 5 pixels instead of 4. We have totaled up all those 0.5 fractions until we get >= 1.0

Don't worry if you don't understand it, here is some lame QBASIC code.

Code: Select all

        CLS
        num = 90
        divisor = 20

        remainder = num

        spancount = 20
span:
        count = 0
again:
        count = count + 1
        remainder = remainder - divisor
        IF remainder >= divisor THEN GOTO again

        PRINT count
        remainder = remainder + num
        spancount = spancount - 1
        IF spancount > 0 THEN GOTO span
Please note how the count value toggles between 4 and 5, this is because we are performing an integer division (using repeated subtraction) AND we are also totaling up all those remainder values of the integer division. Eventually that remainder value (10 in our example) we become large enough to cause an extra iteration of the division loop (10 + 10 = 20, which is >= divisor value).

Closing Words
I hope you understood all that. Sorry if you didn't, just try some different values for yourself (like 10 and 3) and look at the result in count after each span. I still find it amazing that we are able to perform an integer and a fractional divide simply be replacing MOV with an ADD instruction.

Bressy rules ;-P




PIII450, 256MB Ram, 80GB HD + 6,4 GB, RivaTNT, DirectX8.1, SB AWE64, Win2000 + all Updates...

greetz
MrVainSCL! aka Thorsten
Last edited by fsw on Thu Aug 22, 2013 8:04 pm, edited 1 time in total.
Reason: Code updated for 5.20+
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Danilo.

From the very first line it _sounds_ like you
wrote this tutorial, MrVain... but you didnt.

So next time its IMO better to include credits
for the original author and tell use where you
got this tutorial from (Book, URL, etc..).

Another thing: All the code (ASM and QBASIC) is
DOS based, so it would be better if you translate
it to Win32 and not copy & paste the text only.

Thanks for listening MrVain - keep up the good work :)

cya,
...Danilo

(registered PureBasic user)
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by tranquil.

I agree with Danilo. And couse I'm very drunken atm, I will go to bed now. :)

But anyway, MrVain posted this to this forum couse he only wants to help and give Tips and Tricks. But its right, the URL could be enough and we dont need to waste the forums webspace for a mirror of an allready existend website. :))))

Cheers
Mike

Tranquilizer/ Secretly!
http://www.secretly.de
Registred PureBasic User
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi:

I agree with tranquil: I'm very drunken ATM, so I feel so friendly. MRVainSCL's tutorial is good. His copy or not of whatever tutorial will be helpfull, I'm sure. Let's be friends!

Have a nice sleep,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fweil.

I was too much drunken also. What a friday we had !

Francois Weil
14, rue Douer
F64100 Bayonne
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Berikco.

I smell beer in the forum

Regards,

Berikco

http://www.benny.zeb.be
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by MrVainSCL.

Hehe, hi @ all drunken guys:)
I agree with all your replies to my posting! The topic article was wrote long time ago by an good old friend (amiga and pc scener) when i explained how does a line routine works and showed him my way! We discused about some parts of different ways how to speed it up and so on... He wrote down some basic words and basical code snips at a party where we told about the stuff! Some time after the party i got an email from him with the article i posted! I dont really remember his handle and since i lost my adressbook on my good old A1200 (years ago)! Btw he is no longer in scene activce as i heared, but i dont realy know - sorry!

The article is based also on stuff by this guy and me! Danilo, take a look to the forum and search for an old posting, where i explained with a small code snip how does the bresenham line algo works! I really know it and dont used/modified any source i found on the net :wink:

Ok guys.... let us now continue drink and try to code... :wink:

*cheers*
*à votre santé!*
*isalud!*
*cin-cin!*
*prost*

PIII450, 256MB Ram, 80GB HD + 6,4 GB, RivaTNT, DirectX8.1, SB AWE64, Win2000 + all Updates...

greetz
MrVainSCL! aka Thorsten
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by tinman.
Originally posted by MrVainSCL
look to the forum and search for an old posting, where i explained with a small code snip how does the bresenham line algo works! I
Can I just ask why you think this article is useful? Given that:

1) PB has a line command in it
2) The OS will almost certainly have a line drawing command
3) Most graphics hardware should have line drawing acceleration

Is there any real need to know how to draw a line. Of course, you could use it to calculate the positions of a sprite as it moves across the screen, but if you are going to explain how it works, maybe you could give examples of possible uses (otherwise people might not make the link).

BTW, your floating point tip was good - too many people do not understand the limitations.


--
It's not minimalist - I'm increasing efficiency by reducing input effort.
(Win98first ed. + SP1, PB3.30)
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by MrVainSCL.

Hi tinman
Sure you can ask me for the reason i posted it, why not? :wink:

Tinam: "1) PB has a line command in it":
Sure, you are right! But i think its not false to explain for some guys (esp. coding newbies) how does a line command works! (not important if the routine is OS integrated or GfxCard integrated...)

Tinam: "2) The OS will almost certainly have a line drawing command":
Sure! I think a lot of guys are using every day the drawing comamnds like line or any other OS integrated commands but dont really know how does this routine internal work to get a smooth line or whatever command they are calling! And what is false to explain it? If someone is interested, he can modify and create his own line command with some special effects if he is creative :wink: ! Another point how does the line command work, is maybe interested for people who want to draw in a dimension to create/precalculate a texture or whatever and not on sceen i.e.!? :wink:

Tinam: "3) Most graphics hardware should have line drawing acceleration"
May be! For example i wrote my own line routine on blitzbasic amiga with the plot comamnd to have smaller executeables instead of using the line command which added some more KB´s to the executeable! *strange, i know* All in one, i think its not wrong to explain old stuff for newbies...

Just a small point... Nearly all old amiga users know 2D car games like lotus and so on... But ask someone if he could explain you in all (!) parts how to code the stuff in a pure 2D engine... The sad answer today in modern 3D age is just NO!

What i want to say: In today modern 3D age, a lot of coders are searching and using public code-snips like 3d-engines and so on... They are able to get the codesnip work but often the dont know why does it really work to get that result! On Amiga guys know how to code 3d-engines, envmap and so on without a 3d acc.gfxcard... On PC-Scene i saw and talked with some people who released nice intros and demos but they are not all able to explain how does the routine works... They just told me... "I found it on the net and included it". This is not for all coders...

In my opinion its really sad, that modern coders dont really know how to do nice old skool 2d stuff... thats just one reason more, to show guys how does the line routine work :wink:

Thats all... :wink:

PS: When i see how often this topic was clicked by users, i think its an interested topic, else not so many people would take a look to this :wink:


PIII450, 256MB Ram, 80GB HD + 6,4 GB, RivaTNT, DirectX8.1, SB AWE64, Win2000 + all Updates...

greetz
MrVainSCL! aka Thorsten
Post Reply