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

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: 2D ball collisions without trigonometry.
« Reply #90 on: May 28, 2021, 10:16:44 pm »
@bplus
So for frame b(i) check every ball for collisions and if there are any calculate new data and save it in frame nf(i). At what point is the switch made to frame nf(i)?

Just before the loop back. In this program, I broke my normal routine of drawing everything last to drawing everything first, then recalculating new locations.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #91 on: May 29, 2021, 01:10:01 am »
I still don't know how 2d ball collisions work.  That's OK, about 99.999999% of the world's population don't know either.

I will try to do a step by step calculation of the following ball data to arrive at the exit vectors shown.

red  @ (0, 0)  along <0, 100>  exits along <79, 60>           
cyan @ (-100, 50)  along <-100, 100>  exits along <20, 140>  SELECTED

So there is the unit tangent and unit normal vector.  The unit normal vector is also known as the strike vector.  The unit tangent vector is at a right angle to the unit normal vector.

un = unit normal vector
ut = unit tangent vector

lets calculate un

un =  er wait .. what the hek is that again ........
cyan ball position -100x  50y
red ball position        0x     0y

normal vector (strike vector) is -100x   50y

unit normal vector =
(-100^2+ 50^2 )^.5 = 111.8033

-100x / 111.8033 = -.8944x
50y / 111.8033 = .4472y

un =   -.8944x    .4472y   
ut is easy to calculate because it is at right angle to un
multiply y by -1
swap x with y and y with x

ut  = -.4472x   -.8944y 


The literature says that UT is tangent to both balls but with the red ball centered at 0x  0y,  the above math indicates that ut can be in any position and still be the same vector

Ok from this point there are probably more than one way to solve for exit vectors of both balls.

STxAxTIC supplied an interesting equation:

q = 2( un. cyan ball vector  - un . red ball vector )

lets sidetrack and look at a straight on collision

red  @ (0, 0)  along <120, 60>  exits along <139, -70>           
cyan @ (-100, 50)  along <-140, -70>  exits along <-121, 60>  SELECTED

the unit normal vector is the same as before

un =   -.894427x    .447213y   


The cyan ball heading before collision -140x   -70y

actually the display for y should be 70y will fix  that later

divide cyan vector x ,y by cyan vector magnitude to obtain cyan unit vector

unit cyan vector = -.8944x,   .4472y

the dot product of unit normal (un)  with unit cyan  (uc)
 un . uc = -.8944  *  -.8944 +  .4472 *.4472  = .8   +  .2  = 1

The dot product for red ball =1.

un . uc - un . ur = 0

both balls come to a complete stop and rebound at the same velocity then arrived at. 
What happens if the cyan ball heads in a direction that is right angle to un?

red  @ (0, 0)  along <120, 60>  exits along <0, -1>           
cyan @ (-100, 50)  along <56, -115>  exits along <-178, -55>  SELECTED

unit cyan vector (uc)
(56^2 = 115^2 )^.5 = 127.9101

56 / 127.9101 =.4378
115 / 127.9101 = .89906

un . uc = -.8944  *.4378 + 44721 *.89906  = -.3915 + .4 = approx zero

not exactly zero due to small errors in graph output. 

RULE:  The dot product of 2 unit vectors ranges from 0 to 1









« Last Edit: May 30, 2021, 12:03:50 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
Re: 2D ball collisions without trigonometry.
« Reply #92 on: May 29, 2021, 07:55:46 am »
I still don't know how 2d ball collisions work.  That's OK, about 99.999999% of the world's population don't know either.

But you want to know, which is the point of all this.

Quote
I will try to do a step by step calculation of the following ball data to arrive at the exit vectors shown.

red  @ (0, 0)  along <0, 100>  exits along <79, 60>           
cyan @ (-100, 50)  along <-100, 100>  exits along <20, 140>  SELECTED

I set this up (hopefully I did this right) using the version of the test program that I posted at reply #63
https://www.qb64.org/forum/index.php?topic=3866.msg132698#msg132698
(play with this one some and see how unit normal component and unit tangent component vectors mirror around the incoming vectors, they're colored according to where they get their influence)

  [ You are not allowed to view this attachment ]  

The first thing that jumps out at me is not so much that it arrives at different exit vectors, but that the scenario, as presented, would not happen in a properly designed application. The balls, if I interpret your scenario correctly, would be starting from an overlapping state, and a slower ball would be colliding from behind with a faster ball. We can't expect plausible outputs from implausible inputs. I believe that such things were the reason my billiard balls were acting magnet like, because they had overlapped into a situation where the calculations could no longer output physically tenable data. It was a garbage in/garbage out situation.

The test bed assumes that balls are in an impacting state after having traveled in on a given vector, it doesn't make judgements as to whether the travel vectors and starting positions can happen in a proper physics model. It only forces an impact point by adjusting ball radii, then gives exit vectors.

Quote
So there is the unit tangent and unit normal vector.  The unit normal vector is also known as the strike vector.  The unit tangent vector is at a right angle to the unit normal vector.

un = unit normal vector
ut = unit tangent vector

lets calculate un

un =  er wait .. what the hek is that again ........
cyan ball position -100x  50y
red ball position        0x     0y

normal vector (strike vector) is -100x   50y

unit normal vector =
(-100^2+ 50^2 )^.5 = 111.8033

-100x / 111.8033 = -.8944x
50y / 111.8033 = .4472y

un =   -.8944x    .4472y   
ut is easy to calculate because it is at right angle to un
multiply y by -1
swap x with y and y with x

ut  = -.4472x   -.8944y 

All looks good to me.

Quote

The literature says that UT is tangent to both balls but with the red ball centered at 0x  0y,  the above math indicates that ut can be in any position and still be the same vector

That's how vectors work. They can be anywhere in the universe, but it doesn't change their direction and magnitude, and the math will still work the same. It doesn't matter where red ball is except for obtaining the UN vector. After that you've got UT vector as you did above. It's at this point that the Dot Product calculations step in and determine where the balls will deflect based upon which way they came in. The unit normal (UN) is how the opposite ball influences the collision and the unit tangent (UT) is how a ball's incoming affects itself upon exit. Those two are added together to get the final exit vector.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
Re: 2D ball collisions without trigonometry.
« Reply #93 on: May 29, 2021, 11:33:36 am »
Whenever I need to think about balls touching, I use these notes. Same thing Moses is saying:

  [ You are not allowed to view this attachment ]  
  [ You are not allowed to view this attachment ]  
You're not done when it works, you're done when it's right.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #94 on: May 29, 2021, 06:17:12 pm »
@OldMoses
Im using this version.

The image you showed is not the one I'm working on.

 If you type the "f" key the ball data is saved to a file called BALL STUFF.txt.  That lets you copy and paste data.

If you want to minimize the program simply comment out _FULLSCREEN,   it'll still work but you won't get that nice DOS look.

Code: QB64: [Select]
  1. 'Original code by OldMoses
  2. 'Additional code by Novarseg
  3.  
  4. TYPE V2
  5.     x AS SINGLE
  6.     y AS SINGLE
  7.  
  8. TYPE ball
  9.     cn AS STRING * 4 '                                          ball name by color
  10.     c AS _UNSIGNED LONG '                                       color
  11.     p AS V2 '                                                   position
  12.     m AS V2 '                                                   movement (pre-contact) incoming
  13.     x AS V2 '                                                   vector of post contact movement
  14.     s AS INTEGER '                                              magnitude of movement
  15.  
  16.  
  17. DIM TEXT(1) AS STRING
  18. DIM RAD(1) AS SINGLE
  19. RAD = 0
  20. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  21. DIM mouse AS V2
  22. DIM b(1) AS ball
  23.  
  24. 'DIM SHARED AS V2 origin
  25. DIM SHARED origin AS V2
  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(_DESKTOPWIDTH, _DESKTOPHEIGHT, 32) 'prevents FULLSCREEN distortion
  31.  
  32. ' _SCREENMOVE 10, 10
  33.  
  34. MAG = _DESKTOPHEIGHT / 300 'this 300 is from the grid drawing code.
  35.  
  36.  
  37. _FULLSCREEN 'gives a full screen - not a window!
  38.  
  39. 'starting state
  40. b(0).m.x = 0: b(0).m.y = 100 '    reds approach vector
  41. b(0).s = PyT(origin, b(0).m)
  42. b(1).m.x = -100: b(1).m.y = 0 '   cyans approach vector
  43. RAD(1) = 3 * _PI / 2
  44. b(1).s = PyT(origin, b(1).m)
  45. b(0).p.x = 0: b(0).p.y = 0 '     ball position x, y
  46. b(1).p.x = -100: b(1).p.y = 100 'ball position x, y
  47.  
  48.     CLS
  49.     ms = MBS '                                                  process mouse actions dragging endpoints
  50.     IF ms AND 64 THEN
  51.  
  52.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  53.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  54.  
  55.         FOR x = 0 TO 1
  56.             FOR y = 0 TO 1
  57.                 ds! = PyT(vertex(x, y), mouse)
  58.                 IF ds! < ballradius * .5 THEN i = x: j = y
  59.             NEXT y
  60.         NEXT x
  61.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  62.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  63.                 b(i).p = mouse
  64.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  65.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  66.         END SELECT
  67.     END IF
  68.  
  69.     'IF _KEYDOWN(114) THEN i = 0
  70.     'IF _KEYDOWN(99) THEN i = 1
  71.     IF _KEYDOWN(18432) THEN b(i).p.y = b(i).p.y + 1
  72.     IF _KEYDOWN(20480) THEN b(i).p.y = b(i).p.y - 1
  73.     IF _KEYDOWN(19200) THEN b(i).p.x = b(i).p.x - 1
  74.     IF _KEYDOWN(19712) THEN b(i).p.x = b(i).p.x + 1
  75.  
  76.     'IF _KEYDOWN(119) THEN b(i).m.y = b(i).m.y - 1
  77.     'IF _KEYDOWN(115) THEN b(i).m.y = b(i).m.y + 1
  78.     'IF _KEYDOWN(97) THEN b(i).m.x = b(i).m.x + 1
  79.     'IF _KEYDOWN(100) THEN b(i).m.x = b(i).m.x - 1
  80.  
  81.     '**************Code added by Novarseg
  82.     'Vector rotation and vector magnitude adjustment using keyboard input
  83.  
  84.     I$ = INKEY$
  85.  
  86.     _DELAY .04 'allows enough time for keyboard buffer to accumulate characters
  87.     '           so the vector rotation (fast / slow) operates properly
  88.     '           there is probably a better way to do this
  89.     IF I$ = "" THEN f1 = 0
  90.  
  91.     IF f3 = 0 THEN I$ = "b": f3 = 1
  92.     IF I$ = "b" AND f2 = 0 THEN i = 1: f2 = 1: c(1) = "  SELECTED": c(0) = "           ": GOTO LL1 'cyan
  93.     IF I$ = "b" AND f2 = 1 THEN i = 0: f2 = 0: c(0) = "  SELECTED": c(1) = "           " 'red
  94.     LL1:
  95.  
  96.     IF I$ = "c" THEN 'increase vector magnitude
  97.         mult = 1.01
  98.         VecMult b(i).m, mult
  99.     END IF
  100.  
  101.     IF I$ = "v" THEN 'decrease vector magnitude
  102.         div = 1.01
  103.         VecDIV b(i).m, div 'added a new sub
  104.     END IF
  105.  
  106.     IF I$ = "z" THEN 'rotate vector counter clockwise
  107.         IF RAD(i) > _PI * 2 THEN RAD(i) = 0
  108.  
  109.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  110.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) + .05
  111.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) + .005
  112.  
  113.  
  114.         b(i).s = PyT(origin, b(i).m)
  115.         b(i).m.y = b(i).s * COS(RAD(i))
  116.         b(i).m.x = b(i).s * SIN(RAD(i))
  117.  
  118.     END IF
  119.  
  120.     IF I$ = "x" THEN 'rotate vector clockwise
  121.         IF RAD(i) < 0 THEN RAD(i) = _PI * 2
  122.  
  123.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  124.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) - .05
  125.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) - .005
  126.  
  127.         b(i).s = PyT(origin, b(i).m)
  128.         b(i).m.y = b(i).s * COS(RAD(i))
  129.         b(i).m.x = b(i).s * SIN(RAD(i))
  130.     END IF
  131.     '**************END Code added Novarseg
  132.  
  133.  
  134.     'START OF COLLISION MATHEMATICS SECTION
  135.  
  136.     ballradius = PyT(b(0).p, b(1).p) / 2
  137.  
  138.     FOR bn = 0 TO 1
  139.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  140.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  141.     NEXT bn
  142.  
  143.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  144.     B2BCollision b(0), b(1)
  145.     'END OF COLLISION MATHEMATICS SECTION
  146.  
  147.     'graphic representation
  148.     FOR grid = -300 TO 300 STEP 20
  149.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  150.         LINE (grid, 300)-(grid, -300), c& 'Gray  'vertical lines
  151.         LINE (-300, grid)-(300, grid), c& ' Gray  'horizontal lines
  152.     NEXT grid
  153.  
  154.  
  155.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  156.  
  157.     FOR dr = 0 TO 1
  158.  
  159.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c, , , 1 'AR
  160.         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
  161.  
  162.         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
  163.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  164.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  165.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">" + c(dr)
  166.  
  167.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  168.  
  169.         TEXT(dr) = b$ + CHR$(13) + CHR$(10) 'NOVARSEG added this line
  170.     NEXT dr
  171.  
  172.     IF _KEYHIT = ASC("f") THEN 'NOVARSEG added this line
  173.         OPEN "BALL STUFF.TXT" FOR BINARY AS #1 'NOVARSEG added this line
  174.         PUT #1, , TEXT(0) 'NOVARSEG added this line
  175.         PUT #1, , TEXT(1) 'NOVARSEG added this line
  176.         CLOSE 'NOVARSEG added this line
  177.     END IF 'NOVARSEG added this line
  178.  
  179.     _LIMIT 500
  180.     _DISPLAY
  181.  
  182.  
  183. SUB B2BCollision (ball1 AS ball, ball2 AS ball)
  184.  
  185.     ' DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  186.     DIM un AS V2
  187.     DIM ut AS V2
  188.     DIM ncomp1 AS V2
  189.     DIM ncomp2 AS V2
  190.     DIM tcomp1 AS V2
  191.     DIM tcomp2 AS V2
  192.  
  193.  
  194.     un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  195.     ut.x = -un.y: ut.y = un.x '                                 establish unit tangent
  196.     bnci1 = VecDot(un, ball1.m) '
  197.     bnci2 = VecDot(un, ball2.m) '
  198.     btci1 = VecDot(ut, ball1.m) '
  199.     btci2 = VecDot(ut, ball2.m) '
  200.  
  201.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  202.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  203.  
  204.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  205.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  206.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  207.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  208.  
  209.     ball1.x = ncomp1: VecAdd ball1.x, tcomp1, 1 '               add normal and tangent exit vectors
  210.     ball2.x = ncomp2: VecAdd ball2.x, tcomp2, 1 '               add normal and tangent exit vectors
  211.  
  212. END SUB 'B2BCollision
  213.  
  214.  
  215. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  216.  
  217.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  218.  
  219.  
  220.  
  221. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  222.     STATIC StartTimer AS _FLOAT
  223.     STATIC ButtonDown AS INTEGER
  224.     STATIC ClickCount AS INTEGER
  225.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  226.     '                          Down longer counts as a HOLD event.
  227.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  228.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  229.         SELECT CASE SGN(_MOUSEWHEEL)
  230.             CASE 1: MBS = MBS OR 512
  231.             CASE -1: MBS = MBS OR 1024
  232.         END SELECT
  233.     WEND
  234.  
  235.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  236.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  237.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  238.  
  239.     IF StartTimer = 0 THEN
  240.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  241.             ButtonDown = 1: StartTimer = TIMER(0.01)
  242.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  243.         ELSEIF _MOUSEBUTTON(2) THEN
  244.             ButtonDown = 2: StartTimer = TIMER(0.01)
  245.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  246.         ELSEIF _MOUSEBUTTON(3) THEN
  247.             ButtonDown = 3: StartTimer = TIMER(0.01)
  248.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  249.         END IF
  250.     ELSE
  251.         BD = ButtonDown MOD 3
  252.         IF BD = 0 THEN BD = 3
  253.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  254.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  255.         ELSE
  256.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  257.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  258.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  259.             ELSE 'We've now started the hold event
  260.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  261.             END IF
  262.         END IF
  263.     END IF
  264.  
  265.  
  266. FUNCTION PyT (var1 AS V2, var2 AS V2)
  267.  
  268.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  269.  
  270.  
  271. FUNCTION bPyT (var1 AS V2, var2 AS V2, var3) 'to calulate ball radius only
  272.     'var1.x = var1.x * 1.6384
  273.     bPyT = _HYPOT((var1.x - var2.x) * var3, (var1.y - var2.y) * var3)
  274.  
  275.  
  276.  
  277.  
  278. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  279.  
  280.     var.x = -(var.x + (var2.x * var3)) '                           add vector (or a scalar multiple of) var2 to var)
  281.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  282.  
  283. END SUB 'Add_Vector
  284.  
  285.  
  286. FUNCTION VecDot (var AS V2, var2 AS V2)
  287.  
  288.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  289.  
  290. END FUNCTION 'VecDot
  291.  
  292.  
  293. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  294.  
  295.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  296.     vec.y = vec.y * multiplier
  297.  
  298. END SUB 'Vec_Mult
  299.  
  300. SUB VecDIV (vec AS V2, divisor AS SINGLE) 'added by Novarseg
  301.  
  302.     vec.x = vec.x / divisor
  303.     vec.y = vec.y / divisor
  304.  
  305. END SUB 'VecDIV
  306.  
  307.  
  308. SUB VecNorm (var AS V2)
  309.  
  310.     m = PyT(origin, var)
  311.     IF m = 0 THEN
  312.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  313.     ELSE
  314.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  315.     END IF
  316.  
  317. END SUB 'VecNorm

