'+--------------------------------------------------------------------------+
'|                                                                          |
'|                                 ION.inc                                  |
'|                                                                          |
'|                              Version 1.00                                |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                                                                          |
'|                         Author Patrice TERRIER                           |
'|                                                                          |
'|                            copyright(c) 2010                             |
'|                                                                          |
'|                Patrice Terrier http://www.zapsolution.com                |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                  Project started on : 10-24-2010 (MM-DD-YYYY)            |
'|                        Last revised : 10-24-2010 (MM-DD-YYYY)            |
'+--------------------------------------------------------------------------+

%RAND_MAX  = 32767

MACRO PIx2 = 6.28318530718

TYPE TrsVec
    X          AS SINGLE
    Y          AS SINGLE
    Z          AS SINGLE
END TYPE

TYPE TrsRgb
    R          AS SINGLE
    G          AS SINGLE
    B          AS SINGLE
END TYPE

TYPE TrsQuat
    X          AS SINGLE
    Y          AS SINGLE
    Z          AS SINGLE
    W          AS SINGLE
END TYPE

TYPE Temitter
    X          AS SINGLE
    Y          AS SINGLE
    Z          AS SINGLE

    R          AS SINGLE
    G          AS SINGLE
    B          AS SINGLE

    oldposX    AS SINGLE
    oldposY    AS SINGLE
    oldposZ    AS SINGLE

    targetposX AS SINGLE
    targetposY AS SINGLE
    targetposZ AS SINGLE
END TYPE

TYPE Tattracter
    X          AS SINGLE
    Y          AS SINGLE
    Z          AS SINGLE

    R          AS SINGLE
    G          AS SINGLE
    B          AS SINGLE

    oldposX    AS SINGLE
    oldposY    AS SINGLE
    oldposZ    AS SINGLE

    targetposX AS SINGLE
    targetposY AS SINGLE
    targetposZ AS SINGLE
END TYPE

TYPE Tion
    X          AS SINGLE
    Y          AS SINGLE
    Z          AS SINGLE

    R          AS SINGLE
    G          AS SINGLE
    B          AS SINGLE

    iSize      AS SINGLE
    iSpeed     AS SINGLE
END TYPE

TYPE TrsMatrix
    m(15) AS SINGLE
END TYPE

GLOBAL newRGB() AS SINGLE
GLOBAL elist() AS Temitter
GLOBAL alist() AS Tattracter
GLOBAL ilist() AS Tion
GLOBAL billboardMat() AS SINGLE

GLOBAL g_nIons, g_nSize, g_nEmitters, g_nAttracters, g_nSpeed, g_nCameraSpeed AS LONG
GLOBAL g_rFrameTime AS SINGLE


TYPE rsTimer
  elapsed_time            AS SINGLE '// Time since last call to tick()
  waited_time             AS SINGLE '// Time waited so far by wait()
  wait_function_overhead  AS SINGLE
  highResCounterSupported AS LONG
  freq                    AS SINGLE '// high frequency system counts per second
  curr                    AS QUAD
  prev                    AS QUAD
  lowResCurr              AS DWORD  '// for low res timer if high res is unavailable
  lowResPrev              AS DWORD  '// for low res timer if high res is unavailable
END TYPE
GLOBAL rsT AS rsTimer
DECLARE SUB apiSleep LIB "KERNEL32.DLL" ALIAS "Sleep" (BYVAL dwMilliseconds AS DWORD)
DECLARE FUNCTION TimeGetTime LIB "WINMM.DLL" ALIAS "timeGetTime" () AS DWORD
DECLARE FUNCTION QueryPerformanceFrequency LIB "KERNEL32.DLL" ALIAS "QueryPerformanceFrequency" (lpFrequency AS QUAD) AS LONG
DECLARE FUNCTION QueryPerformanceCounter LIB "KERNEL32.DLL" ALIAS "QueryPerformanceCounter" (lpPerformanceCount AS QUAD) AS LONG
SUB TrsTimer()
    rsT.elapsed_time = 0.0
    rsT.waited_time = 0.0
    rsT.wait_function_overhead = 0.0
    '// init high- and low-res timers
    LOCAL nQuad1 AS QUAD
    rsT.highResCounterSupported = QueryPerformanceFrequency(nQuad1)
    rsT.freq = 1.0 / LO(LONG, nQuad1)
    apiSleep(1) ' // make Sleep() and timeGetTime() more accurate
    '// get first tick for high- and low-res timers
    QueryPerformanceCounter(nQuad1)
    rsT.curr = LO(LONG, nQuad1)
    rsT.lowResCurr = timeGetTime()
