Author Topic: 2D ball collisions without trigonometry.  (Read 21169 times)

0 Members and 1 Guest are viewing this topic.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #15 on: May 11, 2021, 09:58:19 pm »
For a head on, elastic collision between two moving balls of equal mass and material, I think that trading vector magnitudes in rebound would be appropriate. In lieu of a more complete knowledge of the subject, on my part.

That said, it would be rather rare for two independent balls to be moving on direct, inverted vectors. They're typically going to be striking at different vectors, or be on inverted vectors that are slightly (or grossly) offset. The angle between their travel vector and the line between the ball centers at the point of collision (I'll call it the strike vector) would govern how each shears away from the other.

Most treatises I've seen on the subject give a shear angle that is perpendicular (orthogonal) to the strike vector, although most of those are assuming a stationary target ball, as per the graphic I posted earlier. There are two orthogonals to the strike vector, and one has to control which is used in order for a ball to not deflect to the "inside", which looks rather weird. I reasoned that one would chose the shear orthogonal that would be closest to a projected point of the striking balls original incoming vector. There may be a simpler method that I'm missing.

After that it gets interesting. I normalized the strike vector and the striking balls incoming vector and apply dot product to those to figure out how much force is transferred to the struck ball, while 1 - dot product result would be retained by the striking ball on its new course. That seemed to yield a satisfying look to my eye, but my application had issues with balls blowing through each other and trading places or getting stuck on one another when in tight groups or high relative speeds. I assume they were moving beyond the collision test before it even had a chance to implement. Oh well, back to the drawing board...

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #16 on: May 11, 2021, 10:17:29 pm »
Speaking of Dot Product, I call foul! the paper said no Trig but look at how Dot Product is calc'd with COS!
https://www.mathsisfun.com/algebra/vectors-dot-product.html



Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #17 on: May 11, 2021, 10:42:28 pm »
The easy method of dot product calculation is:

a · b = ax × bx + ay × by

I typically assign a function to it.
Code: QB64: [Select]
  1. FUNCTION VecDot (var AS V2, var2 AS V2)
  2.     VecDot = var.x * var2.x + var.y * var2.y
  3. END FUNCTION 'VecDot
  4.  

I have to assume it's much faster than COS in the CPU
« Last Edit: May 11, 2021, 10:45:10 pm by OldMoses »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #18 on: May 11, 2021, 10:48:25 pm »
Trying to understand the 7 steps and Dot Product is hanging me up.

From Stx notes on vector math here is Dot Product without need of COS(angle between vectors)
Code: QB64: [Select]
  1. Function v2DotProduct (A As xyType, B As xyType) 'shadow or projection  if A Dot B = 0 then A , B are perpendicular
  2.     v2DotProduct = A.x * B.x + A.y * B.y
  3.  

ha I was just going to post this.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #19 on: May 12, 2021, 01:47:45 am »
 Working with vector addition using the parallelogram method (what I call the mvector) There are times when this works OK and other times when it don't.  Sometimes the mvector is 0, 0  which is valid for 180 degree collisions with a strike angle of zero but any attempt to rotate 0,  0 to a required vector results in loss of final direction of one of the balls.

Mvector is strange. Sometimes it has no direction or magnitude (0, 0 ) at other times it acts like a real vector.  At other times it points in both directions (or should).

From a code example :  say mvector is  0X,   113.137 Y.  That works fine for the red ball but the cyan ball expects
 0X,   -113.137 Y..     It is possible that mvector can point in opposite directions. I'm not sure if that fits the definitions of a vector.


« Last Edit: May 12, 2021, 02:26:11 am by NOVARSEG »

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #20 on: May 12, 2021, 02:39:25 am »
I am pretty sure I said no desire for coffee, coffee still exists at least until it gets cyber attacked.

Almost as bad... No 'desire' for coffee... You should have your mouth washed out with soap... Blasphemy! LOL
Logic is the beginning of wisdom.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #21 on: May 12, 2021, 08:06:04 am »
Working with vector addition using the parallelogram method (what I call the mvector) There are times when this works OK and other times when it don't.  Sometimes the mvector is 0, 0  which is valid for 180 degree collisions with a strike angle of zero but any attempt to rotate 0,  0 to a required vector results in loss of final direction of one of the balls.

Mvector is strange. Sometimes it has no direction or magnitude (0, 0 ) at other times it acts like a real vector.  At other times it points in both directions (or should).

From a code example :  say mvector is  0X,   113.137 Y.  That works fine for the red ball but the cyan ball expects
 0X,   -113.137 Y..     It is possible that mvector can point in opposite directions. I'm not sure if that fits the definitions of a vector.

It sounds like your "Mvector" is just like the strike orthogonals that I mention above, which indeed would go in two different directions. One ball would take one "Mvector" and the other ball would take the other. The important part is controlling which takes which. After determining the magnitude of each Mvector, you would then do the vector addition of the balls respective incoming vector and their separate Mvectors. That would conserve some of their original momentum. If the balls were striking head on, then the Dot product calculation would reflect that fact by resulting in an Mvector magnitude of 0, and should result in a swapping of velocities for the balls as Bplus proved earlier. The result would work whether one ball, or both were moving. Isn't math fun....

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #22 on: May 12, 2021, 12:54:45 pm »
Quote
Isn't math fun....

It is a total b... until you learn the jargon. I am pretty comfortable with Trig but find physics vectors difficult because I haven't practiced with 100's of exercises in it's terminology. The challenge with Trig and Physics is that Basic uses an upside down Quadrant 1 only graphics system which is not how you learned it in math at school. That, I learned is an artifact of printing with print lines always positive and increasing as you go down the screen a rows and cols system for chars. QB64's Locate row, col is a good example.

