Author Topic: Generalized physics engine prototype with arbitrary shapes and collisions  (Read 7047 times)

0 Members and 1 Guest are viewing this topic.

FellippeHeitor

  • Guest
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #15 on: February 23, 2020, 03:37:02 pm »
Potatoes!
😂

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #16 on: February 29, 2020, 07:04:04 pm »
A quick update below. Still deciding what the end-game for this will be.

Code: QB64: [Select]
  1. ' Display
  2. SCREEN _NEWIMAGE(800, 600, 32)
  3. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  4.  
  5. ' Meta
  6. start:
  7.  
  8.  
  9. ' Data
  10. TYPE Vector
  11.     x AS DOUBLE
  12.     y AS DOUBLE
  13.  
  14. TYPE Object
  15.     Centroid AS Vector
  16.     Collisions AS LONG
  17.     CollisionSwitch AS INTEGER
  18.     DeltaO AS DOUBLE
  19.     DeltaV AS Vector
  20.     DiameterMax AS DOUBLE
  21.     DiameterMin AS DOUBLE
  22.     Elements AS INTEGER
  23.     Fixed AS INTEGER
  24.     Mass AS DOUBLE
  25.     MOI AS DOUBLE
  26.     PartialNormal AS Vector
  27.     Omega AS DOUBLE
  28.     Shade AS _UNSIGNED LONG
  29.     Velocity AS Vector
  30.  
  31. DIM vtemp AS Vector
  32.  
  33. ' Statics
  34. DIM SHARED Shape(1000) AS Object
  35. DIM SHARED PointChain(1000, 5000) AS Vector
  36. DIM SHARED TempChain(1000, 5000) AS Vector
  37. DIM SHARED ShapeCount AS INTEGER
  38.  
  39. ' Dynamics
  40. DIM SHARED CollisionCount AS INTEGER
  41. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  42. DIM SHARED ProximalPairsCount
  43.  
  44. ' Environment
  45. DIM ForceField AS Vector ' Gravity
  46. ForceField.x = 0
  47. ForceField.y = -.08
  48.  
  49. ' Initialize
  50. ShapeCount = 0
  51. CollisionCount = 0
  52.  
  53. ' Balls (billiard setup)
  54. x0 = -70
  55. y0 = 120
  56. r1 = 15.5
  57. r2 = 2.5
  58. gg = 2 * r1 + 20
  59. xf = COS(30 * 3.14159 / 180)
  60. yf = SIN(30 * 3.14159 / 180)
  61. gx = gg * xf
  62. gy = gg * yf
  63. CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  64. CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  65. CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  66. CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  67. CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  68. 'CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  69. 'CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  70. 'CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  71. 'CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  72. 'CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  73.  
  74. ' Rectangular border
  75. l = 40
  76. w = 10
  77. CALL NewBrickLine(-_WIDTH / 2 + 2 * l, _HEIGHT / 2 - 2 * w, _WIDTH / 2, _HEIGHT / 2 - 2 * w, l, w)
  78. CALL NewBrickLine(-_WIDTH / 2 + 2 * l, -_HEIGHT / 2 + 2 * w, _WIDTH / 2, -_HEIGHT / 2 + 2 * w, l, w)
  79. CALL NewBrickLine(-_WIDTH / 2 + 2 * w, -_HEIGHT / 2 + 2 * l, -_WIDTH / 2 + 2 * w, _HEIGHT / 2 - 2 * l, l, w)
  80. CALL NewBrickLine(_WIDTH / 2 - 2 * w, -_HEIGHT / 2 + 2 * l, _WIDTH / 2 - 2 * w, _HEIGHT / 2 - 2 * l, l, w)
  81.  
  82. ' Misc. bricks
  83. CALL NewBrickLine(-100 + 4, 100, 0 + 4, 0, 60, 10)
  84. CALL NewBrickLine(100 - 4, 100, 0 - 4, 0, 60, 10)
  85.  
  86. 'CALL NewAutoBrick(0, -100, 200, 100, 0)
  87.  
  88. ' Main object(s)
  89. ShapeCount = ShapeCount + 1
  90. Shape(ShapeCount).Fixed = 0
  91. Shape(ShapeCount).Collisions = 0
  92. x1 = 0
  93. y1 = -150
  94. i = 0
  95. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  96.     i = i + 1
  97.     r = 20 + 15 * COS(1.5 * j) ^ 2
  98.     PointChain(ShapeCount, i).x = x1 + r * COS(j)
  99.     PointChain(ShapeCount, i).y = y1 + r * SIN(j)
  100. Shape(ShapeCount).Elements = i
  101. CALL CalculateMass(ShapeCount, 1)
  102. CALL CalculateCentroid(ShapeCount)
  103. CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  104. CALL CalculateDiameterMin(ShapeCount)
  105. CALL CalculateDiameterMax(ShapeCount)
  106. Shape(ShapeCount).Velocity.x = 0
  107. Shape(ShapeCount).Velocity.y = 0
  108. Shape(ShapeCount).Omega = 0
  109. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  110.  
  111. ' Prompt
  112. CALL Graphics
  113. CALL cprintstring(0, "PRESS ANY KEY TO BEGIN")
  114. CALL cprintstring(-16 * 2, "Arrow keys boost most recently-created object.")
  115. CALL cprintstring(-16 * 3, "Space enters creative mode.")
  116. CALL cprintstring(-16 * 4, "Press 'r' to restart.")
  117.  
  118. ' Main loop
  119.  
  120.     ' Keyboard input
  121.     kk = _KEYHIT
  122.     SELECT CASE kk
  123.         CASE 32
  124.             DO: LOOP UNTIL _KEYHIT
  125.             WHILE _MOUSEINPUT: WEND
  126.             _KEYCLEAR
  127.             CALL cprintstring(16 * 17, "Drag mouse counter-clockwise to draw a new shape.")
  128.             CALL cprintstring(16 * 16, "Make sure centroid is inside body.")
  129.             'CALL Graphics
  130.             CALL NewMouseShape(7.5, 75, 25)
  131.             CLS
  132.         CASE 18432, ASC("w") ' Up arrow
  133.             Shape(ShapeCount).Velocity.y = Shape(ShapeCount).Velocity.y * 1.1 + 2
  134.         CASE 20480, ASC("s") ' Down arrow
  135.             Shape(ShapeCount).Velocity.y = Shape(ShapeCount).Velocity.y * 0.9 - 2
  136.         CASE 19200, ASC("a") ' Left arrow
  137.             Shape(ShapeCount).Velocity.x = Shape(ShapeCount).Velocity.x * 0.9 - 2
  138.         CASE 19712, ASC("d") ' Right arrow
  139.             Shape(ShapeCount).Velocity.x = Shape(ShapeCount).Velocity.x * 1.1 + 2
  140.         CASE ASC("r")
  141.             GOTO start
  142.         CASE 27
  143.             FOR k = 1 TO ShapeCount
  144.                 Shape(k).Velocity.x = .000001
  145.                 Shape(k).Velocity.y = .000001
  146.                 Shape(k).Omega = .000001
  147.             NEXT
  148.     END SELECT
  149.     IF (kk) THEN
  150.         _KEYCLEAR
  151.     END IF
  152.  
  153.     ' Mouse input
  154.     mb = 0
  155.         x = _MOUSEX
  156.         y = _MOUSEY
  157.         IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  158.             IF (_MOUSEBUTTON(1)) THEN
  159.                 IF (mb = 0) THEN
  160.                     mb = 1
  161.                     x = x - (_WIDTH / 2)
  162.                     y = -y + (_HEIGHT / 2)
  163.                     vtemp.x = x - Shape(ShapeCount).Centroid.x
  164.                     vtemp.y = y - Shape(ShapeCount).Centroid.y
  165.                     CALL TranslateShape(ShapeCount, vtemp)
  166.                     Shape(ShapeCount).Velocity.x = 0
  167.                     Shape(ShapeCount).Velocity.y = 0
  168.                     Shape(ShapeCount).Omega = 0
  169.                 END IF
  170.             END IF
  171.             IF (_MOUSEBUTTON(2)) THEN
  172.                 IF (mb = 0) THEN
  173.                     mb = 1
  174.                     x = x - (_WIDTH / 2)
  175.                     y = -y + (_HEIGHT / 2)
  176.                     CALL NewAutoBall(x, y, 20, 0, 1, 1, 1)
  177.                     _DELAY .1
  178.                 END IF
  179.             END IF
  180.         END IF
  181.     LOOP
  182.  
  183.     ' Proximity detection
  184.     ProximalPairsCount = 0
  185.     Shape1 = 0
  186.     Shape2 = 0
  187.     FOR j = 1 TO ShapeCount
  188.         Shape(j).CollisionSwitch = 0
  189.         Shape(j).DeltaO = 0
  190.         Shape(j).DeltaV.x = 0
  191.         Shape(j).DeltaV.y = 0
  192.         Shape(j).PartialNormal.x = 0
  193.         Shape(j).PartialNormal.y = 0
  194.         FOR k = j + 1 TO ShapeCount
  195.             dx = Shape(j).Centroid.x - Shape(k).Centroid.x
  196.             dy = Shape(j).Centroid.y - Shape(k).Centroid.y
  197.             dr = SQR(dx * dx + dy * dy)
  198.             IF (dr < (1.15) * (Shape(j).DiameterMax + Shape(k).DiameterMax)) THEN
  199.                 ProximalPairsCount = ProximalPairsCount + 1
  200.                 ProximalPairs(ProximalPairsCount, 1) = j
  201.                 ProximalPairs(ProximalPairsCount, 2) = k
  202.                 Shape1 = j
  203.                 Shape2 = k
  204.             END IF
  205.         NEXT
  206.     NEXT
  207.  
  208.     IF (ProximalPairsCount > 0) THEN
  209.         FOR n = 1 TO ProximalPairsCount
  210.             Shape1 = ProximalPairs(n, 1)
  211.             Shape2 = ProximalPairs(n, 2)
  212.  
  213.             ' Collision detection
  214.             rmin = 999999999
  215.             ClosestIndex1 = 0
  216.             ClosestIndex2 = 0
  217.             FOR j = 1 TO Shape(Shape1).Elements
  218.                 FOR k = 1 TO Shape(Shape2).Elements
  219.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  220.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  221.                     r2 = dx * dx + dy * dy
  222.  
  223.                     IF (r2 <= 8) THEN
  224.  
  225.                         ' Normal vector 1
  226.                         nx1 = CalculateNormalY(Shape1, j)
  227.                         ny1 = -CalculateNormalX(Shape1, j)
  228.                         nn = SQR(nx1 * nx1 + ny1 * ny1)
  229.                         nx1 = nx1 / nn
  230.                         ny1 = ny1 / nn
  231.                         Shape(Shape1).PartialNormal.x = Shape(Shape1).PartialNormal.x + nx1
  232.                         Shape(Shape1).PartialNormal.y = Shape(Shape1).PartialNormal.y + ny1
  233.  
  234.                         ' Normal vector 2
  235.                         nx2 = CalculateNormalY(Shape2, k)
  236.                         ny2 = -CalculateNormalX(Shape2, k)
  237.                         nn = SQR(nx2 * nx2 + ny2 * ny2)
  238.                         nx2 = nx2 / nn
  239.                         ny2 = ny2 / nn
  240.                         Shape(Shape2).PartialNormal.x = Shape(Shape2).PartialNormal.x + nx2
  241.                         Shape(Shape2).PartialNormal.y = Shape(Shape2).PartialNormal.y + ny2
  242.  
  243.                     END IF
  244.                     IF (r2 < rmin) THEN
  245.                         rmin = r2
  246.                         ClosestIndex1 = j
  247.                         ClosestIndex2 = k
  248.                     END IF
  249.                 NEXT
  250.             NEXT
  251.  
  252.             IF (rmin <= 8) THEN
  253.  
  254.                 ' Undo previous motion
  255.                 IF (Shape(Shape1).Collisions = 0) THEN
  256.                     CALL RotShape(Shape1, Shape(Shape1).Centroid, -Shape(Shape1).Omega)
  257.                     vtemp.x = -(Shape(Shape1).Velocity.x)
  258.                     vtemp.y = -(Shape(Shape1).Velocity.y)
  259.                     CALL TranslateShape(Shape1, vtemp)
  260.                 END IF
  261.                 IF (Shape(Shape2).Collisions = 0) THEN
  262.                     CALL RotShape(Shape2, Shape(Shape2).Centroid, -Shape(Shape2).Omega)
  263.                     vtemp.x = -(Shape(Shape2).Velocity.x)
  264.                     vtemp.y = -(Shape(Shape2).Velocity.y)
  265.                     CALL TranslateShape(Shape2, vtemp)
  266.                 END IF
  267.  
  268.                 IF (Shape(Shape1).Collisions = 0) THEN
  269.                     Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * .75
  270.                     Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * .75
  271.                 END IF
  272.                 IF (Shape(Shape2).Collisions = 0) THEN
  273.                     Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * .75
  274.                     Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * .75
  275.                 END IF
  276.  
  277.                 CollisionCount = CollisionCount + 1
  278.                 Shape(Shape1).CollisionSwitch = 1
  279.                 Shape(Shape2).CollisionSwitch = 1
  280.  
  281.                 ' Normal vector 1 (nx1, ny1)
  282.                 nn = SQR(Shape(Shape1).PartialNormal.x * Shape(Shape1).PartialNormal.x + Shape(Shape1).PartialNormal.y * Shape(Shape1).PartialNormal.y)
  283.                 nx1 = Shape(Shape1).PartialNormal.x / nn
  284.                 ny1 = Shape(Shape1).PartialNormal.y / nn
  285.                 Shape(Shape1).PartialNormal.x = nx1
  286.                 Shape(Shape1).PartialNormal.y = ny1
  287.  
  288.                 ' Normal vector 2 (nx2, ny2)
  289.                 nn = SQR(Shape(Shape2).PartialNormal.x * Shape(Shape2).PartialNormal.x + Shape(Shape2).PartialNormal.y * Shape(Shape2).PartialNormal.y)
  290.                 nx2 = Shape(Shape2).PartialNormal.x / nn
  291.                 ny2 = Shape(Shape2).PartialNormal.y / nn
  292.                 Shape(Shape2).PartialNormal.x = nx2
  293.                 Shape(Shape2).PartialNormal.y = ny2
  294.  
  295.                 ' Contact-centroid differentials 1 (dx1, dy1)
  296.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).Centroid.x
  297.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).Centroid.y
  298.  
  299.                 ' Contact-centroid differentials 2 (dx2, dy2)
  300.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).Centroid.x
  301.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).Centroid.y
  302.  
  303.                 ' Perpendicular vector 1 (px1, py1)
  304.                 px1 = -dy1
  305.                 py1 = dx1
  306.                 pp = SQR(px1 * px1 + py1 * py1)
  307.                 px1 = px1 / pp
  308.                 py1 = py1 / pp
  309.  
  310.                 ' Perpendicular vector 2 (px2, py2)
  311.                 px2 = -dy2
  312.                 py2 = dx2
  313.                 pp = SQR(px2 * px2 + py2 * py2)
  314.                 px2 = px2 / pp
  315.                 py2 = py2 / pp
  316.  
  317.                 ' Angular velocity vector 1 (w1, r1, vx1, vy1)
  318.                 w1 = Shape(Shape1).Omega
  319.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  320.                 vx1 = w1 * r1 * px1
  321.                 vy1 = w1 * r1 * py1
  322.  
  323.                 ' Angular velocity vector 2 (w2, r2, vx2, vy2)
  324.                 w2 = Shape(Shape2).Omega
  325.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  326.                 vx2 = w2 * r2 * px2
  327.                 vy2 = w2 * r2 * py2
  328.  
  329.                 ' Mass terms (m1, m2, mu)
  330.                 m1 = Shape(Shape1).Mass
  331.                 m2 = Shape(Shape2).Mass
  332.                 mu = 1 / (1 / m1 + 1 / m2)
  333.  
  334.                 ' Re-Calculate moment of inertia (i1, i2)
  335.                 vtemp.x = PointChain(Shape1, ClosestIndex1).x
  336.                 vtemp.y = PointChain(Shape1, ClosestIndex1).y
  337.                 CALL CalculateMOI(Shape1, vtemp)
  338.                 vtemp.x = PointChain(Shape2, ClosestIndex2).x
  339.                 vtemp.y = PointChain(Shape2, ClosestIndex2).y
  340.                 CALL CalculateMOI(Shape2, vtemp)
  341.                 i1 = Shape(Shape1).MOI
  342.                 i2 = Shape(Shape2).MOI
  343.  
  344.                 ' Velocity differentials (v1, v2, dvtx, dvty)
  345.                 vcx1 = Shape(Shape1).Velocity.x
  346.                 vcy1 = Shape(Shape1).Velocity.y
  347.                 vcx2 = Shape(Shape2).Velocity.x
  348.                 vcy2 = Shape(Shape2).Velocity.y
  349.                 vtx1 = vcx1 + vx1
  350.                 vty1 = vcy1 + vy1
  351.                 vtx2 = vcx2 + vx2
  352.                 vty2 = vcy2 + vy2
  353.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  354.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  355.                 dvtx = vtx2 - vtx1
  356.                 dvty = vty2 - vty1
  357.  
  358.                 ' Geometry (n1dotdvt, n2dotdvt)
  359.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  360.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  361.  
  362.                 ' Momentum exchange (qx1, qy1, qx2, qy2)
  363.                 qx1 = nx1 * 2 * mu * n1dotdvt
  364.                 qy1 = ny1 * 2 * mu * n1dotdvt
  365.                 qx2 = nx2 * 2 * mu * n2dotdvt
  366.                 qy2 = ny2 * 2 * mu * n2dotdvt
  367.  
  368.                 ' Momentum exchange unit vector
  369.                 qq = SQR(qx1 * qx1 + qy1 * qy1)
  370.                 qhatx1 = qx1 / qq
  371.                 qhaty1 = qy1 / qq
  372.                 qq = SQR(qx2 * qx2 + qy2 * qy2)
  373.                 qhatx2 = qx2 / qq
  374.                 qhaty2 = qy2 / qq
  375.  
  376.                 ' Translational impulse
  377.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  378.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  379.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  380.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  381.                 f1 = -q1dotn1 * n1dotr1hat
  382.                 f2 = -q2dotn2 * n2dotr2hat
  383.                 Shape(Shape1).DeltaV.x = Shape(Shape1).DeltaV.x + f1 * qx1 / m1
  384.                 Shape(Shape1).DeltaV.y = Shape(Shape1).DeltaV.y + f1 * qy1 / m1
  385.                 Shape(Shape2).DeltaV.x = Shape(Shape2).DeltaV.x + f2 * qx2 / m2
  386.                 Shape(Shape2).DeltaV.y = Shape(Shape2).DeltaV.y + f2 * qy2 / m2
  387.  
  388.                 ' Angular impulse
  389.                 q1dotp1 = qx1 * px1 + qy1 * py1
  390.                 q2dotp2 = qx2 * px2 + qy2 * py2
  391.                 dw1 = r1 * q1dotp1 / i1
  392.                 dw2 = -r2 * q2dotp2 / i2
  393.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO + dw1
  394.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO + dw2
  395.  
  396.                 ' Separate along normal
  397.                 'IF (Shape(Shape1).Collisions = 0) THEN
  398.                 vtemp.x = -nx1 * 1 * ((v1 + 0) / m1)
  399.                 vtemp.y = -ny1 * 1 * ((v1 + 0) / m1)
  400.                 CALL TranslateShape(Shape1, vtemp)
  401.                 'END IF
  402.                 'IF (Shape(Shape2).Collisions = 0) THEN
  403.                 vtemp.x = -nx2 * 1 * ((v2 + 0) / m2)
  404.                 vtemp.y = -ny2 * 1 * ((v2 + 0) / m2)
  405.                 CALL TranslateShape(Shape2, vtemp)
  406.                 'END IF
  407.  
  408.                 ' External torque
  409.                 'IF (Shape(Shape1).Collisions > 0) THEN
  410.                 torque1 = dx1 * ForceField.y - dy1 * ForceField.x
  411.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO - m1 * torque1 / i1
  412.                 'END IF
  413.                 'IF (Shape(Shape2).Collisions > 0) THEN
  414.                 torque2 = dx2 * ForceField.y - dy2 * ForceField.x
  415.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO - m2 * torque2 / i2
  416.                 'END IF
  417.  
  418.                 ' Feedback
  419.                 CALL snd(100 * (v1 + v2) / 2, .5)
  420.  
  421.                 'END IF
  422.             END IF
  423.         NEXT
  424.     END IF
  425.  
  426.     FOR ShapeIndex = 1 TO ShapeCount
  427.  
  428.         ' Contact update
  429.         IF (Shape(ShapeIndex).CollisionSwitch = 1) THEN
  430.             Shape(ShapeIndex).Collisions = Shape(ShapeIndex).Collisions + 1
  431.         ELSE
  432.             Shape(ShapeIndex).Collisions = 0
  433.         END IF
  434.  
  435.         IF ((Shape(ShapeIndex).Fixed = 0)) THEN
  436.  
  437.             ' Angular velocity update
  438.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + Shape(ShapeIndex).DeltaO
  439.  
  440.             ' Linear velocity update
  441.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Shape(ShapeIndex).DeltaV.x
  442.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Shape(ShapeIndex).DeltaV.y
  443.  
  444.             IF (Shape(ShapeIndex).Collisions = 0) THEN
  445.                 ' Freefall (if airborne)
  446.                 Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + ForceField.x
  447.                 Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + ForceField.y
  448.             ELSE
  449.                 ' Friction
  450.                 IF (Shape(ShapeIndex).Velocity.x * Shape(ShapeIndex).Velocity.x) < .05 THEN Shape(ShapeIndex).Velocity.x = 0
  451.                 IF (Shape(ShapeIndex).Velocity.y * Shape(ShapeIndex).Velocity.y) < .05 THEN Shape(ShapeIndex).Velocity.y = 0
  452.                 'IF (Shape(ShapeIndex).Omega * Shape(ShapeIndex).Omega) < .001 THEN Shape(ShapeIndex).Omega = 0
  453.             END IF
  454.  
  455.             ' Rotation update
  456.             CALL RotShape(ShapeIndex, Shape(ShapeIndex).Centroid, Shape(ShapeIndex).Omega)
  457.  
  458.             ' Position update
  459.             CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  460.  
  461.             ' Damping
  462.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .995
  463.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .995
  464.             'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * (1 - .25 * (Shape(ShapeIndex).DiameterMin / Shape(ShapeIndex).DiameterMax))
  465.  
  466.         END IF
  467.     NEXT
  468.  
  469.     CALL Graphics
  470.     _LIMIT 120
  471.  
  472.  
  473. SUB Graphics
  474.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  475.     FOR ShapeIndex = 1 TO ShapeCount
  476.         'LOCATE 1, 1: PRINT ProximalPairsCount, CollisionCount ', Shape(ShapeCount).Collisions, Shape(ShapeCount - 1).Collisions
  477.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  478.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  479.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  480.         NEXT
  481.         CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  482.         CALL cline(PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  483.         'CALL cline(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  484.         'CALL ccircle(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, 3, _RGB(255, 255, 255))
  485.     NEXT
  486.     CALL cpaint(Shape(ShapeCount).Centroid.x, Shape(ShapeCount).Centroid.y, Shape(ShapeCount).Shade, Shape(ShapeCount).Shade)
  487.     _DISPLAY
  488.  
  489. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  490.     DIM l AS Vector
  491.     DIM r AS Vector
  492.     li = i - 1
  493.     ri = i + 1
  494.     IF (i = 1) THEN li = Shape(k).Elements
  495.     IF (i = Shape(k).Elements) THEN ri = 1
  496.     l.x = PointChain(k, li).x
  497.     r.x = PointChain(k, ri).x
  498.     dx = r.x - l.x
  499.     CalculateNormalX = dx
  500.  
  501. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  502.     DIM l AS Vector
  503.     DIM r AS Vector
  504.     li = i - 1
  505.     ri = i + 1
  506.     IF (i = 1) THEN li = Shape(k).Elements
  507.     IF (i = Shape(k).Elements) THEN ri = 1
  508.     l.y = PointChain(k, li).y
  509.     r.y = PointChain(k, ri).y
  510.     dy = r.y - l.y
  511.     CalculateNormalY = dy
  512.  
  513. SUB CalculateCentroid (k AS INTEGER)
  514.     xx = 0
  515.     yy = 0
  516.     FOR i = 1 TO Shape(k).Elements
  517.         xx = xx + PointChain(k, i).x
  518.         yy = yy + PointChain(k, i).y
  519.     NEXT
  520.     Shape(k).Centroid.x = xx / Shape(k).Elements
  521.     Shape(k).Centroid.y = yy / Shape(k).Elements
  522.  
  523. SUB CalculateDiameterMin (k AS INTEGER)
  524.     r2min = 999 ^ 999
  525.     FOR i = 1 TO Shape(k).Elements
  526.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  527.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  528.         r2 = xx * xx + yy * yy
  529.         IF (r2 < r2min) THEN
  530.             r2min = r2
  531.         END IF
  532.     NEXT
  533.     Shape(k).DiameterMin = SQR(r2min)
  534.  
  535. SUB CalculateDiameterMax (k AS INTEGER)
  536.     r2max = -1
  537.     FOR i = 1 TO Shape(k).Elements
  538.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  539.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  540.         r2 = xx * xx + yy * yy
  541.         IF (r2 > r2max) THEN
  542.             r2max = r2
  543.         END IF
  544.     NEXT
  545.     Shape(k).DiameterMax = SQR(r2max)
  546.  
  547. SUB CalculateMass (k AS INTEGER, factor AS DOUBLE)
  548.     aa = 0
  549.     FOR i = 1 TO Shape(k).Elements - 1
  550.         x = (Shape(k).Centroid.x - PointChain(k, i).x)
  551.         y = (Shape(k).Centroid.y - PointChain(k, i).y)
  552.         dx = (Shape(k).Centroid.x - PointChain(k, i + 1).x) - x
  553.         dy = (Shape(k).Centroid.y - PointChain(k, i + 1).y) - y
  554.         da = .5 * (x * dy - y * dx)
  555.         aa = aa + da
  556.     NEXT
  557.     Shape(k).Mass = factor * SQR(aa * aa)
  558.  
  559. SUB CalculateMOI (k AS INTEGER, ctrvec AS Vector)
  560.     xx = 0
  561.     yy = 0
  562.     FOR i = 1 TO Shape(k).Elements
  563.         a = ctrvec.x - PointChain(k, i).x
  564.         b = ctrvec.y - PointChain(k, i).y
  565.         xx = xx + a * a
  566.         yy = yy + b * b
  567.     NEXT
  568.     Shape(k).MOI = SQR((xx + yy) * (xx + yy)) * (Shape(k).Mass / Shape(k).Elements)
  569.  
  570. SUB TranslateShape (k AS INTEGER, c AS Vector)
  571.     FOR i = 1 TO Shape(k).Elements
  572.         PointChain(k, i).x = PointChain(k, i).x + c.x
  573.         PointChain(k, i).y = PointChain(k, i).y + c.y
  574.     NEXT
  575.     Shape(k).Centroid.x = Shape(k).Centroid.x + c.x
  576.     Shape(k).Centroid.y = Shape(k).Centroid.y + c.y
  577.  
  578. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  579.     FOR i = 1 TO Shape(k).Elements
  580.         xx = PointChain(k, i).x - c.x
  581.         yy = PointChain(k, i).y - c.y
  582.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  583.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  584.     NEXT
  585.     xx = Shape(k).Centroid.x - c.x
  586.     yy = Shape(k).Centroid.y - c.y
  587.     Shape(k).Centroid.x = c.x + xx * COS(da) - yy * SIN(da)
  588.     Shape(k).Centroid.y = c.y + yy * COS(da) + xx * SIN(da)
  589.  
  590. SUB NewAutoBall (x1, y1, r1, r2, pa, pb, fx)
  591.     ShapeCount = ShapeCount + 1
  592.     Shape(ShapeCount).Fixed = fx
  593.     Shape(ShapeCount).Collisions = 0
  594.     i = 0
  595.     FOR j = 0 TO 2 * 4 * ATN(1) - .15 STEP .15
  596.         i = i + 1
  597.         r = r1 + r2 * COS(pa * j) ^ pb
  598.         PointChain(ShapeCount, i).x = x1 + r * COS(j)
  599.         PointChain(ShapeCount, i).y = y1 + r * SIN(j)
  600.     NEXT
  601.     Shape(ShapeCount).Elements = i
  602.     IF (fx = 0) THEN
  603.         CALL CalculateMass(ShapeCount, 1)
  604.     ELSE
  605.         CALL CalculateMass(ShapeCount, 999999)
  606.     END IF
  607.     CALL CalculateCentroid(ShapeCount)
  608.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  609.     CALL CalculateDiameterMin(ShapeCount)
  610.     CALL CalculateDiameterMax(ShapeCount)
  611.     Shape(ShapeCount).Velocity.x = 0
  612.     Shape(ShapeCount).Velocity.y = 0
  613.     Shape(ShapeCount).Omega = 0
  614.     IF (fx = 0) THEN
  615.         Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  616.     ELSE
  617.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  618.     END IF
  619.  
  620. SUB NewAutoBrick (x1, y1, wx, wy, ang)
  621.     ShapeCount = ShapeCount + 1
  622.     Shape(ShapeCount).Fixed = 1
  623.     Shape(ShapeCount).Collisions = 0
  624.     i = 0
  625.     FOR j = -wy / 2 TO wy / 2 STEP 5
  626.         i = i + 1
  627.         PointChain(ShapeCount, i).x = x1 + wx / 2
  628.         PointChain(ShapeCount, i).y = y1 + j
  629.     NEXT
  630.     FOR j = wx / 2 TO -wx / 2 STEP -5
  631.         i = i + 1
  632.         PointChain(ShapeCount, i).x = x1 + j
  633.         PointChain(ShapeCount, i).y = y1 + wy / 2
  634.     NEXT
  635.     FOR j = wy / 2 TO -wy / 2 STEP -5
  636.         i = i + 1
  637.         PointChain(ShapeCount, i).x = x1 - wx / 2
  638.         PointChain(ShapeCount, i).y = y1 + j
  639.     NEXT
  640.     FOR j = -wx / 2 TO wx / 2 STEP 5
  641.         i = i + 1
  642.         PointChain(ShapeCount, i).x = x1 + j
  643.         PointChain(ShapeCount, i).y = y1 - wy / 2
  644.     NEXT
  645.     Shape(ShapeCount).Elements = i
  646.     CALL CalculateMass(ShapeCount, 99999)
  647.     CALL CalculateCentroid(ShapeCount)
  648.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  649.     CALL CalculateDiameterMin(ShapeCount)
  650.     CALL CalculateDiameterMax(ShapeCount)
  651.     Shape(ShapeCount).MOI = 999 ^ 999
  652.     Shape(ShapeCount).Velocity.x = 0
  653.     Shape(ShapeCount).Velocity.y = 0
  654.     Shape(ShapeCount).Omega = 0
  655.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  656.     CALL RotShape(ShapeCount, Shape(ShapeCount).Centroid, ang)
  657.  
  658. SUB NewBrickLine (xi, yi, xf, yf, l, w)
  659.     d1 = SQR((xf - xi) ^ 2 + (yf - yi) ^ 2)
  660.     d2 = SQR(l ^ 2 + w ^ 2)
  661.     ang = ATN((yf - yi) / (xf - xi))
  662.     FOR t = 0 TO 1 STEP 1.175 * d2 / d1
  663.         CALL NewAutoBrick(xi * (1 - t) + xf * t, yi * (1 - t) + yf * t, l, w, ang)
  664.     NEXT
  665.  
  666. SUB NewMouseShape (rawresolution AS DOUBLE, targetpoints AS INTEGER, smoothiterations AS INTEGER)
  667.     'rawresolution = Raw curve resolution.
  668.     'targetpoints = Number of points per curve.
  669.     'smoothiterations = Magnitude of smooth effect.
  670.  
  671.     ShapeCount = ShapeCount + 1
  672.     Shape(ShapeCount).Fixed = 0
  673.     Shape(ShapeCount).Collisions = 0
  674.  
  675.     numpoints = 0
  676.  
  677.     ' Click+drag mouse button 1 to trace out a curve.
  678.     xold = 999999
  679.     yold = 999999
  680.     DO
  681.         DO WHILE _MOUSEINPUT
  682.             x = _MOUSEX
  683.             y = _MOUSEY
  684.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  685.                 IF _MOUSEBUTTON(1) THEN
  686.                     x = x - (_WIDTH / 2)
  687.                     y = -y + (_HEIGHT / 2)
  688.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  689.  
  690.                     ' Collect data only if the new point is sufficiently far away from the previous point.
  691.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  692.                         numpoints = numpoints + 1
  693.                         PointChain(ShapeCount, numpoints).x = x
  694.                         PointChain(ShapeCount, numpoints).y = y
  695.                         CALL cpset(x, y, _RGB(0, 255, 255))
  696.                         xold = x
  697.                         yold = y
  698.                     END IF
  699.                 END IF
  700.             END IF
  701.         LOOP
  702.         _DISPLAY
  703.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  704.  
  705.     ' If the curve contains less than the minimum numer of points, use interpolation to fill in the gaps.
  706.     DO WHILE (numpoints < targetpoints)
  707.  
  708.         ' Determine the pair of neighboring points that have the greatest separation of all pairs.
  709.         rad2max = -1
  710.         kmax = -1
  711.         FOR k = 1 TO numpoints - 1
  712.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  713.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  714.             rad2 = xfac ^ 2 + yfac ^ 2
  715.             IF rad2 > rad2max THEN
  716.                 kmax = k
  717.                 rad2max = rad2
  718.             END IF
  719.         NEXT
  720.         '''
  721.         edgecase = 0
  722.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  723.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  724.         rad2 = xfac ^ 2 + yfac ^ 2
  725.         IF rad2 > rad2max THEN
  726.             kmax = numpoints
  727.             rad2max = rad2
  728.             edgecase = 1
  729.         END IF
  730.         '''
  731.         IF (edgecase = 0) THEN
  732.  
  733.             numpoints = numpoints + 1
  734.  
  735.             ' Starting next to kmax, create a gap by shifting all other points by one index.
  736.             FOR j = numpoints TO kmax + 1 STEP -1
  737.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  738.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  739.             NEXT
  740.  
  741.             ' Fill the gap with a new point whose position is determined by the average of its neighbors.
  742.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  743.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  744.  
  745.         ELSE
  746.             numpoints = numpoints + 1
  747.             xx = PointChain(ShapeCount, numpoints - 1).x
  748.             yy = PointChain(ShapeCount, numpoints - 1).y
  749.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  750.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  751.         END IF
  752.  
  753.     LOOP
  754.  
  755.     ' At this stage, the curve still has all of its sharp edges. Use a relaxation method to smooth.
  756.     ' The new position of a point is equal to the average position of its neighboring points.
  757.     CALL SmoothShape(ShapeCount, numpoints, smoothiterations)
  758.  
  759.     Shape(ShapeCount).Elements = numpoints
  760.     CALL CalculateMass(ShapeCount, 1)
  761.     CALL CalculateCentroid(ShapeCount)
  762.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  763.     CALL CalculateDiameterMin(ShapeCount)
  764.     CALL CalculateDiameterMax(ShapeCount)
  765.     Shape(ShapeCount).Velocity.x = 0
  766.     Shape(ShapeCount).Velocity.y = 0
  767.     Shape(ShapeCount).Omega = 0
  768.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  769.  
  770.  
  771. SUB SmoothShape (i AS INTEGER, n AS INTEGER, s AS INTEGER)
  772.     FOR j = 1 TO s
  773.         FOR k = 2 TO n - 1
  774.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  775.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  776.         NEXT
  777.         FOR k = 2 TO n - 1
  778.             PointChain(i, k).x = TempChain(i, k).x
  779.             PointChain(i, k).y = TempChain(i, k).y
  780.         NEXT
  781.     NEXT
  782.  
  783. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  784.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  785.  
  786. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  787.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  788.  
  789. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  790.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  791.  
  792. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  793.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  794.  
  795. SUB cprintstring (y, a AS STRING)
  796.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) * 8) / 2, -y + _HEIGHT / 2), a
  797.  
  798. SUB snd (frq, dur)
  799.     IF ((frq >= 37) AND (frq <= 2000)) THEN
  800.         SOUND frq, dur
  801.     END IF