@STxAxTIC

your notes solve with unit normal vector.  Looks like unit tangent vector is not necessary?
« Last Edit: May 29, 2021, 07:40:27 pm by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
Re: 2D ball collisions without trigonometry.
« Reply #95 on: May 29, 2021, 06:37:16 pm »
If you add the four LINE statements to the end of SUB B2BCollision, as shown below, that will show how the vector components react to changes in ball position and velocity. It should work with your version and you can comment them out if you don't want or need them any more. It may help with visualizing the dot product interaction when you can see what component of the exit velocity comes from a ball's own velocity and what comes from the other ball.

Code: QB64: [Select]
  1. SUB B2BCollision (ball1 AS ball, ball2 AS ball)
  2.  
  3.     DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  4.     un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  5.     ut.x = -un.y: ut.y = un.x '                                 establish unit tangent
  6.     bnci1 = VecDot(un, ball1.m) '                               ball 1 normal component of input velocity
  7.     bnci2 = VecDot(un, ball2.m) '                               ball 2 normal component of input velocity
  8.     btci1 = VecDot(ut, ball1.m) '                               ball 1 tangent component of input velocity
  9.     btci2 = VecDot(ut, ball2.m) '                               ball 2 tangent component of input velocity
  10.  
  11.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  12.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  13.  
  14.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  15.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  16.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  17.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  18.  
  19.     ball1.x = ncomp1: VecAdd ball1.x, tcomp1, 1 '               add normal and tangent exit vectors
  20.     ball2.x = ncomp2: VecAdd ball2.x, tcomp2, 1 '               add normal and tangent exit vectors
  21.  
  22.     'The following is for graphic representation only and can be removed without affecting the SUB's purpose
  23.     LINE (ball1.p.x, ball1.p.y)-(ball1.p.x + ncomp1.x, ball1.p.y + ncomp1.y), &H9F00FFFF, , &B0011001100110011
  24.     LINE (ball1.p.x, ball1.p.y)-(ball1.p.x + tcomp1.x, ball1.p.y + tcomp1.y), &H9FFF7F7F, , &B0011001100110011
  25.     LINE (ball2.p.x, ball2.p.y)-(ball2.p.x + ncomp2.x, ball2.p.y + ncomp2.y), &H9FFF7F7F, , &B0011001100110011
  26.     LINE (ball2.p.x, ball2.p.y)-(ball2.p.x + tcomp2.x, ball2.p.y + tcomp2.y), &H9F00FFFF, , &B0011001100110011
  27.  
  28. END SUB 'B2BCollision
  29.  

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
Re: 2D ball collisions without trigonometry.
« Reply #96 on: May 29, 2021, 10:01:44 pm »
@STxAxTIC
your notes solve with unit normal vector.  Looks like unit tangent vector is not necessary?

Correct. Tangent vector is superfluous.
You're not done when it works, you're done when it's right.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #97 on: May 29, 2021, 11:43:26 pm »
OldMoses
I added the 4 lines - looking at it now

STxAxTIC
That is interesting!.   

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #98 on: May 30, 2021, 12:38:57 am »
STxAxTIC

looking at your notes and there is the key observation

 vector q = magnitude of vector q * unit normal vector (un)

magnitude of vector q is found by

2 (un . vector v1   - un . vector v2)

where vec v1 and vec v2 are not unit vectors.

If that is the correct interpretation then how is vector q used?

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
Re: 2D ball collisions without trigonometry.
« Reply #99 on: May 30, 2021, 11:18:13 am »
@NOVARSEG

Hey - yeah I've been meaning to type those notes for a few years but I'm not "on that chapter" yet (long story). Anyway... I did a fairly-ok job of distinguishing between full vectors (with the arrow) and unit vectors (with the hat). Throughout the whole thing, the vectors v1 and v2 are always full vectors, never unit vectors.

Keep in mind too I use the reduced mass term (greek mu) to calculate q

\mu = (m1 + m2) / (m1 * m2)
You're not done when it works, you're done when it's right.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #100 on: June 02, 2021, 12:10:05 am »
Getting close to a solution. Thanks to everyone on this thread. So much information to distill.

Program shows the normal and tangent vectors as dashed lines.  The code is mess but at least I got some results.  The code still does not show the exit vectors.

Well, there are some ball angles that don't make sense at all.

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

Does not seem correct.

****************
Looking at STxAxTIC's notes about momentum exchange

****************
Looking at this paper again  https://www.vobarian.com/collisions/2dcollisions2.pdf

I looked at that paper before, but it was of no use to me at the time, because I did not understand the math.


