Simple Circle/Line Physics Library

User projects written in or related to FreeBASIC.
rolliebollocks
Posts: 2655
Joined: Aug 28, 2008 10:54
Location: new york

Postby rolliebollocks » Feb 11, 2011 19:33

Also, my method works on any polygon... Not just triangles...
Prime Productions
Posts: 147
Joined: May 24, 2009 23:13
Location: Texas, United States, Planet Earth
Contact:

Postby Prime Productions » Feb 11, 2011 21:26

rolliebollocks wrote:Well, a triangle can have multiple heights for the same perimeter. That's why I figured it wasn't possible. I didn't know about Heron's formula. That's a good one.

Heron's formula would not be viable in a loop... Way too many Sqare Roots... Listen to me, Prime Production's method is not a viable solution...


Lol, Yes Rollie, I was just giving him an answer that would work (after all, he asked if it was mathematically possible), but yeah, I agree, it would be long-term better to use some sort of vertice sorting algo, like the one you described.
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Feb 14, 2011 16:04

I've implemented the algo in an article from the QB Express. The only problem is finding out which normal to use. Because if you use the wrong one, the wall turns into a powerup sort of thing, and instead of bouncing, it throws it faster through the wall. Now, I'll probably have figured it out by the time I get back on here, but the question still deserves an answer. I'll be updating the first post's code to what I have done w/o the wall collision detect stuff that I'm working on shortly
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Feb 15, 2011 16:19

What I need is not a collision detect algo now, I already understand that. What I need is a bounce algorithm that I can understand. If I don't understand it, I can't use it. I'm currently working on the vector side of the problem, but can't seem to make the ball bounce off both sides of the line. It bounces off one side, but the other side sends it shooting through the line like in this example.


test.bas (use the files from the first post for 'point2d.bi' and 'circle.bi')

Code: Select all

#Include "fbgfx.bi"
#Include "point2d.bi"
#Include "circle.bi"

Using fb

Type linetype
   coord1 As Point2D
   coord2 As Point2D
   normal As point2d
End Type

Declare Function dist(ByVal a as point2d, ByVal b as point2d) as Single
Declare sub normalize (ByRef p as point2d)
Declare function dot(ByVal a as point2d, ByVal b as point2d) as Single
Declare Function closest_point_on_line(ByVal linee As linetype, ByVal p as point2d) as point2d
Declare function collide_on_line ( l as linetype, ball as circletype) as Integer
Declare sub get_2dnormal (seg as linetype, s as integer)

Const accel = .5

Dim ball As circletype = circletype(100, 40, 10, 4, USER)
Dim p As point2d
Dim lin(1 To 1) As linetype
Dim i As Integer

With lin(1)
   .coord1.x = 0
   .coord1.y = 0
   .coord2.x = 500
   .coord2.y = 500
End With
get_2dnormal(lin(1), 1)

Screen 19

Do
   ScreenLock
   Cls
   For i = 1 To UBound(lin)
      With lin(i)
         Line (.coord1.x, .coord1.y)-(.coord2.x, .coord2.y), 15
      End With
   Next
   ball.display
   ScreenSync
   screenunlock
   If MultiKey(SC_UP) Then ball.yvelocity = ball.yvelocity - accel
   If MultiKey(SC_RIGHT) Then ball.xvelocity = ball.xvelocity + accel
   If MultiKey(SC_DOWN) Then ball.yvelocity = ball.yvelocity + accel
   If MultiKey(SC_LEFT) Then ball.xvelocity = ball.xvelocity - accel
   If MultiKey(SC_INSERT) Then ball.halt
   ball.updatevalues
   For i = 1 To UBound(lin)
      collide_on_line(lin(i), ball)
   Next
   Sleep 25
Loop Until MultiKey(SC_ESCAPE)

Sleep

private sub normalize (ByRef p as point2d)
    dim leng as single
    leng = sqr(p.x * p.x + p.y * p.y )
    p.x = p.x / leng
    p.y = p.y / leng
end Sub

private function get_magnitude ( v as point2d ) as single
   
    return sqr( ( v.x * v.x ) + ( v.y * v.y ) )
   
end Function

function dist(ByVal a as point2d, ByVal b as point2d) as Single
   Dim as single dx, dy

   dx = a.x - b.x
   dy = a.y - b.y
   return SQR((dx*dx)+(dy*dy))
end function

function dot(ByVal a as point2d, ByVal b as point2d) as Single
   return (a.x*b.x + a.y*b.y )
end function

function closest_point_on_line(ByVal linee As linetype, ByVal p as point2d) as point2d

    dim d as single
    dim t as single

    dim as point2d p1, p2, preturn


   p1.x = p.x - linee.coord1.x
   p1.y = p.y - linee.coord1.y


   p2.x = linee.coord2.x - linee.coord1.x
   p2.y = linee.coord2.y - linee.coord1.y

   normalize p2

   'calculate distance between 2 points a and b
   d = dist(linee.coord1, linee.coord2)