« Last Edit: February 29, 2020, 08:08:47 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #17 on: February 29, 2020, 07:18:01 pm »
A quick update below. Still deciding what the end-game for this will be.

Something like Box2D for QB64 would be awesome! :-)))

https://box2d.org/
In order to understand recursion, one must first understand recursion.

FellippeHeitor

  • Guest
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #18 on: February 29, 2020, 07:21:57 pm »
A very interesting update! Objects seem heavier and more easily controllable. I bounced an object against one of the primary eggs so hard that it ended up inside a potato I'd drawn before.

This is so cool, man.

 
Captura de Tela 2020-02-29 às 21.17.48.png

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #19 on: February 29, 2020, 07:34:46 pm »
@TerryRitchie

Yeah, there are a handful of nice examples of 2D physics libraries out there, box2d seems cool, I like matter.js quite a bit too. I'm finding that JS and C++ are a bit faster than QB64 for a variety of reasons, and the more complexity I add, the more complicated the loop gets and things bog down. I'm trying to walk that fine edge between capturing real physics and being just simply efficient. It's an incredibly slow process given my schedule. I can only wrap my head properly around this like one day a week, haha. So there's that effect too.

@FellippeHeitor

Thanks for noting that behavior! I undoubtedly noticed, and oh man - there is some wacky stuff you can do. It turns out that objects being drawn clockwise turned out to have *negative* mass, which is surely a bug I fixed - but now the centroid has to always be inside the object. You can intentionally draw objects within objects, but of course they exchange momentum backwards, haha. If I decide to keep that I'll have to put in a line that reverses a normal vector somewhere.