END SUB

'// return time elapsed since last call to tick()
FUNCTION TrsTick() AS SINGLE
    IF rsT.highResCounterSupported THEN
       rsT.prev = rsT.curr
       LOCAL nQuad1 AS QUAD
       QueryPerformanceCounter(nQuad1)
       rsT.curr = LO(LONG, nQuad1)
       IF rsT.curr >= rsT.prev THEN rsT.elapsed_time = (rsT.curr - rsT.prev) * rsT.freq
    ELSE
       rsT.lowResPrev = rsT.lowResCurr
       rsT.lowResCurr = timeGetTime()
       IF rsT.lowResCurr >= rsT.lowResPrev THEN rsT.elapsed_time = (rsT.lowResCurr - rsT.lowResPrev) * 0.001!
    END IF
    FUNCTION = rsT.elapsed_time
END FUNCTION

FUNCTION rsRandf(BYVAL x AS SINGLE) AS SINGLE
    'return x * (float(rand()) / float(RAND_MAX));
    FUNCTION = x * (RND(0, %RAND_MAX) / %RAND_MAX)
END FUNCTION

FUNCTION rsRandi(BYVAL x AS LONG) AS LONG
    'return rand() % x;
    FUNCTION = RND(0, %RAND_MAX) MOD x
END FUNCTION

SUB emitter_settargetpos(BYVAL i AS LONG, BYVAL x AS SINGLE, y AS SINGLE, z AS SINGLE)
    elist(i).oldposX = elist(i).X
    elist(i).oldposY = elist(i).Y
    elist(i).oldposZ = elist(i).Z
    elist(i).targetposX = x
    elist(i).targetposY = y
    elist(i).targetposZ = z
END SUB

SUB emitter_interppos(BYVAL i AS LONG, BYVAL n AS SINGLE)
    'pPos = oldpos * (1.0f - n) + targetpos * n
    elist(i).X = elist(i).oldposX * (1.0! - n) + elist(i).targetposX * n
    elist(i).Y = elist(i).oldposY * (1.0! - n) + elist(i).targetposY * n
    elist(i).Z = elist(i).oldposZ * (1.0! - n) + elist(i).targetposZ * n
END SUB

SUB emitter_update(BYVAL i AS LONG)
END SUB

SUB attracter_settargetpos(BYVAL i AS LONG, BYVAL x AS SINGLE, y AS SINGLE, z AS SINGLE)
    alist(i).oldposX = alist(i).X
    alist(i).oldposY = alist(i).Y
    alist(i).oldposZ = alist(i).Z
    alist(i).targetposX = x
    alist(i).targetposY = y
    alist(i).targetposZ = z
END SUB

SUB attracter_interppos(BYVAL i AS LONG, BYVAL n AS SINGLE)
    'pPos = oldpos * (1.0f - n) + targetpos * n
    alist(i).X = alist(i).oldposX * (1.0! - n) + alist(i).targetposX * n
    alist(i).Y = alist(i).oldposY * (1.0! - n) + alist(i).targetposY * n
    alist(i).Z = alist(i).oldposZ * (1.0! - n) + alist(i).targetposZ * n
END SUB

MACRO RS_EPSILON = 0.000001!
SUB quat_set(quat AS TrsQuat, BYVAL x AS SINGLE, BYVAL y AS SINGLE, BYVAL z AS SINGLE, BYVAL w AS SINGLE)
    quat.X = x
    quat.Y = y
    quat.Z = z
    quat.W = w
END SUB

SUB quat_make(quat AS TrsQuat, BYVAL a AS SINGLE, v AS TrsVec)
    IF A < RS_EPSILON AND A > -RS_EPSILON THEN
       quat.X = 0.0!
       quat.Y = 0.0!
       quat.Z = 0.0!
       quat.W = 1.0!
    ELSE
       A *= 0.5!
       LOCAL sintheta AS SINGLE
       sintheta = SIN(A)
       quat.X = sintheta * v.X
       quat.Y = sintheta * v.Y
       quat.Z = sintheta * v.Z
       quat.W = COS(A)
    END IF