Code: QB64: [Select]
  1. 'Original code by OldMoses
  2. 'Additional code by Novarseg
  3.  
  4. TYPE V2
  5.     x AS SINGLE
  6.     y AS SINGLE
  7.  
  8. TYPE ball
  9.     cn AS STRING * 4 '                                          ball name by color
  10.     c AS _UNSIGNED LONG '                                       color
  11.     p AS V2 '                                                   ball position
  12.     m AS V2 '                                                   heading vector
  13.     x AS V2 '                                                   exit vector
  14.     s AS INTEGER '                                              magnitude of movement
  15.  
  16. DIM Nvec(1) AS V2
  17. DIM Tvec(1) AS V2
  18. DIM TEXT(1) AS STRING
  19. DIM RAD(1) AS SINGLE
  20. RAD = 0
  21. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  22. DIM mouse AS V2
  23. DIM b(1) AS ball
  24.  
  25. 'DIM SHARED AS V2 origin
  26. DIM SHARED origin AS V2
  27. origin.x = 0: origin.y = 0
  28. b(0).c = Red: b(0).cn = "red"
  29. b(1).c = Cyan: b(1).cn = "cyan"
  30.  
  31. SCREEN _NEWIMAGE(_DESKTOPWIDTH, _DESKTOPHEIGHT, 32) 'prevents FULLSCREEN distortion
  32.  
  33. ' _SCREENMOVE 10, 10
  34.  
  35. MAG = _DESKTOPHEIGHT / 300 'this 300 is from the grid drawing code.
  36. 'MAG = 4
  37.  
  38. _FULLSCREEN 'gives a full screen - not a window!
  39.  
  40. 'starting state
  41. b(0).m.x = 0: b(0).m.y = 100 '    reds approach vector
  42. RAD(0) = 2 * _PI / 2
  43. b(0).s = PyT(origin, b(0).m)
  44.  
  45. b(1).m.x = -100: b(1).m.y = 0 '   cyans approach vector
  46. RAD(1) = 3 * _PI / 2
  47. b(1).s = PyT(origin, b(1).m)
  48.  
  49. b(0).p.x = 0: b(0).p.y = 0 '     ball position x, y
  50. b(1).p.x = -100: b(1).p.y = 100 'ball position x, y
  51.  
  52.     CLS
  53.     ms = MBS '                                                  process mouse actions dragging endpoints
  54.     IF ms AND 64 THEN
  55.  
  56.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  57.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  58.  
  59.         FOR x = 0 TO 1
  60.             FOR y = 0 TO 1
  61.                 ds! = PyT(vertex(x, y), mouse)
  62.                 IF ds! < ballradius * .5 THEN i = x: j = y
  63.             NEXT y
  64.         NEXT x
  65.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  66.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  67.                 b(i).p = mouse
  68.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  69.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  70.         END SELECT
  71.     END IF
  72.  
  73.     'IF _KEYDOWN(114) THEN i = 0
  74.     'IF _KEYDOWN(99) THEN i = 1
  75.     IF _KEYDOWN(18432) THEN b(i).p.y = b(i).p.y + 1
  76.     IF _KEYDOWN(20480) THEN b(i).p.y = b(i).p.y - 1
  77.     IF _KEYDOWN(19200) THEN b(i).p.x = b(i).p.x - 1
  78.     IF _KEYDOWN(19712) THEN b(i).p.x = b(i).p.x + 1
  79.  
  80.     'IF _KEYDOWN(119) THEN b(i).m.y = b(i).m.y - 1
  81.     'IF _KEYDOWN(115) THEN b(i).m.y = b(i).m.y + 1
  82.     'IF _KEYDOWN(97) THEN b(i).m.x = b(i).m.x + 1
  83.     'IF _KEYDOWN(100) THEN b(i).m.x = b(i).m.x - 1
  84.  
  85.     '**************Code added by Novarseg
  86.     'Vector rotation and vector magnitude adjustment using keyboard input
  87.  
  88.     IF f4 = 0 THEN
  89.         f4 = 1
  90.         FOR i = 0 TO 1
  91.  
  92.             b(i).s = PyT(origin, b(i).m)
  93.             b(i).m.y = b(i).s * COS(RAD(i))
  94.             b(i).m.x = b(i).s * SIN(RAD(i))
  95.         NEXT i
  96.         i = 0
  97.     END IF
  98.  
  99.     I$ = INKEY$
  100.  
  101.     _DELAY .04 'allows enough time for keyboard buffer to accumulate characters
  102.     '           so the vector rotation (fast / slow) operates properly
  103.     '           there is probably a better way to do this
  104.     IF I$ = "" THEN f1 = 0
  105.  
  106.     'IF f3 = 0 THEN I$ = "b": f3 = 1
  107.     IF I$ = "b" AND f2 = 0 THEN i = 1: f2 = 1: c(1) = "  SELECTED": c(0) = "           ": GOTO LL1 'cyan
  108.     IF I$ = "b" AND f2 = 1 THEN i = 0: f2 = 0: c(0) = "  SELECTED": c(1) = "           " 'red
  109.     LL1:
  110.  
  111.     IF I$ = "c" THEN 'increase vector magnitude
  112.         mult = 1.01
  113.         VecMult b(i).m, mult
  114.     END IF
  115.  
  116.     IF I$ = "v" THEN 'decrease vector magnitude
  117.         div = 1.01
  118.         VecDIV b(i).m, div 'added a new sub
  119.     END IF
  120.  
  121.     IF I$ = "z" THEN 'rotate vector counter clockwise
  122.         IF RAD(i) > _PI * 2 THEN RAD(i) = 0
  123.  
  124.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  125.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) + .05
  126.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) + .005
  127.  
  128.  
  129.         b(i).s = PyT(origin, b(i).m)
  130.         b(i).m.y = b(i).s * COS(RAD(i))
  131.         b(i).m.x = b(i).s * SIN(RAD(i))
  132.  
  133.     END IF
  134.  
  135.     IF I$ = "x" THEN 'rotate vector clockwise
  136.         IF RAD(i) < 0 THEN RAD(i) = _PI * 2
  137.  
  138.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  139.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) - .05
  140.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) - .005
  141.  
  142.         b(i).s = PyT(origin, b(i).m)
  143.         b(i).m.y = b(i).s * COS(RAD(i))
  144.         b(i).m.x = b(i).s * SIN(RAD(i))
  145.     END IF
  146.     '**************END Code added Novarseg
  147.  
  148.  
  149.     'START OF COLLISION MATHEMATICS SECTION
  150.  
  151.     ballradius = PyT(b(0).p, b(1).p) / 2
  152.  
  153.     FOR bn = 0 TO 1
  154.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  155.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  156.     NEXT bn
  157.  
  158.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  159.     B2BCollision b(0), b(1), Nvec(), Tvec()
  160.     'END OF COLLISION MATHEMATICS SECTION
  161.  
  162.     'graphic representation
  163.     FOR grid = -300 TO 300 STEP 20
  164.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  165.         LINE (grid, 300)-(grid, -300), c& 'Gray  'vertical lines
  166.         LINE (-300, grid)-(300, grid), c& ' Gray  'horizontal lines
  167.     NEXT grid
  168.  
  169.  
  170.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  171.  
  172.     FOR dr = 0 TO 1
  173.  
  174.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c, , , 1 'AR
  175.  
  176.         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
  177.  
  178.         '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
  179.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Nvec(dr).x, b(dr).p.y + Nvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  180.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Tvec(dr).x, b(dr).p.y + Tvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  181.  
  182.  
  183.  
  184.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  185.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  186.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">" + c(dr)
  187.  
  188.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  189.  
  190.         TEXT(dr) = b$ + CHR$(13) + CHR$(10) 'NOVARSEG added this line
  191.     NEXT dr
  192.  
  193.     IF _KEYHIT = ASC("f") THEN 'NOVARSEG added this line
  194.         OPEN "BALL STUFF.TXT" FOR BINARY AS #1 'NOVARSEG added this line
  195.         PUT #1, , TEXT(0) 'NOVARSEG added this line
  196.         PUT #1, , TEXT(1) 'NOVARSEG added this line
  197.         CLOSE 'NOVARSEG added this line
  198.     END IF 'NOVARSEG added this line
  199.  
  200.     _LIMIT 500
  201.     _DISPLAY
  202.  
  203.  
  204. SUB B2BCollision (ball0 AS ball, ball1 AS ball, Nvec() AS V2, Tvec() AS V2)
  205.  
  206.     ' DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  207.     DIM un AS V2
  208.     DIM ut AS V2
  209.     DIM ncomp1 AS V2
  210.     DIM ncomp2 AS V2
  211.     DIM tcomp1 AS V2
  212.     DIM tcomp2 AS V2
  213.     DIM MAG AS SINGLE
  214.  
  215.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  216.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  217.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  218.     DIM utDOT1 AS SINGLE ' unit tangent vector "dot" pre collision heading ball1
  219.  
  220.     DIM unVec0 AS V2
  221.     DIM utVec0 AS V2
  222.     DIM unVec1 AS V2
  223.     DIM utVec1 AS V2
  224.     ' DIM nvec(1) AS V2
  225.     ' DIM Tvec(1) AS V2
  226.  
  227.  
  228.     '  GOSUB getUnitNormal uses  ball1.p ball2.p
  229.  
  230.     'calculate unit normql vector from ball positions
  231.     'ball0 = red, ball1 =cyan
  232.     un.x = ball1.p.x - ball0.p.x
  233.     un.y = ball1.p.y - ball0.p.y
  234.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  235.     un.x = un.x / MAG
  236.     un.y = un.y / MAG
  237.  
  238.     'un = ball0.p: VecAdd un, ball1.p, -1: VecNorm un '
  239.     'un = ball1.p
  240.     'VecAdd un, ball1.p, -1
  241.     ' VecNorm un '          establish unit normal
  242.  
  243.  
  244.     ut.x = -un.y: ut.y = un.x '     establish unit tangent
  245.  
  246.  
  247.     unDOT0 = un.x * ball0.m.x + un.y * ball0.m.y 'unit normal vector DOT ball vector
  248.     unDOT1 = un.x * ball1.m.x + un.y * ball1.m.y 'unit normal vector DOT ball vector
  249.     utDOT0 = ut.x * ball0.m.x + ut.y * ball0.m.y 'unit tangent vector DOT ball vector
  250.     utDOT1 = ut.x * ball1.m.x + ut.y * ball1.m.y 'unit tangent vector DOT ball vector
  251.  
  252.  
  253.     'bnci1 = VecDot(un, ball0.m) '
  254.     'bnci2 = VecDot(un, ball1.m) '
  255.     'btci1 = VecDot(ut, ball0.m) '
  256.     'btci2 = VecDot(ut, ball1.m) '
  257.  
  258.     ' bncx1 = bnci2 '                                   compute normal component of ball 1 exit velocity
  259.     ' bncx2 = bnci1 '                                   compute normal component of ball 2 exit velocity
  260.  
  261.  
  262.  
  263.  
  264.     ' ncomp1 = un:
  265.  
  266.     VecMult2 un, unDOT0, unVec0 '    first two parameters are inputs, the 3rd is output vector
  267.     VecMult2 ut, utDOT0, utVec0 '
  268.     VecMult2 un, unDOT1, unVec1 '
  269.     VecMult2 ut, utDOT1, utVec1 '
  270.  
  271.     LOCATE 30, 10: PRINT "unvec0.x"; unVec0.x
  272.     LOCATE 31, 10: PRINT "unvec0.y"; unVec0.y
  273.  
  274.     LOCATE 32, 10: PRINT "utvec0.x"; utVec0.x
  275.     LOCATE 33, 10: PRINT "utvec0.y"; utVec0.y
  276.  
  277.  
  278.     'ncomp1 = un: VecMult ncomp1, bncx2 '
  279.     'tcomp1 = ut: VecMult tcomp1, btci1 '               unit tangent exit vector x tangent component of exit vector
  280.     'ncomp2 = un: VecMult ncomp2, bncx2 '               same for ball2, unit normal...
  281.     'tcomp2 = ut: VecMult tcomp2, btci2 '               same for ball2, unit tangent...
  282.  
  283.     'ball0.x = ncomp1: VecAdd ball0.x, tcomp1, 1 '      add normal and tangent exit vectors
  284.     'ball1.x = ncomp2: VecAdd ball1.x, tcomp2, 1 '      add normal and tangent exit vectors
  285.  
  286.     'VecAdd2 unVec1, utVec1, Nvec(1) '
  287.     'VecAdd2 unVec0, utVec0, Nvec(0) '
  288.     Nvec(0).x = unVec0.x
  289.     Nvec(0).y = unVec0.y
  290.  
  291.     Nvec(1).x = unVec1.x
  292.     Nvec(1).y = unVec1.y
  293.  
  294.     Tvec(1).x = -utVec0.x
  295.     Tvec(1).y = -utVec0.y
  296.  
  297.     Tvec(0).x = -utVec1.x
  298.     Tvec(0).y = -utVec1.y
  299.  
  300.  
  301. END SUB 'B2BCollision
  302.  
  303.  
  304. SUB B2B2Collision (ball0 AS ball, ball1 AS ball)
  305.  
  306.     ' DIM AS V2 un, ut, ncomp1, ncomp2, tcomp1, tcomp2
  307.     DIM un AS V2
  308.     DIM ut AS V2
  309.     DIM ncomp1 AS V2
  310.     DIM ncomp2 AS V2
  311.     DIM tcomp1 AS V2
  312.     DIM tcomp2 AS V2
  313.  
  314.     DIM MAG AS SINGLE
  315.  
  316.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  317.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  318.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  319.     DIM utDOT1 AS SINGLE ' unit tangent vector "dot" pre collision heading ball1
  320.  
  321.     DIM unVec0 AS V2
  322.     DIM utVec0 AS V2
  323.     DIM unVec1 AS V2
  324.     DIM utVec1 AS V2
  325.  
  326.  
  327.     '  GOSUB getUnitNormal uses  ball1.p ball2.p
  328.  
  329.     'calculate unit normql vector from ball positions
  330.     'ball0 = red, ball1 =cyan
  331.     un.x = ball1.p.x - ball0.p.x
  332.     un.y = ball1.p.y - ball0.p.y
  333.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  334.     un.x = un.x / MAG
  335.     un.y = un.y / MAG
  336.  
  337.  
  338.     ' un = ball2.p: VecAdd un, ball1.p, -1: VecNorm un '          establish unit normal
  339.     ut.x = un.y: ut.y = un.x '                                 establish unit tangent
  340.     bnci1 = VecDot(un, ball0.m) '
  341.     bnci2 = VecDot(un, ball1.m) '
  342.     btci1 = VecDot(ut, ball0.m) '
  343.     btci2 = VecDot(ut, ball1.m) '
  344.  
  345.     bncx1 = bnci2 '                                             compute normal component of ball 1 exit velocity
  346.     bncx2 = bnci1 '                                             compute normal component of ball 2 exit velocity
  347.  
  348.     ncomp1 = un: VecMult ncomp1, bncx1 '                        unit normal exit vector x normal component of exit vector ball1
  349.     tcomp1 = ut: VecMult tcomp1, btci1 '                        unit tangent exit vector x tangent component of exit vector
  350.     ncomp2 = un: VecMult ncomp2, bncx2 '                        same for ball2, unit normal...
  351.     tcomp2 = ut: VecMult tcomp2, btci2 '                        same for ball2, unit tangent...
  352.  
  353.     ball0.x = ncomp1: VecAdd ball0.x, tcomp1, 1 '               add normal and tangent exit vectors
  354.     ball1.x = ncomp2: VecAdd ball1.x, tcomp2, 1 '               add normal and tangent exit vectors
  355.  
  356. END SUB 'B2BCollision
  357.  
  358.  
  359.  
  360. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  361.  
  362.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  363.  
  364.  
  365.  
  366. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  367.     STATIC StartTimer AS _FLOAT
  368.     STATIC ButtonDown AS INTEGER
  369.     STATIC ClickCount AS INTEGER
  370.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  371.     '                          Down longer counts as a HOLD event.
  372.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  373.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  374.         SELECT CASE SGN(_MOUSEWHEEL)
  375.             CASE 1: MBS = MBS OR 512
  376.             CASE -1: MBS = MBS OR 1024
  377.         END SELECT
  378.     WEND
  379.  
  380.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  381.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  382.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  383.  
  384.     IF StartTimer = 0 THEN
  385.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  386.             ButtonDown = 1: StartTimer = TIMER(0.01)
  387.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  388.         ELSEIF _MOUSEBUTTON(2) THEN
  389.             ButtonDown = 2: StartTimer = TIMER(0.01)
  390.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  391.         ELSEIF _MOUSEBUTTON(3) THEN
  392.             ButtonDown = 3: StartTimer = TIMER(0.01)
  393.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  394.         END IF
  395.     ELSE
  396.         BD = ButtonDown MOD 3
  397.         IF BD = 0 THEN BD = 3
  398.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  399.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  400.         ELSE
  401.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  402.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  403.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  404.             ELSE 'We've now started the hold event
  405.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  406.             END IF
  407.         END IF
  408.     END IF
  409.  
  410.  
  411. FUNCTION PyT (var1 AS V2, var2 AS V2)
  412.  
  413.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  414.  
  415.  
  416. FUNCTION bPyT (var1 AS V2, var2 AS V2, var3) 'to calulate ball radius only
  417.     'var1.x = var1.x * 1.6384
  418.     bPyT = _HYPOT((var1.x - var2.x) * var3, (var1.y - var2.y) * var3)
  419.  
  420.  
  421.  
  422.  
  423. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  424.  
  425.     var.x = -(var.x + (var2.x * var3)) '                           add vector (or a scalar multiple of) var2 to var)
  426.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  427.  
  428. END SUB 'Add_Vector
  429.  
  430. SUB VecAdd2 (var1 AS V2, var2 AS V2, var3 AS V2)
  431.  
  432.     'var3.x = var1.x + var2.x '                           add vector (or a scalar multiple of) var2 to var)
  433.     'var3.y = var1.y + var2.y '                           use var3 = -1 to subtract var2 from var
  434.  
  435.     var3.x = var1.x '+ var2.x '                           add vector (or a scalar multiple of) var2 to var)
  436.     var3.y = var1.y '+ var2.y '                           use var3 = -1 to subtract var2 from var
  437.  
  438. END SUB 'Add_Vector
  439.  
  440.  
  441. FUNCTION VecDot (var AS V2, var2 AS V2)
  442.  
  443.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  444.  
  445. END FUNCTION 'VecDot
  446.  
  447.  
  448. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  449.  
  450.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  451.     vec.y = vec.y * multiplier
  452.  
  453. END SUB 'Vec_Mult
  454.  
  455. SUB VecMult2 (var1 AS V2, var2 AS SINGLE, out1 AS V2)
  456.  
  457.     out1.x = var1.x * var2
  458.     out1.y = var1.y * var2
  459.  
  460. END SUB 'Vec_Mult
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468. SUB VecDIV (vec AS V2, divisor AS SINGLE) 'added by Novarseg
  469.  
  470.     vec.x = vec.x / divisor
  471.     vec.y = vec.y / divisor
  472.  
  473. END SUB 'VecDIV
  474.  
  475.  
  476. SUB VecNorm (var AS V2)
  477.  
  478.     m = PyT(origin, var)
  479.     IF m = 0 THEN
  480.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  481.     ELSE
  482.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  483.     END IF
  484.  
  485. END SUB 'VecNorm
  486.  
  487.  
  488.  
