+ for (n = 0; n < st->blotCount; n++)
+ {
+ FLOAT x = rint (RAND_FLOAT_01) * 2 - 1;
+ FLOAT y = rint (RAND_FLOAT_01) * 2 - 1;
+ FLOAT z = rint (RAND_FLOAT_01) * 2 - 1;
+
+ x += RAND_FLOAT_PM1 * 0.3;
+ y += RAND_FLOAT_PM1 * 0.3;
+ z += RAND_FLOAT_PM1 * 0.3;
+
+ initBlot (&st->blots[n], x, y, z);
+ }
+
+ scaleBlotsToRadius1 (st);
+ randomlyRotateBlots (st);
+}
+
+/* set up the initial array of blots to be randomly distributed
+ * on the surface of a tetrahedron */
+static void setupBlotsTetrahedron (struct state *st)
+{
+ /* table of corners of the tetrahedron */
+ static const FLOAT cor[4][3] = { { 0.0, 1.0, 0.0 },
+ { -0.75, -0.5, -0.433013 },
+ { 0.0, -0.5, 0.866025 },
+ { 0.75, -0.5, -0.433013 } };
+
+ int n, c;
+
+ /* derive blotsPerSurface from blotCount, but then do the reverse
+ * since roundoff may have changed blotCount */
+ int blotsPerSurface = st->requestedBlotCount / 4;
+
+ st->blotCount = blotsPerSurface * 4;
+ st->blots = calloc (sizeof (Blot), st->blotCount);
+
+ for (n = 0; n < st->blotCount; n += 4)
+ {
+ /* pick a random point on a unit right triangle */
+ FLOAT rawx = RAND_FLOAT_01;
+ FLOAT rawy = RAND_FLOAT_01;
+
+ if ((rawx + rawy) > 1)
+ {
+ /* swap coords into place */
+ FLOAT t = 1.0 - rawx;
+ rawx = 1.0 - rawy;
+ rawy = t;
+ }
+
+ /* translate the point to be on each of the surfaces */
+ for (c = 0; c < 4; c++)
+ {
+ FLOAT x, y, z;
+
+ int c1 = (c + 1) % 4;
+ int c2 = (c + 2) % 4;
+
+ x = (cor[c1][0] - cor[c][0]) * rawx +
+ (cor[c2][0] - cor[c][0]) * rawy +
+ cor[c][0];
+
+ y = (cor[c1][1] - cor[c][1]) * rawx +
+ (cor[c2][1] - cor[c][1]) * rawy +
+ cor[c][1];
+
+ z = (cor[c1][2] - cor[c][2]) * rawx +
+ (cor[c2][2] - cor[c][2]) * rawy +
+ cor[c][2];
+
+ initBlot (&st->blots[n + c], x, y, z);
+ }
+ }
+
+ randomlyRotateBlots (st);
+}
+
+/* set up the initial array of blots to be an almost-evenly-distributed
+ * square sheet */
+static void setupBlotsSheet (struct state *st)
+{
+ int x, y;
+
+ int blotsPerDimension = floor (sqrt (st->requestedBlotCount));
+ FLOAT spaceBetween;
+
+ if (blotsPerDimension < 2)
+ {
+ blotsPerDimension = 2;
+ }
+
+ spaceBetween = 2.0 / (blotsPerDimension - 1);
+
+ st->blotCount = blotsPerDimension * blotsPerDimension;
+ st->blots = calloc (sizeof (Blot), st->blotCount);
+
+ for (x = 0; x < blotsPerDimension; x++)
+ {
+ for (y = 0; y < blotsPerDimension; y++)
+ {
+ FLOAT x1 = x * spaceBetween - 1.0;
+ FLOAT y1 = y * spaceBetween - 1.0;
+ FLOAT z1 = 0.0;
+
+ x1 += RAND_FLOAT_PM1 * spaceBetween / 3;
+ y1 += RAND_FLOAT_PM1 * spaceBetween / 3;
+ z1 += RAND_FLOAT_PM1 * spaceBetween / 2;
+
+ initBlot (&st->blots[x + y * blotsPerDimension], x1, y1, z1);
+ }
+ }
+
+ scaleBlotsToRadius1 (st);
+ randomlyReorderBlots (st);
+ randomlyRotateBlots (st);
+}
+
+/* set up the initial array of blots to be a swirlycone */
+static void setupBlotsSwirlyCone (struct state *st)
+{
+ FLOAT radSpace = 1.0 / (st->requestedBlotCount - 1);
+ FLOAT zSpace = radSpace * 2;
+ FLOAT rotAmt = RAND_FLOAT_PM1 * M_PI / 10;
+
+ int n;
+ FLOAT rot = 0.0;
+
+ st->blotCount = st->requestedBlotCount;
+ st->blots = calloc (sizeof (Blot), st->blotCount);
+
+ for (n = 0; n < st->blotCount; n++)
+ {
+ FLOAT radius = n * radSpace;
+ FLOAT x = cos (rot) * radius;
+ FLOAT y = sin (rot) * radius;
+ FLOAT z = n * zSpace - 1.0;
+
+ rot += rotAmt;
+ initBlot (&st->blots[n], x, y, z);
+ }
+
+ scaleBlotsToRadius1 (st);
+ randomlyReorderBlots (st);
+ randomlyRotateBlots (st);
+}
+
+/* forward declaration for recursive use immediately below */
+static void setupBlots (struct state *st);
+
+/* set up the blots to be two of the other choices, placed next to
+ * each other */
+static void setupBlotsDuo (struct state *st)