END SUB

SUB quat_preMult(quatDest AS TrsQuat, quatSrce AS TrsQuat)
    LOCAL rX, rY, rZ, rW AS SINGLE

    rX = quatDest.X
    rY = quatDest.Y
    rZ = quatDest.Z
    rW = quatDest.W

    quatDest.X = rW * quatSrce.X + quatSrce.W * rX + rY * quatSrce.Z - quatSrce.Y * rZ
    quatDest.Y = rW * quatSrce.Y + quatSrce.W * rY + rZ * quatSrce.X - quatSrce.Z * rX
    quatDest.Z = rW * quatSrce.Z + quatSrce.W * rZ + rX * quatSrce.Y - quatSrce.X * rY
    quatDest.W = rW * quatSrce.W - rX * quatSrce.X - rY * quatSrce.Y - rZ * quatSrce.Z
END SUB

SUB identity(M AS TrsMatrix)
    M.m(0) = 1.0!
    M.m(1) = 0.0!
    M.m(2) = 0.0!
    M.m(3) = 0.0!
    M.m(4) = 0.0!
    M.m(5) = 1.0!
    M.m(6) = 0.0!
    M.m(7) = 0.0!
    M.m(8) = 0.0!
    M.m(9) = 0.0!
    M.m(10) = 1.0!
    M.m(11) = 0.0!
    M.m(12) = 0.0!
    M.m(13) = 0.0!
    M.m(14) = 0.0!
    M.m(15) = 1.0!
END SUB

SUB mat_fromQuat(M AS TrsMatrix, Q AS TrsQuat)
    LOCAL s, xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz AS SINGLE
    '// must have an axis
    IF (Q.X = 0.0! AND Q.Y = 0.0! AND Q.Z = 0.0!) THEN
       identity(m)
    ELSE
       s = 2.0 / (Q.X * Q.X + Q.Y * Q.Y + Q.Z * Q.Z + Q.W * Q.W)
       xs = Q.X * s
       ys = Q.Y * s
       zs = Q.Z * s
       wx = Q.W * xs
       wy = Q.W * ys
       wz = Q.W * zs
       xx = Q.X * xs
       xy = Q.X * ys
       xz = Q.X * zs
       yy = Q.Y * ys
       yz = Q.Y * zs
       zz = Q.Z * zs

       M.m(0) = 1.0! - yy - zz
       M.m(1) = xy + wz
       M.m(2) = xz - wy
       M.m(3) = 0.0!
       M.m(4) = xy - wz
       M.m(5) = 1.0! - xx - zz
       M.m(6) = yz + wx
       M.m(7) = 0.0!
       M.m(8) = xz + wy
       M.m(9) = yz - wx
       M.m(10) = 1.0! - xx - yy
       M.m(11) = 0.0!
       M.m(12) = 0.0!
       M.m(13) = 0.0!
       M.m(14) = 0.0!
       M.m(15) = 1.0!
    END IF
END SUB

SUB hsl2rgb(BYVAL h AS SINGLE, BYVAL s AS SINGLE, BYVAL l AS SINGLE, r AS SINGLE, g AS SINGLE, b AS SINGLE)
    '// hue influence
    h =  h MOD 1.0!
    IF (h < 0.166667) THEN '// full red, some green
       r = 1.0
       g = h * 6.0!
       b = 0.0
    ELSE
       IF (h < 0.5) THEN '// full green
          g = 1.0
          IF (h < 0.333333) THEN '// some red
             r = 1.0! - ((h - 0.166667!) * 6.0!)
             b = 0.0
          ELSE '// some blue
             b = (h - 0.333333!) * 6.0!
             r = 0.0
          END IF
       ELSE
          IF (h < 0.833333) THEN '// full blue
             b = 1.0
             IF (h < 0.666667) THEN '// some green
                g = 1.0! - ((h - 0.5!) * 6.0!)
                r = 0.0
             ELSE '// some red
                r = (h - 0.666667!) * 6.0!
                g = 0.0
             END IF
          ELSE '// full red, some blue
             r = 1.0
             b = 1.0! - ((h - 0.833333!) * 6.0!)
             g = 0.0
          END IF
       END IF
    END IF

    '// saturation influence
    r = 1.0! - (s * (1.0! - r))
    g = 1.0! - (s * (1.0! - g))
    b = 1.0! - (s * (1.0! - b))

    '// luminosity influence
    r *= l
    g *= l
    b *= l