« Last Edit: June 02, 2021, 08:19:42 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #101 on: June 02, 2021, 11:14:25 pm »
The code seems to work now. Have a look.

Math from https://www.vobarian.com/collisions/2dcollisions2.pdf

Again much thanks to everyone on this thread.

Code: QB64: [Select]
  1.  'Original code by OldMoses
  2. 'Additional code by Novarseg
  3.  
  4. TYPE V2
  5.     x AS SINGLE
  6.     y AS SINGLE
  7.  
  8. TYPE ball
  9.     cn AS STRING * 4 '                                          ball name by color
  10.     c AS _UNSIGNED LONG '                                       color
  11.     p AS V2 '                                                   ball position
  12.     m AS V2 '                                                   heading vector
  13.     x AS V2 '                                                   exit vector
  14.     s AS INTEGER '                                              magnitude of movement
  15.  
  16. DIM Nvec(1) AS V2 'normal velocity vectors
  17. DIM Tvec(1) AS V2 'tangential velocity vectors
  18. DIM Fvec(1) AS V2 'Final or exit vectors
  19.  
  20. DIM TEXT(1) AS STRING
  21. DIM RAD(1) AS SINGLE
  22. RAD = 0
  23. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  24. DIM mouse AS V2
  25. DIM b(1) AS ball
  26.  
  27. 'DIM SHARED AS V2 origin
  28. DIM SHARED origin AS V2
  29. origin.x = 0: origin.y = 0
  30. b(0).c = Red: b(0).cn = "red"
  31. b(1).c = Cyan: b(1).cn = "cyan"
  32.  
  33. SCREEN _NEWIMAGE(_DESKTOPWIDTH, _DESKTOPHEIGHT, 32) 'prevents FULLSCREEN distortion
  34.  
  35. ' _SCREENMOVE 10, 10
  36.  
  37. MAG = _DESKTOPHEIGHT / 300 'this 300 is from the grid drawing code.
  38. 'MAG = 4
  39.  
  40. _FULLSCREEN 'gives a full screen - not a window!
  41.  
  42. 'starting state
  43. b(0).m.x = 0: b(0).m.y = 100 '    reds approach vector
  44. RAD(0) = 2 * _PI / 2
  45. b(0).s = PyT(origin, b(0).m)
  46.  
  47. b(1).m.x = -100: b(1).m.y = 0 '   cyans approach vector
  48. RAD(1) = 3 * _PI / 2
  49. b(1).s = PyT(origin, b(1).m)
  50.  
  51. b(0).p.x = 0: b(0).p.y = 0 '     ball position x, y
  52. b(1).p.x = -100: b(1).p.y = 100 'ball position x, y
  53.  
  54.     CLS
  55.     ms = MBS '                                                  process mouse actions dragging endpoints
  56.     IF ms AND 64 THEN
  57.  
  58.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  59.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  60.  
  61.         FOR x = 0 TO 1
  62.             FOR y = 0 TO 1
  63.                 ds! = PyT(vertex(x, y), mouse)
  64.                 IF ds! < ballradius * .5 THEN i = x: j = y
  65.             NEXT y
  66.         NEXT x
  67.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  68.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  69.                 b(i).p = mouse
  70.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  71.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  72.         END SELECT
  73.     END IF
  74.  
  75.     'IF _KEYDOWN(114) THEN i = 0
  76.     'IF _KEYDOWN(99) THEN i = 1
  77.     IF _KEYDOWN(18432) THEN b(i).p.y = b(i).p.y + 1
  78.     IF _KEYDOWN(20480) THEN b(i).p.y = b(i).p.y - 1
  79.     IF _KEYDOWN(19200) THEN b(i).p.x = b(i).p.x - 1
  80.     IF _KEYDOWN(19712) THEN b(i).p.x = b(i).p.x + 1
  81.  
  82.     'IF _KEYDOWN(119) THEN b(i).m.y = b(i).m.y - 1
  83.     'IF _KEYDOWN(115) THEN b(i).m.y = b(i).m.y + 1
  84.     'IF _KEYDOWN(97) THEN b(i).m.x = b(i).m.x + 1
  85.     'IF _KEYDOWN(100) THEN b(i).m.x = b(i).m.x - 1
  86.  
  87.     '**************Code added by Novarseg
  88.     'Vector rotation and vector magnitude adjustment using keyboard input
  89.  
  90.     IF f4 = 0 THEN
  91.         f4 = 1
  92.         FOR i = 0 TO 1
  93.  
  94.             b(i).s = PyT(origin, b(i).m)
  95.             b(i).m.y = b(i).s * COS(RAD(i))
  96.             b(i).m.x = b(i).s * SIN(RAD(i))
  97.         NEXT i
  98.         i = 0
  99.     END IF
  100.  
  101.     I$ = INKEY$
  102.  
  103.     _DELAY .04 'allows enough time for keyboard buffer to accumulate characters
  104.     '           so the vector rotation (fast / slow) operates properly
  105.     '           there is probably a better way to do this
  106.     IF I$ = "" THEN f1 = 0
  107.  
  108.     'IF f3 = 0 THEN I$ = "b": f3 = 1
  109.     IF I$ = "b" AND f2 = 0 THEN i = 1: f2 = 1: c(1) = "  SELECTED": c(0) = "           ": GOTO LL1 'cyan
  110.     IF I$ = "b" AND f2 = 1 THEN i = 0: f2 = 0: c(0) = "  SELECTED": c(1) = "           " 'red
  111.     LL1:
  112.  
  113.     IF I$ = "c" THEN 'increase vector magnitude
  114.         mult = 1.01
  115.         VecMult b(i).m, mult
  116.     END IF
  117.  
  118.     IF I$ = "v" THEN 'decrease vector magnitude
  119.         div = 1.01
  120.         VecDIV b(i).m, div 'added a new sub
  121.     END IF
  122.  
  123.     IF I$ = "z" THEN 'rotate vector counter clockwise
  124.         IF RAD(i) > _PI * 2 THEN RAD(i) = 0
  125.  
  126.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  127.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) + .05
  128.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) + .005
  129.  
  130.  
  131.         b(i).s = PyT(origin, b(i).m)
  132.         b(i).m.y = b(i).s * COS(RAD(i))
  133.         b(i).m.x = b(i).s * SIN(RAD(i))
  134.  
  135.     END IF
  136.  
  137.     IF I$ = "x" THEN 'rotate vector clockwise
  138.         IF RAD(i) < 0 THEN RAD(i) = _PI * 2
  139.  
  140.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  141.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) - .05
  142.         IF TIMER - t1 <= 1.5 THEN RAD(i) = RAD(i) - .005
  143.  
  144.         b(i).s = PyT(origin, b(i).m)
  145.         b(i).m.y = b(i).s * COS(RAD(i))
  146.         b(i).m.x = b(i).s * SIN(RAD(i))
  147.     END IF
  148.     '**************END Code added Novarseg
  149.  
  150.  
  151.     'START OF COLLISION MATHEMATICS SECTION
  152.  
  153.     ballradius = PyT(b(0).p, b(1).p) / 2
  154.  
  155.     FOR bn = 0 TO 1
  156.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  157.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  158.     NEXT bn
  159.  
  160.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  161.     B2BCollision b(0), b(1), Nvec(), Tvec(), Fvec()
  162.     'END OF COLLISION MATHEMATICS SECTION
  163.  
  164.     'graphic representation
  165.     FOR grid = -300 TO 300 STEP 20
  166.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  167.         LINE (grid, 300)-(grid, -300), c& 'Gray  'vertical lines
  168.         LINE (-300, grid)-(300, grid), c& ' Gray  'horizontal lines
  169.     NEXT grid
  170.  
  171.  
  172.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  173.  
  174.     FOR dr = 0 TO 1
  175.  
  176.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c, , , 1 'AR
  177.  
  178.         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
  179.  
  180.         '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
  181.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Fvec(dr).x, b(dr).p.y + Fvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  182.  
  183.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Nvec(dr).x, b(dr).p.y + Nvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  184.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Tvec(dr).x, b(dr).p.y + Tvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  185.  
  186.  
  187.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  188.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  189.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(b(dr).x.x))) + ", " + _TRIM$(STR$(INT(b(dr).x.y))) + ">" + c(dr)
  190.  
  191.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  192.  
  193.         TEXT(dr) = b$ + CHR$(13) + CHR$(10) 'NOVARSEG added this line
  194.     NEXT dr
  195.  
  196.     IF _KEYHIT = ASC("f") THEN 'NOVARSEG added this line
  197.         OPEN "BALL STUFF.TXT" FOR BINARY AS #1 'NOVARSEG added this line
  198.         PUT #1, , TEXT(0) 'NOVARSEG added this line
  199.         PUT #1, , TEXT(1) 'NOVARSEG added this line
  200.         CLOSE 'NOVARSEG added this line
  201.     END IF 'NOVARSEG added this line
  202.  
  203.     _LIMIT 500
  204.     _DISPLAY
  205.  
  206.  
  207. SUB B2BCollision (ball0 AS ball, ball1 AS ball, Nvec() AS V2, Tvec() AS V2, Fvec() AS V2)
  208.  
  209.     DIM un AS V2
  210.     DIM ut AS V2
  211.     DIM MAG AS SINGLE
  212.  
  213.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  214.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  215.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  216.     DIM utDOT1 AS SINGLE 'unit tangent vector "dot" pre collision heading ball1
  217.  
  218.     DIM unVec0 AS V2
  219.     DIM utVec0 AS V2
  220.     DIM unVec1 AS V2
  221.     DIM utVec1 AS V2
  222.  
  223.     'calculate unit normql vector from ball positions
  224.     'ball0 = red, ball1 =cyan
  225.     un.x = ball1.p.x - ball0.p.x
  226.     un.y = ball1.p.y - ball0.p.y
  227.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  228.     un.x = un.x / MAG
  229.     un.y = un.y / MAG
  230.  
  231.     ut.x = -un.y: ut.y = un.x '     establish unit tangent
  232.  
  233.     unDOT0 = un.x * ball0.m.x + un.y * ball0.m.y 'unit normal vector DOT ball vector
  234.     unDOT1 = un.x * ball1.m.x + un.y * ball1.m.y 'unit normal vector DOT ball vector
  235.     utDOT0 = ut.x * ball0.m.x + ut.y * ball0.m.y 'unit tangent vector DOT ball vector
  236.     utDOT1 = ut.x * ball1.m.x + ut.y * ball1.m.y 'unit tangent vector DOT ball vector
  237.  
  238.     'swap "unit normal" scaled ball vectors (pre collision)   according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  239.     A = unDOT0
  240.     B = unDOT1
  241.     unDOT0 = B: unDOT1 = A
  242.  
  243.     'Convert the scalar normal and tangential velocities into vectors
  244.     'according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  245.     VecMult2 un, unDOT0, unVec0 '  third parameter is output
  246.     VecMult2 ut, utDOT0, utVec0 '  third parameter is output
  247.     VecMult2 un, unDOT1, unVec1 '  third parameter is output
  248.     VecMult2 ut, utDOT1, utVec1 '  third parameter is output
  249.  
  250.     VecAdd2 unVec1, utVec1, Fvec(1) 'vector addition to calculate final ball vectors
  251.     VecAdd2 unVec0, utVec0, Fvec(0) 'vector addition to calculate final ball vectors
  252.  
  253.     Nvec(0).x = -unVec0.x 'not necessary code - for display only
  254.     Nvec(0).y = -unVec0.y 'not necessary code - for display only
  255.  
  256.     Nvec(1).x = -unVec1.x 'not necessary code - for display only
  257.     Nvec(1).y = -unVec1.y 'not necessary code - for display only
  258.  
  259.     Tvec(0).x = -utVec0.x 'not necessary code - for display only
  260.     Tvec(0).y = -utVec0.y 'not necessary code - for display only
  261.  
  262.     Tvec(1).x = -utVec1.x 'not necessary code - for display only
  263.     Tvec(1).y = -utVec1.y 'not necessary code - for display only
  264.  
  265. END SUB 'B2BCollision
  266.  
  267.  
  268. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  269.  
  270.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  271.  
  272.  
  273.  
  274. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  275.     STATIC StartTimer AS _FLOAT
  276.     STATIC ButtonDown AS INTEGER
  277.     STATIC ClickCount AS INTEGER
  278.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  279.     '                          Down longer counts as a HOLD event.
  280.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  281.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  282.         SELECT CASE SGN(_MOUSEWHEEL)
  283.             CASE 1: MBS = MBS OR 512
  284.             CASE -1: MBS = MBS OR 1024
  285.         END SELECT
  286.     WEND
  287.  
  288.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  289.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  290.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  291.  
  292.     IF StartTimer = 0 THEN
  293.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  294.             ButtonDown = 1: StartTimer = TIMER(0.01)
  295.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  296.         ELSEIF _MOUSEBUTTON(2) THEN
  297.             ButtonDown = 2: StartTimer = TIMER(0.01)
  298.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  299.         ELSEIF _MOUSEBUTTON(3) THEN
  300.             ButtonDown = 3: StartTimer = TIMER(0.01)
  301.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  302.         END IF
  303.     ELSE
  304.         BD = ButtonDown MOD 3
  305.         IF BD = 0 THEN BD = 3
  306.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  307.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  308.         ELSE
  309.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  310.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  311.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  312.             ELSE 'We've now started the hold event
  313.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  314.             END IF
  315.         END IF
  316.     END IF
  317.  
  318.  
  319. FUNCTION PyT (var1 AS V2, var2 AS V2)
  320.  
  321.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  322.  
  323.  
  324. FUNCTION bPyT (var1 AS V2, var2 AS V2, var3) 'to calulate ball radius only
  325.  
  326.     bPyT = _HYPOT((var1.x - var2.x) * var3, (var1.y - var2.y) * var3)
  327.  
  328.  
  329.  
  330.  
  331. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  332.  
  333.     var.x = -(var.x + (var2.x * var3)) '                           add vector (or a scalar multiple of) var2 to var)
  334.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  335.  
  336. END SUB 'Add_Vector
  337.  
  338. SUB VecAdd2 (var1 AS V2, var2 AS V2, var3 AS V2)
  339.  
  340.     var3.x = -var1.x + -var2.x '
  341.     var3.y = -var1.y + -var2.y '
  342.  
  343. END SUB 'Add_Vector
  344.  
  345.  
  346. FUNCTION VecDot (var AS V2, var2 AS V2)
  347.  
  348.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  349.  
  350. END FUNCTION 'VecDot
  351.  
  352.  
  353. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  354.  
  355.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  356.     vec.y = vec.y * multiplier
  357.  
  358. END SUB 'Vec_Mult
  359.  
  360. SUB VecMult2 (var1 AS V2, var2 AS SINGLE, out1 AS V2)
  361.  
  362.     out1.x = var1.x * var2
  363.     out1.y = var1.y * var2
  364.  
  365. END SUB 'Vec_Mult
  366.  
  367. SUB VecDIV (vec AS V2, divisor AS SINGLE) 'added by Novarseg
  368.  
  369.     vec.x = vec.x / divisor
  370.     vec.y = vec.y / divisor
  371.  
  372. END SUB 'VecDIV
  373.  
  374.  
  375. SUB VecNorm (var AS V2)
  376.  
  377.     m = PyT(origin, var)
  378.     IF m = 0 THEN
  379.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  380.     ELSE
  381.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  382.     END IF
  383.  
  384. END SUB 'VecNorm
  385.  

