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

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #45 on: May 20, 2021, 06:45:50 pm »
Quote
Don't you have to look at the angle the balls are at to each other, because the tangent line they will reflect from (just like light off a surface) is perpendicular to the angle the balls are at when they kiss. Angle in = Angle out off that tangent line at kiss point. Easy to describe a real b... to get mathematically!

For a ball striking a wall, ya, it's angle in, angle out.

Trying to code an interactive tool that will show two circles at the point of contact.   The strike vector will be rotatable by visual means of two circles rotating about 0x,0y.  (80 pixels from center to center)The bisection line is shown. As strike vector is rotated the mvector will move. Also shown are the final ball vectors.   I thought this tool would be a better way to show ball collisions without any moving balls.

As strike vector is rotated, mvector will line up with the bisection line which is also bvector
 Well I have not even started coding this yet.





« Last Edit: May 20, 2021, 06:54:11 pm by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #46 on: May 20, 2021, 07:35:09 pm »
Speaking of interactive tools. I've been trying to wrap my poor tender head around this algorithm for quite some time and thought that something like that would be helpful to watch the effects of various changes to the equations. Maybe someone can make use of some or all of the following code, and/or substitute their own math functions, as mine are as useful as a turd in a public pool at the moment... ;)  But of course that's why I need this. It's cobbled together from the bones of what I posted earlier.

Anyway, the idea is that you can left click on the ends of the solid line vectors (i.e. incoming vector) and move them around and see what applied algorithms effects are on the exit paths. It acts a bit squirrely and you might have to do a pre-click on a vertex to sort of wake it up that its not a previous move continued. I'm not terribly familiar with Steve's MBS function for drag and drop ops, but figured that there's no better time than the present to play with it.

Moving the ball centers changes their radii, maintaining equivalent size, as I never conceived this as anything beyond identical billiard ball type stuff. It also redefines the coordinate system with WINDOW as Bplus mentioned earlier.

EDIT: Ah! I've discovered the secret to using my own code. Get near the desired vertex, click and hold until the line jumps to the mouse cursor. I guess I wasn't giving time for the delay parameter in Steve's MBS function.

Code: QB64: [Select]
  1. TYPE V2
  2.     x AS SINGLE
  3.     y AS SINGLE
  4.  
  5. TYPE ball
  6.     cn AS STRING * 4 '                                          ball name by color
  7.     c AS _UNSIGNED LONG '                                       color
  8.     p AS V2 '                                                   position
  9.     m AS V2 '                                                   movement (pre-contact) incoming
  10.     u AS V2 '                                                   unit vector of ball.m
  11.     x AS V2 '                                                   vector of post contact movement
  12.     s AS INTEGER '                                              magnitude of movement
  13.     sd AS SINGLE '                                              strike dot
  14.  
  15. DIM AS V2 mvec, strike
  16. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  17. DIM mouse AS V2
  18. DIM b(1) AS ball
  19. DIM SHARED AS V2 origin
  20. origin.x = 0: origin.y = 0
  21. b(0).c = Red: b(0).cn = "red"
  22. b(1).c = Cyan: b(1).cn = "cyan"
  23.  
  24. SCREEN _NEWIMAGE(600, 600, 32)
  25. WINDOW (-300, 300)-(300, -300)
  26. 'These are the approach vectors you give
  27. b(0).m.x = 0: b(0).m.y = -100 '                                 reds approach vector
  28. b(0).s = PyT(origin, b(0).m) '                                  reds magnitude (speed or force)
  29. b(1).m.x = 100: b(1).m.y = -100 '                               cyans approach vector
  30. b(1).s = PyT(origin, b(1).m) '                                  cyans magnitude (speed or force)
  31.  
  32. 'The important part is, where are the balls when they make contact. This determines the strike vector between them
  33. 'choosing arbitrary points of contact... change these around to see the effect to verify its accuracy
  34. b(0).p.x = 30: b(0).p.y = 10
  35. b(1).p.x = -17: b(1).p.y = 0
  36.  
  37.     ms = MBS '                                                  process mouse actions dragging endpoints
  38.     IF ms AND 64 THEN
  39.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  40.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  41.         FOR x = 0 TO 1
  42.             FOR y = 0 TO 1
  43.                 ds! = PyT(vertex(x, y), mouse)
  44.                 IF ds! < ballradius * .5 THEN i = x: j = y
  45.             NEXT y
  46.         NEXT x
  47.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  48.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  49.                 b(i).p = mouse
  50.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  51.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  52.         END SELECT
  53.     ELSE
  54.     END IF
  55.  
  56.  
  57.     'START OF COLLISION MATHEMATICS SECTION
  58.     ballradius = PyT(b(0).p, b(1).p) / 2
  59.     CLS
  60.  
  61.     FOR bn = 0 TO 1
  62.         other = ABS(NOT -bn) '                                  if we're on 0 the other is 1 and vice versa
  63.         b(bn).u = b(bn).m: VecNorm b(bn).u '                    normalize the movement vector
  64.         strike.x = b(other).p.x - b(bn).p.x '                   obtain a strike vector
  65.         strike.y = b(other).p.y - b(bn).p.y
  66.         ShearOrthoUnit mvec, b(bn).m, strike '                  strike is converted to a unit vector and mvec is orthogonal unit of strike
  67.         b(bn).x = mvec '                                        keep the result of mvec in the exit vector, we'll grow it in the next loop
  68.  
  69.         'now this is what we're really shooting for
  70.         b(bn).sd = VecDot(b(bn).u, strike)
  71.  
  72.         'establish the mouse handles for scenario manipulations
  73.         vertex(bn, 0) = b(bn).p
  74.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1
  75.     NEXT bn
  76.     'we finish the above FOR/NEXT loop so that we have all respective mvec's stored in b(#).x
  77.     'then we rerun the loop, converting b(#).x to a full exit vector
  78.     FOR bn = 0 TO 1
  79.         other = ABS(NOT -bn)
  80.         b(bn).x.x = b(bn).x.x * (b(bn).s * b(other).sd)
  81.         b(bn).x.y = b(bn).x.y * (b(bn).s * b(other).sd)
  82.         'PRINT b(bn).cn; " exits at <"; b(bn).x.x; ", "; b(bn).x.y; ">"
  83.     NEXT bn
  84.     'END OF COLLISION MATHEMATICS SECTION
  85.  
  86.     'graphic representation
  87.     FOR grid = -300 TO 300 STEP 20
  88.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  89.         LINE (grid, 300)-(grid, -300), c& 'Gray
  90.         LINE (-300, grid)-(300, grid), c& ' Gray
  91.     NEXT grid
  92.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  93.     FOR dr = 0 TO 1
  94.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c
  95.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x - b(dr).m.x, b(dr).p.y - b(dr).m.y), b(dr).c 'incoming
  96.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + b(dr).x.x, b(dr).p.y + b(dr).x.y), b(dr).c, , &B1111000011110000 'exit vector
  97.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  98.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  99.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">"
  100.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  101.     NEXT dr
  102.  
  103.     _LIMIT 50
  104.     _DISPLAY
  105.  
  106.  
  107. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  108.     STATIC StartTimer AS _FLOAT
  109.     STATIC ButtonDown AS INTEGER
  110.     STATIC ClickCount AS INTEGER
  111.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  112.     '                          Down longer counts as a HOLD event.
  113.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  114.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  115.         SELECT CASE SGN(_MOUSEWHEEL)
  116.             CASE 1: MBS = MBS OR 512
  117.             CASE -1: MBS = MBS OR 1024
  118.         END SELECT
  119.     WEND
  120.  
  121.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  122.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  123.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  124.  
  125.     IF StartTimer = 0 THEN
  126.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  127.             ButtonDown = 1: StartTimer = TIMER(0.01)
  128.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  129.         ELSEIF _MOUSEBUTTON(2) THEN
  130.             ButtonDown = 2: StartTimer = TIMER(0.01)
  131.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  132.         ELSEIF _MOUSEBUTTON(3) THEN
  133.             ButtonDown = 3: StartTimer = TIMER(0.01)
  134.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  135.         END IF
  136.     ELSE
  137.         BD = ButtonDown MOD 3
  138.         IF BD = 0 THEN BD = 3
  139.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  140.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  141.         ELSE
  142.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  143.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  144.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  145.             ELSE 'We've now started the hold event
  146.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  147.             END IF
  148.         END IF
  149.     END IF
  150.  
  151.  
  152. FUNCTION PyT (var1 AS V2, var2 AS V2)
  153.  
  154.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  155.  
  156.  
  157.  
  158. SUB ShearOrthoUnit (nv AS V2, in AS V2, stk AS V2)
  159.  
  160.     DIM AS V2 or1, or2
  161.     VecNorm stk '                                               normalize the strike vector
  162.     or1.x = stk.y: or1.y = -stk.x '                             obtain two potential orthogonal vectors {or1 & or2} of strike
  163.     or2.x = -stk.y: or2.y = stk.x
  164.     dot = VecDot(in, or1) '                                     dot product one of them with the incoming vector
  165.     nv.x = -or2.x * (dot < 0) - stk.x * (dot = 0) - or1.x * (dot > 0) 'exit vector x component
  166.     nv.y = -or2.y * (dot < 0) - stk.y * (dot = 0) - or1.y * (dot > 0) 'exit vector y component
  167.  
  168. END SUB 'ShearOrthoUnit
  169.  
  170.  
  171. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  172.  
  173.     var.x = var.x + (var2.x * var3)
  174.     var.y = var.y + (var2.y * var3)
  175.  
  176. END SUB 'Add_Vector
  177.  
  178.  
  179. SUB VecNorm (var AS V2)
  180.  
  181.     m = PyT(origin, var)
  182.     IF m = 0 THEN
  183.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  184.     ELSE
  185.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  186.     END IF
  187.  
  188. END SUB 'VecNorm
  189.  
  190.  
  191. FUNCTION VecDot (var AS V2, var2 AS V2)
  192.  
  193.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  194.  
  195. END FUNCTION 'VecDot
  196.  
  197.  
  198. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  199.  
  200.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  201.  
  202.  
  203.  