END SUB

SUB ion_start(BYVAL nIon AS LONG)
    LOCAL i AS LONG, offset AS SINGLE
    i = rsRandi(g_nEmitters - 1)
    'iPos = elist(i).iPos
    ilist(nIon).X = elist(i).X
    ilist(nIon).Y = elist(i).Y
    ilist(nIon).Z = elist(i).Z

    offset = g_rFrameTime * ilist(nIon).iSpeed
    SELECT CASE LONG rsRandi(13)
    CASE 0
         ilist(nIon).X += offset
    CASE 1
         ilist(nIon).X -= offset
    CASE 2
         ilist(nIon).Y += offset
    CASE 3
         ilist(nIon).Y -= offset
    CASE 4
         ilist(nIon).Z += offset
    CASE 5
         ilist(nIon).Z -= offset
    CASE 6
         ilist(nIon).X += offset
         ilist(nIon).Y += offset
         ilist(nIon).Z += offset
    CASE 7
         ilist(nIon).X -= offset
         ilist(nIon).Y += offset
         ilist(nIon).Z += offset
    CASE 8
         ilist(nIon).X += offset
         ilist(nIon).Y -= offset
         ilist(nIon).Z += offset
    CASE 9
         ilist(nIon).X -= offset
         ilist(nIon).Y -= offset
         ilist(nIon).Z += offset
    CASE 10
         ilist(nIon).X += offset
         ilist(nIon).Y += offset
         ilist(nIon).Z -= offset
    CASE 11
         ilist(nIon).X -= offset
         ilist(nIon).Y += offset
         ilist(nIon).Z -= offset
    CASE 12
         ilist(nIon).X += offset
         ilist(nIon).Y -= offset
         ilist(nIon).Z -= offset
    CASE 13
         ilist(nIon).X -= offset
         ilist(nIon).Y -= offset
         ilist(nIon).Z -= offset
    END SELECT

    ilist(nIon).R = newRgb(0)
    ilist(nIon).G = newRgb(1)
    ilist(nIon).B = newRgb(2)

END SUB

FUNCTION normalize(v AS TrsVec) AS SINGLE
    LOCAL length AS SINGLE
    length = SQR(v.X * v.X + v.Y * v.Y + v.Z * v.Z)
    IF (length = 0.0!) THEN
       v.Y = 1.0!
    ELSE
       LOCAL normalizer AS SINGLE
       normalizer = 1.0! / length
       v.X *= normalizer
       v.Y *= normalizer
       v.Z *= normalizer
    END IF
    FUNCTION = length
END FUNCTION

