Antialiased Bezier Curves

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
acetoline
Posts: 228
Joined: Oct 27, 2006 6:50
Contact:

Antialiased Bezier Curves

Post by acetoline »

After a little playing around and experimenting with ways that my anti-aliased circles could be used, I came up with this program to draw (very) pretty anti-aliased bezier curves.
It took three hours off of my life to write this prog and perfect it; enjoy!

It looks beautifully smooth, thanks to the algorithm it uses. The main problem it has is speed.

Note that the code is NOT optimized, so it probably isn't suitable for anything that requires more than a few curves.

Code: Select all

'just some simple code for drawing beziers.
#Define Dist(p1, p2) (Sqr((p1.x-p2.x)^2 + (p1.y-p2.y)^2))

'you can play with this variable to adjust the additional accuracy.
'minus values = more speed, less accurate.
'positive values =less speed, more accurate.
'Values beyond 2 have no effect.
Const ACCURACY_OVERKILL = -0.3

Type TPoint
    x As Single
    y As Single
End Type

Sub DrawDot(ByVal x As Single, ByVal y As Single, radius As Single, r As uByte, g As uByte, b As uByte, a As uByte)
    Dim d As Single
    Dim As Single px,py
    Dim As Single rad2
    DIm As Single cr
    Dim As Single x1,y1,x2,y2
    'Print y
    
    If a > 255 Then a = 255
    
    cr=(radius/1.5)
    x1 = Int(x-radius-ACCURACY_OVERKILL)
    y1 = Int(y-radius-ACCURACY_OVERKILL)
    x2 = Int(x+radius+ACCURACY_OVERKILL)
    y2 = Int(y+radius+ACCURACY_OVERKILL)
    
    For py= y1 To y2
        For px = x1 To x2
            d = Sqr(((px-x)^2)+((py-y)^2))
            d = d / radius
            If d < 1 Then
                d ^= cr
                Line(px, py)-(px, py),RGBA(r,g,b,a*(1-d)),BF
            End If
        Next
    Next
End Sub

'this is an adaptive algorithm that I discovered myself.
'TODO: make it smarter when working with widths.
Sub RenderBezier (p1 As TPoint, p2 As TPoint, p3 As TPoint, p4 As TPoint, lwidth As Single, r As uByte, g As uByte, b As uByte, alpha As uByte)
    Dim t As Single = 1 'the most important value in the whole procedure
    Dim vert As Single = 0
    Dim binc As Single
    Dim bdist As Single
    Dim cp As TPoint
    Dim lp As TPoint
    Dim c As Integer=0
    'calculate the base increment (binc) using the base distance (bdist).
    'I'm too lazy to conjure up a direct algorithm to do this.
    'I just use a converging loop.
    binc = 0.3
    For i As Integer = 1 To 10 'don't do an infinite loop if it doesn't seem to converge.
        t = binc
        cp.x = p1.x*(1-t)^3 + 3*p2.x*((1-t)^2)*t + 3*p3.x*(1-t)*(t^2) + p4.x*(t^3)
        cp.y = p1.y*(1-t)^3 + 3*p2.y*((1-t)^2)*t + 3*p3.y*(1-t)*(t^2) + p4.y*(t^3)
        bdist = Dist(cp, p1)
        binc /= bdist
        If (1 > (bdist - 0.02)) And (1 < (bdist + 0.02)) Then Exit For 'converged. yay!
    Next i
    
    'Now, just go up the curve, adjusting the base increment whenever necessary.
    t = 0
    Do Until (t > 1)
        vert = 1-t
        cp.x = p1.x*vert^3 + 3*p2.x*vert*vert*t + 3*p3.x*vert*t*t + p4.x*(t^3)
        cp.y = p1.y*vert^3 + 3*p2.y*vert*vert*t + 3*p3.y*vert*t*t + p4.y*(t^3)
        
        bdist = Dist(cp, lp)
        binc /= bdist

        If (lwidth <= 1.5) Or (c=0) Then DrawDot (cp.x, cp.y, lwidth, r, g, b, 2*alpha/lwidth)
        lp.x = cp.x
        lp.y = cp.y
        t += binc
        c = 1-c
    Loop
End Sub


ScreenRes 600,600, 32, 1, &H40
Randomize Timer

Line (0,0)-(600,600),RGBA(255,255,255,255),BF

RenderBezier (type<TPoint>(100,100), type<TPoint>(300,100), type<TPoint>(300,300), type<TPoint>(500,300), _
                    1.1, 0,0,0,255)