« Last Edit: May 20, 2021, 07:54:20 pm by OldMoses »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #47 on: May 20, 2021, 09:11:16 pm »
OldMoses hey  that  is what I was thinking of.  Great code example.


Are the dashed lines the final ball vectors or are they ball vectors before collision.

Bplus we are both wrong, according to OldMoses simulator the red and blue balls head off in a PARALLEL direction. Both balls have a final heading of north  east. Adjust the strike vector so it is at right angles to a line bisecting the angle between red and blue before collision.

(LATER (never mined what I just said, I misinterpreted what the dashed lines meant)
« Last Edit: May 21, 2021, 01:15:54 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #48 on: May 20, 2021, 09:24:16 pm »
OldMoses hey  that  is what I was thinking of.  Great code example.


Are the dashed lines the final ball vectors or are they ball vectors before collision.

Bplus we are both wrong, according to OldMoses simulator the red and blue balls head off in a PARALLEL direction. Both balls have a final heading of north  east. Adjust the strike vector so it is at right angles to a line bisecting the angle between red and blue before collision.

Thanks, yes the dashed lines are are "supposed" to be the mvectors, but I have my doubts about the math as one can set up some wierd scenarios. I think my math needs a good deal more work on force transference between the balls, but I'm fairly confident that the graphics part is working properly.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #49 on: May 20, 2021, 09:47:04 pm »
Ok scrap whaat I said before.  I kinda see what you've done. Each ball has it's own mvector.  My view of mvector is that it originates at the contact point between balls.  SO maybe 2 mvectors are needed but I was hoping that a single mvector would be enough.

There is an very interesting collision.

say red ball heads -80x, 0y towards 0, 0  and blue ball heads  0x, -80y towards 0, 0 with strike vector along y axis

according to the simulator the red ball does not change direction but the blue ball  reverses?? direction
****
Im not sure if the blue ball reverses direction or comes to a stop. Also does the red ball speed up?
****

If the red ball had been stationary it would head off  in the direction of the blue ball  and the blue ball would come to a stop

****

Another thing it looks like the strike vector is ALWAYS at a right angle to the mvectors which looks to be correct
« Last Edit: May 21, 2021, 01:12:11 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #50 on: May 21, 2021, 12:31:59 am »

There is an very interesting collision.

say red ball heads -80x, 0y towards 0, 0  and blue ball heads  0x, -80y towards 0, 0 with strike vector along y axis

according to the simulator the red ball does not change direction but the blue ball  reverses direction.

If the red ball had been stationary it would head off  in the direction of the blue ball  and the blue ball would come to a stop

That's where my algorithm isn't working as I would expect, another thing that jumped out at me was head on collisions, which should rebound like that, but the program shows them rebounding at right angles.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #51 on: May 21, 2021, 01:07:19 am »
Here is one where it is almost correct.

red @ (-100, 0)    along (100, 0)    exit along (50, 50)   should be (70, 70)   ?         

cyan @ (0, -100)  along (0, 100)    exit along ((70, 70) correct


70 approx 5000^.5

I looked at the head on collisions and that is basically correct. If you fix the above one then that'll look good too.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #52 on: May 21, 2021, 08:13:20 am »
I've gone back to the Berchek paper instead of my own crazy experiments and came up with this variation. While I think the math is getting better as the magnitudes seem to be right, there seems to be an inversion in the code somewhere. The exits consistantly converge instead of rebounding.

Interesting results though...

EDIT:
Even better, adding IF...THEN starting at line 78 seems to have corrected the inversion. Berchek was correct, you only need one strike vector. The second one just confused the issue.

Code: QB64: [Select]
  1. TYPE V2
  2.     x AS SINGLE
  3.     y AS SINGLE
  4.  
  5. TYPE ball
  6.     cn AS STRING * 4 '                                          ball name by color
  7.     c AS _UNSIGNED LONG '                                       color
  8.     p AS V2 '                                                   position
  9.     m AS V2 '                                                   movement (pre-contact) incoming
  10.     um AS V2 '                                                  unit vector of ball.m
  11.     x AS V2 '                                                   vector of post contact movement
  12.     un AS V2 '                                                  unit normal; obtained from strike
  13.     ut AS V2 '                                                  unit tangent; reciprocated from un
  14.     nci AS SINGLE '                                             normal component of input velocity
  15.     tci AS SINGLE '                                             tangential component of input velocity
  16.     ncx AS SINGLE '                                             normal component of exit velocity
  17.     tcx AS SINGLE '                                             tangential component of exit velocity
  18.     s AS INTEGER '                                              magnitude of movement
  19.     sd AS SINGLE '                                              strike dot
  20.  
  21. DIM AS V2 strike, norcom, tancom
  22. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  23. DIM mouse AS V2
  24. DIM b(1) AS ball
  25. DIM SHARED AS V2 origin
  26. origin.x = 0: origin.y = 0
  27. b(0).c = Red: b(0).cn = "red"
  28. b(1).c = Cyan: b(1).cn = "cyan"
  29.  
  30. SCREEN _NEWIMAGE(600, 600, 32)
  31. WINDOW (-300, 300)-(300, -300)
  32. 'These are the approach vectors you give
  33. b(0).m.x = 0: b(0).m.y = -100 '                                 reds approach vector
  34. b(0).s = PyT(origin, b(0).m) '                                  reds magnitude (speed or force)
  35. b(1).m.x = 100: b(1).m.y = -100 '                               cyans approach vector
  36. b(1).s = PyT(origin, b(1).m) '                                  cyans magnitude (speed or force)
  37.  
  38. b(0).p.x = 30: b(0).p.y = 10
  39. b(1).p.x = -17: b(1).p.y = 0
  40.  
  41.     ms = MBS '                                                  process mouse actions dragging endpoints
  42.     IF ms AND 64 THEN
  43.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  44.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  45.         FOR x = 0 TO 1
  46.             FOR y = 0 TO 1
  47.                 ds! = PyT(vertex(x, y), mouse)
  48.                 IF ds! < ballradius * .5 THEN i = x: j = y
  49.             NEXT y
  50.         NEXT x
  51.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  52.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  53.                 b(i).p = mouse
  54.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  55.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  56.         END SELECT
  57.     ELSE
  58.     END IF
  59.  
  60.     'START OF COLLISION MATHEMATICS SECTION
  61.     ballradius = PyT(b(0).p, b(1).p) / 2
  62.     CLS
  63.  
  64.     FOR bn = 0 TO 1
  65.         other = ABS(NOT -bn) '                                  if we're on 0 the other is 1 and vice versa
  66.  
  67.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  68.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  69.  
  70.         'Now for collision processing
  71.         b(bn).um = b(bn).m: VecNorm b(bn).um '                  normalize the movement vector
  72.         IF bn = 0 THEN '                                        we only need one strike vector from the first ball
  73.             strike = b(other).p: VecAdd strike, b(bn).p, -1 '   obtain a strike vector (Berchek step 1)
  74.         END IF
  75.         b(bn).un = strike: VecNorm b(bn).un '                   compute unit normal (Berchek step 1)
  76.         b(bn).ut.x = -b(bn).un.y: b(bn).ut.y = b(bn).un.x '     compute unit tangent (Berchek step 1)
  77.         b(bn).nci = VecDot(b(bn).un, b(bn).m) '                 compute normal component of velocity (Berchek step 3)
  78.         b(bn).tci = VecDot(b(bn).ut, b(bn).m) '                 compute tangential component of velocity (Berchek step 3)
  79.     NEXT bn
  80.     'we finish the above FOR/NEXT loop so that we have all respective data stored in the two ball variables
  81.     'then we rerun the loop, converting b(#).x to a full exit vector
  82.     FOR bn = 0 TO 1
  83.         other = ABS(NOT -bn)
  84.         'Now we tackle Berchek's step 4 tangential and normal velocities
  85.         'It is assumed that both balls are of unit mass, therefore:
  86.         'From Berchek's one dimensional collision formula
  87.         ' b(bn).ncx = (b(bn).nci * (1 - 1) + 2 * 1 * b(other).nci) / (1 + 1), therefore:
  88.         ' b(bn).ncx = (b(bn).nci * 0 + 2 * b(other).nci) / 2, therefore:
  89.         ' b(bn).ncx = 2 * b(other).nci / 2, therefore:
  90.         b(bn).ncx = b(other).nci '                              compute normal component of exit velocity (Berchek step 5)
  91.         'and this concurs with Bplus' conclusion, that under equal mass the balls transfer velocity
  92.         'https://www.qb64.org/forum/index.php?topic=3866.msg132332#msg132332
  93.  
  94.         b(bn).tcx = b(bn).tci '                                 compute tangent component of exit velocity (Berchek step 4)
  95.         'Since these are equivalent, according to Berchek, it is unnecessary to assign a tangent component of exit velocity variable
  96.         'it's only done for ordering the thought process
  97.  
  98.         'and now for step 6, we're almost there...
  99.         norcom = b(bn).un: VecMult norcom, b(bn).ncx '          unit normal exit vector x normal component of exit vector
  100.         tancom = b(bn).ut: VecMult tancom, b(bn).tcx '          unit tangent exit vector x tangent component of exit vector
  101.  
  102.         'Finally, at step 7, add norcom and tancom to get b(bn).x
  103.         b(bn).x = norcom: VecAdd b(bn).x, tancom, 1 '           add normal and tangent exit vectors
  104.         'after that we can update any other ball parameters for subsequent moves
  105.     NEXT bn
  106.     'END OF COLLISION MATHEMATICS SECTION
  107.  
  108.     'graphic representation
  109.     FOR grid = -300 TO 300 STEP 20
  110.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  111.         LINE (grid, 300)-(grid, -300), c& 'Gray
  112.         LINE (-300, grid)-(300, grid), c& ' Gray
  113.     NEXT grid
  114.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  115.     FOR dr = 0 TO 1
  116.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c
  117.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x - b(dr).m.x, b(dr).p.y - b(dr).m.y), b(dr).c 'incoming
  118.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + b(dr).x.x, b(dr).p.y + b(dr).x.y), b(dr).c, , &B1111000011110000 'exit vector
  119.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  120.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  121.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">"
  122.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  123.     NEXT dr
  124.  
  125.     _LIMIT 50
  126.     _DISPLAY
  127.  
  128.  
  129. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  130.     STATIC StartTimer AS _FLOAT
  131.     STATIC ButtonDown AS INTEGER
  132.     STATIC ClickCount AS INTEGER
  133.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  134.     '                          Down longer counts as a HOLD event.
  135.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  136.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  137.         SELECT CASE SGN(_MOUSEWHEEL)
  138.             CASE 1: MBS = MBS OR 512
  139.             CASE -1: MBS = MBS OR 1024
  140.         END SELECT
  141.     WEND
  142.  
  143.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  144.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  145.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  146.  
  147.     IF StartTimer = 0 THEN
  148.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  149.             ButtonDown = 1: StartTimer = TIMER(0.01)
  150.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  151.         ELSEIF _MOUSEBUTTON(2) THEN
  152.             ButtonDown = 2: StartTimer = TIMER(0.01)
  153.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  154.         ELSEIF _MOUSEBUTTON(3) THEN
  155.             ButtonDown = 3: StartTimer = TIMER(0.01)
  156.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  157.         END IF
  158.     ELSE
  159.         BD = ButtonDown MOD 3
  160.         IF BD = 0 THEN BD = 3
  161.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  162.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  163.         ELSE
  164.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  165.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  166.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  167.             ELSE 'We've now started the hold event
  168.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  169.             END IF
  170.         END IF
  171.     END IF
  172.  
  173.  
  174. FUNCTION PyT (var1 AS V2, var2 AS V2)
  175.  
  176.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  177.  
  178.  
  179.  
  180. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  181.  
  182.     var.x = var.x + (var2.x * var3)
  183.     var.y = var.y + (var2.y * var3)
  184.  
  185. END SUB 'Add_Vector
  186.  
  187.  
  188. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  189.  
  190.     'multiply vector by scalar value
  191.     vec.x = vec.x * multiplier
  192.     vec.y = vec.y * multiplier
  193.  
  194. END SUB 'Vec_Mult
  195.  
  196.  
  197. SUB VecNorm (var AS V2)
  198.  
  199.     m = PyT(origin, var)
  200.     IF m = 0 THEN
  201.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  202.     ELSE
  203.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  204.     END IF
  205.  
  206. END SUB 'VecNorm
  207.  
  208.  
  209. FUNCTION VecDot (var AS V2, var2 AS V2)
  210.  
  211.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  212.  
  213. END FUNCTION 'VecDot
  214.  
  215.  
  216. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  217.  
  218.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  219.  
  220.  