I have to say it is fun when it starts producing results for you in computer applications, it's like magic. They say knowledge is power and when math works it is a power alright!

Coffee helps with the practicing ;-)) Maybe something stronger when you've had enough abstract jargon for the day.

2 Ball Collision is really good challenge for any style math.
« Last Edit: May 12, 2021, 12:56:57 pm by bplus »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #23 on: May 12, 2021, 01:06:16 pm »
Yeah, the quadrant issue is such a bear that I generally resort to WINDOW to redefine my coordinate system. Then when I do use trig, I have to remain cognizant of the reversals of various SIN/COS/TAN functions with respect to my redefined system.

The only reason that I've been able to fool with vector math is Stx's tutorials, and many hours watching Professor Leonard's calculus videos on Youtube.

I'm toying with doing a little program to demo a Cross product function in action.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #24 on: May 13, 2021, 09:18:40 am »
https://www.vobarian.com/collisions/2dcollisions2.pdf

Quote
but my application had issues with balls blowing through each other and trading places or getting stuck on one another when in tight groups or high relative speeds.

Last night I coded the whole paper step by step. The code passes the head on test but I am getting balls sticking together also. I will recheck code today and show. Meanwhile I did get a JB by tsh73 port using vectors working fine and much simpler than paper. I will show that one later too. I want to set up 2 more special cases other than Head on ones where 2 balls travel nearly parallel but converging paths and see their bump and reflection then with 2 balls at 90 degree collision and se that reflection.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #25 on: May 13, 2021, 04:29:04 pm »
OK here is first vector code I got working for 2 or more ball collisions by barrowing from tsh73 at JB forum. He was working study for an app for pin ball and one ball was always moving and the other was stationary circle, so I adapted to 2 moving balls and may have oversimplified. Anyway it can handle 20 balls no sticking:

