Author Topic: Reddit's mobile app loading animation  (Read 5673 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.

FellippeHeitor

  • Guest
Reddit's mobile app loading animation
« on: February 05, 2019, 06:41:06 pm »
I am a bit obsessed with loading animations (one may have noticed at this point), be them fading dots, bouncing circles, or, in this case, little orbs orbiting a bigger orb.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(400, 400, 32)
  2.  
  3. _TITLE "Reddit's mobile app loading animation"
  4.  
  5. COLOR _RGB32(39, 78, 177)
  6.     'LINE (0, 0)-(_WIDTH - 1, _HEIGHT - 1), _RGBA32(0, 0, 0, 30), BF
  7.     CLS
  8.  
  9.     CircleFill _WIDTH / 2, _HEIGHT / 2, 35, _DEFAULTCOLOR
  10.  
  11.     a = a + .05
  12.     b = b + .025
  13.     IF a > _PI(2) THEN a = _PI(2) - a
  14.     IF b > _PI(2) THEN b = _PI(2) - b
  15.  
  16.     c = 90
  17.     FOR i = a TO a - _PI(.85) STEP -.001
  18.         PSET (_WIDTH / 2 + COS(i - _PI) * c, _HEIGHT / 2 + SIN(i - _PI) * c)
  19.         PSET (_WIDTH / 2 + COS(i) * c, _HEIGHT / 2 + SIN(i) * c)
  20.     NEXT
  21.     CircleFill _WIDTH / 2 + COS(a - _PI) * c, _HEIGHT / 2 + SIN(a - _PI) * c, 10, _DEFAULTCOLOR
  22.     CircleFill _WIDTH / 2 + COS(a) * c, _HEIGHT / 2 + SIN(a) * c, 10, _DEFAULTCOLOR
  23.  
  24.     d = 180
  25.     FOR i = b TO b - _PI(.85) STEP -.001
  26.         PSET (_WIDTH / 2 + COS(i - _PI) * d, _HEIGHT / 2 + SIN(i - _PI) * d)
  27.         PSET (_WIDTH / 2 + COS(i) * d, _HEIGHT / 2 + SIN(i) * d)
  28.     NEXT
  29.     CircleFill _WIDTH / 2 + COS(b - _PI) * d, _HEIGHT / 2 + SIN(b - _PI) * d, 20, _DEFAULTCOLOR
  30.     CircleFill _WIDTH / 2 + COS(b) * d, _HEIGHT / 2 + SIN(b) * d, 20, _DEFAULTCOLOR
  31.  
  32.     _DISPLAY
  33.     _LIMIT 30
  34.  
  35. SUB CircleFill (x AS LONG, y AS LONG, R AS LONG, C AS _UNSIGNED LONG)
  36.     DIM x0 AS SINGLE, y0 AS SINGLE
  37.     DIM e AS SINGLE
  38.  
  39.     x0 = R
  40.     y0 = 0
  41.     e = -R
  42.     DO WHILE y0 < x0
  43.         IF e <= 0 THEN
  44.             y0 = y0 + 1
  45.             LINE (x - x0, y + y0)-(x + x0, y + y0), C, BF
  46.             LINE (x - x0, y - y0)-(x + x0, y - y0), C, BF
  47.             e = e + 2 * y0
  48.         ELSE
  49.             LINE (x - y0, y - x0)-(x + y0, y - x0), C, BF
  50.             LINE (x - y0, y + x0)-(x + y0, y + x0), C, BF
  51.             x0 = x0 - 1
  52.             e = e - 2 * x0
  53.         END IF
  54.     LOOP
  55.     LINE (x - R, y)-(x + R, y), C, BF

I like to watch them align from time to time.

Captura de Tela 2019-02-05 às 21.41.49.png
« Last Edit: February 05, 2019, 06:43:50 pm by FellippeHeitor »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #1 on: February 05, 2019, 07:02:13 pm »
Look Mark, more balls. Go get 'em boy!

Pete :D
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #2 on: February 05, 2019, 07:21:16 pm »
That CircleFill routine looks familiar - on the other hand I suppose it *should*, because like the irreducible mousetrap, there really are one, maybe two, correct ways to fill a circle in this language. The thing that interests me right now is the choice of of variable types:

Code: QB64: [Select]
  1. SUB CircleFill (x AS LONG, y AS LONG, R AS LONG, C AS _UNSIGNED LONG)
  2.     DIM x0 AS SINGLE, y0 AS SINGLE
  3.     DIM e AS SINGLE

So the sub takes LONGs for coordinates, but then cooks up helper variables that are SINGLE. Shoudn't these types agree? When you go to compute x-x0 for instance, doesn't the result lose any LONGness that may have been intended? (Not to mention, as a pure numbers thing, LONG has way more precision than is necessary to put color on a screen only few thousand pixels wide at best.)

Not a critique, just wanna make a little smoke.
You're not done when it works, you're done when it's right.

FellippeHeitor

  • Guest
Re: Reddit's mobile app loading animation
« Reply #3 on: February 05, 2019, 07:54:58 pm »
I'll refer you to Vince on that one. He provided me with that code for our (mine and Ashish's) p5js.bas library, so I have no idea what goes there, except it works :-)

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #4 on: February 05, 2019, 08:40:16 pm »
vince's circle fill

Code: QB64: [Select]
  1. defint a-z
  2. sub circlef (x, y, r, c as long)
  3.         x0 = r
  4.         y0 = 0
  5.         e = -r
  6.         do while y0 < x0
  7.                 if e <= 0 then
  8.                         y0 = y0 + 1
  9.                         line (x-x0, y+y0)-(x+x0, y+y0), c, bf
  10.                         line (x-x0, y-y0)-(x+x0, y-y0), c, bf
  11.                         e = e + 2*y0
  12.                 else
  13.                         line (x-y0, y-x0)-(x+y0, y-x0), c, bf
  14.                         line (x-y0, y+x0)-(x+y0, y+x0), c, bf
  15.                         x0 = x0 - 1
  16.                         e = e - 2*x0
  17.                 end if
  18.         loop
  19.         line (x-r,y)-(x+r,y),c,bf
  20.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #5 on: February 06, 2019, 10:11:22 am »
That CircleFill routine looks familiar - on the other hand I suppose it *should*, because like the irreducible mousetrap, there really are one, maybe two, correct ways to fill a circle in this language. The thing that interests me right now is the choice of of variable types:

Code: QB64: [Select]
  1. SUB CircleFill (x AS LONG, y AS LONG, R AS LONG, C AS _UNSIGNED LONG)
  2.     DIM x0 AS SINGLE, y0 AS SINGLE
  3.     DIM e AS SINGLE

So the sub takes LONGs for coordinates, but then cooks up helper variables that are SINGLE. Shoudn't these types agree? When you go to compute x-x0 for instance, doesn't the result lose any LONGness that may have been intended? (Not to mention, as a pure numbers thing, LONG has way more precision than is necessary to put color on a screen only few thousand pixels wide at best.)

Not a critique, just wanna make a little smoke.


Smoke this:
IMO State of the art with Circle Fill is this:
Code: QB64: [Select]
  1. ' with Steve's EllipseFill, who needs CircleFill?
  2. ' Is this fast enough for general circle fill (June 2018):  https://www.qb64.org/forum/index.php?topic=298.msg1942#msg1942
  3. '  EllipseFill SMcNeill (Nov 3, 2018) https://www.qb64.org/forum/index.php?topic=755.msg6506#msg6506
  4. SUB EllipseFill (cx AS INTEGER, cy AS INTEGER, rx AS INTEGER, ry AS INTEGER, c AS _UNSIGNED LONG)
  5.     DIM a AS LONG, b AS LONG
  6.     DIM x AS LONG, y AS LONG
  7.     DIM xx AS LONG, yy AS LONG
  8.     DIM sx AS LONG, sy AS LONG
  9.     DIM e AS LONG
  10.  
  11.     a = 2 * rx * rx
  12.     b = 2 * ry * ry
  13.     x = rx
  14.     xx = ry * ry * (1 - rx - rx)
  15.     yy = rx * rx
  16.     sx = b * rx
  17.  
  18.     DO WHILE sx >= sy
  19.         LINE (cx - x, cy - y)-(cx + x, cy - y), c, BF
  20.         IF y <> 0 THEN LINE (cx - x, cy + y)-(cx + x, cy + y), c, BF
  21.  
  22.         y = y + 1
  23.         sy = sy + a
  24.         e = e + yy
  25.         yy = yy + a
  26.  
  27.         IF (e + e + xx) > 0 THEN
  28.             x = x - 1
  29.             sx = sx - b
  30.             e = e + xx
  31.             xx = xx + b
  32.         END IF
  33.     LOOP
  34.  
  35.     x = 0
  36.     y = ry
  37.     xx = rx * ry
  38.     yy = rx * rx * (1 - ry - ry)
  39.     e = 0
  40.     sx = 0
  41.     sy = a * ry
  42.  
  43.     DO WHILE sx <= sy
  44.         LINE (cx - x, cy - y)-(cx + x, cy - y), c, BF
  45.         LINE (cx - x, cy + y)-(cx + x, cy + y), c, BF
  46.  
  47.         DO
  48.             x = x + 1
  49.             sx = sx + b
  50.             e = e + xx
  51.             xx = xx + b
  52.         LOOP UNTIL (e + e + yy) > 0
  53.  
  54.         y = y - 1
  55.         sy = sy - a
  56.         e = e + yy
  57.         yy = yy + a
  58.  
  59.     LOOP
  60.  

We had a conversation and tests for Circle Fill in June 2018 but then Steve comes up with an Ellipse Fill in November 2018 that turns out to be at least as fast and a more general solution to filled ellipse (including circles), see ref links in code above.

About typing, the parameters are integer but the working variables need to be long (at least) for mult and squaring integers.

Being a ball enthusiast I guess I am the only one that noticed?
« Last Edit: February 06, 2019, 10:14:36 am by bplus »

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #6 on: February 06, 2019, 10:41:02 am »
Sure, ellipsefill is better than circlefill for ellipses that aren't tilted, if I read that code correctly. The circlefill codes are by that standard, more general by symmetry argument. Can that handle a rotated filled ellipse? I stand corrected if it can.
« Last Edit: February 06, 2019, 10:53:34 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #7 on: February 06, 2019, 11:20:49 am »
but then Steve comes up with an Ellipse Fill in November 2018 that turns out to be at least as fast and a more general solution to filled ellipse (including circles),

That Steve!!!

Some funny stuff:
Code: QB64: [Select]
  1. xx = ry * ry * (1 - rx - rx)
  2. IF (e + e + xx) > 0 THEN
  3.  
That's on a Steve level of ultraoptimization
« Last Edit: February 06, 2019, 11:28:32 am by _vince »