Here's the same thing reduced and optimized to a single SUB call, it seems to still work the same.
Code: QB64: [Select]
  1. TYPE V2
  2.     x AS SINGLE
  3.     y AS SINGLE
  4.  
  5. TYPE ball
  6.     cn AS STRING * 4 '                                          ball name by color
  7.     c AS _UNSIGNED LONG '                                       color
  8.     p AS V2 '                                                   position
  9.     m AS V2 '                                                   movement (pre-contact) incoming
  10.     x AS V2 '                                                   vector of post contact movement
  11.     s AS INTEGER '                                              magnitude of movement
  12.  
  13. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  14. DIM mouse AS V2
  15. DIM b(1) AS ball
  16. DIM SHARED AS V2 origin
  17. origin.x = 0: origin.y = 0
  18. b(0).c = Red: b(0).cn = "red"
  19. b(1).c = Cyan: b(1).cn = "cyan"
  20.  
  21. SCREEN _NEWIMAGE(600, 600, 32)
  22. WINDOW (-300, 300)-(300, -300)
  23.  
  24. 'starting state
  25. b(0).m.x = 0: b(0).m.y = -100 '                                 reds approach vector
  26. b(0).s = PyT(origin, b(0).m) '                                  reds magnitude (speed or force)
  27. b(1).m.x = 100: b(1).m.y = -100 '                               cyans approach vector
  28. b(1).s = PyT(origin, b(1).m) '                                  cyans magnitude (speed or force)
  29.  
  30. b(0).p.x = 30: b(0).p.y = 10
  31. b(1).p.x = -17: b(1).p.y = 0
  32.  
  33.     ms = MBS '                                                  process mouse actions dragging endpoints
  34.     IF ms AND 64 THEN
  35.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  36.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  37.         FOR x = 0 TO 1
  38.             FOR y = 0 TO 1
  39.                 ds! = PyT(vertex(x, y), mouse)
  40.                 IF ds! < ballradius * .5 THEN i = x: j = y
  41.             NEXT y
  42.         NEXT x
  43.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  44.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  45.                 b(i).p = mouse
  46.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  47.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  48.         END SELECT
  49.     ELSE
  50.     END IF
  51.  
  52.     'START OF COLLISION MATHEMATICS SECTION
  53.     ballradius = PyT(b(0).p, b(1).p) / 2
  54.     CLS
  55.  
  56.     FOR bn = 0 TO 1
  57.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  58.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  59.     NEXT bn
  60.  
  61.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  62.     B2BCollision b(0), b(1)
  63.     'END OF COLLISION MATHEMATICS SECTION
  64.  
  65.     'graphic representation
  66.     FOR grid = -300 TO 300 STEP 20
  67.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  68.         LINE (grid, 300)-(grid, -300), c& 'Gray
  69.         LINE (-300, grid)-(300, grid), c& ' Gray
  70.     NEXT grid
  71.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  72.     FOR dr = 0 TO 1
  73.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c
  74.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x - b(dr).m.x, b(dr).p.y - b(dr).m.y), b(dr).c 'incoming
  75.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + b(dr).x.x, b(dr).p.y + b(dr).x.y), b(dr).c, , &B1111000011110000 'exit vector
  76.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  77.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  78.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">"
  79.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  80.     NEXT dr
  81.  
  82.     _LIMIT 50
  83.     _DISPLAY
  84.  
  85.  
  86. SUB B2BCollision (ball1 AS ball, ball2 AS ball)
  87.  
  88.     DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  89.     un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  90.     ut.x = -un.y: ut.y = un.x '                                 establish unit tangent
  91.     bnci1 = VecDot(un, ball1.m) '
  92.     bnci2 = VecDot(un, ball2.m) '
  93.     btci1 = VecDot(ut, ball1.m) '
  94.     btci2 = VecDot(ut, ball2.m) '
  95.  
  96.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  97.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  98.  
  99.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  100.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  101.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  102.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  103.  
  104.     ball1.x = ncomp1: VecAdd ball1.x, tcomp1, 1 '               add normal and tangent exit vectors
  105.     ball2.x = ncomp2: VecAdd ball2.x, tcomp2, 1 '               add normal and tangent exit vectors
  106.  
  107. END SUB 'B2BCollision
  108.  
  109.  
  110. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  111.  
  112.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  113.  
  114.  
  115.  
  116. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  117.     STATIC StartTimer AS _FLOAT
  118.     STATIC ButtonDown AS INTEGER
  119.     STATIC ClickCount AS INTEGER
  120.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  121.     '                          Down longer counts as a HOLD event.
  122.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  123.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  124.         SELECT CASE SGN(_MOUSEWHEEL)
  125.             CASE 1: MBS = MBS OR 512
  126.             CASE -1: MBS = MBS OR 1024
  127.         END SELECT
  128.     WEND
  129.  
  130.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  131.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  132.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  133.  
  134.     IF StartTimer = 0 THEN
  135.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  136.             ButtonDown = 1: StartTimer = TIMER(0.01)
  137.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  138.         ELSEIF _MOUSEBUTTON(2) THEN
  139.             ButtonDown = 2: StartTimer = TIMER(0.01)
  140.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  141.         ELSEIF _MOUSEBUTTON(3) THEN
  142.             ButtonDown = 3: StartTimer = TIMER(0.01)
  143.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  144.         END IF
  145.     ELSE
  146.         BD = ButtonDown MOD 3
  147.         IF BD = 0 THEN BD = 3
  148.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  149.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  150.         ELSE
  151.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  152.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  153.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  154.             ELSE 'We've now started the hold event
  155.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  156.             END IF
  157.         END IF
  158.     END IF
  159.  
  160.  
  161. FUNCTION PyT (var1 AS V2, var2 AS V2)
  162.  
  163.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  164.  
  165.  
  166.  
  167. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  168.  
  169.     var.x = var.x + (var2.x * var3) '                           add vector (or a scalar multiple of) var2 to var
  170.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  171.  
  172. END SUB 'Add_Vector
  173.  
  174.  
  175. FUNCTION VecDot (var AS V2, var2 AS V2)
  176.  
  177.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  178.  
  179. END FUNCTION 'VecDot
  180.  
  181.  
  182. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  183.  
  184.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  185.     vec.y = vec.y * multiplier
  186.  
  187. END SUB 'Vec_Mult
  188.  
  189.  
  190. SUB VecNorm (var AS V2)
  191.  
  192.     m = PyT(origin, var)
  193.     IF m = 0 THEN
  194.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  195.     ELSE
  196.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  197.     END IF
  198.  
  199. END SUB 'VecNorm
  200.  