'       p
'      /|
'     / |
'    /  |
'   /   |
' a-------------b
'  |----| -> dist "t"
   'get the distance of the projected vector from va by dropping a
   'perpendicular from v1 to a point in v2
   t = dot(p2, p1)


   'if our projected distance is less than or equal to 0 then
   If t<=0 Then Return linee.coord1

   'if it is >= to the magnitude of the line segment v2 - v1 then
   'it's closest to b.
   If t>=d Then Return linee.coord2

   'otherwise the point is n betweent the 2 endpoints so we
   'create a vector with length "t" and ad it to our original point va
   'to trace a line.
   preturn.x = linee.coord1.x + (p2.x * t)
   preturn.y = linee.coord1.y + (p2.y * t)
   return preturn

end Function

function collide_on_line ( l as linetype, ball as circletype) as integer

    dim as point2d  a,b,impulse,impact,xpoint,pnt
   
    a.x = l.coord1.x
    a.y = l.coord1.y
    b.x = l.coord2.x
    b.y = l.coord2.y
    pnt.x = ball.x
    pnt.y = ball.y
   
    xpoint = closest_point_on_line(l, pnt)
   
    dim as single d
   
    d = dist(pnt,xpoint)
   
    if d<=ball.radius Then
       
       
        a.x = ball.xvelocity
        a.y = ball.yvelocity
       
        b.x = -a.x
        b.y = -a.y
       
       
        impact.x = b.x - a.x
        impact.y = b.y - a.y
       
        impulse.x = l.normal.x
        impulse.y = l.normal.y
       
        'new = (impact dot impulse)/|impulse| * impulse
        dim as single comp, leng,s
        comp = dot(impact, impulse)
        leng = get_magnitude(impulse)
        s = (comp)/leng
        impulse.x = impulse.x * s
        impulse.y = impulse.y * s
       
       
       
        ball.xvelocity = ball.xvelocity + impulse.x
        ball.yvelocity = ball.yvelocity + impulse.y
       
        'snap it back
        ball.x = ball.x + l.normal.x * (ball.radius - d )
        ball.y = ball.y + l.normal.y * (ball.radius - d )           
       
        return 1
                   
    end if
   
    return 0
end Function

private sub get_2dnormal (seg as linetype, s as integer)
   
    if s then
    seg.normal.x = -(seg.coord2.y-seg.coord1.y)  'negate to get the other normal
    seg.normal.y = (seg.coord2.x-seg.coord1.x) 'erase negatio here if you want the other
                                    'normal
    else
    seg.normal.x = (seg.coord2.y-seg.coord1.y)  'negate to get the other normal
    seg.normal.y = -(seg.coord2.x-seg.coord1.x) 'erase negatio here if you want the other
                                    'normal
    end if
    normalize (seg.normal)
end Sub
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Feb 16, 2011 18:50

I got it all figured out. Here's the revised code for test.bas:

Code: Select all

#Include "fbgfx.bi"
#Include "point2d.bi"
#Include "Bubble World.bi"

Using fb

Type linetype
   coord1 As Point2D
   coord2 As Point2D
   normal As point2d
End Type

Declare Function dist(ByVal a as point2d, ByVal b as point2d) as Single
Declare sub normalize (ByRef p as point2d)
Declare function dot(ByVal a as point2d, ByVal b as point2d) as Single
Declare Function closest_point_on_line(ByVal linee As linetype, ByVal p as point2d) as point2d
Declare function collide_on_line ( l as linetype, ball as circletype) as Integer
Declare sub get_2dnormal (seg as linetype, s as integer)

Const accel = .5

Dim ball As circletype = circletype(100, 40, 25, 4, USER)
Dim As point2d p, screenmax
Dim lin As linetype
Dim As Integer i, x, y

Screen 19
ScreenInfo x, y
Randomize Timer, 3

screenmax.x = x:screenmax.y = y

With lin
   .coord1.x = Rnd * screenmax.x
   .coord1.y = Rnd * screenmax.y
   .coord2.x = Rnd * screenmax.x
   .coord2.y = Rnd * screenmax.y
End With

Do
   ScreenLock
   Line (0, 0)-(screenmax.x, screenmax.y), 0, bf
   With lin
      Line (.coord1.x, .coord1.y)-(.coord2.x, .coord2.y), 15
   End With
   ball.display
   ScreenSync
   screenunlock
   If MultiKey(SC_UP) Then ball.yvelocity = ball.yvelocity - accel
   If MultiKey(SC_RIGHT) Then ball.xvelocity = ball.xvelocity + accel
   If MultiKey(SC_DOWN) Then ball.yvelocity = ball.yvelocity + accel
   If MultiKey(SC_LEFT) Then ball.xvelocity = ball.xvelocity - accel
   If MultiKey(SC_INSERT) Then ball.halt
   ball.updatevalues
   collide_on_line(lin, ball)
   Sleep 25
Loop Until MultiKey(SC_ESCAPE)

Sleep

private sub normalize (ByRef p as point2d)
    dim leng as single
    leng = sqr(p.x * p.x + p.y * p.y )
    p.x = p.x / leng
    p.y = p.y / leng
