[bmx] vector point to line deflection by Jesse [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:41

Previous topic - Next topic

BlitzBot

Title : vector point to line deflection
Author : Jesse
Posted : 1+ years ago

Description : I have been trying to learn vector math. and is a little bit complicate but nothing I can't Manage. I googled and found this site
<a href="http://tonypa.pri.ee/vectors/start.html" target="_blank">http://tonypa.pri.ee/vectors/start.html</a>
it is a good tutorial source.
it is for flash but the logic/principal is the same. so I did some work around it and managed to figure out a few things. so here is what I got so far. if you like it let me know so I can post a ball to line sample when I figure it out.


Code :
Code (blitzmax) Select
SuperStrict
Type point
Field x#
Field y#
End Type
Type tgame
Field stageW%
Field stageH%
Field maxV#
Field gravity#
Field bounce:vector2d
Field t#
Field MyOb:vector2d
Field v1:vector2d[5]
End Type
Type vector2d
Field p0:point
Field p1:point
Field vx#,vy#
Field dx#,dy#
Field rx#,ry#
Field lx#,ly#
Field Length#
Field timeFrame#
Field lastTime#
Global airf#
Global b#
Global f#
End Type


Global game:tgame = New tgame
     
game.stageW = 320
game.stageH = 240
game.maxV = 10
game.gravity = 0.0;

' Create Object
' point p0 is its starting point in the coordinates x/y
game.myob = New vector2d
game.myOb.airf = 1.0
game.myOb.b = 1.0
game.myOb.f = 1.0
game.myOb.p0 = New point
game.myOb.p0.x = 150
game.myob.p0.y = 100
' vectors x/y components
game.myOb.vx = 1.0;
game.myOb.vy = 0.0;
game.myOb.lastTime = MilliSecs()
' Create first vector
' point p0 is its starting point in the coordinates x/y
' point p1 is its End point in the coordinates x/y
game.v1[1] = New vector2d
game.v1[2] = New vector2d
game.v1[3] = New vector2d
game.v1[4] = New vector2d
game.v1[1].p0 = New point
game.v1[1].p1 = New point
game.v1[1].p0.x = 5
game.v1[1].p0.y = 10
game.v1[1].p1.x = 250
game.v1[1].p1.y = 50
game.v1[1].b = 1.0
game.v1[1].f = 1.0
game.v1[2].p0 = New point
game.v1[2].p1 = New point
game.v1[2].p0.x = 250
game.v1[2].p0.y = 50
game.v1[2].p1.x = 280
game.v1[2].p1.y = 130
game.v1[2].b = 1
game.v1[2].f = 1
game.v1[3].p0 = New point
game.v1[3].p1 = New point
game.v1[3].p0.x = 280
game.v1[3].p0.y = 130
game.v1[3].p1.x = 50
game.v1[3].p1.y = 160
game.v1[3].b = 1
game.v1[3].f = 1
game.v1[4].p0 = New point
game.v1[4].p1 = New point
game.v1[4].p0.x = 50
game.v1[4].p0.y = 160
game.v1[4].p1.x = 5
game.v1[4].p1.y = 10
game.v1[4].b = 1
game.v1[4].f = 1

Graphics game.stageW,game.stageH,32

Repeat

Cls
runme()
Flip()

Until KeyDown(key_escape)



Function runMe ()
Local i%
   
For i = 1 To 4
   updateVector(game.v1[i]);
    Next

Local ob:vector2d = game.myOb;
    ' add air resistance
    ob.vx :* ob.airf
    ob.vy :* ob.airf
    ' dont let it go over Max speed
    If (ob.vx>game.maxV)
    ob.vx = game.maxV
    ElseIf (ob.vx<-game.maxV)
        ob.vx = -game.maxV
    EndIf
    If (ob.vy>game.maxV)
    ob.vy = game.maxV
    Else If (ob.vy<-game.maxV)
        ob.vy = -game.maxV
    EndIf
    ' update the vector parameters
    updateObject(ob)
    ' time To collide something big
    game.t = 1000000
    ' no collision yet
    game.bounce = Null
    ' find collisions with walls
    For Local i% = 1 To 4
    Local t% = findIntersection(ob, game.v1[i]);
    ' If this has collision, save it
        If (t<game.t)
   ' which wall To collide with
        game.bounce = game.v1[i];
               ' save time
            game.t = t;
        EndIf
Next
    ' we have collision
    If game.bounce
    ' set End point To intersection point
    ob.p1.x = ob.p0.x+ob.vx*game.t;
    ob.p1.y = ob.p0.y+ob.vy*game.t;
    ' bounce
    Local newv:vector2d = bounce(ob, game.bounce);
    ' change movement vector
    ob.vx = newv.vx;
    ob.vy = newv.vy;
    ' add New direction To End point
    ob.p1.x :+ ob.vx*(1-game.t);
    ob.p1.y :+ ob.vy*(1-game.t);
    ' save the time
    game.t = 1-game.t;
    EndIf
    ' reset Object To other side If gone out of stage
    If (ob.p1.x>game.stageW)
    ob.p1.x :- game.stageW;
    ElseIf (ob.p1.x<0)
    ob.p1.x :+ game.stageW;
    EndIf
    If (ob.p1.y>game.stageH)
    ob.p1.y :- game.stageH;
    ElseIf (ob.p1.y<0)
    ob.p1.y :+ game.stageH;
    EndIf
' draw it
drawAll(ob);
' make End point equal To starting point For Next cycle
ob.p0 = ob.p1;
' save the movement without time
ob.vx :/ ob.timeFrame;
ob.vy :/ ob.timeFrame;
End Function

' Function To find all parameters For the vector
Function updateVector (v:vector2d)
' x And y components
' End point coordinate - start point coordinate
v.vx = v.p1.x-v.p0.x;
v.vy = v.p1.y-v.p0.y;
' length of vector
v.Length = Sqr(v.vx*v.vx+v.vy*v.vy);
' normalized unti-sized components
If (v.Length>0)
v.dx = v.vx/v.Length;
v.dy = v.vy/v.Length;
Else
v.dx = 0;
v.dy = 0;
EndIf
' Right hand normal
v.rx = -v.vy;
v.ry = v.vx;
' Left hand normal
v.lx = v.vy;
v.ly = -v.vx;
End Function

Function updateObject (v:vector2d)
' find time passed from last update
    Local thisTime# = MilliSecs()
    Local time# = (thisTime-v.lastTime)/6.0
    ' we use time, Not frames To move so multiply movement vector with time passed
    v.vx :* time;
    v.vy :* time;
    ' add gravity, also based on time
    v.vy = v.vy+time*game.gravity;
    'v.p1 = {};
v.p1 = New point
    ' find End point coordinates
    v.p1.x = v.p0.x+v.vx;
    v.p1.y = v.p0.y+v.vy;
    ' length of vector
    v.Length = Sqr(v.vx*v.vx+v.vy*v.vy);
    ' normalized unti-sized components
    v.dx = v.vx/v.Length;
    v.dy = v.vy/v.Length;
    ' Right hand normal
    v.rx = -v.vy;
    v.ry = v.vx;
    ' Left hand normal
    v.lx = v.vy;
    v.ly = -v.vx;
    ' save the current time
    v.lastTime = thisTime;
    ' save time passed
    v.timeFrame = time;
End Function

' find intersection point of 2 vectors

Function findIntersection:Int (v1:vector2d, v2:vector2d)
' vector between starting points
Local t1#,t2#
Local v3a:vector2d = New vector2d
Local v3b:vector2d = New Vector2d
  v3a.vx = v2.p0.x - v1.p0.x
v3a.vy = v2.p0.y - v1.p0.y
    v3b.vx = v1.p0.x - v2.p0.x
v3b.vy = v1.p0.y - v2.p0.y
    ' If they are parallel vectors, Return big number
    If ((v1.dx = v2.dx And v1.dy = v2.dy) Or (v1.dx = -v2.dx And v1.dy = -v2.dy))
    Return 1000000;
Else
    t1 = perP(v3a, v2)/perP(v1, v2)
        t2 = perP(v3b, v1)/perP(v2, v1)
    EndIf
    If (t1>0 And t1<=1 And t2>0 And t2<=1)
    Return t1;
Else
    Return 1000000;
    EndIf
End Function

' calculate perp product of 2 vectors
Function perP# (va:vector2d, vb:vector2d)

Return va.vx*vb.vy-va.vy*vb.vx;
         
End Function
     
' find New vector bouncing from v2
Function bounce:vector2d (v1:vector2d, v2:vector2d)
' projection of v1 on v2
    Local proj1:vector2d = projectVector(v1, v2.dx, v2.dy);
    ' projection of v1 on v2 normal
    Local proj2:vector2d = projectVector(v1, v2.lx/v2.Length, v2.ly/v2.Length);
    Local proj:vector2d = New vector2d
    ' reverse projection on v2 normal
    proj2.vx :* -1;
    proj2.vy :* -1;
    ' add the projections
    proj.vx = v1.f*v2.f*proj1.vx+v1.b*v2.b*proj2.vx;
    proj.vy = v1.f*v2.f*proj1.vy+v1.b*v2.b*proj2.vy;
    Return proj;
End Function

Function projectVector:vector2d (v1:vector2d, dx#, dy#)
' find dot product
Local dp# = v1.vx*dx+v1.vy*dy;
    Local proj:vector2d = New vector2d
    ' projection components
    proj.vx = dp*dx;
    proj.vy = dp*dy;
    Return proj;
End Function

Function drawall(v:vector2d)
Local vn:vector2d
For Local i:Int = 1 To 4
vn = game.v1[i]
DrawLine vn.p0.x,vn.p0.y,vn.p1.x,vn.p1.y
Next
DrawOval v.p0.x-2,v.p0.y-2,4,4
End Function


Comments :


Jesse(Posted 1+ years ago)

 Updated for Monkey!includes a vector class and objects.made an engine that will be expanded for the other vector demos.

Strict

Import mojo

Class Game  Extends App

Field engine:Engine

Method OnCreate:Int()
engine = New Engine(640,480,10,0,.15)
engine.AddBall(New Circle(140,120,90,5,.995,1,1))

engine.AddWall(New Line(50,10,550,150,1,1))
engine.AddWall(New Line(550,150,500,430,1,1))
engine.AddWall(New Line(500,430,100,360,1,1))
engine.AddWall(New Line(100,360,20,100,1,1))
SetUpdateRate 60
Return 1
End Method

Method OnUpdate:Int()
'engine.Update()
Return 1
End Method

Method OnRender:Int()
Cls()
engine.Render()
Return 1
End Method

End Class

Function Main:Int()
New Game
Return 1
End Function

'******************************************************************************** physics *****************************************

Class Engine
Field stage:Vector2D
Field maxV:Vector2D
Field gravity:Vector2D
Field bounce:Line
Field ball:Circle
Field wallList:List<Line>
Field lastTime:Float
Field timeFrame:Float


Method New(width:Float,height:Float,vel:Float,gravityx:Float,gravityy:Float)
stage = New Vector2D(width,height)
maxV = New Vector2D().Add(vel)
gravity = New Vector2D().Add(gravityx,gravityy)

wallList = New List<Line>
lastTime = Millisecs()


End Method

Method AddWall:list.Node<Line>(w:Line)
w.node = wallList.AddLast(w)
Return w.node
End Method


Method AddBall:Void(b:Circle)
ball = b
End Method

Method Update:Void()
   
   Local thisTime:Float = Millisecs()
   timeFrame = (thisTime - lastTime)/16.0
   ' add air resistance
   ball.V.Multiply(ball.airf)

   ' dont let it go over Max speed
   If (ball.V.x > maxV.x)
    ball.V.x = maxV.x
   Elseif (ball.V.x < -maxV.x)
       ball.V.x = -maxV.x
   Endif
   
   If (ball.V.y > maxV.y)
    ball.V.y = maxV.y
   Elseif (ball.V.y < -maxV.y)
       ball.V.y = -maxV.y
   Endif

   
   ' update the vector parameters
   updateObject(ball)
   
   ' time To collide something big
   Local t:Float = INVALID_DISTANCE
   
   ' no collision yet
   bounce = Null
   
   ' find collisions with walls
   Local node:list.Node<Line> = wallList.FirstNode()
   While node
    Local w:Line = node.Value()
    Local tt:Float = w.FindIntersection(ball)
    ' If this has collision, save it
       If (tt < t)
   ' which wall To collide with
       
        bounce = w
           
           ' save time
           t = tt
       Endif
       node = node.NextNode()
Wend

   ' we have collision
   If bounce
    ' move ball To intersection point
    ball.P.Add(ball.V.x*t,ball.V.y*t)
    ' bounce
    ball.V.Set(bounce.Bounce(ball))
    'get remainder of timeframe ball movement.
    t = 1.0 - t
    ball.P.Add(ball.V.x*t,ball.V.y*t)
   Else
    ball.P.Add(ball.V)
   Endif
   
   ' reset Object To other side If gone out of stage
   If (ball.P.x > stage.x)
    ball.P.x -= stage.x
   Elseif (ball.P.x < 0)
    ball.P.x += stage.x
   Endif
   
   If (ball.P.y > stage.y)
    ball.P.y -= stage.y
   Elseif (ball.P.y < 0)
    ball.P.y += stage.y
   Endif
   
' make End point equal To starting point For Next cycle
ball.V.Divide(timeFrame)

   ' save the current time
   lastTime = thisTime
End Method


Method updateObject:Void(b:Circle)
' find time passed from last update
   ' we use time, Not frames To move so multiply movement vector with time passed
   b.V.Multiply(timeFrame)
   ' add gravity, also based on time
   b.V.Add(timeFrame*gravity.x,timeFrame*gravity.y)

   b.Update()
   
End Method

Method Render:Void()
Update()
   Local node:list.Node<Line> = wallList.FirstNode()
   While node
    Local w:Line = node.Value()
w.Render()
       node = node.NextNode()
Wend
ball.Render()
End Method

End Class



'********************************************** vectors ************************************


Class Vector2D
Field x:Float
Field y:Float

Method New(x:Float,y:Float)
Set(x,y)
End Method

Method New(v:Vector2D)
Set(v)
End Method

Method Set:Vector2D(v:Vector2D)
x = v.x
y = v.y
Return Self
End Method

Method Set:Vector2D(x:Float,y:Float)
Self.x = x
Self.y = y
Return Self
End Method

Method Add:Vector2D(x:Float,y:Float)
Self.x += x
Self.y += y
Return Self
End Method

Method Add:Vector2D(v:Vector2D)
Self.x += v.x
Self.y += v.y
Return Self
End Method

Method Add:Vector2D(value:Float)
Self.x += value
Self.y += value
Return Self
End Method

Method Subtract:Vector2D(x:Float,y:Float)
Self.x -= x
Self.y -= y
Return Self
End Method

Method Subtract:Vector2D(v:Vector2D)
Self.x -= v.x
Self.y -= v.y
Return Self
End Method

Method Subtract:Vector2D(value:Float)
Self.x -= value
Self.y -= value
Return Self
End Method

Method Multiply:Vector2D(x:Float,y:Float)
Self.x *= x
Self.y *= y
Return Self
End Method

Method Multiply:Vector2D(v:Vector2D)
Self.x *= v.x
Self.y *= v.y
Return Self
End Method

Method Multiply:Vector2D(value:Float)
Self.x *= value
Self.y *= value
Return Self
End Method

Method Divide:Vector2D(x:Float,y:Float)
Self.x /= x
Self.y /= y
Return Self
End Method

Method Divide:Vector2D(v:Vector2D)
Self.x /= v.x
Self.y /= v.y
Return Self
End Method

Method Divide:Vector2D(value:Float)
Self.x /= value
Self.y /= value
Return Self
End Method

Method DotProduct:Float(x:Float,y:Float)
Return Self.x * x + Self.y * y
End Method

Method DotProduct:Float(v:Vector2D)
Return Self.x * v.x + Self.y * v.y
End Method

Method PerpDotProduct:Float(x:Float,y:Float)
Return Self.x * y - Self.y * x
End Method

Method PerpDotProduct:Float(v:Vector2D)
Return Self.x * v.y - Self.y * v.x
End Method

Method MagnitudeSquare:Float()
Return Self.x * Self.x + Self.y * Self.y
End Method

Method Magnitude:Float()
Return Sqrt(MagnitudeSquare())
End Method

Method Normalize:Vector2D()
Local len:Float = Magnitude()
Divide(len,len)
Return Self
End Method

Method GetAngle:Float()
Return ATan2(y,x)
End Method

Method Projection:Vector2D(dx:Float, dy:Float)'dx,dy must be a one unit vector
    Return New Vector2D(dx,dy).Multiply(x*dx+y*dy)
End Method

Method Projection:Vector2D(v:Vector2D)
Return New Vector2D(x,y).Multiply(x*v.x+y*v.y)
End Method

Method ToString:String()
Return x+"   "+y
End Method

Method Draw:Void(p:Vector2D)
DrawLine p.x,p.y,p.x+x,p.y+y
End method
End Class



'****************************************** objects *****


Const INVALID_DISTANCE:Float = 1000000.0

Class Shape Abstract
Field P:Vector2D
Field V:Vector2D
Field D:Vector2D
Field L:Vector2D
Field length:Float

Field b:Float
Field f:Float
Field airf:Float


Method Update:Void()
length = V.Magnitude()
D.Set(V).Divide(length)
L.Set(D.y,-D.x)
End Method

' find New vector bouncing from v2
Method Bounce:Vector2D(o1:Circle)
' projection of v1 on v2
   Local proj1:Vector2D = D.Projection(o1.V)
   ' projection of v1 on v2 normal
   Local proj2:Vector2D = L.Projection(o1.V)
   ' reverse projection on v2 normal
   proj2.Multiply(-1)
   ' add the projections
   Return New Vector2D(o1.f*f*proj1.x + o1.b*b*proj2.x, o1.f*f*proj1.y+o1.b*b*proj2.y)
End Method

' find intersection point of 2 vectors

Method FindIntersection:Float(o1:Shape)
   ' If they are parallel vectors, Return big number
   If ((o1.D.x = D.x And o1.D.y = D.y) Or (o1.D.x = -D.x And o1.D.y = -D.y))
    Return INVALID_DISTANCE
Endif
Local v3a:Vector2D = New Vector2D(P).Subtract(o1.P)
Local v3b:Vector2D = New Vector2D(o1.P).Subtract(P)
Local t1:Float = v3a.PerpDotProduct(V)/o1.V.PerpDotProduct(V)
Local t2:Float = v3b.PerpDotProduct(o1.V)/V.PerpDotProduct(o1.V)
   If (t1>0 And t1<=1 And t2>0 And t2<=1)
    Return t1
Endif
   Return INVALID_DISTANCE
End Method



End Class

Class Line Extends Shape
Field p2:Vector2D

Field node:list.Node<Line>

Method New(x1:Float,y1:Float,x2:Float,y2:Float,b:Float,f:Float)
P = New Vector2D(x1,y1)
p2 = New Vector2D(x2,y2)
V = New Vector2D(p2).Subtract(P)
D = New Vector2D()
L = New Vector2D()
Super.Update()
Self.b = b
Self.f = f
End Method

Method Render:void()
DrawLine(P.x,P.y,p2.x,p2.y)
End Method

End Class

Class Circle Extends Shape

'x,y,direction(degrees),speed, air(friction),bounce,friction(agains other objects)
Method New(x:Float,y:Float,direction:Float,speed:Float,air:Float,b:Float,f:Float)
P = New Vector2D(x,y)
D = New Vector2D(Cos(direction),Sin(direction))
V = New Vector2D(D).Multiply(speed)
L = New Vector2D(D.y,-D.x)
length = speed

Self.airf = air
Self.b = b
Self.f = f

End Method

Method Render:Void()
DrawCircle(P.x,P.y,2)
DrawLine P.x,P.y,P.x+V.x,P.y+V.y
End Method

End Class