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

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
As the title suggests, this is a generalized physics engine prototype with arbitrary shapes and collisions.

So what does that mean?

1) It's a prototype. If it does something you think is weird, this is still a baby, don't worry. It looks coarse-grained for transparency - when I make all the dots closer to each other, it will be all the smoother looking, and smoother-behaving.

2) The objects in this little 2D universe can have any shape (even open shapes), any mass, rotate about any axis, translate in any direction.

3) The objects collide and properly transfer linear momentum and angular momentum.

4) By "unified" I mean this... most simulations take place in a box, right? Reflect velocity at the wall right? Well, same here, but that's not hard-coded. Instead, the walls are made of the SAME stuff that the objects are made of, I just gave them near-infinite mass so they don't move. This part was worked on the least, so beware the walls (see part 1).

5) If you look at the code, there is a real lot of mathematical mechanism going on, obviously, but I point this out: there are a minimum of nested loops, not a lot of microscopic scanning around for edges, ABSOLUTELY NO TRIG (when it comes to collision mathematics). On that a similar note regarding speed, I point out that this is already passing the large-N test, as the walls are also participating in the loop, so to speak. Doesn't need to stay this way, it's a flex for now.

6) This also supports forces, torques, impulses, all the physics stuff, mostly tested. As a corollary to all this, and the reason I even know it, is during testing I set up a bunch of familiar configurations: pool, newton's cradle, crossfire (kinda) - it all works.

This demo just shows you oddly-shaped pieces crashing into each other while subject to a central "spring" field. Like I said, if you lose a piece, just restart. A few magic numbers will change as I work on this.

SEE BEST ANSWER POST FOR CURRENT CODE

Discussion on this project is continued elsewhere:

https://www.qb64.org/forum/index.php?topic=2295
Untitled.png
* Untitled.png (Filesize: 12.85 KB, Dimensions: 802x600, Views: 244)
« Last Edit: March 05, 2020, 08:13:44 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 #1 on: February 20, 2020, 03:37:12 am »
I wish I had the chops to develop something like this. Thank you for taking the time to create this. Incorporating actual physics into games I create would be great.
In order to understand recursion, one must first understand recursion.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #2 on: February 20, 2020, 05:00:50 am »
As the title suggests, this is a generalized physics engine prototype with arbitrary shapes and collisions.

2) The objects in this little 2D universe ...

2D?  This seems an unnecessary restriction?  Physical things happen in the 3D world (I suppose that to a mathematician even 3D is an unnecessary restriction!), and _MAPTRIANGLE(3D) - not even OpenGL required - would handle that superbly.  This could be your next iteration?

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #3 on: February 20, 2020, 12:58:48 pm »
Hi Qwerkey,

So going from 2d to 3d is ordinarily a giant pain in the ass when everything is expressed by trig, but with vectors this wouldnt be such a hard leap. If I go into the next dimension with this it wont be for a long time, there is plenty to flesh out in 2d first.

As for plotting the result, whenever it comes, I will be not using maptriangle, I like line and pset.
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
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #4 on: February 22, 2020, 03:00:42 am »
New Update / Milestone / Example:

Still playing with magic numbers, but I thought I'd share what happens when pool, air hokey, and crossfire all meet on the street. PRESS SPACE ONCE.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(800, 600, 32)
  2. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  3.  
  4.  
  5. TYPE Vector
  6.     x AS DOUBLE
  7.     y AS DOUBLE
  8.  
  9. TYPE Object
  10.     CtrMass AS Vector
  11.     CtrRot AS Vector
  12.     Diameter AS DOUBLE
  13.     Mass AS DOUBLE
  14.     MOI AS DOUBLE
  15.     Length AS INTEGER
  16.     Omega AS DOUBLE
  17.     Shade AS _UNSIGNED LONG
  18.     Velocity AS Vector
  19.  
  20. DIM vtemp AS Vector
  21.  
  22. DIM SHARED Shape(1000) AS Object
  23. DIM SHARED PointChain(1000, 5000) AS Vector
  24. DIM SHARED ShapeCount AS INTEGER
  25. DIM SHARED CollisionCount AS INTEGER
  26. ShapeCount = 0
  27. CollisionCount = 0
  28.  
  29. DIM Gravity AS Vector
  30. Gravity.x = 0
  31. Gravity.y = -30
  32.  
  33. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  34. DIM SHARED ProximalPairsCount
  35.  
  36. ShapeCount = ShapeCount + 1
  37. Shape(ShapeCount).CtrRot.x = -220
  38. Shape(ShapeCount).CtrRot.y = 0
  39. i = 0
  40. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  41.     i = i + 1
  42.     r = 40 + 25 * COS(1.5 * j) ^ 2
  43.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  44.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  45. Shape(ShapeCount).Length = i
  46. Shape(ShapeCount).Mass = i
  47. Shape(ShapeCount).Velocity.x = 0
  48. Shape(ShapeCount).Velocity.y = 0
  49. Shape(ShapeCount).Omega = 0
  50. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  51. CALL CalculateCtrMass(ShapeCount)
  52. CALL CalculateDiameter(ShapeCount)
  53. CALL CalculateMOI(ShapeCount)
  54.  
  55. ShapeCount = ShapeCount + 1
  56. Shape(ShapeCount).CtrRot.x = 170
  57. Shape(ShapeCount).CtrRot.y = 0
  58. i = 0
  59. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  60.     i = i + 1
  61.     r = 30
  62.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  63.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  64. Shape(ShapeCount).Length = i
  65. Shape(ShapeCount).Mass = i
  66. Shape(ShapeCount).Velocity.x = 0
  67. Shape(ShapeCount).Velocity.y = 0
  68. Shape(ShapeCount).Omega = 0
  69. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  70. CALL CalculateCtrMass(ShapeCount)
  71. CALL CalculateDiameter(ShapeCount)
  72. CALL CalculateMOI(ShapeCount)
  73.  
  74. ShapeCount = ShapeCount + 1
  75. Shape(ShapeCount).CtrRot.x = 170 + 54
  76. Shape(ShapeCount).CtrRot.y = -31.25
  77. i = 0
  78. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  79.     i = i + 1
  80.     r = 30
  81.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  82.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  83. Shape(ShapeCount).Length = i
  84. Shape(ShapeCount).Mass = i
  85. Shape(ShapeCount).Velocity.x = 0
  86. Shape(ShapeCount).Velocity.y = 0
  87. Shape(ShapeCount).Omega = 0
  88. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  89. CALL CalculateCtrMass(ShapeCount)
  90. CALL CalculateDiameter(ShapeCount)
  91. CALL CalculateMOI(ShapeCount)
  92.  
  93. ShapeCount = ShapeCount + 1
  94. Shape(ShapeCount).CtrRot.x = 170 + 54
  95. Shape(ShapeCount).CtrRot.y = 31.25
  96. i = 0
  97. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  98.     i = i + 1
  99.     r = 30
  100.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  101.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  102. Shape(ShapeCount).Length = i
  103. Shape(ShapeCount).Mass = i
  104. Shape(ShapeCount).Velocity.x = 0
  105. Shape(ShapeCount).Velocity.y = 0
  106. Shape(ShapeCount).Omega = 0
  107. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  108. CALL CalculateCtrMass(ShapeCount)
  109. CALL CalculateDiameter(ShapeCount)
  110. CALL CalculateMOI(ShapeCount)
  111.  
  112. ShapeCount = ShapeCount + 1
  113. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  114. Shape(ShapeCount).CtrRot.y = 0
  115. i = 0
  116. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  117.     i = i + 1
  118.     r = 30
  119.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  120.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  121. Shape(ShapeCount).Length = i
  122. Shape(ShapeCount).Mass = i
  123. Shape(ShapeCount).Velocity.x = 0
  124. Shape(ShapeCount).Velocity.y = 0
  125. Shape(ShapeCount).Omega = 0
  126. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  127. CALL CalculateCtrMass(ShapeCount)
  128. CALL CalculateDiameter(ShapeCount)
  129. CALL CalculateMOI(ShapeCount)
  130.  
  131. ShapeCount = ShapeCount + 1
  132. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  133. Shape(ShapeCount).CtrRot.y = -31.25 * 2
  134. i = 0
  135. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  136.     i = i + 1
  137.     r = 30
  138.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  139.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  140. Shape(ShapeCount).Length = i
  141. Shape(ShapeCount).Mass = i
  142. Shape(ShapeCount).Velocity.x = 0
  143. Shape(ShapeCount).Velocity.y = 0
  144. Shape(ShapeCount).Omega = 0
  145. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  146. CALL CalculateCtrMass(ShapeCount)
  147. CALL CalculateDiameter(ShapeCount)
  148. CALL CalculateMOI(ShapeCount)
  149.  
  150. ShapeCount = ShapeCount + 1
  151. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  152. Shape(ShapeCount).CtrRot.y = 31.25 * 2
  153. i = 0
  154. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  155.     i = i + 1
  156.     r = 30
  157.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  158.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  159. Shape(ShapeCount).Length = i
  160. Shape(ShapeCount).Mass = i
  161. Shape(ShapeCount).Velocity.x = 0
  162. Shape(ShapeCount).Velocity.y = 0
  163. Shape(ShapeCount).Omega = 0
  164. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  165. CALL CalculateCtrMass(ShapeCount)
  166. CALL CalculateDiameter(ShapeCount)
  167. CALL CalculateMOI(ShapeCount)
  168.  
  169.  
  170. 'ShapeCount = ShapeCount + 1
  171. 'Shape(ShapeCount).CtrRot.x = -200
  172. 'Shape(ShapeCount).CtrRot.y = 0
  173. 'i = 0
  174. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  175. '    i = i + 1
  176. '    r = 40 + 25 * COS(j) ^ 2
  177. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  178. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  179. 'NEXT
  180. 'Shape(ShapeCount).Length = i
  181. 'Shape(ShapeCount).Mass = i
  182. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  183. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  184. 'Shape(ShapeCount).Omega = 0
  185. 'Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  186. 'CALL CalculateCtrMass(ShapeCount)
  187. 'CALL CalculateDiameter(ShapeCount)
  188. 'CALL CalculateMOI(ShapeCount)
  189.  
  190. 'ShapeCount = ShapeCount + 1
  191. 'Shape(ShapeCount).CtrRot.x = 0
  192. 'Shape(ShapeCount).CtrRot.y = 0
  193. 'i = 0
  194. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  195. '    i = i + 1
  196. '    r = 40 + 25 * COS(1.5 * j) ^ 2
  197. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  198. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  199. 'NEXT
  200. 'Shape(ShapeCount).Length = i
  201. 'Shape(ShapeCount).Mass = i
  202. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  203. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  204. 'Shape(ShapeCount).Omega = 0
  205. 'Shape(ShapeCount).Shade = _RGB(255, 0, 255)
  206. 'CALL CalculateCtrMass(ShapeCount)
  207. 'CALL CalculateDiameter(ShapeCount)
  208. 'CALL CalculateMOI(ShapeCount)
  209.  
  210. 'ShapeCount = ShapeCount + 1
  211. 'Shape(ShapeCount).CtrRot.x = 200
  212. 'Shape(ShapeCount).CtrRot.y = 0
  213. 'i = 0
  214. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  215. '    i = i + 1
  216. '    r = 50
  217. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  218. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  219. 'NEXT
  220. 'Shape(ShapeCount).Length = i
  221. 'Shape(ShapeCount).Mass = i
  222. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  223. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  224. 'Shape(ShapeCount).Omega = 0
  225. 'Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  226. 'CALL CalculateCtrMass(ShapeCount)
  227. 'CALL CalculateDiameter(ShapeCount)
  228. 'CALL CalculateMOI(ShapeCount)
  229.  
  230. 'w = _WIDTH / 2
  231. 'h = _HEIGHT / 2
  232. 'b = 20
  233. 'FOR n = -1 TO 1 STEP 2
  234.  
  235. '    FOR k = (-w + 2 * 5) TO (w - 2 * 5) STEP b
  236. '        ShapeCount = ShapeCount + 1
  237. '        Shape(ShapeCount).CtrRot.x = x
  238. '        Shape(ShapeCount).CtrRot.y = n * h - n * 5
  239. '        i = 0
  240. '        FOR j = (Shape(ShapeCount).CtrRot.x - b / 2) TO (Shape(ShapeCount).CtrRot.x + b / 2) STEP 5
  241. '            i = i + 1
  242. '            PointChain(ShapeCount, i).x = j
  243. '            PointChain(ShapeCount, i).y = n * h - n * 5
  244. '        NEXT
  245. '        Shape(ShapeCount).Length = i
  246. '        Shape(ShapeCount).Mass = 999 ^ 999
  247. '        Shape(ShapeCount).Velocity.x = 0
  248. '        Shape(ShapeCount).Velocity.y = 0
  249. '        Shape(ShapeCount).Omega = 0
  250. '        Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  251. '        CALL CalculateCtrMass(ShapeCount)
  252. '        CALL CalculateDiameter(ShapeCount)
  253. '        CALL CalculateMOI(ShapeCount)
  254. '    NEXT
  255.  
  256. '    'ShapeCount = ShapeCount + 1
  257. '    'Shape(ShapeCount).CtrRot.x = n * w - n * 5
  258. '    'Shape(ShapeCount).CtrRot.y = 0
  259. '    'i = 0
  260. '    'FOR j = (-h + 2 * 5) TO (h - 2 * 5) STEP 3
  261. '    '    i = i + 1
  262. '    '    PointChain(ShapeCount, i).x = n * w - n * 5
  263. '    '    PointChain(ShapeCount, i).y = j
  264. '    'NEXT
  265. '    'Shape(ShapeCount).Length = i
  266. '    'Shape(ShapeCount).Mass = 999 ^ 999
  267. '    'Shape(ShapeCount).Velocity.x = 0
  268. '    'Shape(ShapeCount).Velocity.y = 0
  269. '    'Shape(ShapeCount).Omega = 0
  270. '    'Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  271. '    'CALL CalculateCtrMass(ShapeCount)
  272. '    ''CALL CalculateDiameter(ShapeCount)
  273. '    'Shape(ShapeCount).Diameter = 5
  274. '    'CALL CalculateMOI(ShapeCount)
  275. 'NEXT
  276.  
  277.  
  278.  
  279. ' Walls
  280. b = 10
  281. FOR n = -1 TO 1 STEP 2
  282.     FOR k = -(_WIDTH / 2 - b) TO (_WIDTH / 2 - b) STEP 2 * b
  283.         ShapeCount = ShapeCount + 1
  284.         Shape(ShapeCount).CtrRot.x = k
  285.         Shape(ShapeCount).CtrRot.y = n * (_HEIGHT / 2 - b / 2)
  286.         i = 0
  287.         FOR j = (Shape(ShapeCount).CtrRot.x - b / 2) TO (Shape(ShapeCount).CtrRot.x + b / 2) STEP 3
  288.             i = i + 1
  289.             PointChain(ShapeCount, i).x = j
  290.             PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y
  291.         NEXT
  292.         Shape(ShapeCount).Length = i
  293.         Shape(ShapeCount).Mass = 999 ^ 999
  294.         Shape(ShapeCount).Velocity.x = 0
  295.         Shape(ShapeCount).Velocity.y = 0
  296.         Shape(ShapeCount).Omega = 0
  297.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  298.         CALL CalculateCtrMass(ShapeCount)
  299.         CALL CalculateDiameter(ShapeCount)
  300.         CALL CalculateMOI(ShapeCount)
  301.         Shape(ShapeCount).MOI = 999 ^ 999
  302.     NEXT
  303.  
  304.     FOR k = -(_HEIGHT / 2 - b) TO (_HEIGHT / 2 - b) STEP 2 * b
  305.         ShapeCount = ShapeCount + 1
  306.         Shape(ShapeCount).CtrRot.x = n * (_WIDTH / 2 - b / 2)
  307.         Shape(ShapeCount).CtrRot.y = k
  308.         i = 0
  309.         FOR j = (Shape(ShapeCount).CtrRot.y - b / 2) TO (Shape(ShapeCount).CtrRot.y + b / 2) STEP 3
  310.             i = i + 1
  311.             PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x
  312.             PointChain(ShapeCount, i).y = j
  313.         NEXT
  314.         Shape(ShapeCount).Length = i
  315.         Shape(ShapeCount).Mass = 999 ^ 999
  316.         Shape(ShapeCount).Velocity.x = 0
  317.         Shape(ShapeCount).Velocity.y = 0
  318.         Shape(ShapeCount).Omega = 0
  319.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  320.         CALL CalculateCtrMass(ShapeCount)
  321.         CALL CalculateDiameter(ShapeCount)
  322.         CALL CalculateMOI(ShapeCount)
  323.         Shape(ShapeCount).MOI = 999 ^ 999
  324.     NEXT
  325.  
  326.  
  327.     'DO WHILE _MOUSEINPUT
  328.     '    x = _MOUSEX
  329.     '    y = _MOUSEY
  330.     '    IF ((x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT)) THEN
  331.     '        IF (_MOUSEBUTTON(1)) THEN
  332.     '            x = _MOUSEX - _WIDTH / 2
  333.     '            y = -_MOUSEY + _HEIGHT / 2
  334.     '            Shape(1).CtrRot.x = x
  335.     '            Shape(1).CtrRot.y = y
  336.     '            CALL CalculateMOI(1)
  337.     '            Shape(1).Omega = 0
  338.     '        END IF
  339.     '        IF (_MOUSEBUTTON(2)) THEN
  340.     '            x = _MOUSEX - _WIDTH / 2
  341.     '            y = -_MOUSEY + _HEIGHT / 2
  342.     '            vtemp.x = x - Shape(1).CtrMass.x
  343.     '            vtemp.y = y - Shape(1).CtrMass.y
  344.     '            CALL TranslateShape(1, vtemp)
  345.     '            CALL CalculateMOI(1)
  346.     '            Shape(1).Velocity.x = 0
  347.     '            Shape(1).Velocity.y = 0
  348.     '        END IF
  349.     '        IF _MOUSEWHEEL THEN
  350.     '            CALL RotShape(1, Shape(1).CtrRot, .05)
  351.     '        END IF
  352.     '    END IF
  353.     'LOOP
  354.  
  355.     IF (_KEYHIT = 32) THEN
  356.         Shape(1).Velocity.x = Shape(1).Velocity.x + 4
  357.         Shape(1).Omega = Omega - .08
  358.         Shape(1).Velocity.y = 0
  359.         _KEYCLEAR
  360.     END IF
  361.  
  362.     ' Proximity detection
  363.     ProximalPairsCount = 0
  364.     Shape1 = 0
  365.     Shape2 = 0
  366.     FOR j = 1 TO ShapeCount
  367.         FOR k = j + 1 TO ShapeCount
  368.             dx = Shape(j).CtrMass.x - Shape(k).CtrMass.x
  369.             dy = Shape(j).CtrMass.y - Shape(k).CtrMass.y
  370.             dr = SQR(dx * dx + dy * dy)
  371.             IF (dr < 1.10 * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  372.                 ProximalPairsCount = ProximalPairsCount + 1
  373.                 ProximalPairs(ProximalPairsCount, 1) = j
  374.                 ProximalPairs(ProximalPairsCount, 2) = k
  375.                 Shape1 = j
  376.                 Shape2 = k
  377.             END IF
  378.         NEXT
  379.     NEXT
  380.  
  381.     IF (ProximalPairsCount > 0) THEN
  382.         FOR n = 1 TO ProximalPairsCount
  383.             Shape1 = ProximalPairs(n, 1)
  384.             Shape2 = ProximalPairs(n, 2)
  385.  
  386.             ' Collision detection
  387.             rmin = 999 ^ 999
  388.             ClosestIndex1 = 0
  389.             ClosestIndex2 = 0
  390.             FOR j = 1 TO Shape(Shape1).Length
  391.                 FOR k = 1 TO Shape(Shape2).Length
  392.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  393.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  394.                     r2 = dx * dx + dy * dy
  395.                     IF (r2 < rmin) THEN
  396.                         rmin = r2
  397.                         ClosestIndex1 = j
  398.                         ClosestIndex2 = k
  399.                     END IF
  400.                 NEXT
  401.             NEXT
  402.  
  403.             IF (rmin <= 4.5) THEN
  404.                 CollisionCount = CollisionCount + 1
  405.  
  406.                 ' Normal vector 1
  407.                 x0 = PointChain(Shape1, ClosestIndex1).x
  408.                 y0 = PointChain(Shape1, ClosestIndex1).y
  409.                 CALL ccircle(x0, y0, 3, Shape(Shape1).Shade)
  410.                 xx = CalculateNormalY(Shape1, ClosestIndex1)
  411.                 yy = -CalculateNormalX(Shape1, ClosestIndex1)
  412.                 norm = SQR(xx * xx + yy * yy)
  413.                 xx = xx / norm
  414.                 yy = yy / norm
  415.                 nx1 = xx
  416.                 ny1 = yy
  417.                 CALL cline(x0, y0, x0 + nx1 * 15, y0 + ny1 * 15, Shape(Shape1).Shade)
  418.  
  419.                 ' Normal vector 2
  420.                 x0 = PointChain(Shape2, ClosestIndex2).x
  421.                 y0 = PointChain(Shape2, ClosestIndex2).y
  422.                 CALL ccircle(x0, y0, 3, Shape(Shape2).Shade)
  423.                 xx = CalculateNormalY(Shape2, ClosestIndex2)
  424.                 yy = -CalculateNormalX(Shape2, ClosestIndex2)
  425.                 norm = SQR(xx * xx + yy * yy)
  426.                 xx = xx / norm
  427.                 yy = yy / norm
  428.                 nx2 = xx
  429.                 ny2 = yy
  430.                 CALL cline(x0, y0, x0 + nx2 * 15, y0 + ny2 * 15, Shape(Shape2).Shade)
  431.  
  432.                 ' Perpendicular vector 1
  433.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).CtrRot.x
  434.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).CtrRot.y
  435.                 px1 = -dy1
  436.                 py1 = dx1
  437.                 p1 = SQR(px1 * px1 + py1 * py1)
  438.                 px1 = px1 / p1
  439.                 py1 = py1 / p1
  440.                 CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + px1 * 15, PointChain(Shape1, ClosestIndex1).y + py1 * 15, Shape(Shape1).Shade)
  441.  
  442.                 ' Perpendicular vector 2
  443.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).CtrRot.x
  444.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).CtrRot.y
  445.                 px2 = -dy2
  446.                 py2 = dx2
  447.                 p2 = SQR(px2 * px2 + py2 * py2)
  448.                 px2 = px2 / p2
  449.                 py2 = py2 / p2
  450.                 CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + px2 * 15, PointChain(Shape2, ClosestIndex2).y + py2 * 15, Shape(Shape2).Shade)
  451.  
  452.                 ' Angular velocity vector 1
  453.                 w1 = Shape(Shape1).Omega
  454.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  455.                 vx1 = w1 * r1 * px1
  456.                 vy1 = w1 * r1 * py1
  457.                 CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + vx1 * 5, PointChain(Shape1, ClosestIndex1).y + vy1 * 5, Shape(Shape1).Shade)
  458.  
  459.                 ' Angular velocity vector 2
  460.                 w2 = Shape(Shape2).Omega
  461.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  462.                 vx2 = w2 * r2 * px2
  463.                 vy2 = w2 * r2 * py2
  464.                 CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + vx2 * 5, PointChain(Shape2, ClosestIndex2).y + vy2 * 5, Shape(Shape2).Shade)
  465.  
  466.                 ' Mass terms
  467.                 m1 = Shape(Shape1).Mass
  468.                 i1 = Shape(Shape1).MOI
  469.                 m2 = Shape(Shape2).Mass
  470.                 i2 = Shape(Shape2).MOI
  471.                 mu = 1 / (1 / m1 + 1 / m2)
  472.  
  473.                 ' Velocity differential 1
  474.                 vtx1 = Shape(Shape1).Velocity.x + vx1
  475.                 vty1 = Shape(Shape1).Velocity.y + vy1
  476.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  477.  
  478.                 ' Velocity differential 2
  479.                 vtx2 = Shape(Shape2).Velocity.x + vx2
  480.                 vty2 = Shape(Shape2).Velocity.y + vy2
  481.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  482.  
  483.                 ' Velocity differential total
  484.                 dvtx = vtx2 - vtx1
  485.                 dvty = vty2 - vty1
  486.  
  487.                 ' Geometry
  488.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  489.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  490.  
  491.                 ' Momentum exchange
  492.                 qx1 = nx1 * 2 * mu * n1dotdvt
  493.                 qy1 = ny1 * 2 * mu * n1dotdvt
  494.                 qx2 = nx2 * 2 * mu * n2dotdvt
  495.                 qy2 = ny2 * 2 * mu * n2dotdvt
  496.  
  497.                 ' Momentum exchange unit vector
  498.                 q1 = SQR(qx1 * qx1 + qy1 * qy1)
  499.                 qhatx1 = qx1 / q1
  500.                 qhaty1 = qy1 / q1
  501.                 q2 = SQR(qx2 * qx2 + qy2 * qy2)
  502.                 qhatx2 = qx2 / q2
  503.                 qhaty2 = qy2 / q2
  504.  
  505.                 ' Undo previous motion
  506.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, -1 * Shape(Shape1).Omega)
  507.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, -1 * Shape(Shape2).Omega)
  508.                 vtemp.x = -1 * Shape(Shape1).Velocity.x
  509.                 vtemp.y = -1 * Shape(Shape1).Velocity.y
  510.                 CALL TranslateShape(Shape1, vtemp)
  511.                 vtemp.x = -1 * Shape(Shape2).Velocity.x
  512.                 vtemp.y = -1 * Shape(Shape2).Velocity.y
  513.                 CALL TranslateShape(Shape2, vtemp)
  514.  
  515.                 '' Separate along normal
  516.                 'vtemp.x = -nx1 '* vtx1
  517.                 'vtemp.y = -ny1 '* vty1
  518.                 'CALL TranslateShape(Shape1, vtemp)
  519.                 'vtemp.x = -nx2 '* vtx2
  520.                 'vtemp.y = -ny2 '* vty2
  521.                 'CALL TranslateShape(Shape1, vtemp)
  522.  
  523.                 ' Translational impulse
  524.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  525.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  526.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  527.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  528.                 f1 = -q1dotn1 * n1dotr1hat
  529.                 f2 = -q2dotn2 * n2dotr2hat
  530.                 Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x + f1 * qx1 / m1
  531.                 Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y + f1 * qy1 / m1
  532.                 Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x + f2 * qx2 / m2
  533.                 Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y + f2 * qy2 / m2
  534.  
  535.                 ' Angular impulse
  536.                 q1dotp1 = qx1 * px1 + qy1 * py1
  537.                 q2dotp2 = qx2 * px2 + qy2 * py2
  538.                 dw1 = r1 * q1dotp1 / i1
  539.                 dw2 = -r2 * q2dotp2 / i2
  540.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, dw1)
  541.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, dw2)
  542.                 Shape(Shape1).Omega = Shape(Shape1).Omega + dw1
  543.                 Shape(Shape2).Omega = Shape(Shape2).Omega + dw2
  544.             END IF
  545.         NEXT
  546.     END IF
  547.  
  548.     FOR ShapeIndex = 1 TO ShapeCount
  549.         dx = Shape(ShapeIndex).CtrMass.x - Shape(ShapeIndex).CtrRot.x
  550.         dy = Shape(ShapeIndex).CtrMass.y - Shape(ShapeIndex).CtrRot.y
  551.  
  552.         cx = Shape(ShapeIndex).CtrMass.x
  553.         cy = Shape(ShapeIndex).CtrMass.y
  554.         'Gravity.x = 0 '-cx / 10
  555.         'Gravity.y = 0 '-cy / 10
  556.  
  557.         'torque = dx * Gravity.y - dy * Gravity.x
  558.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + torque / Shape(ShapeIndex).MOI
  559.  
  560.         ' Rotation update
  561.         CALL RotShape(ShapeIndex, Shape(ShapeIndex).CtrRot, Shape(ShapeIndex).Omega)
  562.  
  563.         ' Velocity update
  564.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x '+ Gravity.x
  565.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y '+ Gravity.y
  566.         CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  567.  
  568.         ' Damping
  569.         'Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .995
  570.         'Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .995
  571.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .995
  572.     NEXT
  573.  
  574.     ' Graphics
  575.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  576.     FOR ShapeIndex = 1 TO ShapeCount
  577.         LOCATE 1, 1: PRINT CollisionCount
  578.         FOR i = 1 TO Shape(ShapeIndex).Length
  579.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  580.         NEXT
  581.         CALL ccircle(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, 3, Shape(ShapeIndex).Shade)
  582.         CALL ccircle(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, 3, Shape(ShapeIndex).Shade)
  583.         CALL cline(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, Shape(ShapeIndex).Shade)
  584.     NEXT
  585.     CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, Shape(Shape1).CtrRot.x, Shape(Shape1).CtrRot.y, Shape(Shape1).Shade)
  586.     CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, Shape(Shape2).CtrRot.x, Shape(Shape2).CtrRot.y, Shape(Shape2).Shade)
  587.  
  588.     _DISPLAY
  589.     _LIMIT 120
  590.  
  591.  
  592. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  593.     DIM l AS Vector
  594.     DIM r AS Vector
  595.     li = i - 1
  596.     ri = i + 1
  597.     IF (i = 1) THEN li = Shape(k).Length
  598.     IF (i = Shape(k).Length) THEN ri = 1
  599.     l.x = PointChain(k, li).x
  600.     r.x = PointChain(k, ri).x
  601.     dx = r.x - l.x
  602.     CalculateNormalX = dx
  603.  
  604. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  605.     DIM l AS Vector
  606.     DIM r AS Vector
  607.     li = i - 1
  608.     ri = i + 1
  609.     IF (i = 1) THEN li = Shape(k).Length
  610.     IF (i = Shape(k).Length) THEN ri = 1
  611.     l.y = PointChain(k, li).y
  612.     r.y = PointChain(k, ri).y
  613.     dy = r.y - l.y
  614.     CalculateNormalY = dy
  615.  
  616. SUB CalculateCtrMass (k AS INTEGER)
  617.     xx = 0
  618.     yy = 0
  619.     FOR i = 1 TO Shape(k).Length
  620.         xx = xx + PointChain(k, i).x
  621.         yy = yy + PointChain(k, i).y
  622.     NEXT
  623.     Shape(k).CtrMass.x = xx / Shape(k).Length
  624.     Shape(k).CtrMass.y = yy / Shape(k).Length
  625.  
  626. SUB CalculateDiameter (k AS INTEGER)
  627.     r2max = -1
  628.     FOR i = 1 TO Shape(k).Length
  629.         xx = Shape(k).CtrMass.x - PointChain(k, i).x
  630.         yy = Shape(k).CtrMass.y - PointChain(k, i).y
  631.         r2 = xx * xx + yy * yy
  632.         IF (r2 > r2max) THEN
  633.             r2max = r2
  634.         END IF
  635.     NEXT
  636.     Shape(k).Diameter = SQR(r2max)
  637.  
  638. SUB CalculateMOI (k AS INTEGER)
  639.     xx = 0
  640.     yy = 0
  641.     FOR i = 1 TO Shape(k).Length
  642.         xx = xx + (Shape(k).CtrRot.x - PointChain(k, i).x) ^ 2
  643.         yy = yy + (Shape(k).CtrRot.y - PointChain(k, i).y) ^ 2
  644.     NEXT
  645.     Shape(k).MOI = (xx + yy) * (Shape(k).Mass / Shape(k).Length)
  646.  
  647. SUB TranslateShape (k AS INTEGER, c AS Vector)
  648.     FOR i = 1 TO Shape(k).Length
  649.         PointChain(k, i).x = PointChain(k, i).x + c.x
  650.         PointChain(k, i).y = PointChain(k, i).y + c.y
  651.     NEXT
  652.     Shape(k).CtrRot.x = Shape(k).CtrRot.x + c.x
  653.     Shape(k).CtrRot.y = Shape(k).CtrRot.y + c.y
  654.     Shape(k).CtrMass.x = Shape(k).CtrMass.x + c.x
  655.     Shape(k).CtrMass.y = Shape(k).CtrMass.y + c.y
  656.  
  657. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  658.     FOR i = 1 TO Shape(k).Length
  659.         xx = PointChain(k, i).x - c.x
  660.         yy = PointChain(k, i).y - c.y
  661.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  662.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  663.     NEXT
  664.     xx = Shape(k).CtrMass.x - c.x
  665.     yy = Shape(k).CtrMass.y - c.y
  666.     Shape(k).CtrMass.x = c.x + xx * COS(da) - yy * SIN(da)
  667.     Shape(k).CtrMass.y = c.y + yy * COS(da) + xx * SIN(da)
  668.  
  669. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  670.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  671.  
  672. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  673.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  674.  
  675. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  676.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