All the special case experiments work fine with expected results.
Code: QB64: [Select]
  1. _Title "tsh73 from JB: 2 Ball Collision with Vectors,  r will restart a test" 'B+ 2021-05-13
  2. ' This is tsh73 code for collision though he was doing stationary and moving, I made mod for both moving.
  3. ' The vector functions are modifications of tsh73 JB code converted to QB64 by me.
  4. ' earlier code 2 Ball Collision with Vectors by Arrow Addition ' B+ 2021-05-10
  5. ' 2021-05-10  fixed arrow so points from base x,y To angle
  6. ' from Collision Study #4   press spacebar to toggle tracer" ' b+ 2021-05-09 by bplus
  7.  
  8. ' !!!!!!!!!!!!!!!! Adjust Ball according to experiment (just after restart: line label) 2 is always good!
  9.  
  10. 'Const Balls = 2 '<<<<<<<<< change this to 2 to test special case setups: head on, parallel and perpendicular
  11. Const Balls = 6 '<<<<<<<<<  no more than 20! because have to start all balls not overlapping any other
  12.  
  13.  
  14. Const Xmax = 600 ' screen width
  15. Const Ymax = 600 ' screen height
  16. Const R = 50 '     balls radii
  17. Const R22 = R * R * 4 ' save some time with (2 * R) * (2 * R)
  18.  
  19. Const MV_Speed = 45 ' magnitude of Force Arrow say it's constant for simplicity sake
  20. Type Ball
  21.     As Long x, y
  22.     As Double dx, dy, a ' dx, dy = change x, y axis
  23.     As _Unsigned Long colr
  24.  
  25. Screen _NewImage(Xmax, Ymax, 32)
  26. _Delay .25
  27. ' these can be static as no balls added or subtracted in closed system
  28. Dim As Ball b(1 To Balls), nf(1 To Balls) ' b() is current frame balls data , nf( ) is for next frame balls data
  29. Dim As Long clrMode, i, j, rdm
  30. b(1).colr = &HFFFF0000 'red ball 1 is red
  31. b(2).colr = &HFF0000FF 'blue ball 2 is blue
  32. clrMode = 1
  33. Dim v1$, v2$, dv1$, dv2$, dv1u$, dv2u$, norm$ 'vectors
  34.  
  35. restart:
  36. 'horizontal collisions  Head On
  37. 'b(1).dx = 5
  38. 'b(1).dy = 0 ' b1 is just moving on x axis to the right at 5 pixels per frame
  39. 'b(2).dx = -5
  40. 'b(2).dy = 0 ' b2 is just moving on x axis to the left at 5 pixels per frame
  41. 'b(1).x = 300 - 5 * b(1).dx - R ' in 5 frames b1 will meet b2  kiss at center of screen
  42. 'b(1).y = 300
  43. 'b(2).x = 300 - 5 * b(2).dx + R ' in 5 frames b1 will meet b2  kiss at center of screen
  44. 'b(2).y = 300
  45.  
  46. 'vertical collisions  Head On
  47. 'b(1).dy = 5
  48. 'b(1).dx = 0 ' b1 is just moving on x axis to the right at 5 pixels per frame
  49. 'b(2).dy = -5
  50. 'b(2).dx = 0 ' b2 is just moving on x axis to the left at 5 pixels per frame
  51. 'b(1).y = 300 - 5 * b(1).dy - R ' in 5 frames b1 will meet b2  kiss at center of screen
  52. 'b(1).x = 300
  53. 'b(2).y = 300 - 5 * b(2).dy + R ' in 5 frames b1 will meet b2  kiss at center of screen
  54. 'b(2).x = 300
  55.  
  56. ' 45 & 135 collsions   Head On
  57. 'b(1).dy = 5
  58. 'b(1).dx = 5 ' b1 is just moving on x axis to the right at 5 pixels per frame
  59. 'b(2).dy = -5
  60. 'b(2).dx = -5 ' b2 is just moving on x axis to the left at 5 pixels per frame
  61. 'b(1).y = 300 - 5 * b(1).dy - R ' in 5 frames b1 will meet b2  kiss at center of screen
  62. 'b(1).x = 300 - 5 * b(1).dx - R
  63. 'b(2).y = 300 - 5 * b(2).dy + R ' in 5 frames b1 will meet b2  kiss at center of screen
  64. 'b(2).x = 300 - 5 * b(2).dx + R
  65.  
  66. ' Parallel and converging paths
  67. 'b(1).dy = -6
  68. 'b(1).dx = 1 ' b1 is just moving on x axis to the right at 5 pixels per frame
  69. 'b(2).dy = -6
  70. 'b(2).dx = -1 ' b2 is just moving on x axis to the left at 5 pixels per frame
  71. 'b(1).y = Ymax - R ' in 5 frames b1 will meet b2  kiss at center of screen
  72. 'b(1).x = 300 - 20 * b(1).dx - R
  73. 'b(2).y = Ymax - R ' in 5 frames b1 will meet b2  kiss at center of screen
  74. 'b(2).x = 300 - 20 * b(2).dx + R
  75.  
  76. ' Perpendicular and 90 degree collision   should be just before mid screen
  77. 'b(1).dy = 0
  78. 'b(1).dx = 5
  79. 'b(2).dy = -5
  80. 'b(2).dx = 0
  81. 'b(1).y = 300
  82. 'b(1).x = R
  83. 'b(2).y = Ymax - R
  84. 'b(2).x = 300
  85.  
  86. ' Random assignments    (comment this block for special case 2 ball experiments  and set balls to 2 !)  ==============
  87. For i = 1 To Balls
  88.     tryAgain:
  89.     Print i
  90.     b(i).x = rand&(R, Xmax - R)
  91.     b(i).y = rand&(R, Ymax - R)
  92.     For j = 1 To i - 1
  93.         If _Hypot(b(i).x - b(j).x, b(i).y - b(j).y) < R + R Then GoTo tryAgain
  94.     Next
  95.     b(i).dx = (Rnd * 4 + 1) * rdir
  96.     b(i).dy = (Rnd * 4 + 1) * rdir
  97.     rdm = rand&(1, 7)
  98.     If rdm < 1 Or rdm > 7 Then Beep
  99.     Select Case rdm
  100.         Case 1: b(i).colr = _RGB32(Rnd * 200 + 55, 0, 0)
  101.         Case 2: b(i).colr = _RGB32(0, Rnd * 100 + 55, 0)
  102.         Case 3: b(i).colr = _RGB32(0, 0, Rnd * 200 + 55)
  103.         Case 4: b(i).colr = &HFFFF6600
  104.         Case 5: b(i).colr = &HFFFF0088
  105.         Case 6: b(i).colr = &HFF00FF88
  106.         Case 7: b(i).colr = _RGB32(Rnd * 200 + 55, Rnd * 200 + 55, Rnd * 200 + 55)
  107.     End Select
  108. '====================================================================================================================
  109.  
  110. While _KeyDown(27) = 0
  111.     Dim k$
  112.     k$ = InKey$
  113.     If Len(k$) Then
  114.         If Asc(k$) = 32 Then clrMode = -1 * clrMode
  115.         If Asc(k$) = 27 And Len(k$) = 1 Then End
  116.         If k$ = "r" Then GoTo restart
  117.     End If
  118.     If clrMode > 0 Then Cls
  119.     Circle (300, 300), 2
  120.     For i = 1 To Balls ' draw balls then  update for next frame
  121.  
  122.         ' this just draw the balls with arrows pointing to their headings
  123.         Circle (b(i).x, b(i).y), R, b(i).colr
  124.         b(i).a = _Atan2(b(i).dy, b(i).dx)
  125.         ArrowTo b(i).x, b(i).y, b(i).a, MV_Speed, &HFFFFFF00
  126.         ' debug
  127.         '_PrintString (b(i).x - 8, b(i).y - 8), _Trim$(Right$("00" + Str$(i), 2)) 'all the balls weren't getting colored
  128.  
  129.         ' check for collision
  130.         Dim cd, saveJ
  131.         cd = 100000: saveJ = 0
  132.         For j = 1 To Balls 'find deepest collision in case more than one we want earliest = deepest penetration
  133.             If i <> j Then
  134.                 Dim dx, dy
  135.                 dx = b(i).x - b(j).x: dy = b(i).y - b(j).y
  136.                 If dx * dx + dy * dy <= R22 Then ' collision but is it first or deepest collision
  137.                     If R22 - dx * dx + dy * dy < cd Then cd = R22 - dx * dx + dy * dy: saveJ = j
  138.                 End If
  139.             End If
  140.         Next
  141.         If cd <> 100000 Then ' found collision change ball i dx, dy   calc new course for ball i
  142.  
  143.             ''reflection  from circle  using Vectors  from JB, thanks tsh73
  144.             v1$ = vect$(b(i).x, b(i).y) ' circle i
  145.             v2$ = vect$(b(saveJ).x, b(saveJ).y) ' the other circle j
  146.             dv1$ = vect$(b(i).dx, b(i).dy) ' change in velocity vector
  147.             dv2$ = vect$(b(saveJ).dx, b(saveJ).dy)
  148.             dv1u$ = vectUnit$(dv1$) '1 pixel
  149.             dv2u$ = vectUnit$(dv2$)
  150.             'Print dv$, cv$, dv0$   ' check on things
  151.             '_Display
  152.             'Sleep
  153.             Do ' this should back up the balls to kiss point thanks tsh73
  154.                 v1$ = vectSub$(v1$, dv1u$)
  155.                 v2$ = vectSub(v2$, dv2u$)
  156.             Loop While vectLen(vectSub$(v1$, v2$)) < R + R 'back up our circle i to point on kiss
  157.             ''now, get reflection
  158.             ''radius to radius, norm is
  159.             norm$ = vectSub$(v1$, v2$)
  160.             dv1$ = vectSub$(vectNorm$(dv1$, norm$), vectTangent$(dv1$, norm$)) 'to this
  161.  
  162.             ' store in next frame array
  163.             nf(i).dx = vectX(dv1$)
  164.             nf(i).dy = vectY(dv1$)
  165.  
  166.         Else ' no collision
  167.             nf(i).dx = b(i).dx
  168.             nf(i).dy = b(i).dy
  169.         End If
  170.         'update location of ball next frame
  171.         nf(i).x = b(i).x + nf(i).dx
  172.         nf(i).y = b(i).y + nf(i).dy
  173.  
  174.         ' check in bounds next frame
  175.         If nf(i).x < R Then nf(i).dx = -nf(i).dx: nf(i).x = R
  176.         If nf(i).x > Xmax - R Then nf(i).dx = -nf(i).dx: nf(i).x = Xmax - R
  177.         If nf(i).y < R Then nf(i).dy = -nf(i).dy: nf(i).y = R
  178.         If nf(i).y > Ymax - R Then nf(i).dy = -nf(i).dy: nf(i).y = Ymax - R
  179.     Next
  180.  
  181.     ''now that we've gone through all old locations update b() with nf() data
  182.     For i = 1 To Balls
  183.         b(i).x = nf(i).x: b(i).y = nf(i).y
  184.         b(i).dx = nf(i).dx: b(i).dy = nf(i).dy
  185.     Next
  186.     ' next frame ready to draw
  187.     _Display
  188.     _Limit 30
  189. 'Cls   ' debug why aren't all the balls getting colored
  190. 'For i = 1 To Balls
  191. '    Print i, b(i).colr
  192. 'Next
  193.  
  194. Function rand& (lo As Long, hi As Long) ' fixed?
  195.     rand& = Int(Rnd * (hi - lo) + 1) + lo
  196.  
  197. Function rdir ()
  198.     If Rnd < .5 Then rdir = -1 Else rdir = 1
  199.  
  200. Sub ArrowTo (BaseX As Long, BaseY As Long, rAngle As Double, lngth As Long, colr As _Unsigned Long)
  201.     Dim As Long x1, y1, x2, y2, x3, y3
  202.     x1 = BaseX + lngth * Cos(rAngle)
  203.     y1 = BaseY + lngth * Sin(rAngle)
  204.     x2 = BaseX + .8 * lngth * Cos(rAngle - _Pi(.05))
  205.     y2 = BaseY + .8 * lngth * Sin(rAngle - _Pi(.05))
  206.     x3 = BaseX + .8 * lngth * Cos(rAngle + _Pi(.05))
  207.     y3 = BaseY + .8 * lngth * Sin(rAngle + _Pi(.05))
  208.     Line (BaseX, BaseY)-(x1, y1), colr
  209.     Line (x1, y1)-(x2, y2), colr
  210.     Line (x1, y1)-(x3, y3), colr
  211.  
  212. ' convert some vector functions from JB, turns out much easier to use string functions to pass vectors
  213. ' than using SUBs to do vector calcs
  214.  
  215. Function vect$ (x, y) ' convert x, y to string for passing vectors with Functions
  216.     vect$ = _Trim$(Str$(x)) + "," + _Trim$(Str$(y))
  217.  
  218. Function vectX (v$)
  219.     vectX = Val(LeftOf$(v$, ","))
  220.  
  221. Function vectY (v$)
  222.     vectY = Val(RightOf$(v$, ","))
  223.  
  224. Function vectLen (v$)
  225.     Dim x, y
  226.     x = Val(LeftOf$(v$, ","))
  227.     y = Val(RightOf$(v$, ","))
  228.     vectLen = Sqr(x * x + y * y)
  229.  
  230. Function vectUnit$ (v$)
  231.     Dim x, y, vl
  232.     x = Val(LeftOf$(v$, ","))
  233.     y = Val(RightOf$(v$, ","))
  234.     vl = Sqr(x * x + y * y)
  235.     vectUnit$ = vect$(x / vl, y / vl)
  236.  
  237. Function vectAdd$ (v1$, v2$)
  238.     Dim x1, y1, x2, y2
  239.     x1 = Val(LeftOf$(v1$, ","))
  240.     y1 = Val(RightOf$(v1$, ","))
  241.     x2 = Val(LeftOf$(v2$, ","))
  242.     y2 = Val(RightOf$(v2$, ","))
  243.     vectAdd$ = vect$(x1 + x2, y1 + y2)
  244.  
  245. Function vectSub$ (v1$, v2$)
  246.     Dim x1, y1, x2, y2
  247.     x1 = Val(LeftOf$(v1$, ","))
  248.     y1 = Val(RightOf$(v1$, ","))
  249.     x2 = Val(LeftOf$(v2$, ","))
  250.     y2 = Val(RightOf$(v2$, ","))
  251.     vectSub$ = vect$(x1 - x2, y1 - y2)
  252.  
  253. Function vectDotProduct (v1$, v2$)
  254.     Dim x1, y1, x2, y2
  255.     x1 = Val(LeftOf$(v1$, ","))
  256.     y1 = Val(RightOf$(v1$, ","))
  257.     x2 = Val(LeftOf$(v2$, ","))
  258.     y2 = Val(RightOf$(v2$, ","))
  259.     vectDotProduct = x1 * x2 + y1 * y2
  260.  
  261. Function vectScale$ (a, v$) 'a * vector v$
  262.     Dim x, y
  263.     x = Val(LeftOf$(v$, ","))
  264.     y = Val(RightOf$(v$, ","))
  265.     vectScale$ = vect$(a * x, a * y)
  266.  
  267. Function vectTangent$ (v$, base$)
  268.     Dim n$
  269.     n$ = vectUnit$(base$)
  270.     vectTangent$ = vectScale$(vectDotProduct(n$, v$), n$)
  271.  
  272. Function vectNorm$ (v$, base$)
  273.     vectNorm$ = vectSub$(v$, vectTangent$(v$, base$))
  274.  
  275. ' update these 2 in case of$ is not found! 2021-02-13
  276. Function LeftOf$ (source$, of$)
  277.     If InStr(source$, of$) > 0 Then LeftOf$ = Mid$(source$, 1, InStr(source$, of$) - 1) Else LeftOf$ = source$
  278.  
  279. ' update these 2 in case of$ is not found! 2021-02-13
  280. Function RightOf$ (source$, of$)
  281.     If InStr(source$, of$) > 0 Then RightOf$ = Mid$(source$, InStr(source$, of$) + Len(of$)) Else RightOf$ = ""
  282.  
  283.  
  284.  