Dim As Single cx, cy, lx, ly, ctx, cty, ltx, lty, tim

Print "Press any key to draw a continuous curve"
Sleep
Line (0,0)-(600,600),RGBA(255,255,255,255),BF
tim = timer
For i As Integer = 0 To 20
    cy = 600 * rnd
    cx = 600 * rnd
    ctx = (200 * rnd) - 100
    cty = (200 * rnd) - 100
    RenderBezier (type<TPoint>(lx,ly), type<TPoint>(lx-ltx,ly-lty), type<TPoint>(cx+ctx,cy+cty), type<TPoint>(cx,cy), _
                    1.4, 0,0,0,255)
    ltx = ctx: lty = cty
    lx = cx: ly = cy
Next i
Print "Time: " & Timer - tim

Print "Press any key to draw 50 antialiased bezier curves of various widths, colors, and alphas."
Sleep
Line (0,0)-(600,600),RGBA(255,255,255,255),BF
For i As Integer = 0 To 50
    RenderBezier (type<TPoint>(600*rnd,600*rnd), type<TPoint>(600*rnd,600*rnd), type<TPoint>(600*rnd,600*rnd), type<TPoint>(600*rnd,600*rnd), _
                    1+(2*Rnd), 255*rnd, 255*rnd, 255*rnd, 155+(100*rnd))
Next i
Sleep
Last edited by acetoline on Jan 12, 2007 16:09, edited 1 time in total.
relsoft
Posts: 1767
Joined: May 27, 2005 10:34
Location: Philippines
Contact:

Post by relsoft »

AWESOME!!!
Veggiet
Posts: 156
Joined: Apr 17, 2006 19:41

Post by Veggiet »

Very nice, although it might be slow for a game it would be perfect for a drawing program. one thing:

Code: Select all

'this line
If alpha > 255 Then alpha = 255
'returns an implicit variable allocation. I'm not sure but I guess it should be:
If a > 255 Then a = 255
duke4e
Posts: 717
Joined: Dec 04, 2005 0:16
Location: Varazdin, Croatia, Europe
Contact:

Post by duke4e »

Just wait till Joshy sees this, I'm sure he'll give you plenty speed optimization tips ;)
DNAarray
Posts: 29
Joined: May 15, 2006 21:17

good job

Post by DNAarray »

The next step is really a tough one, an anti-alised scalable font using 100% basic code, thus you would never need to use ttf libraries and the font could be usable by linux or windows. I have been playing around trying to do this. I think will try fuzzycircle(x,y,radius,alpha) where x and y are real numbers and if say x is a 1.4, it would randomly choose 1 60% of the time and 2 40% of the time. Thus you define about 20 points for a font symbol, and a scale, and the routine randomly draws circles where the font should be. Maybe I should stick with non scalable fuzzypset(x,y,alpha) for a test.

Anyways great job!
D.J.Peters
Posts: 8625
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

Hello acetoline
more of this (good) stuff and your first 2d drawing / painting application are borne. :-)

Joshy
acetoline
Posts: 228
Joined: Oct 27, 2006 6:50
Contact:

Post by acetoline »

Thanks for the feedback, guys!

Veggiet: yes, that line doesn't work. Actually, it's reduntant (a is already a ubyte, how could it be >255?) so you can just delete it altogether.

I also realized that there is a slight glitch in the program. I fixed it, and then I realized that it looked better without the fix. Funny how a glitch actually caused the curves to look better. Needless to say, the bug is going to become a feature ;)
Dr_D
Posts: 2453
Joined: May 27, 2005 4:59
Contact:

Post by Dr_D »

Another program with good looking results. These are very nice man.
Hezad
Posts: 469
Joined: Dec 17, 2006 23:37
Contact:

Post by Hezad »

i had to try to code those bezier curves ^^

(Did you know Bezier was a Renault engineer? He worked on those curves to make the shape of the car body!)

Here is a bezier curve ^^ It's not antialiased but you can change the shape with the controlling points.

Code: Select all

'' Courbes de Bezier

Dim as single t,Ptx,Pty,P0x,P1x,p2x,p3x,p0y,p1y,p2y,p3y
Dim as integer mousex,mousey,bouton,sens1,sens2,sensx,r,g,b
sens1=1
sens2=1
sensx=1
r=250
g=50
b=50

Screenres 320,200,16,2

P1x = 100
P1y = 116
p2x = 200
p2y = 112
p3x = 300
p3y = 115
p0x = 10
p0y = 110