« Last Edit: February 22, 2020, 03:05:25 am 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
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #5 on: February 22, 2020, 03:37:24 am »
This one is more fun. We have a gravity test in a low-viscosity fluid. Press space to your heart's content.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(800, 600, 32)
  2. _SCREENMOVE (_DESKTOPWIDTH \ 2 - _WIDTH \ 2) - 3, (_DESKTOPHEIGHT \ 2 - _HEIGHT \ 2) - 29
  3.  
  4.  
  5. TYPE Vector
  6.     x AS DOUBLE
  7.     y AS DOUBLE
  8.  
  9. TYPE Object
  10.     CtrMass AS Vector
  11.     CtrRot AS Vector
  12.     Diameter AS DOUBLE
  13.     Mass AS DOUBLE
  14.     MOI AS DOUBLE
  15.     Length AS INTEGER
  16.     Omega AS DOUBLE
  17.     Shade AS _UNSIGNED LONG
  18.     Velocity AS Vector
  19.  
  20. DIM vtemp AS Vector
  21.  
  22. DIM SHARED Shape(1000) AS Object
  23. DIM SHARED PointChain(1000, 5000) AS Vector
  24. DIM SHARED ShapeCount AS INTEGER
  25. DIM SHARED CollisionCount AS INTEGER
  26. ShapeCount = 0
  27. CollisionCount = 0
  28.  
  29. DIM Gravity AS Vector
  30. Gravity.x = 0
  31. Gravity.y = -2
  32.  
  33. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  34. DIM SHARED ProximalPairsCount
  35.  
  36. ShapeCount = ShapeCount + 1
  37. Shape(ShapeCount).CtrRot.x = -220
  38. Shape(ShapeCount).CtrRot.y = 0
  39. i = 0
  40. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  41.     i = i + 1
  42.     r = 40 + 25 * COS(1.5 * j) ^ 2
  43.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  44.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  45. Shape(ShapeCount).Length = i
  46. Shape(ShapeCount).Mass = i
  47. Shape(ShapeCount).Velocity.x = 0
  48. Shape(ShapeCount).Velocity.y = 0
  49. Shape(ShapeCount).Omega = 0
  50. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  51. CALL CalculateCtrMass(ShapeCount)
  52. CALL CalculateDiameter(ShapeCount)
  53. CALL CalculateMOI(ShapeCount)
  54.  
  55. ShapeCount = ShapeCount + 1
  56. Shape(ShapeCount).CtrRot.x = 170
  57. Shape(ShapeCount).CtrRot.y = 0
  58. i = 0
  59. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  60.     i = i + 1
  61.     r = 27
  62.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  63.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  64. Shape(ShapeCount).Length = i
  65. Shape(ShapeCount).Mass = i
  66. Shape(ShapeCount).Velocity.x = 0
  67. Shape(ShapeCount).Velocity.y = 0
  68. Shape(ShapeCount).Omega = 0
  69. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  70. CALL CalculateCtrMass(ShapeCount)
  71. CALL CalculateDiameter(ShapeCount)
  72. CALL CalculateMOI(ShapeCount)
  73.  
  74. ShapeCount = ShapeCount + 1
  75. Shape(ShapeCount).CtrRot.x = 170 + 54
  76. Shape(ShapeCount).CtrRot.y = -31.25
  77. i = 0
  78. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  79.     i = i + 1
  80.     r = 27
  81.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  82.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  83. Shape(ShapeCount).Length = i
  84. Shape(ShapeCount).Mass = i
  85. Shape(ShapeCount).Velocity.x = 0
  86. Shape(ShapeCount).Velocity.y = 0
  87. Shape(ShapeCount).Omega = 0
  88. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  89. CALL CalculateCtrMass(ShapeCount)
  90. CALL CalculateDiameter(ShapeCount)
  91. CALL CalculateMOI(ShapeCount)
  92.  
  93. ShapeCount = ShapeCount + 1
  94. Shape(ShapeCount).CtrRot.x = 170 + 54
  95. Shape(ShapeCount).CtrRot.y = 31.25
  96. i = 0
  97. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  98.     i = i + 1
  99.     r = 27
  100.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  101.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  102. Shape(ShapeCount).Length = i
  103. Shape(ShapeCount).Mass = i
  104. Shape(ShapeCount).Velocity.x = 0
  105. Shape(ShapeCount).Velocity.y = 0
  106. Shape(ShapeCount).Omega = 0
  107. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  108. CALL CalculateCtrMass(ShapeCount)
  109. CALL CalculateDiameter(ShapeCount)
  110. CALL CalculateMOI(ShapeCount)
  111.  
  112. ShapeCount = ShapeCount + 1
  113. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  114. Shape(ShapeCount).CtrRot.y = 0
  115. i = 0
  116. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  117.     i = i + 1
  118.     r = 27
  119.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  120.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  121. Shape(ShapeCount).Length = i
  122. Shape(ShapeCount).Mass = i
  123. Shape(ShapeCount).Velocity.x = 0
  124. Shape(ShapeCount).Velocity.y = 0
  125. Shape(ShapeCount).Omega = 0
  126. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  127. CALL CalculateCtrMass(ShapeCount)
  128. CALL CalculateDiameter(ShapeCount)
  129. CALL CalculateMOI(ShapeCount)
  130.  
  131. ShapeCount = ShapeCount + 1
  132. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  133. Shape(ShapeCount).CtrRot.y = -31.25 * 2
  134. i = 0
  135. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  136.     i = i + 1
  137.     r = 27
  138.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  139.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  140. Shape(ShapeCount).Length = i
  141. Shape(ShapeCount).Mass = i
  142. Shape(ShapeCount).Velocity.x = 0
  143. Shape(ShapeCount).Velocity.y = 0
  144. Shape(ShapeCount).Omega = 0
  145. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  146. CALL CalculateCtrMass(ShapeCount)
  147. CALL CalculateDiameter(ShapeCount)
  148. CALL CalculateMOI(ShapeCount)
  149.  
  150. ShapeCount = ShapeCount + 1
  151. Shape(ShapeCount).CtrRot.x = 170 + 54 + 54
  152. Shape(ShapeCount).CtrRot.y = 31.25 * 2
  153. i = 0
  154. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  155.     i = i + 1
  156.     r = 27
  157.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  158.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  159. Shape(ShapeCount).Length = i
  160. Shape(ShapeCount).Mass = i
  161. Shape(ShapeCount).Velocity.x = 0
  162. Shape(ShapeCount).Velocity.y = 0
  163. Shape(ShapeCount).Omega = 0
  164. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  165. CALL CalculateCtrMass(ShapeCount)
  166. CALL CalculateDiameter(ShapeCount)
  167. CALL CalculateMOI(ShapeCount)
  168.  
  169. 'ShapeCount = ShapeCount + 1
  170. 'Shape(ShapeCount).CtrRot.x = -200
  171. 'Shape(ShapeCount).CtrRot.y = 0
  172. 'i = 0
  173. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  174. '    i = i + 1
  175. '    r = 40 + 25 * COS(j) ^ 2
  176. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  177. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  178. 'NEXT
  179. 'Shape(ShapeCount).Length = i
  180. 'Shape(ShapeCount).Mass = i
  181. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  182. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  183. 'Shape(ShapeCount).Omega = 0
  184. 'Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  185. 'CALL CalculateCtrMass(ShapeCount)
  186. 'CALL CalculateDiameter(ShapeCount)
  187. 'CALL CalculateMOI(ShapeCount)
  188.  
  189. 'ShapeCount = ShapeCount + 1
  190. 'Shape(ShapeCount).CtrRot.x = 0
  191. 'Shape(ShapeCount).CtrRot.y = 0
  192. 'i = 0
  193. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  194. '    i = i + 1
  195. '    r = 40 + 25 * COS(1.5 * j) ^ 2
  196. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  197. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  198. 'NEXT
  199. 'Shape(ShapeCount).Length = i
  200. 'Shape(ShapeCount).Mass = i
  201. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  202. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  203. 'Shape(ShapeCount).Omega = 0
  204. 'Shape(ShapeCount).Shade = _RGB(255, 0, 255)
  205. 'CALL CalculateCtrMass(ShapeCount)
  206. 'CALL CalculateDiameter(ShapeCount)
  207. 'CALL CalculateMOI(ShapeCount)
  208.  
  209. 'ShapeCount = ShapeCount + 1
  210. 'Shape(ShapeCount).CtrRot.x = 200
  211. 'Shape(ShapeCount).CtrRot.y = 0
  212. 'i = 0
  213. 'FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  214. '    i = i + 1
  215. '    r = 50
  216. '    PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  217. '    PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  218. 'NEXT
  219. 'Shape(ShapeCount).Length = i
  220. 'Shape(ShapeCount).Mass = i
  221. 'Shape(ShapeCount).Velocity.x = 2 * (RND - .5)
  222. 'Shape(ShapeCount).Velocity.y = 2 * (RND - .5)
  223. 'Shape(ShapeCount).Omega = 0
  224. 'Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  225. 'CALL CalculateCtrMass(ShapeCount)
  226. 'CALL CalculateDiameter(ShapeCount)
  227. 'CALL CalculateMOI(ShapeCount)
  228.  
  229. 'w = _WIDTH / 2
  230. 'h = _HEIGHT / 2
  231. 'b = 20
  232. 'FOR n = -1 TO 1 STEP 2
  233.  
  234. '    FOR k = (-w + 2 * 5) TO (w - 2 * 5) STEP b
  235. '        ShapeCount = ShapeCount + 1
  236. '        Shape(ShapeCount).CtrRot.x = x
  237. '        Shape(ShapeCount).CtrRot.y = n * h - n * 5
  238. '        i = 0
  239. '        FOR j = (Shape(ShapeCount).CtrRot.x - b / 2) TO (Shape(ShapeCount).CtrRot.x + b / 2) STEP 5
  240. '            i = i + 1
  241. '            PointChain(ShapeCount, i).x = j
  242. '            PointChain(ShapeCount, i).y = n * h - n * 5
  243. '        NEXT
  244. '        Shape(ShapeCount).Length = i
  245. '        Shape(ShapeCount).Mass = 999 ^ 999
  246. '        Shape(ShapeCount).Velocity.x = 0
  247. '        Shape(ShapeCount).Velocity.y = 0
  248. '        Shape(ShapeCount).Omega = 0
  249. '        Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  250. '        CALL CalculateCtrMass(ShapeCount)
  251. '        CALL CalculateDiameter(ShapeCount)
  252. '        CALL CalculateMOI(ShapeCount)
  253. '    NEXT
  254.  
  255. '    'ShapeCount = ShapeCount + 1
  256. '    'Shape(ShapeCount).CtrRot.x = n * w - n * 5
  257. '    'Shape(ShapeCount).CtrRot.y = 0
  258. '    'i = 0
  259. '    'FOR j = (-h + 2 * 5) TO (h - 2 * 5) STEP 3
  260. '    '    i = i + 1
  261. '    '    PointChain(ShapeCount, i).x = n * w - n * 5
  262. '    '    PointChain(ShapeCount, i).y = j
  263. '    'NEXT
  264. '    'Shape(ShapeCount).Length = i
  265. '    'Shape(ShapeCount).Mass = 999 ^ 999
  266. '    'Shape(ShapeCount).Velocity.x = 0
  267. '    'Shape(ShapeCount).Velocity.y = 0
  268. '    'Shape(ShapeCount).Omega = 0
  269. '    'Shape(ShapeCount).Shade = _RGB(255, 255, 0)
  270. '    'CALL CalculateCtrMass(ShapeCount)
  271. '    ''CALL CalculateDiameter(ShapeCount)
  272. '    'Shape(ShapeCount).Diameter = 5
  273. '    'CALL CalculateMOI(ShapeCount)
  274. 'NEXT
  275.  
  276. ' Walls
  277. b = 10
  278. FOR n = -1 TO 1 STEP 2
  279.     FOR k = -(_WIDTH / 2 - b) TO (_WIDTH / 2 - b) STEP 2 * b
  280.         ShapeCount = ShapeCount + 1
  281.         Shape(ShapeCount).CtrRot.x = k
  282.         Shape(ShapeCount).CtrRot.y = n * (_HEIGHT / 2 - b / 2)
  283.         i = 0
  284.         FOR j = (Shape(ShapeCount).CtrRot.x - b / 2) TO (Shape(ShapeCount).CtrRot.x + b / 2) STEP 3
  285.             i = i + 1
  286.             PointChain(ShapeCount, i).x = j
  287.             PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y
  288.         NEXT
  289.         Shape(ShapeCount).Length = i
  290.         Shape(ShapeCount).Mass = 999 ^ 999
  291.         Shape(ShapeCount).Velocity.x = 0
  292.         Shape(ShapeCount).Velocity.y = 0
  293.         Shape(ShapeCount).Omega = 0
  294.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  295.         CALL CalculateCtrMass(ShapeCount)
  296.         CALL CalculateDiameter(ShapeCount)
  297.         CALL CalculateMOI(ShapeCount)
  298.         Shape(ShapeCount).MOI = 999 ^ 999
  299.     NEXT
  300.  
  301.     FOR k = -(_HEIGHT / 2 - b) TO (_HEIGHT / 2 - b) STEP 2 * b
  302.         ShapeCount = ShapeCount + 1
  303.         Shape(ShapeCount).CtrRot.x = n * (_WIDTH / 2 - b / 2)
  304.         Shape(ShapeCount).CtrRot.y = k
  305.         i = 0
  306.         FOR j = (Shape(ShapeCount).CtrRot.y - b / 2) TO (Shape(ShapeCount).CtrRot.y + b / 2) STEP 3
  307.             i = i + 1
  308.             PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x
  309.             PointChain(ShapeCount, i).y = j
  310.         NEXT
  311.         Shape(ShapeCount).Length = i
  312.         Shape(ShapeCount).Mass = 999 ^ 999
  313.         Shape(ShapeCount).Velocity.x = 0
  314.         Shape(ShapeCount).Velocity.y = 0
  315.         Shape(ShapeCount).Omega = 0
  316.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  317.         CALL CalculateCtrMass(ShapeCount)
  318.         CALL CalculateDiameter(ShapeCount)
  319.         CALL CalculateMOI(ShapeCount)
  320.         Shape(ShapeCount).MOI = 999 ^ 999
  321.     NEXT
  322.  
  323.  
  324.     'DO WHILE _MOUSEINPUT
  325.     '    x = _MOUSEX
  326.     '    y = _MOUSEY
  327.     '    IF ((x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT)) THEN
  328.     '        IF (_MOUSEBUTTON(1)) THEN
  329.     '            x = _MOUSEX - _WIDTH / 2
  330.     '            y = -_MOUSEY + _HEIGHT / 2
  331.     '            Shape(1).CtrRot.x = x
  332.     '            Shape(1).CtrRot.y = y
  333.     '            CALL CalculateMOI(1)
  334.     '            Shape(1).Omega = 0
  335.     '        END IF
  336.     '        IF (_MOUSEBUTTON(2)) THEN
  337.     '            x = _MOUSEX - _WIDTH / 2
  338.     '            y = -_MOUSEY + _HEIGHT / 2
  339.     '            vtemp.x = x - Shape(1).CtrMass.x
  340.     '            vtemp.y = y - Shape(1).CtrMass.y
  341.     '            CALL TranslateShape(1, vtemp)
  342.     '            CALL CalculateMOI(1)
  343.     '            Shape(1).Velocity.x = 0
  344.     '            Shape(1).Velocity.y = 0
  345.     '        END IF
  346.     '        IF _MOUSEWHEEL THEN
  347.     '            CALL RotShape(1, Shape(1).CtrRot, .05)
  348.     '        END IF
  349.     '    END IF
  350.     'LOOP
  351.  
  352.     IF (_KEYHIT = 32) THEN
  353.         Shape(1).Velocity.x = Shape(1).Velocity.x + 4
  354.         Shape(1).Omega = Omega - .08
  355.         Shape(1).Velocity.y = 0
  356.         _KEYCLEAR
  357.     END IF
  358.  
  359.     ' Proximity detection
  360.     ProximalPairsCount = 0
  361.     Shape1 = 0
  362.     Shape2 = 0
  363.     FOR j = 1 TO ShapeCount
  364.         FOR k = j + 1 TO ShapeCount
  365.             dx = Shape(j).CtrMass.x - Shape(k).CtrMass.x
  366.             dy = Shape(j).CtrMass.y - Shape(k).CtrMass.y
  367.             dr = SQR(dx * dx + dy * dy)
  368.             IF (dr < 1.10 * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  369.                 ProximalPairsCount = ProximalPairsCount + 1
  370.                 ProximalPairs(ProximalPairsCount, 1) = j
  371.                 ProximalPairs(ProximalPairsCount, 2) = k
  372.                 Shape1 = j
  373.                 Shape2 = k
  374.             END IF
  375.         NEXT
  376.     NEXT
  377.  
  378.     IF (ProximalPairsCount > 0) THEN
  379.         FOR n = 1 TO ProximalPairsCount
  380.             Shape1 = ProximalPairs(n, 1)
  381.             Shape2 = ProximalPairs(n, 2)
  382.  
  383.             ' Collision detection
  384.             rmin = 999 ^ 999
  385.             ClosestIndex1 = 0
  386.             ClosestIndex2 = 0
  387.             FOR j = 1 TO Shape(Shape1).Length
  388.                 FOR k = 1 TO Shape(Shape2).Length
  389.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  390.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  391.                     r2 = dx * dx + dy * dy
  392.                     IF (r2 < rmin) THEN
  393.                         rmin = r2
  394.                         ClosestIndex1 = j
  395.                         ClosestIndex2 = k
  396.                     END IF
  397.                 NEXT
  398.             NEXT
  399.  
  400.             IF (rmin <= 2) THEN
  401.                 CollisionCount = CollisionCount + 1
  402.  
  403.                 ' Normal vector 1
  404.                 x0 = PointChain(Shape1, ClosestIndex1).x
  405.                 y0 = PointChain(Shape1, ClosestIndex1).y
  406.                 CALL ccircle(x0, y0, 3, Shape(Shape1).Shade)
  407.                 xx = CalculateNormalY(Shape1, ClosestIndex1)
  408.                 yy = -CalculateNormalX(Shape1, ClosestIndex1)
  409.                 norm = SQR(xx * xx + yy * yy)
  410.                 xx = xx / norm
  411.                 yy = yy / norm
  412.                 nx1 = xx
  413.                 ny1 = yy
  414.                 CALL cline(x0, y0, x0 + nx1 * 15, y0 + ny1 * 15, Shape(Shape1).Shade)
  415.  
  416.                 ' Normal vector 2
  417.                 x0 = PointChain(Shape2, ClosestIndex2).x
  418.                 y0 = PointChain(Shape2, ClosestIndex2).y
  419.                 CALL ccircle(x0, y0, 3, Shape(Shape2).Shade)
  420.                 xx = CalculateNormalY(Shape2, ClosestIndex2)
  421.                 yy = -CalculateNormalX(Shape2, ClosestIndex2)
  422.                 norm = SQR(xx * xx + yy * yy)
  423.                 xx = xx / norm
  424.                 yy = yy / norm
  425.                 nx2 = xx
  426.                 ny2 = yy
  427.                 CALL cline(x0, y0, x0 + nx2 * 15, y0 + ny2 * 15, Shape(Shape2).Shade)
  428.  
  429.                 ' Perpendicular vector 1
  430.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).CtrRot.x
  431.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).CtrRot.y
  432.                 px1 = -dy1
  433.                 py1 = dx1
  434.                 p1 = SQR(px1 * px1 + py1 * py1)
  435.                 px1 = px1 / p1
  436.                 py1 = py1 / p1
  437.                 CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + px1 * 15, PointChain(Shape1, ClosestIndex1).y + py1 * 15, Shape(Shape1).Shade)
  438.  
  439.                 ' Perpendicular vector 2
  440.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).CtrRot.x
  441.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).CtrRot.y
  442.                 px2 = -dy2
  443.                 py2 = dx2
  444.                 p2 = SQR(px2 * px2 + py2 * py2)
  445.                 px2 = px2 / p2
  446.                 py2 = py2 / p2
  447.                 CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + px2 * 15, PointChain(Shape2, ClosestIndex2).y + py2 * 15, Shape(Shape2).Shade)
  448.  
  449.                 ' Angular velocity vector 1
  450.                 w1 = Shape(Shape1).Omega
  451.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  452.                 vx1 = w1 * r1 * px1
  453.                 vy1 = w1 * r1 * py1
  454.                 CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + vx1 * 5, PointChain(Shape1, ClosestIndex1).y + vy1 * 5, Shape(Shape1).Shade)
  455.  
  456.                 ' Angular velocity vector 2
  457.                 w2 = Shape(Shape2).Omega
  458.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  459.                 vx2 = w2 * r2 * px2
  460.                 vy2 = w2 * r2 * py2
  461.                 CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + vx2 * 5, PointChain(Shape2, ClosestIndex2).y + vy2 * 5, Shape(Shape2).Shade)
  462.  
  463.                 ' Mass terms
  464.                 m1 = Shape(Shape1).Mass
  465.                 i1 = Shape(Shape1).MOI
  466.                 m2 = Shape(Shape2).Mass
  467.                 i2 = Shape(Shape2).MOI
  468.                 mu = 1 / (1 / m1 + 1 / m2)
  469.  
  470.                 ' Velocity differential 1
  471.                 vtx1 = Shape(Shape1).Velocity.x + vx1
  472.                 vty1 = Shape(Shape1).Velocity.y + vy1
  473.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  474.  
  475.                 ' Velocity differential 2
  476.                 vtx2 = Shape(Shape2).Velocity.x + vx2
  477.                 vty2 = Shape(Shape2).Velocity.y + vy2
  478.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  479.  
  480.                 ' Velocity differential total
  481.                 dvtx = vtx2 - vtx1
  482.                 dvty = vty2 - vty1
  483.  
  484.                 ' Geometry
  485.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  486.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  487.  
  488.                 ' Momentum exchange
  489.                 qx1 = nx1 * 2 * mu * n1dotdvt
  490.                 qy1 = ny1 * 2 * mu * n1dotdvt
  491.                 qx2 = nx2 * 2 * mu * n2dotdvt
  492.                 qy2 = ny2 * 2 * mu * n2dotdvt
  493.  
  494.                 ' Momentum exchange unit vector
  495.                 q1 = SQR(qx1 * qx1 + qy1 * qy1)
  496.                 qhatx1 = qx1 / q1
  497.                 qhaty1 = qy1 / q1
  498.                 q2 = SQR(qx2 * qx2 + qy2 * qy2)
  499.                 qhatx2 = qx2 / q2
  500.                 qhaty2 = qy2 / q2
  501.  
  502.                 ' Undo previous motion
  503.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, -Shape(Shape1).Omega)
  504.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, -Shape(Shape2).Omega)
  505.                 vtemp.x = -Shape(Shape1).Velocity.x
  506.                 vtemp.y = -Shape(Shape1).Velocity.y
  507.                 CALL TranslateShape(Shape1, vtemp)
  508.                 vtemp.x = -Shape(Shape2).Velocity.x
  509.                 vtemp.y = -Shape(Shape2).Velocity.y
  510.                 CALL TranslateShape(Shape2, vtemp)
  511.  
  512.                 ' Separate along normal
  513.                 'vtemp.x = -nx1 * (1.05)
  514.                 'vtemp.y = -ny1 * (1.05)
  515.                 'CALL TranslateShape(Shape1, vtemp)
  516.                 'vtemp.x = -nx2 * (1.05)
  517.                 'vtemp.y = -ny2 * (1.05)
  518.                 'CALL TranslateShape(Shape1, vtemp)
  519.  
  520.                 ' Translational impulse
  521.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  522.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  523.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  524.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  525.                 f1 = -q1dotn1 * n1dotr1hat
  526.                 f2 = -q2dotn2 * n2dotr2hat
  527.                 Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x + f1 * qx1 / m1
  528.                 Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y + f1 * qy1 / m1
  529.                 Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x + f2 * qx2 / m2
  530.                 Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y + f2 * qy2 / m2
  531.  
  532.                 ' Angular impulse
  533.                 q1dotp1 = qx1 * px1 + qy1 * py1
  534.                 q2dotp2 = qx2 * px2 + qy2 * py2
  535.                 dw1 = r1 * q1dotp1 / i1
  536.                 dw2 = -r2 * q2dotp2 / i2
  537.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, dw1)
  538.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, dw2)
  539.                 Shape(Shape1).Omega = Shape(Shape1).Omega + dw1
  540.                 Shape(Shape2).Omega = Shape(Shape2).Omega + dw2
  541.  
  542.                 ' Static friction
  543.                 IF Shape(ShapeIndex).Velocity.x * Shape(ShapeIndex).Velocity.x < .1 THEN Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .01
  544.                 IF Shape(ShapeIndex).Velocity.y * Shape(ShapeIndex).Velocity.y < .1 THEN Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .01
  545.                 IF Shape(ShapeIndex).Omega * Shape(ShapeIndex).Omega < .03 THEN Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .01
  546.  
  547.  
  548.             END IF
  549.         NEXT
  550.     END IF
  551.  
  552.     FOR ShapeIndex = 1 TO ShapeCount
  553.         dx = Shape(ShapeIndex).CtrMass.x - Shape(ShapeIndex).CtrRot.x
  554.         dy = Shape(ShapeIndex).CtrMass.y - Shape(ShapeIndex).CtrRot.y
  555.  
  556.         cx = Shape(ShapeIndex).CtrMass.x
  557.         cy = Shape(ShapeIndex).CtrMass.y
  558.         'Gravity.x = 0 '-cx / 10
  559.         'Gravity.y = 0 '-cy / 10
  560.  
  561.         'torque = dx * Gravity.y - dy * Gravity.x
  562.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + torque / Shape(ShapeIndex).MOI
  563.  
  564.         ' Rotation update
  565.         CALL RotShape(ShapeIndex, Shape(ShapeIndex).CtrRot, Shape(ShapeIndex).Omega)
  566.  
  567.         ' Velocity update
  568.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Gravity.x / Shape(ShapeIndex).Mass
  569.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Gravity.y / Shape(ShapeIndex).Mass
  570.         CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  571.  
  572.         ' Damping
  573.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .995
  574.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .995
  575.         Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .995
  576.     NEXT
  577.  
  578.     ' Graphics
  579.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  580.     FOR ShapeIndex = 1 TO ShapeCount
  581.         LOCATE 1, 1: PRINT ProximalPairsCount, CollisionCount
  582.         FOR i = 1 TO Shape(ShapeIndex).Length
  583.             CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  584.         NEXT
  585.         CALL ccircle(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, 3, Shape(ShapeIndex).Shade)
  586.         CALL ccircle(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, 3, Shape(ShapeIndex).Shade)
  587.         CALL cline(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, Shape(ShapeIndex).Shade)
  588.     NEXT
  589.     CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, Shape(Shape1).CtrRot.x, Shape(Shape1).CtrRot.y, Shape(Shape1).Shade)
  590.     CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, Shape(Shape2).CtrRot.x, Shape(Shape2).CtrRot.y, Shape(Shape2).Shade)
  591.  
  592.     _DISPLAY
  593.     _LIMIT 120
  594.  
  595.  
  596. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  597.     DIM l AS Vector
  598.     DIM r AS Vector
  599.     li = i - 1
  600.     ri = i + 1
  601.     IF (i = 1) THEN li = Shape(k).Length
  602.     IF (i = Shape(k).Length) THEN ri = 1
  603.     l.x = PointChain(k, li).x
  604.     r.x = PointChain(k, ri).x
  605.     dx = r.x - l.x
  606.     CalculateNormalX = dx
  607.  
  608. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  609.     DIM l AS Vector
  610.     DIM r AS Vector
  611.     li = i - 1
  612.     ri = i + 1
  613.     IF (i = 1) THEN li = Shape(k).Length
  614.     IF (i = Shape(k).Length) THEN ri = 1
  615.     l.y = PointChain(k, li).y
  616.     r.y = PointChain(k, ri).y
  617.     dy = r.y - l.y
  618.     CalculateNormalY = dy
  619.  
  620. SUB CalculateCtrMass (k AS INTEGER)
  621.     xx = 0
  622.     yy = 0
  623.     FOR i = 1 TO Shape(k).Length
  624.         xx = xx + PointChain(k, i).x
  625.         yy = yy + PointChain(k, i).y
  626.     NEXT
  627.     Shape(k).CtrMass.x = xx / Shape(k).Length
  628.     Shape(k).CtrMass.y = yy / Shape(k).Length
  629.  
  630. SUB CalculateDiameter (k AS INTEGER)
  631.     r2max = -1
  632.     FOR i = 1 TO Shape(k).Length
  633.         xx = Shape(k).CtrMass.x - PointChain(k, i).x
  634.         yy = Shape(k).CtrMass.y - PointChain(k, i).y
  635.         r2 = xx * xx + yy * yy
  636.         IF (r2 > r2max) THEN
  637.             r2max = r2
  638.         END IF
  639.     NEXT
  640.     Shape(k).Diameter = SQR(r2max)
  641.  
  642. SUB CalculateMOI (k AS INTEGER)
  643.     xx = 0
  644.     yy = 0
  645.     FOR i = 1 TO Shape(k).Length
  646.         xx = xx + (Shape(k).CtrRot.x - PointChain(k, i).x) ^ 2
  647.         yy = yy + (Shape(k).CtrRot.y - PointChain(k, i).y) ^ 2
  648.     NEXT
  649.     Shape(k).MOI = (xx + yy) * (Shape(k).Mass / Shape(k).Length)
  650.  
  651. SUB TranslateShape (k AS INTEGER, c AS Vector)
  652.     FOR i = 1 TO Shape(k).Length
  653.         PointChain(k, i).x = PointChain(k, i).x + c.x
  654.         PointChain(k, i).y = PointChain(k, i).y + c.y
  655.     NEXT
  656.     Shape(k).CtrRot.x = Shape(k).CtrRot.x + c.x
  657.     Shape(k).CtrRot.y = Shape(k).CtrRot.y + c.y
  658.     Shape(k).CtrMass.x = Shape(k).CtrMass.x + c.x
  659.     Shape(k).CtrMass.y = Shape(k).CtrMass.y + c.y
  660.  
  661. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  662.     FOR i = 1 TO Shape(k).Length
  663.         xx = PointChain(k, i).x - c.x
  664.         yy = PointChain(k, i).y - c.y
  665.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  666.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  667.     NEXT
  668.     xx = Shape(k).CtrMass.x - c.x
  669.     yy = Shape(k).CtrMass.y - c.y
  670.     Shape(k).CtrMass.x = c.x + xx * COS(da) - yy * SIN(da)
  671.     Shape(k).CtrMass.y = c.y + yy * COS(da) + xx * SIN(da)
  672.  
  673. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  674.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  675.  
  676. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  677.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  678.  
  679. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  680.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