« Last Edit: May 13, 2021, 04:46:50 pm by bplus »

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #26 on: May 13, 2021, 04:42:37 pm »
Nice. Seems to work pretty good. Occasionally two balls would get stuck on each other, but awesome progress. Physics is hard! 

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #27 on: May 13, 2021, 04:59:29 pm »
Oh man you are getting sticky balls with that code! Well this next is much more fragile keep number of balls low and not too bad, it does really interesting things with ball speeds!

This is my attempt to follow the paper OldMoses gave link to, see ref in code:
Code: QB64: [Select]
  1. _Title "From Paper 2 Ball Collision with Vectors,  press r to restart" 'B+ 2021-05-13
  2. ' Collision tests based on Paper       https://www.vobarian.com/collisions/2dcollisions2.pdf
  3. ' The vector functions are modifications of tsh73 JB code converted to QB64 by me.
  4. ' earlier code 2 Ball Collision with Vectors by Arrow Addition ' B+ 2021-05-10
  5. ' 2021-05-10  fixed arrow so points from base x,y To angle
  6. ' from Collision Study #4   press spacebar to toggle tracer" ' b+ 2021-05-09
  7.  
  8. ' !!!!!!!!!!!!!!!! Adjust Ball according to experiment 2 is always good
  9.  
  10. 'Const Balls = 2 '<<<<<<<<< change this to 2 to test special case setups: head on, parallel and perpendicular
  11. Const Balls = 5 '<<<<<<<<< the more the balls the more problems pop up for Random Numbers experiments
  12. '                            no more than 20! because have to start all balls not overlapping any other
  13.  
  14.  
  15. Const Xmax = 600 ' screen width
  16. Const Ymax = 600 ' screen height
  17. Const R = 50 '     balls radii
  18. Const R22 = R * R * 4 ' save some time with (2 * R) * (2 * R)
  19.  
  20. Const MV_Speed = 45 ' magnitude of Force Arrow say it's constant for simplicity sake
  21. Type Ball
  22.     As Long x, y
  23.     As Double dx, dy, a ' dx, dy = change x, y axis
  24.     As _Unsigned Long colr
  25.  
  26. Screen _NewImage(Xmax, Ymax, 32)
  27. _Delay .25
  28. ' these can be static as no balls added or subtracted in closed system
  29. Dim As Ball b(1 To Balls), nf(1 To Balls) ' b() is current frame balls data , nf( ) is for next frame balls data
  30. Dim As Long clrMode, i, j, rdm
  31. b(1).colr = &HFFFF0000 'red ball 1 is red
  32. b(2).colr = &HFF0000FF 'blue ball 2 is blue
  33. clrMode = 1
  34. Dim v1$, v2$, dv1$, dv2$, dv1u$, dv2u$, norm$, unitNorm$, unitTan$ 'vectors
  35. Dim vp1n$, vp1t$, vp2n$, vp2t$ ' post collision vectors
  36. Dim As Double v1n, v1t, v2n, v2t ' dot products
  37. Dim As Double vp1n, vp1t, vp2n, vp2t ' post collision dot products
  38. restart:
  39. 'horizontal collisions  Head On
  40. 'b(1).dx = 5
  41. 'b(1).dy = 0 ' b1 is just moving on x axis to the right at 5 pixels per frame
  42. 'b(2).dx = -5
  43. 'b(2).dy = 0 ' b2 is just moving on x axis to the left at 5 pixels per frame
  44. 'b(1).x = 300 - 5 * b(1).dx - R ' in 5 frames b1 will meet b2  kiss at center of screen
  45. 'b(1).y = 300
  46. 'b(2).x = 300 - 5 * b(2).dx + R ' in 5 frames b1 will meet b2  kiss at center of screen
  47. 'b(2).y = 300
  48.  
  49. 'vertical collisions  Head On
  50. 'b(1).dy = 5
  51. 'b(1).dx = 0 ' b1 is just moving on x axis to the right at 5 pixels per frame
  52. 'b(2).dy = -5
  53. 'b(2).dx = 0 ' b2 is just moving on x axis to the left at 5 pixels per frame
  54. 'b(1).y = 300 - 5 * b(1).dy - R ' in 5 frames b1 will meet b2  kiss at center of screen
  55. 'b(1).x = 300
  56. 'b(2).y = 300 - 5 * b(2).dy + R ' in 5 frames b1 will meet b2  kiss at center of screen
  57. 'b(2).x = 300
  58.  
  59. ' 45 & 135 collsions   Head On
  60. 'b(1).dy = 5
  61. 'b(1).dx = 5 ' b1 is just moving on x axis to the right at 5 pixels per frame
  62. 'b(2).dy = -5
  63. 'b(2).dx = -5 ' b2 is just moving on x axis to the left at 5 pixels per frame
  64. 'b(1).y = 300 - 5 * b(1).dy - R ' in 5 frames b1 will meet b2  kiss at center of screen
  65. 'b(1).x = 300 - 5 * b(1).dx - R
  66. 'b(2).y = 300 - 5 * b(2).dy + R ' in 5 frames b1 will meet b2  kiss at center of screen
  67. 'b(2).x = 300 - 5 * b(2).dx + R
  68.  
  69. ' Parallel and converging paths
  70. 'b(1).dy = -6
  71. 'b(1).dx = 1 ' b1 is just moving on x axis to the right at 5 pixels per frame
  72. 'b(2).dy = -6
  73. 'b(2).dx = -1 ' b2 is just moving on x axis to the left at 5 pixels per frame
  74. 'b(1).y = Ymax - R ' in 5 frames b1 will meet b2  kiss at center of screen
  75. 'b(1).x = 300 - 20 * b(1).dx - R
  76. 'b(2).y = Ymax - R ' in 5 frames b1 will meet b2  kiss at center of screen
  77. 'b(2).x = 300 - 20 * b(2).dx + R
  78.  
  79. ' Perpendicular and 90 degree collision   should be just before mid screen
  80. 'b(1).dy = 0
  81. 'b(1).dx = 5
  82. 'b(2).dy = -5
  83. 'b(2).dx = 0
  84. 'b(1).y = 300
  85. 'b(1).x = R
  86. 'b(2).y = Ymax - R
  87. 'b(2).x = 300
  88.  
  89. ' random assignments
  90. For i = 1 To Balls
  91.     tryAgain:
  92.     Print i
  93.     b(i).x = rand&(R, Xmax - R)
  94.     b(i).y = rand&(R, Ymax - R)
  95.     For j = 1 To i - 1
  96.         If _Hypot(b(i).x - b(j).x, b(i).y - b(j).y) < R + R Then GoTo tryAgain
  97.     Next
  98.     b(i).dx = (Rnd * 4 + 1) * rdir
  99.     b(i).dy = (Rnd * 4 + 1) * rdir
  100.     rdm = rand&(1, 7)
  101.     If rdm < 1 Or rdm > 7 Then Beep
  102.     Select Case rdm
  103.         Case 1: b(i).colr = _RGB32(Rnd * 200 + 55, 0, 0)
  104.         Case 2: b(i).colr = _RGB32(0, Rnd * 100 + 55, 0)
  105.         Case 3: b(i).colr = _RGB32(0, 0, Rnd * 200 + 55)
  106.         Case 4: b(i).colr = &HFFFF6600
  107.         Case 5: b(i).colr = &HFFFF0088
  108.         Case 6: b(i).colr = &HFF00FF88
  109.         Case 7: b(i).colr = _RGB32(Rnd * 200 + 55, Rnd * 200 + 55, Rnd * 200 + 55)
  110.     End Select
  111.  
  112. While _KeyDown(27) = 0
  113.     Dim k$
  114.     k$ = InKey$
  115.     If Len(k$) Then
  116.         If Asc(k$) = 32 Then clrMode = -1 * clrMode
  117.         If Asc(k$) = 27 And Len(k$) = 1 Then End
  118.         If k$ = "r" Then GoTo restart
  119.     End If
  120.     If clrMode > 0 Then Cls
  121.     Circle (300, 300), 2
  122.     For i = 1 To Balls ' draw balls then  update for next frame
  123.  
  124.         ' this just draw the balls with arrows pointing to their headings
  125.         Circle (b(i).x, b(i).y), R, b(i).colr
  126.         b(i).a = _Atan2(b(i).dy, b(i).dx)
  127.         ArrowTo b(i).x, b(i).y, b(i).a, MV_Speed, &HFFFFFF00
  128.         ' debug
  129.         '_PrintString (b(i).x - 8, b(i).y - 8), _Trim$(Right$("00" + Str$(i), 2)) 'all the balls weren't getting colored
  130.  
  131.         ' check for collision
  132.         Dim cd, saveJ
  133.         cd = 100000: saveJ = 0
  134.         For j = 1 To Balls 'find deepest collision in case more than one we want earliest = deepest penetration
  135.             If i <> j Then
  136.                 Dim dx, dy
  137.                 dx = b(i).x - b(j).x: dy = b(i).y - b(j).y
  138.                 If dx * dx + dy * dy <= R22 Then ' collision but is it first or deepest collision
  139.                     If R22 - dx * dx + dy * dy < cd Then cd = R22 - dx * dx + dy * dy: saveJ = j
  140.                 End If
  141.             End If
  142.         Next
  143.         If cd <> 100000 Then ' found collision change ball i dx, dy   calc new course for ball i
  144.  
  145.             ''reflection  from circle  using Vectors  from JB, thanks tsh73
  146.             v1$ = vect$(b(i).x, b(i).y) ' circle i
  147.             v2$ = vect$(b(saveJ).x, b(saveJ).y) ' the other circle j
  148.             dv1$ = vect$(b(i).dx, b(i).dy) ' change in velocity vector
  149.             dv2$ = vect$(b(saveJ).dx, b(saveJ).dy)
  150.             dv1u$ = vectUnit$(dv1$) '1 pixel
  151.             dv2u$ = vectUnit$(dv2$)
  152.             'Print dv$, cv$, dv0$   ' check on things
  153.             '_Display
  154.             'Sleep
  155.             Do ' this should back up the balls to kiss point thanks tsh73
  156.                 v1$ = vectSub$(v1$, dv1u$)
  157.                 v2$ = vectSub(v2$, dv2u$)
  158.             Loop While vectLen(vectSub$(v1$, v2$)) < R + R 'back up our circle i to point on kiss
  159.             ''now, get reflection speed
  160.             ''radius to radius, norm is
  161.             norm$ = vectSub$(v1$, v2$) ' this to this worked without all between from that collision paper
  162.  
  163.             '  step 1 unit norm and tangent
  164.             unitNorm$ = vectUnit$(norm$)
  165.             unitTan$ = vect$(-vectY(unitNorm$), vectX(unitNorm$))
  166.             ' step 2 v$ and cv$ are 2 ball vectors (locations)  done already
  167.             ' step 3 dot products before collision projecting onto normal and tangent vectors
  168.             v1n = vectDotProduct(dv1$, unitNorm$)
  169.             v1t = vectDotProduct(dv1$, unitTan$)
  170.             v2n = vectDotProduct(dv2$, unitNorm$)
  171.             v2t = vectDotProduct(dv2$, unitTan$)
  172.             ' step 4 simplest  post collision dot products
  173.             vp1t = v1t
  174.             vp2t = v2t
  175.             ' step 5  simplified by m = 1 for both balls just swap the numbers
  176.             vp1n = v2n
  177.             vp2n = v1n
  178.             ' step 6  vp vectors mult the n, t numbers by unit vectors
  179.             vp1n$ = vectScale$(vp1n, unitNorm$)
  180.             vp1t$ = vectScale$(vp1t, unitTan$)
  181.             vp2n$ = vectScale$(vp2n, unitNorm$)
  182.             vp2t$ = vectScale$(vp2t, unitTan$)
  183.             'step  7  add the 2 vectors n and t
  184.             dv1$ = vectAdd$(vp1n$, vp1t$)
  185.  
  186.             ' to this  now just switch tangent and norm
  187.             'dv1$ = vectSub$(vectNorm$(dv1$, norm$), vectTangent$(dv1$, norm$)) 'to this
  188.  
  189.             ' store in next frame array
  190.             nf(i).dx = vectX(dv1$)
  191.             nf(i).dy = vectY(dv1$)
  192.  
  193.         Else ' no collision
  194.             nf(i).dx = b(i).dx
  195.             nf(i).dy = b(i).dy
  196.         End If
  197.         'update location of ball next frame
  198.         nf(i).x = b(i).x + nf(i).dx
  199.         nf(i).y = b(i).y + nf(i).dy
  200.  
  201.         ' check in bounds next frame
  202.         If nf(i).x < R Then nf(i).dx = -nf(i).dx: nf(i).x = R
  203.         If nf(i).x > Xmax - R Then nf(i).dx = -nf(i).dx: nf(i).x = Xmax - R
  204.         If nf(i).y < R Then nf(i).dy = -nf(i).dy: nf(i).y = R
  205.         If nf(i).y > Ymax - R Then nf(i).dy = -nf(i).dy: nf(i).y = Ymax - R
  206.     Next
  207.  
  208.     ''now that we've gone through all old locations update b() with nf() data
  209.     For i = 1 To Balls
  210.         b(i).x = nf(i).x: b(i).y = nf(i).y
  211.         b(i).dx = nf(i).dx: b(i).dy = nf(i).dy
  212.     Next
  213.     ' next frame ready to draw
  214.     _Display
  215.     _Limit 30
  216. 'Cls   ' debug why aren't all the balls getting colored
  217. 'For i = 1 To Balls
  218. '    Print i, b(i).colr
  219. 'Next
  220.  
  221.  
  222. Function rand& (lo As Long, hi As Long) ' fixed?
  223.     rand& = Int(Rnd * (hi - lo) + 1) + lo
  224.  
  225. Function rdir ()
  226.     If Rnd < .5 Then rdir = -1 Else rdir = 1
  227.  
  228.  
  229.  
  230. Sub ArrowTo (BaseX As Long, BaseY As Long, rAngle As Double, lngth As Long, colr As _Unsigned Long)
  231.     Dim As Long x1, y1, x2, y2, x3, y3
  232.     x1 = BaseX + lngth * Cos(rAngle)
  233.     y1 = BaseY + lngth * Sin(rAngle)
  234.     x2 = BaseX + .8 * lngth * Cos(rAngle - _Pi(.05))
  235.     y2 = BaseY + .8 * lngth * Sin(rAngle - _Pi(.05))
  236.     x3 = BaseX + .8 * lngth * Cos(rAngle + _Pi(.05))
  237.     y3 = BaseY + .8 * lngth * Sin(rAngle + _Pi(.05))
  238.     Line (BaseX, BaseY)-(x1, y1), colr
  239.     Line (x1, y1)-(x2, y2), colr
  240.     Line (x1, y1)-(x3, y3), colr
  241.  
  242. ' convert some vector functions from JB, turns out much easier to use string functions to pass vectors
  243. ' than using SUBs to do vector calcs
  244.  
  245. Function vect$ (x, y) ' convert x, y to string for passing vectors with Functions
  246.     vect$ = _Trim$(Str$(x)) + "," + _Trim$(Str$(y))
  247.  
  248. Function vectX (v$)
  249.     vectX = Val(LeftOf$(v$, ","))
  250.  
  251. Function vectY (v$)
  252.     vectY = Val(RightOf$(v$, ","))
  253.  
  254. Function vectLen (v$)
  255.     Dim x, y
  256.     x = Val(LeftOf$(v$, ","))
  257.     y = Val(RightOf$(v$, ","))
  258.     vectLen = Sqr(x * x + y * y)
  259.  
  260. Function vectUnit$ (v$)
  261.     Dim x, y, vl
  262.     x = Val(LeftOf$(v$, ","))
  263.     y = Val(RightOf$(v$, ","))
  264.     vl = Sqr(x * x + y * y)
  265.     vectUnit$ = vect$(x / vl, y / vl)
  266.  
  267. Function vectAdd$ (v1$, v2$)
  268.     Dim x1, y1, x2, y2
  269.     x1 = Val(LeftOf$(v1$, ","))
  270.     y1 = Val(RightOf$(v1$, ","))
  271.     x2 = Val(LeftOf$(v2$, ","))
  272.     y2 = Val(RightOf$(v2$, ","))
  273.     vectAdd$ = vect$(x1 + x2, y1 + y2)
  274.  
  275. Function vectSub$ (v1$, v2$)
  276.     Dim x1, y1, x2, y2
  277.     x1 = Val(LeftOf$(v1$, ","))
  278.     y1 = Val(RightOf$(v1$, ","))
  279.     x2 = Val(LeftOf$(v2$, ","))
  280.     y2 = Val(RightOf$(v2$, ","))
  281.     vectSub$ = vect$(x1 - x2, y1 - y2)
  282.  
  283. Function vectDotProduct (v1$, v2$)
  284.     Dim x1, y1, x2, y2
  285.     x1 = Val(LeftOf$(v1$, ","))
  286.     y1 = Val(RightOf$(v1$, ","))
  287.     x2 = Val(LeftOf$(v2$, ","))
  288.     y2 = Val(RightOf$(v2$, ","))
  289.     vectDotProduct = x1 * x2 + y1 * y2
  290.  
  291. Function vectScale$ (a, v$) 'a * vector v$
  292.     Dim x, y
  293.     x = Val(LeftOf$(v$, ","))
  294.     y = Val(RightOf$(v$, ","))
  295.     vectScale$ = vect$(a * x, a * y)
  296.  
  297. Function vectTangent$ (v$, base$)
  298.     Dim n$
  299.     n$ = vectUnit$(base$)
  300.     vectTangent$ = vectScale$(vectDotProduct(n$, v$), n$)
  301.  
  302. Function vectNorm$ (v$, base$)
  303.     vectNorm$ = vectSub$(v$, vectTangent$(v$, base$))
  304.  
  305. ' update these 2 in case of$ is not found! 2021-02-13
  306. Function LeftOf$ (source$, of$)
  307.     If InStr(source$, of$) > 0 Then LeftOf$ = Mid$(source$, 1, InStr(source$, of$) - 1) Else LeftOf$ = source$
  308.  
  309. ' update these 2 in case of$ is not found! 2021-02-13
  310. Function RightOf$ (source$, of$)
  311.     If InStr(source$, of$) > 0 Then RightOf$ = Mid$(source$, InStr(source$, of$) + Len(of$)) Else RightOf$ = ""
  312.  
  313.  
  314.  