« Last Edit: May 21, 2021, 08:47:35 pm by OldMoses »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #53 on: May 21, 2021, 12:19:10 pm »
@OldMoses was that Berchek paper the same you gave link to that I used?

Oh yeah, I see at end 2009 Chad Berchek. It worked for me except for the problem of hanging when balls overlap. I am still trying to figure out how to get rid of that specially on breaks in Pool.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #54 on: May 21, 2021, 12:41:09 pm »
It's the same issue I had with mine, the breaks, tight groups and/or high speeds made weird things happen. At least I seem to have a good handle, provisionally speaking, on how the balls are supposed to move now.


EDIT:
I'm thinking that if one were to obtain the magnitude of the fastest ball and use that as the upper end of a loop counter, you could advance the fast ball one unit vector at a time, while using FUNCTION map! to advance the slower ball by the proper amount, you could determine more precise ball (x, y) positions at impact. Use those points as temporary positions to get the exit vectors and then finish out the movement along those new vectors. I used a similar system for planet/ship collisions in my starship program where both entities were in motion.
« Last Edit: May 21, 2021, 12:57:08 pm by OldMoses »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #55 on: May 22, 2021, 12:26:43 am »
The dashed lines are the exit vectors.   Dotted line is the strike vector. solid lines are the ball headings. 

