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

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #60 on: May 23, 2021, 03:44:35 am »
Ha! I stopped overlapping balls and now cue ball goes through rack like Moses parting the Red Sea ;-)) crazy!

That wasn't my hanging problem because it still does.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #61 on: May 23, 2021, 03:51:30 am »
Oldmoses

Quote
b(0).s = PyT(origin, b(0).m)

what does  variable "origin" refer to ?

Quote
TYPE V2
    x AS SINGLE
    y AS SINGLE
END TYPE

DIM SHARED origin AS V2

Quote
origin.x = 0: origin.y = 0


Ok I think Ive answered my own question


Quote
FUNCTION PyT (var1 AS V2, var2 AS V2)
 
    PyT = _HYPOT(var1.x - var2.x, var1.y - var2.y)
 
END FUNCTION 'PyT

the .x or .y are supplied in the function.   I really don't get the dot stuff   The only advantage is a smaller DIM list.    I find the dot stuff harder to read






« Last Edit: May 23, 2021, 04:17:49 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #62 on: May 23, 2021, 07:40:59 am »
PyT is my method of using pythagorean theorem for obtaining speeds/magnitudes of vectors AND distances between points. I usually define an "origin" so that I can do the former, and simply plug in the two points if I'm after the later.

In this application, PyT only handles the V2 type, but in another of my projects it does both R2 and R3 vectors by accepting a third parameter telling it which to work on.

Code: QB64: [Select]
  1. FUNCTION PyT (var AS _BYTE, var1 AS V3, var2 AS V3)
  2.  
  3.     SELECT CASE var   'find distance/magnitude between 2D or 3D points
  4.         CASE IS = 2
  5.             PyT = _HYPOT(var1.pX - var2.pX, var1.pY - var2.pY)
  6.         CASE IS = 3
  7.             PyT = _HYPOT(_HYPOT(var1.pX - var2.pX, var1.pY - var2.pY), var1.pZ - var2.pZ)
  8.     END SELECT
  9.  
  10.  

In some speed testing I've done, I've found the _HYPOT command to be as fast or faster than more traditional methods. I'm thinking of trying to optimize that some more, by ditching the SELECT CASE part.

On the Dot Product topic, I only became aware of it a few months ago and was shocked to discover that it is an indispensable tool for game design. Getting at this sort of programming problem without it is like changing a water pump without wrenches. You might be able to use other tools, but the job will be exponentially harder.

Dot Product is a way of multiplying two vectors together which results in a non-vector number (scalar). The result gives one the ability to determine how much of one vector is "projected" onto another. In our case it is the ability to obtain the portion of red ball's force that is applied directly at cyan ball and what is directed toward red balls exit motion, and of course the same applied from cyan to red and cyan's exit.

  [ You are not allowed to view this attachment ]  

You needn't worry about the trig formula in the picture, that's mainly for obtaining a dot product when theta is a known quantity, which we're not concerned with in this topic. It might also be apparent, by the picture, that dot product is also closely related to the pythagorean theorem.

In ball collision we are primarily concerned with how much of the unit normal and unit tangent vectors are applied by a collision, which dot product supplies quite well.

Dot Product simply takes the components of two vectors, multiplies them together and sums the results. Example, two vectors A and B, dotted together are:

 A dot B = A.x * B.x + A.y * B.y    'and that doesn't get much simpler for what can be done with it...

and if you're dealing in 3D:

A dot B = A.x * B.x + A.y * B.y + A.z * B.z

Which is exactly what SUB VecDot does in my code, for our 2D representation. The order doesn't matter, but it is good to convert one of the vectors you're projecting into a unit vector, if vector projection is your main purpose. That's what my SUB VecNorm does to the "strike" (aka unit normal), and it's orthogonal (aka unit tangent). BTW, you were correct, I didn't need different strikes & orthogonals for each ball. One was sufficient.

The number that dot product returns will also tell you how the vectors are oriented to each other. A negative dot product indicates vectors angled away from each other (obtuse), a dot product of 0 indicates vectors that are perpendicular, and a positive dot product are vectors that subtend an acute angle. The closest thing to a mathematical multi-tool that I've seen so far. Once you learn it, you'll be looking for places to use it.

This guy does a pretty good job of presenting Dot Product:
&t=31s
« Last Edit: May 23, 2021, 07:45:27 am by OldMoses »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #63 on: May 23, 2021, 01:57:50 pm »
Going back to the interactive analysis tool, I've added fine dashed lines to represent the normal and tangent vectors that influence how each ball's position and incoming vector affect the final exit vector. Each is colored by where its influence derives from.

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

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #64 on: May 23, 2021, 06:26:58 pm »
Found source of hanging problem, it wasn't overlapping balls, it was infinite loop of subtracting vectors of no length.

This code does cleanup overlapping balls before next shot, Pool 2 at Steve's forum:
https://qb64.freeforums.net/thread/150/pool?page=1&scrollTo=615

So OldMoses link to paper still works.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #65 on: May 23, 2021, 07:12:34 pm »
That was outstanding, I ran the whole table with no issues at all.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #66 on: May 23, 2021, 09:57:57 pm »
That was outstanding, I ran the whole table with no issues at all.

Holy balls the whole table!? Man I am just now converted to ball images like you and am patting myself on back at how much easier to see shots because I made all outside edges the same color.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #67 on: May 23, 2021, 10:18:44 pm »
OldMoses

