SetGraphicsDriver GLMax2DDriver() Graphics 800,600,32,60 SeedRnd MilliSecs() Global linx:Float,liny:Float Global lcx:Float,lcy:Float Global cpx:Float,cpy:Float Global fps_time:Float,fps_loop:Float,fps_fps:Float Global world_group:group=group.Create(1) Type group Global elasticity:Float=1 Global group_list:TList=New TList Field circle_list:Tlist Field lyne_list:TList Field x:Float,y:Float Field width:Float,height:Float Field self_collisions:Int Field avoid_groups:TList Function Create:group(self_collisions:Int) g:group=New group g.self_collisions=self_collisions g.circle_list=New TList g.lyne_list=New TList g.avoid_groups=New TList group.group_list.AddLast g Return g EndFunction Function CollideCircles(c:point,cc:point,dist:Float=-1) Local x:Float=cc.x-c.x Local y:Float=cc.y-c.y If dist<0 Then dist=Sqr(x*x+y*y) If dist=0 Then dist=.0001 x=x/dist y=y/dist If x=0 And y=0 Then x=1 Local mass1:Float=cc.mass/(c.mass+cc.mass) Local mass2:Float=c.mass/(c.mass+cc.mass) Local offset:Float=(cc.r+c.r)-dist cc.x=cc.x+x*mass2*offset cc.y=cc.y+y*mass2*offset c.x=c.x-x*mass1*offset c.y=c.y-y*mass1*offset Local p:Float=(2.0*((c.xv*x+c.yv*y)-(cc.xv*x+cc.yv*y))) c.xv=c.xv-p*mass1*x c.yv=c.yv-p*mass1*y cc.xv=cc.xv+p*mass2*x cc.yv=cc.yv+p*mass2*y EndFunction Function CollideCircleLine(c:point,l:constraint,cr:Float=0) If cr<1 Then cr=c.r If LineCircle(l.a.x,l.a.y,l.b.x,l.b.y,c.x,c.y,cr)=1 Local d:Float=Sqr((l.a.x-l.b.x)*(l.a.x-l.b.x)+(l.a.y-l.b.y)*(l.a.y-l.b.y)) Local D0:Float=Sqr((l.a.x-lcx)*(l.a.x-lcx)+(l.a.y-lcy)*(l.a.y-lcy)) 'calculate the %'s of the masses and velocities based on where the point of contact is on each line Local p2:Float=D0/d If p2>1 Then p2=1 Local p1:Float=1.0-p2 Local tmass1:Float=p1*l.a.mass+p2*l.b.mass Local tvelx1:Float=p1*l.a.xv+p2*l.b.xv Local tvely1:Float=p1*l.a.yv+p2*l.b.yv If D0>d/2.0 Then D0=d-D0 Local angle:Float=ATan2(lcy-c.y,lcx-c.x)+180 Local angle1:Float=ATan2(c.y-lcy,c.x-lcx)+180 'find the how much is crossing over the other line Local x:Float=Cos(angle) Local y:Float=Sin(angle) Local mass1:Float=c.mass/(tmass1+c.mass) Local mass2:Float=tmass1/(tmass1+c.mass) Local offset:Float=(cr-Sqr((lcx-c.x)*(lcx-c.x)+(lcy-c.y)*(lcy-c.y))) l.a.x=l.a.x-x*offset*p1*mass1 l.a.y=l.a.y-y*offset*p1*mass1 l.b.x=l.b.x-x*offset*p2*mass1 l.b.y=l.b.y-y*offset*p2*mass1 c.x=c.x+x*offset*mass2 c.y=c.y+y*offset*mass2 Local p:Float=((2.0*((tvelx1*x+tvely1*y)-(c.xv*x+c.yv*y)))/(tmass1+c.mass)) tvelx1=-p*c.mass*x tvely1=-p*c.mass*y l.a.xv=l.a.xv+tvelx1*p1 l.a.yv=l.a.yv+tvely1*p1 l.b.xv=l.b.xv+tvelx1*p2 l.b.yv=l.b.yv+tvely1*p2 c.xv=c.xv+p*tmass1*x c.yv=c.yv+p*tmass1*y Return 1 EndIf Return 0 EndFunction Function CollideLines(l:constraint,ll:constraint) Local offset:Float,angle:Float,d5:Float,d6:Float Local d1:Float=(l.a.x-linx)*(l.a.x-linx)+(l.a.y-liny)*(l.a.y-liny) Local d2:Float=(l.b.x-linx)*(l.b.x-linx)+(l.b.y-liny)*(l.b.y-liny) If d2<d1 d1=-1 d5=d2 Else d2=-1 d5=d1 EndIf Local d3:Float=(ll.a.x-linx)*(ll.a.x-linx)+(ll.a.y-liny)*(ll.a.y-liny) Local d4:Float=(ll.b.x-linx)*(ll.b.x-linx)+(ll.b.y-liny)*(ll.b.y-liny) If d4<d3 d3=-1 d6=d4 Else d4=-1 d6=d3 EndIf If d5<d6 If d1=-1 ClosestPoint(ll.a.x,ll.a.y,ll.b.x,ll.b.y,l.b.x,l.b.y,0) offset=Sqr((l.b.x-cpx)*(l.b.x-cpx)+(l.b.y-cpy)*(l.b.y-cpy))*2.0 angle=ATan2(l.b.y-cpy,l.b.x-cpx)+180 Else ClosestPoint(ll.a.x,ll.a.y,ll.b.x,ll.b.y,l.a.x,l.a.y,0) offset=Sqr((l.a.x-cpx)*(l.a.x-cpx)+(l.a.y-cpy)*(l.a.y-cpy))*2.0 angle=ATan2(l.a.y-cpy,l.a.x-cpx)+180 EndIf Else If d3=-1 ClosestPoint(l.a.x,l.a.y,l.b.x,l.b.y,ll.b.x,ll.b.y,0) offset=Sqr((ll.b.x-cpx)*(ll.b.x-cpx)+(ll.b.y-cpy)*(ll.b.y-cpy))*2.0 angle=ATan2(ll.b.y-cpy,ll.b.x-cpx)'+180 Else ClosestPoint(l.a.x,l.a.y,l.b.x,l.b.y,ll.a.x,ll.a.y,0) offset=Sqr((ll.a.x-cpx)*(ll.a.x-cpx)+(ll.a.y-cpy)*(ll.a.y-cpy))*2.0 angle=ATan2(ll.a.y-cpy,ll.a.x-cpx)'+180 EndIf EndIf angle=angle+180.0 Local lind0:Float=Sqr((l.a.x-l.b.x)*(l.a.x-l.b.x)+(l.a.y-l.b.y)*(l.a.y-l.b.y)) Local lind1:Float=Sqr((ll.a.x-ll.b.x)*(ll.a.x-ll.b.x)+(ll.a.y-ll.b.y)*(ll.a.y-ll.b.y)) d1=Sqr((l.a.x-linx)*(l.a.x-linx)+(l.a.y-liny)*(l.a.y-liny)) d2=Sqr((ll.a.x-linx)*(ll.a.x-linx)+(ll.a.y-liny)*(ll.a.y-liny)) 'calculate the %'s of the masses and velocities based on where the point of contact is on each line Local p2:Float=d1/lind0 Local p1:Float=1.0-p2 Local p4:Float=d2/lind1 Local p3:Float=1.0-p4 'see if the first point has a velocity Local tmass1:Float=p1*l.a.mass+p2*l.b.mass Local tmass2:Float=p3*ll.a.mass+p4*ll.b.mass Local tvelx1:Float=p1*l.a.xv+p2*l.b.xv Local tvely1:Float=p1*l.a.yv+p2*l.b.yv Local tvelx2:Float=p3*ll.a.xv+p4*ll.b.xv Local tvely2:Float=p3*ll.a.yv+p4*ll.b.yv Local x:Float=Cos(angle) Local y:Float=Sin(angle) Local mass1:Float=tmass2/(tmass1+tmass2) Local mass2:Float=tmass1/(tmass1+tmass2) l.a.x=l.a.x-x*offset*p1*mass1 l.a.y=l.a.y-y*offset*p1*mass1 l.b.x=l.b.x-x*offset*p2*mass1 l.b.y=l.b.y-y*offset*p2*mass1 ll.a.x=ll.a.x+x*offset*p3*mass2 ll.a.y=ll.a.y+y*offset*p3*mass2 ll.b.x=ll.b.x+x*offset*p4*mass2 ll.b.y=ll.b.y+y*offset*p4*mass2 Local v1:Float=tvelx1*x+tvely1*y Local v2:Float=tvelx2*x+tvely2*y Local p:Float=(2.0*(v1-v2))/(tmass1+tmass2) tvelx1=p*tmass2*x tvely1=p*tmass2*y tvelx2=p*tmass1*x tvely2=p*tmass1*y l.a.xv=l.a.xv-tvelx1*p1 l.a.yv=l.a.yv-tvely1*p1 l.b.xv=l.b.xv-tvelx1*p2 l.b.yv=l.b.yv-tvely1*p2 ll.a.xv=ll.a.xv+tvelx2*p3 ll.a.yv=ll.a.yv+tvely2*p3 ll.b.xv=ll.b.xv+tvelx2*p4 ll.b.yv=ll.b.yv+tvely2*p4 EndFunction Function predict:Float(x0:Float,y0:Float,x1:Float,y1:Float,xv0:Float,yv0:Float,xv1:Float,yv1:Float,cr:Float) 'thanks to daftabrush @gamedev.net for this detection method! 'get the distance of the line Local d:Float=Sqr((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)) If d=0 Then d=.0001 Local a:Float=((xv1-xv0)*(-yv0)-(yv1-yv0)*(-xv0))/d Local b:Float=((x1-x0)*(-yv0)+(-y0)*(xv1-xv0)+(y0-y1)*(-xv0)+(x0)*(yv1-yv0))/d ' Lose the -cr from the c1 calculation initially Local c:Float=((x1-x0)*(-y0)+(y0-y1)*(-x0))/d ' The check the sign of the Cross-product If c>=0 ' Cross-Product is +ve or zero c=c-cr+1 ' subtract cr Else ' Cross-Product is Negative c=c+cr-1 ' add cr instead EndIf Local t:Float=0 Local root:Float=b*b-4.0*a*c If root>0 Or a=0 'they might collide b=0.0-b If a=0 t=c/b Else root=Sqr(root) t=(b+root)/(2*a) Local t1:Float=(b-root)/(2*a) If t<0 Or t>1 t=t1 ElseIf t1>0 And t1<1 And t1<t t=t1 EndIf EndIf Return t EndIf Return -1 EndFunction Function Update() Local g:group,gg:group Local cc:point,c:point Local l:constraint,ll:constraint Local is_first_coord:Int=0 Local max_d:Float,D0:Float,D1:Float,t:Float,x:Float,y:Float,x0:Float,y0:Float,x1:Float,y1:Float,d:Float For g=EachIn group.group_list is_first_coord=0 g.x=0 g.y=0 g.width=0 g.height=0 For c=EachIn g.circle_list If is_first_coord=0 is_first_coord=1 g.x=c.x-c.r g.y=c.y-c.r g.width=c.x+c.r g.height=c.y+c.r EndIf If c.x+c.xv*(c.xv<0)-c.r<g.x Then g.x=c.x+c.xv*(c.xv<0)-c.r If c.y+c.yv*(c.yv<0)-c.r<g.y Then g.y=c.y+c.yv*(c.yv<0)-c.r If c.x+c.xv*(c.xv>0)+c.r>g.width Then g.width=c.x+c.xv*(c.xv>0)+c.r If c.y+c.yv*(c.yv>0)+c.r>g.height Then g.height=c.y+c.yv*(c.yv>0)+c.r Next For l=EachIn g.lyne_list If is_first_coord=0 is_first_coord=1 g.x=l.a.x-l.a.r g.y=l.a.y-l.a.r g.width=l.a.x+l.a.r g.height=l.a.y+l.a.r EndIf If l.a.x+l.a.xv*(l.a.xv<0)-l.a.r<g.x Then g.x=l.a.x+l.a.xv*(l.a.xv<0)-l.a.r If l.a.y+l.a.yv*(l.a.yv<0)-l.a.r<g.y Then g.y=l.a.y+l.a.yv*(l.a.yv<0)-l.a.r If l.a.x+l.a.xv*(l.a.xv>0)+l.a.r>g.width Then g.width=l.a.x+l.a.xv*(l.a.xv>0)+l.a.r If l.a.y+l.a.yv*(l.a.yv>0)+l.a.r>g.height Then g.height=l.a.y+l.a.yv*(l.a.yv>0)+l.a.r If l.b.x+l.b.xv*(l.b.xv<0)-l.b.r<g.x Then g.x=l.b.x+l.b.xv*(l.b.xv<0)-l.b.r If l.b.y+l.b.yv*(l.b.yv<0)-l.b.r<g.y Then g.y=l.b.y+l.b.yv*(l.b.yv<0)-l.b.r If l.b.x+l.b.xv*(l.b.xv>0)+l.b.r>g.width Then g.width=l.b.x+l.b.xv*(l.b.xv>0)+l.b.r If l.b.y+l.b.yv*(l.b.yv>0)+l.b.r>g.height Then g.height=l.b.y+l.b.yv*(l.b.yv>0)+l.b.r Next g.width=g.width-g.x+1 g.height=g.height-g.y+1 'rect2(g.x,g.y,g.width,g.height,1) Next Local col_circle:circle_col Local t1:Float,t2:Float,t3:Float,t4:Float Local mass1:Float,mass2:Float Local p1:Float,p2:Float,tmass1:Float,tvelx1:Float,tvely1:Float,angle:Float,angle1:Float,offset:Float,p:Float Local self_collisions:Int Local xv0:Float,yv0:Float,xv1:Float,yv1:Float,xv:Float,yv:Float Local lp0:Int,lp1:Int For g=EachIn group.group_list self_collisions=0 If g.self_collisions=1 gg=g self_collisions=1 Else gg=g.After() EndIf While gg 'if the 2 groups are colliding If rectoverlap(g.x,g.y,g.width,g.height,gg.x,gg.y,gg.width,gg.height)=1 For c=EachIn g.circle_list 'loop through the circles in the other group If self_collisions=1 cc=c.After() Else cc=point(gg.circle_list.First()) EndIf While cc 'the distance between the circles D0=Sqr((c.x-cc.x)*(c.x-cc.x)+(c.y-cc.y)*(c.y-cc.y)) max_d=c.r+cc.r If D0<max_d 'they are hitting eachother group.CollideCircles(c,cc,D0) Else x0=cc.xv*cc.p*cc.fx y0=cc.yv*cc.p*cc.fy x1=c.xv*c.p*c.fx-x0 y1=c.yv*c.p*c.fy-y0 'see if they might collide D1=(x1*x1)+(y1*y1)'(c.xv*c.p-cc.xv*cc.p)*(c.xv*c.p-cc.xv*cc.p)+(c.yv*c.p-cc.yv*cc.p)*(c.yv*c.p-cc.yv*cc.p) 'if they are moving If D1>0 D1=Sqr(D1) 'the time of collision t=(D0-max_d)/D1 If t<1 'the new positions x0=cc.x+cc.xv*t*cc.p*cc.fx y0=cc.y+cc.yv*t*cc.p*cc.fy x1=c.x+c.xv*t*c.p*c.fx y1=c.y+c.yv*t*c.p*c.fy 'double check if theres a gap D0=Sqr((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)) t=t+2.0*(D0-max_d+1)/D1 If t>1 Then t=1 x0=cc.x+cc.xv*t*cc.p y0=cc.y+cc.yv*t*cc.p*cc.fy x1=c.x+c.xv*t*c.p*c.fx y1=c.y+c.yv*t*c.p*c.fy If (x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)<max_d*max_d 'it will collide if nothing else hits it 'add it to the list based on its time circle_col.AddCol(t,cc) EndIf EndIf EndIf 'if they can push eachother If D0<max_d+c.push_rad And c.push_rad>0 'they can 'get the distances from the edge of the circles D0=Sqr((c.x-cc.x)*(c.x-cc.x)+(c.y-cc.y)*(c.y-cc.y))-max_d mass1=cc.mass/(c.mass+cc.mass) mass2=c.mass/(c.mass+cc.mass) If D0=0 Then D0=.0001 d=(1.0-D0/c.push_rad)*c.push x=(cc.x-c.x)/D0 y=(cc.y-c.y)/D0 If x=0 And y=0 Then x=1 c.xv=c.xv-x*mass1*d c.yv=c.yv-y*mass1*d cc.xv=cc.xv+x*mass2*d cc.yv=cc.yv+y*mass2*d EndIf EndIf 'move to the next circle cc=cc.After() Wend 'loop through the lines in that group For l=EachIn gg.lyne_list 'is the circle not one of the end points of the line If c<>l.a And c<>l.b' Then CollideCircleLine(c,l) If CollideCircleLine(c,l)=0 xv=c.xv*c.fx*c.p yv=c.yv*c.fy*c.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv t=group.predict(l.a.x-c.x,l.a.y-c.y,l.b.x-c.x,l.b.y-c.y,xv0,yv0,xv1,yv1,c.r) If t>0 And t<1 'is it within 1 timestep x0=l.a.x+l.a.xv*t*l.a.p*l.a.fx y0=l.a.y+l.a.yv*t*l.a.p*l.a.fy x1=l.b.x+l.b.xv*t*l.b.p*l.b.fx y1=l.b.y+l.b.yv*t*l.b.p*l.b.fy x=c.x+c.xv*c.fx*c.p*t y=c.y+c.yv*c.fy*c.p*t 'do they overlap If LineCircle(x0,y0,x1,y1,x,y,c.r) circle_col.AddLyne(t,l) EndIf EndIf EndIf EndIf Next 'is the any collisions to check 'check if they still collide For col_circle=EachIn circle_col.circle_col_list 'is it a circle collision If col_circle.is_lyne=0 cc=col_circle.a 'get the distance D0=Sqr((c.x-cc.x)*(c.x-cc.x)+(c.y-cc.y)*(c.y-cc.y)) max_d=c.r+cc.r 'are they already colliding If D0<max_d group.CollideCircles(c,cc,D0) Else x0=cc.xv*cc.p*cc.fx y0=cc.yv*cc.p*cc.fy x1=c.xv*c.p*c.fx-x0 y1=c.yv*c.p*c.fy-y0 'is it still within 1 timestep D1=(x1*x1)+(y1*y1)'D1=(c.xv*c.p-cc.xv*cc.p)*(c.xv*c.p-cc.xv*cc.p)+(c.yv*c.p-cc.yv*cc.p)*(c.yv*c.p-cc.yv*cc.p) 'is it moving If D1>0 D1=Sqr(D1) 'the time t=(D0-max_d)/D1 'is it still within 1 timestep If t<=1 And t>0 'calc the new positions x0=cc.x+cc.xv*t*cc.p*cc.fx y0=cc.y+cc.yv*t*cc.p*cc.fy x1=c.x+c.xv*t*c.p*c.fx y1=c.y+c.yv*t*c.p*c.fy 'double check if theres a gap D0=Sqr((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)) t=t+2.0*(D0-max_d+1)/D1 If t>1 Then t=1 x0=cc.x+cc.xv*t*cc.p*cc.fx y0=cc.y+cc.yv*t*cc.p*cc.fy x1=c.x+c.xv*t*c.p*c.fx y1=c.y+c.yv*t*c.p*c.fy 'are they colliding D0=(x0-x1)*(x0-x1)+(y0-y1)*(y0-y1) If D0<max_d*max_d cc.x=x0 cc.y=y0 c.x=x1 c.y=y1 c.p=c.p-c.p*t If c.p<0 Then c.p=0 cc.p=cc.p-cc.p*t If cc.p<0 Then cc.p=0 group.CollideCircles(c,cc,Sqr(D0)) EndIf EndIf EndIf EndIf 'is it a line ElseIf col_circle.is_lyne=1 l=col_circle.b 'are they already colliding If CollideCircleLine(c,l)=0 xv=c.xv*c.fx*c.p yv=c.yv*c.fy*c.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv t=group.predict(l.a.x-c.x,l.a.y-c.y,l.b.x-c.x,l.b.y-c.y,xv0,yv0,xv1,yv1,c.r) If t>0 And t<1 'is it within 1 timestep x0=l.a.x+l.a.xv*t*l.a.p*l.a.fx y0=l.a.y+l.a.yv*t*l.a.p*l.a.fy x1=l.b.x+l.b.xv*t*l.b.p*l.b.fx y1=l.b.y+l.b.yv*t*l.b.p*l.b.fy x=c.x+c.xv*c.fx*c.p*t y=c.y+c.yv*c.fy*c.p*t 'do they overlap If LineCircle(x0,y0,x1,y1,x,y,c.r) l.a.x=x0 l.a.y=y0 l.b.x=x1 l.b.y=y1 c.x=x c.y=y c.p=c.p-c.p*t If c.p<0 Then c.p=0 l.a.p=l.a.p-l.a.p*t If l.a.p<0 Then l.a.p=0 l.b.p=l.b.p-l.b.p*t If l.b.p<0 Then l.b.p=0 group.CollideCircleLine(c,l) EndIf EndIf EndIf EndIf Next 'clear all the attempted collisions ClearList(circle_col.circle_col_list) Next 'loop through the lines For l=EachIn g.lyne_list If self_collisions=0 'is the circle not one of the end points of the line For c=EachIn gg.circle_list If c<>l.a And c<>l.b' Then CollideCircleLine(c,l) If CollideCircleLine(c,l)=0 xv=c.xv*c.fx*c.p yv=c.yv*c.fy*c.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-c.x y0=l.a.y-c.y x1=l.b.x-c.x y1=l.b.y-c.y t=group.predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,c.r) If t>0 And t<1 'is it within 1 timestep x0=l.a.x+l.a.xv*t*l.a.p*l.a.fx y0=l.a.y+l.a.yv*t*l.a.p*l.a.fy x1=l.b.x+l.b.xv*t*l.b.p*l.b.fx y1=l.b.y+l.b.yv*t*l.b.p*l.b.fy x=c.x+c.xv*c.fx*c.p*t y=c.y+c.yv*c.fy*c.p*t 'do they overlap If LineCircle(x0,y0,x1,y1,x,y,c.r) circle_col.AddCol(t,c) EndIf EndIf EndIf EndIf Next EndIf If self_collisions=1 ll=l.After() Else ll=constraint(gg.lyne_list.First()) EndIf While ll 'make sure they don't share a point If ll.a<>l.a And ll.b<>l.a And ll.a<>l.b And ll.b<>l.b If LinesCross(l.a.x,l.a.y,l.b.x,l.b.y,ll.a.x,ll.a.y,ll.b.x,ll.b.y)=1 CollideLines(l,ll) Else 'do a point collide check Rem '---------------------------------------------- 'the first point of the 1st line xv=l.a.xv*l.a.fx*l.a.p yv=l.a.yv*l.a.fy*l.a.p xv0=ll.a.xv*ll.a.fx*ll.a.p-xv yv0=ll.a.yv*ll.a.fy*ll.a.p-yv xv1=ll.b.xv*ll.b.fx*ll.b.p-xv yv1=ll.b.yv*ll.b.fy*ll.b.p-yv x0=ll.a.x-l.a.x y0=ll.a.y-l.a.y x1=ll.b.x-l.a.x y1=ll.b.y-l.a.y t1=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) 'the second point of the 1st line xv=l.b.xv*l.b.fx*l.b.p yv=l.b.yv*l.b.fy*l.b.p xv0=ll.a.xv*ll.a.fx*ll.a.p-xv yv0=ll.a.yv*ll.a.fy*ll.a.p-yv xv1=ll.b.xv*ll.b.fx*ll.b.p-xv yv1=ll.b.yv*ll.b.fy*ll.b.p-yv x0=ll.a.x-l.b.x y0=ll.a.y-l.b.y x1=ll.b.x-l.b.x y1=ll.b.y-l.b.y t2=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) t=t1 If t1<0 Or t1>1 t=t2 ElseIf t2>0 And t2<1 And t2<t1 t=t2 EndIf '---------------------------------------------- 'the first point of the 2nd line xv=ll.a.xv*ll.a.fx*ll.a.p yv=ll.a.yv*ll.a.fy*ll.a.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-ll.a.x y0=l.a.y-ll.a.y x1=l.b.x-ll.a.x y1=l.b.y-ll.a.y t3=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) 'the second point of the 2nd line xv=ll.b.xv*ll.b.fx*ll.b.p yv=ll.b.yv*ll.b.fy*ll.b.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-ll.b.x y0=l.a.y-ll.b.y x1=l.b.x-ll.b.x y1=l.b.y-ll.b.y t4=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) t1=t3 If t3<0 Or t3>1 t1=t4 ElseIf t4>0 And t4<1 And t4<t3 t1=t4 EndIf '---------------------------------------------- If t<0 Or t>1 t=t1 ElseIf t1>0 And t1<1 And t1<t t=t1 EndIf If t>=0 And t<1 circle_col.AddLyne(t,ll) EndIf EndRem EndIf EndIf ll=ll.After() Wend 'is the any collisions to check 'check if they still collide For col_circle=EachIn circle_col.circle_col_list If col_circle.is_lyne=1 c=col_circle.a 'are they already colliding If CollideCircleLine(c,l)=0 xv=c.xv*c.fx*c.p yv=c.yv*c.fy*c.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-c.x y0=l.a.y-c.y x1=l.b.x-c.x y1=l.b.y-c.y t=group.predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,c.r) If t>0 And t<1 'is it within 1 timestep x0=l.a.x+l.a.xv*t*l.a.p*l.a.fx y0=l.a.y+l.a.yv*t*l.a.p*l.a.fy x1=l.b.x+l.b.xv*t*l.b.p*l.b.fx y1=l.b.y+l.b.yv*t*l.b.p*l.b.fy x=c.x+c.xv*c.fx*c.p*t y=c.y+c.yv*c.fy*c.p*t 'do they overlap If LineCircle(x0,y0,x1,y1,x,y,c.r) l.a.x=x0 l.a.y=y0 l.b.x=x1 l.b.y=y1 c.x=x c.y=y c.p=c.p-c.p*t If c.p<0 Then c.p=0 l.a.p=l.a.p-l.a.p*t If l.a.p<0 Then l.a.p=0 l.b.p=l.b.p-l.b.p*t If l.b.p<0 Then l.b.p=0 group.CollideCircleLine(c,l) EndIf EndIf EndIf Else'its a line 'the first point of the 2nd line ll=col_circle.b If LinesCross(l.a.x,l.a.y,l.b.x,l.b.y,ll.a.x,ll.a.y,ll.b.x,ll.b.y)=1 CollideLines(l,ll) Else Rem 'do a point collide check '---------------------------------------------- 'the first point of the 1st line xv=l.a.xv*l.a.fx*l.a.p yv=l.a.yv*l.a.fy*l.a.p xv0=ll.a.xv*ll.a.fx*ll.a.p-xv yv0=ll.a.yv*ll.a.fy*ll.a.p-yv xv1=ll.b.xv*ll.b.fx*ll.b.p-xv yv1=ll.b.yv*ll.b.fy*ll.b.p-yv x0=ll.a.x-l.a.x y0=ll.a.y-l.a.y x1=ll.b.x-l.a.x y1=ll.b.y-l.a.y t1=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) 'the second point of the 1st line xv=l.b.xv*l.b.fx*l.b.p yv=l.b.yv*l.b.fy*l.b.p xv0=ll.a.xv*ll.a.fx*ll.a.p-xv yv0=ll.a.yv*ll.a.fy*ll.a.p-yv xv1=ll.b.xv*ll.b.fx*ll.b.p-xv yv1=ll.b.yv*ll.b.fy*ll.b.p-yv x0=ll.a.x-l.b.x y0=ll.a.y-l.b.y x1=ll.b.x-l.b.x y1=ll.b.y-l.b.y t2=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) t=t1 c=Null If t1>=0 And t1<1 Then c=ll.a If t1<0 Or t1>1 t=t2 c=ll.b ElseIf t2>0 And t2<1 And t2<t1 t=t2 c=ll.b EndIf '---------------------------------------------- 'the first point of the 2nd line xv=ll.a.xv*ll.a.fx*ll.a.p yv=ll.a.yv*ll.a.fy*ll.a.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-ll.a.x y0=l.a.y-ll.a.y x1=l.b.x-ll.a.x y1=l.b.y-ll.a.y t3=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) 'the second point of the 2nd line xv=ll.b.xv*ll.b.fx*ll.b.p yv=ll.b.yv*ll.b.fy*ll.b.p xv0=l.a.xv*l.a.fx*l.a.p-xv yv0=l.a.yv*l.a.fy*l.a.p-yv xv1=l.b.xv*l.b.fx*l.b.p-xv yv1=l.b.yv*l.b.fy*l.b.p-yv x0=l.a.x-ll.b.x y0=l.a.y-ll.b.y x1=l.b.x-ll.b.x y1=l.b.y-ll.b.y t4=predict(x0,y0,x1,y1,xv0,yv0,xv1,yv1,2) t1=t3 cc=Null If t3>=0 And t3<1 Then cc=l.a If t3<0 Or t3>1 t1=t4 cc=l.b ElseIf t4>0 And t4<1 And t4<t3 t1=t4 cc=l.b EndIf '---------------------------------------------- If t<0 Or t>1 t=t1 c=Null ElseIf t1>=0 And t1<1 And t1<t t=t1 c=Null EndIf If t>=0 And t<1 x0=l.a.x+l.a.xv*l.a.fx*l.a.p*t y0=l.a.y+l.a.yv*l.a.fy*l.a.p*t x1=l.b.x+l.b.xv*l.b.fx*l.b.p*t y1=l.b.y+l.b.yv*l.b.fy*l.b.p*t xv0=ll.a.x+ll.a.xv*ll.a.fx*ll.a.p*t yv0=ll.a.y+ll.a.yv*ll.a.fy*ll.a.p*t xv1=ll.b.x+ll.b.xv*ll.b.fx*ll.b.p*t yv1=ll.b.y+ll.b.yv*ll.b.fy*ll.b.p*t If c=Null Then c=cc If c<>Null Local ok:Int If c=l.a Or c=l.b ok=LineCircle(xv0,yv0,xv1,yv1,c.x,c.y,2) Else ok=LineCircle(x0,y0,x1,y1,c.x,c.y,2) EndIf If ok=1 'If LinesCross(x0,y0,x1,y1,xv0,yv0,xv1,yv1)=1 l.a.x=x0 l.a.y=y0 l.b.x=x1 l.b.y=y1 ll.a.x=xv0 ll.a.y=yv0 ll.b.x=xv1 ll.b.y=yv1 l.a.p=l.a.p-l.a.p*t If l.a.p<0 Then l.a.p=0 l.b.p=l.b.p-l.b.p*t If l.b.p<0 Then l.b.p=0 ll.a.p=ll.a.p-ll.a.p*t If ll.a.p<0 Then ll.a.p=0 ll.b.p=ll.b.p-ll.b.p*t If ll.b.p<0 Then ll.b.p=0 If c=l.a Or c=l.b group.CollideCircleLine(c,ll,2) Else group.CollideCircleLine(c,l,2) EndIf EndIf EndIf EndIf EndRem EndIf EndIf Next 'clear all the attempted collisions ClearList(circle_col.circle_col_list) Next EndIf gg=gg.After() Wend Next EndFunction Method After:group() Local link:TLink=group_list.FindLink(Self).NextLink() If link Then Return group(link.Value()) Return Null EndMethod EndType Type point Global point_list:TList=New TList Field x:Float,y:Float Field xv:Float,yv:Float Field fx:Float,fy:Float Field r:Float Field mass:Float Field p:Float Field push:Float,push_rad:Float Field in_group:group=Null Field spin:Float,angle:Float,grip:Float=1 Function Create:point(x:Float,y:Float,xv:Float,yv:Float,fx:Float,fy:Float,r:Float,mass:Float,push:Float,push_rad:Float,in_group:group=Null) Local p:point=New point p.x=x p.y=y p.xv=xv p.yv=yv p.fx=fx p.fy=fy p.push=push p.push_rad=push_rad p.r=r If p.r>0 If in_group=Null Then in_group=world_group p.in_group=in_group in_group.circle_list.Addlast p EndIf p.mass=mass p.p=1 point_list.AddLast p Return p EndFunction Function UpdatePoints() SetBlend LIGHTBLEND For p:point=EachIn point_list p.yv=p.yv+.2 p.xv=p.xv*p.fx p.yv=p.yv*p.fy p.x=p.x+p.xv*p.p p.y=p.y+p.yv*p.p p.p=1 SetColor 255,255,255 SetAlpha 1 oval2(p.x-p.r,p.y-p.r,p.r*2.0,p.r*2.0,0) p.angle=p.angle+p.spin DrawLine p.x,p.y,p.x+Cos(angle)*p.r,p.y+Sin(angle)*p.r SetColor 255,0,0 SetAlpha .25 oval2(p.x-p.r-p.push_rad,p.y-p.r-p.push_rad,(p.r+p.push_rad)*2,(p.r+p.push_rad)*2,1) If p.x<p.r p.x=p.r p.xv=-p.xv EndIf If p.y<p.r p.y=p.r p.yv=-p.yv EndIf If p.x>800-p.r p.x=800-p.r p.xv=-p.xv EndIf If p.y>600-p.r p.y=600-p.r p.yv=-p.yv EndIf Next EndFunction Method After:point() Local link:TLink=in_group.circle_list.FindLink(Self).NextLink() If link Then Return point(link.Value()) Return Null EndMethod EndType Type circle_col Global circle_col_list:TList=New TList Field t:Float Field a:point Field b:constraint Field is_lyne:Int Function AddLyne:circle_col(t:Float,b:constraint) c:circle_col=New circle_col c.t=t c.b=b c.is_lyne=1 If circle_col_list=Null circle_col_list.AddLast c Else Local cc:circle_col Local added:Int=0 For cc=EachIn circle_col.circle_col_list If t<cc.t circle_col_list.InsertBeforeLink(c,circle_col_list.FindLink(cc)) added=1 Exit EndIf Next If added=0 circle_col_list.AddLast c EndIf EndIf Return c End Function Function AddCol:circle_col(t:Float,a:point) c:circle_col=New circle_col c.t=t c.a=a If circle_col_list=Null circle_col_list.AddLast c Else Local cc:circle_col Local added:Int=0 For cc=EachIn circle_col.circle_col_list If t<cc.t circle_col_list.InsertBeforeLink(c,circle_col_list.FindLink(cc)) added=1 Exit EndIf Next If added=0 circle_col_list.AddLast c EndIf EndIf Return c EndFunction Method Before:circle_col() Local link:TLink=circle_col_list.FindLink(Self).PrevLink() If link Then Return circle_col(link.Value()) Return Null EndMethod Method After:circle_col() Local link:TLink=circle_col_list.FindLink(Self).NextLink() If link Then Return circle_col(link.Value()) Return Null EndMethod EndType Type constraint Global constraint_list:TList=New TList Field a:point Field b:point Field dist:Float Field tension:Float Field in_group:group=Null Field minimum:Float,maximum:Float Function Create:constraint(a:point,b:point,dist:Float,tension:Float,minimum:Float,maximum:Float,is_lyne:Int,in_group:group=Null) c:constraint=New constraint c.a=a c.b=b c.dist=dist c.minimum=minimum c.maximum=maximum If c.dist<0 Then c.dist=Sqr((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)) c.tension=tension If is_lyne=1 If in_group=Null Then in_group=world_group c.in_group=in_group in_group.lyne_list.Addlast c EndIf constraint_list.Addlast c Return c End Function Function UpdateConstraints() Local c:constraint Local x:Float,y:Float Local diff:Float Local mass1:Float,mass2:Float Local dist:Float For c=EachIn constraint.constraint_list x=c.a.x-c.b.x y=c.a.y-c.b.y dist=Sqr(x*x+y*y) If (d<c.minimum Or c.minimum<0) Or (d>c.maximum Or c.maximum<0) If dist=0 x=1 y=0 dist=.0001 EndIf diff=((dist-c.dist)/dist)*c.tension x=x*diff y=y*diff mass1=c.b.mass/(c.a.mass+c.b.mass) mass2=c.a.mass/(c.a.mass+c.b.mass) c.a.x=c.a.x-mass1*x*c.a.fx c.a.y=c.a.y-mass1*y*c.a.fy c.b.x=c.b.x+mass2*x*c.b.fx c.b.y=c.b.y+mass2*y*c.b.fy c.a.xv=c.a.xv-mass1*x*c.a.fx c.a.yv=c.a.yv-mass1*y*c.a.fy c.b.xv=c.b.xv+mass2*x*c.b.fx c.b.yv=c.b.yv+mass2*y*c.b.fy EndIf DrawLine c.a.x,c.a.y,c.b.x,c.b.y Next EndFunction Method After:constraint() Local link:TLink = in_group.lyne_list.FindLink(Self).NextLink() If link Then Return constraint(link.Value()) Return Null EndMethod End Type 'Local point0:point,point1:point '\ / ' \ / ' \ / ' \ / ' ' __________ mass=10000 Local point0:point,point1:point,point2:point,point3:point point0=point.Create(100,100,0,0,.0,.0,0,mass,0,0) point1=point.Create(300,300,0,0,.0,.0,0,mass,0,0) point2=point.Create(100,110,0,0,.0,.0,0,mass,0,0) point3=point.Create(300,310,0,0,.0,.0,0,mass,0,0) constraint.Create(point0,point1,-1,1,-1,-1,1) constraint.Create(point0,point2,-1,1,-1,-1,1) constraint.Create(point2,point3,-1,1,-1,-1,1) constraint.Create(point3,point1,-1,1,-1,-1,1) point0=point.Create(700,100,0,0,.0,.0,0,mass,0,0) point1=point.Create(500,300,0,0,.0,.0,0,mass,0,0) point2=point.Create(700,110,0,0,.0,.0,0,mass,0,0) point3=point.Create(500,310,0,0,.0,.0,0,mass,0,0) constraint.Create(point0,point1,-1,1,-1,-1,1) constraint.Create(point0,point2,-1,1,-1,-1,1) constraint.Create(point2,point3,-1,1,-1,-1,1) constraint.Create(point3,point1,-1,1,-1,-1,1) point0=point.Create(290,405,0,0,.0,.0,0,mass,0,0) point1=point.Create(510,405,0,0,.0,.0,0,mass,0,0) point2=point.Create(290,415,0,0,.0,.0,0,mass,0,0) point3=point.Create(510,415,0,0,.0,.0,0,mass,0,0) constraint.Create(point0,point1,-1,1,-1,-1,1) constraint.Create(point0,point2,-1,1,-1,-1,1) constraint.Create(point2,point3,-1,1,-1,-1,1) constraint.Create(point3,point1,-1,1,-1,-1,1) point0=point.Create(0,450,0,0,.0,.0,0,mass,0,0) point1=point.Create(150,600,0,0,.0,.0,0,mass,0,0) constraint.Create(point0,point1,-1,1,-1,-1,1) point0=point.Create(800,450,0,0,.0,.0,0,mass,0,0) point1=point.Create(650,600,0,0,.0,.0,0,mass,0,0) constraint.Create(point0,point1,-1,1,-1,-1,1) point0=point.Create(200,50,0,0,.99,.99,0,5,0,0) point1=point.Create(300,50,0,0,.99,.99,0,5,0,0) point2=point.Create(400,150,0,0,.99,.99,0,5,0,0) point3=point.Create(300,150,0,0,.99,.99,0,5,0,0) constraint.Create(point0,point1,100,1,-1,-1,1) constraint.Create(point1,point2,100,1,-1,-1,1) constraint.Create(point2,point3,100,1,-1,-1,1) constraint.Create(point3,point0,100,1,-1,-1,1) constraint.Create(point0,point2,Sqr(100*100+100*100),1,-1,-1,0) constraint.Create(point1,point3,Sqr(100*100+100*100),1,-1,-1,0) point0=point.Create(350,200,0,0,.99,.99,0,5,0,0) point1=point.Create(450,200,0,0,.99,.99,0,5,0,0) point2=point.Create(450,300,0,0,.99,.99,0,5,0,0) point3=point.Create(350,300,0,0,.99,.99,0,5,0,0) constraint.Create(point0,point1,90,1,-1,-1,1) constraint.Create(point1,point2,90,1,-1,-1,1) constraint.Create(point2,point3,90,1,-1,-1,1) constraint.Create(point3,point0,90,1,-1,-1,1) constraint.Create(point0,point2,Sqr(90*90+90*90),1,-1,-1,0) constraint.Create(point1,point3,Sqr(90*90+90*90),1,-1,-1,0) Global main_loop:Int Local cx:Int=400,cy:Int=100 While KeyHit(KEY_ESCAPE)=0 Cls constraint.UpdateConstraints() SetColor 255,255,255 SetAlpha 1 DrawText "Click to fire",0,0 DrawText "WASD to move turret",0,20 FPS(0,40) If KeyDown(KEY_W)=1 Then cy=cy-3 If KeyDown(KEY_S)=1 Then cy=cy+3 If KeyDown(KEY_A)=1 Then cx=cx-3 If KeyDown(KEY_D)=1 Then cx=cx+3 If cx<0 Then cx=0 If cx>800 Then cx=800 If cy<0 Then cy=0 If cy>600 Then cy=600 Local angle:Float=ATan2(cy-MouseY(),cx-MouseX())+180 SetAlpha 1 SetColor 255,255,255 DrawLine cx,cy,cx+Cos(angle)*100,cy+Sin(angle)*100 point.UpdatePoints() If KeyDown(KEY_SPACE) For p:point=EachIn point.point_list p.xv=p.xv+Rand(-5,5) p.yv=p.yv+Rand(-5,5) Next EndIf If KeyHit(KEY_F10) Then take_screenshot() main_loop=main_loop+1 If MouseDown(1)=1 And main_loop>5 main_loop=0 speed:Int=100 point.Create(cx,cy,Cos(angle)*speed,Sin(angle)*speed,.99,.99,15,5,0,0) EndIf group.Update() Flip Wend EndGraphics() End Function oval2(x:Float,y:Float,width:Float,height:Float,fill:Int=1) glpolygonmode(GL_FRONT,GL_FILL) If fill=0 Then glpolygonmode(GL_FRONT,GL_LINE) DrawOval x,y,width,height glpolygonmode(GL_FRONT,GL_FILL) EndFunction Function rect2(x:Float,y:Float,width:Float,height:Float,fill:Int=1) glpolygonmode(GL_FRONT,GL_FILL) If fill=0 Then glpolygonmode(GL_FRONT,GL_LINE) DrawRect x,y,width,height glpolygonmode(GL_FRONT,GL_FILL) EndFunction Function LinesCross:Int(x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float) Local d:Float=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2) If Abs(d)<0.0001 Then Return -1 Local n:Float=(y0-y2)*(x3-x2)-(x0-x2)*(y3-y2) Local AB:Float=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d If AB>0.0 And AB<1.0 Local CD:Float=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d If CD>0.0 And CD<1.0 linx=x0+AB*(x1-x0) liny=y0+AB*(y1-y0) Return 1 EndIf EndIf Return 0 End Function Function LineCircle:Int(x0:Float,y0:Float,x1:Float,y1:Float,cx:Float,cy:Float,cr:Float) 'thanks to daftabrush @gamedev.net for the 's' equation Local d:Float=(x0-x1)*(x0-x1)+(y0-y1)*(y0-y1) If d=0 Then d=.0001 Local s:Float=((cx-x0)*(x1-x0)+(cy-y0)*(y1-y0))/d If s<0 Then s=0 If s>1 Then s=1 x1=x0+(x1-x0)*s y1=y0+(y1-y0)*s If (cx-x1)*(cx-x1)+(cy-y1)*(cy-y1)<cr*cr lcx=x1 lcy=y1 Return 1 EndIf Return 0 End Function Function ClosestPoint(x0:Float,y0:Float,x1:Float,y1:Float,cx:Float,cy:Float,cap:Int) Local d:Float=(x0-x1)*(x0-x1)+(y0-y1)*(y0-y1) If d=0 Then d=.0001 Local s:Float=((cx-x0)*(x1-x0)+(cy-y0)*(y1-y0))/d If cap=1 If s<0 Then s=0 If s>1 Then s=1 EndIf cpx=x0+(x1-x0)*s cpy=y0+(y1-y0)*s EndFunction Function AngleDifference:Float(x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float) Local angle0:Float,angle1:Float If x0>x1 angle0=ATan2(y0-y1,x0-x1)+180 Else angle0=ATan2(y1-y0,x1-x0)+180 EndIf If x2>x3 angle1=ATan2(y2-y3,x2-x3)+180 Else angle1=ATan2(y3-y2,x3-x2)+180 EndIf Return 1.0-Abs(Abs(angle0-angle1)-90)/90.0 End Function Function RectOverlap(x:Int,y:Int,w:Int,h:Int,xx:Int,yy:Int,ww:Int,hh:Int) If x+w>xx And y+h>yy And xx+ww>x And yy+hh>y Then Return 1 Return 0 End Function Function FPS(x:Int,y:Int) If MilliSecs()-fps_time>1000 fps_fps=fps_loop/(MilliSecs()-fps_time) fps_time=MilliSecs() fps_loop=0 EndIf fps_loop=fps_loop+1 SetColor 255,255,255 SetAlpha 1 DrawText "FPS: "+(Int(fps_fps*1000.0)),x,y EndFunction Function take_screenshot() Local filename:String, padded:String Local num:Int = 0 padded = num While padded.length < 3 padded = "0"+padded Wend filename = "screen"+padded+".png" While FileType(filename) <> 0 num:+1 padded = num While padded.length < 3 padded = "0"+padded Wend filename = "screen"+padded+".png" Wend Local img:tpixmap = GrabPixmap(0,0,GraphicsWidth(),GraphicsHeight()) SavePixmapPNG(img,filename) EndFunction
Here's an overview of how the system is structured:
there's points and constraints. Points can have masses, radius's, velocities, etc. Constraints can connect 2 points and potentialy become lines. The circles in the demo are points with radius's, the lines are constraints flagged to be lines.
When a line or circle gets created, it gets added to a list in a group. To begin with the only group is world_group and if a circle or line isn't assigned a group, thats where it goes. Groups can have self collisions enabled/disabled. Each group also has a bouding box to determine if it's colliding with another group.
A line is 2 connected points. If the masses of the points differ, then the mass of a point on the line is determined by how far/close the point is to the end points. Lines can also have circles on the each end by raising the radius of the end points in the demo.
tonyg, the reason the balls won't move out from the corners is because the corner isn't a polygon in the sense you are thinking of it. It is just a line...if the ball hits the line it responds, it isn't a filled corner. This is also why balls can stay inside the boxes.
The reason why the balls get in them boxes or out of the corners is because other objects force them to move to a point that is at least half way on the other side of a line, so when the ball and line collide it forces it the rest of the way to the other side.
Haram, i've heard about the SAT but my understanding of it still has some holes:
1) get the angle between the 2 bodies
2) draw a line in between them perpendicular to the one connecting them
3) if a point from both bodies crosses the line then they are colliding.
If this is correct then it seems like a bad idea waiting to happen...
The lines do abort detection if they are parallel (see the LinesCross function).