« Last Edit: February 22, 2020, 03:58:08 am 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
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #6 on: February 22, 2020, 01:08:59 pm »
And here's an update that lets you crash oddly-shaped potatoes together. Pardon any jitters or explosions, it's not really optimized (too early). It auto-smooths what you draw using code taken shamelessly from the Samples collection.

A curious thing this does *without being directly asked, only indirectly by forces* is to seek the ideal packing of oddly-shaped items in two dimensions.

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

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

Note the center of mass isn't always inside the object, which leads to trouble when PAINTing about the center of mass. It'll stay wireframe for now.

The screenshot below is made from drawing a few odd-shaped potatoes and things that are centrally attracted to the middle of the screen. They self-arrange to find a local energy minimum. I find it amazing that the shapes and contours find each other.

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.  
  7. ' Data
  8. TYPE Vector
  9.     x AS DOUBLE
  10.     y AS DOUBLE
  11.  
  12. TYPE Object
  13.     CtrMass AS Vector
  14.     CtrRot AS Vector
  15.     Diameter AS DOUBLE
  16.     Elements AS INTEGER
  17.     Mass AS DOUBLE
  18.     MOI AS DOUBLE
  19.     Omega AS DOUBLE
  20.     Shade AS _UNSIGNED LONG
  21.     Velocity AS Vector
  22.  
  23. DIM vtemp AS Vector
  24.  
  25. ' Statics
  26. DIM SHARED Shape(1000) AS Object
  27. DIM SHARED PointChain(1000, 5000) AS Vector
  28. DIM SHARED TempChain(1000, 5000) AS Vector
  29. DIM SHARED ShapeCount AS INTEGER
  30. ShapeCount = 0
  31.  
  32. ' Dynamics
  33. DIM SHARED CollisionCount AS INTEGER
  34. CollisionCount = 0
  35.  
  36. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  37. DIM SHARED ProximalPairsCount
  38.  
  39. DIM Gravity AS Vector
  40. Gravity.x = 0
  41. Gravity.y = 0 '-10
  42.  
  43. ' Create test object(s)
  44.  
  45. ShapeCount = ShapeCount + 1
  46. Shape(ShapeCount).CtrRot.x = -100
  47. Shape(ShapeCount).CtrRot.y = 0
  48. i = 0
  49. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  50.     i = i + 1
  51.     r = 40 + 25 * COS(1.5 * j) ^ 2
  52.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  53.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  54. Shape(ShapeCount).Elements = i
  55. Shape(ShapeCount).Mass = i
  56. Shape(ShapeCount).Velocity.x = 0
  57. Shape(ShapeCount).Velocity.y = 0
  58. Shape(ShapeCount).Omega = .1
  59. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  60. CALL CalculateCtrMass(ShapeCount)
  61. CALL CalculateDiameter(ShapeCount)
  62. CALL CalculateMOI(ShapeCount)
  63.  
  64. ' Walls
  65. b = 50
  66. FOR n = -1 TO 1 STEP 2
  67.     FOR k = -(_WIDTH / 2 - b) + b TO (_WIDTH / 2 - b) - b STEP 2 * b
  68.         ShapeCount = ShapeCount + 1
  69.         Shape(ShapeCount).CtrRot.x = k
  70.         Shape(ShapeCount).CtrRot.y = n * (_HEIGHT / 2 - b * .5)
  71.         i = 0
  72.         FOR j = (Shape(ShapeCount).CtrRot.x + n * b * .9) TO (Shape(ShapeCount).CtrRot.x - n * b * .9) STEP -n * 2
  73.             i = i + 1
  74.             PointChain(ShapeCount, i).x = j
  75.             PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y
  76.         NEXT
  77.         Shape(ShapeCount).Elements = i
  78.         Shape(ShapeCount).Mass = 999 ^ 999
  79.         Shape(ShapeCount).Velocity.x = 0
  80.         Shape(ShapeCount).Velocity.y = 0
  81.         Shape(ShapeCount).Omega = 0
  82.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  83.         CALL CalculateCtrMass(ShapeCount)
  84.         CALL CalculateDiameter(ShapeCount)
  85.         CALL CalculateMOI(ShapeCount)
  86.         Shape(ShapeCount).MOI = 999 ^ 999
  87.     NEXT
  88.  
  89.     FOR k = -(_HEIGHT / 2 - b) + b TO (_HEIGHT / 2 - b) - b STEP 2 * b
  90.         ShapeCount = ShapeCount + 1
  91.         Shape(ShapeCount).CtrRot.x = n * (_WIDTH / 2 - b * .5)
  92.         Shape(ShapeCount).CtrRot.y = k
  93.         i = 0
  94.         FOR j = (Shape(ShapeCount).CtrRot.y + n * b * .9) TO (Shape(ShapeCount).CtrRot.y - n * b * .9) STEP -n * 2
  95.             i = i + 1
  96.             PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x
  97.             PointChain(ShapeCount, i).y = j
  98.         NEXT
  99.         Shape(ShapeCount).Elements = i
  100.         Shape(ShapeCount).Mass = 999 ^ 999
  101.         Shape(ShapeCount).Velocity.x = 0
  102.         Shape(ShapeCount).Velocity.y = 0
  103.         Shape(ShapeCount).Omega = 0
  104.         Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  105.         CALL CalculateCtrMass(ShapeCount)
  106.         CALL CalculateDiameter(ShapeCount)
  107.         CALL CalculateMOI(ShapeCount)
  108.         Shape(ShapeCount).MOI = 999 ^ 999
  109.     NEXT
  110.  
  111.  
  112.     IF (_KEYHIT = 32) THEN
  113.         CLS
  114.         CALL Graphics
  115.         LOCATE 1, 1: PRINT "Click & Drag counter-clockwise to create a shape."
  116.         CALL CreateShape
  117.         _KEYCLEAR
  118.     END IF
  119.  
  120.     ' Proximity detection
  121.     ProximalPairsCount = 0
  122.     Shape1 = 0
  123.     Shape2 = 0
  124.     FOR j = 1 TO ShapeCount
  125.         FOR k = j + 1 TO ShapeCount
  126.             dx = Shape(j).CtrMass.x - Shape(k).CtrMass.x
  127.             dy = Shape(j).CtrMass.y - Shape(k).CtrMass.y
  128.             dr = SQR(dx * dx + dy * dy)
  129.             IF (dr < 1.15 * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  130.                 ProximalPairsCount = ProximalPairsCount + 1
  131.                 ProximalPairs(ProximalPairsCount, 1) = j
  132.                 ProximalPairs(ProximalPairsCount, 2) = k
  133.                 Shape1 = j
  134.                 Shape2 = k
  135.             END IF
  136.         NEXT
  137.     NEXT
  138.  
  139.     IF (ProximalPairsCount > 0) THEN
  140.         FOR n = 1 TO ProximalPairsCount
  141.             Shape1 = ProximalPairs(n, 1)
  142.             Shape2 = ProximalPairs(n, 2)
  143.  
  144.             ' Collision detection
  145.             rmin = 999 ^ 999
  146.             ClosestIndex1 = 0
  147.             ClosestIndex2 = 0
  148.             FOR j = 1 TO Shape(Shape1).Elements
  149.                 FOR k = 1 TO Shape(Shape2).Elements
  150.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  151.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  152.                     r2 = dx * dx + dy * dy
  153.                     IF (r2 < rmin) THEN
  154.                         rmin = r2
  155.                         ClosestIndex1 = j
  156.                         ClosestIndex2 = k
  157.                     END IF
  158.                 NEXT
  159.             NEXT
  160.  
  161.             IF (rmin <= 2) THEN
  162.                 CollisionCount = CollisionCount + 1
  163.  
  164.                 ' Normal vector 1
  165.                 x0 = PointChain(Shape1, ClosestIndex1).x
  166.                 y0 = PointChain(Shape1, ClosestIndex1).y
  167.                 'CALL ccircle(x0, y0, 3, Shape(Shape1).Shade)
  168.                 xx = CalculateNormalY(Shape1, ClosestIndex1)
  169.                 yy = -CalculateNormalX(Shape1, ClosestIndex1)
  170.                 norm = SQR(xx * xx + yy * yy)
  171.                 xx = xx / norm
  172.                 yy = yy / norm
  173.                 nx1 = xx
  174.                 ny1 = yy
  175.                 'CALL cline(x0, y0, x0 + nx1 * 15, y0 + ny1 * 15, Shape(Shape1).Shade)
  176.  
  177.                 ' Normal vector 2
  178.                 x0 = PointChain(Shape2, ClosestIndex2).x
  179.                 y0 = PointChain(Shape2, ClosestIndex2).y
  180.                 'CALL ccircle(x0, y0, 3, Shape(Shape2).Shade)
  181.                 xx = CalculateNormalY(Shape2, ClosestIndex2)
  182.                 yy = -CalculateNormalX(Shape2, ClosestIndex2)
  183.                 norm = SQR(xx * xx + yy * yy)
  184.                 xx = xx / norm
  185.                 yy = yy / norm
  186.                 nx2 = xx
  187.                 ny2 = yy
  188.                 'CALL cline(x0, y0, x0 + nx2 * 15, y0 + ny2 * 15, Shape(Shape2).Shade)
  189.  
  190.                 ' Perpendicular vector 1
  191.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).CtrRot.x
  192.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).CtrRot.y
  193.                 px1 = -dy1
  194.                 py1 = dx1
  195.                 p1 = SQR(px1 * px1 + py1 * py1)
  196.                 px1 = px1 / p1
  197.                 py1 = py1 / p1
  198.                 'CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + px1 * 15, PointChain(Shape1, ClosestIndex1).y + py1 * 15, Shape(Shape1).Shade)
  199.  
  200.                 ' Perpendicular vector 2
  201.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).CtrRot.x
  202.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).CtrRot.y
  203.                 px2 = -dy2
  204.                 py2 = dx2
  205.                 p2 = SQR(px2 * px2 + py2 * py2)
  206.                 px2 = px2 / p2
  207.                 py2 = py2 / p2
  208.                 'CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + px2 * 15, PointChain(Shape2, ClosestIndex2).y + py2 * 15, Shape(Shape2).Shade)
  209.  
  210.                 ' Angular velocity vector 1
  211.                 w1 = Shape(Shape1).Omega
  212.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  213.                 vx1 = w1 * r1 * px1
  214.                 vy1 = w1 * r1 * py1
  215.                 'CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, PointChain(Shape1, ClosestIndex1).x + vx1 * 5, PointChain(Shape1, ClosestIndex1).y + vy1 * 5, Shape(Shape1).Shade)
  216.  
  217.                 ' Angular velocity vector 2
  218.                 w2 = Shape(Shape2).Omega
  219.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  220.                 vx2 = w2 * r2 * px2
  221.                 vy2 = w2 * r2 * py2
  222.                 'CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, PointChain(Shape2, ClosestIndex2).x + vx2 * 5, PointChain(Shape2, ClosestIndex2).y + vy2 * 5, Shape(Shape2).Shade)
  223.  
  224.                 ' Mass terms
  225.                 m1 = Shape(Shape1).Mass
  226.                 i1 = Shape(Shape1).MOI
  227.                 m2 = Shape(Shape2).Mass
  228.                 i2 = Shape(Shape2).MOI
  229.                 mu = 1 / (1 / m1 + 1 / m2)
  230.  
  231.                 ' Velocity differential 1
  232.                 vtx1 = Shape(Shape1).Velocity.x + vx1
  233.                 vty1 = Shape(Shape1).Velocity.y + vy1
  234.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  235.  
  236.                 ' Velocity differential 2
  237.                 vtx2 = Shape(Shape2).Velocity.x + vx2
  238.                 vty2 = Shape(Shape2).Velocity.y + vy2
  239.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  240.  
  241.                 ' Velocity differential total
  242.                 dvtx = vtx2 - vtx1
  243.                 dvty = vty2 - vty1
  244.  
  245.                 ' Geometry
  246.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  247.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  248.  
  249.                 ' Momentum exchange
  250.                 qx1 = nx1 * 2 * mu * n1dotdvt
  251.                 qy1 = ny1 * 2 * mu * n1dotdvt
  252.                 qx2 = nx2 * 2 * mu * n2dotdvt
  253.                 qy2 = ny2 * 2 * mu * n2dotdvt
  254.  
  255.                 ' Momentum exchange unit vector
  256.                 q1 = SQR(qx1 * qx1 + qy1 * qy1)
  257.                 qhatx1 = qx1 / q1
  258.                 qhaty1 = qy1 / q1
  259.                 q2 = SQR(qx2 * qx2 + qy2 * qy2)
  260.                 qhatx2 = qx2 / q2
  261.                 qhaty2 = qy2 / q2
  262.  
  263.                 ' Undo previous motion
  264.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, -Shape(Shape1).Omega)
  265.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, -Shape(Shape2).Omega)
  266.                 vtemp.x = -Shape(Shape1).Velocity.x
  267.                 vtemp.y = -Shape(Shape1).Velocity.y
  268.                 CALL TranslateShape(Shape1, vtemp)
  269.                 vtemp.x = -Shape(Shape2).Velocity.x
  270.                 vtemp.y = -Shape(Shape2).Velocity.y
  271.                 CALL TranslateShape(Shape2, vtemp)
  272.  
  273.                 ' Separate along normal
  274.                 vtemp.x = -nx1 * (v1 + .05)
  275.                 vtemp.y = -ny1 * (v1 + .05)
  276.                 CALL TranslateShape(Shape1, vtemp)
  277.                 vtemp.x = -nx2 * (v2 + .05)
  278.                 vtemp.y = -ny2 * (v2 + .05)
  279.                 CALL TranslateShape(Shape2, vtemp)
  280.  
  281.                 ' Translational impulse
  282.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  283.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  284.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  285.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  286.                 f1 = -q1dotn1 * n1dotr1hat
  287.                 f2 = -q2dotn2 * n2dotr2hat
  288.                 Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x + f1 * qx1 / m1
  289.                 Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y + f1 * qy1 / m1
  290.                 Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x + f2 * qx2 / m2
  291.                 Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y + f2 * qy2 / m2
  292.  
  293.                 ' Angular impulse
  294.                 q1dotp1 = qx1 * px1 + qy1 * py1
  295.                 q2dotp2 = qx2 * px2 + qy2 * py2
  296.                 dw1 = r1 * q1dotp1 / i1
  297.                 dw2 = -r2 * q2dotp2 / i2
  298.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, dw1)
  299.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, dw2)
  300.                 Shape(Shape1).Omega = Shape(Shape1).Omega + dw1
  301.                 Shape(Shape2).Omega = Shape(Shape2).Omega + dw2
  302.  
  303.                 ' Dent along normal
  304.                 'PointChain(Shape1, ClosestIndex1).x = PointChain(Shape1, ClosestIndex1).x - v1 * nx1
  305.                 'PointChain(Shape1, ClosestIndex1).y = PointChain(Shape1, ClosestIndex1).y - v1 * ny1
  306.                 'PointChain(Shape2, ClosestIndex2).x = PointChain(Shape2, ClosestIndex2).x - v2 * nx2
  307.                 'PointChain(Shape2, ClosestIndex2).y = PointChain(Shape2, ClosestIndex2).y - v2 * ny2
  308.                 'CALL SmoothShape(Shape1, Shape(Shape1).Elements, 1)
  309.                 'CALL SmoothShape(Shape2, Shape(Shape2).Elements, 1)
  310.  
  311.                 ' Static friction
  312.                 IF (Shape(Shape1).Velocity.x * Shape(Shape1).Velocity.x < .05) THEN Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * .001
  313.                 IF (Shape(Shape1).Velocity.y * Shape(Shape1).Velocity.y < .05) THEN Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * .001
  314.                 IF (Shape(Shape2).Velocity.x * Shape(Shape2).Velocity.x < .05) THEN Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * .001
  315.                 IF (Shape(Shape2).Velocity.y * Shape(Shape2).Velocity.y < .05) THEN Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * .001
  316.                 IF (Shape(Shape1).Omega * Shape(Shape1).Omega < .0001) THEN Shape(Shape1).Omega = Shape(Shape1).Omega * .01
  317.                 IF (Shape(Shape2).Omega * Shape(Shape2).Omega < .0001) THEN Shape(Shape2).Omega = Shape(Shape2).Omega * .01
  318.             END IF
  319.         NEXT
  320.     END IF
  321.  
  322.     FOR ShapeIndex = 1 TO ShapeCount
  323.         dx = Shape(ShapeIndex).CtrMass.x - Shape(ShapeIndex).CtrRot.x
  324.         dy = Shape(ShapeIndex).CtrMass.y - Shape(ShapeIndex).CtrRot.y
  325.  
  326.         cx = Shape(ShapeIndex).CtrMass.x
  327.         cy = Shape(ShapeIndex).CtrMass.y
  328.         Gravity.x = -(cx / Shape(ShapeIndex).Mass) / 20
  329.         Gravity.y = -(cy / Shape(ShapeIndex).Mass) / 20
  330.  
  331.         'torque = dx * Gravity.y - dy * Gravity.x
  332.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + torque / Shape(ShapeIndex).MOI
  333.  
  334.         ' Rotation update
  335.         CALL RotShape(ShapeIndex, Shape(ShapeIndex).CtrRot, Shape(ShapeIndex).Omega)
  336.  
  337.         ' Velocity update
  338.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Gravity.x
  339.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Gravity.y
  340.         CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  341.  
  342.         ' Damping
  343.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .975
  344.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .975
  345.         Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .975
  346.  
  347.     NEXT
  348.  
  349.     CALL Graphics
  350.     _LIMIT 60
  351.  
  352.  
  353. SUB Graphics
  354.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  355.     FOR ShapeIndex = 1 TO ShapeCount
  356.         LOCATE 2, 1: PRINT ProximalPairsCount, CollisionCount
  357.         LOCATE 1, 1: PRINT "Press space to enable creative mode."
  358.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  359.             x1 = PointChain(ShapeIndex, i).x
  360.             y1 = PointChain(ShapeIndex, i).y
  361.             x2 = PointChain(ShapeIndex, i + 1).x
  362.             y2 = PointChain(ShapeIndex, i + 1).y
  363.             'CALL cpset(x1, y1, Shape(ShapeIndex).Shade)
  364.             CALL cline(x1, y1, x2, y2, Shape(ShapeIndex).Shade)
  365.         NEXT
  366.         x1 = PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x
  367.         y1 = PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y
  368.         x2 = PointChain(ShapeIndex, 1).x
  369.         y2 = PointChain(ShapeIndex, 1).y
  370.         'CALL cpset(x1, y1, Shape(ShapeIndex).Shade)
  371.         CALL cline(x1, y1, x2, y2, Shape(ShapeIndex).Shade)
  372.         'CALL ccircle(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, 3, Shape(ShapeIndex).Shade)
  373.         CALL ccircle(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, 3, Shape(ShapeIndex).Shade)
  374.         'CALL cline(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, Shape(ShapeIndex).Shade)
  375.         'CALL cpaint(Shape(ShapeIndex).CtrMass.x, Shape(ShapeIndex).CtrMass.y, Shape(ShapeIndex).Shade, Shape(ShapeIndex).Shade)
  376.     NEXT
  377.     'CALL cline(PointChain(Shape1, ClosestIndex1).x, PointChain(Shape1, ClosestIndex1).y, Shape(Shape1).CtrRot.x, Shape(Shape1).CtrRot.y, Shape(Shape1).Shade)
  378.     'CALL cline(PointChain(Shape2, ClosestIndex2).x, PointChain(Shape2, ClosestIndex2).y, Shape(Shape2).CtrRot.x, Shape(Shape2).CtrRot.y, Shape(Shape2).Shade)
  379.     _DISPLAY
  380.  
  381. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  382.     DIM l AS Vector
  383.     DIM r AS Vector
  384.     li = i - 1
  385.     ri = i + 1
  386.     IF (i = 1) THEN li = Shape(k).Elements
  387.     IF (i = Shape(k).Elements) THEN ri = 1
  388.     l.x = PointChain(k, li).x
  389.     r.x = PointChain(k, ri).x
  390.     dx = r.x - l.x
  391.     CalculateNormalX = dx
  392.  
  393. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  394.     DIM l AS Vector
  395.     DIM r AS Vector
  396.     li = i - 1
  397.     ri = i + 1
  398.     IF (i = 1) THEN li = Shape(k).Elements
  399.     IF (i = Shape(k).Elements) THEN ri = 1
  400.     l.y = PointChain(k, li).y
  401.     r.y = PointChain(k, ri).y
  402.     dy = r.y - l.y
  403.     CalculateNormalY = dy
  404.  
  405. SUB CalculateCtrMass (k AS INTEGER)
  406.     xx = 0
  407.     yy = 0
  408.     FOR i = 1 TO Shape(k).Elements
  409.         xx = xx + PointChain(k, i).x
  410.         yy = yy + PointChain(k, i).y
  411.     NEXT
  412.     Shape(k).CtrMass.x = xx / Shape(k).Elements
  413.     Shape(k).CtrMass.y = yy / Shape(k).Elements
  414.  
  415. SUB CalculateDiameter (k AS INTEGER)
  416.     r2max = -1
  417.     FOR i = 1 TO Shape(k).Elements
  418.         xx = Shape(k).CtrMass.x - PointChain(k, i).x
  419.         yy = Shape(k).CtrMass.y - PointChain(k, i).y
  420.         r2 = xx * xx + yy * yy
  421.         IF (r2 > r2max) THEN
  422.             r2max = r2
  423.         END IF
  424.     NEXT
  425.     Shape(k).Diameter = SQR(r2max)
  426.  
  427. SUB CalculateMOI (k AS INTEGER)
  428.     xx = 0
  429.     yy = 0
  430.     FOR i = 1 TO Shape(k).Elements
  431.         xx = xx + (Shape(k).CtrRot.x - PointChain(k, i).x) ^ 2
  432.         yy = yy + (Shape(k).CtrRot.y - PointChain(k, i).y) ^ 2
  433.     NEXT
  434.     Shape(k).MOI = (xx + yy) * (Shape(k).Mass / Shape(k).Elements)
  435.  
  436. SUB TranslateShape (k AS INTEGER, c AS Vector)
  437.     FOR i = 1 TO Shape(k).Elements
  438.         PointChain(k, i).x = PointChain(k, i).x + c.x
  439.         PointChain(k, i).y = PointChain(k, i).y + c.y
  440.     NEXT
  441.     Shape(k).CtrRot.x = Shape(k).CtrRot.x + c.x
  442.     Shape(k).CtrRot.y = Shape(k).CtrRot.y + c.y
  443.     Shape(k).CtrMass.x = Shape(k).CtrMass.x + c.x
  444.     Shape(k).CtrMass.y = Shape(k).CtrMass.y + c.y
  445.  
  446. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  447.     FOR i = 1 TO Shape(k).Elements
  448.         xx = PointChain(k, i).x - c.x
  449.         yy = PointChain(k, i).y - c.y
  450.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  451.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  452.     NEXT
  453.     xx = Shape(k).CtrMass.x - c.x
  454.     yy = Shape(k).CtrMass.y - c.y
  455.     Shape(k).CtrMass.x = c.x + xx * COS(da) - yy * SIN(da)
  456.     Shape(k).CtrMass.y = c.y + yy * COS(da) + xx * SIN(da)
  457.  
  458. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  459.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  460.  
  461. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  462.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  463.  
  464. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  465.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  466.  
  467. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  468.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  469.  
  470. SUB CreateShape
  471.     rawresolution = 7.5 ' Raw curve resolution.
  472.     targetpoints = 120 ' Number of points per curve.
  473.     smoothiterations = 15 ' Magnitude of smooth effect.
  474.  
  475.     ShapeCount = ShapeCount + 1
  476.  
  477.     numpoints = 0
  478.     xold = 999999
  479.     yold = 999999
  480.  
  481.  
  482.     ' Gather raw data for one curve at a time.
  483.     ' Click+drag mouse button 1 to trace out a curve.
  484.     DO
  485.         DO WHILE _MOUSEINPUT
  486.             x = _MOUSEX
  487.             y = _MOUSEY
  488.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  489.                 IF _MOUSEBUTTON(1) THEN
  490.                     x = x - (_WIDTH / 2)
  491.                     y = -y + (_HEIGHT / 2)
  492.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  493.  
  494.                     ' Collect data only if the new point is sufficiently far away from the previous point.
  495.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  496.                         numpoints = numpoints + 1
  497.                         PointChain(ShapeCount, numpoints).x = x
  498.                         PointChain(ShapeCount, numpoints).y = y
  499.                         CALL cpset(x, y, _RGB(255, 255, 255))
  500.                         xold = x
  501.                         yold = y
  502.                     END IF
  503.                 END IF
  504.             END IF
  505.         LOOP
  506.         _DISPLAY
  507.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  508.  
  509.     ' If the curve contains less than the minimum numer of points, use interpolation to fill in the gaps.
  510.     DO WHILE (numpoints < targetpoints)
  511.  
  512.         ' Determine the pair of neighboring points that have the greatest separation of all pairs.
  513.         rad2max = -1
  514.         kmax = -1
  515.         FOR k = 1 TO numpoints - 1
  516.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  517.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  518.             rad2 = xfac ^ 2 + yfac ^ 2
  519.             IF rad2 > rad2max THEN
  520.                 kmax = k
  521.                 rad2max = rad2
  522.             END IF
  523.         NEXT
  524.         '''
  525.         cornercase = 0
  526.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  527.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  528.         rad2 = xfac ^ 2 + yfac ^ 2
  529.         IF rad2 > rad2max THEN
  530.             kmax = numpoints
  531.             rad2max = rad2
  532.             cornercase = 1
  533.         END IF
  534.         '''
  535.         IF (cornercase = 0) THEN
  536.  
  537.             numpoints = numpoints + 1
  538.  
  539.             ' Starting next to kmax, create a `gap' by shifting all other points by one index.
  540.             FOR j = numpoints TO kmax + 1 STEP -1
  541.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  542.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  543.             NEXT
  544.  
  545.             ' Fill the gap with a new point whose position is determined by the average of its neighbors.
  546.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  547.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  548.  
  549.         ELSE
  550.             numpoints = numpoints + 1
  551.             xx = PointChain(ShapeCount, numpoints - 1).x
  552.             yy = PointChain(ShapeCount, numpoints - 1).y
  553.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  554.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  555.         END IF
  556.  
  557.     LOOP
  558.  
  559.     ' At this stage, the curve still has all of its sharp edges. Use a `relaxation method' to smooth.
  560.     ' The new position of a point is equal to the average position of its neighboring points.
  561.     CALL SmoothShape(ShapeCount, numpoints, smoothiterations)
  562.  
  563.     Shape(ShapeCount).Elements = numpoints
  564.     Shape(ShapeCount).Mass = numpoints
  565.     Shape(ShapeCount).Velocity.x = 0
  566.     Shape(ShapeCount).Velocity.y = 0
  567.     Shape(ShapeCount).Omega = 0
  568.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  569.     CALL CalculateCtrMass(ShapeCount)
  570.     Shape(ShapeCount).CtrRot.x = Shape(ShapeCount).CtrMass.x
  571.     Shape(ShapeCount).CtrRot.y = Shape(ShapeCount).CtrMass.y
  572.     CALL CalculateDiameter(ShapeCount)
  573.     CALL CalculateMOI(ShapeCount)
  574.  
  575.  
  576. SUB SmoothShape (i AS INTEGER, n AS INTEGER, s AS INTEGER)
  577.     FOR j = 1 TO s
  578.         FOR k = 2 TO n - 1
  579.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  580.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  581.         NEXT
  582.         FOR k = 2 TO n - 1
  583.             PointChain(i, k).x = TempChain(i, k).x
  584.             PointChain(i, k).y = TempChain(i, k).y
  585.         NEXT
  586.     NEXT
  587.  