The position fine tuning works great.

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

"along <-100, 0>" is correct but the actual cyan vector on the screen shows
<100, 0>

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #68 on: May 24, 2021, 03:19:11 am »
"along <-100, 0>" is correct but the actual cyan vector on the screen shows
<100, 0>

I tried setting that up, but kept getting a slight rounding error on the cyan's exit vector. It would add a 1 in the Y axis

Is this similar to what you're getting?

  [ You are not allowed to view this attachment ]  

If so, I'd say it's working OK.

I've seen the math do some funny things with it, but that mostly happens when it's representing scenarios that are highly unlikely to happen in a real world ball collision. Such as when a slow ball gets around the backside of a fast one. In fact, I'm wondering if that might be the source of my magnetic ball bug.
« Last Edit: May 24, 2021, 03:23:49 am by OldMoses »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #69 on: May 25, 2021, 09:50:41 pm »
Code added to OldMoses program.
(some trig used)

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 / coarse control.   hold down key longer, for coarse rotation and momentary for fine rotation

Don't know how this will interact with the mouse.
There might be a few bugs?

Will run on the older version of QB64 as well

Oh, If you hit the f key the ball parameters are saved in a file called
BALL STUFF.TXT and you can copy and paste from notepad etc.

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

example:
red  @ (0, 0)  along <0, 100>  exits along <200, 100>           
cyan @ (-100, 0)  along <-201, 0>  exits along <0, 0>  SELECTED

the cyan ball will come to a complete stop no matter how fast it's going


Added FULLSCREEN use q or w keys to adjust aspect ratio

Balls don't contact in FULLSCREEN mode, working on code to fix that

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

FULLSCREEN distorts the x  y axis so the vector magnitude will appear to be changing when rotating. That is from the x y distortion - not the code.

 Trying to correct distortion when in FULLSCREEN mode.



« Last Edit: May 26, 2021, 01:21:15 am by NOVARSEG »

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #70 on: May 26, 2021, 06:22:16 am »
It did mess with the mouse some, but it's more intuitive to use on the keyboard.

I suspect the issue with full screen distortion is that WINDOW needs to be altered to reflect the full screen aspect ratio. Grid drawing loop will need changing too, I believe.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #71 on: May 26, 2021, 04:21:31 pm »
How does FULLSCREEN calculate the aspect ratio?

Quote
WINDOW needs to be altered to reflect the full screen aspect ratio

I will try that







« Last Edit: May 26, 2021, 05:39:36 pm by NOVARSEG »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #72 on: May 26, 2021, 05:22:05 pm »
_FULLSCREEN just stretches the QB64 screen over your computer screen.

For Square Window SqrXSqr, use a square QB64 screen, that should eliminate distortions.
ie Screen _NewImage(mySqr, mySqr, 32)

Oh you want _DeskTopWidth and _DeskTopHeight for your screen dimensions. Ah! then use those for _FullScreen without distortion.
« Last Edit: May 26, 2021, 05:32:14 pm by bplus »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #73 on: May 26, 2021, 05:56:00 pm »
Interesting
Lets say FULLSCREEN uses _DeskTopWidth and _DeskTopHeight  so taking your square screen idea like newimage(600,600,32).

  x1 = ( 600 / (  (_DeskTopWidth  /_DeskTopHeight ) + 1 )  )
y1 = ( 600 / (  (_DeskTopHeight  /_DeskTopWidth  ) + 1 )  )


WINDOW (-x1,y1)-(x1,-y1)
er maybe it's
WINDOW (-x1,300)-(x1,-300)?

will it work?  The result should be an expanded square screen that will fit into DeskTopHeight dimensions. The negative signs make the center of the screen 0x, 0y

Gotta use _FULLSCREEN as well to make this work

Example, say DeskTopWidth  /_DeskTopHeight   = 1   then 1 + 1 = 2

600 / 2 = 300

WINDOW (-300, 300)-(300, -300)  the original code

but as we know, most if not all computer screens have more pixels wide than high so it looks that is why _FULLSCREEN distorts in certain cases.  Nothing wrong with FULLSCREEN though.

example say DeskTopWidth  /_DeskTopHeight   = 1.5   then 1.5 + 1 = 2.5

600 / 2.5 = 240

WINDOW (-240, 300)-(240, -300)

will that correct FULLSCREEN distortion?.  As OldMoses pointed out, the code that draws the grid has to work with these numbers as well.
« Last Edit: May 26, 2021, 06:32:35 pm by NOVARSEG »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: 2D ball collisions without trigonometry.
« Reply #74 on: May 26, 2021, 06:45:02 pm »
Don't "gotta use full screen to make this work"

gotta use full screen to make this complicated as hell ;-))

Whatever your screen ratio height to width has to be matched in QB64 screen and Window screen they all need same proportions for _FULLSCREEN not to be distorted.

Say your Desktopwidth to height is 4 to 3 then make qb64 screen 400 X 300 or 800x600 or 1200x900
and window -200 to 200, 150 to -150 or -400 to 400, 300, -300.

And note your screen is not same as every one else screen. I would skip FULLSCREEN.

If you don't use FULLSCREEN then you can keep things simple with square ranges of x and y.


Wait is -200 to 200 = 400 pixels or 401 pixels probably 401 pixels.
« Last Edit: May 26, 2021, 06:49:07 pm by bplus »