Marked as best answer by on October 25, 2024, 12:56:13 am

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Reddit's mobile app loading animation
« Reply #8 on: February 06, 2019, 11:28:33 am »
  • Undo Best Answer
  • Sure, ellipsefill is better than circlefill for ellipses that aren't tilted, if I read that code correctly. The circlefill codes are by that standard, more general by symmetry argument. Can that handle a rotated filled ellipse? I stand corrected if it can.

    ? A circle is a special kind of ellipse, thus I say the ellipse is more general than a circle, like a square is a special case of rectangles which are special cases of quads. (EDIT: I guess I should have said a more general solution to filled circles, not filled ellipses.)

    And yes, so far ellipses can't be tilted. Good challenge though!
    « Last Edit: February 06, 2019, 11:40:02 am by bplus »

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Reddit's mobile app loading animation
    « Reply #9 on: February 06, 2019, 11:36:05 am »
    Code: QB64: [Select]
    1.  
    2. SCREEN _NEWIMAGE(400, 400, 32)
    3.  
    4. _TITLE "Loading Animation Ellipse mod" 'B+ mod 2019-02-06
    5. ' Fellipe's original: https://www.qb64.org/forum/index.php?topic=1042.0
    6.  
    7. COLOR _RGBA32(39, 78, 177, 10)
    8. c = 100: c1 = 35
    9. d = 140: d1 = 180
    10. b = _PI(.25)
    11. cx = 200: cy = 200
    12.     CLS
    13.     FOR r = 20 TO 1 STEP -1
    14.         EllipseFill cx, cy, r, r, _RGB32(12 * (25 - r), 12 * (25 - r), 12 * (25 - r))
    15.     NEXT
    16.  
    17.     a = a + .05
    18.     b = b + .025
    19.     IF a > _PI(2) THEN a = _PI(2) - a
    20.     IF b > _PI(2) THEN b = _PI(2) - b
    21.  
    22.     FOR i = a TO a - _PI(.85) STEP -.001
    23.         PSET (cx + COS(i - _PI) * c, cy + SIN(i - _PI) * c1)
    24.         PSET (cx + COS(i) * c, cy + SIN(i) * c1)
    25.     NEXT
    26.     FOR r = 10 TO 1 STEP -1
    27.         k = (10 - r) * 25
    28.         EllipseFill cx + COS(a - _PI) * c, cy + SIN(a - _PI) * c1, r, r, _RGB32(k, k, 0)
    29.         EllipseFill cx + COS(a) * c, cy + SIN(a) * c1, r, r, _RGB32(k, 0, k)
    30.     NEXT
    31.     FOR i = b TO b - _PI(.85) STEP -.001
    32.         PSET (cx + COS(i - _PI) * d, cy + SIN(i - _PI) * d1)
    33.         PSET (cx + COS(i) * d, cy + SIN(i) * d1)
    34.     NEXT
    35.     FOR r = 20 TO 1 STEP -1
    36.         k = (20 - r) * 6
    37.         EllipseFill cx + COS(b - _PI) * d, cy + SIN(b - _PI) * d1, r, r, _RGB32(0, k, 0)
    38.         EllipseFill cx + COS(b) * d, cy + SIN(b) * d1, r, r, _RGB32(k, 0, 0)
    39.     NEXT
    40.     _DISPLAY
    41.     _LIMIT 30
    42.  
    43.  
    44. ' with Steve's EllipseFill, who needs CircleFill?
    45. ' Is this fast enough for general circle fill (June 2018):  https://www.qb64.org/forum/index.php?topic=298.msg1942#msg1942
    46. '  EllipseFill SMcNeill (Nov 3, 2018) https://www.qb64.org/forum/index.php?topic=755.msg6506#msg6506
    47. SUB EllipseFill (cx AS INTEGER, cy AS INTEGER, rx AS INTEGER, ry AS INTEGER, c AS _UNSIGNED LONG)
    48.     DIM a AS LONG, b AS LONG
    49.     DIM x AS LONG, y AS LONG
    50.     DIM xx AS LONG, yy AS LONG
    51.     DIM sx AS LONG, sy AS LONG
    52.     DIM e AS LONG
    53.  
    54.     a = 2 * rx * rx
    55.     b = 2 * ry * ry
    56.     x = rx
    57.     xx = ry * ry * (1 - rx - rx)
    58.     yy = rx * rx
    59.     sx = b * rx
    60.  
    61.     DO WHILE sx >= sy
    62.         LINE (cx - x, cy - y)-(cx + x, cy - y), c, BF
    63.         IF y <> 0 THEN LINE (cx - x, cy + y)-(cx + x, cy + y), c, BF
    64.  
    65.         y = y + 1
    66.         sy = sy + a
    67.         e = e + yy
    68.         yy = yy + a
    69.  
    70.         IF (e + e + xx) > 0 THEN
    71.             x = x - 1
    72.             sx = sx - b
    73.             e = e + xx
    74.             xx = xx + b
    75.         END IF
    76.     LOOP
    77.  
    78.     x = 0
    79.     y = ry
    80.     xx = rx * ry
    81.     yy = rx * rx * (1 - ry - ry)
    82.     e = 0
    83.     sx = 0
    84.     sy = a * ry
    85.  
    86.     DO WHILE sx <= sy
    87.         LINE (cx - x, cy - y)-(cx + x, cy - y), c, BF
    88.         LINE (cx - x, cy + y)-(cx + x, cy + y), c, BF
    89.  
    90.         DO
    91.             x = x + 1
    92.             sx = sx + b
    93.             e = e + xx
    94.             xx = xx + b
    95.         LOOP UNTIL (e + e + yy) > 0
    96.  
    97.         y = y - 1
    98.         sy = sy - a
    99.         e = e + yy
    100.         yy = yy + a
    101.  
    102.     LOOP
    103.  
    104.  
    105.  

     
    Loading Animation Ellipse Mod.PNG


    Append: Oh! I should report: While creating this mod, I learned that EllipseFill can't take a radius of 0, the routine needs a fix.
    « Last Edit: February 06, 2019, 11:46:52 am by bplus »

    FellippeHeitor

    • Guest
    Re: Reddit's mobile app loading animation
    « Reply #10 on: February 06, 2019, 11:52:05 am »
    Cool mod, bplus!

    Offline STxAxTIC

    • Library Staff
    • Forum Resident
    • Posts: 1091
    • he lives
      • View Profile
    Re: Reddit's mobile app loading animation
    « Reply #11 on: February 06, 2019, 12:18:22 pm »
    ... and if you want to blow your balls up, run this thing. Download both attachments, and load data-reddit.txt as an argument. (Drop onto Sanctum.exe). While running, press (and hold if you wish) n to witness some destruction. Things don't just blow up and disappear when you do that. They dismantle, particle-by-particle, into a puddle at z=0. If you've got the pilot skills, go blow up the moon and the UFO afterward.

    Code: QB64: [Select]
    1. $EXEICON:'sanctum.ico'
    2. REM $include:'Color32.BI'
    3.  
    4. ' Video.
    5. SCREEN _NEWIMAGE(640, 480, 32)
    6. 'SCREEN _NEWIMAGE(800, 600, 32)
    7. 'SCREEN _NEWIMAGE(1024, 768, 32)
    8. screenwidth = _WIDTH
    9. screenheight = _HEIGHT
    10. plotmode = 1
    11.  
    12. ' Memory.
    13. DIM bignumber AS LONG
    14. bignumber = 3000000
    15. DIM numparticleorig AS LONG
    16. numparticleorig = bignumber
    17. numparticlevisible = bignumber
    18. numgroupvisible = 0
    19.  
    20. ' Structure to store groups of particles.
    21. TYPE GroupElement
    22.     Identity AS LONG
    23.     Pointer AS LONG
    24.     Lagger AS LONG
    25.     ContentName AS STRING * 25
    26.     FirstParticle AS LONG
    27.     LastParticle AS LONG
    28.     EventTimer AS LONG
    29.     COMx AS SINGLE
    30.     COMy AS SINGLE
    31.     COMz AS SINGLE
    32.     CORx AS SINGLE
    33.     CORy AS SINGLE
    34.     CORz AS SINGLE
    35. DIM VectorGroup(bignumber / 10) AS GroupElement
    36.  
    37. ' Constant(s).
    38. pi = 3.1415926536
    39. ee = 2.7182818285
    40.  
    41. ' Camera orientation vectors.
    42. DIM uhat(1 TO 3), vhat(1 TO 3), nhat(1 TO 3)
    43.  
    44. ' Clipping planes.
    45. DIM nearplane(1 TO 4), farplane(1 TO 4), rightplane(1 TO 4), leftplane(1 TO 4), topplane(1 TO 4), bottomplane(1 TO 4)
    46.  
    47. ' Basis vectors defined in xyz three-space.
    48. DIM xhat(1 TO 3), yhat(1 TO 3), zhat(1 TO 3)
    49. xhat(1) = 1: xhat(2) = 0: xhat(3) = 0
    50. yhat(1) = 0: yhat(2) = 1: yhat(3) = 0
    51. zhat(1) = 0: zhat(2) = 0: zhat(3) = 1
    52.  
    53. ' Basis vectors projected in uv two-space.
    54. DIM xhatp(1 TO 2), yhatp(1 TO 2), zhatp(1 TO 2)
    55.  
    56. ' Particle vectors defined in xyz three-space.
    57. DIM vecgroupname(bignumber / 10) AS STRING
    58. DIM vecorig(numparticleorig, 3)
    59. DIM vecorigvel(numparticleorig, 3)
    60. DIM vecorigrot(numparticleorig, 3)
    61. DIM vecorigacc(numparticleorig, 3)
    62. DIM veccolor(numparticleorig) AS LONG
    63. DIM vec(numparticleorig, 3)
    64. DIM vecvisible(numparticlevisible, 3)
    65. DIM vecvisiblecolor(numparticlevisible) AS LONG
    66.  
    67. ' Particle vectors projected in uv two-space.
    68. DIM vecpuv(numparticleorig, 1 TO 2)
    69. DIM vecvisiblepuv(numparticlevisible, 1 TO 2)
    70.  
    71. ' Particle projections adjusted for screen uv two-space.
    72. DIM vecpuvs(numparticleorig, 1 TO 2)
    73. DIM vecvisiblepuvs(numparticlevisible, 1 TO 2)
    74.  
    75. ' State.
    76. nearplane(4) = 1
    77. farplane(4) = -100
    78. rightplane(4) = 0 '*' fovd * (nhat(1) * rightplane(1) + nhat(2) * rightplane(2) + nhat(3) * rightplane(3))
    79. leftplane(4) = 0
    80. topplane(4) = 0
    81. bottomplane(4) = 0
    82. centerx = screenwidth / 2
    83. centery = screenheight / 2
    84. fovd = -256
    85. numparticleorig = 0
    86. numparticlevisible = 0
    87. particleindex = 0
    88. vecgroupcounter = 0
    89. vecgroupindex = 0
    90. speeddamp = 1 / 66 ' Rotation damper.
    91. speedboost = 1.5 ' Linear boost.
    92. timevar = 0
    93. 'camx = -42
    94. 'camy = 28
    95. 'camz = 40
    96. camx = 18.69336: camy = 24.12129: camz = 20.24639
    97. uhat(1) = -1: uhat(2) = 0: uhat(3) = 0
    98. vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    99. 'uhat(1) = -.2078192: uhat(2) = -.9781672: uhat(3) = 0
    100. 'vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    101. toggleanimatestandard = 1
    102. toggleanimateforced = 1
    103. toggleinvertmouse = -1
    104. togglehud = -1
    105.  
    106. ' Prime main loop.
    107. GOSUB initialize.objects
    108. GOSUB redraw
    109.  
    110. ' Begin main loop.
    111.     IF toggleanimatestandard = 1 THEN flagredraw = 1
    112.     IF flagredraw = 1 THEN
    113.         GOSUB redraw
    114.         flagredraw = -1
    115.     END IF
    116.     GOSUB mouseprocess
    117.     GOSUB keyprocess
    118.  
    119.     _DISPLAY
    120.     _KEYCLEAR
    121.     _LIMIT 60
    122.  
    123.  
    124. ' Gosubs.
    125.  
    126. mouseprocess:
    127. mx = 0
    128. my = 0
    129.     mx = mx + _MOUSEMOVEMENTX
    130.     my = my + _MOUSEMOVEMENTY
    131.  
    132.     IF _MOUSEWHEEL > 0 THEN GOSUB rotate.clockwise
    133.     IF _MOUSEWHEEL < 0 THEN GOSUB rotate.counterclockwise
    134.     IF mx > 0 THEN
    135.         GOSUB rotate.uhat.plus: GOSUB normalize.screen.vectors
    136.     END IF
    137.     IF mx < 0 THEN
    138.         GOSUB rotate.uhat.minus: GOSUB normalize.screen.vectors
    139.     END IF
    140.     IF my > 0 THEN
    141.         IF toggleinvertmouse = -1 THEN
    142.             GOSUB rotate.vhat.plus: GOSUB normalize.screen.vectors
    143.         ELSE
    144.             GOSUB rotate.vhat.minus: GOSUB normalize.screen.vectors
    145.         END IF
    146.     END IF
    147.     IF my < 0 THEN
    148.         IF toggleinvertmouse = -1 THEN
    149.             GOSUB rotate.vhat.minus: GOSUB normalize.screen.vectors
    150.         ELSE
    151.             GOSUB rotate.vhat.plus: GOSUB normalize.screen.vectors
    152.         END IF
    153.     END IF
    154.     mx = 0
    155.     my = 0
    156.  
    157. 'MouseLB = _MOUSEBUTTON(1)
    158. 'MouseRB = _MOUSEBUTTON(2)
    159. flagredraw = 1
    160.  
    161. keyprocess:
    162. IF key$ <> "" THEN flagredraw = 1
    163.     CASE "8", CHR$(0) + CHR$(73)
    164.         GOSUB rotate.vhat.plus
    165.     CASE "2", CHR$(0) + CHR$(81)
    166.         GOSUB rotate.vhat.minus
    167.     CASE "4", CHR$(0) + CHR$(75)
    168.         GOSUB rotate.uhat.minus
    169.     CASE "6", CHR$(0) + CHR$(77)
    170.         GOSUB rotate.uhat.plus
    171.     CASE "7"
    172.         GOSUB rotate.clockwise
    173.     CASE "9"
    174.         GOSUB rotate.counterclockwise
    175.     CASE "1"
    176.         GOSUB rotate.uhat.minus: GOSUB normalize.screen.vectors: GOSUB rotate.clockwise
    177.     CASE "3"
    178.         GOSUB rotate.uhat.plus: GOSUB normalize.screen.vectors: GOSUB rotate.counterclockwise
    179.     CASE "s", CHR$(0) + CHR$(80)
    180.         GOSUB strafe.camera.nhat.plus
    181.     CASE "w", CHR$(0) + CHR$(72)
    182.         GOSUB strafe.camera.nhat.minus
    183.     CASE "d"
    184.         GOSUB strafe.camera.uhat.plus
    185.     CASE "a"
    186.         GOSUB strafe.camera.uhat.minus
    187.     CASE "e"
    188.         GOSUB strafe.camera.vhat.plus
    189.     CASE "q"
    190.         GOSUB strafe.camera.vhat.minus
    191.     CASE "x"
    192.         uhat(1) = 0: uhat(2) = 1: uhat(3) = 0
    193.         vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    194.     CASE "X"
    195.         uhat(1) = 0: uhat(2) = -1: uhat(3) = 0
    196.         vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    197.     CASE "y"
    198.         uhat(1) = -1: uhat(2) = 0: uhat(3) = 0
    199.         vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    200.     CASE "Y"
    201.         uhat(1) = 1: uhat(2) = 0: uhat(3) = 0
    202.         vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
    203.     CASE "z"
    204.         uhat(1) = 1: uhat(2) = 0: uhat(3) = 0
    205.         vhat(1) = 0: vhat(2) = 1: vhat(3) = 0
    206.         GOSUB normalize.screen.vectors
    207.     CASE "Z"
    208.         uhat(1) = 0: uhat(2) = 1: uhat(3) = 0
    209.         vhat(1) = 1: vhat(2) = 0: vhat(3) = 0
    210.     CASE "]"
    211.         farplane(4) = farplane(4) - 1
    212.     CASE "["
    213.         farplane(4) = farplane(4) + 1
    214.     CASE " "
    215.         togglehud = -togglehud
    216.     CASE "t"
    217.         toggleanimatestandard = -toggleanimatestandard
    218.     CASE "i"
    219.         toggleinvertmouse = -toggleinvertmouse
    220.     CASE "p"
    221.         plotmode = -plotmode
    222.     CASE "v"
    223.         OPEN "snapshot-camera.txt" FOR OUTPUT AS #1
    224.         PRINT #1, camx, camy, camz
    225.         PRINT #1, uhat(1), uhat(2), uhat(3)
    226.         PRINT #1, vhat(1), vhat(2), vhat(3)
    227.         CLOSE #1
    228.     CASE CHR$(27)
    229.         SYSTEM
    230.     CASE "f"
    231.         explodex = camx - nhat(1) * (1 - .1 * RND) * 60
    232.         explodey = camy - nhat(2) * (1 - .1 * RND) * 60
    233.         explodez = camz - nhat(3) * (1 - .1 * RND) * 60
    234.         GOSUB explode
    235.     CASE "n"
    236.         VectorGroup(closestgroup).ContentName = "Newtonian"
    237.         FOR particleindex = VectorGroup(closestgroup).FirstParticle TO VectorGroup(closestgroup).LastParticle
    238.             vecorigvel(particleindex, 1) = (RND - .5) * 20
    239.             vecorigvel(particleindex, 2) = (RND - .5) * 20
    240.             vecorigvel(particleindex, 3) = (RND - .5) * 20
    241.             vecorigacc(particleindex, 3) = -30
    242.         NEXT
    243.         VectorGroup(closestgroup).EventTimer = INT(TIMER)
    244.     CASE "k"
    245.         'IF LTRIM$(RTRIM$(VectorGroup(closestgroup).ContentName)) <> "Sparks" THEN
    246.         explodex = VectorGroup(closestgroup).COMx
    247.         explodey = VectorGroup(closestgroup).COMy
    248.         explodez = VectorGroup(closestgroup).COMz
    249.         GOSUB explode
    250.         p = VectorGroup(closestgroup).Pointer
    251.         l = VectorGroup(closestgroup).Lagger
    252.         VectorGroup(l).Pointer = p
    253.         IF p <> -999 THEN
    254.             VectorGroup(p).Lagger = l
    255.         ELSE
    256.             'determine last object id
    257.             p = 1
    258.             DO
    259.                 k = VectorGroup(p).Identity
    260.                 p = VectorGroup(k).Pointer
    261.                 IF p = -999 THEN EXIT DO
    262.             LOOP
    263.             lastobjectid = k
    264.             VectorGroup(lastobjectid).Pointer = -999
    265.         END IF
    266.         'END IF
    267.     CASE "b"
    268.         'determine last object id
    269.         p = 1
    270.         DO
    271.             k = VectorGroup(p).Identity
    272.             p = VectorGroup(k).Pointer
    273.             IF p = -999 THEN EXIT DO
    274.         LOOP
    275.         lastobjectid = k
    276.         particleindex = VectorGroup(lastobjectid).LastParticle
    277.         'create new group
    278.         vecgroupcounter = vecgroupcounter + 1
    279.         vecgroupindex = vecgroupcounter
    280.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    281.         VectorGroup(vecgroupindex).Pointer = -999
    282.         VectorGroup(vecgroupindex).Lagger = lastobjectid
    283.         VectorGroup(vecgroupindex).ContentName = "Block"
    284.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    285.         FOR r = 1 TO 400
    286.             particleindex = particleindex + 1
    287.             vecorig(particleindex, 1) = camx + -40 * nhat(1) + RND * tilesize / 2
    288.             vecorig(particleindex, 2) = camy + -40 * nhat(2) + RND * tilesize / 2
    289.             vecorig(particleindex, 3) = camz + -40 * nhat(3) + RND * tilesize / 2
    290.             IF RND > .5 THEN
    291.                 veccolor(particleindex) = Lime
    292.             ELSE
    293.                 veccolor(particleindex) = Purple
    294.             END IF
    295.             GOSUB calccom
    296.         NEXT
    297.         VectorGroup(vecgroupindex).LastParticle = particleindex
    298.         GOSUB adjustcom
    299.         'connect new group to last group
    300.         VectorGroup(lastobjectid).Pointer = vecgroupindex
    301.  
    302. convert:
    303. ' Convert graphics from uv-cartesian coordinates to monitor coordinates.
    304. x0 = x: y0 = y
    305. x = x0 + centerx
    306. y = -y0 + centery
    307.  
    308. rotate.uhat.plus:
    309. uhat(1) = uhat(1) + nhat(1) * speeddamp
    310. uhat(2) = uhat(2) + nhat(2) * speeddamp
    311. uhat(3) = uhat(3) + nhat(3) * speeddamp
    312.  
    313. rotate.uhat.minus:
    314. uhat(1) = uhat(1) - nhat(1) * speeddamp
    315. uhat(2) = uhat(2) - nhat(2) * speeddamp
    316. uhat(3) = uhat(3) - nhat(3) * speeddamp
    317.  
    318. rotate.vhat.plus:
    319. vhat(1) = vhat(1) + nhat(1) * speeddamp
    320. vhat(2) = vhat(2) + nhat(2) * speeddamp
    321. vhat(3) = vhat(3) + nhat(3) * speeddamp
    322.  
    323. rotate.vhat.minus:
    324. vhat(1) = vhat(1) - nhat(1) * speeddamp
    325. vhat(2) = vhat(2) - nhat(2) * speeddamp
    326. vhat(3) = vhat(3) - nhat(3) * speeddamp
    327.  
    328. rotate.counterclockwise:
    329. v1 = vhat(1)
    330. v2 = vhat(2)
    331. v3 = vhat(3)
    332. vhat(1) = vhat(1) + uhat(1) * speeddamp
    333. vhat(2) = vhat(2) + uhat(2) * speeddamp
    334. vhat(3) = vhat(3) + uhat(3) * speeddamp
    335. uhat(1) = uhat(1) - v1 * speeddamp
    336. uhat(2) = uhat(2) - v2 * speeddamp
    337. uhat(3) = uhat(3) - v3 * speeddamp
    338.  
    339. rotate.clockwise:
    340. v1 = vhat(1)
    341. v2 = vhat(2)
    342. v3 = vhat(3)
    343. vhat(1) = vhat(1) - uhat(1) * speeddamp
    344. vhat(2) = vhat(2) - uhat(2) * speeddamp
    345. vhat(3) = vhat(3) - uhat(3) * speeddamp
    346. uhat(1) = uhat(1) + v1 * speeddamp
    347. uhat(2) = uhat(2) + v2 * speeddamp
    348. uhat(3) = uhat(3) + v3 * speeddamp
    349.  
    350. strafe.camera.uhat.plus:
    351. camx = camx + uhat(1) * speedboost
    352. camy = camy + uhat(2) * speedboost
    353. camz = camz + uhat(3) * speedboost
    354.  
    355. strafe.camera.uhat.minus:
    356. camx = camx - uhat(1) * speedboost
    357. camy = camy - uhat(2) * speedboost
    358. camz = camz - uhat(3) * speedboost
    359.  
    360. strafe.camera.vhat.plus:
    361. camx = camx + vhat(1) * speedboost
    362. camy = camy + vhat(2) * speedboost
    363. camz = camz + vhat(3) * speedboost
    364.  
    365. strafe.camera.vhat.minus:
    366. camx = camx - vhat(1) * speedboost
    367. camy = camy - vhat(2) * speedboost
    368. camz = camz - vhat(3) * speedboost
    369.  
    370. strafe.camera.nhat.plus:
    371. camx = camx + nhat(1) * speedboost
    372. camy = camy + nhat(2) * speedboost
    373. camz = camz + nhat(3) * speedboost
    374.  
    375. strafe.camera.nhat.minus:
    376. camx = camx - nhat(1) * speedboost
    377. camy = camy - nhat(2) * speedboost
    378. camz = camz - nhat(3) * speedboost
    379.  
    380. normalize.screen.vectors:
    381. uhatmag = SQR(uhat(1) ^ 2 + uhat(2) ^ 2 + uhat(3) ^ 2)
    382. uhat(1) = uhat(1) / uhatmag: uhat(2) = uhat(2) / uhatmag: uhat(3) = uhat(3) / uhatmag
    383. vhatmag = SQR(vhat(1) ^ 2 + vhat(2) ^ 2 + vhat(3) ^ 2)
    384. vhat(1) = vhat(1) / vhatmag: vhat(2) = vhat(2) / vhatmag: vhat(3) = vhat(3) / vhatmag
    385. uhatdotvhat = uhat(1) * vhat(1) + uhat(2) * vhat(2) + uhat(3) * vhat(3)
    386. IF SQR(uhatdotvhat ^ 2) > .0005 THEN
    387.     BEEP ' ... if vectors are somenow not normalized. This has never happened.
    388.     uhat(1) = 0.8251367: uhat(2) = -0.564903: uhat(3) = -0.005829525
    389.     vhat(1) = 0.065519: vhat(2) = 0.08544215: vhat(3) = 0.9941866
    390. ' The normal vector points toward the eye.
    391. nhat(1) = uhat(2) * vhat(3) - uhat(3) * vhat(2)
    392. nhat(2) = uhat(3) * vhat(1) - uhat(1) * vhat(3)
    393. nhat(3) = uhat(1) * vhat(2) - uhat(2) * vhat(1)
    394. nhatmag = SQR(nhat(1) ^ 2 + nhat(2) ^ 2 + nhat(3) ^ 2)
    395. nhat(1) = nhat(1) / nhatmag: nhat(2) = nhat(2) / nhatmag: nhat(3) = nhat(3) / nhatmag
    396.  
    397. redraw:
    398. GOSUB normalize.screen.vectors
    399. GOSUB calculate.clippingplanes
    400. ' Project the three-space basis vectors onto the screen plane.
    401. xhatp(1) = xhat(1) * uhat(1) + xhat(2) * uhat(2) + xhat(3) * uhat(3)
    402. xhatp(2) = xhat(1) * vhat(1) + xhat(2) * vhat(2) + xhat(3) * vhat(3)
    403. yhatp(1) = yhat(1) * uhat(1) + yhat(2) * uhat(2) + yhat(3) * uhat(3)
    404. yhatp(2) = yhat(1) * vhat(1) + yhat(2) * vhat(2) + yhat(3) * vhat(3)
    405. zhatp(1) = zhat(1) * uhat(1) + zhat(2) * uhat(2) + zhat(3) * uhat(3)
    406. zhatp(2) = zhat(1) * vhat(1) + zhat(2) * vhat(2) + zhat(3) * vhat(3)
    407. GOSUB compute.visible.groups
    408. GOSUB project.particles
    409. GOSUB draw.all.objects
    410.  
    411. compute.visible.groups:
    412. numparticlevisible = 0
    413. numgroupvisible = 0
    414. closestdist2 = 10000 ^ 2
    415. closestgroup = 1
    416. ci = -1
    417. cf = -1
    418.  
    419. k = 1
    420. k = VectorGroup(k).Identity
    421.  
    422.     xcom = VectorGroup(k).COMx
    423.     ycom = VectorGroup(k).COMy
    424.     zcom = VectorGroup(k).COMz
    425.     thename$ = LTRIM$(RTRIM$(VectorGroup(k).ContentName))
    426.  
    427.     ' Force animation regardless of distance from camera.
    428.     IF thename$ = "Rotor" THEN
    429.         vecgroupindex = k
    430.         GOSUB animateforced
    431.     END IF
    432.  
    433.     dist2 = (camx - xcom) ^ 2 + (camy - ycom) ^ 2 + (camz - zcom) ^ 2
    434.  
    435.     IF dist2 < farplane(4) ^ 2 THEN
    436.  
    437.         groupinview = 1 ' Disable near plane group clipping to make things more interesting at zero distance.
    438.         'IF (xcom - camx) * nearplane(1) + (ycom - camy) * nearplane(2) + (zcom - camz) * nearplane(3) - nearplane(4) < 0 THEN groupinview = 0
    439.         'IF (xcom - camx) * farplane(1) + (ycom - camy) * farplane(2) + (zcom - camz) * farplane(3) - farplane(4) < 0 THEN groupinview = 0
    440.         IF (xcom - camx) * rightplane(1) + (ycom - camy) * rightplane(2) + (zcom - camz) * rightplane(3) - rightplane(4) < 0 THEN groupinview = 0
    441.         IF (xcom - camx) * leftplane(1) + (ycom - camy) * leftplane(2) + (zcom - camz) * leftplane(3) - leftplane(4) < 0 THEN groupinview = 0
    442.         IF (xcom - camx) * topplane(1) + (ycom - camy) * topplane(2) + (zcom - camz) * topplane(3) - topplane(4) < 0 THEN groupinview = 0
    443.         IF (xcom - camx) * bottomplane(1) + (ycom - camy) * bottomplane(2) + (zcom - camz) * bottomplane(3) - bottomplane(4) < 0 THEN groupinview = 0
    444.         IF groupinview = 1 THEN
    445.  
    446.             numgroupvisible = numgroupvisible + 1
    447.  
    448.             IF toggleanimatestandard = 1 THEN
    449.                 vecgroupindex = k
    450.                 GOSUB animatestandard
    451.             END IF
    452.  
    453.             IF dist2 < closestdist2 AND thename$ <> "Sparks" AND thename$ <> "Newtonian" THEN
    454.                 closestdist2 = dist2
    455.                 closestgroup = k
    456.             END IF
    457.  
    458.             IF k = closestgroup THEN ci = numparticlevisible
    459.  
    460.             FOR i = VectorGroup(k).FirstParticle TO VectorGroup(k).LastParticle
    461.                 vec(i, 1) = vecorig(i, 1) - camx
    462.                 vec(i, 2) = vecorig(i, 2) - camy
    463.                 vec(i, 3) = vecorig(i, 3) - camz
    464.                 GOSUB clip.particle.viewplanes
    465.             NEXT
    466.  
    467.             IF k = closestgroup THEN cf = numparticlevisible
    468.  
    469.         END IF
    470.  
    471.     END IF
    472.  
    473.     k = VectorGroup(k).Pointer
    474.     IF k = -999 THEN EXIT DO
    475.     k = VectorGroup(k).Identity
    476.  
    477.  
    478. IF ci <> -1 AND cf <> -1 THEN
    479.     FOR i = ci TO cf
    480.         vecvisiblecolor(i) = Yellow
    481.     NEXT
    482.  
    483.  
    484. clip.particle.viewplanes:
    485. ' requires i
    486. particleinview = 1
    487. fogswitch = -1
    488. ' Perform standard view plane clipping and implement fog effect.
    489. IF vec(i, 1) * nearplane(1) + vec(i, 2) * nearplane(2) + vec(i, 3) * nearplane(3) - nearplane(4) < 0 THEN particleinview = 0
    490. IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) < 0 THEN particleinview = 0
    491. IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) * .9 < 0 THEN fogswitch = 1
    492. IF vec(i, 1) * rightplane(1) + vec(i, 2) * rightplane(2) + vec(i, 3) * rightplane(3) - rightplane(4) < 0 THEN particleinview = 0
    493. IF vec(i, 1) * leftplane(1) + vec(i, 2) * leftplane(2) + vec(i, 3) * leftplane(3) - leftplane(4) < 0 THEN particleinview = 0
    494. IF vec(i, 1) * topplane(1) + vec(i, 2) * topplane(2) + vec(i, 3) * topplane(3) - topplane(4) < 0 THEN particleinview = 0
    495. IF vec(i, 1) * bottomplane(1) + vec(i, 2) * bottomplane(2) + vec(i, 3) * bottomplane(3) - bottomplane(4) < 0 THEN particleinview = 0
    496. IF particleinview = 1 THEN
    497.     numparticlevisible = numparticlevisible + 1
    498.     vecvisible(numparticlevisible, 1) = vec(i, 1)
    499.     vecvisible(numparticlevisible, 2) = vec(i, 2)
    500.     vecvisible(numparticlevisible, 3) = vec(i, 3)
    501.     'IF closestgroup = k THEN
    502.     'vecvisiblecolor(numparticlevisible) = Yellow
    503.     'ELSE
    504.     vecvisiblecolor(numparticlevisible) = veccolor(i)
    505.     'END IF
    506.     IF fogswitch = 1 THEN vecvisiblecolor(numparticlevisible) = Gray
    507.  
    508. project.particles:
    509. ' Project vectors onto the screen plane.
    510. FOR i = 1 TO numparticlevisible
    511.     vecvisibledotnhat = vecvisible(i, 1) * nhat(1) + vecvisible(i, 2) * nhat(2) + vecvisible(i, 3) * nhat(3)
    512.     vecvisiblepuv(i, 1) = vecvisible(i, 1) * uhat(1) + vecvisible(i, 2) * uhat(2) + vecvisible(i, 3) * uhat(3)
    513.     vecvisiblepuv(i, 2) = vecvisible(i, 1) * vhat(1) + vecvisible(i, 2) * vhat(2) + vecvisible(i, 3) * vhat(3)
    514.     vecvisiblepuvs(i, 1) = vecvisiblepuv(i, 1) * fovd / vecvisibledotnhat
    515.     vecvisiblepuvs(i, 2) = vecvisiblepuv(i, 2) * fovd / vecvisibledotnhat
    516.  
    517. draw.all.objects:
    518. GOSUB plot.particles
    519. ' Redraw compass.
    520. x = 30 * xhatp(1): y = 30 * xhatp(2): GOSUB convert
    521. LINE (centerx, centery)-(x, y), Red
    522. x = 30 * yhatp(1): y = 30 * yhatp(2): GOSUB convert
    523. LINE (centerx, centery)-(x, y), Green
    524. x = 30 * zhatp(1): y = 30 * zhatp(2): GOSUB convert
    525. LINE (centerx, centery)-(x, y), Blue
    526. IF togglehud = 1 THEN
    527.  
    528.     COLOR LimeGreen
    529.     LOCATE 2, 2: PRINT "- View Info -"
    530.     COLOR DarkKhaki
    531.     LOCATE 3, 2: PRINT "Particles:"; numparticlevisible
    532.     LOCATE 4, 2: PRINT "Groups:"; numgroupvisible
    533.     LOCATE 5, 2: PRINT "Depth:"; -farplane(4)
    534.     LOCATE 6, 2: PRINT "Adjust via [ ]"
    535.     COLOR LimeGreen
    536.     LOCATE 8, 2: PRINT "- Camera -"
    537.     COLOR DarkKhaki
    538.     LOCATE 9, 2: PRINT INT(camx); INT(camy); INT(camz)
    539.     COLOR LimeGreen
    540.     LOCATE 11, 2: PRINT "- Closest: -"
    541.     COLOR DarkKhaki
    542.     dist = INT(SQR((camx - VectorGroup(closestgroup).COMx) ^ 2 + (camy - VectorGroup(closestgroup).COMy) ^ 2 + (camz - VectorGroup(closestgroup).COMz) ^ 2))
    543.     LOCATE 12, 2: PRINT LTRIM$(RTRIM$(VectorGroup(closestgroup).ContentName)) + " (" + LTRIM$(RTRIM$(STR$(dist))) + ")"
    544.     COLOR LimeGreen
    545.     a$ = "MOVE - ALIGN": LOCATE 2, screenwidth / 8 - LEN(a$): PRINT a$
    546.     COLOR DarkKhaki
    547.     a$ = "q w e - x y z": LOCATE 3, screenwidth / 8 - LEN(a$): PRINT a$
    548.     a$ = "a s d - X Y Z": LOCATE 4, screenwidth / 8 - LEN(a$): PRINT a$
    549.     a$ = "i = invert ms": LOCATE 5, screenwidth / 8 - LEN(a$): PRINT a$
    550.     COLOR LimeGreen
    551.     a$ = "- ROTATE -": LOCATE 7, screenwidth / 8 - LEN(a$): PRINT a$
    552.     COLOR DarkKhaki
    553.     a$ = "7 8 9 Mouse": LOCATE 8, screenwidth / 8 - LEN(a$): PRINT a$
    554.     a$ = "4 5 6   +  ": LOCATE 9, screenwidth / 8 - LEN(a$): PRINT a$
    555.     a$ = "1 2 3 Wheel": LOCATE 10, screenwidth / 8 - LEN(a$): PRINT a$
    556.     COLOR LimeGreen
    557.     a$ = "- CONTROL -": LOCATE 12, screenwidth / 8 - LEN(a$): PRINT a$
    558.     COLOR DarkKhaki
    559.     a$ = "b = Create": LOCATE 13, screenwidth / 8 - LEN(a$): PRINT a$
    560.     a$ = "k = Destroy": LOCATE 14, screenwidth / 8 - LEN(a$): PRINT a$
    561.     a$ = "f = Fireworks": LOCATE 15, screenwidth / 8 - LEN(a$): PRINT a$
    562.     a$ = "t = Stop time": LOCATE 16, screenwidth / 8 - LEN(a$): PRINT a$
    563.     COLOR LimeGreen
    564.     a$ = "SPACE = Hide Info": LOCATE (screenheight / 16) - 3, (screenwidth / 8) / 2 - LEN(a$) / 2: PRINT a$
    565.     COLOR LimeGreen
    566.     a$ = "You See: " + LTRIM$(RTRIM$(VectorGroup(closestgroup).ContentName)): LOCATE (screenheight / 16) - 3, (screenwidth / 8) / 2 - LEN(a$) / 2: PRINT a$
    567.  
    568. calculate.clippingplanes:
    569. ' Calculate normal vectors to all clipping planes.
    570. nearplane(1) = -nhat(1)
    571. nearplane(2) = -nhat(2)
    572. nearplane(3) = -nhat(3)
    573. farplane(1) = nhat(1)
    574. farplane(2) = nhat(2)
    575. farplane(3) = nhat(3)
    576. rightplane(1) = (screenheight / 2) * fovd * uhat(1) - (screenheight / 2) * (screenwidth / 2) * nhat(1)
    577. rightplane(2) = (screenheight / 2) * fovd * uhat(2) - (screenheight / 2) * (screenwidth / 2) * nhat(2)
    578. rightplane(3) = (screenheight / 2) * fovd * uhat(3) - (screenheight / 2) * (screenwidth / 2) * nhat(3)
    579. mag = SQR((rightplane(1)) ^ 2 + (rightplane(2)) ^ 2 + (rightplane(3)) ^ 2)
    580. rightplane(1) = rightplane(1) / mag
    581. rightplane(2) = rightplane(2) / mag
    582. rightplane(3) = rightplane(3) / mag
    583. leftplane(1) = -(screenheight / 2) * fovd * uhat(1) - (screenheight / 2) * (screenwidth / 2) * nhat(1)
    584. leftplane(2) = -(screenheight / 2) * fovd * uhat(2) - (screenheight / 2) * (screenwidth / 2) * nhat(2)
    585. leftplane(3) = -(screenheight / 2) * fovd * uhat(3) - (screenheight / 2) * (screenwidth / 2) * nhat(3)
    586. mag = SQR((leftplane(1)) ^ 2 + (leftplane(2)) ^ 2 + (leftplane(3)) ^ 2)
    587. leftplane(1) = leftplane(1) / mag
    588. leftplane(2) = leftplane(2) / mag
    589. leftplane(3) = leftplane(3) / mag
    590. topplane(1) = (screenwidth / 2) * fovd * vhat(1) - (screenheight / 2) * (screenwidth / 2) * nhat(1)
    591. topplane(2) = (screenwidth / 2) * fovd * vhat(2) - (screenheight / 2) * (screenwidth / 2) * nhat(2)
    592. topplane(3) = (screenwidth / 2) * fovd * vhat(3) - (screenheight / 2) * (screenwidth / 2) * nhat(3)
    593. mag = SQR((topplane(1)) ^ 2 + (topplane(2)) ^ 2 + (topplane(3)) ^ 2)
    594. topplane(1) = topplane(1) / mag
    595. topplane(2) = topplane(2) / mag
    596. topplane(3) = topplane(3) / mag
    597. bottomplane(1) = -(screenwidth / 2) * fovd * vhat(1) - (screenheight / 2) * (screenwidth / 2) * nhat(1)
    598. bottomplane(2) = -(screenwidth / 2) * fovd * vhat(2) - (screenheight / 2) * (screenwidth / 2) * nhat(2)
    599. bottomplane(3) = -(screenwidth / 2) * fovd * vhat(3) - (screenheight / 2) * (screenwidth / 2) * nhat(3)
    600. mag = SQR((bottomplane(1)) ^ 2 + (bottomplane(2)) ^ 2 + (bottomplane(3)) ^ 2)
    601. bottomplane(1) = bottomplane(1) / mag
    602. bottomplane(2) = bottomplane(2) / mag
    603. bottomplane(3) = bottomplane(3) / mag
    604.  
    605. plot.particles:
    606. FOR i = 1 TO numparticlevisible - 1
    607.     x = vecvisiblepuvs(i, 1): y = vecvisiblepuvs(i, 2): GOSUB convert: x1 = x: y1 = y
    608.     x = vecvisiblepuvs(i + 1, 1): y = vecvisiblepuvs(i + 1, 2): GOSUB convert: x2 = x: y2 = y
    609.     IF ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) < 15 ^ 2 THEN
    610.         IF plotmode = 1 THEN
    611.             LINE (x1, y1)-(x2, y2), vecvisiblecolor(i)
    612.         ELSE
    613.             LINE (x1, y1)-(x2, y2), vecvisiblecolor(i), B
    614.         END IF
    615.     ELSE
    616.         CIRCLE (x1, y1), 1, vecvisiblecolor(i)
    617.     END IF
    618.  
    619. ' Data.
    620.  
    621. initialize.objects:
    622. particleindex = 0
    623. vecgroupcounter = 0
    624. gridsize = 550
    625. tilesize = 15
    626.  
    627. '__AAA
    628. vecgroupcounter = vecgroupcounter + 1
    629. vecgroupindex = vecgroupcounter
    630. VectorGroup(vecgroupindex).Identity = vecgroupindex
    631. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    632. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    633. VectorGroup(vecgroupindex).ContentName = "__AAA"
    634. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    635. FOR r = 1 TO 5
    636.     particleindex = particleindex + 1
    637.     vecorig(particleindex, 1) = -1000
    638.     vecorig(particleindex, 2) = -1000
    639.     vecorig(particleindex, 3) = -1000
    640.     veccolor(particleindex) = White
    641.     GOSUB calccom
    642. VectorGroup(vecgroupindex).LastParticle = particleindex
    643. GOSUB adjustcom
    644.  
    645. 'Banner
    646. vecgroupcounter = vecgroupcounter + 1
    647. vecgroupindex = vecgroupcounter
    648. VectorGroup(vecgroupindex).Identity = vecgroupindex
    649. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    650. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    651. VectorGroup(vecgroupindex).ContentName = "Banner"
    652. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    653. xl = -1.9: xr = 1.9: dx = .32
    654. yl = -1: yr = 1: dy = .32
    655. xl = xl * 4: xr = xr * 4: yl = yl * 4: yr = yr * 4
    656. xrange = 1 + INT((-xl + xr) / dx)
    657. yrange = 1 + INT((-yl + yr) / dy)
    658. t = 0
    659. FOR i = xl TO xr STEP dx
    660.     FOR j = yl TO yr STEP dy
    661.         particleindex = particleindex + 1
    662.         vecorig(particleindex, 1) = 70 + 1.25 * COS(i - 2 * t) ^ 2 - 1.25 * SIN(j - t) ^ 2
    663.         vecorig(particleindex, 2) = 30 + i
    664.         vecorig(particleindex, 3) = 40 + j
    665.         IF vecorig(particleindex, 1) < 70 THEN
    666.             veccolor(particleindex) = Red
    667.         ELSE
    668.             veccolor(particleindex) = DarkOrange
    669.         END IF
    670.         GOSUB calccom
    671.     NEXT
    672. VectorGroup(vecgroupindex).LastParticle = particleindex
    673. GOSUB adjustcom
    674.  
    675. 'Clock Face (Rolex)
    676. vecgroupcounter = vecgroupcounter + 1
    677. vecgroupindex = vecgroupcounter
    678. VectorGroup(vecgroupindex).Identity = vecgroupindex
    679. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    680. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    681. VectorGroup(vecgroupindex).ContentName = "Clock Face (Rolex)"
    682. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    683. FOR q = 0 TO 2 * pi STEP (2 * pi / 12) / 25
    684.     FOR r = 10 TO 12 STEP .5
    685.         particleindex = particleindex + 1
    686.         vecorig(particleindex, 1) = 40 + r * COS(q)
    687.         vecorig(particleindex, 2) = -20 + r
    688.         vecorig(particleindex, 3) = 40 + r * SIN(q)
    689.         veccolor(particleindex) = DarkSlateGray
    690.         GOSUB calccom
    691.     NEXT
    692. VectorGroup(vecgroupindex).LastParticle = particleindex
    693. GOSUB adjustcom
    694.  
    695. 'Clock Hands
    696. vecgroupcounter = vecgroupcounter + 1
    697. vecgroupindex = vecgroupcounter
    698. VectorGroup(vecgroupindex).Identity = vecgroupindex
    699. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    700. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    701. VectorGroup(vecgroupindex).ContentName = "Clock Hands"
    702. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    703. DIM hand(3)
    704. DIM hcol(3) AS LONG
    705. hand(1) = VAL(LEFT$(TIME$, 2)) * (2 * pi / 12)
    706. hand(2) = VAL(MID$(TIME$, 4, 2)) * (2 * pi / 60)
    707. hand(3) = VAL(RIGHT$(TIME$, 2)) * (2 * pi / 60)
    708. phase = pi / 2
    709. hcol(1) = Gold
    710. hcol(2) = Lime
    711. hcol(3) = OrangeRed
    712. FOR q = 1 TO 3
    713.     FOR r = 0 TO 5 + q STEP .1
    714.         particleindex = particleindex + 1
    715.         vecorig(particleindex, 1) = 40 + r * COS(hand(q) + phase)
    716.         vecorig(particleindex, 2) = -10
    717.         vecorig(particleindex, 3) = 40 + r * SIN(hand(q) + phase)
    718.         veccolor(particleindex) = hcol(q)
    719.         GOSUB calccom
    720.     NEXT
    721. VectorGroup(vecgroupindex).LastParticle = particleindex
    722. GOSUB adjustcom
    723.  
    724. 'Dirt
    725. h = 5
    726. FOR w = 1 TO 5
    727.     FOR u = -gridsize TO gridsize STEP tilesize
    728.         FOR v = -gridsize TO gridsize STEP tilesize
    729.             vecgroupcounter = vecgroupcounter + 1
    730.             vecgroupindex = vecgroupcounter
    731.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    732.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    733.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    734.             VectorGroup(vecgroupindex).ContentName = "Dirt"
    735.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    736.             FOR i = u TO u + tilesize STEP h
    737.                 FOR j = v TO v + tilesize STEP h
    738.                     IF RND > 1 - w / 5 THEN
    739.                         particleindex = particleindex + 1
    740.                         vecorig(particleindex, 1) = i + RND * h - RND * h
    741.                         vecorig(particleindex, 2) = j + RND * h - RND * h
    742.                         vecorig(particleindex, 3) = -(w - 1) * 70 - RND * 70
    743.                         IF RND > .5 THEN
    744.                             veccolor(particleindex) = DarkGoldenRod
    745.                         ELSE
    746.                             IF RND > .5 THEN
    747.                                 veccolor(particleindex) = SaddleBrown
    748.                             ELSE
    749.                                 veccolor(particleindex) = Sienna
    750.                             END IF
    751.                         END IF
    752.                         GOSUB calccom
    753.                     END IF
    754.                 NEXT
    755.             NEXT
    756.             VectorGroup(vecgroupindex).LastParticle = particleindex
    757.             GOSUB adjustcom
    758.         NEXT
    759.     NEXT
    760.  
    761. 'Grass and Puddles
    762. h = 2
    763. FOR u = -gridsize TO gridsize STEP tilesize
    764.     FOR v = -gridsize TO gridsize STEP tilesize
    765.         vecgroupcounter = vecgroupcounter + 1
    766.         vecgroupindex = vecgroupcounter
    767.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    768.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    769.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    770.         VectorGroup(vecgroupindex).ContentName = "Grass and Puddles"
    771.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    772.         FOR i = u TO u + tilesize STEP h
    773.             FOR j = v TO v + tilesize STEP h
    774.                 particleindex = particleindex + 1
    775.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    776.                 vecorig(particleindex, 2) = j + RND * h - RND * h
    777.                 vecorig(particleindex, 3) = .5 + 1 * COS((i - 15) * .08) - 1 * COS((j - 6) * .12)
    778.                 IF vecorig(particleindex, 3) > 0 THEN
    779.                     IF RND > .5 THEN
    780.                         veccolor(particleindex) = Green
    781.                     ELSE
    782.                         veccolor(particleindex) = ForestGreen
    783.                     END IF
    784.                 ELSE
    785.                     IF RND > .2 THEN
    786.                         veccolor(particleindex) = LightSeaGreen
    787.                     ELSE
    788.                         veccolor(particleindex) = Blue
    789.                     END IF
    790.                 END IF
    791.                 GOSUB calccom
    792.             NEXT
    793.         NEXT
    794.         VectorGroup(vecgroupindex).LastParticle = particleindex
    795.         GOSUB adjustcom
    796.     NEXT
    797.  
    798. 'Grave
    799. 'xloc = -90
    800. 'yloc = 0
    801. thickness = 2.5
    802. span = 20
    803. height = 30
    804. crux = 22
    805. FOR xloc = -90 TO -290 STEP -60
    806.     FOR yloc = 0 TO 180 STEP 45
    807.         FOR k = 0 TO height
    808.             vecgroupcounter = vecgroupcounter + 1
    809.             vecgroupindex = vecgroupcounter
    810.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    811.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    812.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    813.             VectorGroup(vecgroupindex).ContentName = "Grave"
    814.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    815.             FOR i = -thickness TO thickness STEP thickness / 2
    816.                 FOR j = -thickness TO thickness STEP thickness / 2
    817.                     particleindex = particleindex + 1
    818.                     vecorig(particleindex, 1) = xloc + i + (RND - .5) * 2
    819.                     vecorig(particleindex, 2) = yloc + j + (RND - .5) * 2
    820.                     vecorig(particleindex, 3) = k + (RND - .5) * 2
    821.                     IF RND > .5 THEN
    822.                         veccolor(particleindex) = SlateGray
    823.                     ELSE
    824.                         veccolor(particleindex) = DarkGray
    825.                     END IF
    826.                     GOSUB calccom
    827.                 NEXT
    828.             NEXT
    829.             VectorGroup(vecgroupindex).LastParticle = particleindex
    830.             GOSUB adjustcom
    831.         NEXT
    832.         FOR j = -span / 2 TO -thickness
    833.             vecgroupcounter = vecgroupcounter + 1
    834.             vecgroupindex = vecgroupcounter
    835.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    836.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    837.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    838.             VectorGroup(vecgroupindex).ContentName = "Grave"
    839.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    840.             FOR k = -thickness TO thickness STEP thickness / 2
    841.                 FOR i = -thickness TO thickness STEP thickness / 2
    842.                     particleindex = particleindex + 1
    843.                     vecorig(particleindex, 1) = xloc + i + (RND - .5) * 2
    844.                     vecorig(particleindex, 2) = yloc + j + (RND - .5) * 2
    845.                     vecorig(particleindex, 3) = crux + k + (RND - .5) * 2
    846.                     IF RND > .5 THEN
    847.                         veccolor(particleindex) = SlateGray
    848.                     ELSE
    849.                         veccolor(particleindex) = DarkGray
    850.                     END IF
    851.                     GOSUB calccom
    852.                 NEXT
    853.             NEXT
    854.             VectorGroup(vecgroupindex).LastParticle = particleindex
    855.             GOSUB adjustcom
    856.         NEXT
    857.         FOR j = thickness TO span / 2
    858.             vecgroupcounter = vecgroupcounter + 1
    859.             vecgroupindex = vecgroupcounter
    860.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    861.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    862.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    863.             VectorGroup(vecgroupindex).ContentName = "Grave"
    864.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    865.             FOR k = -thickness TO thickness STEP thickness / 2
    866.                 FOR i = -thickness TO thickness STEP thickness / 2
    867.                     particleindex = particleindex + 1
    868.                     vecorig(particleindex, 1) = xloc + i + (RND - .5) * 2
    869.                     vecorig(particleindex, 2) = yloc + j + (RND - .5) * 2
    870.                     vecorig(particleindex, 3) = crux + k + (RND - .5) * 2
    871.                     IF RND > .5 THEN
    872.                         veccolor(particleindex) = SlateGray
    873.                     ELSE
    874.                         veccolor(particleindex) = DarkGray
    875.                     END IF
    876.                     GOSUB calccom
    877.                 NEXT
    878.             NEXT
    879.             VectorGroup(vecgroupindex).LastParticle = particleindex
    880.             GOSUB adjustcom
    881.         NEXT
    882.     NEXT
    883.  
    884. 'Heaven's Bottom Layer
    885. h = 2
    886. FOR u = -gridsize TO gridsize STEP tilesize
    887.     FOR v = -gridsize TO gridsize STEP tilesize
    888.         vecgroupcounter = vecgroupcounter + 1
    889.         vecgroupindex = vecgroupcounter
    890.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    891.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    892.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    893.         VectorGroup(vecgroupindex).ContentName = "Heaven's Bottom Layer"
    894.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    895.         FOR i = u TO u + tilesize STEP h
    896.             FOR j = v TO v + tilesize STEP h
    897.                 particleindex = particleindex + 1
    898.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    899.                 vecorig(particleindex, 2) = j + RND * h - RND * h
    900.                 vecorig(particleindex, 3) = 420 - RND
    901.                 IF RND > .5 THEN
    902.                     veccolor(particleindex) = BlueViolet
    903.                 ELSE
    904.                     veccolor(particleindex) = Cyan
    905.                 END IF
    906.                 GOSUB calccom
    907.             NEXT
    908.         NEXT
    909.         VectorGroup(vecgroupindex).LastParticle = particleindex
    910.         GOSUB adjustcom
    911.     NEXT
    912.  
    913. 'Hell Spawn
    914. FOR u = -gridsize TO gridsize STEP tilesize
    915.     FOR v = -gridsize TO gridsize STEP tilesize
    916.         vecgroupcounter = vecgroupcounter + 1
    917.         vecgroupindex = vecgroupcounter
    918.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    919.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    920.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    921.         VectorGroup(vecgroupindex).ContentName = "Hell Spawn"
    922.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    923.         FOR i = u TO u + tilesize STEP tilesize / 5
    924.             FOR j = v TO v + tilesize STEP tilesize / 5
    925.                 particleindex = particleindex + 1
    926.                 vecorig(particleindex, 1) = i + RND * tilesize / 5
    927.                 vecorig(particleindex, 2) = j + RND * tilesize / 5
    928.                 vecorig(particleindex, 3) = -350 - RND * 70
    929.                 IF RND > .2 THEN
    930.                     veccolor(particleindex) = Red
    931.                 ELSE
    932.                     veccolor(particleindex) = DarkGoldenRod
    933.                 END IF
    934.                 GOSUB calccom
    935.             NEXT
    936.         NEXT
    937.         VectorGroup(vecgroupindex).LastParticle = particleindex
    938.         GOSUB adjustcom
    939.     NEXT
    940.  
    941. 'Icewall East
    942. h = 2
    943. FOR u = -gridsize TO gridsize STEP tilesize
    944.     FOR v = 0 TO 70 STEP tilesize
    945.         vecgroupcounter = vecgroupcounter + 1
    946.         vecgroupindex = vecgroupcounter
    947.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    948.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    949.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    950.         VectorGroup(vecgroupindex).ContentName = "Icewall East"
    951.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    952.         FOR i = u TO u + tilesize STEP h
    953.             FOR j = v TO v + tilesize STEP h
    954.                 particleindex = particleindex + 1
    955.                 vecorig(particleindex, 1) = gridsize
    956.                 vecorig(particleindex, 2) = i + RND * h - RND * h
    957.                 vecorig(particleindex, 3) = j + RND * h - RND * h
    958.                 IF RND > .5 THEN
    959.                     veccolor(particleindex) = White
    960.                 ELSE
    961.                     veccolor(particleindex) = Ivory
    962.                 END IF
    963.                 GOSUB calccom
    964.             NEXT
    965.         NEXT
    966.         VectorGroup(vecgroupindex).LastParticle = particleindex
    967.         GOSUB adjustcom
    968.     NEXT
    969.  
    970. 'Icewall South
    971. h = 2
    972. FOR u = -gridsize TO gridsize STEP tilesize
    973.     FOR v = 0 TO 70 STEP tilesize
    974.         vecgroupcounter = vecgroupcounter + 1
    975.         vecgroupindex = vecgroupcounter
    976.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    977.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    978.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    979.         VectorGroup(vecgroupindex).ContentName = "Icewall South"
    980.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    981.         FOR i = u TO u + tilesize STEP h
    982.             FOR j = v TO v + tilesize STEP h
    983.                 particleindex = particleindex + 1
    984.                 vecorig(particleindex, 1) = -gridsize
    985.                 vecorig(particleindex, 2) = i + RND * h - RND * h
    986.                 vecorig(particleindex, 3) = j + RND * h - RND * h
    987.                 IF RND > .5 THEN
    988.                     veccolor(particleindex) = White
    989.                 ELSE
    990.                     veccolor(particleindex) = Ivory
    991.                 END IF
    992.                 GOSUB calccom
    993.             NEXT
    994.         NEXT
    995.         VectorGroup(vecgroupindex).LastParticle = particleindex
    996.         GOSUB adjustcom
    997.     NEXT
    998.  
    999. 'Icewall North
    1000. h = 2
    1001. FOR u = -gridsize TO gridsize STEP tilesize
    1002.     FOR v = 0 TO 70 STEP tilesize
    1003.         vecgroupcounter = vecgroupcounter + 1
    1004.         vecgroupindex = vecgroupcounter
    1005.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1006.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1007.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1008.         VectorGroup(vecgroupindex).ContentName = "Icewall North"
    1009.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1010.         FOR i = u TO u + tilesize STEP h
    1011.             FOR j = v TO v + tilesize STEP h
    1012.                 particleindex = particleindex + 1
    1013.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    1014.                 vecorig(particleindex, 2) = gridsize
    1015.                 vecorig(particleindex, 3) = j + RND * h - RND * h
    1016.                 IF RND > .5 THEN
    1017.                     veccolor(particleindex) = White
    1018.                 ELSE
    1019.                     veccolor(particleindex) = Ivory
    1020.                 END IF
    1021.                 GOSUB calccom
    1022.             NEXT
    1023.         NEXT
    1024.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1025.         GOSUB adjustcom
    1026.     NEXT
    1027.  
    1028. 'Icewall West
    1029. h = 2
    1030. FOR u = -gridsize TO gridsize STEP tilesize
    1031.     FOR v = 0 TO 70 STEP tilesize
    1032.         vecgroupcounter = vecgroupcounter + 1
    1033.         vecgroupindex = vecgroupcounter
    1034.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1035.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1036.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1037.         VectorGroup(vecgroupindex).ContentName = "Icewall West"
    1038.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1039.         FOR i = u TO u + tilesize STEP h
    1040.             FOR j = v TO v + tilesize STEP h
    1041.                 particleindex = particleindex + 1
    1042.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    1043.                 vecorig(particleindex, 2) = -gridsize
    1044.                 vecorig(particleindex, 3) = j + RND * h - RND * h
    1045.                 IF RND > .5 THEN
    1046.                     veccolor(particleindex) = White
    1047.                 ELSE
    1048.                     veccolor(particleindex) = Ivory
    1049.                 END IF
    1050.                 GOSUB calccom
    1051.             NEXT
    1052.         NEXT
    1053.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1054.         GOSUB adjustcom
    1055.     NEXT
    1056.  
    1057. 'Lake of Fire
    1058. h = 2
    1059. FOR u = -gridsize TO gridsize STEP tilesize
    1060.     FOR v = -gridsize TO gridsize STEP tilesize
    1061.         vecgroupcounter = vecgroupcounter + 1
    1062.         vecgroupindex = vecgroupcounter
    1063.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1064.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1065.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1066.         VectorGroup(vecgroupindex).ContentName = "Lake of Fire"
    1067.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1068.         FOR i = u TO u + tilesize STEP h
    1069.             FOR j = v TO v + tilesize STEP h
    1070.                 particleindex = particleindex + 1
    1071.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    1072.                 vecorig(particleindex, 2) = j + RND * h - RND * h
    1073.                 vecorig(particleindex, 3) = -350 - 70 - RND
    1074.                 IF RND > .2 THEN
    1075.                     veccolor(particleindex) = Red
    1076.                 ELSE
    1077.                     veccolor(particleindex) = Indigo
    1078.                 END IF
    1079.                 GOSUB calccom
    1080.             NEXT
    1081.         NEXT
    1082.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1083.         GOSUB adjustcom
    1084.     NEXT
    1085.  
    1086. 'Megalith
    1087. ctrx = -90
    1088. ctry = -320
    1089. ctrz = 4
    1090. w = 8
    1091. h = 256
    1092. dens = 100
    1093. FOR k = 1 TO h STEP w
    1094.     FOR i = -h / 20 + k / 20 TO h / 20 - k / 20 STEP w
    1095.         FOR j = -h / 20 + k / 20 TO h / 20 - k / 20 STEP w
    1096.             vecgroupcounter = vecgroupcounter + 1
    1097.             vecgroupindex = vecgroupcounter
    1098.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    1099.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1100.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1101.             VectorGroup(vecgroupindex).ContentName = "Megalith"
    1102.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1103.             FOR q = 1 TO dens
    1104.                 particleindex = particleindex + 1
    1105.                 vecorig(particleindex, 1) = ctrx + i + (RND - .5) * w
    1106.                 vecorig(particleindex, 2) = ctry + j + (RND - .5) * w
    1107.                 vecorig(particleindex, 3) = ctrz + k + (RND - .5) * w
    1108.                 IF RND > .5 THEN
    1109.                     veccolor(particleindex) = Cyan
    1110.                 ELSE
    1111.                     veccolor(particleindex) = Teal
    1112.                 END IF
    1113.                 GOSUB calccom
    1114.             NEXT
    1115.             VectorGroup(vecgroupindex).LastParticle = particleindex
    1116.             GOSUB adjustcom
    1117.         NEXT
    1118.     NEXT
    1119.  
    1120. 'Moon
    1121. vecgroupcounter = vecgroupcounter + 1
    1122. vecgroupindex = vecgroupcounter
    1123. VectorGroup(vecgroupindex).Identity = vecgroupindex
    1124. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1125. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1126. VectorGroup(vecgroupindex).ContentName = "Moon"
    1127. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1128. radius = 4
    1129. au = 60
    1130. dx = (2 * pi / radius) * .05
    1131. dy = (2 * pi / radius) * .05
    1132. xl = 0: xr = 2 * pi
    1133. yl = 0: yr = pi
    1134. xrange = 1 + INT((-xl + xr) / dx)
    1135. yrange = 1 + INT((-yl + yr) / dy)
    1136. FOR i = 1 TO xrange
    1137.     FOR j = 1 TO yrange
    1138.         particleindex = particleindex + 1
    1139.         theta = i * dx - dx
    1140.         phi = j * dy - dy
    1141.         vecorig(particleindex, 1) = au + radius * SIN(phi) * COS(theta)
    1142.         vecorig(particleindex, 2) = radius * SIN(phi) * SIN(theta)
    1143.         vecorig(particleindex, 3) = 90 + radius * COS(phi)
    1144.         IF RND > .5 THEN
    1145.             veccolor(particleindex) = Gray
    1146.         ELSE
    1147.             veccolor(particleindex) = PaleGoldenRod
    1148.         END IF
    1149.         GOSUB calccom
    1150.     NEXT
    1151. VectorGroup(vecgroupindex).LastParticle = particleindex
    1152. GOSUB adjustcom
    1153.  
    1154. 'Pyramid
    1155. ctrx = -90
    1156. ctry = -120
    1157. ctrz = 4
    1158. w = 8
    1159. h = 56
    1160. dens = 50
    1161. FOR k = 1 TO h STEP w
    1162.     FOR i = -h / 2 + k / 2 TO h / 2 - k / 2 STEP w
    1163.         FOR j = -h / 2 + k / 2 TO h / 2 - k / 2 STEP w
    1164.             vecgroupcounter = vecgroupcounter + 1
    1165.             vecgroupindex = vecgroupcounter
    1166.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    1167.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1168.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1169.             VectorGroup(vecgroupindex).ContentName = "Pyramid"
    1170.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1171.             FOR q = 1 TO dens
    1172.                 particleindex = particleindex + 1
    1173.                 vecorig(particleindex, 1) = ctrx + i + (RND - .5) * w
    1174.                 vecorig(particleindex, 2) = ctry + j + (RND - .5) * w
    1175.                 vecorig(particleindex, 3) = ctrz + k + (RND - .5) * w
    1176.                 IF RND > .5 THEN
    1177.                     veccolor(particleindex) = DarkGoldenRod
    1178.                 ELSE
    1179.                     veccolor(particleindex) = GoldenRod
    1180.                 END IF
    1181.                 GOSUB calccom
    1182.             NEXT
    1183.             VectorGroup(vecgroupindex).LastParticle = particleindex
    1184.             GOSUB adjustcom
    1185.         NEXT
    1186.     NEXT
    1187.  
    1188. 'Rain
    1189. FOR u = -gridsize TO gridsize STEP tilesize
    1190.     FOR v = -gridsize TO gridsize STEP tilesize
    1191.         vecgroupcounter = vecgroupcounter + 1
    1192.         vecgroupindex = vecgroupcounter
    1193.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1194.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1195.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1196.         VectorGroup(vecgroupindex).ContentName = "Rain"
    1197.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1198.         FOR i = u TO u + tilesize STEP tilesize '/ 3
    1199.             FOR j = v TO v + tilesize STEP tilesize '/ 3
    1200.                 particleindex = particleindex + 1
    1201.                 vecorig(particleindex, 1) = i + RND * tilesize
    1202.                 vecorig(particleindex, 2) = j + RND * tilesize
    1203.                 vecorig(particleindex, 3) = RND * 70
    1204.                 IF RND > 5 THEN
    1205.                     veccolor(particleindex) = Aquamarine
    1206.                 ELSE
    1207.                     veccolor(particleindex) = DodgerBlue
    1208.                 END IF
    1209.                 GOSUB calccom
    1210.             NEXT
    1211.         NEXT
    1212.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1213.         GOSUB adjustcom
    1214.     NEXT
    1215.  
    1216. ''Rotor
    1217. 'vecgroupcounter = vecgroupcounter + 1
    1218. 'vecgroupindex = vecgroupcounter
    1219. 'VectorGroup(vecgroupindex).Identity = vecgroupindex
    1220. 'VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1221. 'VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1222. 'VectorGroup(vecgroupindex).ContentName = "Rotor"
    1223. 'VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1224. 'xc = 5
    1225. 'yc = 5
    1226. 'zc = 40
    1227. 'rad = 20
    1228. 'FOR k = 1 TO 600
    1229. '    xx = RND * 2 * rad - rad
    1230. '    yy = 0
    1231. '    zz = RND * 2 * rad - rad
    1232. '    IF xx ^ 2 + yy ^ 2 + zz ^ 2 < rad ^ 2 THEN
    1233. '        particleindex = particleindex + 1
    1234. '        vecorig(particleindex, 1) = xc + xx
    1235. '        vecorig(particleindex, 2) = yc + yy
    1236. '        vecorig(particleindex, 3) = zc + zz
    1237. '        vecorigrot(particleindex, 1) = 50
    1238. '        vecorigrot(particleindex, 2) = 50
    1239. '        vecorigrot(particleindex, 3) = 50
    1240. '        GOSUB calccom
    1241. '        veccolor(particleindex) = _RGB(INT(RND * 255), INT(RND * 255), INT(RND * 255))
    1242. '    END IF
    1243. 'NEXT
    1244. 'VectorGroup(vecgroupindex).LastParticle = particleindex
    1245. 'GOSUB adjustcom
    1246. 'VectorGroup(vecgroupindex).CORx = VectorGroup(vecgroupindex).COMx
    1247. 'VectorGroup(vecgroupindex).CORy = VectorGroup(vecgroupindex).COMy
    1248. 'VectorGroup(vecgroupindex).CORz = VectorGroup(vecgroupindex).COMz
    1249.  
    1250. 'Sky
    1251. h = 2
    1252. FOR u = -gridsize TO gridsize STEP tilesize
    1253.     FOR v = -gridsize TO gridsize STEP tilesize
    1254.         vecgroupcounter = vecgroupcounter + 1
    1255.         vecgroupindex = vecgroupcounter
    1256.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1257.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1258.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1259.         VectorGroup(vecgroupindex).ContentName = "Sky"
    1260.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1261.         FOR i = u TO u + tilesize STEP h
    1262.             FOR j = v TO v + tilesize STEP h
    1263.                 particleindex = particleindex + 1
    1264.                 vecorig(particleindex, 1) = i + RND * h - RND * h
    1265.                 vecorig(particleindex, 2) = j + RND * h - RND * h
    1266.                 vecorig(particleindex, 3) = 70 + RND * h - RND * h
    1267.                 IF RND > .5 THEN
    1268.                     veccolor(particleindex) = Snow
    1269.                 ELSE
    1270.                     veccolor(particleindex) = RoyalBlue
    1271.                 END IF
    1272.                 GOSUB calccom
    1273.             NEXT
    1274.         NEXT
    1275.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1276.         GOSUB adjustcom
    1277.     NEXT
    1278.  
    1279. 'Snake?
    1280. vecgroupcounter = vecgroupcounter + 1
    1281. vecgroupindex = vecgroupcounter
    1282. VectorGroup(vecgroupindex).Identity = vecgroupindex
    1283. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1284. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1285. VectorGroup(vecgroupindex).ContentName = "Snake?"
    1286. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1287. FOR i = -pi TO pi STEP .005
    1288.     particleindex = particleindex + 1
    1289.     vecorig(particleindex, 1) = -10 + 5 * COS(i)
    1290.     vecorig(particleindex, 2) = -20 + 5 * SIN(i)
    1291.     vecorig(particleindex, 3) = 25 - 3 * COS(6 * i) * SIN(3 * i)
    1292.     veccolor(particleindex) = Coral
    1293.     GOSUB calccom
    1294. VectorGroup(vecgroupindex).LastParticle = particleindex
    1295. GOSUB adjustcom
    1296.  
    1297. 'Sparks
    1298. vecgroupcounter = vecgroupcounter + 1
    1299. vecgroupindex = vecgroupcounter
    1300. VectorGroup(vecgroupindex).Identity = vecgroupindex
    1301. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1302. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1303. VectorGroup(vecgroupindex).ContentName = "Sparks"
    1304. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1305. u = 0
    1306. v = 0
    1307. FOR i = 1 TO 300
    1308.     particleindex = particleindex + 1
    1309.     vecorig(particleindex, 1) = RND - .5
    1310.     vecorig(particleindex, 2) = RND - .5
    1311.     vecorig(particleindex, 3) = 20 + RND - .5
    1312.     vecorigvel(particleindex, 1) = 8 * (RND - .5)
    1313.     vecorigvel(particleindex, 2) = 8 * (RND - .5)
    1314.     vecorigvel(particleindex, 3) = 20 * RND
    1315.     vecorigacc(particleindex, 1) = 0
    1316.     vecorigacc(particleindex, 2) = 0
    1317.     vecorigacc(particleindex, 3) = -40
    1318.     veccolor(particleindex) = _RGB(INT(RND * 255), INT(RND * 255), INT(RND * 255))
    1319.     GOSUB calccom
    1320. VectorGroup(vecgroupindex).LastParticle = particleindex
    1321. GOSUB adjustcom
    1322.  
    1323. 'Stars
    1324. h = 5
    1325. FOR w = 1 TO 5
    1326.     FOR u = -gridsize TO gridsize STEP tilesize
    1327.         FOR v = -gridsize TO gridsize STEP tilesize
    1328.             vecgroupcounter = vecgroupcounter + 1
    1329.             vecgroupindex = vecgroupcounter
    1330.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    1331.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1332.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1333.             VectorGroup(vecgroupindex).ContentName = "Stars"
    1334.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1335.             FOR i = u TO u + tilesize STEP h
    1336.                 FOR j = v TO v + tilesize STEP h
    1337.                     IF RND > 1 - w / 5 THEN
    1338.                         particleindex = particleindex + 1
    1339.                         vecorig(particleindex, 1) = i + RND * h - RND * h
    1340.                         vecorig(particleindex, 2) = j + RND * h - RND * h
    1341.                         vecorig(particleindex, 3) = w * 70 + RND * 70
    1342.                         IF RND > .5 THEN
    1343.                             veccolor(particleindex) = GhostWhite
    1344.                         ELSE
    1345.                             IF RND > .5 THEN
    1346.                                 veccolor(particleindex) = White
    1347.                             ELSE
    1348.                                 veccolor(particleindex) = DarkGray
    1349.                             END IF
    1350.                         END IF
    1351.                         GOSUB calccom
    1352.                     END IF
    1353.                 NEXT
    1354.             NEXT
    1355.             VectorGroup(vecgroupindex).LastParticle = particleindex
    1356.             GOSUB adjustcom
    1357.         NEXT
    1358.     NEXT
    1359.  
    1360. 'Sun
    1361. radius = 10
    1362. dx = .0628
    1363. dy = .0628
    1364. xl = 0: xr = 2 * pi
    1365. yl = 0: yr = pi
    1366. xrange = 1 + INT((-xl + xr) / dx)
    1367. yrange = 1 + INT((-yl + yr) / dy)
    1368. FOR i = 1 TO xrange STEP 10
    1369.     FOR j = 1 TO yrange STEP 10
    1370.         vecgroupcounter = vecgroupcounter + 1
    1371.         vecgroupindex = vecgroupcounter
    1372.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1373.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1374.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1375.         VectorGroup(vecgroupindex).ContentName = "Sun"
    1376.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1377.         FOR u = i TO i + 10 STEP 1
    1378.             FOR v = j TO j + 10 STEP 1
    1379.                 particleindex = particleindex + 1
    1380.                 theta = u * dx - dx
    1381.                 phi = v * dy - dy
    1382.                 vecorig(particleindex, 1) = radius * SIN(phi) * COS(theta)
    1383.                 vecorig(particleindex, 2) = radius * SIN(phi) * SIN(theta)
    1384.                 vecorig(particleindex, 3) = 90 + radius * COS(phi)
    1385.                 IF RND > .5 THEN
    1386.                     veccolor(particleindex) = Sunglow
    1387.                 ELSE
    1388.                     veccolor(particleindex) = SunsetOrange
    1389.                 END IF
    1390.                 GOSUB calccom
    1391.             NEXT
    1392.         NEXT
    1393.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1394.         GOSUB adjustcom
    1395.     NEXT
    1396.  
    1397. 'UFO!!!
    1398. vecgroupcounter = vecgroupcounter + 1
    1399. vecgroupindex = vecgroupcounter
    1400. VectorGroup(vecgroupindex).Identity = vecgroupindex
    1401. VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1402. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1403. VectorGroup(vecgroupindex).ContentName = "UFO!!!"
    1404. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1405. FOR q = 0 TO 2 * pi STEP (2 * pi / 12) / 25
    1406.     FOR r = 10 TO 12 STEP .5
    1407.         particleindex = particleindex + 1
    1408.         vecorig(particleindex, 1) = -90 + r * COS(q)
    1409.         vecorig(particleindex, 2) = -120 + r * SIN(q)
    1410.         vecorig(particleindex, 3) = 100 + r - 12
    1411.         veccolor(particleindex) = HotPink
    1412.         GOSUB calccom
    1413.     NEXT
    1414.     FOR r = 12 TO 10 STEP -.5
    1415.         particleindex = particleindex + 1
    1416.         vecorig(particleindex, 1) = -90 + r * COS(q)
    1417.         vecorig(particleindex, 2) = -120 + r * SIN(q)
    1418.         vecorig(particleindex, 3) = 100 - r + 12
    1419.         veccolor(particleindex) = Red
    1420.         GOSUB calccom
    1421.     NEXT
    1422. r = 5
    1423. h = 30
    1424. FOR w = 1 TO 400
    1425.     xx = (RND - .5) * 2 * r
    1426.     yy = (RND - .5) * 2 * r
    1427.     IF xx ^ 2 + yy ^ 2 < r ^ 2 THEN
    1428.         particleindex = particleindex + 1
    1429.         vecorig(particleindex, 1) = -90 + xx
    1430.         vecorig(particleindex, 2) = -120 + yy
    1431.         vecorig(particleindex, 3) = 100 - RND * h
    1432.         IF RND > .5 THEN
    1433.             veccolor(particleindex) = Aquamarine
    1434.         ELSE
    1435.             veccolor(particleindex) = Lime
    1436.         END IF
    1437.         GOSUB calccom
    1438.     END IF
    1439. VectorGroup(vecgroupindex).LastParticle = particleindex
    1440. GOSUB adjustcom
    1441.  
    1442. 'Waves or Particles? (1)
    1443. FOR i = 1 TO 5 STEP 1
    1444.     FOR k = 1 TO 5 STEP 1
    1445.         vecgroupcounter = vecgroupcounter + 1
    1446.         vecgroupindex = vecgroupcounter
    1447.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1448.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1449.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1450.         VectorGroup(vecgroupindex).ContentName = "Waves or Particles?"
    1451.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1452.         FOR u = i TO i + 1 STEP .05
    1453.             FOR v = k TO k + 1 STEP .05
    1454.                 particleindex = particleindex + 1
    1455.                 vecorig(particleindex, 1) = 70 + 7 * u
    1456.                 vecorig(particleindex, 2) = 80 + 1 * COS((u ^ 2 - v ^ 2))
    1457.                 vecorig(particleindex, 3) = 10 + 7 * v
    1458.                 IF vecorig(particleindex, 2) < 80 THEN
    1459.                     veccolor(particleindex) = DarkBlue
    1460.                 ELSE
    1461.                     veccolor(particleindex) = DeepPink
    1462.                 END IF
    1463.                 GOSUB calccom
    1464.             NEXT
    1465.         NEXT
    1466.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1467.         GOSUB adjustcom
    1468.     NEXT
    1469.  
    1470. 'Waves or Particles? (2)
    1471. FOR i = 1 TO 5 STEP 1
    1472.     FOR k = 1 TO 5 STEP 1
    1473.         vecgroupcounter = vecgroupcounter + 1
    1474.         vecgroupindex = vecgroupcounter
    1475.         VectorGroup(vecgroupindex).Identity = vecgroupindex
    1476.         VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1477.         VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1478.         VectorGroup(vecgroupindex).ContentName = "Waves or Particles?"
    1479.         VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1480.         FOR u = i TO i + 1 STEP .05
    1481.             FOR v = k TO k + 1 STEP .05
    1482.                 particleindex = particleindex + 1
    1483.                 vecorig(particleindex, 1) = -7 * u
    1484.                 vecorig(particleindex, 2) = 80 + 1 * COS(2 * ((u - 7) ^ 2 - (v - 5) ^ 2))
    1485.                 vecorig(particleindex, 3) = 10 + 7 * v
    1486.                 IF vecorig(particleindex, 2) < 80 THEN
    1487.                     veccolor(particleindex) = Magenta
    1488.                 ELSE
    1489.                     veccolor(particleindex) = Chocolate
    1490.                 END IF
    1491.                 GOSUB calccom
    1492.             NEXT
    1493.         NEXT
    1494.         VectorGroup(vecgroupindex).LastParticle = particleindex
    1495.         GOSUB adjustcom
    1496.     NEXT
    1497.  
    1498. 'File
    1499.     DO WHILE NOT EOF(1)
    1500.         INPUT #1, xx, yy, zz, co
    1501.         recordtype = 0
    1502.         IF xx = -111111 AND yy = -111111 AND zz = -111111 AND co = -111111 THEN ' Next record begins a group.
    1503.             recordtype = 111111
    1504.             INPUT #1, n$
    1505.             vecgroupcounter = vecgroupcounter + 1
    1506.             vecgroupindex = vecgroupcounter
    1507.             VectorGroup(vecgroupindex).Identity = vecgroupindex
    1508.             VectorGroup(vecgroupindex).Pointer = vecgroupindex + 1
    1509.             VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1510.             VectorGroup(vecgroupindex).ContentName = n$
    1511.             VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1512.         END IF
    1513.         IF xx = -222222 AND yy = -222222 AND zz = -222222 AND co = -222222 THEN ' Next record is position offset for entire group.
    1514.             recordtype = 222222
    1515.             INPUT #1, xx, yy, zz
    1516.             vecorigvel(particleindex, 1) = 0 'xx
    1517.             vecorigvel(particleindex, 2) = 0 'yy
    1518.             vecorigvel(particleindex, 3) = 0 'zz
    1519.         END IF
    1520.  
    1521.         IF xx = -333333 AND yy = -333333 AND zz = -333333 AND co = -333333 THEN ' Next record is previous particle's linear velocity vector.
    1522.             recordtype = 333333
    1523.             INPUT #1, xx, yy, zz
    1524.             vecorigvel(particleindex, 1) = xx
    1525.             vecorigvel(particleindex, 2) = yy
    1526.             vecorigvel(particleindex, 3) = zz
    1527.         END IF
    1528.         IF xx = -444444 AND yy = -444444 AND zz = -444444 AND co = -444444 THEN ' Next record is previous particle's linear acceleration vector.
    1529.             recordtype = 444444
    1530.             INPUT #1, xx, yy, zz
    1531.             vecorigacc(particleindex, 1) = xx
    1532.             vecorigacc(particleindex, 2) = yy
    1533.             vecorigacc(particleindex, 3) = zz
    1534.         END IF
    1535.         IF xx = -555555 AND yy = -555555 AND zz = -555555 AND co = -555555 THEN ' Next record is rotation point for entire group.
    1536.             recordtype = 555555
    1537.             INPUT #1, xx, yy, zz
    1538.             VectorGroup(vecgroupindex).CORx = xx
    1539.             VectorGroup(vecgroupindex).CORy = yy
    1540.             VectorGroup(vecgroupindex).CORz = zz
    1541.         END IF
    1542.         IF xx = -666666 AND yy = -666666 AND zz = -666666 AND co = -666666 THEN ' Next record is previous particle's angular velocity vector.
    1543.             recordtype = 666666
    1544.             INPUT #1, xx, yy, zz
    1545.             vecorigrot(particleindex, 1) = xx
    1546.             vecorigrot(particleindex, 2) = yy
    1547.             vecorigrot(particleindex, 3) = zz
    1548.         END IF
    1549.  
    1550.         IF xx = -999999 AND yy = -999999 AND zz = -999999 AND co = -999999 THEN ' Previous record was last in group.
    1551.             recordtype = 999999
    1552.             VectorGroup(vecgroupindex).LastParticle = particleindex
    1553.             GOSUB adjustcom
    1554.         END IF
    1555.         IF recordtype = 0 THEN ' Not a special record. Store position and color.
    1556.             particleindex = particleindex + 1
    1557.             vecorig(particleindex, 1) = xx
    1558.             vecorig(particleindex, 2) = yy
    1559.             vecorig(particleindex, 3) = zz
    1560.             SELECT CASE co
    1561.                 CASE 0
    1562.                     veccolor(particleindex) = Black
    1563.                 CASE 1
    1564.                     veccolor(particleindex) = Blue
    1565.                 CASE 2
    1566.                     veccolor(particleindex) = Green
    1567.                 CASE 3
    1568.                     veccolor(particleindex) = Cyan
    1569.                 CASE 4
    1570.                     veccolor(particleindex) = Red
    1571.                 CASE 5
    1572.                     veccolor(particleindex) = Purple
    1573.                 CASE 6
    1574.                     veccolor(particleindex) = Orange
    1575.                 CASE 7
    1576.                     veccolor(particleindex) = LightGray
    1577.                 CASE 8
    1578.                     veccolor(particleindex) = DarkGray
    1579.                 CASE 9
    1580.                     veccolor(particleindex) = LightBlue
    1581.                 CASE 10
    1582.                     veccolor(particleindex) = LightGreen
    1583.                 CASE 11
    1584.                     veccolor(particleindex) = LightCyan
    1585.                 CASE 12
    1586.                     veccolor(particleindex) = OrangeRed 'lightred
    1587.                 CASE 13
    1588.                     veccolor(particleindex) = Violet 'lightpurple
    1589.                 CASE 14
    1590.                     veccolor(particleindex) = Yellow
    1591.                 CASE 15
    1592.                     veccolor(particleindex) = White
    1593.                 CASE ELSE
    1594.                     veccolor(particleindex) = White
    1595.             END SELECT
    1596.             GOSUB calccom
    1597.         END IF
    1598.     LOOP
    1599.     CLOSE #1
    1600.  
    1601. '__ZZZ
    1602. vecgroupcounter = vecgroupcounter + 1
    1603. vecgroupindex = vecgroupcounter
    1604. VectorGroup(vecgroupindex).Identity = vecgroupindex
    1605. VectorGroup(vecgroupindex).Pointer = -999
    1606. VectorGroup(vecgroupindex).Lagger = vecgroupindex - 1
    1607. VectorGroup(vecgroupindex).ContentName = "__ZZZ"
    1608. VectorGroup(vecgroupindex).FirstParticle = particleindex + 1
    1609. FOR r = 1 TO 5
    1610.     particleindex = particleindex + 1
    1611.     vecorig(particleindex, 1) = -1000
    1612.     vecorig(particleindex, 2) = -1000
    1613.     vecorig(particleindex, 3) = -1000
    1614.     veccolor(particleindex) = White
    1615.     GOSUB calccom
    1616. VectorGroup(vecgroupindex).LastParticle = particleindex
    1617. GOSUB adjustcom
    1618.  
    1619. numparticleorig = particleindex
    1620.  
    1621. animateforced:
    1622. ' requires vecgroupindex
    1623. ' protects timevar
    1624. IF thename$ = "Rotor" THEN
    1625.     GOSUB resetcom
    1626.     FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1627.         x = vecorig(particleindex, 1) - VectorGroup(vecgroupindex).CORx
    1628.         y = vecorig(particleindex, 2) - VectorGroup(vecgroupindex).CORy
    1629.         z = vecorig(particleindex, 3) - VectorGroup(vecgroupindex).CORz
    1630.         ax = vecorigrot(particleindex, 1)
    1631.         ay = vecorigrot(particleindex, 2)
    1632.         az = vecorigrot(particleindex, 3)
    1633.         IF az <> 0 THEN
    1634.             da = timestep * az
    1635.             xx = x
    1636.             yy = y
    1637.             x = xx * COS(da) - yy * SIN(da)
    1638.             y = xx * SIN(da) + yy * COS(da)
    1639.         END IF
    1640.         IF ay <> 0 THEN
    1641.             da = timestep * ay
    1642.             xx = x
    1643.             zz = z
    1644.             x = xx * COS(da) + zz * SIN(da)
    1645.             z = -xx * SIN(da) + zz * COS(da)
    1646.         END IF
    1647.         IF ax <> 0 THEN
    1648.             da = timestep * ax
    1649.             yy = y
    1650.             zz = z
    1651.             y = yy * COS(da) - zz * SIN(da)
    1652.             z = yy * SIN(da) + zz * COS(da)
    1653.         END IF
    1654.         vecorig(particleindex, 1) = x + VectorGroup(vecgroupindex).CORx
    1655.         vecorig(particleindex, 2) = y + VectorGroup(vecgroupindex).CORy
    1656.         vecorig(particleindex, 3) = z + VectorGroup(vecgroupindex).CORz
    1657.         GOSUB calccom
    1658.     NEXT
    1659.     GOSUB adjustcom
    1660.  
    1661.  
    1662. animatestandard:
    1663. ' requires vecgroupindex
    1664. ' protects timevar
    1665. timestep = .001
    1666. timevar = timevar + timestep
    1667. IF timevar > 10 ^ 6 THEN timevar = 0
    1668. thename$ = LTRIM$(RTRIM$(VectorGroup(vecgroupindex).ContentName))
    1669. IF thename$ = "Clock Hands" THEN
    1670.     pin = VectorGroup(vecgroupindex).FirstParticle
    1671.     hands(1, 1) = VAL(LEFT$(TIME$, 2)) * (2 * pi / 12)
    1672.     hands(2, 1) = VAL(MID$(TIME$, 4, 2)) * (2 * pi / 60)
    1673.     hands(3, 1) = VAL(RIGHT$(TIME$, 2)) * (2 * pi / 60)
    1674.     FOR q = 1 TO 3
    1675.         FOR r = 0 TO 5 + q STEP .1
    1676.             pin = pin + 1
    1677.             vecorig(pin, 1) = 40 + r * COS(hands(q, 1) + pi / 2)
    1678.             vecorig(pin, 2) = -10
    1679.             vecorig(pin, 3) = 40 + r * SIN(hands(q, 1) + pi / 2)
    1680.         NEXT
    1681.     NEXT
    1682. IF thename$ = "Banner" THEN
    1683.     pin = VectorGroup(vecgroupindex).FirstParticle
    1684.     t = timevar / 50
    1685.     xl = -1.9: xr = 1.9: dx = .32
    1686.     yl = -1: yr = 1: dy = .32
    1687.     xl = xl * 4: xr = xr * 4: yl = yl * 4: yr = yr * 4
    1688.     FOR i = xl TO xr STEP dx
    1689.         FOR j = yl TO yr STEP dy
    1690.             pin = pin + 1
    1691.             vecorig(pin, 1) = 70 + 1.25 * COS(i - 2 * t) ^ 2 - 1.25 * SIN(j - t) ^ 2
    1692.             vecorig(pin, 2) = 30 + i
    1693.             vecorig(pin, 3) = 40 + j
    1694.             IF vecorig(pin, 1) < 70 THEN
    1695.                 veccolor(pin) = Red
    1696.             ELSE
    1697.                 veccolor(pin) = DarkOrange
    1698.             END IF
    1699.         NEXT
    1700.     NEXT
    1701. IF thename$ = "Hell Spawn" THEN
    1702.     FOR i = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1703.         vecorig(i, 3) = vecorig(i, 3) + .3
    1704.         IF vecorig(i, 3) > -350 THEN vecorig(i, 3) = -350 - 70
    1705.     NEXT
    1706. IF thename$ = "Moon" THEN
    1707.     t = timevar * .00001
    1708.     GOSUB resetcom
    1709.     FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1710.         xx = vecorig(particleindex, 1)
    1711.         yy = vecorig(particleindex, 2)
    1712.         vecorig(particleindex, 1) = xx * COS(t) - yy * SIN(t)
    1713.         vecorig(particleindex, 2) = xx * SIN(t) + yy * COS(t)
    1714.         GOSUB calccom
    1715.     NEXT
    1716.     GOSUB adjustcom
    1717. IF thename$ = "Rain" THEN
    1718.     FOR i = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1719.         vecorig(i, 3) = vecorig(i, 3) - .3
    1720.         IF vecorig(i, 3) < 0 THEN vecorig(i, 3) = 70
    1721.     NEXT
    1722. IF thename$ = "Snake?" THEN
    1723.     pin = VectorGroup(vecgroupindex).FirstParticle
    1724.     t = timevar * .1
    1725.     FOR i = -pi TO pi STEP .005
    1726.         vecorig(pin, 1) = -10 + 5 * COS(i + t)
    1727.         vecorig(pin, 2) = -20 + 5 * SIN(i + t)
    1728.         vecorig(pin, 3) = 25 - 3 * COS(6 * i + t) * SIN(3 * i + t)
    1729.         pin = pin + 1
    1730.     NEXT
    1731. IF thename$ = "Sparks" THEN
    1732.     GOSUB resetcom
    1733.     dt = timestep * 100
    1734.     FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1735.         vecorigvel(particleindex, 1) = vecorigvel(particleindex, 1) + vecorigacc(particleindex, 1) * dt
    1736.         vecorigvel(particleindex, 2) = vecorigvel(particleindex, 2) + vecorigacc(particleindex, 2) * dt
    1737.         vecorigvel(particleindex, 3) = vecorigvel(particleindex, 3) + vecorigacc(particleindex, 3) * dt
    1738.         vecorig(particleindex, 1) = vecorig(particleindex, 1) + vecorigvel(particleindex, 1) * dt
    1739.         vecorig(particleindex, 2) = vecorig(particleindex, 2) + vecorigvel(particleindex, 2) * dt
    1740.         vecorig(particleindex, 3) = vecorig(particleindex, 3) + vecorigvel(particleindex, 3) * dt
    1741.         IF vecorig(particleindex, 3) < 0 THEN
    1742.             vecorig(particleindex, 3) = 0
    1743.             vecorigvel(particleindex, 1) = vecorigvel(particleindex, 1) * .75
    1744.             vecorigvel(particleindex, 2) = vecorigvel(particleindex, 2) * .75
    1745.             vecorigvel(particleindex, 3) = -vecorigvel(particleindex, 3) * .5
    1746.         END IF
    1747.         GOSUB calccom
    1748.     NEXT
    1749.     GOSUB adjustcom
    1750. IF thename$ = "Newtonian" THEN
    1751.     GOSUB resetcom
    1752.     dt = timestep * 100
    1753.     FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1754.         vecorigvel(particleindex, 1) = vecorigvel(particleindex, 1) + vecorigacc(particleindex, 1) * dt
    1755.         vecorigvel(particleindex, 2) = vecorigvel(particleindex, 2) + vecorigacc(particleindex, 2) * dt
    1756.         vecorigvel(particleindex, 3) = vecorigvel(particleindex, 3) + vecorigacc(particleindex, 3) * dt
    1757.         vecorig(particleindex, 1) = vecorig(particleindex, 1) + vecorigvel(particleindex, 1) * dt
    1758.         vecorig(particleindex, 2) = vecorig(particleindex, 2) + vecorigvel(particleindex, 2) * dt
    1759.         vecorig(particleindex, 3) = vecorig(particleindex, 3) + vecorigvel(particleindex, 3) * dt
    1760.         IF vecorig(particleindex, 3) < 0 THEN
    1761.             vecorig(particleindex, 3) = 0
    1762.             vecorigvel(particleindex, 1) = vecorigvel(particleindex, 1) * .75
    1763.             vecorigvel(particleindex, 2) = vecorigvel(particleindex, 2) * .75
    1764.             vecorigvel(particleindex, 3) = -vecorigvel(particleindex, 3) * .5
    1765.         END IF
    1766.         GOSUB calccom
    1767.     NEXT
    1768.     GOSUB adjustcom
    1769.     IF INT(TIMER) - VectorGroup(vecgroupindex).EventTimer > 20000 THEN
    1770.         PRINT VectorGroup(vecgroupindex).EventTimer
    1771.         END
    1772.  
    1773.         VectorGroup(vecgroupindex).ContentName = "Destroyed"
    1774.     END IF
    1775. IF thename$ = "UFO!!!" THEN
    1776.     GOSUB resetcom
    1777.     dt = timestep
    1778.     FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1779.         xx = vecorig(particleindex, 1) - 150
    1780.         yy = vecorig(particleindex, 2) - 150
    1781.         vecorig(particleindex, 1) = xx * COS(dt) - yy * SIN(dt) + 150
    1782.         vecorig(particleindex, 2) = xx * SIN(dt) + yy * COS(dt) + 150
    1783.         GOSUB calccom
    1784.     NEXT
    1785.     GOSUB adjustcom
    1786.  
    1787.  
    1788. explode:
    1789. GOSUB resetcom
    1790. vecgroupindex = VectorGroup(1).Identity
    1791.     IF LTRIM$(RTRIM$(VectorGroup(vecgroupindex).ContentName)) = "Sparks" THEN
    1792.         FOR particleindex = VectorGroup(vecgroupindex).FirstParticle TO VectorGroup(vecgroupindex).LastParticle
    1793.             vecorig(particleindex, 1) = explodex
    1794.             vecorig(particleindex, 2) = explodey
    1795.             vecorig(particleindex, 3) = explodez
    1796.             vecorigvel(particleindex, 1) = (RND - .5) * 20
    1797.             vecorigvel(particleindex, 2) = (RND - .5) * 20
    1798.             vecorigvel(particleindex, 3) = (RND - .2) * 40
    1799.             vecorigacc(particleindex, 3) = -30
    1800.             GOSUB calccom
    1801.         NEXT
    1802.         GOSUB adjustcom
    1803.         EXIT DO
    1804.     END IF
    1805.     vecgroupindex = VectorGroup(vecgroupindex).Pointer
    1806.     IF vecgroupindex = -999 THEN EXIT DO
    1807.     vecgroupindex = VectorGroup(vecgroupindex).Identity
    1808.  
    1809. calccom:
    1810. VectorGroup(vecgroupindex).COMx = vecorig(particleindex, 1) + VectorGroup(vecgroupindex).COMx
    1811. VectorGroup(vecgroupindex).COMy = vecorig(particleindex, 2) + VectorGroup(vecgroupindex).COMy
    1812. VectorGroup(vecgroupindex).COMz = vecorig(particleindex, 3) + VectorGroup(vecgroupindex).COMz
    1813.  
    1814. adjustcom:
    1815. f = 1 + VectorGroup(vecgroupindex).LastParticle - VectorGroup(vecgroupindex).FirstParticle
    1816. VectorGroup(vecgroupindex).COMx = VectorGroup(vecgroupindex).COMx / f
    1817. VectorGroup(vecgroupindex).COMy = VectorGroup(vecgroupindex).COMy / f
    1818. VectorGroup(vecgroupindex).COMz = VectorGroup(vecgroupindex).COMz / f
    1819.  
    1820. resetcom:
    1821. VectorGroup(vecgroupindex).COMx = 0
    1822. VectorGroup(vecgroupindex).COMy = 0
    1823. VectorGroup(vecgroupindex).COMz = 0
    * Color32.BI (Filesize: 10.92 KB, Downloads: 140)
    * data-reddit.txt (Filesize: 144.4 KB, Downloads: 151)
    balls.png
    * balls.png (Filesize: 42.09 KB, Dimensions: 641x479, Views: 152)
    « Last Edit: February 06, 2019, 12:30:22 pm by STxAxTIC »
    You're not done when it works, you're done when it's right.

    FellippeHeitor

    • Guest
    Re: Reddit's mobile app loading animation
    « Reply #12 on: February 06, 2019, 01:36:39 pm »
    This is very satisfying to watch, STx! Destroying the rotor piece by piece was even more satisfying, I must say!

    Offline _vince

    • Seasoned Forum Regular
    • Posts: 422
      • View Profile
    Re: Reddit's mobile app loading animation
    « Reply #13 on: February 12, 2019, 03:53:51 pm »
    another variant, might be faster might be slower, needs a test.  Should be much faster for larger circles and possibly slower for smaller circles.  It draws the majority of the circle as a single LINE BF using Steve's algorithm

    Code: QB64: [Select]
    1. 'vince's implementation of bresenham circles using steve-like optimizations
    2. sub circlef(x, y, r, c as long)
    3.         x0 = r
    4.         y0 = 0
    5.         e = -r
    6.     rr = r/sqr(2)
    7.         do while y0 < x0
    8.                 if e <= 0 then
    9.                         y0 = y0 + 1
    10.             line (x-x0, y+y0)-(x-rr, y+y0), c, bf
    11.             line (x+x0, y+y0)-(x+rr, y+y0), c, bf
    12.                         line (x-x0, y-y0)-(x-rr, y-y0), c, bf
    13.             line (x+x0, y-y0)-(x+rr, y-y0), c, bf
    14.                         e = e + 2*y0
    15.                 else
    16.                         line (x-y0, y-x0)-(x+y0, y-x0), c, bf
    17.                         line (x-y0, y+x0)-(x+y0, y+x0), c, bf
    18.                         x0 = x0 - 1
    19.                         e = e - 2*x0
    20.                 end if
    21.         loop
    22.         line (x-r,y)-(x-rr,y),c,bf
    23.     line (x+r,y)-(x+rr,y),c,bf
    24.     line (x-rr+1, y-rr)-(x+rr-1,y+rr),c,bf
    25.