SUB ion_update(BYVAL nIon AS LONG)
    LOCAL i, startOver AS LONG

    STATIC startOverDistance AS SINGLE
    STATIC force, tempvec AS TrsVec
    STATIC length, temp AS SINGLE

    force.X = 0.0!: force.Y = 0.0!: force.Z = 0.0!

    FOR i = 0 TO g_nEmitters - 1
        'tempvec = iPos - elist(i).iPos
        tempvec.X = ilist(nIon).X - elist(i).X
        tempvec.Y = ilist(nIon).Y - elist(i).Y
        tempvec.Z = ilist(nIon).Z - elist(i).Z
        length = normalize(tempvec)
        IF (length > 11000.0!) THEN startOver = 1
        IF (length <= 1.0!) THEN
           temp = 1.0!
        ELSE
           temp = 1.0! / length
        END IF
        'tempvec *= temp
        tempvec.X *= temp
        tempvec.Y *= temp
        tempvec.Z *= temp
       'force += tempvec
        force.X += tempvec.X
        force.Y += tempvec.Y
        force.Z += tempvec.Z
    NEXT
    startOverDistance = ilist(nIon).iSpeed * g_rFrameTime

    FOR i = 0 TO g_nAttracters - 1
        'tempvec = alist(i).iPos - iPos
        tempvec.X = alist(i).X - ilist(nIon).X
        tempvec.Y = alist(i).Y - ilist(nIon).Y
        tempvec.Z = alist(i).Z - ilist(nIon).Z
        length = normalize(tempvec)
        IF (length < startOverDistance) THEN startOver = 1
        IF (length <= 1.0!) THEN
           temp = 1.0!
        ELSE
           temp = 1.0! / length
        END IF
        'tempvec *= temp
        tempvec.X *= temp
        tempvec.Y *= temp
        tempvec.Z *= temp
        'force += tempvec
        force.X += tempvec.X
        force.Y += tempvec.Y
        force.Z += tempvec.Z
    NEXT
    '// Start this ion at an emitter if it gets too close to an attracter
    '// or too far from an emitter
    IF (startOver) THEN
       ion_start(nIon)
    ELSE
       normalize(force)
       'iPos += (force * g_rFrameTime * iSpeed)
       LOCAL offset AS SINGLE
       offset = g_rFrameTime * ilist(nIon).iSpeed
       ilist(nIon).X += force.X * offset
       ilist(nIon).Y += force.Y * offset
       ilist(nIon).Z += force.Z * offset
    END IF

END SUB

SUB ion_draw(BYVAL nIon AS LONG, BYVAL cPulse AS SINGLE)
    LOCAL x, y, z, p AS SINGLE
    x = ilist(nIon).X * billboardMat(0) + ilist(nIon).Y * billboardMat(4) + ilist(nIon).Z * billboardMat(8)
    y = ilist(nIon).X * billboardMat(1) + ilist(nIon).Y * billboardMat(5) + ilist(nIon).Z * billboardMat(9)
    z = ilist(nIon).X * billboardMat(2) + ilist(nIon).Y * billboardMat(6) + ilist(nIon).Z * billboardMat(10)
    glColor3f(ilist(nIon).R, ilist(nIon).G, ilist(nIon).B)
    glPushMatrix()
       glTranslatef(x, y, z)
       p = (cPulse * ilist(nIon).iSize) * 1.5
       glScalef(p, p, p)
       'glScalef(ilist(nIon).iSize, ilist(nIon).iSize, ilist(nIon).iSize)
       glCallList(1)
    glPopMatrix()
END SUB