Since I'm not sure I want this code dominating my life and I'm only one guy, I may let this round off right where it is: doing collisions more-or-less perfectly, but avoid doing rag-doll physics, meshes, membranes, or whatever would be next.
You're not done when it works, you're done when it's right.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Made the code far tighter, and to celebrate, here's the best pool break I've made to date. It's not the real game of course, but if anyone in that department wants to start thinking about graphics, you've got the motion part already.

By "tighter", I mean everything that specifies the game, including physics constants, takes place before line, I think 115 in this edition, and that can be made even sooner.

Code: QB64: [Select]
  1. ' Display
  2. SCREEN _NEWIMAGE(800, 600, 32)
  3. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  4.  
  5. ' Meta
  6. start:
  7.  
  8.  
  9. ' Data structures
  10. TYPE Vector
  11.     x AS DOUBLE
  12.     y AS DOUBLE
  13.  
  14. DIM SHARED vtemp AS Vector
  15.  
  16. ' Object type
  17. TYPE Object
  18.     Centroid AS Vector
  19.     Collisions AS LONG
  20.     CollisionSwitch AS INTEGER
  21.     DeltaO AS DOUBLE
  22.     DeltaV AS Vector
  23.     DiameterMax AS DOUBLE
  24.     DiameterMin AS DOUBLE
  25.     Elements AS INTEGER
  26.     Fixed AS INTEGER
  27.     Mass AS DOUBLE
  28.     MOI AS DOUBLE
  29.     PartialNormal AS Vector
  30.     Omega AS DOUBLE
  31.     Shade AS _UNSIGNED LONG
  32.     Velocity AS Vector
  33.  
  34. ' Object storage
  35. DIM SHARED Shape(300) AS Object
  36. DIM SHARED PointChain(300, 500) AS Vector
  37. DIM SHARED TempChain(300, 500) AS Vector
  38. DIM SHARED ShapeCount AS INTEGER
  39.  
  40. ' Dynamics
  41. DIM SHARED CollisionCount AS INTEGER
  42. DIM SHARED ProximalPairs(300 / 2, 1 TO 2)
  43. DIM SHARED ProximalPairsCount
  44. DIM SHARED CPC, FPC, RST, VD, SV
  45.  
  46. ' Environment
  47. DIM SHARED ForceField AS Vector ' Gravity
  48.  
  49. ' Initialize
  50. ShapeCount = 0
  51. CollisionCount = 0
  52.  
  53. ' Prompt
  54. CALL cprintstring(16 * 4, "WELCOME!                  ")
  55. CALL cprintstring(16 * 3, "Press 1 for Pool Prototype")
  56. CALL cprintstring(16 * 2, "Press 2 for Wacky Game    ")
  57.  
  58.     kk = _KEYHIT
  59.     SELECT CASE kk
  60.         CASE ASC("1")
  61.             CALL SetupPool
  62.             EXIT DO
  63.         CASE ASC("2")
  64.             CALL SetupWackyGame
  65.             EXIT DO
  66.     END SELECT
  67.     IF (kk) THEN
  68.         _KEYCLEAR
  69.     END IF
  70.     _LIMIT 30
  71.  
  72. CALL Graphics
  73. CALL cprintstring(-16 * 3, "During Play:")
  74. CALL cprintstring(-16 * 5, "Arrow keys boost most recently-created object.")
  75. CALL cprintstring(-16 * 6, "Mouse 1 moves most recently-created object.   ")
  76. CALL cprintstring(-16 * 7, "ESC halts all motion.                         ")
  77. CALL cprintstring(-16 * 8, "Space enters creative mode.                   ")
  78. CALL cprintstring(-16 * 9, "Press 'r' to restart.                         ")
  79. CALL cprintstring(-16 * 11, "PRESS ANY KEY TO BEGIN.")
  80.  
  81. ' Main loop
  82.     IF (UserInput = -1) THEN GOTO start
  83.     CALL PairDynamics(CPC, FPC, RST)
  84.     CALL FleetDynamics(VD, SV)
  85.     CALL Graphics
  86.     _LIMIT 120
  87.  
  88.  
  89. FUNCTION UserInput
  90.     TheReturn = 0
  91.     ' Keyboard input
  92.     kk = _KEYHIT
  93.     SELECT CASE kk
  94.         CASE 32
  95.             DO: LOOP UNTIL _KEYHIT
  96.             WHILE _MOUSEINPUT: WEND
  97.             _KEYCLEAR
  98.             CALL cprintstring(16 * 17, "Drag mouse counter-clockwise to draw a new shape.")
  99.             CALL cprintstring(16 * 16, "Make sure centroid is inside body.")
  100.             'CALL Graphics
  101.             CALL NewMouseShape(7.5, 75, 25)
  102.             CLS
  103.         CASE 18432, ASC("w") ' Up arrow
  104.             Shape(ShapeCount).Velocity.y = Shape(ShapeCount).Velocity.y * 1.1 + 2
  105.         CASE 20480, ASC("s") ' Down arrow
  106.             Shape(ShapeCount).Velocity.y = Shape(ShapeCount).Velocity.y * 0.9 - 2
  107.         CASE 19200, ASC("a") ' Left arrow
  108.             Shape(ShapeCount).Velocity.x = Shape(ShapeCount).Velocity.x * 0.9 - 2
  109.         CASE 19712, ASC("d") ' Right arrow
  110.             Shape(ShapeCount).Velocity.x = Shape(ShapeCount).Velocity.x * 1.1 + 2
  111.         CASE ASC("r")
  112.             TheReturn = -1
  113.         CASE 27
  114.             FOR k = 1 TO ShapeCount
  115.                 Shape(k).Velocity.x = .000001
  116.                 Shape(k).Velocity.y = .000001
  117.                 Shape(k).Omega = .000001
  118.             NEXT
  119.     END SELECT
  120.     IF (kk) THEN
  121.         _KEYCLEAR
  122.     END IF
  123.  
  124.     ' Mouse input
  125.     mb = 0
  126.         x = _MOUSEX
  127.         y = _MOUSEY
  128.         IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  129.             IF (_MOUSEBUTTON(1)) THEN
  130.                 IF (mb = 0) THEN
  131.                     mb = 1
  132.                     x = x - (_WIDTH / 2)
  133.                     y = -y + (_HEIGHT / 2)
  134.                     vtemp.x = x - Shape(ShapeCount).Centroid.x
  135.                     vtemp.y = y - Shape(ShapeCount).Centroid.y
  136.                     CALL TranslateShape(ShapeCount, vtemp)
  137.                     Shape(ShapeCount).Velocity.x = 0
  138.                     Shape(ShapeCount).Velocity.y = 0
  139.                     Shape(ShapeCount).Omega = 0
  140.                 END IF
  141.             END IF
  142.             IF (_MOUSEBUTTON(2)) THEN
  143.                 IF (mb = 0) THEN
  144.                     mb = 1
  145.                     x = x - (_WIDTH / 2)
  146.                     y = -y + (_HEIGHT / 2)
  147.                     CALL NewAutoBall(x, y, 5, 0, 1, 1, 1)
  148.                     _DELAY .1
  149.                 END IF
  150.             END IF
  151.         END IF
  152.     LOOP
  153.     UserInput = TheReturn
  154.  
  155. SUB PairDynamics (CoarseProximityConstant, FineProximityConstant, Restitution)
  156.  
  157.     ' Proximity detection
  158.     ProximalPairsCount = 0
  159.     Shape1 = 0
  160.     Shape2 = 0
  161.     FOR j = 1 TO ShapeCount
  162.         Shape(j).CollisionSwitch = 0
  163.         Shape(j).DeltaO = 0
  164.         Shape(j).DeltaV.x = 0
  165.         Shape(j).DeltaV.y = 0
  166.         Shape(j).PartialNormal.x = 0
  167.         Shape(j).PartialNormal.y = 0
  168.         FOR k = j + 1 TO ShapeCount
  169.             dx = Shape(j).Centroid.x - Shape(k).Centroid.x
  170.             dy = Shape(j).Centroid.y - Shape(k).Centroid.y
  171.             dr = SQR(dx * dx + dy * dy)
  172.             IF (dr < (CoarseProximityConstant) * (Shape(j).DiameterMax + Shape(k).DiameterMax)) THEN
  173.                 ProximalPairsCount = ProximalPairsCount + 1
  174.                 ProximalPairs(ProximalPairsCount, 1) = j
  175.                 ProximalPairs(ProximalPairsCount, 2) = k
  176.                 Shape1 = j
  177.                 Shape2 = k
  178.             END IF
  179.         NEXT
  180.     NEXT
  181.  
  182.     IF (ProximalPairsCount > 0) THEN
  183.         FOR n = 1 TO ProximalPairsCount
  184.             Shape1 = ProximalPairs(n, 1)
  185.             Shape2 = ProximalPairs(n, 2)
  186.  
  187.             ' Collision detection
  188.             rmin = 999999999
  189.             ClosestIndex1 = 0
  190.             ClosestIndex2 = 0
  191.             FOR j = 1 TO Shape(Shape1).Elements
  192.                 FOR k = 1 TO Shape(Shape2).Elements
  193.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  194.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  195.                     r2 = dx * dx + dy * dy
  196.  
  197.                     IF (r2 <= FineProximityConstant) THEN
  198.  
  199.                         ' Normal vector 1
  200.                         nx1 = CalculateNormalY(Shape1, j)
  201.                         ny1 = -CalculateNormalX(Shape1, j)
  202.                         nn = SQR(nx1 * nx1 + ny1 * ny1)
  203.                         nx1 = nx1 / nn
  204.                         ny1 = ny1 / nn
  205.                         Shape(Shape1).PartialNormal.x = Shape(Shape1).PartialNormal.x + nx1
  206.                         Shape(Shape1).PartialNormal.y = Shape(Shape1).PartialNormal.y + ny1
  207.  
  208.                         ' Normal vector 2
  209.                         nx2 = CalculateNormalY(Shape2, k)
  210.                         ny2 = -CalculateNormalX(Shape2, k)
  211.                         nn = SQR(nx2 * nx2 + ny2 * ny2)
  212.                         nx2 = nx2 / nn
  213.                         ny2 = ny2 / nn
  214.                         Shape(Shape2).PartialNormal.x = Shape(Shape2).PartialNormal.x + nx2
  215.                         Shape(Shape2).PartialNormal.y = Shape(Shape2).PartialNormal.y + ny2
  216.  
  217.                     END IF
  218.                     IF (r2 < rmin) THEN
  219.                         rmin = r2
  220.                         ClosestIndex1 = j
  221.                         ClosestIndex2 = k
  222.                     END IF
  223.                 NEXT
  224.             NEXT
  225.  
  226.             IF (rmin <= FineProximityConstant) THEN
  227.  
  228.                 CollisionCount = CollisionCount + 1
  229.                 Shape(Shape1).CollisionSwitch = 1
  230.                 Shape(Shape2).CollisionSwitch = 1
  231.  
  232.                 ' Undo previous motion
  233.                 IF (Shape(Shape1).Collisions = 0) THEN
  234.                     CALL RotShape(Shape1, Shape(Shape1).Centroid, -Shape(Shape1).Omega)
  235.                     vtemp.x = -(Shape(Shape1).Velocity.x)
  236.                     vtemp.y = -(Shape(Shape1).Velocity.y)
  237.                     CALL TranslateShape(Shape1, vtemp)
  238.                 END IF
  239.                 IF (Shape(Shape2).Collisions = 0) THEN
  240.                     CALL RotShape(Shape2, Shape(Shape2).Centroid, -Shape(Shape2).Omega)
  241.                     vtemp.x = -(Shape(Shape2).Velocity.x)
  242.                     vtemp.y = -(Shape(Shape2).Velocity.y)
  243.                     CALL TranslateShape(Shape2, vtemp)
  244.                 END IF
  245.  
  246.                 IF (Shape(Shape1).Collisions = 0) THEN
  247.                     Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * Restitution
  248.                     Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * Restitution
  249.                 END IF
  250.                 IF (Shape(Shape2).Collisions = 0) THEN
  251.                     Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * Restitution
  252.                     Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * Restitution
  253.                 END IF
  254.  
  255.                 ' Normal vector 1 (nx1, ny1)
  256.                 nn = SQR(Shape(Shape1).PartialNormal.x * Shape(Shape1).PartialNormal.x + Shape(Shape1).PartialNormal.y * Shape(Shape1).PartialNormal.y)
  257.                 nx1 = Shape(Shape1).PartialNormal.x / nn
  258.                 ny1 = Shape(Shape1).PartialNormal.y / nn
  259.                 Shape(Shape1).PartialNormal.x = nx1
  260.                 Shape(Shape1).PartialNormal.y = ny1
  261.  
  262.                 ' Normal vector 2 (nx2, ny2)
  263.                 nn = SQR(Shape(Shape2).PartialNormal.x * Shape(Shape2).PartialNormal.x + Shape(Shape2).PartialNormal.y * Shape(Shape2).PartialNormal.y)
  264.                 nx2 = Shape(Shape2).PartialNormal.x / nn
  265.                 ny2 = Shape(Shape2).PartialNormal.y / nn
  266.                 Shape(Shape2).PartialNormal.x = nx2
  267.                 Shape(Shape2).PartialNormal.y = ny2
  268.  
  269.                 ' Contact-centroid differentials 1 (dx1, dy1)
  270.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).Centroid.x
  271.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).Centroid.y
  272.  
  273.                 ' Contact-centroid differentials 2 (dx2, dy2)
  274.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).Centroid.x
  275.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).Centroid.y
  276.  
  277.                 ' Perpendicular vector 1 (px1, py1)
  278.                 px1 = -dy1
  279.                 py1 = dx1
  280.                 pp = SQR(px1 * px1 + py1 * py1)
  281.                 px1 = px1 / pp
  282.                 py1 = py1 / pp
  283.  
  284.                 ' Perpendicular vector 2 (px2, py2)
  285.                 px2 = -dy2
  286.                 py2 = dx2
  287.                 pp = SQR(px2 * px2 + py2 * py2)
  288.                 px2 = px2 / pp
  289.                 py2 = py2 / pp
  290.  
  291.                 ' Angular velocity vector 1 (w1, r1, vx1, vy1)
  292.                 w1 = Shape(Shape1).Omega
  293.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  294.                 vx1 = w1 * r1 * px1
  295.                 vy1 = w1 * r1 * py1
  296.  
  297.                 ' Angular velocity vector 2 (w2, r2, vx2, vy2)
  298.                 w2 = Shape(Shape2).Omega
  299.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  300.                 vx2 = w2 * r2 * px2
  301.                 vy2 = w2 * r2 * py2
  302.  
  303.                 ' Mass terms (m1, m2, mu)
  304.                 m1 = Shape(Shape1).Mass
  305.                 m2 = Shape(Shape2).Mass
  306.                 mu = 1 / (1 / m1 + 1 / m2)
  307.  
  308.                 ' Re-Calculate moment of inertia (i1, i2)
  309.                 vtemp.x = PointChain(Shape1, ClosestIndex1).x
  310.                 vtemp.y = PointChain(Shape1, ClosestIndex1).y
  311.                 CALL CalculateMOI(Shape1, vtemp)
  312.                 vtemp.x = PointChain(Shape2, ClosestIndex2).x
  313.                 vtemp.y = PointChain(Shape2, ClosestIndex2).y
  314.                 CALL CalculateMOI(Shape2, vtemp)
  315.                 i1 = Shape(Shape1).MOI
  316.                 i2 = Shape(Shape2).MOI
  317.  
  318.                 ' Velocity differentials (v1, v2, dvtx, dvty)
  319.                 vcx1 = Shape(Shape1).Velocity.x
  320.                 vcy1 = Shape(Shape1).Velocity.y
  321.                 vcx2 = Shape(Shape2).Velocity.x
  322.                 vcy2 = Shape(Shape2).Velocity.y
  323.                 vtx1 = vcx1 + vx1
  324.                 vty1 = vcy1 + vy1
  325.                 vtx2 = vcx2 + vx2
  326.                 vty2 = vcy2 + vy2
  327.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  328.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  329.                 dvtx = vtx2 - vtx1
  330.                 dvty = vty2 - vty1
  331.  
  332.                 ' Geometry (n1dotdvt, n2dotdvt)
  333.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  334.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  335.  
  336.                 ' Momentum exchange (qx1, qy1, qx2, qy2)
  337.                 qx1 = nx1 * 2 * mu * n1dotdvt
  338.                 qy1 = ny1 * 2 * mu * n1dotdvt
  339.                 qx2 = nx2 * 2 * mu * n2dotdvt
  340.                 qy2 = ny2 * 2 * mu * n2dotdvt
  341.  
  342.                 ' Momentum exchange unit vector
  343.                 qq = SQR(qx1 * qx1 + qy1 * qy1)
  344.                 qhatx1 = qx1 / qq
  345.                 qhaty1 = qy1 / qq
  346.                 qq = SQR(qx2 * qx2 + qy2 * qy2)
  347.                 qhatx2 = qx2 / qq
  348.                 qhaty2 = qy2 / qq
  349.  
  350.                 ' Translational impulse
  351.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  352.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  353.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  354.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  355.                 f1 = -q1dotn1 * n1dotr1hat
  356.                 f2 = -q2dotn2 * n2dotr2hat
  357.                 Shape(Shape1).DeltaV.x = Shape(Shape1).DeltaV.x + f1 * qx1 / m1
  358.                 Shape(Shape1).DeltaV.y = Shape(Shape1).DeltaV.y + f1 * qy1 / m1
  359.                 Shape(Shape2).DeltaV.x = Shape(Shape2).DeltaV.x + f2 * qx2 / m2
  360.                 Shape(Shape2).DeltaV.y = Shape(Shape2).DeltaV.y + f2 * qy2 / m2
  361.  
  362.                 ' Angular impulse
  363.                 q1dotp1 = qx1 * px1 + qy1 * py1
  364.                 q2dotp2 = qx2 * px2 + qy2 * py2
  365.                 dw1 = r1 * q1dotp1 / i1
  366.                 dw2 = -r2 * q2dotp2 / i2
  367.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO + dw1
  368.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO + dw2
  369.  
  370.                 ' Separate along normal
  371.                 'IF (Shape(Shape1).Collisions = 0) THEN
  372.                 vtemp.x = -nx1 * 1 * ((v1 + 0) / m1)
  373.                 vtemp.y = -ny1 * 1 * ((v1 + 0) / m1)
  374.                 CALL TranslateShape(Shape1, vtemp)
  375.                 'END IF
  376.                 'IF (Shape(Shape2).Collisions = 0) THEN
  377.                 vtemp.x = -nx2 * 1 * ((v2 + 0) / m2)
  378.                 vtemp.y = -ny2 * 1 * ((v2 + 0) / m2)
  379.                 CALL TranslateShape(Shape2, vtemp)
  380.                 'END IF
  381.  
  382.                 ' External torque
  383.                 'IF (Shape(Shape1).Collisions > 0) THEN
  384.                 torque1 = m1 * (dx1 * ForceField.y - dy1 * ForceField.x)
  385.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO - torque1 / i1
  386.                 'END IF
  387.                 'IF (Shape(Shape2).Collisions > 0) THEN
  388.                 torque2 = m2 * (dx2 * ForceField.y - dy2 * ForceField.x)
  389.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO - torque2 / i2
  390.                 'END IF
  391.  
  392.                 ' Feedback
  393.                 IF (Shape(Shape1).Collisions = 0) AND (Shape(Shape2).Collisions = 0) THEN
  394.                     CALL snd(100 * (v1 + v2) / 2, .5)
  395.                 END IF
  396.  
  397.             END IF
  398.         NEXT
  399.     END IF
  400.  
  401. SUB FleetDynamics (VelocityDamping, StoppingVelocity)
  402.     FOR ShapeIndex = 1 TO ShapeCount
  403.  
  404.         ' Contact update
  405.         IF (Shape(ShapeIndex).CollisionSwitch = 1) THEN
  406.             Shape(ShapeIndex).Collisions = Shape(ShapeIndex).Collisions + 1
  407.         ELSE
  408.             Shape(ShapeIndex).Collisions = 0
  409.         END IF
  410.  
  411.         IF ((Shape(ShapeIndex).Fixed = 0)) THEN
  412.  
  413.             ' Angular velocity update
  414.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + Shape(ShapeIndex).DeltaO
  415.  
  416.             ' Linear velocity update
  417.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Shape(ShapeIndex).DeltaV.x
  418.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Shape(ShapeIndex).DeltaV.y
  419.  
  420.             IF (Shape(ShapeIndex).Collisions = 0) THEN
  421.                 ' Freefall (if airborne)
  422.                 Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + ForceField.x
  423.                 Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + ForceField.y
  424.             ELSE
  425.                 ' Static friction
  426.                 IF (Shape(ShapeIndex).Velocity.x * Shape(ShapeIndex).Velocity.x) < StoppingVelocity THEN Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .05
  427.                 IF (Shape(ShapeIndex).Velocity.y * Shape(ShapeIndex).Velocity.y) < StoppingVelocity THEN Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .05
  428.                 IF (Shape(ShapeIndex).Omega * Shape(ShapeIndex).Omega) < .0001 * StoppingVelocity THEN Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .05
  429.             END IF
  430.  
  431.             ' Rotation update
  432.             CALL RotShape(ShapeIndex, Shape(ShapeIndex).Centroid, Shape(ShapeIndex).Omega)
  433.  
  434.             ' Position update
  435.             CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  436.  
  437.             ' Velocity Damping
  438.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * VelocityDamping
  439.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * VelocityDamping
  440.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * VelocityDamping
  441.         END IF
  442.     NEXT
  443.  
  444. SUB Graphics
  445.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  446.     'LOCATE 1, 1: PRINT ProximalPairsCount, CollisionCount
  447.     FOR ShapeIndex = 1 TO ShapeCount
  448.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  449.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  450.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  451.         NEXT
  452.         CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  453.         CALL cline(PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  454.         CALL ccircle(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, 3, Shape(ShapeIndex).Shade)
  455.     NEXT
  456.     _DISPLAY
  457.  
  458. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  459.     DIM l AS Vector
  460.     DIM r AS Vector
  461.     li = i - 1
  462.     ri = i + 1
  463.     IF (i = 1) THEN li = Shape(k).Elements
  464.     IF (i = Shape(k).Elements) THEN ri = 1
  465.     l.x = PointChain(k, li).x
  466.     r.x = PointChain(k, ri).x
  467.     dx = r.x - l.x
  468.     CalculateNormalX = dx
  469.  
  470. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  471.     DIM l AS Vector
  472.     DIM r AS Vector
  473.     li = i - 1
  474.     ri = i + 1
  475.     IF (i = 1) THEN li = Shape(k).Elements
  476.     IF (i = Shape(k).Elements) THEN ri = 1
  477.     l.y = PointChain(k, li).y
  478.     r.y = PointChain(k, ri).y
  479.     dy = r.y - l.y
  480.     CalculateNormalY = dy
  481.  
  482. SUB CalculateCentroid (k AS INTEGER)
  483.     xx = 0
  484.     yy = 0
  485.     FOR i = 1 TO Shape(k).Elements
  486.         xx = xx + PointChain(k, i).x
  487.         yy = yy + PointChain(k, i).y
  488.     NEXT
  489.     Shape(k).Centroid.x = xx / Shape(k).Elements
  490.     Shape(k).Centroid.y = yy / Shape(k).Elements
  491.  
  492. SUB CalculateDiameterMin (k AS INTEGER)
  493.     r2min = 999 ^ 999
  494.     FOR i = 1 TO Shape(k).Elements
  495.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  496.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  497.         r2 = xx * xx + yy * yy
  498.         IF (r2 < r2min) THEN
  499.             r2min = r2
  500.         END IF
  501.     NEXT
  502.     Shape(k).DiameterMin = SQR(r2min)
  503.  
  504. SUB CalculateDiameterMax (k AS INTEGER)
  505.     r2max = -1
  506.     FOR i = 1 TO Shape(k).Elements
  507.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  508.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  509.         r2 = xx * xx + yy * yy
  510.         IF (r2 > r2max) THEN
  511.             r2max = r2
  512.         END IF
  513.     NEXT
  514.     Shape(k).DiameterMax = SQR(r2max)
  515.  
  516. SUB CalculateMass (k AS INTEGER, factor AS DOUBLE)
  517.     aa = 0
  518.     FOR i = 2 TO Shape(k).Elements
  519.         x = PointChain(k, i).x - Shape(k).Centroid.x
  520.         y = PointChain(k, i).y - Shape(k).Centroid.y
  521.         dx = (PointChain(k, i).x - PointChain(k, i - 1).x)
  522.         dy = (PointChain(k, i).y - PointChain(k, i - 1).y)
  523.         da = .5 * (x * dy - y * dx)
  524.         aa = aa + da
  525.     NEXT
  526.     Shape(k).Mass = factor * SQR(aa * aa)
  527.  
  528. SUB CalculateMOI (k AS INTEGER, ctrvec AS Vector)
  529.     xx = 0
  530.     yy = 0
  531.     FOR i = 1 TO Shape(k).Elements
  532.         a = ctrvec.x - PointChain(k, i).x
  533.         b = ctrvec.y - PointChain(k, i).y
  534.         xx = xx + a * a
  535.         yy = yy + b * b
  536.     NEXT
  537.     Shape(k).MOI = SQR((xx + yy) * (xx + yy)) * (Shape(k).Mass / Shape(k).Elements)
  538.  
  539. SUB TranslateShape (k AS INTEGER, c AS Vector)
  540.     FOR i = 1 TO Shape(k).Elements
  541.         PointChain(k, i).x = PointChain(k, i).x + c.x
  542.         PointChain(k, i).y = PointChain(k, i).y + c.y
  543.     NEXT
  544.     Shape(k).Centroid.x = Shape(k).Centroid.x + c.x
  545.     Shape(k).Centroid.y = Shape(k).Centroid.y + c.y
  546.  
  547. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  548.     FOR i = 1 TO Shape(k).Elements
  549.         xx = PointChain(k, i).x - c.x
  550.         yy = PointChain(k, i).y - c.y
  551.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  552.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  553.     NEXT
  554.     xx = Shape(k).Centroid.x - c.x
  555.     yy = Shape(k).Centroid.y - c.y
  556.     Shape(k).Centroid.x = c.x + xx * COS(da) - yy * SIN(da)
  557.     Shape(k).Centroid.y = c.y + yy * COS(da) + xx * SIN(da)
  558.  
  559. SUB NewAutoBall (x1, y1, r1, r2, pa, pb, fx)
  560.     ShapeCount = ShapeCount + 1
  561.     Shape(ShapeCount).Fixed = fx
  562.     Shape(ShapeCount).Collisions = 0
  563.     i = 0
  564.     FOR j = 0 TO 2 * 4 * ATN(1) STEP .15
  565.         i = i + 1
  566.         r = r1 + r2 * COS(pa * j) ^ pb
  567.         PointChain(ShapeCount, i).x = x1 + r * COS(j)
  568.         PointChain(ShapeCount, i).y = y1 + r * SIN(j)
  569.     NEXT
  570.     Shape(ShapeCount).Elements = i
  571.     CALL CalculateCentroid(ShapeCount)
  572.     IF (fx = 0) THEN
  573.         CALL CalculateMass(ShapeCount, 1)
  574.     ELSE
  575.         CALL CalculateMass(ShapeCount, 999999)
  576.     END IF
  577.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  578.     CALL CalculateDiameterMin(ShapeCount)
  579.     CALL CalculateDiameterMax(ShapeCount)
  580.     Shape(ShapeCount).Velocity.x = 0
  581.     Shape(ShapeCount).Velocity.y = 0
  582.     Shape(ShapeCount).Omega = 0
  583.     IF (fx = 0) THEN
  584.         Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  585.     ELSE
  586.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  587.     END IF
  588.  
  589. SUB NewAutoBrick (x1, y1, wx, wy, ang)
  590.     ShapeCount = ShapeCount + 1
  591.     Shape(ShapeCount).Fixed = 1
  592.     Shape(ShapeCount).Collisions = 0
  593.     i = 0
  594.     FOR j = -wy / 2 TO wy / 2 STEP 5
  595.         i = i + 1
  596.         PointChain(ShapeCount, i).x = x1 + wx / 2
  597.         PointChain(ShapeCount, i).y = y1 + j
  598.     NEXT
  599.     FOR j = wx / 2 TO -wx / 2 STEP -5
  600.         i = i + 1
  601.         PointChain(ShapeCount, i).x = x1 + j
  602.         PointChain(ShapeCount, i).y = y1 + wy / 2
  603.     NEXT
  604.     FOR j = wy / 2 TO -wy / 2 STEP -5
  605.         i = i + 1
  606.         PointChain(ShapeCount, i).x = x1 - wx / 2
  607.         PointChain(ShapeCount, i).y = y1 + j
  608.     NEXT
  609.     FOR j = -wx / 2 TO wx / 2 STEP 5
  610.         i = i + 1
  611.         PointChain(ShapeCount, i).x = x1 + j
  612.         PointChain(ShapeCount, i).y = y1 - wy / 2
  613.     NEXT
  614.     Shape(ShapeCount).Elements = i
  615.     CALL CalculateCentroid(ShapeCount)
  616.     CALL CalculateMass(ShapeCount, 99999)
  617.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  618.     CALL CalculateDiameterMin(ShapeCount)
  619.     CALL CalculateDiameterMax(ShapeCount)
  620.     Shape(ShapeCount).MOI = 999 ^ 999
  621.     Shape(ShapeCount).Velocity.x = 0
  622.     Shape(ShapeCount).Velocity.y = 0
  623.     Shape(ShapeCount).Omega = 0
  624.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  625.     CALL RotShape(ShapeCount, Shape(ShapeCount).Centroid, ang)
  626.  
  627. SUB NewBrickLine (xi, yi, xf, yf, wx, wy)
  628.     d1 = SQR((xf - xi) ^ 2 + (yf - yi) ^ 2)
  629.     d2 = SQR(wx ^ 2 + wy ^ 2)
  630.     ang = ATN((yf - yi) / (xf - xi))
  631.     f = 1.2 * d2 / d1
  632.     FOR t = 0 TO 1 + f STEP f
  633.         CALL NewAutoBrick(xi * (1 - t) + xf * t, yi * (1 - t) + yf * t, wx, wy, ang)
  634.     NEXT
  635.  
  636. SUB NewMouseShape (rawresolution AS DOUBLE, targetpoints AS INTEGER, smoothiterations AS INTEGER)
  637.  
  638.     ShapeCount = ShapeCount + 1
  639.     Shape(ShapeCount).Fixed = 0
  640.     Shape(ShapeCount).Collisions = 0
  641.  
  642.     numpoints = 0
  643.  
  644.     xold = 999999
  645.     yold = 999999
  646.     DO
  647.         DO WHILE _MOUSEINPUT
  648.             x = _MOUSEX
  649.             y = _MOUSEY
  650.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  651.                 IF _MOUSEBUTTON(1) THEN
  652.                     x = x - (_WIDTH / 2)
  653.                     y = -y + (_HEIGHT / 2)
  654.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  655.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  656.                         numpoints = numpoints + 1
  657.                         PointChain(ShapeCount, numpoints).x = x
  658.                         PointChain(ShapeCount, numpoints).y = y
  659.                         CALL cpset(x, y, _RGB(0, 255, 255))
  660.                         xold = x
  661.                         yold = y
  662.                     END IF
  663.                 END IF
  664.             END IF
  665.         LOOP
  666.         _DISPLAY
  667.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  668.  
  669.     DO WHILE (numpoints < targetpoints)
  670.         rad2max = -1
  671.         kmax = -1
  672.         FOR k = 1 TO numpoints - 1
  673.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  674.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  675.             rad2 = xfac ^ 2 + yfac ^ 2
  676.             IF rad2 > rad2max THEN
  677.                 kmax = k
  678.                 rad2max = rad2
  679.             END IF
  680.         NEXT
  681.         edgecase = 0
  682.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  683.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  684.         rad2 = xfac ^ 2 + yfac ^ 2
  685.         IF (rad2 > rad2max) THEN
  686.             kmax = numpoints
  687.             rad2max = rad2
  688.             edgecase = 1
  689.         END IF
  690.         IF (edgecase = 0) THEN
  691.             numpoints = numpoints + 1
  692.             FOR j = numpoints TO kmax + 1 STEP -1
  693.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  694.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  695.             NEXT
  696.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  697.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  698.         ELSE
  699.             numpoints = numpoints + 1
  700.             xx = PointChain(ShapeCount, numpoints - 1).x
  701.             yy = PointChain(ShapeCount, numpoints - 1).y
  702.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  703.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  704.         END IF
  705.     LOOP
  706.  
  707.     FOR j = 1 TO smoothiterations
  708.         FOR k = 2 TO n - 1
  709.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  710.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  711.         NEXT
  712.         FOR k = 2 TO n - 1
  713.             PointChain(i, k).x = TempChain(i, k).x
  714.             PointChain(i, k).y = TempChain(i, k).y
  715.         NEXT
  716.     NEXT
  717.  
  718.     Shape(ShapeCount).Elements = numpoints
  719.     CALL CalculateCentroid(ShapeCount)
  720.     CALL CalculateMass(ShapeCount, 1)
  721.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  722.     CALL CalculateDiameterMin(ShapeCount)
  723.     CALL CalculateDiameterMax(ShapeCount)
  724.     Shape(ShapeCount).Velocity.x = 0
  725.     Shape(ShapeCount).Velocity.y = 0
  726.     Shape(ShapeCount).Omega = 0
  727.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  728.  
  729. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  730.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  731.  
  732. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  733.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  734.  
  735. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  736.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  737.  
  738. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  739.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  740.  
  741. SUB cprintstring (y, a AS STRING)
  742.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) * 8) / 2, -y + _HEIGHT / 2), a
  743.  
  744. SUB snd (frq, dur)
  745.     IF ((frq >= 37) AND (frq <= 2000)) THEN
  746.         SOUND frq, dur
  747.     END IF
  748.  
  749. SUB SetupPool
  750.  
  751.     ' Set external field
  752.     ForceField.x = 0
  753.     ForceField.y = 0 '-.08
  754.  
  755.     ' Rectangular border
  756.     wx = 42
  757.     wy = 10
  758.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  759.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  760.     wx = 40
  761.     wy = 10
  762.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  763.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  764.  
  765.     ' Balls (billiard setup)
  766.     x0 = 160
  767.     y0 = 0
  768.     r = 15
  769.     gg = 2 * r + 3.5
  770.     xf = COS(30 * 3.14159 / 180)
  771.     yf = SIN(30 * 3.14159 / 180)
  772.     gx = gg * xf
  773.     gy = gg * yf
  774.  
  775.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  776.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  777.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  778.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  779.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  780.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  781.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r, 0, 1, 1, 0)
  782.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  783.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  784.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r, 0, 1, 1, 0)
  785.  
  786.     ' Cue ball
  787.     CALL NewAutoBall(-220, 0, r, 0, 1, 1, 0)
  788.     Shape(ShapeCount).Velocity.x = 8 + 5 * RND
  789.     Shape(ShapeCount).Velocity.y = .5 * (RND - .5)
  790.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  791.  
  792.     ' Parameters
  793.     CPC = 1.15
  794.     FPC = 8
  795.     RST = 0.75
  796.     VD = 0.995
  797.     SV = 0
  798.  
  799. SUB SetupWackyGame
  800.     ' Set external field
  801.     ForceField.x = 0
  802.     ForceField.y = -.08
  803.  
  804.     ' Rectangular border
  805.     wx = 42
  806.     wy = 10
  807.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  808.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  809.     wx = 40
  810.     wy = 10
  811.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  812.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  813.  
  814.     ' Balls
  815.     x0 = -70
  816.     y0 = 120
  817.     r1 = 15
  818.     r2 = 2.5
  819.     gg = 2 * (r1 + r2) + 3.5
  820.     xf = COS(30 * 3.14159 / 180)
  821.     yf = SIN(30 * 3.14159 / 180)
  822.     gx = gg * xf
  823.     gy = gg * yf
  824.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  825.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  826.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  827.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  828.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  829.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  830.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  831.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  832.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  833.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  834.  
  835.     ' Misc. bricks
  836.     wx = 60
  837.     wy = 10
  838.     ww = SQR(wx * wx + wy * wy) * .75
  839.     CALL NewBrickLine(ww, 0, 100 + ww, 100, wx, wy)
  840.     CALL NewBrickLine(-ww, 0, -100 - ww, 100, wx, wy)
  841.  
  842.     ' Fidget spinner
  843.     CALL NewAutoBall(-220, 0, 20, 15, 1.5, 2, 0)
  844.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  845.  
  846.     ' Parameters
  847.     CPC = 1.15
  848.     FPC = 8
  849.     RST = 0.75
  850.     VD = 0.995
  851.     SV = 0.025
  852.  
