hello all.
I've been hugely impressed by grable's simple verlet code
http://www.blitzbasic.com/codearcs/codearcs.php?code=1769
probably like many others!
and would like to get something working out of it.
it seems to me in order to go on, we need circle-to-circle collisions (between particles), but, most importantly, circle-to-line collisions, for collisions between particles and "background" lines, like platforms, ground etc.
I suck at it, but gave it a try. I am not a programmer and not familiar with these things though.
first, I explored circle-line collisions and line-line collisions.
the idea (have no clue how sound it is) is that if a particle collides with a line, project it to outside the line (hopefully on the proper side!).
if it doesnt, check its path on this step (oldx,y to newx,y) and see if you get a collision between path line and static line. If those collide, project the particle at the proper spot. (got the proper side working too!)
contains functions found in the archives.
I modified some so they return a x,y array, or -1,-1 if no collision is happening.
use 1,2 to set x,y of line, 3,4 to set x,y of path
the circle at mousepoint is the size we're testing.
here's my testbed code:
(how do I put the code in those neat boxes so they dont take up all this space?)
----------------------------------------------------
then I modified grable's code.
created a new object, staticline and staticgroup, to contain the lines.
there are also other mdifications in here, you will see a rope hanging ready to be tinkered, plus you can make a constraint soft or hard by pressing 1-6 while making it (1-rubbery, 6-as solid as it gets).
you can also use arrow keys to move previously selected verlet.
I sort of got the collision working but have no idea what I'm doing wrong. particles go through the lines at high-ish speeds and they also seem to develop a force pushing them left!
any pointers or help greatly appreciated.
I believe some solid collision code on grable's example can result in a nice and simple verlet engine that can be quite useable!
best of all, if we can get grable to properly do it for us!! ;-)
cheers to all and merry christmas.
modified grable's verlet example code:
I've been hugely impressed by grable's simple verlet code
http://www.blitzbasic.com/codearcs/codearcs.php?code=1769
probably like many others!
and would like to get something working out of it.
it seems to me in order to go on, we need circle-to-circle collisions (between particles), but, most importantly, circle-to-line collisions, for collisions between particles and "background" lines, like platforms, ground etc.
I suck at it, but gave it a try. I am not a programmer and not familiar with these things though.
first, I explored circle-line collisions and line-line collisions.
the idea (have no clue how sound it is) is that if a particle collides with a line, project it to outside the line (hopefully on the proper side!).
if it doesnt, check its path on this step (oldx,y to newx,y) and see if you get a collision between path line and static line. If those collide, project the particle at the proper spot. (got the proper side working too!)
contains functions found in the archives.
I modified some so they return a x,y array, or -1,-1 if no collision is happening.
use 1,2 to set x,y of line, 3,4 to set x,y of path
the circle at mousepoint is the size we're testing.
here's my testbed code:
(how do I put the code in those neat boxes so they dont take up all this space?)
----------------------------------------------------
Function GetAngDiff#(ang1#,ang2#) Local diff#=ang2-ang1 While diff>180 diff=diff-360 Wend While diff<-180 diff=diff+360 Wend Return diff End Function 'Adapted from Fredborg's code Function LinesCross[]( x0:Float, y0:Float , x1:Float, y1:Float,.. x2:Float ,y2:Float, x3:Float, y3:Float ) Local n:Float=(y0-y2)*(x3-x2)-(x0-x2)*(y3-y2) Local d:Float=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2) If Abs(d) < 0.0001 ' Lines are parallel! Return [-1,-1] Else ' Lines might cross! Local Sn:Float=(y0-y2)*(x1-x0)-(x0-x2)*(y1-y0) Local AB:Float=n/d If AB>0.0 And AB<1.0 Local CD:Float=Sn/d If CD>0.0 And CD<1.0 ' Intersection Point Local X=x0+AB*(x1-x0) Local Y=y0+AB*(y1-y0) 'SetColor 255,0,255 'DrawOval x-(2),y-(2), 5,5 'DrawText x+","+y, 0,45 Return [x,y] End If End If ' Lines didn't cross, because the intersection was beyond the end points of the lines EndIf ' Lines do Not cross! Return [-1,-1] End Function Function LineToCircle[]( x1:Float, y1:Float, x2:Float, y2:Float, px:Float, py:Float, r:Float ) Local sx:Float = x2-x1 Local sy:Float = y2-y1 Local q:Float = ((px-x1) * (x2-x1) + (py - y1) * (y2-y1)) / (sx*sx + sy*sy) If q < 0.0 Then q = 0.0 If q > 1.0 Then q = 1.0 Local cx:Float=(1-q)*x1+q*x2 Local cy:Float=(1-q)*y1 + q*y2 'DrawText "dist: "+PointToPointDist(px,py,cx,cy), 0,15 'Local ang%=angle%(px,py,cx,cy) 'DrawText "ang: "+ang, 0,30 If PointToPointDist(px,py,cx,cy) < r Return [Int(cx),Int(cy)] Else Return [-1,-1] EndIf End Function Function PointToPointDist:Float( x1:Float, y1:Float, x2:Float, y2:Float ) Local dx:Float = x1-x2 Local dy:Float = y1-y2 Return Sqr(dx*dx + dy*dy) End Function Function angle%(x1#,y1#,x2#,y2#) Local angle%=ATan2(x2-x1,y2-y1) Return 180-angle End Function Function LineToCircle_givepos[]( x1, y1, x2, y2, x, y, r ) 'circle to line Local aha[2] aha = LineToCircle( x1, y1, x2, y2, x, y, r ) If Not(aha[0]=-1 And aha[1]=-1) 'SetColor 255,0,255 'DrawOval aha[0]-(2),aha[1]-(2), 5,5 'DrawLine x,y,aha[0],aha[1] Local ang%=angle%(x,y,aha[0],aha[1]) Local nx# = aha[0]+(r*Cos(ang+90)) Local ny# = aha[1]+(r*Sin(ang+90)) 'DrawOval nx-(2),ny-(2), 5,5 Return [Int(nx),Int(ny)] Else Return [-1,-1] EndIf End Function Function LinesCross_givepos[]( x1, y1, x2, y2, x21, y21, x22, y22, static_ang#, static_xoff#, static_yoff#, r ) Local aha2[2] aha2 = Linescross( x1, y1, x2, y2, x21, y21, x22, y22 ) 'DrawText aha2[0]+","+aha2[1], 0,60 If Not(aha2[0]=-1 And aha2[1]=-1) '..have crossed If static_ang=-1 'no angle given, so calculate it static_ang = angle(x1,y1,x2,y2) static_xoff=(Cos(static_ang+180)) static_yoff=(Sin(static_ang+180)) EndIf Local ang22%=angle%(x21,y21,x22,y22) Local angdif=GetAngDiff#(static_ang,ang22) static_xoff#:*r static_yoff#:*r 'center of circle projection from impact point If angdif<0 Then static_xoff:*-1;static_yoff:*-1 Return [Int(aha2[0]+static_xoff), Int(aha2[1]+static_yoff)] Else Return [-1,-1] EndIf EndFunction ' ' TEST ' Global x1:Float= Rnd(200),x2:Float=Rnd(200)+500,y1:Float=Rnd(200),y2:Float=Rnd(200)+500,x:Float,y:Float,r:Float=100 Global x21:Float= Rnd(200),x22:Float=Rnd(200)+500,y21:Float=Rnd(200),y22:Float=Rnd(200)+500 Global mx:Int,my:Int Graphics 1024,768, 0 Repeat If KeyDown( KEY_SPACE) Then x1= Rnd(1000) x2=Rnd(1000) y1=Rnd(800) y2=Rnd(800) x21= Rnd(1000) x22=Rnd(1000) y21=Rnd(800) y22=Rnd(800) EndIf x=MouseX() y=MouseY() If KeyDown(KEY_1) Then x1=x;y1=y If KeyDown(KEY_2) Then x2=x;y2=y If KeyDown(KEY_3) Then x21=x;y21=y If KeyDown(KEY_4) Then x22=x;y22=y DrawOval x-(r),y-(r), r*2,r*2 SetColor 255,255,255 DrawLine x1,y1,x2,y2 SetColor 155,155,155 DrawLine x21,y21,x22,y22 SetColor 0,255,0 DrawOval x1-(2),y1-(2), 5,5 SetColor 0,155,0 DrawOval x21-(2),y21-(2), 5,5 Local pos[2] pos = LineToCircle_givepos( x1, y1, x2, y2, x, y, r ) If Not(pos[0]=-1 And pos[1]=-1) 'circle in line, position circle at pos SetColor 255,0,255 DrawOval pos[0]-(2),pos[1]-(2), 5,5 'DrawLine x,y,pos[0],pos[1] EndIf Local pos2[2] Local static_ang# = angle(x1,y1,x2,y2) Local static_xoff#=(Cos(static_ang+180)) Local static_yoff#=(Sin(static_ang+180)) pos2 = LinesCross_givepos( x1, y1, x2, y2, x21, y21, x22, y22, static_ang, static_xoff, static_yoff, r ) If Not(pos2[0]=-1 And pos2[1]=-1) 'line on line, position circle at pos2 SetColor 255,0,255 DrawOval pos2[0]-(2),pos2[1]-(2), 5,5 EndIf SetColor 255,255,255 DrawText "press 1,2 for start,end of solid line, 3,4 for path line", 0,0 DrawText "purple point is where circle should be positioned to stay above the solid line.", 0,15 Flip 1 Cls Until KeyHit( KEY_ESCAPE) End
then I modified grable's code.
created a new object, staticline and staticgroup, to contain the lines.
there are also other mdifications in here, you will see a rope hanging ready to be tinkered, plus you can make a constraint soft or hard by pressing 1-6 while making it (1-rubbery, 6-as solid as it gets).
you can also use arrow keys to move previously selected verlet.
I sort of got the collision working but have no idea what I'm doing wrong. particles go through the lines at high-ish speeds and they also seem to develop a force pushing them left!
any pointers or help greatly appreciated.
I believe some solid collision code on grable's example can result in a nice and simple verlet engine that can be quite useable!
best of all, if we can get grable to properly do it for us!! ;-)
cheers to all and merry christmas.
modified grable's verlet example code:
Strict Const S_TIMESTEP:Float = 0.1 Function GetAngDiff#(ang1#,ang2#) Local diff#=ang2-ang1 While diff>180 diff=diff-360 Wend While diff<-180 diff=diff+360 Wend Return diff End Function 'Adapted from Fredborg's code Function LinesCross[]( x0:Float, y0:Float , x1:Float, y1:Float,.. x2:Float ,y2:Float, x3:Float, y3:Float ) Local n:Float=(y0-y2)*(x3-x2)-(x0-x2)*(y3-y2) Local d:Float=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2) If Abs(d) < 0.0001 ' Lines are parallel! Return [-1,-1] Else ' Lines might cross! Local Sn:Float=(y0-y2)*(x1-x0)-(x0-x2)*(y1-y0) Local AB:Float=n/d If AB>0.0 And AB<1.0 Local CD:Float=Sn/d If CD>0.0 And CD<1.0 ' Intersection Point Local X=x0+AB*(x1-x0) Local Y=y0+AB*(y1-y0) 'SetColor 255,0,255 'DrawOval x-(2),y-(2), 5,5 'DrawText x+","+y, 0,45 Return [x,y] End If End If ' Lines didn't cross, because the intersection was beyond the end points of the lines EndIf ' Lines do Not cross! Return [-1,-1] End Function Function LineToCircle[]( x1:Float, y1:Float, x2:Float, y2:Float, px:Float, py:Float, r:Float ) Local sx:Float = x2-x1 Local sy:Float = y2-y1 Local q:Float = ((px-x1) * (x2-x1) + (py - y1) * (y2-y1)) / (sx*sx + sy*sy) If q < 0.0 Then q = 0.0 If q > 1.0 Then q = 1.0 Local cx:Float=(1-q)*x1+q*x2 Local cy:Float=(1-q)*y1 + q*y2 'DrawText "dist: "+PointToPointDist(px,py,cx,cy), 0,15 'Local ang%=angle%(px,py,cx,cy) 'DrawText "ang: "+ang, 0,30 If PointToPointDist(px,py,cx,cy) < r Return [Int(cx),Int(cy)] Else Return [-1,-1] EndIf End Function Function PointToPointDist:Float( x1:Float, y1:Float, x2:Float, y2:Float ) Local dx:Float = x1-x2 Local dy:Float = y1-y2 Return Sqr(dx*dx + dy*dy) End Function Function angle%(x1#,y1#,x2#,y2#) Local angle%=ATan2(x2-x1,y2-y1) Return 180-angle End Function Function LineToCircle_givepos[]( x1, y1, x2, y2, x, y, r ) 'circle to line Local aha[2] aha = LineToCircle( x1, y1, x2, y2, x, y, r ) If Not(aha[0]=-1 And aha[1]=-1) 'SetColor 255,0,255 'DrawOval aha[0]-(2),aha[1]-(2), 5,5 'DrawLine x,y,aha[0],aha[1] Local ang%=angle%(x,y,aha[0],aha[1]) Local nx# = aha[0]+(r*Cos(ang+90)) Local ny# = aha[1]+(r*Sin(ang+90)) 'DrawOval nx-(2),ny-(2), 5,5 Return [Int(nx),Int(ny)] Else Return [-1,-1] EndIf End Function Function LinesCross_givepos[]( x1, y1, x2, y2, x21, y21, x22, y22, static_ang#, static_xoff#, static_yoff#, r ) Local aha2[2] aha2 = Linescross( x1, y1, x2, y2, x21, y21, x22, y22 ) 'DrawText aha2[0]+","+aha2[1], 0,60 If Not(aha2[0]=-1 And aha2[1]=-1) '..have crossed If static_ang=-1 'no angle given, so calculate it static_ang = angle(x1,y1,x2,y2) static_xoff=(Cos(static_ang+180)) static_yoff=(Sin(static_ang+180)) EndIf Local ang22%=angle%(x21,y21,x22,y22) Local angdif=GetAngDiff#(static_ang,ang22) static_xoff#:*r static_yoff#:*r 'center of circle projection from impact point If angdif<0 Then static_xoff:*-1;static_yoff:*-1 Return [Int(aha2[0]+static_xoff), Int(aha2[1]+static_yoff)] Else Return [-1,-1] EndIf EndFunction Type TSStaticline Field x1:Float,y1:Float Field x2:Float,y2:Float Field minx:Float, maxx:Float Field miny:Float, maxy:Float Field friction:Float,radius:Float 'unused for now Field static_ang#, static_xoff#, static_yoff# Function Create:TSStaticLine( x1:Float,y1:Float, x2:Float,y2:Float, friction:Float=1.0, radius:Float=1.0) Local l:TSStaticline = New TSStaticline l.x1 = x1 l.y1 = y1 l.x2 = x2 l.y2 = y2 l.friction=friction l.radius=radius If x1<x2 l.minx=x1-radius l.maxx=x2+radius Else l.minx=x2-radius l.maxx=x1+radius EndIf If y1<y2 l.miny=y1-radius l.maxy=y2+radius Else l.miny=y2-radius l.maxy=y1+radius EndIf l.static_ang# = angle(x1,y1,x2,y2) l.static_xoff#=(Cos(l.static_ang+180)) l.static_yoff#=(Sin(l.static_ang+180)) Return l EndFunction Method Render() SetColor 0,100,0 DrawLine x1,y1, x2,y2 EndMethod EndType Type TSStaticGroup Field lines:TList = New TList Field active:Int Field docollisions:Int Function Create:TSStaticGroup(active:Int = True, docollisions:Int = True) Local g:TSStaticGroup = New TSStaticGroup g.active = active g.docollisions = docollisions Return g EndFunction Method AddLine( l:TSStaticLine) If l Then lines.AddLast( l) EndMethod Method Render() For Local l:TSStaticLine = EachIn lines l.Render() Next EndMethod EndType Type TSPoint Field x:Float,y:Float ' current position Field oldx:Float,oldy:Float ' old position Field fx:Float,fy:Float ' impulse force Field mass:Float Field active:Int Field size:Int Function Create:TSPoint( x:Float,y:Float, mass:Float, active:Int = True, size:Int=15) Local p:TSPoint = New TSPoint p.x = x p.y = y p.oldx = x p.oldy = y p.mass = mass p.active = active p.size=size Return p EndFunction Function Create_nonew:TSPoint(poi:TSPoint, x:Float,y:Float, mass:Float, active:Int = True, size:Int=15) 'Local p:TSPoint = New TSPoint poi.x = x poi.y = y poi.oldx = x poi.oldy = y poi.mass = mass poi.active = active poi.size=size 'Return p EndFunction Method Update() If Not active Then Return ' forces Local tmpx1:Float = x Local tmpy1:Float = y Local tmpx2:Float = fx * S_TIMESTEP * S_TIMESTEP Local tmpy2:Float = fy * S_TIMESTEP * S_TIMESTEP oldx :+ tmpx2 oldy :+ tmpy2 x :- oldx y :- oldy x :+ tmpx1 y :+ tmpy1 oldx = tmpx1 oldy = tmpy1 fx = 0 fy = 0 EndMethod Method Check_BG_Collisions(bg:TSStaticGroup) Local b:Float, bx:Float, by:Float,deltalength:Float, diff:Float 'Local steps = Ceil(Sqr( For Local l:TSStaticLine = EachIn bg.lines If x>l.minx - size And x<l.maxx+size And y>l.miny-size And y<l.maxy+size ' in vincinity of line, so run checks Local pos[2] pos = LineToCircle_givepos( l.x1, l.y1, l.x2, l.y2, x, y, size/2 ) If Not(pos[0]=-1 And pos[1]=-1) 'circle in line, position circle at pos 'SetColor 255,0,255 'DrawOval pos[0]-(2),pos[1]-(2), 5,5 'DrawLine x,y,pos[0],pos[1] x=pos[0] y=pos[1] 'oldx=x 'oldy=y Else Local pos2[2] 'Local static_ang# = angle(x1,y1,x2,y2) 'Local static_xoff#=(Cos(static_ang+180)) 'Local static_yoff#=(Sin(static_ang+180)) pos2 = LinesCross_givepos( l.x1, l.y1, l.x2, l.y2, oldx, oldy, x, y, l.static_ang, l.static_xoff, l.static_yoff, size/2 ) If Not(pos2[0]=-1 And pos2[1]=-1) 'line on line, position circle at pos2 'SetColor 255,0,255 'DrawOval pos2[0]-(2),pos2[1]-(2), 5,5 x=pos2[0] y=pos2[1] 'oldx=x 'oldy=y EndIf EndIf EndIf Next EndMethod Method Render() SetColor 0,0,255 If size>1 Then SetColor 0,100,255 If Not active Then SetColor 0,0,80 Local si:Int =5+size DrawOval x-(si/2),y-(si/2), si,si EndMethod Method Translate( x:Float,y:Float, reset:Int = False) Self.x :+ x Self.y :+ y ' reset movement If reset Then oldx = Self.x oldy = Self.y EndIf EndMethod Method Rotate( dir:Float, center:Float[], reset:Int = False) Local xr:Float = x - center[0] Local yr:Float = y - center[1] x = xr * Cos(dir) - yr * Sin(dir) y = xr * Sin(dir) + yr * Cos(dir) x :+ center[0] y :+ center[1] ' reset movement If reset Then oldx = x oldy = y EndIf EndMethod EndType Type TSLink Field p1:TSPoint Field p2:TSPoint Field restLength:Float Field k:Float Field stress:Float Function Create:TSLink( p1:TSPoint, p2:TSPoint, k:Float) Local l:TSLink = New TSLink l.p1 = p1 l.p2 = p2 l.k = k l.CalcRestLength() Return l EndFunction Function Create_nonew:TSLink(l:TSLink, p1:TSPoint, p2:TSPoint, k:Float) 'Local l:TSLink = New TSLink l.p1 = p1 l.p2 = p2 l.k = k l.CalcRestLength() 'Return l EndFunction Function find:TSLink(obj:TSGroup, p1:TSPoint, p2:TSPoint) For Local l:TSLink = EachIn obj.links If (l.p1=p1 And l.p2=p2) Or (l.p1=p2 And l.p2=p1) Return l Exit EndIf Next Return Null EndFunction Method Update() Local dx:Float = p1.x - p2.x Local dy:Float = p1.y - p2.y Local dist:Float = Sqr( dx*dx + dy*dy) Local w:Float = p1.mass + p2.mass If p1.active Then p1.x :- ((dx / dist) * ((dist - restLength) * k)) * (p1.mass / w) p1.y :- ((dy / dist) * ((dist - restLength) * k)) * (p1.mass / w) EndIf If p2.active Then p2.x :+ ((dx / dist) * ((dist - restLength) * k)) * (p2.mass / w) p2.y :+ ((dy / dist) * ((dist - restLength) * k)) * (p2.mass / w) EndIf stress = (dist - restLength) / restLength EndMethod Method Delete() EndMethod Method Render() SetColor 255-(stress*100),k*255,255 DrawLine p1.x,p1.y, p2.x,p2.y EndMethod Method CalcRestLength() restLength = Sqr((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) EndMethod EndType Type TSGroup Field points:TList = New TList Field links:TList = New TList Field gravity:Float Field active:Int Field bbox:Float[4] Field center:Float[2] Function Create:TSGroup( gravity:Float = 0.0, active:Int = True) Local g:TSGroup = New TSGroup g.gravity = gravity g.active = active Return g EndFunction Function Create_nonew:TSGroup(g:TSGroup, gravity:Float = 0.0, active:Int = True) 'Local g:TSGroup = New TSGroup g.gravity = gravity g.active = active 'Return g EndFunction Method AddPoint( p:TSPoint) If p Then points.AddLast( p) EndMethod Method AddLink( l:TSLink) If l Then links.AddLast( l) EndMethod Method Update(bg:TSStaticGroup) If Not active Then Return For Local p:TSPoint = EachIn points p.fy = gravity p.Update() Next For Local l:TSLink = EachIn links l.Update() Next For Local p:TSPoint = EachIn points p.Check_BG_Collisions(bg) Next CalcBoundingBox() CalcCenterPoint() EndMethod Method Render() For Local l:TSLink = EachIn links l.Render() Next For Local p:TSPoint = EachIn points p.Render() Next SetColor 0,192,0 DrawFrame( bbox[0], bbox[1], bbox[2], bbox[3]) SetColor 255,0,0 DrawOval center[0]-2,center[1]-2,4,4 EndMethod Method Translate( x:Float,y:Float, reset:Int = False) For Local p:TSPoint = EachIn points p.Translate( x,y, reset) Next CalcBoundingBox() CalcCenterPoint() EndMethod Method Rotate( dir:Float, reset:Int = False) For Local p:TSPoint = EachIn points p.Rotate( dir, center, reset) Next CalcBoundingBox() CalcCenterPoint() EndMethod Method CalcBoundingBox() bbox[0] = $FFFFFFF bbox[1] = $FFFFFFF bbox[2] = 0 bbox[3] = 0 For Local p:TSPoint = EachIn points bbox[0] = Min( bbox[0], p.x) bbox[1] = Min( bbox[1], p.y) bbox[2] = Max( bbox[2], p.x) bbox[3] = Max( bbox[3], p.y) Next bbox[2] :- bbox[0] bbox[3] :- bbox[1] EndMethod Method CalcCenterPoint() Local xtmp:Float,ytmp:Float, sz:Int = points.Count() For Local p:TSPoint = EachIn points xtmp :+ p.x ytmp :+ p.y Next center[0] = xtmp / sz center[1] = ytmp / sz EndMethod EndType Function DrawFrame( x:Float,y:Float, w:Float,h:Float) DrawLine x,y, x+w,y ' top DrawLine x,y+h, x+w,y+h ' bottom DrawLine x,y, x,y+h ' left DrawLine x+w,y, x+w,y+h ' right EndFunction Function PointInRect:Int( px:Int,py:Int, rect:Int[]) Return (px >= rect[0]) And (py >= rect[1]) And (px < rect[0] + rect[2]) And (py < rect[1] + rect[3]) EndFunction ' ' TEST ' Graphics 1024,768, 0 Const BOX_COEF:Float = 1 Const BOX_MASS:Float = 30 ' statick lines to collide with Local background:TSStaticGroup = TSStaticGroup.Create() Local l1:TSStaticLine = TSStaticLine.Create(0,650,100,700) Local l2:TSStaticLine = TSStaticLine.Create(100,700,600,680) Local l3:TSStaticLine = TSStaticLine.Create(600,680,1024,700) background.AddLine(l1) background.AddLine(l2) background.AddLine(l3) 'goodol' square Local obj:TSGroup = TSGroup.Create( -10, False) Local p1:TSPoint = TSPoint.Create( 0,0, BOX_MASS, True) Local p2:TSPoint = TSPoint.Create( 64,0, BOX_MASS, True) Local p3:TSPoint = TSPoint.Create( 0,64, BOX_MASS, True) Local p4:TSPoint = TSPoint.Create( 64,64, BOX_MASS, True) obj.AddPoint( p1) obj.AddPoint( p2) obj.AddPoint( p3) obj.AddPoint( p4) obj.AddLink( TSLink.Create( p1, p2, BOX_COEF)) ' top obj.AddLink( TSLink.Create( p2, p4, BOX_COEF)) ' right obj.AddLink( TSLink.Create( p4, p3, BOX_COEF)) ' bottom obj.AddLink( TSLink.Create( p3, p1, BOX_COEF)) ' left obj.AddLink( TSLink.Create( p3, p2, BOX_COEF)) ' cross 1 obj.AddLink( TSLink.Create( p1, p4, BOX_COEF)) ' cross 2 ' move it some to the right obj.Translate( 405,32, True) ' rotate it and give it some speed obj.Rotate( 10) obj.Translate( 4,0) 'triangles Global t:Int For t=1 To 2 Local tmp1:TSPoint = TSPoint.Create( Rnd(150)+50,Rnd(150)+50,BOX_MASS, True) Local tmp2:TSPoint = TSPoint.Create( Rnd(150)+50,Rnd(150)+50,BOX_MASS, True) Local tmp3:TSPoint = TSPoint.Create( Rnd(150)+50,Rnd(150)+50,BOX_MASS, True) obj.AddPoint( tmp1) obj.AddPoint( tmp2) obj.AddPoint( tmp3) obj.AddLink( TSLink.Create( tmp1, tmp2, BOX_COEF)) obj.AddLink( TSLink.Create( tmp3, tmp2, BOX_COEF)) obj.AddLink( TSLink.Create( tmp3, tmp1, BOX_COEF)) tmp1.translate(Rnd(10)-3,Rnd(8)-5) Next 'one big particle Local tmp1:TSPoint = TSPoint.Create( Rnd(150)+50,Rnd(150)+50,BOX_MASS, True,70) obj.AddPoint( tmp1) Local obj2:TSGroup = TSGroup.Create( -5, False) 'a rope Local po:TSPoint[30], li:TSLink[30] po[0] = New TSPoint po[0].Create_nonew(po[0],180,180,100,False,10) obj.AddPoint( po[0]) Global te:Int For te=1 To 29 po[te] = New TSPoint po[te].Create_nonew(po[te],180,180+(te*15),100,True,1) obj.AddPoint( po[te]) li[te] = New TSLink li[te].Create_nonew(li[te],po[te],po[te-1],1) obj.AddLink(li[te]) Next 'ignore - second objext 'Local ptemp:TSPoint = TSPoint.Create( 164,164, BOX_MASS, False, 10) 'obj2.AddPoint( ptemp) 'Local p[5]:TSPoint = ptemp 'Local p[6]:TSPoint = TSPoint.Create( 184,184, BOX_MASS, True, 8) 'obj2.AddPoint( p[5]) 'obj2.AddPoint( p[6]) 'obj2.AddLink( TSLink.Create( p1, p[5], 0.05)) ' top 'obj2.AddLink( TSLink.Create( p[6], p[5], 0.05)) ' top ' globals Global mb1:Int,mb2:Int Global mx:Int,my:Int Global mpoint:TSPoint Global last_selected_point:TSPoint = p1 Repeat If KeyHit( KEY_SPACE) Then obj.active = Not obj.active obj2.active = Not obj2.active EndIf ' create points / links If KeyDown( KEY_LCONTROL) And (Not obj.active) Then mx = MouseX() my = MouseY() If MouseHit(1) Then ' create point obj.AddPoint( TSPoint.Create( mx,my, BOX_MASS, True)) ElseIf MouseHit(2) ' create link Local rect:Int[4] If mpoint = Null Then ' select first point For Local p:TSPoint = EachIn obj.points rect[0] = p.x - 4 rect[1] = p.y - 4 rect[2] = 8 rect[3] = 8 If PointInRect( mx,my, rect) Then mpoint = p last_selected_point = p Exit EndIf Next Else ' select second point Local coef:Float=0.9 If KeyDown( KEY_1) Then coef=0.001 If KeyDown( KEY_2) Then coef=0.05 If KeyDown( KEY_3) Then coef=0.25 If KeyDown( KEY_4) Then coef=0.5 If KeyDown( KEY_5) Then coef=0.75 If KeyDown( KEY_6) Then coef=1 'If KeyDown( KEY_0) Then coef=0 For Local p:TSPoint = EachIn obj.points rect[0] = p.x - 4 rect[1] = p.y - 4 rect[2] = 8 rect[3] = 8 If PointInRect( mx,my, rect) Then If coef>0 Then obj.AddLink( TSLink.Create( mpoint, p, COEF)) Exit Else 'remove link Local p1:TSLink = TSLink.find(obj,mpoint,p) 'If p1<>Null Then p1.delete(p1) EndIf EndIf Next mpoint = Null EndIf EndIf FlushMouse() Else ' move single point / modify link If MouseDown(1) Then mx = MouseX() my = MouseY() If Not mb1 Then ' select point Local rectl:Int[4] For Local pa:TSPoint = EachIn obj.points rectl[0] = pa.x - 4 rectl[1] = pa.y - 4 rectl[2] = 8 rectl[3] = 8 If PointInRect( mx,my, rectl) Then mpoint = pa last_selected_point = pa Exit EndIf Next mb1 = True EndIf ' modify point If mpoint Then mpoint.x = mx mpoint.y = my ' modify connected links If Not obj.active Then ' search for links with this point For Local l:TSLink = EachIn obj.links If (l.p1 = mpoint) Or (l.p2 = mpoint) Then l.CalcRestLength() EndIf Next ' cancel allow movement mpoint.oldx = mpoint.x mpoint.oldy = mpoint.y EndIf EndIf Else If mb1 Then ' reset mb1 = False mpoint = Null FlushMouse() EndIf EndIf EndIf If mpoint <> Null Then SetColor 0,200,255 DrawOval mpoint.x-4,mpoint.y-4, 8,8 EndIf ' turn point on/off If KeyHit( KEY_A) Then If mpoint <> Null Then mpoint.active = Not mpoint.active EndIf EndIf ' size If KeyHit( KEY_NUMADD) Then If mpoint <> Null Then mpoint.size =mpoint.size+1 EndIf EndIf If KeyHit( KEY_NUMSUBTRACT) Then If mpoint <> Null Then mpoint.size =mpoint.size-1 EndIf EndIf ' rotate box If KeyDown( KEY_Q) Then obj.Rotate( -0.5, False) ElseIf KeyDown( KEY_W) Then obj.Rotate( 0.5, False) EndIf ' move last selected point Local sp:Int =1 If KeyDown( KEY_UP) Then last_selected_point.translate(0,-sp) If KeyDown( KEY_DOWN) Then last_selected_point.translate(0,sp) If KeyDown( KEY_LEFT) Then last_selected_point.translate(-sp,0) If KeyDown( KEY_RIGHT) Then last_selected_point.translate(sp,0) SetColor 150,100,155 DrawOval last_selected_point.x-4, last_selected_point.y-4, 8,8 obj.Update(background) obj2.Update(background) ' constrain all points to screen edges For Local paa:TSPoint = EachIn obj.points ' bottom If paa.y > GraphicsHeight() Then paa.y = GraphicsHeight() ' full friction paa.oldx = paa.x EndIf ' left, right If paa.x < 0 Then paa.x = 0 ElseIf paa.x > GraphicsWidth() Then paa.x = GraphicsWidth() EndIf Next background.Render() obj.Render() obj2.Render() ' some help SetColor 255,255,255 DrawText "HELP:", 0,0 DrawText " Pause Simulation/Edit mode: SPACE", 0,15 DrawText " Rotate Left/Right: q / w", 0,30 DrawText " Modify Point: MB1 + DRAG", 0,45 DrawText " Create Point: CTRL + MB1", 0,60 DrawText " Create Link: CTRL + MB2 (select 2 points)", 0,75 DrawText " Turn point On/Off: A (on selected point)", 0,90 If mpoint <> Null Then DrawText " size: "+mpoint.size+" (+,- num)", 300,15 DrawText " mass: "+mpoint.mass+" ", 300,30 'DrawText " Modify Point: MB1 + DRAG", 300,45 EndIf Flip 1 Cls Until KeyHit( KEY_ESCAPE) End