example
red  @ (0, 0)  along <0, 100>  exits along <100, -1>
cyan @ (-100, 100)  along <100, 0>  exits along <0, 100>

Exit vectors are correct.   The -1 looks to be a rounding error.

Now move cyan ball to

red  @ (0, 0)  along <0, 100>  exits along <100, 100>
cyan @ (-100, 0)  along <100, 0>  exits along <0, 0>

And a very interesting result. The cyan ball comes to a complete stop and the red ball speeds up. Intuitive guess is the red ball should be travelling twice as fast (200) but the red ball exit velocity is

(100^ + 100^2 ).5 = 141.421356

Solving for kinetic energy
                                     red             cyan
141.421356 ^ 2 / 2 =   100^2 /2 + 100^2 /2   =  10000
« Last Edit: May 22, 2021, 01:05:09 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #56 on: May 22, 2021, 07:27:39 am »
example
red  @ (0, 0)  along <0, 100>  exits along <100, -1>
cyan @ (-100, 100)  along <100, 0>  exits along <0, 100>

Exit vectors are correct.   The -1 looks to be a rounding error.

Yeah, for the actual vector components I'm using SINGLE, but the data bar is using INT on the numbers. I imagine it has oodles of rounding issues.


Now move cyan ball to

red  @ (0, 0)  along <0, 100>  exits along <100, 100>
cyan @ (-100, 0)  along <100, 0>  exits along <0, 0>

And a very interesting result. The cyan ball comes to a complete stop and the red ball speeds up. Intuitive guess is the red ball should be travelling twice as fast (200) but the red ball exit velocity is


Now that seems appropriate, given that the cyan is striking red square while red is getting T-boned. Red retains its unit tangent component (along Y) and receives all the energy from cyan along the unit normal component (along X). Red is imparting zero force along its unit tangent in X to Cyan. Ergo, cyan loses all momentum.

In trying to set up the scenarios you tested, I've had some challenge getting things to set on the exact numbers. Mouse control is a bit coarse. So I added some _KEYDOWNs to allow finer control.

"r" to choose red ball
"c" to choose cyan ball
, or the last ball chosen by the mouse will be active
arrow keys will move the chosen ball in x/y
"aswd" keys will move the input vector tail in x/y

Code: QB64: [Select]
  1. TYPE V2
  2.     x AS SINGLE
  3.     y AS SINGLE
  4.  
  5. TYPE ball
  6.     cn AS STRING * 4 '                                          ball name by color
  7.     c AS _UNSIGNED LONG '                                       color
  8.     p AS V2 '                                                   position
  9.     m AS V2 '                                                   movement (pre-contact) incoming
  10.     x AS V2 '                                                   vector of post contact movement
  11.     s AS INTEGER '                                              magnitude of movement
  12.  
  13. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  14. DIM mouse AS V2
  15. DIM b(1) AS ball
  16. DIM SHARED AS V2 origin
  17. origin.x = 0: origin.y = 0
  18. b(0).c = Red: b(0).cn = "red"
  19. b(1).c = Cyan: b(1).cn = "cyan"
  20.  
  21. SCREEN _NEWIMAGE(600, 600, 32)
  22. WINDOW (-300, 300)-(300, -300)
  23.  
  24. 'starting state
  25. b(0).m.x = 0: b(0).m.y = -100 '                                 reds approach vector
  26. b(0).s = PyT(origin, b(0).m) '                                  reds magnitude (speed or force)
  27. b(1).m.x = 100: b(1).m.y = -100 '                               cyans approach vector
  28. b(1).s = PyT(origin, b(1).m) '                                  cyans magnitude (speed or force)
  29.  
  30. b(0).p.x = 30: b(0).p.y = 10
  31. b(1).p.x = -17: b(1).p.y = 0
  32.  
  33.     ms = MBS '                                                  process mouse actions dragging endpoints
  34.     IF ms AND 64 THEN
  35.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  36.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  37.         FOR x = 0 TO 1
  38.             FOR y = 0 TO 1
  39.                 ds! = PyT(vertex(x, y), mouse)
  40.                 IF ds! < ballradius * .5 THEN i = x: j = y
  41.             NEXT y
  42.         NEXT x
  43.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  44.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  45.                 b(i).p = mouse
  46.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  47.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  48.         END SELECT
  49.     ELSE
  50.         IF _KEYDOWN(114) THEN i = 0
  51.         IF _KEYDOWN(99) THEN i = 1
  52.         IF _KEYDOWN(18432) THEN b(i).p.y = b(i).p.y + 1
  53.         IF _KEYDOWN(20480) THEN b(i).p.y = b(i).p.y - 1
  54.         IF _KEYDOWN(19200) THEN b(i).p.x = b(i).p.x - 1
  55.         IF _KEYDOWN(19712) THEN b(i).p.x = b(i).p.x + 1
  56.         IF _KEYDOWN(119) THEN b(i).m.y = b(i).m.y - 1
  57.         IF _KEYDOWN(115) THEN b(i).m.y = b(i).m.y + 1
  58.         IF _KEYDOWN(97) THEN b(i).m.x = b(i).m.x + 1
  59.         IF _KEYDOWN(100) THEN b(i).m.x = b(i).m.x - 1
  60.         _DELAY .1
  61.     END IF
  62.  
  63.     'START OF COLLISION MATHEMATICS SECTION
  64.     ballradius = PyT(b(0).p, b(1).p) / 2
  65.     CLS
  66.  
  67.     FOR bn = 0 TO 1
  68.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  69.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  70.     NEXT bn
  71.  
  72.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  73.     B2BCollision b(0), b(1)
  74.     'END OF COLLISION MATHEMATICS SECTION
  75.  
  76.     'graphic representation
  77.     FOR grid = -300 TO 300 STEP 20
  78.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  79.         LINE (grid, 300)-(grid, -300), c& 'Gray
  80.         LINE (-300, grid)-(300, grid), c& ' Gray
  81.     NEXT grid
  82.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  83.     FOR dr = 0 TO 1
  84.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c
  85.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x - b(dr).m.x, b(dr).p.y - b(dr).m.y), b(dr).c 'incoming
  86.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + b(dr).x.x, b(dr).p.y + b(dr).x.y), b(dr).c, , &B1111000011110000 'exit vector
  87.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  88.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  89.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">"
  90.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  91.     NEXT dr
  92.  
  93.     _LIMIT 50
  94.     _DISPLAY
  95.  
  96.  
  97. SUB B2BCollision (ball1 AS ball, ball2 AS ball)
  98.  
  99.     DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  100.     un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  101.     ut.x = -un.y: ut.y = un.x '                                 establish unit tangent
  102.     bnci1 = VecDot(un, ball1.m) '
  103.     bnci2 = VecDot(un, ball2.m) '
  104.     btci1 = VecDot(ut, ball1.m) '
  105.     btci2 = VecDot(ut, ball2.m) '
  106.  
  107.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  108.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  109.  
  110.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  111.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  112.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  113.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  114.  
  115.     ball1.x = ncomp1: VecAdd ball1.x, tcomp1, 1 '               add normal and tangent exit vectors
  116.     ball2.x = ncomp2: VecAdd ball2.x, tcomp2, 1 '               add normal and tangent exit vectors
  117.  
  118. END SUB 'B2BCollision
  119.  
  120.  
  121. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  122.  
  123.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  124.  
  125.  
  126.  
  127. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  128.     STATIC StartTimer AS _FLOAT
  129.     STATIC ButtonDown AS INTEGER
  130.     STATIC ClickCount AS INTEGER
  131.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  132.     '                          Down longer counts as a HOLD event.
  133.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  134.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  135.         SELECT CASE SGN(_MOUSEWHEEL)
  136.             CASE 1: MBS = MBS OR 512
  137.             CASE -1: MBS = MBS OR 1024
  138.         END SELECT
  139.     WEND
  140.  
  141.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  142.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  143.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  144.  
  145.     IF StartTimer = 0 THEN
  146.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  147.             ButtonDown = 1: StartTimer = TIMER(0.01)
  148.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  149.         ELSEIF _MOUSEBUTTON(2) THEN
  150.             ButtonDown = 2: StartTimer = TIMER(0.01)
  151.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  152.         ELSEIF _MOUSEBUTTON(3) THEN
  153.             ButtonDown = 3: StartTimer = TIMER(0.01)
  154.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  155.         END IF
  156.     ELSE
  157.         BD = ButtonDown MOD 3
  158.         IF BD = 0 THEN BD = 3
  159.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  160.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  161.         ELSE
  162.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  163.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  164.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  165.             ELSE 'We've now started the hold event
  166.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  167.             END IF
  168.         END IF
  169.     END IF
  170.  
  171.  
  172. FUNCTION PyT (var1 AS V2, var2 AS V2)
  173.  
  174.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  175.  
  176.  
  177.  
  178. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  179.  
  180.     var.x = var.x + (var2.x * var3) '                           add vector (or a scalar multiple of) var2 to var
  181.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  182.  
  183. END SUB 'Add_Vector
  184.  
  185.  
  186. FUNCTION VecDot (var AS V2, var2 AS V2)
  187.  
  188.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  189.  
  190. END FUNCTION 'VecDot
  191.  
  192.  
  193. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  194.  
  195.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  196.     vec.y = vec.y * multiplier
  197.  
  198. END SUB 'Vec_Mult
  199.  
  200.  
  201. SUB VecNorm (var AS V2)
  202.  
  203.     m = PyT(origin, var)
  204.     IF m = 0 THEN
  205.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  206.     ELSE
  207.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  208.     END IF
  209.  
  210. END SUB 'VecNorm
  211.  