The previous code was  annoying when rotating the vectors. The vector kept rotating when the key was released.  fixed with 3 speed rotation now.

Keys to rotate vector are z or x
keys to adjust vector magnitude (ball velocity) are c or v
Key to toggle red or cyan ball is b

As in the original code, the arrow keys move the ball positions.

rotate vector has an auto  - fine / medium / coarse control.   hold down key longer to obtain effect.

Code: QB64: [Select]
  1. 'Original code and concept by OldMoses
  2. 'additional code and mods by Novarseg
  3.  
  4. TYPE V2
  5.     x AS SINGLE
  6.     y AS SINGLE
  7.  
  8. TYPE ball
  9.     cn AS STRING * 4 '                                          ball name by color
  10.     c AS _UNSIGNED LONG '                                       color
  11.     p AS V2 '                                                   ball position
  12.     m AS V2 '                                                   heading vector
  13.     x AS V2 '                                                   exit vector
  14.     s AS INTEGER '                                              magnitude of movement
  15.  
  16. DIM Nvec(1) AS V2 'normal velocity vectors
  17. DIM Tvec(1) AS V2 'tangential velocity vectors
  18. DIM Fvec(1) AS V2 'Final or exit vectors
  19.  
  20. DIM TEXT(1) AS STRING
  21. DIM RAD(1) AS SINGLE
  22.  
  23.  
  24. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  25. DIM mouse AS V2
  26. DIM b(1) AS ball
  27.  
  28. 'DIM SHARED AS V2 origin
  29. DIM SHARED origin AS V2
  30. origin.x = 0: origin.y = 0
  31. b(0).c = Red: b(0).cn = "red"
  32. b(1).c = Cyan: b(1).cn = "cyan"
  33.  
  34. SCREEN _NEWIMAGE(_DESKTOPWIDTH, _DESKTOPHEIGHT, 32) 'prevents FULLSCREEN distortion
  35.  
  36. MAG = _DESKTOPHEIGHT / 300 'this 300 is from the grid drawing code.
  37.  
  38.  
  39. _FULLSCREEN 'gives a full screen - not a window!
  40. 'advantage:  only one position - the screen!
  41. 'disadvantage: can't be minimized - or can it?
  42.  
  43. 'starting state
  44. b(0).m.x = 0: b(0).m.y = 100 '    reds approach vector
  45. RAD(0) = 2 * _PI / 2
  46. b(0).s = PyT(origin, b(0).m)
  47.  
  48. b(1).m.x = -100: b(1).m.y = 0 '   cyans approach vector
  49. RAD(1) = 3 * _PI / 2
  50. b(1).s = PyT(origin, b(1).m)
  51.  
  52. b(0).p.x = 0: b(0).p.y = 0 '     ball position x, y
  53. b(1).p.x = -100: b(1).p.y = 100 'ball position x, y
  54.  
  55. f4 = 0 'initial ball select flag
  56. f5 = 0 'inital ball display flag
  57.  
  58.  
  59.     CLS
  60.     ms = MBS '                                                  process mouse actions dragging endpoints
  61.     IF ms AND 64 THEN
  62.  
  63.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  64.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  65.  
  66.         FOR x = 0 TO 1
  67.             FOR y = 0 TO 1
  68.                 ds! = PyT(vertex(x, y), mouse)
  69.                 IF ds! < ballradius * .5 THEN i = x: j = y
  70.             NEXT y
  71.         NEXT x
  72.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  73.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  74.                 b(i).p = mouse
  75.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  76.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  77.         END SELECT
  78.     END IF
  79.  
  80.     IF kh = 18432 THEN b(i).p.y = b(i).p.y + 1
  81.     IF kh = 20480 THEN b(i).p.y = b(i).p.y - 1
  82.     IF kh = 19200 THEN b(i).p.x = b(i).p.x - 1
  83.     IF kh = 19712 THEN b(i).p.x = b(i).p.x + 1
  84.  
  85.  
  86.     'Vector rotation and vector magnitude adjustment using keyboard input
  87.  
  88.     IF f5 = 0 THEN
  89.         f5 = 1
  90.         FOR i = 0 TO 1
  91.             b(i).s = PyT(origin, b(i).m)
  92.             b(i).m.y = b(i).s * COS(RAD(i))
  93.             b(i).m.x = b(i).s * SIN(RAD(i))
  94.         NEXT i
  95.         i = 1
  96.     END IF
  97.  
  98.     IF kh = 122 OR kh = 120 THEN f3 = 1
  99.  
  100.     IF (kh = -122 OR kh = -120) AND f3 = 1 THEN f1 = 0: f3 = 0
  101.  
  102.     IF f4 = 0 THEN kh = 98: f4 = 1
  103.     IF kh = 98 AND f2 = 0 THEN i = 1: f2 = 1: c(1) = "  SELECTED": c(0) = "           ": GOTO LL1 'cyan
  104.     IF kh = 98 AND f2 = 1 THEN i = 0: f2 = 0: c(0) = "  SELECTED": c(1) = "           " 'red
  105.     LL1:
  106.  
  107.     IF kh = 99 THEN 'increase vector magnitude  press c
  108.         mult = 1.01
  109.         VecMult b(i).m, mult
  110.     END IF
  111.  
  112.     IF kh = 118 THEN 'decrease vector magnitude     press v
  113.         div = 1.01
  114.         VecDIV b(i).m, div 'added a new sub
  115.     END IF
  116.  
  117.     IF kh = 122 THEN 'rotate vector counter clockwise  'press z
  118.         IF RAD(i) > _PI * 2 THEN RAD(i) = 0
  119.  
  120.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  121.  
  122.         IF TIMER - t1 <= 1 THEN RAD(i) = RAD(i) + .005
  123.         IF TIMER - t1 > 1 THEN RAD(i) = RAD(i) + .020
  124.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) + .05
  125.  
  126.         b(i).s = PyT(origin, b(i).m)
  127.         b(i).m.y = b(i).s * COS(RAD(i))
  128.         b(i).m.x = b(i).s * SIN(RAD(i))
  129.  
  130.     END IF
  131.  
  132.     IF kh = 120 THEN 'rotate vector clockwise   'press x
  133.         IF RAD(i) < 0 THEN RAD(i) = _PI * 2
  134.  
  135.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  136.  
  137.         IF TIMER - t1 <= 1 THEN RAD(i) = RAD(i) - .005
  138.         IF TIMER - t1 > 1 THEN RAD(i) = RAD(i) - .020
  139.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) - .05
  140.  
  141.         b(i).s = PyT(origin, b(i).m)
  142.         b(i).m.y = b(i).s * COS(RAD(i))
  143.         b(i).m.x = b(i).s * SIN(RAD(i))
  144.     END IF
  145.  
  146.     'START OF COLLISION MATHEMATICS SECTION
  147.  
  148.     ballradius = PyT(b(0).p, b(1).p) / 2
  149.  
  150.     FOR bn = 0 TO 1
  151.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  152.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  153.     NEXT bn
  154.  
  155.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  156.     B2BCollision b(0), b(1), Nvec(), Tvec(), Fvec()
  157.     'END OF COLLISION MATHEMATICS SECTION
  158.  
  159.     'graphic representation
  160.     FOR grid = -300 TO 300 STEP 20
  161.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  162.         LINE (grid, 300)-(grid, -300), c& 'Gray  'vertical lines
  163.         LINE (-300, grid)-(300, grid), c& ' Gray  'horizontal lines
  164.     NEXT grid
  165.  
  166.  
  167.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  168.  
  169.     FOR dr = 0 TO 1
  170.  
  171.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c, , , 1 'AR
  172.  
  173.         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
  174.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Fvec(dr).x, b(dr).p.y + Fvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  175.  
  176.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Nvec(dr).x, b(dr).p.y + Nvec(dr).y), b(dr).c, , &B1111000011110000 ' for testing
  177.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Tvec(dr).x, b(dr).p.y + Tvec(dr).y), b(dr).c, , &B1111000011110000 ' for testing
  178.  
  179.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  180.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  181.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(Fvec(dr).x))) + ", " + _TRIM$(STR$(INT(Fvec(dr).y))) + ">" + c(dr)
  182.  
  183.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  184.  
  185.         TEXT(dr) = b$ + CHR$(13) + CHR$(10) 'NOVARSEG added this line
  186.     NEXT dr
  187.  
  188.     IF _KEYHIT = ASC("f") THEN 'NOVARSEG added this line
  189.         OPEN "BALL STUFF.TXT" FOR BINARY AS #1 'NOVARSEG added this line
  190.         PUT #1, , TEXT(0) 'NOVARSEG added this line
  191.         PUT #1, , TEXT(1) 'NOVARSEG added this line
  192.         CLOSE 'NOVARSEG added this line
  193.     END IF 'NOVARSEG added this line
  194.  
  195.     _DISPLAY
  196.  
  197.     DO
  198.         _LIMIT 500
  199.         kh = _KEYHIT
  200.         IF kh > 0 OR kh < 0 THEN EXIT DO
  201.     LOOP
  202.  
  203.  
  204.  
  205.  
  206. SUB B2BCollision (ball0 AS ball, ball1 AS ball, Nvec() AS V2, Tvec() AS V2, Fvec() AS V2)
  207.  
  208.     DIM un AS V2
  209.     DIM ut AS V2
  210.     DIM MAG AS SINGLE
  211.  
  212.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  213.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  214.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  215.     DIM utDOT1 AS SINGLE 'unit tangent vector "dot" pre collision heading ball1
  216.  
  217.     DIM unVec0 AS V2
  218.     DIM utVec0 AS V2
  219.     DIM unVec1 AS V2
  220.     DIM utVec1 AS V2
  221.  
  222.     'calculate unit normql vector from ball positions
  223.     'ball0 = red, ball1 =cyan
  224.     un.x = ball1.p.x - ball0.p.x
  225.     un.y = ball1.p.y - ball0.p.y
  226.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  227.     un.x = un.x / MAG
  228.     un.y = un.y / MAG
  229.  
  230.     ut.x = -un.y: ut.y = un.x '     establish unit tangent
  231.  
  232.     unDOT0 = un.x * ball0.m.x + un.y * ball0.m.y 'unit normal vector DOT ball vector
  233.     unDOT1 = un.x * ball1.m.x + un.y * ball1.m.y 'unit normal vector DOT ball vector
  234.     utDOT0 = ut.x * ball0.m.x + ut.y * ball0.m.y 'unit tangent vector DOT ball vector
  235.     utDOT1 = ut.x * ball1.m.x + ut.y * ball1.m.y 'unit tangent vector DOT ball vector
  236.  
  237.     'swap "unit normal" scaled ball vectors (pre collision)   according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  238.     A = unDOT0
  239.     B = unDOT1
  240.     unDOT0 = B: unDOT1 = A
  241.  
  242.     'Convert the scalar normal and tangential velocities into vectors
  243.     'according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  244.     VecMult2 un, unDOT0, unVec0 '  third parameter is output
  245.     VecMult2 ut, utDOT0, utVec0 '  third parameter is output
  246.     VecMult2 un, unDOT1, unVec1 '  third parameter is output
  247.     VecMult2 ut, utDOT1, utVec1 '  third parameter is output
  248.  
  249.     VecAdd2 unVec1, utVec1, Fvec(1) 'vector addition to calculate final ball vectors
  250.     VecAdd2 unVec0, utVec0, Fvec(0) 'vector addition to calculate final ball vectors
  251.  
  252.     Nvec(0).x = -unVec0.x 'not necessary code - for display only
  253.     Nvec(0).y = -unVec0.y 'not necessary code - for display only
  254.  
  255.     Nvec(1).x = -unVec1.x 'not necessary code - for display only
  256.     Nvec(1).y = -unVec1.y 'not necessary code - for display only
  257.  
  258.     Tvec(0).x = -utVec0.x 'not necessary code - for display only
  259.     Tvec(0).y = -utVec0.y 'not necessary code - for display only
  260.  
  261.     Tvec(1).x = -utVec1.x 'not necessary code - for display only
  262.     Tvec(1).y = -utVec1.y 'not necessary code - for display only
  263.  
  264. END SUB 'B2BCollision
  265.  
  266.  
  267. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  268.  
  269.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  270.  
  271.  
  272.  
  273. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  274.     STATIC StartTimer AS _FLOAT
  275.     STATIC ButtonDown AS INTEGER
  276.     STATIC ClickCount AS INTEGER
  277.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  278.     '                          Down longer counts as a HOLD event.
  279.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  280.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  281.         SELECT CASE SGN(_MOUSEWHEEL)
  282.             CASE 1: MBS = MBS OR 512
  283.             CASE -1: MBS = MBS OR 1024
  284.         END SELECT
  285.     WEND
  286.  
  287.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  288.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  289.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  290.  
  291.     IF StartTimer = 0 THEN
  292.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  293.             ButtonDown = 1: StartTimer = TIMER(0.01)
  294.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  295.         ELSEIF _MOUSEBUTTON(2) THEN
  296.             ButtonDown = 2: StartTimer = TIMER(0.01)
  297.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  298.         ELSEIF _MOUSEBUTTON(3) THEN
  299.             ButtonDown = 3: StartTimer = TIMER(0.01)
  300.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  301.         END IF
  302.     ELSE
  303.         BD = ButtonDown MOD 3
  304.         IF BD = 0 THEN BD = 3
  305.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  306.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  307.         ELSE
  308.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  309.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  310.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  311.             ELSE 'We've now started the hold event
  312.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  313.             END IF
  314.         END IF
  315.     END IF
  316.  
  317.  
  318. FUNCTION PyT (var1 AS V2, var2 AS V2)
  319.  
  320.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  321.  
  322.  
  323. FUNCTION bPyT (var1 AS V2, var2 AS V2, var3) 'to calulate ball radius only
  324.  
  325.     bPyT = _HYPOT((var1.x - var2.x) * var3, (var1.y - var2.y) * var3)
  326.  
  327.  
  328.  
  329.  
  330. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  331.  
  332.     var.x = -(var.x + (var2.x * var3)) '                           add vector (or a scalar multiple of) var2 to var)
  333.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  334.  
  335. END SUB 'Add_Vector
  336.  
  337. SUB VecAdd2 (var1 AS V2, var2 AS V2, var3 AS V2)
  338.  
  339.     var3.x = -var1.x + -var2.x '
  340.     var3.y = -var1.y + -var2.y '
  341.  
  342. END SUB 'Add_Vector
  343.  
  344.  
  345. FUNCTION VecDot (var AS V2, var2 AS V2)
  346.  
  347.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  348.  
  349. END FUNCTION 'VecDot
  350.  
  351.  
  352. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  353.  
  354.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  355.     vec.y = vec.y * multiplier
  356.  
  357. END SUB 'Vec_Mult
  358.  
  359. SUB VecMult2 (var1 AS V2, var2 AS SINGLE, out1 AS V2)
  360.  
  361.     out1.x = var1.x * var2
  362.     out1.y = var1.y * var2
  363.  
  364. END SUB 'Vec_Mult
  365.  
  366. SUB VecDIV (vec AS V2, divisor AS SINGLE) 'added by Novarseg
  367.  
  368.     vec.x = vec.x / divisor
  369.     vec.y = vec.y / divisor
  370.  
  371. END SUB 'VecDIV
  372.  
  373.  
  374. SUB VecNorm (var AS V2)
  375.  
  376.     m = PyT(origin, var)
  377.     IF m = 0 THEN
  378.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  379.     ELSE
  380.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  381.     END IF
  382.  
  383. END SUB 'VecNorm
  384.  