PS It also passed all the special case collision tests: head on (3 angles), parallel converging and perpendicular.
The more balls the more sticky ball syndrome and sometime code will hang because not calculating fast enough for _limit to keep it smooth? But as I said really interesting, realistic?, changes in speed from collisions.

PPS make sure you have Const Balls set to 2 for all the Special Case Tests and comment last experiment and uncomment your next experiment.
« Last Edit: May 13, 2021, 05:06:33 pm by bplus »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #28 on: May 13, 2021, 07:13:04 pm »
That looks pretty good. I like how it handles balls getting into tight bunches, which was the downfall of my billiards program.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #29 on: May 14, 2021, 04:07:08 pm »
That looks pretty good. I like how it handles balls getting into tight bunches, which was the downfall of my billiards program.

Yes, I was very pleasantly surprised the ball action could stun balls into stillness or nearly so and then be made active again with just another little bump. The tsh73 version (which may never have been intended for 2 moving balls) and my own ball collision with trig only always have balls moving. My trig version you could start with overlapping balls and they will sort themselves and be clear of each other in a few frames, never a sticky ball but I never got balls stunned to stillness. It's a nice effect hinting I should do some more work trying to speed up the system.

How could I loose the $ Functions and use numbers and still have functions for vectors?
Yeah go ahead and say FB LOL!