« Last Edit: May 22, 2021, 07:36:34 am by OldMoses »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #57 on: May 22, 2021, 07:20:55 pm »
Well some folks might have big balls, but I've had teleporting balls and now I have magnetic balls. :D

Anyway, this is the result of plugging SUB B2BCollision into my billiards project. The basic issue has to be impact point control, but I have to say, I found the bug much more amusing this time.

left click to shoot the cue ball, right click to re-rack em, esc to quit.

Code: QB64: [Select]
  1.  
  2. 'ball colors 1 yellow 2 blue 3 red 4 purple 5 orange 6 green 7 maroon 8 black
  3. '9 yellow/s 10 blue/s 11 red/s 12 purple/s 13 orange/s 14 green/s 15 maroon/s
  4.  
  5. TYPE V2
  6.     x AS SINGLE
  7.     y AS SINGLE
  8.  
  9. TYPE ball
  10.     sunk AS _BYTE '                                             has ball been sunk true/false
  11.     im AS _BYTE '                                               impact register
  12.     c AS _UNSIGNED LONG '                                       ball color
  13.     p AS V2 '                                                 position vector
  14.     d AS V2 '                                                 direction vector
  15.     n AS V2 '                                                 normalized direction vector
  16.     s AS SINGLE '                                               speed
  17.     r AS _BYTE '                                                rack position
  18.  
  19. DIM SHARED bsiz AS INTEGER '                                    radius of ball
  20. DIM SHARED bsiz2 AS INTEGER '                                   ball diameter or sphere of contact
  21. DIM SHARED bl(15) AS ball '                                     ball data
  22. DIM SHARED bnum(15) AS LONG
  23. DIM SHARED smack(15, 16) AS _BYTE
  24. DIM SHARED origin AS V2
  25. origin.x = 0: origin.y = 0
  26.  
  27. 'Set the table size
  28.     xtable = _DESKTOPWIDTH - 100: ytable = xtable / 2
  29.     ytable = _DESKTOPHEIGHT - 80: xtable = ytable * 2
  30.  
  31. bsiz = INT(((xtable / 118.1102) * 2.375) / 2) '                 size balls to table
  32. bsiz2 = bsiz * 2
  33.  
  34. FOR x = 0 TO 15
  35.     READ bl(x).c
  36.  
  37. MakeBalls
  38. RackEmUp
  39. bl(0).p.y = INT(ytable * .5) '                                  position the cue
  40. bl(0).p.x = INT(xtable * .75)
  41.  
  42.  
  43. a& = _NEWIMAGE(xtable, ytable, 32)
  44. shot& = _NEWIMAGE(xtable, ytable, 32)
  45. _DEST a&: SCREEN a&
  46. COLOR , &HFF3AAF61
  47. 'set up the table
  48. FOR x = 0 TO 2
  49.     LINE (x, x)-(xtable - x, ytable - x), Black, B
  50.     FCirc xtable * .75, ytable * .5, 5, Gray, Gray
  51.     FCirc xtable * .75, ytable * .5, 2, White, White
  52.     CLS
  53.     FOR x = 0 TO 15
  54.         VecAdd bl(x).p, bl(x).d, 1
  55.         VecMult bl(x).d, .99
  56.         ColCheck x
  57.         _PUTIMAGE (INT(bl(x).p.x) - CINT(_WIDTH(bnum(x)) / 2), INT(bl(x).p.y) - CINT(_HEIGHT(bnum(x)) / 2)), bnum(x), a&
  58.     NEXT x
  59.  
  60.     ms = MBS%
  61.     IF ms AND 1 THEN
  62.         bl(0).d.x = (bl(0).p.x - _MOUSEX) * 0.05
  63.         bl(0).d.y = (bl(0).p.y - _MOUSEY) * 0.05
  64.     END IF
  65.     IF ms AND 2 THEN
  66.         BallStop
  67.         bl(0).p.y = INT(ytable * .5)
  68.         bl(0).p.x = INT(xtable * .75)
  69.         RackEmUp
  70.     END IF
  71.     LINE (_MOUSEX, _MOUSEY)-(CINT(bl(0).p.x), CINT(bl(0).p.y))
  72.     'slope of target line
  73.     pathx = CINT(bl(0).p.x) - _MOUSEX: pathy = CINT(bl(0).p.y) - _MOUSEY
  74.     LINE (bl(0).p.x, bl(0).p.y)-(pathx * 1000, pathy * 1000), Blue
  75.     _DISPLAY
  76.     _LIMIT 100 '100 seems good
  77.  
  78.  
  79. '                                                               DATA SECTION
  80. hue:
  81. DATA 4294967295,4294967040,4278190335,4294901760,4286578816,4294944000,4278222848,4286578688
  82. DATA 4278190080,4294967040,4278190335,4294901760,4286578816,4294944000,4278222848,4286578688
  83.  
  84. start:
  85. DATA 1,2,15,14,8,3,4,6,11,13,12,7,9,10,5,0
  86.  
  87. SUB B2BCollision (ball1 AS ball, ball2 AS ball)
  88.  
  89.     DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  90.     un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  91.     ut.x = -un.y: ut.y = un.x '                                 establish unit tangent
  92.     bnci1 = VecDot(un, ball1.d) '                               ball 1 normal component of input velocity
  93.     bnci2 = VecDot(un, ball2.d) '                               ball 2 normal component of input velocity
  94.     btci1 = VecDot(ut, ball1.d) '                               ball 1 tangent component of input velocity
  95.     btci2 = VecDot(ut, ball2.d) '                               ball 2 tangent component of input velocity
  96.  
  97.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  98.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  99.  
  100.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  101.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  102.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  103.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  104.  
  105.     ball1.d = ncomp1: VecAdd ball1.d, tcomp1, 1 '               add normal and tangent exit vectors
  106.     ball2.d = ncomp2: VecAdd ball2.d, tcomp2, 1 '               add normal and tangent exit vectors
  107.  
  108. END SUB 'B2BCollision
  109.  
  110.  
  111. SUB BallStop
  112.  
  113.     FOR x = 0 TO 15
  114.         bl(x).d = origin
  115.     NEXT x
  116.  
  117. END SUB 'BallStop
  118.  
  119.  
  120. SUB ColCheck (var AS INTEGER)
  121.  
  122.     'check for ball in displacement radius
  123.     disp = SQR(bl(var).d.x * bl(var).d.x + bl(var).d.y * bl(var).d.y) 'vector magnitude for this iteration
  124.     FOR x = 0 TO 15
  125.         IF x <> var THEN '                                      won't collide with self so skip self check
  126.             IF NOT bl(var).sunk THEN '                          if ball not already out of play
  127.                 dist = PyT(bl(var).p, bl(x).p) '                calculate distance between var and x
  128.                 IF dist - bsiz2 < disp THEN '                   if ball x is within reach of magnitude
  129.                     'check to see if there is an impact point-  Ray trace routine
  130.                     DIM neari AS V2
  131.                     'DIM strike AS V2
  132.                     'DIM strikeorth AS V2
  133.                     'DIM striker AS V2
  134.                     'DIM strikee AS V2
  135.                     'DIM orthox AS V2
  136.                     'DIM orthoy AS V2
  137.  
  138.                     'Using a variation of the collision routine from CTVector.bas
  139.                     'but the damn thing isn't working as envisioned... well it didn't work there either.
  140.                     dx = bl(var).p.x - bl(x).p.x
  141.                     dy = bl(var).p.y - bl(x).p.y
  142.                     A## = (bl(var).d.x * bl(var).d.x) + (bl(var).d.y * bl(var).d.y) 'displacement range
  143.                     B## = 2 * bl(var).d.x * dx + 2 * bl(var).d.y * dy
  144.                         C## = (bl(x).p.x * bl(x).p.x) + (bl(x).p.y * bl(x).p.y) + (bl(var).p.x * bl(var).p.x)_
  145.                              + (bl(var).p.y * bl(var).p.y) + -2 * (bl(x).p.x * bl(var).p.x + bl(x).p.y * bl(var).p.y) - (bsiz2 * bsiz2)
  146.                     disabc## = (B## * B##) - 4 * A## * C##
  147.                     IF disabc## <= 0 THEN 'let's make this <=, since a tangent brush by would ideally not move the ball
  148.                     ELSE
  149.                         t## = (-B## - ((B## * B##) - 4 * A## * C##) ^ .5) / (2 * A##) 'near intersect quadratic gives percentage of displacement to contact
  150.                         neari.x = bl(var).p.x + t## * bl(var).d.x: neari.y = bl(var).p.y + t## * bl(var).d.y 'contact point
  151.                         'now that we have a contact point, we can proceed to deflect the displacements of var and x
  152.  
  153.                         'Not sure yet how to get impact points but we can replace the following with SUB B2BCollision
  154.                         'bl(var).p = neari
  155.                         ''// get strike angle unit vector
  156.                         'strike.x = bl(x).p.x - neari.x: strike.y = bl(x).p.y - neari.y
  157.                         ''// get the two orthogonal vectors to strike
  158.                         'orthox.x = -strike.y: orthox.y = strike.x
  159.                         'orthoy.x = strike.y: orthoy.y = -strike.x
  160.                         ''// add orthogonals to impact point
  161.                         'VecAdd orthox, neari, 1: VecAdd orthoy, neari, 1
  162.                         ''// add present var displacement to ortho's
  163.                         'VecAdd orthox, bl(var).d, 1: VecAdd orthoy, bl(var).d, 1
  164.                         ''// check distances and compare, using farthest one for striker's new vector
  165.                         'vox = PyT(orthox, bl(x).p): voy = PyT(orthoy, bl(x).p)
  166.                         'IF vox > voy THEN
  167.                         '    strikeorth.x = -strike.y: strikeorth.y = strike.x
  168.                         'ELSEIF vox = voy THEN
  169.                         '    strikeorth = strike
  170.                         'ELSE
  171.                         '    strikeorth.x = strike.y: strikeorth.y = -strike.x
  172.                         'END IF
  173.                         ''// normalize strike vectors
  174.                         'VecNorm strike: VecNorm strikeorth
  175.  
  176.  
  177.                         'striker = bl(var).d: VecNorm striker '                  get striker unit vector
  178.                         'strikee = bl(x).d: VecNorm strikee '                    get strikee unit vector
  179.                         'dot = striker.x * strike.x + striker.y * strike.y '     apply to struck balls displacement magnitude along strike
  180.                         'dotback = 1 - dot '                                     apply to striking balls displacement along orthogonal of strike
  181.  
  182.                         ''get proportion of energy transfer and add it to existing vector of unit x
  183.                         'VecMult strike, 0.99 * dot * PyT(origin, bl(var).d)
  184.                         'VecAdd bl(x).d, strike, 1
  185.                         ''do the same with var using balance of energy
  186.                         'VecMult strikeorth, 0.99 * dotback * PyT(origin, bl(var).d)
  187.                         ''VecAdd bl(var).d, strikeorth, 1
  188.                         'bl(var).d = strikeorth
  189.                         ''bl(var).d.x = -bl(var).d.x: bl(var).d.y = -bl(var).d.y
  190.                         B2BCollision bl(var), bl(x)
  191.                     END IF 'disabc <= 0
  192.                 END IF 'dist < disp
  193.             END IF 'NOT bl(var).sunk
  194.         END IF 'x <> var
  195.     NEXT x
  196.  
  197.     'wall bounces
  198.     IF bl(var).p.x < bsiz OR bl(var).p.x > xtable - bsiz THEN
  199.         bl(var).d.x = -bl(var).d.x
  200.         IF bl(var).p.x < bsiz THEN '                            if beyond left edge
  201.             bl(var).p.y = bl(var).p.y - (bl(var).d.y * ((bl(var).p.x - bsiz) / bl(var).d.x))
  202.             bl(var).p.x = bsiz
  203.         END IF
  204.         IF bl(var).p.x > xtable - bsiz THEN '                   if beyond right edge
  205.             bl(var).p.y = bl(var).p.y - (bl(var).d.y * ((bl(var).p.x - xtable + bsiz) / bl(var).d.x))
  206.             bl(var).p.x = xtable - bsiz
  207.         END IF
  208.     END IF
  209.     IF bl(var).p.y < bsiz OR bl(var).p.y > ytable - bsiz THEN
  210.         bl(var).d.y = -bl(var).d.y
  211.         IF bl(var).p.y < bsiz THEN '                            if beyond top edge
  212.             bl(var).p.x = bl(var).p.x - (bl(var).d.x * ((bl(var).p.y - bsiz) / bl(var).d.y))
  213.             bl(var).p.y = bsiz
  214.         END IF
  215.         IF bl(var).p.y > ytable - bsiz THEN '                   if beyond bottom edge
  216.             bl(var).p.x = bl(var).p.x - (bl(var).d.x * ((bl(var).p.y - ytable + bsiz) / bl(var).d.y))
  217.             bl(var).p.y = ytable - bsiz
  218.         END IF
  219.     END IF
  220.  
  221. END SUB 'ColCheck
  222.  
  223.  
  224.     DIM R AS INTEGER, RError AS INTEGER '                       SMcNeill's circle fill
  225.     DIM X AS INTEGER, Y AS INTEGER
  226.  
  227.     R = ABS(RR)
  228.     RError = -R
  229.     X = R
  230.     Y = 0
  231.     IF R = 0 THEN PSET (CX, CY), C: EXIT SUB
  232.     LINE (CX - X, CY)-(CX + X, CY), C, BF
  233.     WHILE X > Y
  234.         RError = RError + Y * 2 + 1
  235.         IF RError >= 0 THEN
  236.             IF X <> Y + 1 THEN
  237.                 LINE (CX - Y, CY - X)-(CX + Y, CY - X), C2, BF 'these two need white here for 9-15 balls
  238.                 LINE (CX - Y, CY + X)-(CX + Y, CY + X), C2, BF
  239.             END IF
  240.             X = X - 1
  241.             RError = RError - X * 2
  242.         END IF
  243.         Y = Y + 1
  244.         LINE (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  245.         LINE (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  246.     WEND
  247. END SUB 'FCirc
  248.  
  249.  
  250. SUB LoadColArray
  251.  
  252.     FOR a = 0 TO 15
  253.         FOR b = 0 TO 16
  254.  
  255.     NEXT b, a
  256.  
  257. END SUB 'LoadColArray
  258.  
  259.  
  260. SUB MakeBalls
  261.  
  262.     FOR x = 0 TO 15
  263.         'make ball images here
  264.         bnum(x) = _NEWIMAGE(bsiz * 2 + 4, bsiz * 2 + 4, 32)
  265.         _DEST bnum(x)
  266.         IF x = 0 THEN '                                         Cue ball
  267.             FCirc INT(_WIDTH(bnum(x)) / 2), INT(_HEIGHT(bnum(x)) / 2), bsiz, bl(x).c, bl(x).c
  268.             CIRCLE (_WIDTH(bnum(x)) / 2, _HEIGHT(bnum(x)) / 2), bsiz + 1, Black
  269.         ELSE
  270.             'Solids or stripes
  271.             IF x <= 8 THEN
  272.                 FCirc INT(_WIDTH(bnum(x)) / 2), INT(_HEIGHT(bnum(x)) / 2), bsiz, bl(x).c, bl(x).c ' solid
  273.             ELSE
  274.                 FCirc INT(_WIDTH(bnum(x)) / 2), INT(_HEIGHT(bnum(x)) / 2), bsiz, bl(x).c, White '   stripe
  275.             END IF
  276.             FCirc INT(_WIDTH(bnum(x)) / 2), INT(_HEIGHT(bnum(x)) / 2), bsiz - 5, White, White 'number circle
  277.             CIRCLE (_WIDTH(bnum(x)) / 2, _HEIGHT(bnum(x)) / 2), bsiz + 1, Black
  278.             n$ = _TRIM$(STR$(x))
  279.             t& = _NEWIMAGE(16, 16, 32)
  280.             _DEST t&
  281.             COLOR Black
  282.             _PRINTMODE _KEEPBACKGROUND
  283.             IF LEN(n$) > 1 THEN a = 0 ELSE a = 4
  284.             _PRINTSTRING (a, 0), n$, t&
  285.             _DEST bnum(x)
  286.             _PUTIMAGE (8, 8)-(_WIDTH(bnum(x)) - 8, _HEIGHT(bnum(x)) - 8), t&, bnum(x)
  287.             _FREEIMAGE t&
  288.         END IF
  289.     NEXT x
  290.  
  291. END SUB 'MakeBalls
  292.  
  293.  
  294. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  295.     STATIC StartTimer AS _FLOAT
  296.     STATIC ButtonDown AS INTEGER
  297.     STATIC ClickCount AS INTEGER
  298.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  299.     '                          Down longer counts as a HOLD event.
  300.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  301.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  302.         SELECT CASE SGN(_MOUSEWHEEL)
  303.             CASE 1: MBS = MBS OR 512
  304.             CASE -1: MBS = MBS OR 1024
  305.         END SELECT
  306.     WEND
  307.  
  308.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  309.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  310.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  311.  
  312.     IF StartTimer = 0 THEN
  313.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  314.             ButtonDown = 1: StartTimer = TIMER(0.01)
  315.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  316.         ELSEIF _MOUSEBUTTON(2) THEN
  317.             ButtonDown = 2: StartTimer = TIMER(0.01)
  318.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  319.         ELSEIF _MOUSEBUTTON(3) THEN
  320.             ButtonDown = 3: StartTimer = TIMER(0.01)
  321.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  322.         END IF
  323.     ELSE
  324.         BD = ButtonDown MOD 3
  325.         IF BD = 0 THEN BD = 3
  326.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  327.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  328.         ELSE
  329.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  330.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  331.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  332.             ELSE 'We've now started the hold event
  333.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  334.             END IF
  335.         END IF
  336.     END IF
  337.  
  338.  
  339. FUNCTION PyT (var1 AS V2, var2 AS V2)
  340.  
  341.     PyT = _HYPOT(ABS(var1.x - var2.x), ABS(var1.y - var2.y)) '  distances and magnitudes
  342.  
  343.  
  344.  
  345. SUB RackEmUp
  346.  
  347.     yoff = bsiz2 + 4
  348.     xoff = SQR((yoff / 2) * (yoff / 2) + yoff * yoff)
  349.  
  350.     RESTORE start
  351.     FOR rank = 1 TO 5
  352.         FOR b = 1 TO rank
  353.             READ k
  354.             bl(k).p.x = (.25 * xtable) - (xoff * (rank - 1))
  355.             bl(k).p.y = (.5 * ytable) - ((rank - 1) * (.5 * yoff)) + ((b - 1) * yoff)
  356.         NEXT b
  357.     NEXT rank
  358.  
  359. END SUB 'RackEmUp
  360.  
  361.  
  362. SUB VecAdd (var AS V2, var2 AS V2, var3 AS INTEGER)
  363.  
  364.     var.x = var.x + (var2.x * var3) '                           add (or subtract) two vectors defined by unitpoint
  365.     var.y = var.y + (var2.y * var3) '                           var= base vector, var2= vector to add
  366.  
  367. END SUB 'VecAdd
  368.  
  369.  
  370. FUNCTION VecDot (var AS V2, var2 AS V2)
  371.  
  372.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  373.  
  374. END FUNCTION 'VecDot
  375.  
  376.  
  377. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  378.  
  379.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  380.     vec.y = vec.y * multiplier
  381.  
  382. END SUB 'VecMult
  383.  
  384. SUB VecNorm (var AS V2)
  385.  
  386.     m = SQR(var.x * var.x + var.y * var.y) '                    convert var to unit vector
  387.     var.x = var.x / m
  388.     var.y = var.y / m
  389.  
  390. END SUB 'VecNorm
  391.  
  392.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #58 on: May 22, 2021, 09:14:54 pm »
@OldMoses

Well if this is wrong I don't want to be right, I like the look of your balls, I'm still trying to figure how you get the stripe but going with images is good. I could do it with lines and Paint I guess.

For sticky, balls I've found if you back the collided balls back to collision kiss point then calculate the vectors they don't stick.

The ball angles look pretty good too!

Update: Oh you changed the Fcirc sub to make stripes.
« Last Edit: May 22, 2021, 09:40:10 pm by bplus »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #59 on: May 22, 2021, 09:54:49 pm »
Once upon a time, I wanted to figure out how Steve's circle fill worked, and so I put a couple sleep commands in and watched it progress. When I started the billiards thing, it seemed the natural solution. It might even be good with rotozoom.

I'm trying to come up with a handler that will do a break, but I'm getting a nagging suspicion that I might have to do something recursive to deal with potential multiple strikes in a single displacement loop. Never tried anything like that before...beyond here there be dragons...