Connected balls

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
hhr
Posts: 211
Joined: Nov 29, 2019 10:41

Connected balls

Post by hhr »

Code: Select all

#cmdline "-s gui"

Open Scrn As #1 : Close #1 ' Wait until console is active, useful in Linux with QTerminal

Dim As Long w = 800,h = 600
Screenres w,h
Window (0,0)-(1,1)

Dim Shared As Single r,dt,c,d
r = 0.01  ' radius
dt = 0.02 ' interval
c = 0.02

Type ball
   Dim As Single x,y,vx,vy
End Type

Dim Shared As ball b(1 To 2)

Sub initialise
   b(1).x = 0.7
   b(1).y = 0.1
   b(1).vx = 0.1
   b(1).vy = 0.1
   
   b(2).x = 0.1
   b(2).y = 0.5
   b(2).vx = 0.5
   b(2).vy = 0.5
End Sub

Sub YoYo
   d = Sqr((b(1).x - b(2).x)^2 + (b(1).y - b(2).y)^2) ' distance
   
   If d > 2*r Then ' 20*r
      b(1).x = b(1).vx*dt + b(1).x - (b(1).x - b(2).x)*c
      b(1).y = b(1).vy*dt + b(1).y - (b(1).y - b(2).y)*c
      
      b(2).x = b(2).vx*dt + b(2).x + (b(1).x - b(2).x)*c
      b(2).y = b(2).vy*dt + b(2).y + (b(1).y - b(2).y)*c
   Else
      b(1).x = b(1).vx*dt + b(1).x + (b(1).x - b(2).x)*c
      b(1).y = b(1).vy*dt + b(1).y + (b(1).y - b(2).y)*c
      
      b(2).x = b(2).vx*dt + b(2).x - (b(1).x - b(2).x)*c
      b(2).y = b(2).vy*dt + b(2).y - (b(1).y - b(2).y)*c
   End If
   
   If (b(1).x >= (1-r)) Or (b(1).x <= r) Then b(1).vx = -b(1).vx
   If (b(1).y >= (1-r)) Or (b(1).y <= r) Then b(1).vy = -b(1).vy
   
   If (b(2).x >= (1-r)) Or (b(2).x <= r) Then b(2).vx = -b(2).vx
   If (b(2).y >= (1-r)) Or (b(2).y <= r) Then b(2).vy = -b(2).vy
End Sub

initialise
Do
   YoYo
   Screenlock
   Cls
   Circle (b(1).x,b(1).y),r,,,,,F
   Circle (b(2).x,b(2).y),r,,,,,F
   Line (b(1).x,b(1).y)-(b(2).x,b(2).y)
   Screenunlock
   Sleep 10
Loop Until Len(Inkey)

initialise
Cls
Print : Print " The inner ball"
Do
   YoYo
   Screenlock
   Circle (b(1).x,b(1).y),0.002,,,,,F
   Screenunlock
   Sleep 10
Loop Until Len(Inkey)

initialise
Cls
Print : Print " The outer ball"
Do
   YoYo
   Screenlock
   Circle (b(2).x,b(2).y),0.002,,,,,F
   Screenunlock
   Sleep 10
Loop Until Len(Inkey)

initialise
Cls
Print : Print " The connecting line"
Do
   YoYo
   Screenlock
   Line (b(1).x,b(1).y)-(b(2).x,b(2).y)
   Screenunlock
   Sleep 10
Loop Until Len(Inkey)
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Connected balls

Post by dodicat »

Thanks hhr.
Here is one I did a few years ago:

Code: Select all


Screen 19,,2
Screenset 1,0
Dim Shared As Integer xres,yres,border=50,gf=500
Screeninfo xres,yres

