First, the main lib:
physics.bi:
Code: Select all
#Include "fbgfx.bi"
#Include Once "ext/math/line2.bi"
#Include Once "ext/math/projections.bi"
#Include Once "ext/math/vector2.bi"
Using fb
Type vector2d As ext.math.fbext_Vector2( ((Single)) )
Type line2d As ext.math.fbext_Line2( ((Single)) )
Type circle2d
posi As vector2d
vel As vector2d
radius As Integer
ccolor As Integer
maxvelocity As Integer
Declare Function bounce (ByRef ball As circle2d) As Integer
Declare Function bounce Overload (ByRef wall As line2d) As Integer
Declare Sub halt()
Declare Sub display(ByVal xx_ As single, ByVal yy_ As Single)
Declare Constructor(x As Single, y As Single, rad As Integer, colr As Integer, maxvel As Integer)
Declare Constructor()
End Type
Declare Function lineNormal(ByVal l As line2d, ByVal num As Integer) As vector2d
function circle2d.bounce (byref wall as line2d) as integer
dim as vector2d impulse,impact,xpoint,normal,normal1,normal2,blah1,blah2
xpoint = ext.math.ClampProjectedPoint(wall, this.posi)
dim as single d
d = this.posi.distance(xpoint)
if d < this.radius Then
normal1 = lineNormal(wall, 1)
normal2 = lineNormal(wall, 2)
blah1 = xpoint + normal1
blah2 = xpoint + normal2
if this.posi.distance(blah1) < this.posi.distance(blah2) then normal = normal1
if this.posi.distance(blah1) > this.posi.distance(blah2) then normal = normal2
impact.x = -this.vel.x - this.vel.x
impact.y = -this.vel.y - this.vel.y
impulse = normal
dim as single comp, leng, s
comp = impact.dot(impulse)
leng = impulse.magnitude()
s = (comp)/leng
impulse = impulse * s
this.vel = this.vel + impulse
this.posi = this.posi + normal * (this.radius - d)
return 1
end if
return 0
end function
function circle2d.bounce(byref ball as circle2d) As Integer
if this.posi.distance(ball.posi) <= this.radius + ball.radius then
dim as vector2d impact, impulse
dim impactspeed as double
impact = this.vel - ball.vel
impulse = this.posi - ball.posi
impulse.normalize
impactspeed = impulse.dot(impact)
impulse = impulse * impactspeed
this.vel = this.vel + -(impulse)
ball.vel = ball.vel + impulse
return 1
end if
return 0
end Function
Sub circle2d.halt()
this.vel.x = 0
this.vel.y = 0
End Sub
Sub circle2d.display(ByVal xx_ As single, ByVal yy_ As Single)
Circle(xx_, yy_), this.radius, this.ccolor,,,,f
End Sub
Constructor circle2d(x_ As Single, y_ As Single, rad_ As Integer, colr_ As Integer, maxvel_ As Integer)
this.posi.x = x_
this.posi.y = y_
this.radius = rad_
this.ccolor = colr_
this.maxvelocity = maxvel_
End Constructor
Constructor circle2d()
this.posi.x = 0
this.posi.y = 0
this.radius = 0
this.ccolor = 0
this.maxvelocity = 0
End Constructor
Function lineNormal(ByVal l As line2d, ByVal num As Integer) As vector2d
Dim As vector2d normal1, normal2
normal1.x = -(l.b.y-l.a.y)
normal1.y = (l.b.x-l.a.x)
normal2.x = (l.b.y-l.a.y)
normal2.y = -(l.b.x-l.a.x)
normal1.normalize
normal2.normalize
If num = 1 Then Return normal1
If num = 2 Then Return normal2
End Function
A simple program to test angled line collisions:
Controls:
Arrow keys move the blue circle,
Insert stops the blue circle,
space for new random line
test.bas:
Code: Select all
#Include "fbgfx.bi"
#Include "physics.bi"
Using fb
Const accel = .5
Const friction = .02
Dim ball(1 To 6) As circle2d
Dim As vector2d screenmax, blahx, blahy
Dim As line2d l, wall(1 To 4)
Dim As Integer i, j, x, y
Screen 19
Screeninfo x, y
Randomize Timer, 3
screenmax.x = x:screenmax.y = y
With wall(1)
.a.x = 0
.a.y = 0
.b.x = screenmax.x
.b.y = 0
End With
With wall(2)
.a.x = screenmax.x
.a.y = 0
.b.x = screenmax.x
.b.y = screenmax.y
End With
With wall(3)
.a.x = screenmax.x
.a.y = screenmax.y
.b.x = 0
.b.y = screenmax.y
End With
With wall(4)
.a.x = 0
.a.y = screenmax.y
.b.x = 0
.b.y = 0
End With
For i = 1 To 6
With ball(i)
.posi.x = CInt(Rnd * screenmax.x)
.posi.y = CInt(Rnd * screenmax.y)
.radius = CInt(Rnd * 20) + 10
.ccolor = i
End With
Next
Do
With l
.a.x = Rnd * screenmax.x
.a.y = Rnd * screenmax.y
.b.x = Rnd * screenmax.x
.b.y = Rnd * screenmax.y
End With
Do
Screenlock
Cls
With l
Line (.a.x, .a.y)-(.b.x, .b.y), 15
End With
For i = 1 To 6
ball(i).display(ball(i).posi.x, ball(i).posi.y)
Next i
ScreenSync
Screenunlock
If Multikey(SC_UP) Then ball(1).vel.y = ball(1).vel.y - accel
If Multikey(SC_RIGHT) Then ball(1).vel.x = ball(1).vel.x + accel
If Multikey(SC_DOWN) Then ball(1).vel.y = ball(1).vel.y + accel
If Multikey(SC_LEFT) Then ball(1).vel.x = ball(1).vel.x - accel
If Multikey(SC_INSERT) Then ball(1).halt
For i = 1 To 6
ball(i).posi.x += ball(i).vel.x
ball(i).posi.y += ball(i).vel.y
ball(i).vel.x *= (1-friction)
ball(i).vel.y *= (1-friction)
ball(i).bounce(l)
For j = 1 To 4
ball(i).bounce(wall(j))
Next
For j = 1 To 6
ball(i).bounce(ball(j))
Next j
Next
Loop Until Multikey(SC_ESCAPE) Or Multikey(SC_SPACE)
Loop Until Multikey(SC_ESCAPE)
This libby does not deal with mass, so the ball to ball collision is kinda weird. I would have added it, but I wanted to keep the code as simple as possible. If you need mass, it should be simple enough to add.
Edit:
The lib is mostly finished. The code has been updated. If there are any bugs, just post them here.
Edit:
Finished. Code updated.