SUB setTargets(BYVAL whichTarget AS LONG)

    LOCAL i AS LONG, rChange, rPosition, rHeight, rAngle, rCosAngle, rSinAngle AS SINGLE

    SELECT CASE LONG whichTarget
    CASE 0 '// random
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rsRandf(1000.0!) - 500.0!, rsRandf(1000.0!) - 500.0!, rsRandf(1000.0!) - 500.0!)
         NEXT
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rsRandf(1000.0!) - 500.0!, rsRandf(1000.0!) - 500.0!, rsRandf(1000.0!) - 500.0!)
         NEXT

    CASE 1 '// line (all emitters on one side, all attracters on the other)
         rPosition = -500.0!
         rChange = 1000.0! / (g_nEmitters + g_nAttracters - 1)
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rPosition, rPosition * 0.5!, 0.0!)
             rPosition += rChange
         NEXT
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rPosition, rPosition * 0.5!, 0.0!)
             rPosition += rChange
         NEXT

    CASE 2 '// line (emitters and attracters staggered)
         IF  (g_nEmitters > g_nAttracters) THEN
            rChange = 1000.0! / (g_nEmitters * 2 - 1)
         ELSE
            rChange = 1000.0! / (g_nAttracters * 2 - 1)
         END IF
         rPosition = -500.0!
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rPosition, rPosition * 0.5!, 0.0!)
             rPosition += rChange * 2.0!
         NEXT
         rPosition = -500.0! + rChange
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rPosition, rPosition * 0.5!, 0.0!)
             rPosition += rChange * 2.0!
         NEXT

    CASE 3 '// 2 lines (parallel)
         rChange = 1000.0! / (g_nEmitters * 2 - 1)
         rPosition = -500.0!
         rHeight = -525.0! + (g_nEmitters * 25)
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rPosition, rHeight, -50.0!)
             rPosition += rChange * 2.0!
         NEXT
         rChange = 1000.0! / (g_nAttracters * 2 - 1)
         rPosition = -500.0!
         rHeight = 525.0! - (g_nAttracters * 25)
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rPosition, rHeight, 50.0!)
             rPosition += rChange * 2.0!
         NEXT

    CASE 4 '// 2 lines (skewed)
         rChange = 1000.0! / (g_nEmitters * 2 - 1)
         rPosition = -500.0!
         rHeight = -525.0! + (g_nEmitters * 25)
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rPosition, rHeight, 0.0!)
             rPosition += rChange * 2.0!
         NEXT
         rChange = 1000.0! / (g_nAttracters * 2 - 1)
         rPosition = -500.0!
         rHeight = 525.0! - (g_nAttracters * 25)
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, 10.0!, rHeight, rPosition)
             rPosition += rChange * 2.0!
         NEXT

    CASE 5 '// random distribution across a plane
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rsRandf(1000.0!) - 500.0!, 0.0!, rsRandf(1000.0!) - 500.0!)
         NEXT
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rsRandf(1000.0!) - 500.0!, 0.0!, rsRandf(1000.0!) - 500.0!)
         NEXT

    CASE 6 '// random distribution across 2 planes
         rHeight = -525.0! + (g_nEmitters * 25)
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, rsRandf(1000.0!) - 500.0!, rHeight, rsRandf(1000.0!) - 500.0!)
         NEXT
         rHeight = 525.0! - (g_nAttracters * 25)
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, rsRandf(1000.0!) - 500.0!, rHeight, rsRandf(1000.0!) - 500.0!)
         NEXT

    CASE 7 '// 2 rings (1 inside and 1 outside)
         rAngle = 0.5!
         rChange = PIx2 / (g_nEmitters)
         FOR i = 0 TO g_nEmitters - 1
             rAngle += rChange
             rCosAngle = COS(rAngle) * 200.0!
             rSinAngle = SIN(rAngle) * 200.0!
             emitter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
         NEXT
         rAngle = 1.5!
         rChange = PIx2 / (g_nAttracters)
         FOR i = 0 TO g_nAttracters - 1
             rAngle += rChange
             rCosAngle = COS(rAngle) * 500.0!
             rSinAngle = SIN(rAngle) * 500.0!
             attracter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
         NEXT

    CASE 8 '// ring (all emitters on one side, all attracters on the other)
         rAngle = 0.5!
         rChange = PIx2 / (g_nEmitters + g_nAttracters)
         FOR i = 0 TO g_nEmitters - 1
             rAngle += rChange
             rCosAngle = COS(rAngle) * 500.0!
             rSinAngle = SIN(rAngle) * 500.0!
             emitter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
         NEXT
         FOR i = 0 TO g_nAttracters - 1
             rAngle += rChange
             rCosAngle = COS(rAngle) * 500.0!
             rSinAngle = SIN(rAngle) * 500.0!
             attracter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
         NEXT

    CASE 9 '// ring (emitters and attracters staggered)
         IF (g_nEmitters > g_nAttracters) THEN
            rChange = PIx2 / (g_nEmitters * 2)
         ELSE
            rChange = PIx2 / (g_nAttracters * 2)
         END IF
         rAngle = 0.5
         FOR i = 0 TO g_nEmitters - 1
             rCosAngle = COS(rAngle) * 500.0!
             rSinAngle = SIN(rAngle) * 500.0!
             emitter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
             rAngle += rChange * 2.0!
         NEXT
         rAngle = 0.5! + rChange
         FOR i = 0 TO g_nAttracters - 1
             rCosAngle = COS(rAngle) * 500.0!
             rSinAngle = SIN(rAngle) * 500.0!
             attracter_settargetpos(i, rCosAngle, rSinAngle, 0.0!)
             rAngle += rChange * 2.0!
         NEXT

    CASE 10 '// 2 points
         FOR i = 0 TO g_nEmitters - 1
             emitter_settargetpos(i, 500.0!, 100.0!, 50.0!)
         NEXT
         FOR i = 0 TO g_nAttracters - 1
             attracter_settargetpos(i, -500.0!, -100.0!, -50.0!)
         NEXT

    END SELECT
END SUB