ss.png
* ss.png (Filesize: 12.06 KB, Dimensions: 801x600, Views: 183)
« Last Edit: March 01, 2020, 07:46:48 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Almighty, this is coming along nicely. It's usable in its current state, but can also be extended if I want to go on capturing more physics and simulating more kinds of things. Since the weekend is about over, time to let this cook out in public for a few days. In this version you have two options at startup. You can use the mouse to click and drag any piece. Do so carefully. Press "r" to restart during play.

Code: QB64: [Select]
  1. ' Display
  2. SCREEN _NEWIMAGE(800, 600, 32)
  3. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  4.  
  5. ' Meta
  6. start:
  7.  
  8.  
  9. ' Data structures
  10. TYPE Vector
  11.     x AS DOUBLE
  12.     y AS DOUBLE
  13.  
  14. DIM SHARED vtemp AS Vector
  15.  
  16. ' Object type
  17. TYPE Object
  18.     Centroid AS Vector
  19.     Collisions AS LONG
  20.     CollisionSwitch AS INTEGER
  21.     DeltaO AS DOUBLE
  22.     DeltaV AS Vector
  23.     DiameterMax AS DOUBLE
  24.     DiameterMin AS DOUBLE
  25.     Elements AS INTEGER
  26.     Fixed AS INTEGER
  27.     Mass AS DOUBLE
  28.     MOI AS DOUBLE
  29.     PartialNormal AS Vector
  30.     Omega AS DOUBLE
  31.     Shade AS _UNSIGNED LONG
  32.     Velocity AS Vector
  33.  
  34. ' Object storage
  35. DIM SHARED Shape(300) AS Object
  36. DIM SHARED PointChain(300, 500) AS Vector
  37. DIM SHARED TempChain(300, 500) AS Vector
  38. DIM SHARED ShapeCount AS INTEGER
  39. DIM SHARED SelectedShape AS INTEGER
  40.  
  41. ' Dynamics
  42. DIM SHARED CollisionCount AS INTEGER
  43. DIM SHARED ProximalPairs(300 / 2, 1 TO 2)
  44. DIM SHARED ProximalPairsCount
  45. DIM SHARED CPC, FPC, RST, VD, SV
  46.  
  47. ' Environment
  48. DIM SHARED ForceField AS Vector ' Gravity
  49.  
  50. ' Initialize
  51. ShapeCount = 0
  52. CollisionCount = 0
  53.  
  54. ' Prompt
  55. CALL cprintstring(16 * 4, "WELCOME!                  ")
  56. CALL cprintstring(16 * 3, "Press 1 for Pool Prototype")
  57. CALL cprintstring(16 * 2, "Press 2 for Wacky Game    ")
  58.  
  59.     kk = _KEYHIT
  60.     SELECT CASE kk
  61.         CASE ASC("1")
  62.             CALL SetupPool
  63.             EXIT DO
  64.         CASE ASC("2")
  65.             CALL SetupWackyGame
  66.             EXIT DO
  67.     END SELECT
  68.     IF (kk) THEN
  69.         _KEYCLEAR
  70.     END IF
  71.     _LIMIT 30
  72.  
  73. CALL Graphics
  74. CALL cprintstring(-16 * 3, "During Play:")
  75. CALL cprintstring(-16 * 5, "Arrow keys boost most recently-selected object.")
  76. CALL cprintstring(-16 * 6, "Mouse 1 moves most recently-selected object.   ")
  77. CALL cprintstring(-16 * 7, "ESC halts all motion.                         ")
  78. CALL cprintstring(-16 * 8, "Space enters creative mode.                   ")
  79. CALL cprintstring(-16 * 9, "Press 'r' to restart.                         ")
  80. CALL cprintstring(-16 * 11, "PRESS ANY KEY TO BEGIN.")
  81.  
  82. ' Main loop
  83.     IF (UserInput = -1) THEN GOTO start
  84.     CALL PairDynamics(CPC, FPC, RST)
  85.     CALL FleetDynamics(VD, SV)
  86.     CALL Graphics
  87.     _LIMIT 120
  88.  
  89.  
  90. FUNCTION UserInput
  91.     TheReturn = 0
  92.     ' Keyboard input
  93.     kk = _KEYHIT
  94.     SELECT CASE kk
  95.         CASE 32
  96.             DO: LOOP UNTIL _KEYHIT
  97.             WHILE _MOUSEINPUT: WEND
  98.             _KEYCLEAR
  99.             CALL cprintstring(16 * 17, "Drag mouse counter-clockwise to draw a new shape.")
  100.             CALL cprintstring(16 * 16, "Make sure centroid is inside body.")
  101.             'CALL Graphics
  102.             CALL NewMouseShape(7.5, 75, 25)
  103.             CLS
  104.         CASE 18432, ASC("w") ' Up arrow
  105.             Shape(SelectedShape).Velocity.y = Shape(SelectedShape).Velocity.y * 1.1 + 2
  106.         CASE 20480, ASC("s") ' Down arrow
  107.             Shape(SelectedShape).Velocity.y = Shape(SelectedShape).Velocity.y * 0.9 - 2
  108.         CASE 19200, ASC("a") ' Left arrow
  109.             Shape(SelectedShape).Velocity.x = Shape(SelectedShape).Velocity.x * 0.9 - 2
  110.         CASE 19712, ASC("d") ' Right arrow
  111.             Shape(SelectedShape).Velocity.x = Shape(SelectedShape).Velocity.x * 1.1 + 2
  112.         CASE ASC("r")
  113.             TheReturn = -1
  114.         CASE 27
  115.             FOR k = 1 TO ShapeCount
  116.                 Shape(k).Velocity.x = .000001 * (RND - .5)
  117.                 Shape(k).Velocity.y = .000001 * (RND - .5)
  118.                 Shape(k).Omega = .000001 * (RND - .5)
  119.             NEXT
  120.     END SELECT
  121.     IF (kk) THEN
  122.         _KEYCLEAR
  123.     END IF
  124.  
  125.     ' Mouse input
  126.     mb = 0
  127.         x = _MOUSEX
  128.         y = _MOUSEY
  129.         IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  130.             x = x - (_WIDTH / 2)
  131.             y = -y + (_HEIGHT / 2)
  132.  
  133.             rmin = 999999999
  134.             FOR k = 1 TO ShapeCount
  135.                 dx = x - Shape(k).Centroid.x
  136.                 dy = y - Shape(k).Centroid.y
  137.                 r2 = dx * dx + dy * dy
  138.                 IF (r2 < rmin) THEN
  139.                     rmin = r2
  140.                     SelectedShape = k
  141.                 END IF
  142.             NEXT
  143.             IF (_MOUSEBUTTON(1)) THEN
  144.                 IF (mb = 0) THEN
  145.                     mb = 1
  146.                     vtemp.x = x - Shape(SelectedShape).Centroid.x
  147.                     vtemp.y = y - Shape(SelectedShape).Centroid.y
  148.                     CALL TranslateShape(SelectedShape, vtemp)
  149.                     Shape(SelectedShape).Velocity.x = 0
  150.                     Shape(SelectedShape).Velocity.y = 0
  151.                     Shape(SelectedShape).Omega = 0
  152.                 END IF
  153.             END IF
  154.             IF (_MOUSEBUTTON(2)) THEN
  155.                 IF (mb = 0) THEN
  156.                     mb = 1
  157.                     CALL NewAutoBall(x, y, 5, 0, 1, 1, 1)
  158.                     _DELAY .1
  159.                 END IF
  160.             END IF
  161.         END IF
  162.     LOOP
  163.     UserInput = TheReturn
  164.  
  165. SUB PairDynamics (CoarseProximityConstant, FineProximityConstant, Restitution)
  166.  
  167.     ' Proximity detection
  168.     ProximalPairsCount = 0
  169.     Shape1 = 0
  170.     Shape2 = 0
  171.     FOR j = 1 TO ShapeCount
  172.         Shape(j).CollisionSwitch = 0
  173.         Shape(j).DeltaO = 0
  174.         Shape(j).DeltaV.x = 0
  175.         Shape(j).DeltaV.y = 0
  176.         Shape(j).PartialNormal.x = 0
  177.         Shape(j).PartialNormal.y = 0
  178.         FOR k = j + 1 TO ShapeCount
  179.             dx = Shape(j).Centroid.x - Shape(k).Centroid.x
  180.             dy = Shape(j).Centroid.y - Shape(k).Centroid.y
  181.             dr = SQR(dx * dx + dy * dy)
  182.             IF (dr < (CoarseProximityConstant) * (Shape(j).DiameterMax + Shape(k).DiameterMax)) THEN
  183.                 ProximalPairsCount = ProximalPairsCount + 1
  184.                 ProximalPairs(ProximalPairsCount, 1) = j
  185.                 ProximalPairs(ProximalPairsCount, 2) = k
  186.                 Shape1 = j
  187.                 Shape2 = k
  188.             END IF
  189.         NEXT
  190.     NEXT
  191.  
  192.     IF (ProximalPairsCount > 0) THEN
  193.         FOR n = 1 TO ProximalPairsCount
  194.             Shape1 = ProximalPairs(n, 1)
  195.             Shape2 = ProximalPairs(n, 2)
  196.  
  197.             ' Collision detection
  198.             rmin = 999999999
  199.             ClosestIndex1 = 0
  200.             ClosestIndex2 = 0
  201.             FOR j = 1 TO Shape(Shape1).Elements
  202.                 FOR k = 1 TO Shape(Shape2).Elements
  203.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  204.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  205.                     r2 = dx * dx + dy * dy
  206.  
  207.                     IF (r2 <= FineProximityConstant) THEN
  208.  
  209.                         ' Normal vector 1
  210.                         nx1 = CalculateNormalY(Shape1, j)
  211.                         ny1 = -CalculateNormalX(Shape1, j)
  212.                         nn = SQR(nx1 * nx1 + ny1 * ny1)
  213.                         nx1 = nx1 / nn
  214.                         ny1 = ny1 / nn
  215.                         Shape(Shape1).PartialNormal.x = Shape(Shape1).PartialNormal.x + nx1
  216.                         Shape(Shape1).PartialNormal.y = Shape(Shape1).PartialNormal.y + ny1
  217.  
  218.                         ' Normal vector 2
  219.                         nx2 = CalculateNormalY(Shape2, k)
  220.                         ny2 = -CalculateNormalX(Shape2, k)
  221.                         nn = SQR(nx2 * nx2 + ny2 * ny2)
  222.                         nx2 = nx2 / nn
  223.                         ny2 = ny2 / nn
  224.                         Shape(Shape2).PartialNormal.x = Shape(Shape2).PartialNormal.x + nx2
  225.                         Shape(Shape2).PartialNormal.y = Shape(Shape2).PartialNormal.y + ny2
  226.  
  227.                     END IF
  228.                     IF (r2 < rmin) THEN
  229.                         rmin = r2
  230.                         ClosestIndex1 = j
  231.                         ClosestIndex2 = k
  232.                     END IF
  233.                 NEXT
  234.             NEXT
  235.  
  236.             IF (rmin <= FineProximityConstant) THEN
  237.  
  238.                 CollisionCount = CollisionCount + 1
  239.                 Shape(Shape1).CollisionSwitch = 1
  240.                 Shape(Shape2).CollisionSwitch = 1
  241.  
  242.                 ' Undo previous motion
  243.                 IF (Shape(Shape1).Collisions = 0) THEN
  244.                     CALL RotShape(Shape1, Shape(Shape1).Centroid, -Shape(Shape1).Omega)
  245.                     vtemp.x = -(Shape(Shape1).Velocity.x)
  246.                     vtemp.y = -(Shape(Shape1).Velocity.y)
  247.                     CALL TranslateShape(Shape1, vtemp)
  248.                 END IF
  249.                 IF (Shape(Shape2).Collisions = 0) THEN
  250.                     CALL RotShape(Shape2, Shape(Shape2).Centroid, -Shape(Shape2).Omega)
  251.                     vtemp.x = -(Shape(Shape2).Velocity.x)
  252.                     vtemp.y = -(Shape(Shape2).Velocity.y)
  253.                     CALL TranslateShape(Shape2, vtemp)
  254.                 END IF
  255.  
  256.                 IF (Shape(Shape1).Collisions = 0) THEN
  257.                     Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * Restitution
  258.                     Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * Restitution
  259.                 END IF
  260.                 IF (Shape(Shape2).Collisions = 0) THEN
  261.                     Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * Restitution
  262.                     Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * Restitution
  263.                 END IF
  264.  
  265.                 ' Normal vector 1 (nx1, ny1)
  266.                 nn = SQR(Shape(Shape1).PartialNormal.x * Shape(Shape1).PartialNormal.x + Shape(Shape1).PartialNormal.y * Shape(Shape1).PartialNormal.y)
  267.                 nx1 = Shape(Shape1).PartialNormal.x / nn
  268.                 ny1 = Shape(Shape1).PartialNormal.y / nn
  269.                 Shape(Shape1).PartialNormal.x = nx1
  270.                 Shape(Shape1).PartialNormal.y = ny1
  271.  
  272.                 ' Normal vector 2 (nx2, ny2)
  273.                 nn = SQR(Shape(Shape2).PartialNormal.x * Shape(Shape2).PartialNormal.x + Shape(Shape2).PartialNormal.y * Shape(Shape2).PartialNormal.y)
  274.                 nx2 = Shape(Shape2).PartialNormal.x / nn
  275.                 ny2 = Shape(Shape2).PartialNormal.y / nn
  276.                 Shape(Shape2).PartialNormal.x = nx2
  277.                 Shape(Shape2).PartialNormal.y = ny2
  278.  
  279.                 ' Contact-centroid differentials 1 (dx1, dy1)
  280.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).Centroid.x
  281.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).Centroid.y
  282.  
  283.                 ' Contact-centroid differentials 2 (dx2, dy2)
  284.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).Centroid.x
  285.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).Centroid.y
  286.  
  287.                 ' Perpendicular vector 1 (px1, py1)
  288.                 px1 = -dy1
  289.                 py1 = dx1
  290.                 pp = SQR(px1 * px1 + py1 * py1)
  291.                 px1 = px1 / pp
  292.                 py1 = py1 / pp
  293.  
  294.                 ' Perpendicular vector 2 (px2, py2)
  295.                 px2 = -dy2
  296.                 py2 = dx2
  297.                 pp = SQR(px2 * px2 + py2 * py2)
  298.                 px2 = px2 / pp
  299.                 py2 = py2 / pp
  300.  
  301.                 ' Angular velocity vector 1 (w1, r1, vx1, vy1)
  302.                 w1 = Shape(Shape1).Omega
  303.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  304.                 vx1 = w1 * r1 * px1
  305.                 vy1 = w1 * r1 * py1
  306.  
  307.                 ' Angular velocity vector 2 (w2, r2, vx2, vy2)
  308.                 w2 = Shape(Shape2).Omega
  309.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  310.                 vx2 = w2 * r2 * px2
  311.                 vy2 = w2 * r2 * py2
  312.  
  313.                 ' Mass terms (m1, m2, mu)
  314.                 m1 = Shape(Shape1).Mass
  315.                 m2 = Shape(Shape2).Mass
  316.                 mu = 1 / (1 / m1 + 1 / m2)
  317.  
  318.                 ' Re-Calculate moment of inertia (i1, i2)
  319.                 vtemp.x = PointChain(Shape1, ClosestIndex1).x
  320.                 vtemp.y = PointChain(Shape1, ClosestIndex1).y
  321.                 CALL CalculateMOI(Shape1, vtemp)
  322.                 vtemp.x = PointChain(Shape2, ClosestIndex2).x
  323.                 vtemp.y = PointChain(Shape2, ClosestIndex2).y
  324.                 CALL CalculateMOI(Shape2, vtemp)
  325.                 i1 = Shape(Shape1).MOI
  326.                 i2 = Shape(Shape2).MOI
  327.  
  328.                 ' Velocity differentials (v1, v2, dvtx, dvty)
  329.                 vcx1 = Shape(Shape1).Velocity.x
  330.                 vcy1 = Shape(Shape1).Velocity.y
  331.                 vcx2 = Shape(Shape2).Velocity.x
  332.                 vcy2 = Shape(Shape2).Velocity.y
  333.                 vtx1 = vcx1 + vx1
  334.                 vty1 = vcy1 + vy1
  335.                 vtx2 = vcx2 + vx2
  336.                 vty2 = vcy2 + vy2
  337.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  338.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  339.                 dvtx = vtx2 - vtx1
  340.                 dvty = vty2 - vty1
  341.  
  342.                 ' Geometry (n1dotdvt, n2dotdvt)
  343.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  344.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  345.  
  346.                 ' Momentum exchange (qx1, qy1, qx2, qy2)
  347.                 qx1 = nx1 * 2 * mu * n1dotdvt
  348.                 qy1 = ny1 * 2 * mu * n1dotdvt
  349.                 qx2 = nx2 * 2 * mu * n2dotdvt
  350.                 qy2 = ny2 * 2 * mu * n2dotdvt
  351.  
  352.                 ' Momentum exchange unit vector
  353.                 qq = SQR(qx1 * qx1 + qy1 * qy1)
  354.                 qhatx1 = qx1 / qq
  355.                 qhaty1 = qy1 / qq
  356.                 qq = SQR(qx2 * qx2 + qy2 * qy2)
  357.                 qhatx2 = qx2 / qq
  358.                 qhaty2 = qy2 / qq
  359.  
  360.                 ' Translational impulse
  361.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  362.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  363.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  364.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  365.                 f1 = -q1dotn1 * n1dotr1hat
  366.                 f2 = -q2dotn2 * n2dotr2hat
  367.                 Shape(Shape1).DeltaV.x = Shape(Shape1).DeltaV.x + f1 * qx1 / m1
  368.                 Shape(Shape1).DeltaV.y = Shape(Shape1).DeltaV.y + f1 * qy1 / m1
  369.                 Shape(Shape2).DeltaV.x = Shape(Shape2).DeltaV.x + f2 * qx2 / m2
  370.                 Shape(Shape2).DeltaV.y = Shape(Shape2).DeltaV.y + f2 * qy2 / m2
  371.  
  372.                 ' Angular impulse
  373.                 q1dotp1 = qx1 * px1 + qy1 * py1
  374.                 q2dotp2 = qx2 * px2 + qy2 * py2
  375.                 dw1 = r1 * q1dotp1 / i1
  376.                 dw2 = -r2 * q2dotp2 / i2
  377.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO + dw1
  378.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO + dw2
  379.  
  380.                 ' Separate along normal
  381.                 'IF (Shape(Shape1).Collisions = 0) THEN
  382.                 vtemp.x = -nx1 * 1 * ((v1 + 0) / m1)
  383.                 vtemp.y = -ny1 * 1 * ((v1 + 0) / m1)
  384.                 CALL TranslateShape(Shape1, vtemp)
  385.                 'END IF
  386.                 'IF (Shape(Shape2).Collisions = 0) THEN
  387.                 vtemp.x = -nx2 * 1 * ((v2 + 0) / m2)
  388.                 vtemp.y = -ny2 * 1 * ((v2 + 0) / m2)
  389.                 CALL TranslateShape(Shape2, vtemp)
  390.                 'END IF
  391.  
  392.                 ' External torque
  393.                 'IF (Shape(Shape1).Collisions > 0) THEN
  394.                 torque1 = m1 * (dx1 * ForceField.y - dy1 * ForceField.x)
  395.                 Shape(Shape1).DeltaO = Shape(Shape1).DeltaO - torque1 / i1
  396.                 'END IF
  397.                 'IF (Shape(Shape2).Collisions > 0) THEN
  398.                 torque2 = m2 * (dx2 * ForceField.y - dy2 * ForceField.x)
  399.                 Shape(Shape2).DeltaO = Shape(Shape2).DeltaO - torque2 / i2
  400.                 'END IF
  401.  
  402.                 ' Feedback
  403.                 IF (Shape(Shape1).Collisions = 0) AND (Shape(Shape2).Collisions = 0) THEN
  404.                     CALL snd(100 * (v1 + v2) / 2, .5)
  405.                 END IF
  406.  
  407.             END IF
  408.         NEXT
  409.     END IF
  410.  
  411. SUB FleetDynamics (VelocityDamping, StoppingVelocity)
  412.     FOR ShapeIndex = 1 TO ShapeCount
  413.  
  414.         ' Contact update
  415.         IF (Shape(ShapeIndex).CollisionSwitch = 1) THEN
  416.             Shape(ShapeIndex).Collisions = Shape(ShapeIndex).Collisions + 1
  417.         ELSE
  418.             Shape(ShapeIndex).Collisions = 0
  419.         END IF
  420.  
  421.         IF ((Shape(ShapeIndex).Fixed = 0)) THEN
  422.  
  423.             ' Angular velocity update
  424.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + Shape(ShapeIndex).DeltaO
  425.  
  426.             ' Linear velocity update
  427.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Shape(ShapeIndex).DeltaV.x
  428.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Shape(ShapeIndex).DeltaV.y
  429.  
  430.             IF (Shape(ShapeIndex).Collisions = 0) THEN
  431.                 ' Freefall (if airborne)
  432.                 Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + ForceField.x
  433.                 Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + ForceField.y
  434.             ELSE
  435.                 ' Static friction
  436.                 IF (Shape(ShapeIndex).Velocity.x * Shape(ShapeIndex).Velocity.x) < StoppingVelocity THEN Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .05
  437.                 IF (Shape(ShapeIndex).Velocity.y * Shape(ShapeIndex).Velocity.y) < StoppingVelocity THEN Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .05
  438.                 IF (Shape(ShapeIndex).Omega * Shape(ShapeIndex).Omega) < .0001 * StoppingVelocity THEN Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .05
  439.             END IF
  440.  
  441.             ' Rotation update
  442.             CALL RotShape(ShapeIndex, Shape(ShapeIndex).Centroid, Shape(ShapeIndex).Omega)
  443.  
  444.             ' Position update
  445.             CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  446.  
  447.             ' Velocity Damping
  448.             Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * VelocityDamping
  449.             Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * VelocityDamping
  450.             Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * VelocityDamping
  451.         END IF
  452.     NEXT
  453.  
  454. SUB Graphics
  455.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  456.     'LOCATE 1, 1: PRINT ProximalPairsCount, CollisionCount
  457.     FOR ShapeIndex = 1 TO ShapeCount
  458.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  459.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  460.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  461.         NEXT
  462.         CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  463.         CALL cline(PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, Shape(ShapeIndex).Shade)
  464.         IF (ShapeIndex = SelectedShape) THEN
  465.             CALL cpaint(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, Shape(ShapeIndex).Shade, Shape(ShapeIndex).Shade)
  466.         END IF
  467.         CALL ccircle(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, 3, _RGB(0, 0, 0))
  468.         CALL cpaint(Shape(ShapeIndex).Centroid.x, Shape(ShapeIndex).Centroid.y, _RGB(0, 0, 0), _RGB(0, 0, 0))
  469.     NEXT
  470.     _DISPLAY
  471.  
  472. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  473.     DIM l AS Vector
  474.     DIM r AS Vector
  475.     li = i - 1
  476.     ri = i + 1
  477.     IF (i = 1) THEN li = Shape(k).Elements
  478.     IF (i = Shape(k).Elements) THEN ri = 1
  479.     l.x = PointChain(k, li).x
  480.     r.x = PointChain(k, ri).x
  481.     dx = r.x - l.x
  482.     CalculateNormalX = dx
  483.  
  484. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  485.     DIM l AS Vector
  486.     DIM r AS Vector
  487.     li = i - 1
  488.     ri = i + 1
  489.     IF (i = 1) THEN li = Shape(k).Elements
  490.     IF (i = Shape(k).Elements) THEN ri = 1
  491.     l.y = PointChain(k, li).y
  492.     r.y = PointChain(k, ri).y
  493.     dy = r.y - l.y
  494.     CalculateNormalY = dy
  495.  
  496. SUB CalculateCentroid (k AS INTEGER)
  497.     xx = 0
  498.     yy = 0
  499.     FOR i = 1 TO Shape(k).Elements
  500.         xx = xx + PointChain(k, i).x
  501.         yy = yy + PointChain(k, i).y
  502.     NEXT
  503.     Shape(k).Centroid.x = xx / Shape(k).Elements
  504.     Shape(k).Centroid.y = yy / Shape(k).Elements
  505.  
  506. SUB CalculateDiameterMin (k AS INTEGER)
  507.     r2min = 999 ^ 999
  508.     FOR i = 1 TO Shape(k).Elements
  509.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  510.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  511.         r2 = xx * xx + yy * yy
  512.         IF (r2 < r2min) THEN
  513.             r2min = r2
  514.         END IF
  515.     NEXT
  516.     Shape(k).DiameterMin = SQR(r2min)
  517.  
  518. SUB CalculateDiameterMax (k AS INTEGER)
  519.     r2max = -1
  520.     FOR i = 1 TO Shape(k).Elements
  521.         xx = Shape(k).Centroid.x - PointChain(k, i).x
  522.         yy = Shape(k).Centroid.y - PointChain(k, i).y
  523.         r2 = xx * xx + yy * yy
  524.         IF (r2 > r2max) THEN
  525.             r2max = r2
  526.         END IF
  527.     NEXT
  528.     Shape(k).DiameterMax = SQR(r2max)
  529.  
  530. SUB CalculateMass (k AS INTEGER, factor AS DOUBLE)
  531.     aa = 0
  532.     FOR i = 2 TO Shape(k).Elements
  533.         x = PointChain(k, i).x - Shape(k).Centroid.x
  534.         y = PointChain(k, i).y - Shape(k).Centroid.y
  535.         dx = (PointChain(k, i).x - PointChain(k, i - 1).x)
  536.         dy = (PointChain(k, i).y - PointChain(k, i - 1).y)
  537.         da = .5 * (x * dy - y * dx)
  538.         aa = aa + da
  539.     NEXT
  540.     Shape(k).Mass = factor * SQR(aa * aa)
  541.  
  542. SUB CalculateMOI (k AS INTEGER, ctrvec AS Vector)
  543.     xx = 0
  544.     yy = 0
  545.     FOR i = 1 TO Shape(k).Elements
  546.         a = ctrvec.x - PointChain(k, i).x
  547.         b = ctrvec.y - PointChain(k, i).y
  548.         xx = xx + a * a
  549.         yy = yy + b * b
  550.     NEXT
  551.     Shape(k).MOI = SQR((xx + yy) * (xx + yy)) * (Shape(k).Mass / Shape(k).Elements)
  552.  
  553. SUB TranslateShape (k AS INTEGER, c AS Vector)
  554.     FOR i = 1 TO Shape(k).Elements
  555.         PointChain(k, i).x = PointChain(k, i).x + c.x
  556.         PointChain(k, i).y = PointChain(k, i).y + c.y
  557.     NEXT
  558.     Shape(k).Centroid.x = Shape(k).Centroid.x + c.x
  559.     Shape(k).Centroid.y = Shape(k).Centroid.y + c.y
  560.  
  561. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  562.     FOR i = 1 TO Shape(k).Elements
  563.         xx = PointChain(k, i).x - c.x
  564.         yy = PointChain(k, i).y - c.y
  565.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  566.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  567.     NEXT
  568.     xx = Shape(k).Centroid.x - c.x
  569.     yy = Shape(k).Centroid.y - c.y
  570.     Shape(k).Centroid.x = c.x + xx * COS(da) - yy * SIN(da)
  571.     Shape(k).Centroid.y = c.y + yy * COS(da) + xx * SIN(da)
  572.  
  573. SUB NewAutoBall (x1, y1, r1, r2, pa, pb, fx)
  574.     ShapeCount = ShapeCount + 1
  575.     Shape(ShapeCount).Fixed = fx
  576.     Shape(ShapeCount).Collisions = 0
  577.     i = 0
  578.     FOR j = 0 TO 2 * 4 * ATN(1) STEP .15
  579.         i = i + 1
  580.         r = r1 + r2 * COS(pa * j) ^ pb
  581.         PointChain(ShapeCount, i).x = x1 + r * COS(j)
  582.         PointChain(ShapeCount, i).y = y1 + r * SIN(j)
  583.     NEXT
  584.     Shape(ShapeCount).Elements = i
  585.     CALL CalculateCentroid(ShapeCount)
  586.     IF (fx = 0) THEN
  587.         CALL CalculateMass(ShapeCount, 1)
  588.     ELSE
  589.         CALL CalculateMass(ShapeCount, 999999)
  590.     END IF
  591.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  592.     CALL CalculateDiameterMin(ShapeCount)
  593.     CALL CalculateDiameterMax(ShapeCount)
  594.     Shape(ShapeCount).Velocity.x = 0
  595.     Shape(ShapeCount).Velocity.y = 0
  596.     Shape(ShapeCount).Omega = 0
  597.     IF (fx = 0) THEN
  598.         Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  599.     ELSE
  600.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  601.     END IF
  602.     SelectedShape = ShapeCount
  603.  
  604. SUB NewAutoBrick (x1, y1, wx, wy, ang)
  605.     ShapeCount = ShapeCount + 1
  606.     Shape(ShapeCount).Fixed = 1
  607.     Shape(ShapeCount).Collisions = 0
  608.     i = 0
  609.     FOR j = -wy / 2 TO wy / 2 STEP 5
  610.         i = i + 1
  611.         PointChain(ShapeCount, i).x = x1 + wx / 2
  612.         PointChain(ShapeCount, i).y = y1 + j
  613.     NEXT
  614.     FOR j = wx / 2 TO -wx / 2 STEP -5
  615.         i = i + 1
  616.         PointChain(ShapeCount, i).x = x1 + j
  617.         PointChain(ShapeCount, i).y = y1 + wy / 2
  618.     NEXT
  619.     FOR j = wy / 2 TO -wy / 2 STEP -5
  620.         i = i + 1
  621.         PointChain(ShapeCount, i).x = x1 - wx / 2
  622.         PointChain(ShapeCount, i).y = y1 + j
  623.     NEXT
  624.     FOR j = -wx / 2 TO wx / 2 STEP 5
  625.         i = i + 1
  626.         PointChain(ShapeCount, i).x = x1 + j
  627.         PointChain(ShapeCount, i).y = y1 - wy / 2
  628.     NEXT
  629.     Shape(ShapeCount).Elements = i
  630.     CALL CalculateCentroid(ShapeCount)
  631.     CALL CalculateMass(ShapeCount, 99999)
  632.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  633.     CALL CalculateDiameterMin(ShapeCount)
  634.     CALL CalculateDiameterMax(ShapeCount)
  635.     Shape(ShapeCount).MOI = 999 ^ 999
  636.     Shape(ShapeCount).Velocity.x = 0
  637.     Shape(ShapeCount).Velocity.y = 0
  638.     Shape(ShapeCount).Omega = 0
  639.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  640.     SelectedShape = ShapeCount
  641.     CALL RotShape(ShapeCount, Shape(ShapeCount).Centroid, ang)
  642.  
  643. SUB NewBrickLine (xi, yi, xf, yf, wx, wy)
  644.     d1 = SQR((xf - xi) ^ 2 + (yf - yi) ^ 2)
  645.     d2 = SQR(wx ^ 2 + wy ^ 2)
  646.     ang = ATN((yf - yi) / (xf - xi))
  647.     f = 1.2 * d2 / d1
  648.     FOR t = 0 TO 1 + f STEP f
  649.         CALL NewAutoBrick(xi * (1 - t) + xf * t, yi * (1 - t) + yf * t, wx, wy, ang)
  650.     NEXT
  651.  
  652. SUB NewMouseShape (rawresolution AS DOUBLE, targetpoints AS INTEGER, smoothiterations AS INTEGER)
  653.  
  654.     ShapeCount = ShapeCount + 1
  655.     Shape(ShapeCount).Fixed = 0
  656.     Shape(ShapeCount).Collisions = 0
  657.  
  658.     numpoints = 0
  659.  
  660.     xold = 999999
  661.     yold = 999999
  662.     DO
  663.         DO WHILE _MOUSEINPUT
  664.             x = _MOUSEX
  665.             y = _MOUSEY
  666.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  667.                 IF _MOUSEBUTTON(1) THEN
  668.                     x = x - (_WIDTH / 2)
  669.                     y = -y + (_HEIGHT / 2)
  670.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  671.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  672.                         numpoints = numpoints + 1
  673.                         PointChain(ShapeCount, numpoints).x = x
  674.                         PointChain(ShapeCount, numpoints).y = y
  675.                         CALL cpset(x, y, _RGB(0, 255, 255))
  676.                         xold = x
  677.                         yold = y
  678.                     END IF
  679.                 END IF
  680.             END IF
  681.         LOOP
  682.         _DISPLAY
  683.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  684.  
  685.     DO WHILE (numpoints < targetpoints)
  686.         rad2max = -1
  687.         kmax = -1
  688.         FOR k = 1 TO numpoints - 1
  689.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  690.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  691.             rad2 = xfac ^ 2 + yfac ^ 2
  692.             IF rad2 > rad2max THEN
  693.                 kmax = k
  694.                 rad2max = rad2
  695.             END IF
  696.         NEXT
  697.         edgecase = 0
  698.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  699.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  700.         rad2 = xfac ^ 2 + yfac ^ 2
  701.         IF (rad2 > rad2max) THEN
  702.             kmax = numpoints
  703.             rad2max = rad2
  704.             edgecase = 1
  705.         END IF
  706.         IF (edgecase = 0) THEN
  707.             numpoints = numpoints + 1
  708.             FOR j = numpoints TO kmax + 1 STEP -1
  709.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  710.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  711.             NEXT
  712.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  713.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  714.         ELSE
  715.             numpoints = numpoints + 1
  716.             xx = PointChain(ShapeCount, numpoints - 1).x
  717.             yy = PointChain(ShapeCount, numpoints - 1).y
  718.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  719.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  720.         END IF
  721.     LOOP
  722.  
  723.     FOR j = 1 TO smoothiterations
  724.         FOR k = 2 TO n - 1
  725.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  726.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  727.         NEXT
  728.         FOR k = 2 TO n - 1
  729.             PointChain(i, k).x = TempChain(i, k).x
  730.             PointChain(i, k).y = TempChain(i, k).y
  731.         NEXT
  732.     NEXT
  733.  
  734.     Shape(ShapeCount).Elements = numpoints
  735.     CALL CalculateCentroid(ShapeCount)
  736.     CALL CalculateMass(ShapeCount, 1)
  737.     CALL CalculateMOI(ShapeCount, Shape(ShapeCount).Centroid)
  738.     CALL CalculateDiameterMin(ShapeCount)
  739.     CALL CalculateDiameterMax(ShapeCount)
  740.     Shape(ShapeCount).Velocity.x = 0
  741.     Shape(ShapeCount).Velocity.y = 0
  742.     Shape(ShapeCount).Omega = 0
  743.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  744.     SelectedShape = ShapeCount
  745.  
  746. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  747.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  748.  
  749. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  750.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  751.  
  752. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  753.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  754.  
  755. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  756.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  757.  
  758. SUB cprintstring (y, a AS STRING)
  759.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) * 8) / 2, -y + _HEIGHT / 2), a
  760.  
  761. SUB snd (frq, dur)
  762.     IF ((frq >= 37) AND (frq <= 2000)) THEN
  763.         SOUND frq, dur
  764.     END IF
  765.  
  766. SUB SetupPool
  767.  
  768.     ' Set external field
  769.     ForceField.x = 0
  770.     ForceField.y = 0 '-.08
  771.  
  772.     ' Rectangular border
  773.     wx = 42
  774.     wy = 10
  775.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  776.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  777.     wx = 40
  778.     wy = 10
  779.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  780.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  781.  
  782.     ' Balls (billiard setup)
  783.     x0 = 160
  784.     y0 = 0
  785.     r = 15
  786.     gg = 2 * r + 3.5
  787.     xf = COS(30 * 3.14159 / 180)
  788.     yf = SIN(30 * 3.14159 / 180)
  789.     gx = gg * xf
  790.     gy = gg * yf
  791.  
  792.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  793.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  794.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  795.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r, 0, 1, 1, 0)
  796.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r, 0, 1, 1, 0)
  797.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r, 0, 1, 1, 0)
  798.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r, 0, 1, 1, 0)
  799.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r, 0, 1, 1, 0)
  800.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r, 0, 1, 1, 0)
  801.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r, 0, 1, 1, 0)
  802.  
  803.     ' Cue ball
  804.     CALL NewAutoBall(-220, 0, r, 0, 1, 1, 0)
  805.     Shape(ShapeCount).Velocity.x = 8 + 5 * RND
  806.     Shape(ShapeCount).Velocity.y = .5 * (RND - .5)
  807.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  808.  
  809.     ' Parameters
  810.     CPC = 1.15
  811.     FPC = 8
  812.     RST = 0.75
  813.     VD = 0.995
  814.     SV = 0
  815.  
  816. SUB SetupWackyGame
  817.     ' Set external field
  818.     ForceField.x = 0
  819.     ForceField.y = -.08
  820.  
  821.     ' Rectangular border
  822.     wx = 42
  823.     wy = 10
  824.     CALL NewBrickLine(-_WIDTH / 2 + wx, _HEIGHT / 2 - wy, _WIDTH / 2 - wx, _HEIGHT / 2 - wy, wx, wy)
  825.     CALL NewBrickLine(-_WIDTH / 2 + wx, -_HEIGHT / 2 + wy, _WIDTH / 2 - wx, -_HEIGHT / 2 + wy, wx, wy)
  826.     wx = 40
  827.     wy = 10
  828.     CALL NewBrickLine(-_WIDTH / 2 + wy, -_HEIGHT / 2 + 2 * wx, -_WIDTH / 2 + wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  829.     CALL NewBrickLine(_WIDTH / 2 - wy, -_HEIGHT / 2 + 2 * wx, _WIDTH / 2 - wy, _HEIGHT / 2 - 2 * wx, wx, wy)
  830.  
  831.     ' Balls
  832.     x0 = -70
  833.     y0 = 120
  834.     r1 = 15
  835.     r2 = 2.5
  836.     gg = 2 * (r1 + r2) + 3.5
  837.     xf = COS(30 * 3.14159 / 180)
  838.     yf = SIN(30 * 3.14159 / 180)
  839.     gx = gg * xf
  840.     gy = gg * yf
  841.     CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  842.     CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  843.     CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  844.     CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  845.     CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  846.     CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  847.     CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  848.     CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  849.     CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  850.     CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, r1, r2, INT(RND * 3) + 1, INT(RND * 1) + 2, 0)
  851.  
  852.     ' Misc. bricks
  853.     wx = 60
  854.     wy = 10
  855.     ww = SQR(wx * wx + wy * wy) * .75
  856.     CALL NewBrickLine(ww, 0, 100 + ww, 100, wx, wy)
  857.     CALL NewBrickLine(-ww, 0, -100 - ww, 100, wx, wy)
  858.  
  859.     ' Fidget spinner
  860.     CALL NewAutoBall(-220, 0, 20, 15, 1.5, 2, 0)
  861.     Shape(ShapeCount).Shade = _RGB(255, 255, 255)
  862.  
  863.     ' Parameters
  864.     CPC = 1.15
  865.     FPC = 8
  866.     RST = 0.75
  867.     VD = 0.995
  868.     SV = 0.025
  869.  
You're not done when it works, you're done when it's right.

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Wow @STxAxTIC Now this is something.. :O
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Marked as best answer by STxAxTIC on March 05, 2020, 03:13:04 pm

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Discussion on this project is continued elsewhere:

https://www.qb64.org/forum/index.php?topic=2295
« Last Edit: March 05, 2020, 08:11:01 pm by STxAxTIC »
You're not done when it works, you're done when it's right.