end Sub

private function get_magnitude ( v as point2d ) as single
   
    return sqr( ( v.x * v.x ) + ( v.y * v.y ) )
   
end Function

function dist(ByVal a as point2d, ByVal b as point2d) as Single
   Dim as single dx, dy

   dx = a.x - b.x
   dy = a.y - b.y
   return SQR((dx*dx)+(dy*dy))
end function

function dot(ByVal a as point2d, ByVal b as point2d) as Single
   return (a.x*b.x + a.y*b.y )
end function

function closest_point_on_line(ByVal linee As linetype, ByVal p as point2d) as point2d

    dim d as single
    dim t as single

    dim as point2d p1, p2, preturn


   p1.x = p.x - linee.coord1.x
   p1.y = p.y - linee.coord1.y


   p2.x = linee.coord2.x - linee.coord1.x
   p2.y = linee.coord2.y - linee.coord1.y

   normalize p2

   'calculate distance between 2 points a and b
   d = dist(linee.coord1, linee.coord2)
'       p
'      /|
'     / |
'    /  |
'   /   |
' a-------------b
'  |----| -> dist "t"
   'get the distance of the projected vector from va by dropping a
   'perpendicular from v1 to a point in v2
   t = dot(p2, p1)


   'if our projected distance is less than or equal to 0 then
   If t<=0 Then Return linee.coord1

   'if it is >= to the magnitude of the line segment v2 - v1 then
   'it's closest to b.
   If t>=d Then Return linee.coord2

   'otherwise the point is n betweent the 2 endpoints so we
   'create a vector with length "t" and ad it to our original point va
   'to trace a line.
   preturn.x = linee.coord1.x + (p2.x * t)
   preturn.y = linee.coord1.y + (p2.y * t)
   return preturn

end Function

function collide_on_line ( l as linetype, ball as circletype) as integer

    dim as point2d  a,b,impulse,impact,xpoint,pnt,normal,normal0,normal1,blah0,blah1
   
    a.x = l.coord1.x
    a.y = l.coord1.y
    b.x = l.coord2.x
    b.y = l.coord2.y
    pnt.x = ball.x
    pnt.y = ball.y
   
    xpoint = closest_point_on_line(l, pnt)
   
    dim as single d
   
    d = dist(pnt,xpoint)
   
    if d<=ball.radius Then
       get_2dnormal(l, 0)
       normal0 = l.normal
       get_2dnormal(l, 1)
       normal1 = l.normal
       blah0.x = xpoint.x + normal0.x
       blah0.y = xpoint.y + normal0.y
       blah1.x = xpoint.x + normal1.x
       blah1.y = xpoint.y + normal1.y
       If dist(blah0, pnt) < dist(blah1, pnt) Then l.normal = normal0
       If dist(blah0, pnt) > dist(blah1, pnt) Then l.normal = normal1   
       
        a.x = ball.xvelocity
        a.y = ball.yvelocity
       
        b.x = -a.x
        b.y = -a.y
       
       
        impact.x = b.x - a.x
        impact.y = b.y - a.y
       
        impulse.x = l.normal.x
        impulse.y = l.normal.y
       
        'new = (impact dot impulse)/|impulse| * impulse
        dim as single comp, leng,s
        comp = dot(impact, impulse)
        leng = get_magnitude(impulse)
        s = (comp)/leng
        impulse.x = impulse.x * s
        impulse.y = impulse.y * s
       
       
       
        ball.xvelocity = ball.xvelocity + impulse.x
        ball.yvelocity = ball.yvelocity + impulse.y
       
        'snap it back
        ball.x = ball.x + l.normal.x * (ball.radius - d )
        ball.y = ball.y + l.normal.y * (ball.radius - d )           
       
        return 1
                   
    end if
   
    return 0
end Function

private sub get_2dnormal (seg as linetype, s as integer)
   
    if s then
    seg.normal.x = -(seg.coord2.y-seg.coord1.y)  'negate to get the other normal
    seg.normal.y = (seg.coord2.x-seg.coord1.x) 'erase negatio here if you want the other
                                    'normal
    else
    seg.normal.x = (seg.coord2.y-seg.coord1.y)  'negate to get the other normal
    seg.normal.y = -(seg.coord2.x-seg.coord1.x) 'erase negatio here if you want the other
                                    'normal
    end if
    normalize (seg.normal)
end Sub


Compare with the code of the last post to figure out what happened.
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Mar 11, 2011 17:43

I decided to bump this because I'm still working on it, Its just that I ran into the Extended FreeBASIC Library and all of its vector funtions, which are very helpful. I've decided to port my code to it and to move on to the next stage which is circle-circle collision detection. If any of you have any ideas or tuts that might help they would be appreciated.
h4tt3n
Posts: 694
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Mar 11, 2011 19:16

qbworker wrote:I've decided to port my code to it and to move on to the next stage which is circle-circle collision detection. If any of you have any ideas or tuts that might help they would be appreciated.