« Last Edit: June 03, 2021, 02:35:58 am by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
Re: 2D ball collisions without trigonometry.
« Reply #102 on: June 06, 2021, 10:50:02 pm »
Got the math from STxAxTIC's notes working

In the following code there are 2 subs

B2B_Collision   which is based on math from STxAxTIC's notes

B2BCollision   which is based on math from  https://www.vobarian.com/collisions/2dcollisions2.pdf

Both subs produce the same final (exit) vectors

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 '                                                   ball position
  9.     m AS V2 '                                                   heading vector
  10.     x AS V2 '                                                   exit vector
  11.     s AS INTEGER '                                              magnitude of movement
  12.  
  13. DIM Nvec(1) AS V2 'normal velocity vectors
  14. DIM Tvec(1) AS V2 'tangential velocity vectors
  15. DIM Fvec(1) AS V2 'Final or exit vectors
  16.  
  17. DIM TEXT(1) AS STRING
  18. DIM RAD(1) AS SINGLE
  19.  
  20.  
  21. DIM vertex(1, 1) AS V2 '                                        mouse grabbing handles
  22. DIM mouse AS V2
  23. DIM b(1) AS ball
  24.  
  25. 'DIM SHARED AS V2 origin
  26. DIM SHARED origin AS V2
  27. origin.x = 0: origin.y = 0
  28. b(0).c = Red: b(0).cn = "red"
  29. b(1).c = Cyan: b(1).cn = "cyan"
  30.  
  31. SCREEN _NEWIMAGE(_DESKTOPWIDTH, _DESKTOPHEIGHT, 32) 'prevents FULLSCREEN distortion
  32.  
  33. MAG = _DESKTOPHEIGHT / 300 'this 300 is from the grid drawing code.
  34.  
  35.  
  36. _FULLSCREEN 'gives a full screen - not a window!
  37. 'advantage:  only one position - the screen!
  38. 'disadvantage: can't be minimized - or can it?
  39.  
  40. 'starting state
  41. b(0).m.x = 0: b(0).m.y = 100 '    reds approach vector
  42. RAD(0) = 2 * _PI / 2
  43. b(0).s = PyT(origin, b(0).m)
  44.  
  45. b(1).m.x = -100: b(1).m.y = 0 '   cyans approach vector
  46. RAD(1) = 3 * _PI / 2
  47. b(1).s = PyT(origin, b(1).m)
  48.  
  49. b(0).p.x = 0: b(0).p.y = 0 '     ball position x, y
  50. b(1).p.x = -100: b(1).p.y = 100 'ball position x, y
  51.  
  52. f4 = 0 'initial ball select flag
  53. f5 = 0 'inital ball display flag
  54.  
  55.  
  56.     CLS
  57.     ms = MBS '                                                  process mouse actions dragging endpoints
  58.     IF ms AND 64 THEN
  59.  
  60.         mouse.x = map!(_MOUSEX, 0, 599, -300, 300)
  61.         mouse.y = map!(_MOUSEY, 0, 599, 300, -300)
  62.  
  63.         FOR x = 0 TO 1
  64.             FOR y = 0 TO 1
  65.                 ds! = PyT(vertex(x, y), mouse)
  66.                 IF ds! < ballradius * .5 THEN i = x: j = y
  67.             NEXT y
  68.         NEXT x
  69.         SELECT CASE j '                                         grabbing impact position or start of incoming vector
  70.             CASE IS = 0 '                                       impact position- here we use mouse as the new b(#).p
  71.                 b(i).p = mouse
  72.             CASE IS = 1 '                                       starting point- here we obtain the b(#).m mathematically
  73.                 b(i).m = b(i).p: VecAdd b(i).m, mouse, -1
  74.         END SELECT
  75.     END IF
  76.  
  77.     IF kh = 18432 THEN b(i).p.y = b(i).p.y + 1
  78.     IF kh = 20480 THEN b(i).p.y = b(i).p.y - 1
  79.     IF kh = 19200 THEN b(i).p.x = b(i).p.x - 1
  80.     IF kh = 19712 THEN b(i).p.x = b(i).p.x + 1
  81.  
  82.  
  83.     'Vector rotation and vector magnitude adjustment using keyboard input
  84.  
  85.     IF f5 = 0 THEN
  86.         f5 = 1
  87.         FOR i = 0 TO 1
  88.             b(i).s = PyT(origin, b(i).m)
  89.             b(i).m.y = b(i).s * COS(RAD(i))
  90.             b(i).m.x = b(i).s * SIN(RAD(i))
  91.         NEXT i
  92.         i = 1
  93.     END IF
  94.  
  95.     IF kh = 122 OR kh = 120 THEN f3 = 1
  96.  
  97.     IF (kh = -122 OR kh = -120) AND f3 = 1 THEN f1 = 0: f3 = 0
  98.  
  99.     IF f4 = 0 THEN kh = 98: f4 = 1
  100.     IF kh = 98 AND f2 = 0 THEN i = 1: f2 = 1: c(1) = "  SELECTED": c(0) = "           ": GOTO LL1 'cyan
  101.     IF kh = 98 AND f2 = 1 THEN i = 0: f2 = 0: c(0) = "  SELECTED": c(1) = "           " 'red
  102.     LL1:
  103.  
  104.     IF kh = 99 THEN 'increase vector magnitude  press c
  105.         mult = 1.01
  106.         VecMult b(i).m, mult
  107.     END IF
  108.  
  109.     IF kh = 118 THEN 'decrease vector magnitude     press v
  110.         div = 1.01
  111.         VecDIV b(i).m, div 'added a new sub
  112.     END IF
  113.  
  114.     IF kh = 122 THEN 'rotate vector counter clockwise  'press z
  115.         IF RAD(i) > _PI * 2 THEN RAD(i) = 0
  116.  
  117.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  118.  
  119.         IF TIMER - t1 <= 1 THEN RAD(i) = RAD(i) + .005
  120.         IF TIMER - t1 > 1 THEN RAD(i) = RAD(i) + .020
  121.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) + .05
  122.  
  123.         b(i).s = PyT(origin, b(i).m)
  124.         b(i).m.y = b(i).s * COS(RAD(i))
  125.         b(i).m.x = b(i).s * SIN(RAD(i))
  126.  
  127.     END IF
  128.  
  129.     IF kh = 120 THEN 'rotate vector clockwise   'press x
  130.         IF RAD(i) < 0 THEN RAD(i) = _PI * 2
  131.  
  132.         IF f1 = 0 THEN t1 = TIMER: f1 = 1
  133.  
  134.         IF TIMER - t1 <= 1 THEN RAD(i) = RAD(i) - .005
  135.         IF TIMER - t1 > 1 THEN RAD(i) = RAD(i) - .020
  136.         IF TIMER - t1 > 1.5 THEN RAD(i) = RAD(i) - .05
  137.  
  138.         b(i).s = PyT(origin, b(i).m)
  139.         b(i).m.y = b(i).s * COS(RAD(i))
  140.         b(i).m.x = b(i).s * SIN(RAD(i))
  141.     END IF
  142.  
  143.     'START OF COLLISION MATHEMATICS SECTION
  144.  
  145.     ballradius = PyT(b(0).p, b(1).p) / 2
  146.  
  147.     FOR bn = 0 TO 1
  148.         vertex(bn, 0) = b(bn).p '                               first we establish the mouse handles for ball position
  149.         vertex(bn, 1) = b(bn).p: VecAdd vertex(bn, 1), b(bn).m, -1 ' and incoming vector starting point
  150.     NEXT bn
  151.  
  152.     'Now all the previous garbage is distilled into a single SUB call once a collision is determined
  153.     B2B_Collision b(0), b(1), Nvec(), Tvec(), Fvec()
  154.     'END OF COLLISION MATHEMATICS SECTION
  155.  
  156.     'graphic representation
  157.     FOR grid = -300 TO 300 STEP 20
  158.         IF grid MOD 100 = 0 THEN c& = &HFF7F7F7F ELSE c& = &H5F7F7F7F
  159.         LINE (grid, 300)-(grid, -300), c& 'Gray  'vertical lines
  160.         LINE (-300, grid)-(300, grid), c& ' Gray  'horizontal lines
  161.     NEXT grid
  162.  
  163.  
  164.     LINE (b(1).p.x, b(1).p.y)-(b(0).p.x, b(0).p.y), White, , &B0010001000100010 'strike vector
  165.  
  166.     FOR dr = 0 TO 1
  167.  
  168.         CIRCLE (b(dr).p.x, b(dr).p.y), ballradius, b(dr).c, , , 1 'AR
  169.  
  170.         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
  171.         LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Fvec(dr).x, b(dr).p.y + Fvec(dr).y), b(dr).c, , &B1111000011110000 'exit vector
  172.  
  173.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Nvec(dr).x, b(dr).p.y + Nvec(dr).y), b(dr).c, , &B1111000011110000 ' for testing
  174.         ' LINE (b(dr).p.x, b(dr).p.y)-(b(dr).p.x + Tvec(dr).x, b(dr).p.y + Tvec(dr).y), b(dr).c, , &B1111000011110000 ' for testing
  175.  
  176.         b$ = b(dr).cn + " @ (" + _TRIM$(STR$(INT(b(dr).p.x))) + ", " + _TRIM$(STR$(INT(b(dr).p.y))) + ")"
  177.         b$ = b$ + "  along <" + _TRIM$(STR$(INT(b(dr).m.x))) + ", " + _TRIM$(STR$(INT(b(dr).m.y))) + ">"
  178.         b$ = b$ + "  exits along <" + _TRIM$(STR$(INT(Fvec(dr).x))) + ", " + _TRIM$(STR$(INT(Fvec(dr).y))) + ">" + c(dr)
  179.  
  180.         _PRINTSTRING (0, 567 + (16 * dr)), b$
  181.  
  182.         TEXT(dr) = b$ + CHR$(13) + CHR$(10) 'NOVARSEG added this line
  183.     NEXT dr
  184.  
  185.     IF _KEYHIT = ASC("f") THEN 'NOVARSEG added this line
  186.         OPEN "BALL STUFF.TXT" FOR BINARY AS #1 'NOVARSEG added this line
  187.         PUT #1, , TEXT(0) 'NOVARSEG added this line
  188.         PUT #1, , TEXT(1) 'NOVARSEG added this line
  189.         CLOSE 'NOVARSEG added this line
  190.     END IF 'NOVARSEG added this line
  191.  
  192.     _DISPLAY
  193.  
  194.     DO
  195.         _LIMIT 500
  196.         kh = _KEYHIT
  197.         IF kh > 0 OR kh < 0 THEN EXIT DO
  198.     LOOP
  199.  
  200.  
  201.  
  202.  
  203. SUB B2BCollision (ball0 AS ball, ball1 AS ball, Nvec() AS V2, Tvec() AS V2, Fvec() AS V2)
  204.  
  205.     DIM un AS V2
  206.     DIM ut AS V2
  207.     DIM MAG AS SINGLE
  208.  
  209.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  210.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  211.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  212.     DIM utDOT1 AS SINGLE 'unit tangent vector "dot" pre collision heading ball1
  213.  
  214.     DIM unVec0 AS V2
  215.     DIM utVec0 AS V2
  216.     DIM unVec1 AS V2
  217.     DIM utVec1 AS V2
  218.  
  219.     'calculate unit normql vector from ball positions
  220.     'ball0 = red, ball1 =cyan
  221.     un.x = ball1.p.x - ball0.p.x
  222.     un.y = ball1.p.y - ball0.p.y
  223.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  224.     un.x = un.x / MAG
  225.     un.y = un.y / MAG
  226.  
  227.     ut.x = -un.y: ut.y = un.x '     establish unit tangent
  228.  
  229.     unDOT0 = un.x * ball0.m.x + un.y * ball0.m.y 'unit normal vector DOT ball vector
  230.     unDOT1 = un.x * ball1.m.x + un.y * ball1.m.y 'unit normal vector DOT ball vector
  231.     utDOT0 = ut.x * ball0.m.x + ut.y * ball0.m.y 'unit tangent vector DOT ball vector
  232.     utDOT1 = ut.x * ball1.m.x + ut.y * ball1.m.y 'unit tangent vector DOT ball vector
  233.  
  234.     'swap "unit normal" scaled ball vectors (pre collision)   according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  235.     A = unDOT0
  236.     B = unDOT1
  237.     unDOT0 = B: unDOT1 = A
  238.  
  239.     'Convert the scalar normal and tangential velocities into vectors
  240.     'according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  241.     VecMult2 un, unDOT0, unVec0 '  third parameter is output
  242.     VecMult2 ut, utDOT0, utVec0 '  third parameter is output
  243.     VecMult2 un, unDOT1, unVec1 '  third parameter is output
  244.     VecMult2 ut, utDOT1, utVec1 '  third parameter is output
  245.  
  246.     VecAdd2 unVec1, utVec1, Fvec(1) 'vector addition to calculate final ball vectors
  247.     VecAdd2 unVec0, utVec0, Fvec(0) 'vector addition to calculate final ball vectors
  248.  
  249.     'Nvec(0).x = -unVec0.x 'not necessary code - for testing only
  250.     'Nvec(0).y = -unVec0.y 'not necessary code - for testing only
  251.  
  252.     'Nvec(1).x = -unVec1.x 'not necessary code - for testing only
  253.     'Nvec(1).y = -unVec1.y 'not necessary code - for testing only
  254.  
  255.     'Tvec(0).x = -utVec0.x 'not necessary code - for testing only
  256.     'Tvec(0).y = -utVec0.y 'not necessary code - for testing only
  257.  
  258.     'Tvec(1).x = -utVec1.x 'not necessary code - for testing only
  259.     'Tvec(1).y = -utVec1.y 'not necessary code - for testing only
  260.  
  261. END SUB 'B2BCollision
  262. '************
  263. SUB B2B_Collision (ball0 AS ball, ball1 AS ball, Nvec() AS V2, Tvec() AS V2, Fvec() AS V2)
  264.  
  265.     DIM un AS V2
  266.     DIM ut AS V2
  267.     DIM MAG AS SINGLE
  268.  
  269.     DIM unDOT0 AS SINGLE 'unit normal vector "dot" pre collision heading ball0
  270.     DIM unDOT1 AS SINGLE 'unit normal vector "dot" pre collision heading ball1
  271.     DIM utDOT0 AS SINGLE 'unit tangent vector "dot" pre collision heading ball0
  272.     DIM utDOT1 AS SINGLE 'unit tangent vector "dot" pre collision heading ball1
  273.  
  274.     DIM unVec0 AS V2
  275.     DIM utVec0 AS V2
  276.     DIM unVec1 AS V2
  277.     DIM utVec1 AS V2
  278.  
  279.     'calculate unit normql vector from ball positions
  280.     'ball0 = red, ball1 =cyan
  281.     un.x = ball1.p.x - ball0.p.x
  282.     un.y = ball1.p.y - ball0.p.y
  283.     MAG = (un.x ^ 2 + un.y ^ 2) ^ .5
  284.     un.x = un.x / MAG
  285.     un.y = un.y / MAG
  286.  
  287.     'ut.x = -un.y: ut.y = un.x '     establish unit tangent
  288.  
  289.     unDOT0 = un.x * ball0.m.x + un.y * ball0.m.y 'unit normal vector DOT ball vector
  290.     unDOT1 = un.x * ball1.m.x + un.y * ball1.m.y 'unit normal vector DOT ball vector
  291.     ' utDOT0 = ut.x * ball0.m.x + ut.y * ball0.m.y 'unit tangent vector DOT ball vector
  292.     ' utDOT1 = ut.x * ball1.m.x + ut.y * ball1.m.y 'unit tangent vector DOT ball vector
  293.  
  294.     ' 'swap "unit normal" scaled ball vectors (pre collision)   according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  295.     ' A = unDOT0
  296.     ' B = unDOT1
  297.     ' unDOT0 = B: unDOT1 = A
  298.  
  299.     'Convert the scalar normal and tangential velocities into vectors
  300.     'according to https://www.vobarian.com/collisions/2dcollisions2.pdf
  301.  
  302.     VecMult2 un, unDOT1 - unDOT0, unVec0 '  third parameter is output
  303.  
  304.     'VecMult2 ut, utDOT0, utVec0 '  third parameter is output
  305.  
  306.     VecMult2 un, unDOT0 - unDOT1, unVec1 '  third parameter is output
  307.  
  308.     'VecMult2 ut, utDOT1, utVec1 '  third parameter is output
  309.  
  310.     VecAdd2 unVec1, ball1.m, Fvec(1) 'vector addition to calculate final ball vectors
  311.     VecAdd2 unVec0, ball0.m, Fvec(0) 'vector addition to calculate final ball vectors
  312.  
  313. END SUB 'B2B_Collision
  314.  
  315.  
  316.  
  317. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  318.  
  319.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  320.  
  321.  
  322.  
  323. FUNCTION MBS% 'Mouse Button Status  Author: Steve McNeill
  324.     STATIC StartTimer AS _FLOAT
  325.     STATIC ButtonDown AS INTEGER
  326.     STATIC ClickCount AS INTEGER
  327.     CONST ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
  328.     '                          Down longer counts as a HOLD event.
  329.     SHARED Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY
  330.     WHILE _MOUSEINPUT 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
  331.         SELECT CASE SGN(_MOUSEWHEEL)
  332.             CASE 1: MBS = MBS OR 512
  333.             CASE -1: MBS = MBS OR 1024
  334.         END SELECT
  335.     WEND
  336.  
  337.     IF _MOUSEBUTTON(1) THEN MBS = MBS OR 1
  338.     IF _MOUSEBUTTON(2) THEN MBS = MBS OR 2
  339.     IF _MOUSEBUTTON(3) THEN MBS = MBS OR 4
  340.  
  341.     IF StartTimer = 0 THEN
  342.         IF _MOUSEBUTTON(1) THEN 'If a button is pressed, start the timer to see what it does (click or hold)
  343.             ButtonDown = 1: StartTimer = TIMER(0.01)
  344.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  345.         ELSEIF _MOUSEBUTTON(2) THEN
  346.             ButtonDown = 2: StartTimer = TIMER(0.01)
  347.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  348.         ELSEIF _MOUSEBUTTON(3) THEN
  349.             ButtonDown = 3: StartTimer = TIMER(0.01)
  350.             Mouse_StartX = _MOUSEX: Mouse_StartY = _MOUSEY
  351.         END IF
  352.     ELSE
  353.         BD = ButtonDown MOD 3
  354.         IF BD = 0 THEN BD = 3
  355.         IF TIMER(0.01) - StartTimer <= ClickLimit THEN 'Button was down, then up, within time limit.  It's a click
  356.             IF _MOUSEBUTTON(BD) = 0 THEN MBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
  357.         ELSE
  358.             IF _MOUSEBUTTON(BD) = 0 THEN 'hold event has now ended
  359.                 MBS = 0: ButtonDown = 0: StartTimer = 0
  360.                 Mouse_EndX = _MOUSEX: Mouse_EndY = _MOUSEY
  361.             ELSE 'We've now started the hold event
  362.                 MBS = MBS OR 32 * 2 ^ ButtonDown
  363.             END IF
  364.         END IF
  365.     END IF
  366.  
  367.  
  368. FUNCTION PyT (var1 AS V2, var2 AS V2)
  369.  
  370.     PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
  371.  
  372.  
  373.  
  374. SUB VecAdd (var AS V2, var2 AS V2, var3 AS SINGLE)
  375.  
  376.     var.x = -(var.x + (var2.x * var3)) '                           add vector (or a scalar multiple of) var2 to var)
  377.     var.y = var.y + (var2.y * var3) '                           use var3 = -1 to subtract var2 from var
  378.  
  379. END SUB 'Add_Vector
  380.  
  381. SUB VecAdd2 (var1 AS V2, var2 AS V2, var3 AS V2)
  382.  
  383.     var3.x = -var1.x + -var2.x '
  384.     var3.y = -var1.y + -var2.y '
  385.  
  386. END SUB 'Add_Vector
  387.  
  388.  
  389. FUNCTION VecDot (var AS V2, var2 AS V2)
  390.  
  391.     VecDot = var.x * var2.x + var.y * var2.y '                  get dot product of var & var2
  392.  
  393. END FUNCTION 'VecDot
  394.  
  395.  
  396. SUB VecMult (vec AS V2, multiplier AS SINGLE)
  397.  
  398.     vec.x = vec.x * multiplier '                                multiply vector by scalar value
  399.     vec.y = vec.y * multiplier
  400.  
  401. END SUB 'Vec_Mult
  402.  
  403. SUB VecMult2 (var1 AS V2, var2 AS SINGLE, out1 AS V2)
  404.  
  405.     out1.x = var1.x * var2
  406.     out1.y = var1.y * var2
  407.  
  408. END SUB 'Vec_Mult
  409.  
  410. SUB VecDIV (vec AS V2, divisor AS SINGLE)
  411.  
  412.     vec.x = vec.x / divisor
  413.     vec.y = vec.y / divisor
  414.  
  415. END SUB 'VecDIV
  416.  
  417.  
  418. SUB VecNorm (var AS V2)
  419.  
  420.     m = PyT(origin, var)
  421.     IF m = 0 THEN
  422.         var.x = 0: var.y = 0 '                                  vector with magnitude 0 is a zero vector
  423.     ELSE
  424.         var.x = var.x / m: var.y = var.y / m '                  convert var to unit vector
  425.     END IF
  426.  
  427. END SUB 'VecNorm
  428.