Do
    
    Screenset 1,0
    CLS
    
    '' Contours et points de controle
    Line (p0x,p0y)-(p1x,p1y),rgb(120,120,120)
    Line (p1x,p1y)-(p2x,p2y),rgb(120,120,120)
    Line (p2x,p2y)-(p3x,p3y),rgb(120,120,120)
    
    Line (p0x-2,p0y-2)-(p0x+2,p0y+2),rgb (150,150,150),bf
    Line (p1x-2,p1y-2)-(p1x+2,p1y+2),rgb (150,150,150),bf
    Line (p2x-2,p2y-2)-(p2x+2,p2y+2),rgb (150,150,150),bf
    Line (p3x-2,p3y-2)-(p3x+2,p3y+2),rgb (150,150,150),bf

    '' Interaction
    
    Getmouse mousex,mousey,,bouton
    
    if bouton and 1 then
    if mousex<=p0x + 20 and mousex>=p0x - 20 then
        if mousey<=p0y + 20 and mousey>=p0y - 20 then
            P0x = mousex
            P0y = mousey
        end if
    end if
    
    if mousex<=p1x + 20 and mousex>=p1x - 20 then
        if mousey<=p1y + 20 and mousey>=p1y - 20 then
            P1x = mousex
            P1y = mousey
        end if
    end if
    
    if mousex<=p2x + 20 and mousex>=p2x - 20 then
        if mousey<=p2y + 20 and mousey>=p2y - 20 then
            P2x = mousex
            P2y = mousey
        end if
    end if
    
    if mousex<=p3x + 20 and mousex>=p3x - 20 then
        if mousey<=p3y + 20 and mousey>=p3y - 20 then
            P3x = mousex
            P3y = mousey
        end if
    end if
    
    end if

    '' Mouvement local
        
        'P1y+=1*sens1
        'P2y+=1*sens2
        
        'if p1y>195 or p1y<5 then sens1=-sens1
        'if p2y>140 or p2y<50 then sens2=-sens2
    
    '' Affichage

    For t = 0 to 1 step 0.001    
        Ptx = P0x * ((1 - t)^3) + 3 * P1x * t * ((1 - t)^2) + 3 * P2x * (t^2) * (1 - t) + P3x * (t^3)
        Pty = P0y * ((1 - t)^3) + 3 * P1y * t * ((1 - t)^2) + 3 * P2y * (t^2) * (1 - t) + P3y * (t^3)
        r=p2y+(t*100)
        g=p1y+(t*100)
        if r>255 then r=255
        if g>255 then g=255
        Circle(Ptx, Pty),2,rgb(r,g,b),,,,f
    next

    Screensync
    Screencopy
    
loop until multikey(&h01)
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Post by anonymous1337 »

I finally took a look at these and I must congratulate you on this. I love these curves :D
Hezad
Posts: 469
Joined: Dec 17, 2006 23:37
Contact:

Post by Hezad »

Thanks :)

The curves are not so difficult to code, you just have to know the equation (I got them on wikipedia) and make the control points "controllable" by the mouse :)
But this one was just a little test, the acetoline version is far better :) I even don't know how to do antialiasing ^^

Next step : The same in 3D :)
acetoline
Posts: 228
Joined: Oct 27, 2006 6:50
Contact:

Post by acetoline »

Thank you all for your comments. Right now I'm experimenting with filled bezier paths and such.
There are so few good resources on the web for quality graphics. When I have the time, I might write a set of tutorials or something ;)
relsoft
Posts: 1767
Joined: May 27, 2005 10:34
Location: Philippines
Contact:

Post by relsoft »

Filled bezier paths?
acetoline
Posts: 228
Joined: Oct 27, 2006 6:50
Contact:

Post by acetoline »

Filled bezier paths?
Yeah, those cool shapes you get when you take a bunch of beziers, connect them to form a path, and then fill them.
loul
Posts: 12
Joined: Jul 17, 2006 2:27

Help implementing your Bezier Crurves

Post by loul »

As a beginning programmer (age 71) I need a little help implementing your Beziers. What I want to do is use arrays of various lengths as input data for the curves (sort of like the curves for a stock) and while the B-Splines might be best, the Beziers should do. The "X" increment would be a constant 1 and of course the "Y" increment varies with the data. The Curve would have varying numbers of data points, say from 7 to 37. My problem is, where do I plug in the constants for the data points?

Thanks for any suggestions.

lou howard
Post Reply