The sph.zip in the thread linked to below contains a very effective circle-circle collision detection. I've commented the code, so you should be able to find it.

cheers Mike

http://www.freebasic.net/forum/viewtopi ... hlight=sph
rolliebollocks
Posts: 2655
Joined: Aug 28, 2008 10:54
Location: new york

Postby rolliebollocks » Mar 11, 2011 19:48

Circle to Circle collision is just the distance formula again. If the distance from the center of one circle to another is less than the two radii summed you have a collision.
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Mar 11, 2011 20:06

The idea is I want them to bounce, not just stop when they hit each other.
rolliebollocks
Posts: 2655
Joined: Aug 28, 2008 10:54
Location: new york

Postby rolliebollocks » Mar 11, 2011 20:44

I found this:

'' physics engine that simulates bouncing balls using vector math
'' This code is based on the 2D elastic collision tutorial found at:
'' http://www.geocities.com/vobarian/2dcollisions/

Code: Select all

Randomize Timer

Type Vector_2D
  As Double X, Y
End Type

Type Ball
  As Uinteger Col
  As Vector_2D Pos, Vel
  As Double Mass, Density, Radius
End Type

Declare Function Vector_Sub (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
Declare Function Vector_Add (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
Declare Function Vector_Magnitude (Byref vector As vector_2d) As Double
Declare Function Vector_Normalize (Byref vector As vector_2d) As Vector_2D
Declare Function Dot_Product (Byref v1 As vector_2d, Byref v2 As vector_2d) As Double
Declare Function Vector_Scalar_Mul (Byref s1 As Double, Byref v1 As vector_2d) As vector_2D
Declare Sub DoElasticCollision (Byref Ball_1 As Ball, Byref Ball_2 As Ball)

Const Pi = 4*Atn(1)
Dim As Vector_2D Dist
Dim As Double Distance, Dist_Min, Momentum, momentum_init, velocity, Ekin, Ekin_init
Dim As String X_Clicked = Chr(255)+"k"
Dim As Integer Screen_x, Screen_Y, i, i2
Dim Ball(1 to 5) As Ball

''  screen settings
screen_x = 600          ''  screen width
screen_y = 400          ''  screen height
screenres screen_x, screen_y, 16

randomize timer

With Ball(1)
  .Mass = 16
  .Density = 0.001
  .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
  .Pos.X = 100
  .Pos.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(255, 32, 32)
End With

With Ball(2)
  .Mass = 32
  .Density = 0.001
  .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
  .Pos.X = 200
  .Pos.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(255, 32, 255)
End With

With Ball(3)
  .Mass = 64
  .Density = 0.001
  .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
  .Pos.X = 300
  .Pos.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(32, 32, 255)
End With

With Ball(4)
  .Mass = 128
  .Density = 0.001
  .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
  .Pos.X = 400
  .Pos.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(32, 255, 32)
End With

With Ball(5)
  .Mass = 256
  .Density = 0.001
  .Radius = ((.Mass/.Density)/((4/3)*pi))^(1/3)
  .Pos.X = 500
  .Pos.Y = (Screen_Y\2)
  .Vel.X = rnd-rnd
  .Vel.Y = rnd-rnd
  .Col = RGB(255, 255, 32)
End With

For i = Lbound(Ball) to Ubound(Ball)
  Velocity = Vector_Magnitude(Ball(i).vel)
  momentum_init += Ball(i).mass*Velocity
  Ekin_init += 0.5*Ball(i).Mass*Velocity^2
Next

Do
 
  Screenlock
   
    Cls
   
    For i = Lbound(Ball) to Ubound(Ball)
     
      With Ball(i)
       
        ''ball - wall collision detection
        If .Pos.X > screen_x-.Radius-1 Then
          .Pos.X = screen_x-.Radius-1
          .Vel.X = -.Vel.X
        Elseif .Pos.X < .Radius Then
          .Pos.X = .Radius
          .Vel.X = -.Vel.X
        End If
        If .Pos.Y > screen_y-.Radius-1 Then
          .Pos.Y = screen_y-.Radius-1
          .Vel.Y = -.Vel.Y
        Elseif .Pos.Y < .Radius Then
          .Pos.Y = .Radius
          .Vel.Y = -.Vel.Y
        End If
       
        ''  ball - ball collision detection
        For i2 = i+1 to Ubound(Ball)
         
          Dist = Vector_Sub(.Pos, Ball(i2).Pos)
          Distance = Vector_Magnitude(Dist)
          Dist_Min = .Radius+Ball(i2).Radius
         
          ''  elastic bounce
          If Distance < Dist_Min Then
           
            DoElasticCollision(Ball(i), Ball(i2))
           
          End If
         
        Next
       
        .Pos = Vector_Add(.Pos, .Vel)
        Circle (.Pos.X, .Pos.Y), .Radius, .col,,,1,f
       
        Momentum = 0
        Ekin = 0
       
        For i2 = Lbound(Ball) to Ubound(Ball)
          Velocity = Vector_Magnitude(Ball(i2).vel)
          momentum += Ball(i2).mass*Velocity
          Ekin += 0.5*Ball(i2).Mass*Velocity^2
        Next
       
        Locate 1, 1: Print Momentum_Init
        Locate 2, 1: Print Momentum
        Locate 4, 1: Print Ekin_Init
        Locate 5, 1: Print Ekin
       
      End With
     
    Next
   
  screenunlock
 
  sleep 2, 1
 
Loop Until Multikey(1) Or Inkey = X_Clicked

End


'-------------------------------------------------------------------------------


Function Vector_Sub (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
  Dim As Vector_2D v
  v.x = v1.x-v2.x
  v.y = v1.y-v2.y
  Return v
End Function

Function Vector_Add (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
  Dim As Vector_2D v
  v.x = v1.x+v2.x
  v.y = v1.y+v2.y
  Return v
End Function

Function Vector_Magnitude (Byref vector As vector_2d) As Double
  Dim as Double x, y, r
  x = Vector.x^2
  y = Vector.y^2
  r = Sqr(x+y)
  Return r
End Function

Function Vector_Normalize (Byref vector As vector_2d) As Vector_2D
  Dim As Vector_2D v
  Dim as Double r
  r = Vector_Magnitude(Vector)
  v.x = vector.x/r
  v.y = vector.y/r
  Return v
End Function

Function Dot_Product (Byref v1 As vector_2d, Byref v2 As vector_2d) As Double
  Return v1.x*v2.x+v1.y*v2.y
End Function

Function Vector_Scalar_Mul (Byref s1 As Double, Byref v1 As vector_2d) As vector_2D
  Dim as Vector_2d v
  v.x = v1.x*s1
  v.y = v1.y*s1
  Return v
End Function

Sub DoElasticCollision (Byref Ball_1 As Ball, Byref Ball_2 As Ball)
 
  Dim As Vector_2D v_n, v_un, v_ut, v_v1tPrime, v_v2tPrime, v_v1nPrime, v_v2nPrime
  Dim As Double v1n, v1t, v2n, v2t, v1tPrime, v2tPrime, v1nPrime, v2nPrime
 
  ''  vector normal (perpendicular) to collision surface
  v_n = Vector_Sub(Ball_2.Pos, Ball_1.Pos)
 
  ''  normalize vector - make it a unit vector
  v_un = Vector_Normalize(v_n)
 
  ''  unit tangent vector (parallel to collision surface)
  v_ut.x = -v_un.y
  v_ut.y = v_un.x
 
  ''  old normal and tangential velocity vectors
  v1n = Dot_Product(v_un, Ball_1.vel)
  v1t = Dot_Product(v_ut, Ball_1.vel)
  v2n = Dot_Product(v_un, Ball_2.vel)
  v2t = Dot_Product(v_ut, Ball_2.vel)
 
  ''  new tangential velocity (uncanged)
  v1tPrime = v1t
  v2tPrime = v2t
 
  ''  new normal velocity
  v1nPrime = (v1n * (ball_1.mass - ball_2.mass) + 2 * ball_2.mass * v2n) / (ball_1.mass + ball_2.mass)
  v2nPrime = (v2n * (ball_2.mass - ball_1.mass) + 2 * ball_1.mass * v1n) / (ball_1.mass + ball_2.mass)
 
  ''  new normal and tangential velocity vectors
  v_v1nPrime = Vector_scalar_mul(v1nPrime, v_un)
  v_v1tPrime = Vector_scalar_mul(v1tPrime, v_ut)
  v_v2nPrime = Vector_scalar_mul(v2nPrime, v_un)
  v_v2tPrime = Vector_scalar_mul(v2tPrime, v_ut)
 
  ''  new ball velocities
  Ball_1.Vel = Vector_Add(v_v1nPrime, v_v1tPrime)
  Ball_2.Vel = Vector_Add(v_v2nPrime, v_v2tPrime)
 
End Sub
 
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Mar 14, 2011 17:39

Thanks! I'll just turn this into a massless proggie and I'll have what I'm looking for. I just wanted not a full physics simulation just something easy to build upon to make different stuff that needs this sort of thing.
dodicat
Posts: 6754
Joined: Jan 10, 2006 20:30
Location: Scotland

Postby dodicat » Mar 14, 2011 21:59

qbworker wrote:Thanks! I'll just turn this into a massless proggie and I'll have what I'm looking for. I just wanted not a full physics simulation just something easy to build upon to make different stuff that needs this sort of thing.

Hi qbworker
I'm messing about with 2d collisions, and trying out simple vector stuff.
I've kinda simplified this code a bit, pretty nearly mathematically correct, but needs touched up.
The balls bounce off each other, off the sides and off some lines, if they get through the bottom gap, they come back on screen at the top.
You can nudge them with the mouse, and use the up/down keys to adjust for a bit of gravity.
The basic motion of the balls is Newton's second equation
S=START+U*T + .5*G*T^2
where T increments by 1 each frame, and on every impact T becomes zero, START becomes the position at impact, in other words a new trajectory starts at each collision.
The up/down keys are not too sensitive, I'll have to fix that.
It's all a bit silly really.

Code: Select all


'BALLS

Type vector3d
    As Single x,y,z
   
End Type

Type plane
    As vector3d v1,v2,v3
End Type

Type line3d
    As vector3d v1,v2
    End Type
#define vct type<vector3d>
#define ln type<line3d>
#define pl type<plane>
#define cross <>
#define dot *
operator + (v1 As vector3d,v2 As vector3d) As vector3d
Return Type<vector3d>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)
End operator

operator -(v1 As vector3d,v2 As vector3d) As vector3d
Return Type<vector3d>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z)
End operator
'______________ SCALARs and  products_______________________
operator * Overload (v1 As vector3d,v2 As vector3d) As Single
Return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
End operator
operator * Overload (f As Single,v1 As vector3d) As vector3d
Return Type<vector3d>(f*v1.x,f*v1.y,f*v1.z)
End operator
operator * Overload (v1 As vector3d,f As Single) As vector3d
Return Type<vector3d>(f*v1.x,f*v1.y,f*v1.z)
End operator
'________________END SCALERS _________________

operator <>(v1 As vector3d,v2 As vector3d) As vector3d 'cross
Return Type<vector3d>(v1.y*v2.z-v2.y*v1.z,-(v1.x*v2.z-v2.x*v1.z),v1.x*v2.y-v2.x*v1.y)
End operator

declare Function length(v As vector3d) As Single
declare Function normalize(v As vector3d) As vector3d
declare Function r(first As Single, last As Single) As Single
declare Function isleft(L As line3d,p As vector3d) As Double 
declare Function planedistance(v3 As PLANE,p As vector3d) As Single
declare sub drawline(l as line3d)
declare Function drop_perpendicular(p As vector3d,L2 As line3d) As vector3d
declare Sub balls(n As Integer,rad As Single)
declare Function segmentdistance(L() As line3d,p As vector3d,Byref p2 As vector3d=vct(0,0,0)) As Single
dim shared gravity as single
Dim Shared As Integer xres,yres
Screen 19,32
Screeninfo xres,yres
Dim As vector3d vl1,vl2,vl3
vL1=vct(0,yres/3,0):vL2=vct(xres/2.5,yres,0):vl3=vct(0,yres/3,100)
Dim Shared As line3d line1,line2,lineseg(1)
Dim Shared As plane plane1,plane2
line1=ln(vL1,vL2)
plane1=pl(vl1,vl2,vl3)

vL1=vct(xres,yres/3,0):vL2=vct(xres-xres/2.5,yres,0):vl3=vct(xres,yres/3,100)
line2=ln(vL1,vL2)
plane2=pl(vl1,vl2,vl3)
vl1=vct(.3*xres,.2*yres,0):vl2=vct(.6*xres,.4*yres,0)
lineseg(1)=ln(vl1,vl2)
dim as string i
Do
    i=inkey
    if i=chr(255)&"P" then gravity=gravity+.005
    if i=chr(255)&"H" then gravity=gravity-.005:if gravity<0 then gravity=0
    Screenlock
    Cls
    locate 2,2
    print "Gravity = "; gravity
    drawline(line1)
    drawline(line2)
    drawline(lineseg(1))
    balls(5,50)
    Screenunlock
    Sleep 1,1
    Loop Until Inkey=Chr(27)
Sleep

Function segmentdistance(L() As line3d,p As vector3d,Byref p2 As vector3d=vct(0,0,0)) As Single
    Dim As vector3d near,far,pp
    Dim n As Integer=L(0).v1.z
    If length(p-L(n).v1) > length(p-L(n).v2) Then
        far=L(n).v1
        near=L(n).v2
    Else
        far=L(n).v2
        near=L(n).v1
    End If
    pp=drop_perpendicular(p,L(n))
    If length(far-pp)<length(L(n).v1-L(n).v2) Then
        p2=pp
        Return length(p-pp)
    Else
        p2=near
        Return length(p-near)
    End If   
End Function
Function length(v As vector3d) As Single
    Return Sqr(v.x*v.x+v.y*v.y+v.z*v.z)
End Function

Function normalize(v As vector3d) As vector3d
    Dim n As Single=length(v)
    If n<>0 Then
    Return Type<vector3d>(v.x/n,v.y/n,v.z/n)
    End If
    End Function

Function r(first As Single, last As Single) As Single
    Function = Rnd * (last - first) + first
End Function

Function isleft(L As line3d,p As vector3d) As Double 
    Return -Sgn(  (L.v1.x-L.v2.x)*(p.y-L.v2.y) - (p.x-L.v2.x)*(L.v1.y-L.v2.y))
End Function

Function planedistance(v3 As PLANE,p As vector3d) As Single
    Dim As vector3d pv=p-v3.v1
    Dim As vector3d vp1,vp2,unitcross
    vp1=v3.v1-v3.v2 
    vp2=v3.v2-v3.v3
    unitcross=normalize(vp1 cross vp2)
    Return unitcross dot pv
End Function
 sub drawline(l as line3d)
    line(l.v1.x,l.v1.y)-(l.v2.x,l.v2.y),rgb(200,200,200)
end sub
Function drop_perpendicular(p As vector3d,L2 As line3d) As vector3d
     Dim As Double M1,M2,C1,C2
    Dim As vector3d pt
    M2=(L2.v2.y-L2.v1.y)/(L2.v2.x-L2.v1.x)
    M1=-1/M2
    C1=p.y-M1*p.x
    C2=(L2.v1.y*L2.v2.x-L2.v1.x*L2.v2.y)/(L2.v2.x-L2.v1.x)
    pt.x=(C2-C1)/(M1-M2)
    pt.y=(M1*C2-M2*C1)/(M1-M2)
    Return pt
End Function

Sub balls(n As Integer,rad As Single)
    Static runflag As Byte
        Static As vector3d b()
        Redim Preserve b(1 To n)
        Static As vector3d u()
        Redim Preserve u(1 To n)
        Static As Uinteger col()
        Redim Preserve col(1 To n)
        Static As Single sp()
        Redim Preserve sp(1 To n)
        Static As Single k()
        Redim Preserve k(1 To n)
        Static As vector3d startpos()
        Redim Preserve startpos(1 To n)
        Static As Single _rad()
        Redim Preserve _rad(1 To n)
        dim as vector3d impact
If runflag=0 Then
For x As Integer=1 To n
    b(x)=vct(r(0,xres),0,0)
    _rad(x)=rad
    sp(x)=1
    k(x)=0
    col(x)=Rgb(00,200,00)
    startpos(x)=vct(r(0,xres),0,0)
Next x   
    runflag=1
End If
Dim As vector3d impulse,p2,mouse
dim as integer mx,my
#macro sides()

For z2 As Integer=1 To n
    lineseg(0).v1.z=1
    if segmentdistance(lineseg(1),b(z2),impact)<_rad(z2) then
        impulse=normalize(b(z2)-impact)
        u(z2)=u(z2)+impulse
        sp(z2)=length(u(z2))
    u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
        end if
   
   
      If planedistance(plane1,b(z2))<_rad(z2) Then
    p2=drop_perpendicular(b(z2),line1)
    impulse=normalize(b(z2)-p2)
    If isleft(line1,b(z2))<0 Then
    u(z2)=u(z2)+impulse
Else
    u(z2)=u(z2)-impulse
    End If
   ' sp(z2)=length(u(z2))
   ' u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
    End If
   
    If -planedistance(plane2,b(z2))<rad Then     
    p2=drop_perpendicular(b(z2),line2)
    impulse=normalize(b(z2)-p2)
    If -isleft(line2,b(z2))<0 Then
    u(z2)=u(z2)+impulse
Else
    u(z2)=u(z2)-impulse
    End If
    'sp(z2)=length(u(z2))
    'u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
End If

    If b(z2).y>yres Then'-rad
    If (b(z2).x>line1.v2.x) And (b(z2).x<line2.v2.x)  Then
    For x As Integer=2 To 5
     If z2 Mod x  Then
         Select Case col(z2)
         Case Rgb(0,200,0)
         col(z2)=Rgb(200,0,0)
         Exit Select
     Case Rgb(200,0,0)
         col(z2)=Rgb(0,0,200)
         Exit Select
     Case Rgb(0,0,200)
         col(z2)=Rgb(200,200,0)
         Exit Select
     Case Rgb(200,200,0)
         col(z2)=Rgb(0,200,200)
         Exit Select
     Case Rgb(0,200,200)
         col(z2)=Rgb(0,200,0)
         Exit Select
         End Select
    k(z2)=0
    startpos(z2).y=0
    sp(z2)=.01
   
End If
Next x
Else
   impulse=vct(0,-1,0)
    u(z2)=u(z2)+impulse
    sp(z2)=length(u(z2))
    u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
    End If
    End If   
       
    If b(z2).x<rad Then
    u(z2)=u(z2)+vct(1,0,0)
    sp(z2)=length(u(z2))
    u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
End If
If b(z2).x>xres-rad Then
    u(z2)=u(z2)+vct(-1,0,0)
    sp(z2)=length(u(z2))
    u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
End If
If b(z2).y<rad Then
    u(z2)=u(z2)+vct(0,1,0)
    sp(z2)=length(u(z2))
    u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
End If
getmouse(mx,my)
mouse=vct(mx,my,0)
circle(mx,my),20,rgb(100,100,100),,,,f
if length(b(z2)-mouse)<_rad(z2)+20 then
    impulse=normalize(b(z2)-mouse)
    u(z2)=u(z2)+impulse
    'sp(z2)=length(u(z2))
    'u(z2)=normalize(u(z2))
    k(z2)=0
    startpos(z2)=b(z2)
    end if
Next z2


#endmacro

#macro ball_to_ball()
For xx As Integer=1 To n
    For yy As Integer=1 To n
        If xx<>yy Then
            If length(b(xx)-b(yy))<=_rad(xx)+_rad(yy) Then
                impulse=normalize(b(xx)-b(yy))
              u(xx)=u(xx)+impulse
                u(yy)=u(yy)-impulse
                sp(xx)=length(u(xx))
                sp(yy)=length(u(yy))
               u(xx)= normalize(u(xx)):u(yy)=normalize(u(yy))
               k(xx)=0:k(yy)=0:startpos(xx)=b(xx):startpos(yy)=b(yy)
            End If
        End If
    Next yy
Next xx


#endmacro
'Dim As vector3d c

    sides()
    ball_to_ball()
    For z As Integer=1 To n
        k(z)=k(z)+1
        b(z)=startpos(z)+k(z)*sp(z)*u(z)+sp(z)*vct(0,gravity*k(z)*k(z),0)
        'c.x=c.x+b(z).x:c.y=c.y+b(z).y
    Circle(b(z).x,b(z).y),rad,col(z),,,,f
Next z
'c.x=c.x/n:c.y=c.y/n
'_rad(n)=rad
'b(n).x=c.x:b(n).y=c.y:col(n)=Rgba(100,0,0,200)
'face(c.x,c.y)
    End Sub


qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » Apr 04, 2011 15:25

dodicat: thanks for the help!(a month later!)

Ok, I'm having trouble here. Far as I can see, somehow when I collide with something, I change the velocity of my circle to infinity, and when I add infinity to my position, it changes the position to infinity(#QNAN)

Basically here's what happens:

1. I bump the line.
2. The velocity becomes 1.#IND (both the x and y velocity)
3. The position becomes 1.#IND, and the velocity becomes 1.#QNAN.
4. The position becomes 1.#QNAN

From googling, I found out that #QNAN stands for quiet not a number. #IND I haven't been able to figure out though. It's too much like Indiana, so, on googling, it gives me millions of links to sites about Indiana! Thanks for all the help so far. The programs are below(requires the FreeBASIC Extended Libary)

physics.bi

Code: Select all

#Include "fbgfx.bi"
#Include Once "ext/math/line2.bi"
#Include Once "ext/math/projections.bi"
#Include Once "ext/math/reflections.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,normal0,normal1,blah0,blah1
   
    xpoint = ext.math.ClampProjectedPoint(wall, this.posi)
   
    dim as single d
   
    d = this.posi.distance(xpoint)
   
    if d < this.radius then
        normal0 = lineNormal(wall, 1)
        normal1 = lineNormal(wall, 1)
        blah0 = xpoint + normal0
        blah1 = xpoint + normal1
        if this.posi.distance(blah0) < this.posi.distance(blah1) then normal = normal0
        if this.posi.distance(blah0) > this.posi.distance(blah1) then normal = normal1
        Print normal.x, normal.y
        Sleep
        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


test.bas

Code: Select all

#Include "fbgfx.bi"
#Include "physics.bi"

Using fb

Const accel = .5
Const friction = .02

Dim ball As circle2d
Dim As vector2d screenmax, blahx, blahy
Dim l As line2d
Dim As Integer i, j, x, y, ubindex = 0

Screen 19
Screeninfo x, y
Randomize Timer, 3

screenmax.x = x:screenmax.y = y

With ball
   .posi.x = CInt(Rnd * screenmax.x)
   .posi.y = CInt(Rnd * screenmax.y)
   .radius = CInt(Rnd * 20) + 10
   .ccolor = i + 1
   .maxvelocity = .radius / 2
End With

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
        ball.display(ball.posi.x, ball.posi.y)
        Locate 0, 0
        Print "X"; ball.posi.x, "Y"; ball.posi.y, "XV"; ball.vel.x, "YV"; ball.vel.y
        ScreenSync
        Screenunlock
        If Multikey(SC_UP) Then ball.vel.y = ball.vel.y - accel
        If Multikey(SC_RIGHT) Then ball.vel.x = ball.vel.x + accel
        If Multikey(SC_DOWN) Then ball.vel.y = ball.vel.y + accel
        If Multikey(SC_LEFT) Then ball.vel.x = ball.vel.x - accel
        If Multikey(SC_INSERT) Then ball.halt
       ball.posi.x += ball.vel.x
       ball.posi.y += ball.vel.y
       ball.vel *= (1 - friction)
       ball.bounce(l)
       If Abs(ball.vel.magnitude) > ball.maxvelocity Then ball.vel = ball.vel - 1
    Loop Until Multikey(SC_ESCAPE) Or Multikey(SC_SPACE)
Loop Until Multikey(SC_ESCAPE)
qbworker
Posts: 73
Joined: Jan 14, 2011 2:34

Postby qbworker » May 03, 2011 21:22

Bug fixed. First post updated.
Dr_D
Posts: 2415
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » May 03, 2011 21:29

I'm glad to see you're using the fbext vector stuff. this makes me happy. i thought no one would ever use it. thank you. :)

Return to “Projects”

Who is online

Users browsing this forum: No registered users and 14 guests