Function Regulate(Byval MyFps As Integer,Byref fps As Integer) As Integer
    Static As Double timervalue,lastsleeptime,t3,frames
    Var t=Timer
    frames+=1
    If (t-t3)>=1 Then t3=t:fps=frames:frames=0
    Var sleeptime=lastsleeptime+((1/myfps)-T+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    lastsleeptime=sleeptime
    timervalue=T
    Return sleeptime
End Function

#define map(a,b,_x_,c,d) ((d)-(c))*((_x_)-(a))/((b)-(a))+(c)

Type thing 
    As Single x,y,z     'position
    As Single dx,dy   'velocity
    As Uinteger col   'colour
    As Integer radius
    As Integer collide_distance 
    As Integer applyG
    As Single fy =.01     'drag on y
    As Single fx=.01      'drag on x
    As Single gravity=.05
    Declare Function DetectBallCollisions(Byref As thing) As Single
    Declare Sub friction()
    Declare Sub boundaries()
    Declare Sub motion(() As thing,As Integer)
    Declare Static Sub BallCollisions(() As thing)
    Declare Static Sub initialize(() As thing)
    Declare  Sub Tie(() As thing,As Integer)
    Declare Sub Draw()
End Type

Function incircle(x As Single,y As Single,r As Integer,mx As Integer,my As Integer) As Integer
    Return ((mx-x)^2+(my-y)^2) < r
End Function

Function Distance(p1 As thing,p2 As thing) As Single
    Return Sqr((p1.x-p2.x)*(p1.x-p2.x)  + (p1.y-p2.y)*(p1.y-p2.y))
End Function

Sub lineto(x1 As Single,y1 As Single,x2 As Single,y2 As Single,L As Single,Byref ox As Single,Byref oy As Single)
    Var dx=x2-x1,dy=y2-y1
    ox=x1+dx*L
    oy=y1+dy*L
End Sub

Sub thing.tie(points() As thing,n As Integer)
    Dim As thing f1,f2
    Dim As Single f=this.gravity/gf
    Var d1=distance(points(n),points(n-1)),d2=distance(points(n),points(n+1))
    Var diffx1=points(n).x-points(n-1).x,diffy1=points(n).y-points(n-1).y
    Var diffx2=points(n).x-points(n+1).x,diffy2=points(n).y-points(n+1).y
    diffx1=diffx1*d1*f
    diffy1=diffy1*d1*f
    diffx2=diffx2*d2*f
    diffy2=diffy2*d2*f 
    dx-=(diffx1+diffx2)
    dy-=(diffy1+diffy2)
End Sub

Function thing.DetectBallCollisions(Byref player As thing) As Single
    Dim As Single xdiff = this.x-player.x
    Dim As Single ydiff = this.y-player.y
    If Abs(xdiff) > this.collide_distance*2 Then Return 0
    If Abs(ydiff) > this.collide_distance*2 Then Return 0
    Var L=Sqr(xdiff*xdiff+ydiff*ydiff)
    If L<=this.collide_distance*2 Then Function=L
End Function

Sub thing.friction()
    If dy > 0 Then dy -=  fy Else  dy += fy 
    If dx > 0 Then dx -= fx Else  dx += fx 
End Sub

Sub thing.boundaries()
    If y>yres-radius-border Then y=yres-radius-border:  dy=-dy
    If y<radius+border Then y=radius+border :dy=-dy
    If x<radius+border Then x=radius+border: dx=-dx
    If  x>xres-radius-border Then x=xres-radius-border:dx=-dx
End Sub

Sub thing.motion(p() As thing,n As Integer)
    If n>1 And n<Ubound(p)-1 Then
        x+=dx
        y+=dy
        If applyG Then dy+=gravity
    End If
End Sub

Sub thing.draw()
    Circle(x,y),radius,col,,,,f
End Sub

Sub thing.BallCollisions(points() As thing)
    For n1 As Integer =1 To Ubound(points)-2
        For n2 As Integer =n1+1 To Ubound(points)-1
            Var L=points(n1).DetectBallCollisions(points(n2))
            If L Then
                Var impulsex=(points(n1).x-points(n2).x)
                Var impulsey=(points(n1).y-points(n2).y)
                Dim As Single ln=Sqr(impulsex*impulsex+impulsey*impulsey)
                impulsex/=ln'normalize the impulse
                impulsey/=ln
                'In case of overlap circles, reset to non overlap positions
                points(n1).x=points(n2).x+(points(n1).collide_distance*2)*impulsex
                points(n1).y=points(n2).y+(points(n1).collide_distance*2)*impulsey
                Var impactx=points(n1).dx-points(n2).dx
                Var impacty=points(n1).dy-points(n2).dy
                Var dot=impactx*impulsex+impacty*impulsey
                points(n1).dx-=dot*impulsex
                points(n1).dy-=dot*impulsey
                points(n2).dx+=dot*impulsex
                points(n2).dy+=dot*impulsey
            End If
        Next n2
    Next n1
End Sub

Sub thing.initialize(b() As thing)
    Dim As Integer ct
    For x As Integer=border+15 To yres-border-15 Step 60
        ct+=1
        Redim Preserve b(0 To ct)
        With b(ct)
            .x=400
            .y=x
            .radius=20
            .col=Int(Rnd*14)
            .dx=0
            .dy=0
            .collide_distance=.radius
            .applyG=1
        End With
    Next x
    Redim Preserve b(Ubound(b)+1)
    b(1).x=xres/2
    b(1).y=.1*yres
    b(1).applyg=0:b(Ubound(b)-1).applyg=0
    b(0)=b(1):b(Ubound(b))=b(Ubound(b)-1)
    b(Ubound(b)-1).radius=20'18
End Sub

Function ShortSpline(p() As thing,t As Single) As thing
    #macro set(n)
    0.5 *(     (2 * P(2).n) +_
    (-1*P(1).n + P(3).n) * t +_
    (2*P(1).n - 5*P(2).n + 4*P(3).n - P(4).n) * t*t +_
    (-1*P(1).n + 3*P(2).n- 3*P(3).n + P(4).n) * t*t*t)
    #endmacro
    Dim As thing G
    G.x=set(x):G.y=set(y):G.z=set(z)
    Return g
End Function

Sub GetSpline(v() As Thing,outarray() As Thing,colour As Uinteger,arraysize As Integer=1000)
    Dim As Thing p(1 To 4)
    Redim outarray(0)
    Dim As Single stepsize=(Ubound(v)-1)/(arraysize)
    If stepsize>1 Then stepsize=1
    For n As Integer=Lbound(v)+1 To Ubound(v)-2
        p(1)=v(n-1):p(2)=v(n):p(3)=v(n+1):p(4)=v(n+2)
        For t As Single=0 To 1 Step stepsize
            Redim Preserve outarray(1 To Ubound(outarray)+1)
            outarray(Ubound(outarray))=ShortSpline(p(),t)
            outarray(Ubound(outarray)).col=colour
        Next t
    Next n
End Sub

Sub DrawCurve(a() As thing,ydisp As Integer=0)
    Pset(a(Lbound(a)).x,a(Lbound(a)).y+ydisp),a(Lbound(a)).col
    For z As Integer=Lbound(a)+1 To Ubound(a)
        Line-(a(z).x,a(z).y+ydisp),a(z).col
    Next z
End Sub

#macro show()
GetSpline(b(),C(),0,500)
lineto(b(2).x,b(2).y,b(1).x,b(1).y,2,ox,oy)
b(0).x=ox:b(0).y=oy
#define ub2 Ubound(b)-2
#define ub1 Ubound(b)-1
lineto(b(ub2).x,b(ub2).y,b(ub1).x,b(ub1).y,2,ox,oy)
b(Ubound(b)).x=ox:b(Ubound(b)).y=oy
Cls
Line(border,border)-(xres-border+1,yres-border+1),4,b
Paint(0,0),3,4
Paint(border+1,border+1),15,4
Draw String (10,10), "fps= " & fps
Draw String (10,30), "Slackness = " &int(map(200,2000,gf,1,9))
drawCurVe(C())
acc=0
thing.BallCollisions(b())

For n As Integer=1 To Ubound(b)-1
    b(n).motion(b(),n)
    b(n).friction()
    b(n).boundaries()
    b(n).draw()
    If n>1 And n<Ubound(b)-1 Then
        b(n).tie(b(),n)
        b(n).fx=speed
        b(n).fy=speed
    End If
    acc+=Abs(b(n).dx)+Abs(b(n).dy)
Next n
speed=map(0,30,acc,0,.2)
Flip
#endmacro
Sub drawline(x As Integer,y As Integer,angle As Single,length As Double,Byref x2 As Single=0,Byref y2 As Single=0,flag As Integer=1)
    angle=angle*Atn(1)/45
    x2=x+length*Cos(angle)
    y2=y-length*Sin(angle)
End Sub
#define irange(f,l) Int(Rnd*((l+1)-(f))+(f))
'======= RUN =========
Randomize 3
Redim As  Thing b(0):Thing.initialize(b())
Redim As Thing C()
Dim As Single acc,speed,ox,oy,k=2
Dim As Integer fps
Dim As String i
Dim As Single angle,b1,b2,range=190,pi=4*Atn(1)
Do
    angle=angle+(1/60)
    If angle>=2*pi Then angle=0
    i=Inkey
    If Rnd >.99 Then k=irange(1,9)
    gf=map(1,9,k,200,2000)
    drawline(xres/2,.1*yres,270+Sin(angle)*range/(4*Atn(1)),380,b1,b2)
    b(Ubound(b)-1).x=b1
    b(Ubound(b)-1).y=b2
    show()
    Sleep regulate(60,fps),1
Loop Until i=Chr(27)
Sleep
 
Note:
I notice that the colours are very slightly opaque in Win 11.
I can see the background console through the fb screen 19, although very sightly.
Post Reply