ss.png
* ss.png (Filesize: 12.03 KB, Dimensions: 800x599, Views: 121)
« Last Edit: February 23, 2020, 12:16:37 am 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
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #9 on: February 23, 2020, 02:36:28 am »
POOL GAME UPDATE

Kindof. Bear with me: This is a simulation of what happens when you dump a bowl of lucky charms with milk all over the table, and then shove the pieces around with a fidget spinner. Complete with keyboard controls and those early 90's sounds. Have a ball.

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

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.  
  7. ' Data
  8. TYPE Vector
  9.     x AS DOUBLE
  10.     y AS DOUBLE
  11.  
  12. TYPE Object
  13.     CtrMass AS Vector
  14.     CtrRot AS Vector
  15.     Diameter AS DOUBLE
  16.     Elements AS INTEGER
  17.     Mass AS DOUBLE
  18.     MOI AS DOUBLE
  19.     Omega AS DOUBLE
  20.     Shade AS _UNSIGNED LONG
  21.     Velocity AS Vector
  22.  
  23. DIM vtemp AS Vector
  24.  
  25. ' Statics
  26. DIM SHARED Shape(1000) AS Object
  27. DIM SHARED PointChain(1000, 5000) AS Vector
  28. DIM SHARED TempChain(1000, 5000) AS Vector
  29. DIM SHARED ShapeCount AS INTEGER
  30. ShapeCount = 0
  31.  
  32. ' Dynamics
  33. DIM SHARED CollisionCount AS INTEGER
  34. CollisionCount = 0
  35.  
  36. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  37. DIM SHARED ProximalPairsCount
  38.  
  39. DIM Gravity AS Vector
  40. Gravity.x = 0
  41. Gravity.y = -3
  42.  
  43. ' Main objecct
  44.  
  45. ShapeCount = ShapeCount + 1
  46. Shape(ShapeCount).CtrRot.x = -200
  47. Shape(ShapeCount).CtrRot.y = 0
  48. i = 0
  49. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  50.     i = i + 1
  51.     r = 40 + 25 * COS(1.5 * j) ^ 2
  52.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  53.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  54. Shape(ShapeCount).Elements = i
  55. Shape(ShapeCount).Mass = i
  56. Shape(ShapeCount).Velocity.x = 0
  57. Shape(ShapeCount).Velocity.y = 0
  58. Shape(ShapeCount).Omega = 0
  59. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  60. CALL CalculateCtrMass(ShapeCount)
  61. CALL CalculateDiameter(ShapeCount)
  62. CALL CalculateMOI(ShapeCount)
  63.  
  64. ' Balls
  65. x0 = 120
  66. y0 = 0
  67. rr = 22.5
  68. gg = 2 * rr + 20
  69. xf = COS(30 * 3.14159 / 180)
  70. yf = SIN(30 * 3.14159 / 180)
  71. gx = gg * xf
  72. gy = gg * yf
  73. CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, rr)
  74. CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, rr)
  75. CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, rr)
  76. CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, rr)
  77. CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, rr)
  78. CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, rr)
  79. CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, rr)
  80. CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, rr)
  81. CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, rr)
  82. CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, rr)
  83.  
  84. ' Walls
  85. px = 40
  86. py = 10
  87. x0 = 0
  88. y0 = -(_HEIGHT / 2) + 2 * py
  89. FOR n = -1 TO 1 STEP 2
  90.     FOR k = 0 TO ((_WIDTH / 2) - px) STEP px * 1.1
  91.         CALL NewAutoBrick(x0 + k, n * y0, px, py)
  92.         IF (k > 0) THEN CALL NewAutoBrick(x0 - k, n * y0, px, py)
  93.     NEXT
  94. px = 10
  95. py = 40
  96. x0 = -(_WIDTH) / 2 + 2 * px
  97. y0 = 0
  98. FOR n = -1 TO 1 STEP 2
  99.     FOR k = 0 TO ((_HEIGHT / 2)) - py STEP py * 1.1
  100.         CALL NewAutoBrick(n * x0, y0 + k, px, py)
  101.         IF (k > 0) THEN CALL NewAutoBrick(n * x0, y0 - k, px, py)
  102.     NEXT
  103.  
  104. CALL Graphics
  105. LOCATE 10, 15: PRINT "PRESS ANY KEY TO BEGIN"
  106. Shape(1).Velocity.x = 5
  107. Shape(1).Velocity.y = 1
  108. Shape(1).Omega = .1
  109.  
  110.  
  111.     kk = _KEYHIT
  112.     SELECT CASE kk
  113.         CASE 32
  114.             CLS
  115.             CALL Graphics
  116.             LOCATE 1, 1: PRINT "Click & Drag counter-clockwise to create a shape."
  117.             CALL NewMouseShape
  118.         CASE 18432 ' Up arrow
  119.             Shape(1).Velocity.y = Shape(1).Velocity.y + 5
  120.         CASE 20480 ' Down arrow
  121.             Shape(1).Velocity.y = Shape(1).Velocity.y - 5
  122.         CASE 19200 ' Left arrow
  123.             Shape(1).Velocity.x = Shape(1).Velocity.x - 5
  124.         CASE 19712 ' Right arrow
  125.             Shape(1).Velocity.x = Shape(1).Velocity.x + 5
  126.     END SELECT
  127.     IF (kk) THEN
  128.         _KEYCLEAR
  129.     END IF
  130.  
  131.     ' Proximity detection
  132.     ProximalPairsCount = 0
  133.     Shape1 = 0
  134.     Shape2 = 0
  135.     FOR j = 1 TO ShapeCount
  136.         FOR k = j + 1 TO ShapeCount
  137.             dx = Shape(j).CtrMass.x - Shape(k).CtrMass.x
  138.             dy = Shape(j).CtrMass.y - Shape(k).CtrMass.y
  139.             dr = SQR(dx * dx + dy * dy)
  140.             IF (dr < 1.15 * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  141.                 ProximalPairsCount = ProximalPairsCount + 1
  142.                 ProximalPairs(ProximalPairsCount, 1) = j
  143.                 ProximalPairs(ProximalPairsCount, 2) = k
  144.                 Shape1 = j
  145.                 Shape2 = k
  146.             END IF
  147.         NEXT
  148.     NEXT
  149.  
  150.     IF (ProximalPairsCount > 0) THEN
  151.         FOR n = 1 TO ProximalPairsCount
  152.             Shape1 = ProximalPairs(n, 1)
  153.             Shape2 = ProximalPairs(n, 2)
  154.  
  155.             ' Collision detection
  156.             rmin = 999 ^ 999
  157.             ClosestIndex1 = 0
  158.             ClosestIndex2 = 0
  159.             FOR j = 1 TO Shape(Shape1).Elements
  160.                 FOR k = 1 TO Shape(Shape2).Elements
  161.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  162.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  163.                     r2 = dx * dx + dy * dy
  164.                     IF (r2 < rmin) THEN
  165.                         rmin = r2
  166.                         ClosestIndex1 = j
  167.                         ClosestIndex2 = k
  168.                     END IF
  169.                 NEXT
  170.             NEXT
  171.  
  172.             IF (rmin <= 2) THEN
  173.                 CollisionCount = CollisionCount + 1
  174.  
  175.                 ' Normal vector 1
  176.                 xx = CalculateNormalY(Shape1, ClosestIndex1)
  177.                 yy = -CalculateNormalX(Shape1, ClosestIndex1)
  178.                 norm = SQR(xx * xx + yy * yy)
  179.                 xx = xx / norm
  180.                 yy = yy / norm
  181.                 nx1 = xx
  182.                 ny1 = yy
  183.  
  184.                 ' Normal vector 2
  185.                 xx = CalculateNormalY(Shape2, ClosestIndex2)
  186.                 yy = -CalculateNormalX(Shape2, ClosestIndex2)
  187.                 norm = SQR(xx * xx + yy * yy)
  188.                 xx = xx / norm
  189.                 yy = yy / norm
  190.                 nx2 = xx
  191.                 ny2 = yy
  192.  
  193.                 ' Perpendicular vector 1
  194.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).CtrRot.x
  195.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).CtrRot.y
  196.                 px1 = -dy1
  197.                 py1 = dx1
  198.                 p1 = SQR(px1 * px1 + py1 * py1)
  199.                 px1 = px1 / p1
  200.                 py1 = py1 / p1
  201.  
  202.                 ' Perpendicular vector 2
  203.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).CtrRot.x
  204.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).CtrRot.y
  205.                 px2 = -dy2
  206.                 py2 = dx2
  207.                 p2 = SQR(px2 * px2 + py2 * py2)
  208.                 px2 = px2 / p2
  209.                 py2 = py2 / p2
  210.  
  211.                 ' Angular velocity vector 1
  212.                 w1 = Shape(Shape1).Omega
  213.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  214.                 vx1 = w1 * r1 * px1
  215.                 vy1 = w1 * r1 * py1
  216.  
  217.                 ' Angular velocity vector 2
  218.                 w2 = Shape(Shape2).Omega
  219.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  220.                 vx2 = w2 * r2 * px2
  221.                 vy2 = w2 * r2 * py2
  222.  
  223.                 ' Mass terms
  224.                 m1 = Shape(Shape1).Mass
  225.                 i1 = Shape(Shape1).MOI
  226.                 m2 = Shape(Shape2).Mass
  227.                 i2 = Shape(Shape2).MOI
  228.                 mu = 1 / (1 / m1 + 1 / m2)
  229.  
  230.                 ' Velocity differential 1
  231.                 vtx1 = Shape(Shape1).Velocity.x + vx1
  232.                 vty1 = Shape(Shape1).Velocity.y + vy1
  233.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  234.  
  235.                 ' Velocity differential 2
  236.                 vtx2 = Shape(Shape2).Velocity.x + vx2
  237.                 vty2 = Shape(Shape2).Velocity.y + vy2
  238.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  239.  
  240.                 ' Velocity differential total
  241.                 dvtx = vtx2 - vtx1
  242.                 dvty = vty2 - vty1
  243.  
  244.                 ' Geometry
  245.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  246.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  247.  
  248.                 ' Momentum exchange
  249.                 qx1 = nx1 * 2 * mu * n1dotdvt
  250.                 qy1 = ny1 * 2 * mu * n1dotdvt
  251.                 qx2 = nx2 * 2 * mu * n2dotdvt
  252.                 qy2 = ny2 * 2 * mu * n2dotdvt
  253.  
  254.                 ' Momentum exchange unit vector
  255.                 q1 = SQR(qx1 * qx1 + qy1 * qy1)
  256.                 qhatx1 = qx1 / q1
  257.                 qhaty1 = qy1 / q1
  258.                 q2 = SQR(qx2 * qx2 + qy2 * qy2)
  259.                 qhatx2 = qx2 / q2
  260.                 qhaty2 = qy2 / q2
  261.  
  262.                 ' Undo previous motion
  263.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, -Shape(Shape1).Omega)
  264.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, -Shape(Shape2).Omega)
  265.                 vtemp.x = -Shape(Shape1).Velocity.x
  266.                 vtemp.y = -Shape(Shape1).Velocity.y
  267.                 CALL TranslateShape(Shape1, vtemp)
  268.                 vtemp.x = -Shape(Shape2).Velocity.x
  269.                 vtemp.y = -Shape(Shape2).Velocity.y
  270.                 CALL TranslateShape(Shape2, vtemp)
  271.  
  272.                 ' Separate along normal
  273.                 vtemp.x = -nx1 * ((v1 + v1 / m1) + .01)
  274.                 vtemp.y = -ny1 * ((v1 + v1 / m1) + .01)
  275.                 CALL TranslateShape(Shape1, vtemp)
  276.                 vtemp.x = -nx2 * ((v2 + v2 / m2) + .01)
  277.                 vtemp.y = -ny2 * ((v2 + v2 / m2) + .01)
  278.                 CALL TranslateShape(Shape2, vtemp)
  279.  
  280.                 ' Translational impulse
  281.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  282.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  283.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  284.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  285.                 f1 = -q1dotn1 * n1dotr1hat
  286.                 f2 = -q2dotn2 * n2dotr2hat
  287.                 Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x + f1 * qx1 / m1
  288.                 Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y + f1 * qy1 / m1
  289.                 Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x + f2 * qx2 / m2
  290.                 Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y + f2 * qy2 / m2
  291.  
  292.                 ' Angular impulse
  293.                 q1dotp1 = qx1 * px1 + qy1 * py1
  294.                 q2dotp2 = qx2 * px2 + qy2 * py2
  295.                 dw1 = r1 * q1dotp1 / i1
  296.                 dw2 = -r2 * q2dotp2 / i2
  297.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, dw1)
  298.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, dw2)
  299.                 Shape(Shape1).Omega = Shape(Shape1).Omega + dw1
  300.                 Shape(Shape2).Omega = Shape(Shape2).Omega + dw2
  301.  
  302.                 ' Dent
  303.                 'PointChain(Shape1, ClosestIndex1).x = PointChain(Shape1, ClosestIndex1).x - v1 * nx1
  304.                 'PointChain(Shape1, ClosestIndex1).y = PointChain(Shape1, ClosestIndex1).y - v1 * ny1
  305.                 'PointChain(Shape2, ClosestIndex2).x = PointChain(Shape2, ClosestIndex2).x - v2 * nx2
  306.                 'PointChain(Shape2, ClosestIndex2).y = PointChain(Shape2, ClosestIndex2).y - v2 * ny2
  307.                 'CALL SmoothShape(Shape1, Shape(Shape1).Elements, 1)
  308.                 'CALL SmoothShape(Shape2, Shape(Shape2).Elements, 1)
  309.  
  310.                 ' Static friction
  311.                 IF (Shape(Shape1).Velocity.x * Shape(Shape1).Velocity.x < .075) THEN Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * .001
  312.                 IF (Shape(Shape1).Velocity.y * Shape(Shape1).Velocity.y < .075) THEN Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * .001
  313.                 IF (Shape(Shape2).Velocity.x * Shape(Shape2).Velocity.x < .075) THEN Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * .001
  314.                 IF (Shape(Shape2).Velocity.y * Shape(Shape2).Velocity.y < .075) THEN Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * .001
  315.                 'IF (Shape(Shape1).Omega * Shape(Shape1).Omega < .0001) THEN Shape(Shape1).Omega = Shape(Shape1).Omega * .01
  316.                 'IF (Shape(Shape2).Omega * Shape(Shape2).Omega < .0001) THEN Shape(Shape2).Omega = Shape(Shape2).Omega * .01
  317.  
  318.                 CALL snd(100 * (v1 + v2) / 2, 1)
  319.  
  320.             END IF
  321.         NEXT
  322.     END IF
  323.  
  324.     FOR ShapeIndex = 1 TO ShapeCount
  325.         dx = Shape(ShapeIndex).CtrMass.x - Shape(ShapeIndex).CtrRot.x
  326.         dy = Shape(ShapeIndex).CtrMass.y - Shape(ShapeIndex).CtrRot.y
  327.  
  328.         cx = Shape(ShapeIndex).CtrMass.x
  329.         cy = Shape(ShapeIndex).CtrMass.y
  330.         'Gravity.x = -(cx) / 20
  331.         'Gravity.y = -(cy) / 20
  332.  
  333.         'torque = dx * Gravity.y - dy * Gravity.x
  334.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + torque / Shape(ShapeIndex).MOI
  335.  
  336.         ' Rotation update
  337.         CALL RotShape(ShapeIndex, Shape(ShapeIndex).CtrRot, Shape(ShapeIndex).Omega)
  338.  
  339.         ' Velocity update
  340.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + Gravity.x / Shape(ShapeIndex).Mass
  341.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + Gravity.y / Shape(ShapeIndex).Mass
  342.         CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  343.  
  344.         ' Damping
  345.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .995
  346.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .995
  347.         Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .995
  348.  
  349.     NEXT
  350.  
  351.     CALL Graphics
  352.     _LIMIT 60
  353.  
  354.  
  355. SUB Graphics
  356.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  357.     FOR ShapeIndex = 1 TO ShapeCount
  358.         LOCATE 1, 1: PRINT "Tap arrow keys (don't hold)."
  359.         LOCATE 2, 1: PRINT ProximalPairsCount, CollisionCount
  360.         CALL ccircle(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, 3, Shape(ShapeIndex).Shade)
  361.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  362.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  363.             'CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  364.         NEXT
  365.         CALL cline(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  366.         'CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  367.     NEXT
  368.     _DISPLAY
  369.  
  370. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  371.     DIM l AS Vector
  372.     DIM r AS Vector
  373.     li = i - 1
  374.     ri = i + 1
  375.     IF (i = 1) THEN li = Shape(k).Elements
  376.     IF (i = Shape(k).Elements) THEN ri = 1
  377.     l.x = PointChain(k, li).x
  378.     r.x = PointChain(k, ri).x
  379.     dx = r.x - l.x
  380.     CalculateNormalX = dx
  381.  
  382. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  383.     DIM l AS Vector
  384.     DIM r AS Vector
  385.     li = i - 1
  386.     ri = i + 1
  387.     IF (i = 1) THEN li = Shape(k).Elements
  388.     IF (i = Shape(k).Elements) THEN ri = 1
  389.     l.y = PointChain(k, li).y
  390.     r.y = PointChain(k, ri).y
  391.     dy = r.y - l.y
  392.     CalculateNormalY = dy
  393.  
  394. SUB CalculateCtrMass (k AS INTEGER)
  395.     xx = 0
  396.     yy = 0
  397.     FOR i = 1 TO Shape(k).Elements
  398.         xx = xx + PointChain(k, i).x
  399.         yy = yy + PointChain(k, i).y
  400.     NEXT
  401.     Shape(k).CtrMass.x = xx / Shape(k).Elements
  402.     Shape(k).CtrMass.y = yy / Shape(k).Elements
  403.  
  404. SUB CalculateDiameter (k AS INTEGER)
  405.     r2max = -1
  406.     FOR i = 1 TO Shape(k).Elements
  407.         xx = Shape(k).CtrMass.x - PointChain(k, i).x
  408.         yy = Shape(k).CtrMass.y - PointChain(k, i).y
  409.         r2 = xx * xx + yy * yy
  410.         IF (r2 > r2max) THEN
  411.             r2max = r2
  412.         END IF
  413.     NEXT
  414.     Shape(k).Diameter = SQR(r2max)
  415.  
  416. SUB CalculateMOI (k AS INTEGER)
  417.     xx = 0
  418.     yy = 0
  419.     FOR i = 1 TO Shape(k).Elements
  420.         xx = xx + (Shape(k).CtrRot.x - PointChain(k, i).x) ^ 2
  421.         yy = yy + (Shape(k).CtrRot.y - PointChain(k, i).y) ^ 2
  422.     NEXT
  423.     Shape(k).MOI = (xx + yy) * (Shape(k).Mass / Shape(k).Elements)
  424.  
  425. SUB TranslateShape (k AS INTEGER, c AS Vector)
  426.     FOR i = 1 TO Shape(k).Elements
  427.         PointChain(k, i).x = PointChain(k, i).x + c.x
  428.         PointChain(k, i).y = PointChain(k, i).y + c.y
  429.     NEXT
  430.     Shape(k).CtrRot.x = Shape(k).CtrRot.x + c.x
  431.     Shape(k).CtrRot.y = Shape(k).CtrRot.y + c.y
  432.     Shape(k).CtrMass.x = Shape(k).CtrMass.x + c.x
  433.     Shape(k).CtrMass.y = Shape(k).CtrMass.y + c.y
  434.  
  435. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  436.     FOR i = 1 TO Shape(k).Elements
  437.         xx = PointChain(k, i).x - c.x
  438.         yy = PointChain(k, i).y - c.y
  439.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  440.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  441.     NEXT
  442.     xx = Shape(k).CtrMass.x - c.x
  443.     yy = Shape(k).CtrMass.y - c.y
  444.     Shape(k).CtrMass.x = c.x + xx * COS(da) - yy * SIN(da)
  445.     Shape(k).CtrMass.y = c.y + yy * COS(da) + xx * SIN(da)
  446.  
  447. SUB NewAutoBall (x1, y1, r1)
  448.     pa = INT(RND * 3) + 1
  449.     pb = INT(RND * 2) + 1
  450.     ShapeCount = ShapeCount + 1
  451.     Shape(ShapeCount).CtrRot.x = x1
  452.     Shape(ShapeCount).CtrRot.y = y1
  453.     i = 0
  454.     FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  455.         i = i + 1
  456.         r = r1 + 5 * COS(pa * j) ^ pb
  457.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  458.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  459.     NEXT
  460.     Shape(ShapeCount).Elements = i
  461.     Shape(ShapeCount).Mass = i
  462.     Shape(ShapeCount).Velocity.x = 0
  463.     Shape(ShapeCount).Velocity.y = 0
  464.     Shape(ShapeCount).Omega = 0
  465.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  466.     CALL CalculateCtrMass(ShapeCount)
  467.     CALL CalculateDiameter(ShapeCount)
  468.     CALL CalculateMOI(ShapeCount)
  469.  
  470. SUB NewAutoBrick (x1, y1, wx, wy)
  471.     ShapeCount = ShapeCount + 1
  472.     Shape(ShapeCount).CtrRot.x = x1
  473.     Shape(ShapeCount).CtrRot.y = y1
  474.     i = 0
  475.     FOR j = -wy / 2 TO wy / 2 STEP 2
  476.         i = i + 1
  477.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + wx / 2
  478.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + j
  479.     NEXT
  480.     FOR j = wx / 2 TO -wx / 2 STEP -2
  481.         i = i + 1
  482.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + j
  483.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + wy / 2
  484.     NEXT
  485.     FOR j = wy / 2 TO -wy / 2 STEP -2
  486.         i = i + 1
  487.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x - wx / 2
  488.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + j
  489.     NEXT
  490.     FOR j = -wx / 2 TO wx / 2 STEP 2
  491.         i = i + 1
  492.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + j
  493.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y - wy / 2
  494.     NEXT
  495.     Shape(ShapeCount).Elements = i
  496.     Shape(ShapeCount).Mass = 999 ^ 999
  497.     Shape(ShapeCount).Velocity.x = 0
  498.     Shape(ShapeCount).Velocity.y = 0
  499.     Shape(ShapeCount).Omega = 0
  500.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  501.     CALL CalculateCtrMass(ShapeCount)
  502.     CALL CalculateDiameter(ShapeCount)
  503.     CALL CalculateMOI(ShapeCount)
  504.     Shape(ShapeCount).MOI = 999 ^ 999
  505.  
  506.  
  507. SUB NewMouseShape
  508.     rawresolution = 7.5 ' Raw curve resolution.
  509.     targetpoints = 120 ' Number of points per curve.
  510.     smoothiterations = 15 ' Magnitude of smooth effect.
  511.  
  512.     ShapeCount = ShapeCount + 1
  513.  
  514.     numpoints = 0
  515.     xold = 999999
  516.     yold = 999999
  517.  
  518.  
  519.     ' Gather raw data for one curve at a time.
  520.     ' Click+drag mouse button 1 to trace out a curve.
  521.     DO
  522.         DO WHILE _MOUSEINPUT
  523.             x = _MOUSEX
  524.             y = _MOUSEY
  525.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  526.                 IF _MOUSEBUTTON(1) THEN
  527.                     x = x - (_WIDTH / 2)
  528.                     y = -y + (_HEIGHT / 2)
  529.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  530.  
  531.                     ' Collect data only if the new point is sufficiently far away from the previous point.
  532.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  533.                         numpoints = numpoints + 1
  534.                         PointChain(ShapeCount, numpoints).x = x
  535.                         PointChain(ShapeCount, numpoints).y = y
  536.                         CALL cpset(x, y, _RGB(255, 255, 255))
  537.                         xold = x
  538.                         yold = y
  539.                     END IF
  540.                 END IF
  541.             END IF
  542.         LOOP
  543.         _DISPLAY
  544.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  545.  
  546.     ' If the curve contains less than the minimum numer of points, use interpolation to fill in the gaps.
  547.     DO WHILE (numpoints < targetpoints)
  548.  
  549.         ' Determine the pair of neighboring points that have the greatest separation of all pairs.
  550.         rad2max = -1
  551.         kmax = -1
  552.         FOR k = 1 TO numpoints - 1
  553.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  554.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  555.             rad2 = xfac ^ 2 + yfac ^ 2
  556.             IF rad2 > rad2max THEN
  557.                 kmax = k
  558.                 rad2max = rad2
  559.             END IF
  560.         NEXT
  561.         '''
  562.         cornercase = 0
  563.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  564.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  565.         rad2 = xfac ^ 2 + yfac ^ 2
  566.         IF rad2 > rad2max THEN
  567.             kmax = numpoints
  568.             rad2max = rad2
  569.             cornercase = 1
  570.         END IF
  571.         '''
  572.         IF (cornercase = 0) THEN
  573.  
  574.             numpoints = numpoints + 1
  575.  
  576.             ' Starting next to kmax, create a `gap' by shifting all other points by one index.
  577.             FOR j = numpoints TO kmax + 1 STEP -1
  578.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  579.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  580.             NEXT
  581.  
  582.             ' Fill the gap with a new point whose position is determined by the average of its neighbors.
  583.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  584.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  585.  
  586.         ELSE
  587.             numpoints = numpoints + 1
  588.             xx = PointChain(ShapeCount, numpoints - 1).x
  589.             yy = PointChain(ShapeCount, numpoints - 1).y
  590.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  591.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  592.         END IF
  593.  
  594.     LOOP
  595.  
  596.     ' At this stage, the curve still has all of its sharp edges. Use a `relaxation method' to smooth.
  597.     ' The new position of a point is equal to the average position of its neighboring points.
  598.     CALL SmoothShape(ShapeCount, numpoints, smoothiterations)
  599.  
  600.     Shape(ShapeCount).Elements = numpoints
  601.     Shape(ShapeCount).Mass = numpoints
  602.     Shape(ShapeCount).Velocity.x = 0
  603.     Shape(ShapeCount).Velocity.y = 0
  604.     Shape(ShapeCount).Omega = 0
  605.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  606.     CALL CalculateCtrMass(ShapeCount)
  607.     Shape(ShapeCount).CtrRot.x = Shape(ShapeCount).CtrMass.x
  608.     Shape(ShapeCount).CtrRot.y = Shape(ShapeCount).CtrMass.y
  609.     CALL CalculateDiameter(ShapeCount)
  610.     CALL CalculateMOI(ShapeCount)
  611.  
  612.  
  613. SUB SmoothShape (i AS INTEGER, n AS INTEGER, s AS INTEGER)
  614.     FOR j = 1 TO s
  615.         FOR k = 2 TO n - 1
  616.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  617.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  618.         NEXT
  619.         FOR k = 2 TO n - 1
  620.             PointChain(i, k).x = TempChain(i, k).x
  621.             PointChain(i, k).y = TempChain(i, k).y
  622.         NEXT
  623.     NEXT
  624.  
  625. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  626.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  627.  
  628. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  629.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  630.  
  631. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  632.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  633.  
  634. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  635.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  636.  
  637. SUB snd (frq, dur)
  638.     IF ((frq >= 37) AND (frq <= 32767 - 25000)) THEN
  639.         SOUND frq, dur
  640.     END IF
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #11 on: February 23, 2020, 11:59:57 am »
POOL GAME UPDATE
 
STxAxTIC Pool.PNG


LOL Man what a gas! I mean it even works, sorta ;-))
« Last Edit: February 23, 2020, 12:11:57 pm by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #12 on: February 23, 2020, 12:36:24 pm »
Thanks for trying it bplus. This is moving so fast, I'm embarrassed by how the code was X-1 hours ago for any given X. This version is the cutting edge:

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.  
  7. ' Data
  8. TYPE Vector
  9.     x AS DOUBLE
  10.     y AS DOUBLE
  11.  
  12. TYPE Object
  13.     CtrMass AS Vector
  14.     CtrRot AS Vector
  15.     Diameter AS DOUBLE
  16.     Elements AS INTEGER
  17.     Mass AS DOUBLE
  18.     MOI AS DOUBLE
  19.     Omega AS DOUBLE
  20.     Shade AS _UNSIGNED LONG
  21.     Velocity AS Vector
  22.  
  23. DIM vtemp AS Vector
  24.  
  25. ' Statics
  26. DIM SHARED Shape(1000) AS Object
  27. DIM SHARED PointChain(1000, 5000) AS Vector
  28. DIM SHARED TempChain(1000, 5000) AS Vector
  29. DIM SHARED ShapeCount AS INTEGER
  30. ShapeCount = 0
  31.  
  32. ' Dynamics
  33. DIM SHARED CollisionCount AS INTEGER
  34. CollisionCount = 0
  35. DIM SHARED ProximalPairs(1000 / 2, 1 TO 2)
  36. DIM SHARED ProximalPairsCount
  37.  
  38. ' Environment
  39. DIM ForceField AS Vector
  40. ForceField.x = 0
  41. ForceField.y = 0 '-8
  42.  
  43. ' Main objecct(s)
  44. ShapeCount = ShapeCount + 1
  45. Shape(ShapeCount).CtrRot.x = -200
  46. Shape(ShapeCount).CtrRot.y = 0
  47. i = 0
  48. FOR j = 0 TO 2 * 4 * ATN(1) STEP .05
  49.     i = i + 1
  50.     r = 40 + 25 * COS(1.5 * j) ^ 2
  51.     PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  52.     PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  53. Shape(ShapeCount).Elements = i
  54. Shape(ShapeCount).Mass = i
  55. Shape(ShapeCount).Velocity.x = 0
  56. Shape(ShapeCount).Velocity.y = 0
  57. Shape(ShapeCount).Omega = 0
  58. Shape(ShapeCount).Shade = _RGB(0, 255, 255)
  59. CALL CalculateCtrMass(ShapeCount)
  60. CALL CalculateDiameter(ShapeCount)
  61. CALL CalculateMOI(ShapeCount)
  62.  
  63. ' Balls
  64. x0 = 120
  65. y0 = 0
  66. rr = 22.5
  67. gg = 2 * rr + 20
  68. xf = COS(30 * 3.14159 / 180)
  69. yf = SIN(30 * 3.14159 / 180)
  70. gx = gg * xf
  71. gy = gg * yf
  72. CALL NewAutoBall(x0 + 0 * gx, y0 + 0 * gy, rr)
  73. CALL NewAutoBall(x0 + 1 * gx, y0 + 1 * gy, rr)
  74. CALL NewAutoBall(x0 + 1 * gx, y0 - 1 * gy, rr)
  75. CALL NewAutoBall(x0 + 2 * gx, y0 + 2 * gy, rr)
  76. CALL NewAutoBall(x0 + 2 * gx, y0 + 0 * gy, rr)
  77. CALL NewAutoBall(x0 + 2 * gx, y0 - 2 * gy, rr)
  78. CALL NewAutoBall(x0 + 3 * gx, y0 + 3 * gy, rr)
  79. CALL NewAutoBall(x0 + 3 * gx, y0 + 1 * gy, rr)
  80. CALL NewAutoBall(x0 + 3 * gx, y0 - 1 * gy, rr)
  81. CALL NewAutoBall(x0 + 3 * gx, y0 - 3 * gy, rr)
  82.  
  83. ' Walls
  84. px = 40
  85. py = 10
  86. x0 = 0
  87. y0 = -(_HEIGHT / 2) + 2 * py
  88. FOR n = -1 TO 1 STEP 2
  89.     FOR k = 0 TO ((_WIDTH / 2) - px) STEP px * 1.1
  90.         CALL NewAutoBrick(x0 + k, n * y0, px, py, 0)
  91.         IF (k > 0) THEN CALL NewAutoBrick(x0 - k, n * y0, px, py, 0)
  92.     NEXT
  93. px = 10
  94. py = 40
  95. x0 = -(_WIDTH) / 2 + 2 * px
  96. y0 = 0
  97. FOR n = -1 TO 1 STEP 2
  98.     FOR k = 0 TO ((_HEIGHT / 2)) - py STEP py * 1.1
  99.         CALL NewAutoBrick(n * x0, y0 + k, px, py, 0)
  100.         IF (k > 0) THEN CALL NewAutoBrick(n * x0, y0 - k, px, py, 0)
  101.     NEXT
  102.  
  103. ' Initial state
  104. FOR k = 1 TO ShapeCount
  105.     Shape(k).Velocity.x = -500 * RND / Shape(k).Mass
  106.     Shape(k).Velocity.y = 0
  107.     Shape(k).Omega = 5000 * RND / Shape(k).MOI
  108.  
  109. Shape(1).Velocity.x = 4
  110. Shape(1).Velocity.y = 0
  111. Shape(1).Omega = .1
  112. CALL Graphics
  113.  
  114. ' Prompt
  115. LOCATE 10, 15: PRINT "PRESS ANY KEY TO BEGIN"
  116.  
  117. ' Main loop
  118.  
  119.     kk = _KEYHIT
  120.     SELECT CASE kk
  121.         CASE 32
  122.             CLS
  123.             CALL Graphics
  124.             LOCATE 1, 1: PRINT "Click & Drag counter-clockwise to create a shape.                                 "
  125.             LOCATE 2, 1: PRINT "                                                                                  "
  126.             CALL NewMouseShape
  127.         CASE 18432 ' Up arrow
  128.             Shape(1).Velocity.y = Shape(1).Velocity.y + 3
  129.         CASE 20480 ' Down arrow
  130.             Shape(1).Velocity.y = Shape(1).Velocity.y - 3
  131.         CASE 19200 ' Left arrow
  132.             Shape(1).Velocity.x = Shape(1).Velocity.x - 3
  133.         CASE 19712 ' Right arrow
  134.             Shape(1).Velocity.x = Shape(1).Velocity.x + 3
  135.     END SELECT
  136.     IF (kk) THEN
  137.         _KEYCLEAR
  138.     END IF
  139.  
  140.     mb = 0
  141.         x = _MOUSEX
  142.         y = _MOUSEY
  143.         IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  144.             IF (_MOUSEBUTTON(1)) THEN
  145.                 IF (mb = 0) THEN
  146.                     mb = 1
  147.                     x = x - (_WIDTH / 2)
  148.                     y = -y + (_HEIGHT / 2)
  149.                     CALL NewAutoBrick(x, y, 40, 10, 0)
  150.                     _DELAY .1
  151.                 END IF
  152.             END IF
  153.             IF (_MOUSEBUTTON(2)) THEN
  154.                 IF (mb = 0) THEN
  155.                     mb = 1
  156.                     x = x - (_WIDTH / 2)
  157.                     y = -y + (_HEIGHT / 2)
  158.                     CALL NewAutoBrick(x, y, 10, 40, 0)
  159.                     _DELAY .1
  160.                 END IF
  161.             END IF
  162.         END IF
  163.     LOOP
  164.  
  165.     ' Proximity detection
  166.     ProximalPairsCount = 0
  167.     Shape1 = 0
  168.     Shape2 = 0
  169.     FOR j = 1 TO ShapeCount
  170.         FOR k = j + 1 TO ShapeCount
  171.             dx = Shape(j).CtrMass.x - Shape(k).CtrMass.x
  172.             dy = Shape(j).CtrMass.y - Shape(k).CtrMass.y
  173.             dr = SQR(dx * dx + dy * dy)
  174.             IF (dr < 1.15 * (Shape(j).Diameter + Shape(k).Diameter)) THEN
  175.                 ProximalPairsCount = ProximalPairsCount + 1
  176.                 ProximalPairs(ProximalPairsCount, 1) = j
  177.                 ProximalPairs(ProximalPairsCount, 2) = k
  178.                 Shape1 = j
  179.                 Shape2 = k
  180.             END IF
  181.         NEXT
  182.     NEXT
  183.  
  184.     IF (ProximalPairsCount > 0) THEN
  185.         FOR n = 1 TO ProximalPairsCount
  186.             Shape1 = ProximalPairs(n, 1)
  187.             Shape2 = ProximalPairs(n, 2)
  188.  
  189.             ' Collision detection
  190.             rmin = 999 ^ 999
  191.             ClosestIndex1 = 0
  192.             ClosestIndex2 = 0
  193.             FOR j = 1 TO Shape(Shape1).Elements
  194.                 FOR k = 1 TO Shape(Shape2).Elements
  195.                     dx = PointChain(Shape1, j).x - PointChain(Shape2, k).x
  196.                     dy = PointChain(Shape1, j).y - PointChain(Shape2, k).y
  197.                     r2 = dx * dx + dy * dy
  198.                     IF (r2 < rmin) THEN
  199.                         rmin = r2
  200.                         ClosestIndex1 = j
  201.                         ClosestIndex2 = k
  202.                     END IF
  203.                 NEXT
  204.             NEXT
  205.  
  206.             IF (rmin <= 4) THEN
  207.                 CollisionCount = CollisionCount + 1
  208.  
  209.                 ' Normal vector 1
  210.                 xx = CalculateNormalY(Shape1, ClosestIndex1)
  211.                 yy = -CalculateNormalX(Shape1, ClosestIndex1)
  212.                 norm = SQR(xx * xx + yy * yy)
  213.                 xx = xx / norm
  214.                 yy = yy / norm
  215.                 nx1 = xx
  216.                 ny1 = yy
  217.  
  218.                 ' Normal vector 2
  219.                 xx = CalculateNormalY(Shape2, ClosestIndex2)
  220.                 yy = -CalculateNormalX(Shape2, ClosestIndex2)
  221.                 norm = SQR(xx * xx + yy * yy)
  222.                 xx = xx / norm
  223.                 yy = yy / norm
  224.                 nx2 = xx
  225.                 ny2 = yy
  226.  
  227.                 ' Perpendicular vector 1
  228.                 dx1 = PointChain(Shape1, ClosestIndex1).x - Shape(Shape1).CtrRot.x
  229.                 dy1 = PointChain(Shape1, ClosestIndex1).y - Shape(Shape1).CtrRot.y
  230.                 px1 = -dy1
  231.                 py1 = dx1
  232.                 p1 = SQR(px1 * px1 + py1 * py1)
  233.                 px1 = px1 / p1
  234.                 py1 = py1 / p1
  235.  
  236.                 ' Perpendicular vector 2
  237.                 dx2 = PointChain(Shape2, ClosestIndex2).x - Shape(Shape2).CtrRot.x
  238.                 dy2 = PointChain(Shape2, ClosestIndex2).y - Shape(Shape2).CtrRot.y
  239.                 px2 = -dy2
  240.                 py2 = dx2
  241.                 p2 = SQR(px2 * px2 + py2 * py2)
  242.                 px2 = px2 / p2
  243.                 py2 = py2 / p2
  244.  
  245.                 ' Angular velocity vector 1
  246.                 w1 = Shape(Shape1).Omega
  247.                 r1 = SQR(dx1 * dx1 + dy1 * dy1)
  248.                 vx1 = w1 * r1 * px1
  249.                 vy1 = w1 * r1 * py1
  250.  
  251.                 ' Angular velocity vector 2
  252.                 w2 = Shape(Shape2).Omega
  253.                 r2 = SQR(dx2 * dx2 + dy2 * dy2)
  254.                 vx2 = w2 * r2 * px2
  255.                 vy2 = w2 * r2 * py2
  256.  
  257.                 ' Mass terms
  258.                 m1 = Shape(Shape1).Mass
  259.                 i1 = Shape(Shape1).MOI
  260.                 m2 = Shape(Shape2).Mass
  261.                 i2 = Shape(Shape2).MOI
  262.                 mu = 1 / (1 / m1 + 1 / m2)
  263.  
  264.                 ' Velocity differential 1
  265.                 vtx1 = Shape(Shape1).Velocity.x + vx1
  266.                 vty1 = Shape(Shape1).Velocity.y + vy1
  267.                 v1 = SQR(vtx1 * vtx1 + vty1 * vty1)
  268.  
  269.                 ' Velocity differential 2
  270.                 vtx2 = Shape(Shape2).Velocity.x + vx2
  271.                 vty2 = Shape(Shape2).Velocity.y + vy2
  272.                 v2 = SQR(vtx2 * vtx2 + vty2 * vty2)
  273.  
  274.                 ' Velocity differential total
  275.                 dvtx = vtx2 - vtx1
  276.                 dvty = vty2 - vty1
  277.  
  278.                 ' Geometry
  279.                 n1dotdvt = nx1 * dvtx + ny1 * dvty
  280.                 n2dotdvt = nx2 * dvtx + ny2 * dvty
  281.  
  282.                 ' Momentum exchange
  283.                 qx1 = nx1 * 2 * mu * n1dotdvt
  284.                 qy1 = ny1 * 2 * mu * n1dotdvt
  285.                 qx2 = nx2 * 2 * mu * n2dotdvt
  286.                 qy2 = ny2 * 2 * mu * n2dotdvt
  287.  
  288.                 ' Momentum exchange unit vector
  289.                 q1 = SQR(qx1 * qx1 + qy1 * qy1)
  290.                 qhatx1 = qx1 / q1
  291.                 qhaty1 = qy1 / q1
  292.                 q2 = SQR(qx2 * qx2 + qy2 * qy2)
  293.                 qhatx2 = qx2 / q2
  294.                 qhaty2 = qy2 / q2
  295.  
  296.                 ' Undo previous motion
  297.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, -Shape(Shape1).Omega)
  298.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, -Shape(Shape2).Omega)
  299.                 vtemp.x = -Shape(Shape1).Velocity.x
  300.                 vtemp.y = -Shape(Shape1).Velocity.y
  301.                 CALL TranslateShape(Shape1, vtemp)
  302.                 vtemp.x = -Shape(Shape2).Velocity.x
  303.                 vtemp.y = -Shape(Shape2).Velocity.y
  304.                 CALL TranslateShape(Shape2, vtemp)
  305.  
  306.                 ' Separate along normal
  307.                 vtemp.x = -nx1 * (.5 * v1 + .5 / m1)
  308.                 vtemp.y = -ny1 * (.5 * v1 + .5 / m1)
  309.                 CALL TranslateShape(Shape1, vtemp)
  310.                 vtemp.x = -nx2 * (.5 * v2 + .5 / m2)
  311.                 vtemp.y = -ny2 * (.5 * v2 + .5 / m2)
  312.                 CALL TranslateShape(Shape2, vtemp)
  313.  
  314.                 ' Translational impulse
  315.                 q1dotn1 = qhatx1 * nx1 + qhaty1 * ny1
  316.                 q2dotn2 = qhatx2 * nx2 + qhaty2 * ny2
  317.                 n1dotr1hat = (nx1 * dx1 + ny1 * dy1) / r1
  318.                 n2dotr2hat = (nx2 * dx2 + ny2 * dy2) / r2
  319.                 f1 = -q1dotn1 * n1dotr1hat
  320.                 f2 = -q2dotn2 * n2dotr2hat
  321.                 Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x + f1 * qx1 / m1
  322.                 Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y + f1 * qy1 / m1
  323.                 Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x + f2 * qx2 / m2
  324.                 Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y + f2 * qy2 / m2
  325.  
  326.                 ' Angular impulse
  327.                 q1dotp1 = qx1 * px1 + qy1 * py1
  328.                 q2dotp2 = qx2 * px2 + qy2 * py2
  329.                 dw1 = r1 * q1dotp1 / i1
  330.                 dw2 = -r2 * q2dotp2 / i2
  331.                 CALL RotShape(Shape1, Shape(Shape1).CtrRot, dw1)
  332.                 CALL RotShape(Shape2, Shape(Shape2).CtrRot, dw2)
  333.                 Shape(Shape1).Omega = Shape(Shape1).Omega + dw1
  334.                 Shape(Shape2).Omega = Shape(Shape2).Omega + dw2
  335.  
  336.                 ' Restitution
  337.                 Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .9
  338.                 Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .9
  339.                 Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .9
  340.  
  341.                 ' Dent
  342.                 'PointChain(Shape1, ClosestIndex1).x = PointChain(Shape1, ClosestIndex1).x - v1 * nx1
  343.                 'PointChain(Shape1, ClosestIndex1).y = PointChain(Shape1, ClosestIndex1).y - v1 * ny1
  344.                 'PointChain(Shape2, ClosestIndex2).x = PointChain(Shape2, ClosestIndex2).x - v2 * nx2
  345.                 'PointChain(Shape2, ClosestIndex2).y = PointChain(Shape2, ClosestIndex2).y - v2 * ny2
  346.                 'CALL SmoothShape(Shape1, Shape(Shape1).Elements, 1)
  347.                 'CALL SmoothShape(Shape2, Shape(Shape2).Elements, 1)
  348.  
  349.                 ' Static friction
  350.                 IF (Shape(Shape1).Velocity.x * Shape(Shape1).Velocity.x < .075) THEN Shape(Shape1).Velocity.x = Shape(Shape1).Velocity.x * .001
  351.                 IF (Shape(Shape1).Velocity.y * Shape(Shape1).Velocity.y < .075) THEN Shape(Shape1).Velocity.y = Shape(Shape1).Velocity.y * .001
  352.                 IF (Shape(Shape2).Velocity.x * Shape(Shape2).Velocity.x < .075) THEN Shape(Shape2).Velocity.x = Shape(Shape2).Velocity.x * .001
  353.                 IF (Shape(Shape2).Velocity.y * Shape(Shape2).Velocity.y < .075) THEN Shape(Shape2).Velocity.y = Shape(Shape2).Velocity.y * .001
  354.                 IF (Shape(Shape1).Omega * Shape(Shape1).Omega < .0001) THEN Shape(Shape1).Omega = Shape(Shape1).Omega * .01
  355.                 IF (Shape(Shape2).Omega * Shape(Shape2).Omega < .0001) THEN Shape(Shape2).Omega = Shape(Shape2).Omega * .01
  356.  
  357.                 CALL snd(100 * (v1 + v2) / 2, 1)
  358.  
  359.             END IF
  360.         NEXT
  361.     END IF
  362.  
  363.     FOR ShapeIndex = 1 TO ShapeCount
  364.         dx = Shape(ShapeIndex).CtrMass.x - Shape(ShapeIndex).CtrRot.x
  365.         dy = Shape(ShapeIndex).CtrMass.y - Shape(ShapeIndex).CtrRot.y
  366.  
  367.         cx = Shape(ShapeIndex).CtrMass.x
  368.         cy = Shape(ShapeIndex).CtrMass.y
  369.         'ForceField.x = -cx / 20
  370.         'ForceField.y = -cy / 20
  371.  
  372.         'torque = dx * ForceField.y - dy * ForceField.x
  373.         'Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega + torque / Shape(ShapeIndex).MOI
  374.  
  375.         ' Rotation update
  376.         CALL RotShape(ShapeIndex, Shape(ShapeIndex).CtrRot, Shape(ShapeIndex).Omega)
  377.  
  378.         ' Velocity update
  379.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x + ForceField.x / Shape(ShapeIndex).Mass
  380.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y + ForceField.y / Shape(ShapeIndex).Mass
  381.         CALL TranslateShape(ShapeIndex, Shape(ShapeIndex).Velocity)
  382.  
  383.         ' Damping
  384.         Shape(ShapeIndex).Velocity.x = Shape(ShapeIndex).Velocity.x * .985
  385.         Shape(ShapeIndex).Velocity.y = Shape(ShapeIndex).Velocity.y * .985
  386.         Shape(ShapeIndex).Omega = Shape(ShapeIndex).Omega * .985
  387.  
  388.     NEXT
  389.  
  390.     CALL Graphics
  391.     _LIMIT 120
  392.  
  393.  
  394. SUB Graphics
  395.     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA(0, 0, 0, 200), BF
  396.     FOR ShapeIndex = 1 TO ShapeCount
  397.         LOCATE 1, 1: PRINT "Tap arrow keys (don't hold) to move fidget spinner."
  398.         PRINT "Press SPACE to enter creative mode. Left or Right click to create bricks."
  399.         'LOCATE 3, 1: PRINT ProximalPairsCount, CollisionCount
  400.         'CALL ccircle(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, 3, Shape(ShapeIndex).Shade)
  401.         FOR i = 1 TO Shape(ShapeIndex).Elements - 1
  402.             CALL cline(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, PointChain(ShapeIndex, i + 1).x, PointChain(ShapeIndex, i + 1).y, Shape(ShapeIndex).Shade)
  403.             'CALL cpset(PointChain(ShapeIndex, i).x, PointChain(ShapeIndex, i).y, Shape(ShapeIndex).Shade)
  404.         NEXT
  405.         CALL cline(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, Shape(ShapeIndex).Elements).y, PointChain(ShapeIndex, 1).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  406.         'CALL cpaint(Shape(ShapeIndex).CtrRot.x, Shape(ShapeIndex).CtrRot.y, Shape(ShapeIndex).Shade, Shape(ShapeIndex).Shade)
  407.         'CALL cpset(PointChain(ShapeIndex, Shape(ShapeIndex).Elements).x, PointChain(ShapeIndex, 1).y, Shape(ShapeIndex).Shade)
  408.     NEXT
  409.     _DISPLAY
  410.  
  411. FUNCTION CalculateNormalX (k AS INTEGER, i AS INTEGER)
  412.     DIM l AS Vector
  413.     DIM r AS Vector
  414.     li = i - 1
  415.     ri = i + 1
  416.     IF (i = 1) THEN li = Shape(k).Elements
  417.     IF (i = Shape(k).Elements) THEN ri = 1
  418.     l.x = PointChain(k, li).x
  419.     r.x = PointChain(k, ri).x
  420.     dx = r.x - l.x
  421.     CalculateNormalX = dx
  422.  
  423. FUNCTION CalculateNormalY (k AS INTEGER, i AS INTEGER)
  424.     DIM l AS Vector
  425.     DIM r AS Vector
  426.     li = i - 1
  427.     ri = i + 1
  428.     IF (i = 1) THEN li = Shape(k).Elements
  429.     IF (i = Shape(k).Elements) THEN ri = 1
  430.     l.y = PointChain(k, li).y
  431.     r.y = PointChain(k, ri).y
  432.     dy = r.y - l.y
  433.     CalculateNormalY = dy
  434.  
  435. SUB CalculateCtrMass (k AS INTEGER)
  436.     xx = 0
  437.     yy = 0
  438.     FOR i = 1 TO Shape(k).Elements
  439.         xx = xx + PointChain(k, i).x
  440.         yy = yy + PointChain(k, i).y
  441.     NEXT
  442.     Shape(k).CtrMass.x = xx / Shape(k).Elements
  443.     Shape(k).CtrMass.y = yy / Shape(k).Elements
  444.  
  445. SUB CalculateDiameter (k AS INTEGER)
  446.     r2max = -1
  447.     FOR i = 1 TO Shape(k).Elements
  448.         xx = Shape(k).CtrMass.x - PointChain(k, i).x
  449.         yy = Shape(k).CtrMass.y - PointChain(k, i).y
  450.         r2 = xx * xx + yy * yy
  451.         IF (r2 > r2max) THEN
  452.             r2max = r2
  453.         END IF
  454.     NEXT
  455.     Shape(k).Diameter = SQR(r2max)
  456.  
  457. SUB CalculateMOI (k AS INTEGER)
  458.     xx = 0
  459.     yy = 0
  460.     FOR i = 1 TO Shape(k).Elements
  461.         xx = xx + (Shape(k).CtrRot.x - PointChain(k, i).x) ^ 2
  462.         yy = yy + (Shape(k).CtrRot.y - PointChain(k, i).y) ^ 2
  463.     NEXT
  464.     Shape(k).MOI = (xx + yy) * (Shape(k).Mass / Shape(k).Elements)
  465.  
  466. SUB TranslateShape (k AS INTEGER, c AS Vector)
  467.     FOR i = 1 TO Shape(k).Elements
  468.         PointChain(k, i).x = PointChain(k, i).x + c.x
  469.         PointChain(k, i).y = PointChain(k, i).y + c.y
  470.     NEXT
  471.     Shape(k).CtrRot.x = Shape(k).CtrRot.x + c.x
  472.     Shape(k).CtrRot.y = Shape(k).CtrRot.y + c.y
  473.     Shape(k).CtrMass.x = Shape(k).CtrMass.x + c.x
  474.     Shape(k).CtrMass.y = Shape(k).CtrMass.y + c.y
  475.  
  476. SUB RotShape (k AS INTEGER, c AS Vector, da AS DOUBLE)
  477.     FOR i = 1 TO Shape(k).Elements
  478.         xx = PointChain(k, i).x - c.x
  479.         yy = PointChain(k, i).y - c.y
  480.         PointChain(k, i).x = c.x + xx * COS(da) - yy * SIN(da)
  481.         PointChain(k, i).y = c.y + yy * COS(da) + xx * SIN(da)
  482.     NEXT
  483.     xx = Shape(k).CtrMass.x - c.x
  484.     yy = Shape(k).CtrMass.y - c.y
  485.     Shape(k).CtrMass.x = c.x + xx * COS(da) - yy * SIN(da)
  486.     Shape(k).CtrMass.y = c.y + yy * COS(da) + xx * SIN(da)
  487.  
  488. SUB NewAutoBall (x1, y1, r1)
  489.     pa = INT(RND * 4) + 1
  490.     pb = INT(RND * 2) + 1
  491.     ShapeCount = ShapeCount + 1
  492.     Shape(ShapeCount).CtrRot.x = x1
  493.     Shape(ShapeCount).CtrRot.y = y1
  494.     i = 0
  495.     FOR j = 0 TO 2 * 4 * ATN(1) - .05 STEP .05
  496.         i = i + 1
  497.         r = r1 + 5 * COS(pa * j) ^ pb
  498.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + r * COS(j)
  499.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + r * SIN(j)
  500.     NEXT
  501.     Shape(ShapeCount).Elements = i
  502.     Shape(ShapeCount).Mass = i
  503.     Shape(ShapeCount).Velocity.x = 0
  504.     Shape(ShapeCount).Velocity.y = 0
  505.     Shape(ShapeCount).Omega = 0
  506.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  507.     CALL CalculateCtrMass(ShapeCount)
  508.     CALL CalculateDiameter(ShapeCount)
  509.     CALL CalculateMOI(ShapeCount)
  510.  
  511. SUB NewAutoBrick (x1, y1, wx, wy, ang)
  512.     ShapeCount = ShapeCount + 1
  513.     Shape(ShapeCount).CtrRot.x = x1
  514.     Shape(ShapeCount).CtrRot.y = y1
  515.     i = 0
  516.     FOR j = -wy / 2 TO wy / 2 STEP 2
  517.         i = i + 1
  518.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + wx / 2
  519.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + j
  520.     NEXT
  521.     FOR j = wx / 2 TO -wx / 2 STEP -2
  522.         i = i + 1
  523.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + j
  524.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + wy / 2
  525.     NEXT
  526.     FOR j = wy / 2 TO -wy / 2 STEP -2
  527.         i = i + 1
  528.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x - wx / 2
  529.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y + j
  530.     NEXT
  531.     FOR j = -wx / 2 TO wx / 2 STEP 2
  532.         i = i + 1
  533.         PointChain(ShapeCount, i).x = Shape(ShapeCount).CtrRot.x + j
  534.         PointChain(ShapeCount, i).y = Shape(ShapeCount).CtrRot.y - wy / 2
  535.     NEXT
  536.     Shape(ShapeCount).Elements = i
  537.     Shape(ShapeCount).Mass = 999 ^ 999
  538.     Shape(ShapeCount).Velocity.x = 0
  539.     Shape(ShapeCount).Velocity.y = 0
  540.     Shape(ShapeCount).Omega = 0
  541.     Shape(ShapeCount).Shade = _RGB(100, 100, 100)
  542.     CALL CalculateCtrMass(ShapeCount)
  543.     CALL CalculateDiameter(ShapeCount)
  544.     CALL CalculateMOI(ShapeCount)
  545.     Shape(ShapeCount).MOI = 999 ^ 999
  546.  
  547.     CALL RotShape(ShapeCount, Shape(ShapeCount).CtrMass, ang)
  548.  
  549.  
  550. SUB NewMouseShape
  551.     rawresolution = 7.5 ' Raw curve resolution.
  552.     targetpoints = 120 ' Number of points per curve.
  553.     smoothiterations = 15 ' Magnitude of smooth effect.
  554.  
  555.     ShapeCount = ShapeCount + 1
  556.     numpoints = 0
  557.  
  558.  
  559.     ' Gather raw data for one curve at a time.
  560.     ' Click+drag mouse button 1 to trace out a curve.
  561.     xold = 999999
  562.     yold = 999999
  563.     DO
  564.         DO WHILE _MOUSEINPUT
  565.             x = _MOUSEX
  566.             y = _MOUSEY
  567.             IF (x > 0) AND (x < _WIDTH) AND (y > 0) AND (y < _HEIGHT) THEN
  568.                 IF _MOUSEBUTTON(1) THEN
  569.                     x = x - (_WIDTH / 2)
  570.                     y = -y + (_HEIGHT / 2)
  571.                     delta = SQR((x - xold) ^ 2 + (y - yold) ^ 2)
  572.  
  573.                     ' Collect data only if the new point is sufficiently far away from the previous point.
  574.                     IF (delta > rawresolution) AND (numpoints < targetpoints - 1) THEN
  575.                         numpoints = numpoints + 1
  576.                         PointChain(ShapeCount, numpoints).x = x
  577.                         PointChain(ShapeCount, numpoints).y = y
  578.                         CALL cpset(x, y, _RGB(255, 255, 255))
  579.                         xold = x
  580.                         yold = y
  581.                     END IF
  582.                 END IF
  583.             END IF
  584.         LOOP
  585.         _DISPLAY
  586.     LOOP UNTIL NOT _MOUSEBUTTON(1) AND (numpoints > 1)
  587.  
  588.     ' If the curve contains less than the minimum numer of points, use interpolation to fill in the gaps.
  589.     DO WHILE (numpoints < targetpoints)
  590.  
  591.         ' Determine the pair of neighboring points that have the greatest separation of all pairs.
  592.         rad2max = -1
  593.         kmax = -1
  594.         FOR k = 1 TO numpoints - 1
  595.             xfac = PointChain(ShapeCount, k).x - PointChain(ShapeCount, k + 1).x
  596.             yfac = PointChain(ShapeCount, k).y - PointChain(ShapeCount, k + 1).y
  597.             rad2 = xfac ^ 2 + yfac ^ 2
  598.             IF rad2 > rad2max THEN
  599.                 kmax = k
  600.                 rad2max = rad2
  601.             END IF
  602.         NEXT
  603.         '''
  604.         cornercase = 0
  605.         xfac = PointChain(ShapeCount, numpoints).x - PointChain(ShapeCount, 1).x
  606.         yfac = PointChain(ShapeCount, numpoints).y - PointChain(ShapeCount, 1).y
  607.         rad2 = xfac ^ 2 + yfac ^ 2
  608.         IF rad2 > rad2max THEN
  609.             kmax = numpoints
  610.             rad2max = rad2
  611.             cornercase = 1
  612.         END IF
  613.         '''
  614.         IF (cornercase = 0) THEN
  615.  
  616.             numpoints = numpoints + 1
  617.  
  618.             ' Starting next to kmax, create a `gap' by shifting all other points by one index.
  619.             FOR j = numpoints TO kmax + 1 STEP -1
  620.                 PointChain(ShapeCount, j).x = PointChain(ShapeCount, j - 1).x
  621.                 PointChain(ShapeCount, j).y = PointChain(ShapeCount, j - 1).y
  622.             NEXT
  623.  
  624.             ' Fill the gap with a new point whose position is determined by the average of its neighbors.
  625.             PointChain(ShapeCount, kmax + 1).x = (1 / 2) * (PointChain(ShapeCount, kmax).x + PointChain(ShapeCount, kmax + 2).x)
  626.             PointChain(ShapeCount, kmax + 1).y = (1 / 2) * (PointChain(ShapeCount, kmax).y + PointChain(ShapeCount, kmax + 2).y)
  627.  
  628.         ELSE
  629.             numpoints = numpoints + 1
  630.             xx = PointChain(ShapeCount, numpoints - 1).x
  631.             yy = PointChain(ShapeCount, numpoints - 1).y
  632.             PointChain(ShapeCount, numpoints).x = (1 / 2) * (PointChain(ShapeCount, 1).x + xx)
  633.             PointChain(ShapeCount, numpoints).y = (1 / 2) * (PointChain(ShapeCount, 1).y + yy)
  634.         END IF
  635.  
  636.     LOOP
  637.  
  638.     ' At this stage, the curve still has all of its sharp edges. Use a `relaxation method' to smooth.
  639.     ' The new position of a point is equal to the average position of its neighboring points.
  640.     CALL SmoothShape(ShapeCount, numpoints, smoothiterations)
  641.  
  642.     Shape(ShapeCount).Elements = numpoints
  643.     Shape(ShapeCount).Mass = numpoints
  644.     Shape(ShapeCount).Velocity.x = 0
  645.     Shape(ShapeCount).Velocity.y = 0
  646.     Shape(ShapeCount).Omega = 0
  647.     Shape(ShapeCount).Shade = _RGB(100 + INT(RND * 155), 100 + INT(RND * 155), 100 + INT(RND * 155))
  648.     CALL CalculateCtrMass(ShapeCount)
  649.     Shape(ShapeCount).CtrRot.x = Shape(ShapeCount).CtrMass.x
  650.     Shape(ShapeCount).CtrRot.y = Shape(ShapeCount).CtrMass.y
  651.     CALL CalculateDiameter(ShapeCount)
  652.     CALL CalculateMOI(ShapeCount)
  653.  
  654.  
  655. SUB SmoothShape (i AS INTEGER, n AS INTEGER, s AS INTEGER)
  656.     FOR j = 1 TO s
  657.         FOR k = 2 TO n - 1
  658.             TempChain(i, k).x = (1 / 2) * (PointChain(i, k - 1).x + PointChain(i, k + 1).x)
  659.             TempChain(i, k).y = (1 / 2) * (PointChain(i, k - 1).y + PointChain(i, k + 1).y)
  660.         NEXT
  661.         FOR k = 2 TO n - 1
  662.             PointChain(i, k).x = TempChain(i, k).x
  663.             PointChain(i, k).y = TempChain(i, k).y
  664.         NEXT
  665.     NEXT
  666.  
  667. SUB cline (x1, y1, x2, y2, col AS _UNSIGNED LONG)
  668.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  669.  
  670. SUB ccircle (x1, y1, rad, col AS _UNSIGNED LONG)
  671.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  672.  
  673. SUB cpset (x1, y1, col AS _UNSIGNED LONG)
  674.     PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
  675.  
  676. SUB cpaint (x1, y1, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
  677.     PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
  678.  
  679. SUB snd (frq, dur)
  680.     IF ((frq >= 37) AND (frq <= 2000)) THEN
  681.         SOUND frq, dur
  682.     END IF
« Last Edit: February 23, 2020, 12:39:53 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

FellippeHeitor

  • Guest
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #13 on: February 23, 2020, 02:54:41 pm »
THIS IS ALL SO COOL.

It's exciting to have this physics engine coming to life as we watch it. This is gonna be even cooler in the long run.

Thanks for the effort, it's a very exciting time.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Generalized physics engine prototype with arbitrary shapes and collisions
« Reply #14 on: February 23, 2020, 03:34:16 pm »
Okay, I have *got* to stop working on this so I can make way for other things. Newest code below.

To summarize the last few days' work:

Every one of the points in the top post still stands. This is completely unified - everything is made of the same stuff, even the walls, all obeying the same equations. It's been tested across a wild variety of parameters, not excluding popular games. This code is 80% ready to become any 2D version of (in abc order) bowling, breakout, darts, gorillas, pinball, pong, pool, projectiles, whatever. Any place where the motion needs to look correct, or god willing *be* correct.

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