Exemple #1
0
/* Calculate new positions for all images.
 */
static void
set_new_positions(photopile_state *ss)
{
  ModeInfo *mi = ss->mi;
  int i;

  for (i = 0; i < MI_COUNT(mi)+1; ++i)
    {
      image *frame = ss->frames + i;
      GLfloat w = frame->w;
      GLfloat h = frame->h;
      GLfloat d = sqrt(w*w + h*h);
      GLfloat leave = frand(M_PI * 2.0);
      GLfloat enter = frand(M_PI * 2.0);

      /* start position */
      frame->pos[0] = frame->pos[3];

      /* end position */
      frame->pos[3].x = BELLRAND(MI_WIDTH(mi));
      frame->pos[3].y = BELLRAND(MI_HEIGHT(mi));
      frame->pos[3].angle = (frand(2.0) - 1.0) * max_tilt;

      /* Try to keep the images mostly inside the screen bounds */
      frame->pos[3].x = MAX(0.5*w, MIN(MI_WIDTH(mi)-0.5*w, frame->pos[3].x));
      frame->pos[3].y = MAX(0.5*h, MIN(MI_HEIGHT(mi)-0.5*h, frame->pos[3].y));

      /* intermediate points */
      frame->pos[1] = offset_pos(frame->pos[0], leave, d * (0.5 + frand(1.0)));
      frame->pos[2] = offset_pos(frame->pos[3], enter, d * (0.5 + frand(1.0)));
    }
}
Exemple #2
0
/* Start someone walking through the scene.
 */
static void
add_pedestrian (ModeInfo *mi)
{
  camera_configuration *bp = &bps[MI_SCREEN(mi)];
  pedestrian *p = (pedestrian *) calloc (1, sizeof(*p));
  static int id = 100;
  p->id = id++;
  p->length = 35;
  p->ratio = 0;
  p->pos.x = -p->length/2;
  p->pos.y = 3 + frand(10);
  p->pos.z = -1.5 + frand(4) + ((random() % 10) ? 0 : frand(8));
  p->frequency = 4 + frand(4);
  p->amplitude = 0.1 + ((random() % 10) ? BELLRAND(0.45) : BELLRAND(1.5));
  p->ratio = 0;
  p->speed = ((4 + frand(4) + ((random() % 10) ? 0 : frand(10)))
              * (random() & 1 ? 1 : -1)
              * speed_arg);

  if (bp->pedestrians)
    {
      pedestrian *p2;	/* add it to the end */
      for (p2 = bp->pedestrians; p2->next; p2 = p2->next)
        ;
      p2->next = p;
    }
  else
    {
      p->next = bp->pedestrians;
      bp->pedestrians = p;
    }
}
static void
reset_floater (ModeInfo *mi, floater *f)
{
  cow_configuration *bp = &bps[MI_SCREEN(mi)];

  f->y = -BOTTOM;
  f->x = f->ix;
  f->z = f->iz;

  /* Yes, I know I'm varying the force of gravity instead of varying the
     launch velocity.  That's intentional: empirical studies indicate
     that it's way, way funnier that way. */

  f->dy = 5.0;
  f->dx = 0;
  f->dz = 0;

  /* -0.18 max  -0.3 top -0.4 middle  -0.6 bottom */
  f->ddy = speed * (-0.6 + BELLRAND(0.45));
  f->ddx = 0;
  f->ddz = 0;

  f->spinner_p = !(random() % (12 * bp->nfloaters));

  if (! (random() % (30 * bp->nfloaters)))
    {
      f->dx = BELLRAND(1.8) * RANDSIGN();
      f->dz = BELLRAND(1.8) * RANDSIGN();
    }
}
static void
reset_floater (ModeInfo *mi, floater *f)
{
/*  toaster_configuration *bp = &bps[MI_SCREEN(mi)]; */

  GLfloat n = GRID_SIZE/2.0;
  GLfloat n2 = GRID_DEPTH/2.0;
  GLfloat delta = GRID_SIZE * speed / 200.0;

  f->dx = 0;
  f->dy = 0;
  f->dz = delta;

  f->dz += BELLRAND(delta) - delta/3;

  if (! (random() % 5)) {
    f->dx += (BELLRAND(delta*2) - delta);
    f->dy += (BELLRAND(delta*2) - delta);
  }

  if (! (random() % 40)) f->dz *= 10;    /* occasional speedy one */

  f->x = frand(n) - n/2;
  f->y = frand(n) - n/2;
  f->z = -n2 - frand(delta * 4);

  if (f->toaster_p)
    {
      f->loaded = 0;
      f->knob_pos = frand(180) - 90;
      f->handle_pos = ((random() & 1) ? 0.0 : 1.0);

      if (f->handle_pos > 0.8 && (! (random() % 5)))
        f->loaded = (random() & 3);  /* let's toast! */
    }
  else
    {
      if (! (random() % 10))
        f->toast_type = 1;	/* toast_bitten */
    }
}
Exemple #5
0
static void
init_stars (ModeInfo *mi)
{
  planetstruct *gp = &planets[MI_SCREEN(mi)];
  int i, j;
  int width  = MI_WIDTH(mi);
  int height = MI_HEIGHT(mi);
  int size = (width > height ? width : height);
  int nstars = size * size / 80;
  int max_size = 3;
  GLfloat inc = 0.5;
  int steps = max_size / inc;
  GLfloat scale = 1;

  if (MI_WIDTH(mi) > 2560) {  /* Retina displays */
    scale *= 2;
    nstars /= 2;
  }

  gp->starlist = glGenLists(1);
  glNewList(gp->starlist, GL_COMPILE);
  for (j = 1; j <= steps; j++)
    {
      glPointSize(inc * j * scale);
      glBegin (GL_POINTS);
      for (i = 0; i < nstars / steps; i++)
        {
          GLfloat d = 0.1;
          GLfloat r = 0.15 + frand(0.3);
          GLfloat g = r + frand(d) - d;
          GLfloat b = r + frand(d) - d;

          GLfloat x = frand(1)-0.5;
          GLfloat y = frand(1)-0.5;
          GLfloat z = ((random() & 1)
                       ? frand(1)-0.5
                       : (BELLRAND(1)-0.5)/12);   /* milky way */
          d = sqrt (x*x + y*y + z*z);
          x /= d;
          y /= d;
          z /= d;
          glColor3f (r, g, b);
          glVertex3f (x, y, z);
          gp->starcount++;
        }
      glEnd ();
    }
  glEndList ();

  check_gl_error("stars initialization");
}
Exemple #6
0
static void *
cwaves_init (Display *dpy, Window window)
{
  int i;
  XGCValues gcv;
  state *st = (state *) calloc (1, sizeof (*st));

  st->dpy = dpy;
  st->window = window;
  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);

  st->debug_p = get_boolean_resource (dpy, "debug", "Boolean");
  st->scale = get_integer_resource (dpy, "scale", "Integer");
  if (st->scale <= 0) st->scale = 1;
  st->ncolors = get_integer_resource (dpy, "ncolors", "Integer");
  if (st->ncolors < 4) st->ncolors = 4;
  st->colors = (XColor *) malloc (sizeof(*st->colors) * (st->ncolors+1));
  make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
                        st->colors, &st->ncolors,
                        True, 0, False);

  st->gc = XCreateGC (st->dpy, st->window, 0, &gcv);
  st->delay = get_integer_resource (dpy, "delay", "Integer");

  st->nwaves  = get_integer_resource (dpy, "nwaves", "Integer");
  st->waves  = (wave *) calloc (st->nwaves,  sizeof(*st->waves));

  for (i = 0; i < st->nwaves; i++)
    {
      st->waves[i].scale  = frand(0.03) + 0.005;
      st->waves[i].offset = frand(M_PI);
      st->waves[i].delta  = (BELLRAND(2)-1) / 15.0;
    }

  return st;
}
/* Given a newly-created gear, place it next to its parent in the scene,
   with its teeth meshed and the proper velocity.  Returns False if it
   didn't work.  (Call this a bunch of times until either it works, or
   you decide it's probably not going to.)
 */
static Bool
place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p)
{
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];

  /* If this gear takes up more than 1/3rd of the screen, it's no good.
   */
  if (((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_width ||
      ((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_height)
    {
      if (verbose_p && debug_placement_p && 0)
        fprintf (stderr,
                 "%s: placement: too big: %.2f @ %.2f vs %.2f x %.2f\n",
                 progname,
                 (g->r + g->tooth_h), gear_size,
                 pp->vp_width, pp->vp_height);
      pp->debug_size_failures++;
      return False;
    }

  /* Compute this gear's velocity.
   */
  if (! parent)
    {
      g->ratio = 0.8 + BELLRAND(0.4);  /* 0.8-1.2 = 8-12rpm @ 60fps */
      g->th = frand (90) * ((random() & 1) ? 1.0 : -1.0);
    }
  else if (coaxial_p)
    {
      g->ratio = parent->ratio; /* bound gears have the same ratio */
      g->th = parent->th;
      g->rpm = parent->rpm;
      g->wobble = parent->wobble;
    }
  else
    {
      /* Gearing ratio is the ratio of the number of teeth to previous gear
         (which is also the ratio of the circumferences.)
       */
      g->ratio = (double) parent->nteeth / (double) g->nteeth;

      /* Set our initial rotation to match that of the previous gear,
         multiplied by the gearing ratio.  (This is finessed later,
         once we know the exact position of the gear relative to its
         parent.)
      */
      g->th = -(parent->th * g->ratio);

      if (g->nteeth & 1)    /* rotate 1/2 tooth-size if odd number of teeth */
        {
          double off = (180.0 / g->nteeth);
          if (g->th > 0)
            g->th += off;
          else
            g->th -= off;
        }

      /* ratios are cumulative for all gears in the train. */
      g->ratio *= parent->ratio;
    }


  /* Place the gear relative to the parent.
   */

  if (! parent)
    {
      gear *rg = farthest_gear (mi, False);
      double right = (rg ? rg->x + rg->r + rg->tooth_h : 0);
      if (right < pp->layout_left) /* place off screen */
        right = pp->layout_left;

      g->x = right + g->r + g->tooth_h + (0.01 / gear_size);
      g->y = 0;
      g->z = 0;

      if (debug_one_gear_p)
        g->x = 0;
    }
  else if (coaxial_p)
    {
      double off = pp->plane_displacement;

      g->x = parent->x;
      g->y = parent->y;
      g->z = parent->z + (g->r > parent->r      /* small gear on top */
                          ? -off : off);

      if (parent->r > g->r)     /* mark which is top and which is bottom */
        {
          parent->coax_p = 1;
          g->coax_p      = 2;
          parent->wobble = 0;   /* looks bad when axle moves */
        }
      else
        {
          parent->coax_p = 2;
          g->coax_p      = 1;
          g->wobble      = 0;
        }

      g->coax_thickness      = parent->thickness;
      parent->coax_thickness = g->thickness;

      /* Don't let the train get too close to or far from the screen.
         If we're getting too close, give up on this gear.
         (But getting very far away is fine.)
       */
      if (g->z >=  off * 4 ||
          g->z <= -off * 4)
        {
          if (verbose_p && debug_placement_p)
            fprintf (stderr, "%s: placement: bad depth: %.2f\n",
                     progname, g->z);
          pp->debug_position_failures++;
          return False;
        }
    }
  else                          /* position it somewhere next to the parent. */
    {
      double r_off = parent->r + g->r;
      int angle;

      if ((random() % 3) != 0)
        angle = (random() % 240) - 120;   /* mostly -120 to +120 degrees */
      else
        angle = (random() % 360) - 180;   /* sometimes -180 to +180 degrees */

      g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
      g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
      g->z = parent->z;

      /* If the angle we picked would have positioned this gear
         more than halfway off screen, that's no good. */
      if (g->y > pp->vp_top ||
          g->y < pp->vp_bottom)
        {
          if (verbose_p && debug_placement_p)
            fprintf (stderr, "%s: placement: out of bounds: %s\n",
                     progname, (g->y > pp->vp_top ? "top" : "bottom"));
          pp->debug_position_failures++;
          return False;
        }

      /* avoid accidentally changing sign of "th" in the math below. */
      g->th += (g->th > 0 ? 360 : -360);

      /* Adjust the rotation of the gear so that its teeth line up with its
         parent, based on the position of the gear and the current rotation
         of the parent.
       */
      {
        double p_c = 2 * M_PI * parent->r;  /* circumference of parent */
        double g_c = 2 * M_PI * g->r;       /* circumference of g  */

        double p_t = p_c * (angle/360.0);   /* distance travelled along
                                               circumference of parent when
                                               moving "angle" degrees along
                                               parent. */
        double g_rat = p_t / g_c;           /* if travelling that distance
                                               along circumference of g,
                                               ratio of g's circumference
                                               travelled. */
        double g_th = 360.0 * g_rat;        /* that ratio in degrees */

        g->th += angle + g_th;
      }
    }

  if (debug_one_gear_p)
    {
      compute_rpm (mi, g);
      return True;
    }

  /* If the position we picked for this gear would cause it to already
     be visible on the screen, give up.  This can happen when the train
     is growing backwards, and we don't want to see gears flash into
     existence.
   */
  if (g->x - g->r - g->tooth_h < pp->render_right)
    {
      if (verbose_p && debug_placement_p)
        fprintf (stderr, "%s: placement: out of bounds: left\n", progname);
      pp->debug_position_failures++;
      return False;
    }

  /* If the position we picked for this gear causes it to overlap
     with any earlier gear in the train, give up.
   */
  {
    int i;

    for (i = pp->ngears-1; i >= 0; i--)
      {
        gear *og = pp->gears[i];

        if (og == g) continue;
        if (og == parent) continue;
        if (g->z != og->z) continue;    /* Ignore unless on same layer */

        /* Collision detection without sqrt:
             d = sqrt(a^2 + b^2)   d^2 = a^2 + b^2
             d < r1 + r2           d^2 < (r1 + r2)^2
         */
        if (((g->x - og->x) * (g->x - og->x) +
             (g->y - og->y) * (g->y - og->y)) <
            ((g->r + g->tooth_h + og->r + og->tooth_h) *
             (g->r + g->tooth_h + og->r + og->tooth_h)))
          {
            if (verbose_p && debug_placement_p)
              fprintf (stderr, "%s: placement: collision with %lu\n",
                       progname, og->id);
            pp->debug_position_failures++;
            return False;
          }
      }
  }

  compute_rpm (mi, g);


  /* Make deeper gears be darker.
   */
  {
    double depth = g->z / pp->plane_displacement;
    double brightness = 1 + (depth / 6);
    double limit = 0.4;
    if (brightness < limit)   brightness = limit;
    if (brightness > 1/limit) brightness = 1/limit;
    g->color[0]  *= brightness;
    g->color[1]  *= brightness;
    g->color[2]  *= brightness;
    g->color2[0] *= brightness;
    g->color2[1] *= brightness;
    g->color2[2] *= brightness;
  }

  /* If a single frame of animation would cause the gear to rotate by
     more than 1/2 the size of a single tooth, then it won't look right:
     the gear will appear to be turning at some lower harmonic of its
     actual speed.
   */
  {
    double ratio = g->ratio * spin_speed;
    double blur_limit = 180.0 / g->nteeth;

    if (ratio > blur_limit)
      g->motion_blur_p = 1;

    if (!coaxial_p)
      {
        /* ride until the wheels fall off... */
        if (ratio > blur_limit * 0.7) g->wobble += (random() % 2);
        if (ratio > blur_limit * 0.9) g->wobble += (random() % 2);
        if (ratio > blur_limit * 1.1) g->wobble += (random() % 2);
        if (ratio > blur_limit * 1.3) g->wobble += (random() % 2);
        if (ratio > blur_limit * 1.5) g->wobble += (random() % 2);
        if (ratio > blur_limit * 1.7) g->wobble += (random() % 2);
      }
  }

  return True;
}
/* Create and return a new gear sized for placement next to or on top of
   the given parent gear (if any.)  Returns 0 if out of memory.
 */
static gear *
new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p)
{
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
  gear *g = (gear *) calloc (1, sizeof (*g));
  int loop_count = 0;
  static unsigned long id = 0;  /* only used in debugging output */

  if (!g) return 0;
  if (coaxial_p && !parent) abort();
  g->id = ++id;

  g->coax_displacement = pp->plane_displacement;

  while (1)
    {
      loop_count++;
      if (loop_count > 1000)
        /* The only time we loop in here is when making a coaxial gear, and
           trying to pick a radius that is either significantly larger or
           smaller than its parent.  That shouldn't be hard, so something
           must be really wrong if we can't do that in only a few tries.
         */
        abort();

      /* Pick the size of the teeth.
       */
      if (parent && !coaxial_p) /* adjascent gears need matching teeth */
        {
          g->tooth_w = parent->tooth_w;
          g->tooth_h = parent->tooth_h;
          g->thickness  = parent->thickness;
          g->thickness2 = parent->thickness2;
          g->thickness3 = parent->thickness3;
        }
      else                 /* gears that begin trains get any size they want */
        {
          double scale = (1.0 + BELLRAND(4.0)) * gear_size;
          g->tooth_w = 0.007 * scale;
          g->tooth_h = 0.005 * scale;
          g->thickness  = g->tooth_h * (0.1 + BELLRAND(1.5));
          g->thickness2 = g->thickness / 4;
          g->thickness3 = g->thickness;
        }

      /* Pick the number of teeth, and thus, the radius.
       */
      {
        double c;

      AGAIN:
        g->nteeth = 3 + (random() % 97);    /* from 3 to 100 teeth */

        if (g->nteeth < 7 && (random() % 5) != 0)
          goto AGAIN;   /* Let's make very small tooth-counts more rare */

        c = g->nteeth * g->tooth_w * 2;     /* circumference = teeth + gaps */
        g->r = c / (M_PI * 2);              /* c = 2 pi r  */
      }


      /* Are we done now?
       */
      if (! coaxial_p) break;   /* yes */
      if (g->nteeth == parent->nteeth) continue; /* ugly */
      if (g->r  < parent->r * 0.6) break;  /* g much smaller than parent */
      if (parent->r < g->r  * 0.6) break;  /* g much larger than parent  */
    }

  /* g->tooth_slope = (parent ? -parent->tooth_slope : 4); */

  /* Colorize
   */
  g->color[0] = 0.5 + frand(0.5);
  g->color[1] = 0.5 + frand(0.5);
  g->color[2] = 0.5 + frand(0.5);
  g->color[3] = 1.0;

  g->color2[0] = g->color[0] * 0.85;
  g->color2[1] = g->color[1] * 0.85;
  g->color2[2] = g->color[2] * 0.85;
  g->color2[3] = g->color[3];


  /* Decide on shape of gear interior:
     - just a ring with teeth;
     - that, plus a thinner in-set "plate" in the middle;
     - that, plus a thin raised "lip" on the inner plate;
     - or, a wide lip (really, a thicker third inner plate.)
   */
  if ((random() % 10) == 0)
    {
      /* inner_r can go all the way in; there's no inset disc. */
      g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
      g->inner_r2 = 0;
      g->inner_r3 = 0;
    }
  else
    {
      /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
      g->inner_r  = (g->r * 0.5)  + frand((g->r - g->tooth_h) * 0.4);
      g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
      g->inner_r3 = 0;

      if (g->inner_r2 > (g->r * 0.2))
        {
          int nn = (random() % 10);
          if (nn <= 2)
            g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
          else if (nn <= 7 && g->inner_r2 >= 0.1)
            g->inner_r3 = g->inner_r2 - 0.01;
        }
    }

  /* Coaxial gears need to have the same innermost hole size (for the axle.)
     Use whichever of the two is smaller.  (Modifies parent.)
   */
  if (coaxial_p)
    {
      double hole1 = (g->inner_r3 ? g->inner_r3 :
                      g->inner_r2 ? g->inner_r2 :
                      g->inner_r);
      double hole2 = (parent->inner_r3 ? parent->inner_r3 :
                      parent->inner_r2 ? parent->inner_r2 :
                      parent->inner_r);
      double hole = (hole1 < hole2 ? hole1 : hole2);
      if (hole <= 0) abort();

      if      (g->inner_r3) g->inner_r3 = hole;
      else if (g->inner_r2) g->inner_r2 = hole;
      else                  g->inner_r  = hole;

      if      (parent->inner_r3) parent->inner_r3 = hole;
      else if (parent->inner_r2) parent->inner_r2 = hole;
      else                       parent->inner_r  = hole;
    }

  /* If we have three discs, sometimes make the middle disc be spokes.
   */
  if (g->inner_r3 && ((random() % 5) == 0))
    {
      g->spokes = 2 + BELLRAND (5);
      g->spoke_thickness = 1 + frand(7.0);
      if (g->spokes == 2 && g->spoke_thickness < 2)
        g->spoke_thickness += 1;
    }

  /* Sometimes add little nubbly bits, if there is room.
   */
  if (g->nteeth > 5)
    {
      double size = 0;
      involute_biggest_ring (g, 0, &size, 0);
      if (size > g->r * 0.2 && (random() % 5) == 0)
        {
          g->nubs = 1 + (random() % 16);
          if (g->nubs > 8) g->nubs = 1;
        }
    }

  if (g->inner_r3 > g->inner_r2) abort();
  if (g->inner_r2 > g->inner_r) abort();
  if (g->inner_r  > g->r) abort();

  /* Decide how complex the polygon model should be.
   */
  {
    double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
    if (pix <= 2.5)      g->size = INVOLUTE_SMALL;
    else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
    else                 g->size = INVOLUTE_LARGE;
  }

  g->base_p = !parent;

  return g;
}
Exemple #9
0
static void
state_change (ModeInfo *mi)
{
  voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
  double now = double_time();

  if (vp->dragging)
    {
      vp->last_time = now;
      vp->adding = 0;
      vp->zooming = 0;
      return;
    }

  switch (vp->mode)
    {
    case MODE_WAITING:
      if (vp->last_time + zoom_delay <= now)
        {
          node *tn = vp->nodes;
          vp->zoom_toward[0] = (tn ? tn->x : 0.5);
          vp->zoom_toward[1] = (tn ? tn->y : 0.5);

          vp->mode = MODE_ZOOMING;
          vp->zooming = 1;

          vp->last_time = now;
        }
      break;

    case MODE_ADDING:
      if (vp->last_time + point_delay <= now)
        {
          add_node (vp, 
                    BELLRAND(0.5) + 0.25, 
                    BELLRAND(0.5) + 0.25);
          vp->last_time = now;
          vp->adding--;
          if (vp->adding <= 0)
            {
              vp->adding = 0;
              vp->mode = MODE_WAITING;
              vp->last_time = now;
            }
        }
      break;

    case MODE_ZOOMING:
      {
        zoom_points (vp);
        if (vp->zooming <= 0)
          {
            vp->mode = MODE_ADDING;
            vp->adding = npoints;
            vp->last_time = now;
          }
      }
      break;

    default:
      abort();
    }
}
Exemple #10
0
/* Re-randomize the state of one strip.
 */
static void
reset_strip (ModeInfo *mi, strip *s)
{
  matrix_configuration *mp = &mps[MI_SCREEN(mi)];
  int i;
  Bool time_displayed_p = False;  /* never display time twice in one strip */

  memset (s, 0, sizeof(*s));
  s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
  s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5));      /* shift top slightly */
  s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
  s->spinner_y = 0;

  s->dx = 0;
/*  s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
  s->dy = 0;
  s->dz = (BELLRAND(0.02) * speed);

  s->spinner_speed = (BELLRAND(0.3) * speed);

  s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
  s->spin_tick  = 0;

  s->wave_position = 0;
  s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
  s->wave_tick  = 0;

  for (i = 0; i < GRID_SIZE; i++)
    if (do_clock &&
        !time_displayed_p &&
        (i < GRID_SIZE-5) &&   /* display approx. once per 5 strips */
	!(random() % (GRID_SIZE-5)*5))
      {
        unsigned int j;
	char text[80];
        time_t now = time ((time_t *) 0);
        struct tm *tm = localtime (&now);
	strftime (text, sizeof(text)-1, timefmt, tm);

	/* render time into the strip */
	for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
	  {
	    s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
	    s->highlight[i] = True;
	  }

        time_displayed_p = True;
      }
    else
      {
	int draw_p = (random() % 7);
	int spin_p = (draw_p && !(random() % 20));
	int g = (draw_p
		 ? mp->glyph_map[(random() % mp->nglyphs)] + 1
		 : 0);
	if (spin_p) g = -g;
	s->glyphs[i] = g;
	s->highlight[i] = False;
      }

  s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
}
Exemple #11
0
/* Start a new cluster of lines going.
   Pick their anim type, and in, mid, and out positions.
 */
static void
reset_lines (ModeInfo *mi)
{
  fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
  int i;
  line *prev = 0;
  GLfloat minx, maxx, miny, maxy, minz, maxz, maxw, maxh;

  sc->rotation.x = 5 - BELLRAND(10);
  sc->rotation.y = 5 - BELLRAND(10);
  sc->rotation.z = 5 - BELLRAND(10);

  switch (random() % 8)
    {
    case 0:  sc->anim_type = SCROLL_TOP;    break;
    case 1:  sc->anim_type = SCROLL_BOTTOM; break;
    default: sc->anim_type = SPIN;          break;
    }

  minx = sc->left_margin  * 0.9;
  maxx = sc->right_margin * 0.9;

  miny = sc->bottom_margin * 0.9;
  maxy = sc->top_margin    * 0.9;

  minz = sc->left_margin  * 5;
  maxz = sc->right_margin * 2;

  maxw = sc->font_wrap_pixels * sc->font_scale;
  maxh = max_lines * sc->line_height * sc->font_scale;
 
  if (maxw > maxx - minx)
    maxw = maxx - minx;
  if (maxh > maxy - miny)
    maxh = maxy - miny;
      
  if (alignment_random_p)
    sc->alignment = (random() % 3) - 1;

  if      (sc->alignment == -1) maxx -= maxw;
  else if (sc->alignment ==  1) minx += maxw;
  else                          minx += maxw/2, maxx -= maxw/2;

  miny += maxh/2;
  maxy -= maxh/2;

  sc->mid.x = minx + frand (maxx - minx);
  if (sc->anim_type == SPIN)
    sc->mid.y = miny + BELLRAND (maxy - miny);
  else
    sc->mid.y = miny + frand (maxy - miny);

  sc->in.x  = BELLRAND(sc->right_margin * 2) - sc->right_margin;
  sc->out.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;

  sc->in.y  = miny + frand(maxy - miny);
  sc->out.y = miny + frand(maxy - miny);

  sc->in.z  = minz + frand(maxz - minz);
  sc->out.z = minz + frand(maxz - minz);

  sc->mid.z = 0;

  if (sc->anim_type == SPIN && sc->in.z  > 0) sc->in.z  /= 4;
  if (sc->anim_type == SPIN && sc->out.z > 0) sc->out.z /= 4;

  for (i = 0; i < max_lines; i++)
    {
      line *line = make_line (sc, (i == 0));
      if (!line) break;			/* no text available */
      if (i >= min_lines &&
          (!line->text || !*line->text))	/* blank after min */
        break;
    }

  for (i = 0; i < sc->nlines; i++)
    {
      line *line = sc->lines[i];
      if (!prev)
        {
          line->from.y = sc->bottom_margin;
          line->to.y   = 0;
        }
      else
        {
          line->from.y = prev->from.y - prev->height;
          line->to.y   = prev->to.y   - prev->height;
        }
      line->cluster_pos = i;
      line->cluster_size = sc->nlines;
      prev = line;
    }
}
Exemple #12
0
ENTRYPOINT void 
init_gears (ModeInfo *mi)
{
  gears_configuration *bp;
  int wire = MI_IS_WIREFRAME(mi);
  int i;

  if (!bps) {
    bps = (gears_configuration *)
      calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration));
    if (!bps) {
      fprintf(stderr, "%s: out of memory\n", progname);
      exit(1);
    }
  }

  bp = &bps[MI_SCREEN(mi)];

  bp->glx_context = init_GL(mi);

  reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));

  if (!wire)
    {
      GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
      GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
      GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};

      glEnable(GL_LIGHTING);
      glEnable(GL_LIGHT0);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_CULL_FACE);

      glLightfv(GL_LIGHT0, GL_POSITION, pos);
      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
      glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
    }

  {
    double spin_speed   = 0.5;
    double wander_speed = 0.01;
    double spin_accel   = 0.25;

    bp->rot = make_rotator (do_spin ? spin_speed : 0,
                            do_spin ? spin_speed : 0,
                            do_spin ? spin_speed : 0,
                            spin_accel,
                            do_wander ? wander_speed : 0,
                            True
                            );
    bp->trackball = gltrackball_init ();
  }

  if (!(random() % 8))
    {
      planetary_gears (mi);
    }
  else
    {
      gear *g = 0;
      int total_gears = MI_COUNT (mi);
      int i;
      if (total_gears <= 0)
        total_gears = 3 + abs (BELLRAND (8) - 4);  /* 3 - 7, mostly 3. */

      bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears));
      bp->ngears = 0;

      for (i = 0; i < total_gears; i++)
        g = place_new_gear (mi, g);
    }


  /* Center gears in scene. */
  {
    GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999;
    int i;
    for (i = 0; i < bp->ngears; i++)
      {
        gear *g = bp->gears[i];
        if (g->x - g->r < minx) minx = g->x - g->r;
        if (g->x + g->r > maxx) maxx = g->x + g->r;
        if (g->y - g->r < miny) miny = g->y - g->r;
        if (g->y + g->r > maxy) maxy = g->y + g->r;
      }
    bp->bbox.x1 = minx;
    bp->bbox.y1 = miny;
    bp->bbox.x2 = maxx;
    bp->bbox.y2 = maxy;
  }

  /* Now render each gear into its display list.
   */
  for (i = 0; i < bp->ngears; i++)
    {
      gear *g = bp->gears[i];
      g->dlist = glGenLists (1);
      if (! g->dlist)
        {
          check_gl_error ("glGenLists");
          abort();
        }

      glNewList (g->dlist, GL_COMPILE);
      g->polygons += draw_involute_gear (g, wire);
      glEndList ();
    }
  if (bp->planetary_p)
    armature (mi);
}
Exemple #13
0
/* Given a newly-created gear, place it next to its parent in the scene,
   with its teeth meshed and the proper velocity.  Returns False if it
   didn't work.  (Call this a bunch of times until either it works, or
   you decide it's probably not going to.)
   [Mostly lifted from pinion.c]
 */
static Bool
place_gear (ModeInfo *mi, gear *g, gear *parent)
{
  gears_configuration *bp = &bps[MI_SCREEN(mi)];

  /* Compute this gear's velocity.
   */
  if (! parent)
    {
      g->ratio = 0.8 + BELLRAND(0.4);  /* 0.8-1.2 = 8-12rpm @ 60fps */
      g->th = 1; /* not 0 */
    }
  else
    {
      /* Gearing ratio is the ratio of the number of teeth to previous gear
         (which is also the ratio of the circumferences.)
       */
      g->ratio = (double) parent->nteeth / (double) g->nteeth;

      /* Set our initial rotation to match that of the previous gear,
         multiplied by the gearing ratio.  (This is finessed later,
         once we know the exact position of the gear relative to its
         parent.)
      */
      g->th = -(parent->th * g->ratio);

      if (g->nteeth & 1)    /* rotate 1/2 tooth-size if odd number of teeth */
        {
          double off = (180.0 / g->nteeth);
          if (g->th > 0)
            g->th += off;
          else
            g->th -= off;
        }

      /* ratios are cumulative for all gears in the train. */
      g->ratio *= parent->ratio;
    }


  if (parent)	/* Place the gear next to the parent. */
    {
      double r_off = parent->r + g->r;
      int angle;

      angle = (random() % 360) - 180;   /* -180 to +180 degrees */

      g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
      g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
      g->z = parent->z;

      /* avoid accidentally changing sign of "th" in the math below. */
      g->th += (g->th > 0 ? 360 : -360);

      /* Adjust the rotation of the gear so that its teeth line up with its
         parent, based on the position of the gear and the current rotation
         of the parent.
       */
      {
        double p_c = 2 * M_PI * parent->r;  /* circumference of parent */
        double g_c = 2 * M_PI * g->r;       /* circumference of g  */

        double p_t = p_c * (angle/360.0);   /* distance travelled along
                                               circumference of parent when
                                               moving "angle" degrees along
                                               parent. */
        double g_rat = p_t / g_c;           /* if travelling that distance
                                               along circumference of g,
                                               ratio of g's circumference
                                               travelled. */
        double g_th = 360.0 * g_rat;        /* that ratio in degrees */

        g->th += angle + g_th;
      }
    }

  /* If the position we picked for this gear causes it to overlap
     with any earlier gear in the train, give up.
   */
  {
    int i;

    for (i = bp->ngears-1; i >= 0; i--)
      {
        gear *og = bp->gears[i];

        if (og == g) continue;
        if (og == parent) continue;
        if (g->z != og->z) continue;    /* Ignore unless on same layer */

        /* Collision detection without sqrt:
             d = sqrt(a^2 + b^2)   d^2 = a^2 + b^2
             d < r1 + r2           d^2 < (r1 + r2)^2
         */
        if (((g->x - og->x) * (g->x - og->x) +
             (g->y - og->y) * (g->y - og->y)) <
            ((g->r + g->tooth_h + og->r + og->tooth_h) *
             (g->r + g->tooth_h + og->r + og->tooth_h)))
          return False;
      }
  }

  return True;
}
Exemple #14
0
/* Create and return a new gear sized for placement next to or on top of
   the given parent gear (if any.)  Returns 0 if out of memory.
   [Mostly lifted from pinion.c]
 */
static gear *
new_gear (ModeInfo *mi, gear *parent)
{
  gears_configuration *bp = &bps[MI_SCREEN(mi)];
  gear *g = (gear *) calloc (1, sizeof (*g));
  static unsigned long id = 0;  /* only used in debugging output */

  if (!g) return 0;
  g->id = ++id;

  /* Pick the size of the teeth.
   */
  if (parent) /* adjascent gears need matching teeth */
    {
      g->tooth_w = parent->tooth_w;
      g->tooth_h = parent->tooth_h;
      g->tooth_slope = -parent->tooth_slope;
    }
  else                 /* gears that begin trains get any size they want */
    {
      g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0));
      g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0));
/*
      g->tooth_slope = ((random() % 8)
                        ? 0
                        : 0.5 + BELLRAND(1));
 */
    }

  /* Pick the number of teeth, and thus, the radius.
   */
  {
    double c;

    if (!parent || bp->ngears > 4)
      g->nteeth = 5 + BELLRAND (20);
    else
      g->nteeth = parent->nteeth * (0.5 + BELLRAND(2));

    c = g->nteeth * g->tooth_w * 2;     /* circumference = teeth + gaps */
    g->r = c / (M_PI * 2);              /* c = 2 pi r  */
  }

  g->thickness  = g->tooth_w + frand (g->r);
  g->thickness2 = g->thickness * 0.7;
  g->thickness3 = g->thickness;

  /* Colorize
   */
  g->color[0] = 0.5 + frand(0.5);
  g->color[1] = 0.5 + frand(0.5);
  g->color[2] = 0.5 + frand(0.5);
  g->color[3] = 1.0;

  g->color2[0] = g->color[0] * 0.85;
  g->color2[1] = g->color[1] * 0.85;
  g->color2[2] = g->color[2] * 0.85;
  g->color2[3] = g->color[3];


  /* Decide on shape of gear interior:
     - just a ring with teeth;
     - that, plus a thinner in-set "plate" in the middle;
     - that, plus a thin raised "lip" on the inner plate;
     - or, a wide lip (really, a thicker third inner plate.)
   */
  if ((random() % 10) == 0)
    {
      /* inner_r can go all the way in; there's no inset disc. */
      g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
      g->inner_r2 = 0;
      g->inner_r3 = 0;
    }
  else
    {
      /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
      g->inner_r  = (g->r * 0.5)  + frand((g->r - g->tooth_h) * 0.4);
      g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
      g->inner_r3 = 0;

      if (g->inner_r2 > (g->r * 0.2))
        {
          int nn = (random() % 10);
          if (nn <= 2)
            g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
          else if (nn <= 7 && g->inner_r2 >= 0.1)
            g->inner_r3 = g->inner_r2 - 0.01;
        }
    }

  /* If we have three discs, sometimes make the middle disc be spokes.
   */
  if (g->inner_r3 && ((random() % 5) == 0))
    {
      g->spokes = 2 + BELLRAND (5);
      g->spoke_thickness = 1 + frand(7.0);
      if (g->spokes == 2 && g->spoke_thickness < 2)
        g->spoke_thickness += 1;
    }

  /* Sometimes add little nubbly bits, if there is room.
   */
  if (g->nteeth > 5)
    {
      double size = 0;
      involute_biggest_ring (g, 0, &size, 0);
      if (size > g->r * 0.2 && (random() % 5) == 0)
        {
          g->nubs = 1 + (random() % 16);
          if (g->nubs > 8) g->nubs = 1;
        }
    }

  if (g->inner_r3 > g->inner_r2) abort();
  if (g->inner_r2 > g->inner_r) abort();
  if (g->inner_r  > g->r) abort();

  /* Decide how complex the polygon model should be.
   */
  {
    double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
    if (pix <= 2.5)      g->size = INVOLUTE_SMALL;
    else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
    else                 g->size = INVOLUTE_LARGE;
  }

  g->base_p = !parent;

  return g;
}
Exemple #15
0
/* Returns a rotator object, which encapsulates rotation and motion state.

   spin_[xyz]_speed indicates the relative speed of rotation.
   Specify 0 if you don't want any rotation around that axis.

   spin_accel specifies a scaling factor for the acceleration that is
   randomly applied to spin: if you want the speed to change faster,
   make this > 1.

   wander_speed indicates the relative speed through space.

   If randomize_initial_state_p is true, then the initial position and
   rotation will be randomized (even if the spin speeds are 0.)  If it
   is false, then all values will be initially zeroed.
 */
rotator *
make_rotator (double spin_x_speed,
              double spin_y_speed,
              double spin_z_speed,
              double spin_accel,
              double wander_speed,
              int randomize_initial_state_p)
{
  rotator *r = (rotator *) calloc (1, sizeof(*r));
  double d, dd;

  if (!r) return 0;

  if (spin_x_speed < 0 || spin_y_speed < 0 || spin_z_speed < 0 ||
      wander_speed < 0)
    abort();

  r->spin_x_speed = spin_x_speed;
  r->spin_y_speed = spin_y_speed;
  r->spin_z_speed = spin_z_speed;
  r->wander_speed = wander_speed;

  if (randomize_initial_state_p)
    {
      r->rotx = FLOATRAND(1.0) * RANDSIGN();
      r->roty = FLOATRAND(1.0) * RANDSIGN();
      r->rotz = FLOATRAND(1.0) * RANDSIGN();

      r->wander_frame = LRAND() % 0xFFFF;
    }
  else
    {
      r->rotx = r->roty = r->rotz = 0;
      r->wander_frame = 0;
    }

  d  = 0.006;
  dd = 0.00006;

  r->dx = BELLRAND(d * r->spin_x_speed);
  r->dy = BELLRAND(d * r->spin_y_speed);
  r->dz = BELLRAND(d * r->spin_z_speed);

  r->d_max = r->dx * 2;

  r->ddx = (dd + FLOATRAND(dd+dd)) * r->spin_x_speed * spin_accel;
  r->ddy = (dd + FLOATRAND(dd+dd)) * r->spin_y_speed * spin_accel;
  r->ddz = (dd + FLOATRAND(dd+dd)) * r->spin_z_speed * spin_accel;

# if 0
  fprintf (stderr, "rotator:\n");
  fprintf (stderr, "   wander: %3d %6.2f\n", r->wander_frame, r->wander_speed);
  fprintf (stderr, "    speed: %6.2f %6.2f %6.2f\n",
           r->spin_x_speed, r->spin_y_speed, r->spin_z_speed);
  fprintf (stderr, "      rot: %6.2f %6.2f %6.2f\n",
           r->rotx, r->roty, r->rotz);
  fprintf (stderr, "        d: %6.2f %6.2f %6.2f, %6.2f\n",
           r->dx, r->dy, r->dz,
           r->d_max);
  fprintf (stderr, "       dd: %6.2f %6.2f %6.2f\n",
           r->ddx, r->ddy, r->ddz);
# endif

  return r;
}
Exemple #16
0
static void
build_ball (ModeInfo *mi)
{
  ball_configuration *bp = &bps[MI_SCREEN(mi)];
  int rows = MI_COUNT (mi);

  GLfloat tile_size = M_PI / rows;
  GLfloat th0, th1;

  struct { XYZ position; GLfloat strength; } dents[5];
  int dent_count = random() % countof(dents);
  int i;
  for (i = 0; i < dent_count; i++)
    {
      GLfloat dist;
      dents[i].position.x = RANDSIGN() * (2 - BELLRAND(0.2));
      dents[i].position.y = RANDSIGN() * (2 - BELLRAND(0.2));
      dents[i].position.z = RANDSIGN() * (2 - BELLRAND(0.2));
      dist = sqrt (dents[i].position.x * dents[i].position.x +
                   dents[i].position.y * dents[i].position.y +
                   dents[i].position.z * dents[i].position.z);
      dents[i].strength = dist - (1 - BELLRAND(0.3));
      dents[i].strength = dist - (1 - BELLRAND(0.3));
    }


  for (th1 = M_PI/2; th1 > -(M_PI/2 + tile_size/2); th1 -= tile_size)
    {
      GLfloat x  = cos (th1);
      GLfloat y  = sin (th1);
      GLfloat x0 = cos (th1 - tile_size/2);
      GLfloat x1 = cos (th1 + tile_size/2);
      GLfloat circ0 = M_PI * x0 * 2;
      GLfloat circ1 = M_PI * x1 * 2;
      GLfloat circ  = (circ0 < circ1 ? circ0 : circ1);
      int row_tiles = floor ((circ < 0 ? 0 : circ) / tile_size);
      GLfloat spacing;
      GLfloat dropsy = 0.13 + frand(0.04);

      if (row_tiles <= 0) row_tiles = 1;
      spacing = M_PI*2 / row_tiles;

      for (th0 = 0; th0 < M_PI*2; th0 += spacing)
        {
          tile *t = (tile *) calloc (1, sizeof(*t));
          t->size = tile_size * 0.85;
          t->position.x = cos (th0) * x;
          t->position.y = sin (th0) * x;
          t->position.z = y;

          t->normal = t->position;

          /* Apply pressure on position from the dents. */
          for (i = 0; i < dent_count; i++)
            {
              GLfloat dist;
              XYZ direction;

              if (! (random() % 150))	/* Drop tiles randomly */
                {
                  free (t);
                  goto SKIP;
                }

              direction.x = t->position.x - dents[i].position.x;
              direction.y = t->position.y - dents[i].position.y;
              direction.z = t->position.z - dents[i].position.z;
              dist = sqrt (direction.x * direction.x +
                           direction.y * direction.y +
                           direction.z * direction.z);
              if (dist < dents[i].strength)
                {
                  GLfloat s = 1 - (dents[i].strength - dist) * 0.66;
                  XYZ n2 = t->normal;
                  GLfloat angle = vector_angle (t->position, dents[i].position);

                  /* Drop out the tiles near the apex of the dent. */
                  if (angle < dropsy)
                    {
                      free (t);
                      goto SKIP;
                    }

                  t->position.x *= s;
                  t->position.y *= s;
                  t->position.z *= s;

                  direction = normalize (direction);
                  n2.x -= direction.x;
                  n2.y -= direction.y;
                  n2.z -= direction.z;

                  t->normal.x = (t->normal.x + n2.x) / 2;
                  t->normal.y = (t->normal.y + n2.y) / 2;
                  t->normal.z = (t->normal.z + n2.z) / 2;
                }
            }

          /* Skew the direction the tile is facing slightly. */
          t->normal.x += 0.12 - frand(0.06);
          t->normal.y += 0.12 - frand(0.06);
          t->normal.z += 0.12 - frand(0.06);
          t->tilt = 4 - BELLRAND(8);

          t->next = bp->tiles;
          bp->tiles = t;
        SKIP: ;
        }
    }

  bp->nrays = 5 + BELLRAND(10);
  bp->rays = (ray *) calloc (bp->nrays, sizeof(*bp->rays));
  for (i = 0; i < bp->nrays; i++)
    {
      GLfloat th = frand(M_PI * 2);
      bp->rays[i].normal.x = cos (th);
      bp->rays[i].normal.y = sin (th);
      bp->rays[i].normal.z = 1;
      bp->rays[i].normal = normalize (bp->rays[i].normal);
      bp->rays[i].color[0] = 0.9 + frand(0.1);
      bp->rays[i].color[1] = 0.6 + frand(0.4);
      bp->rays[i].color[2] = 0.6 + frand(0.2);
      bp->rays[i].color[3] = 1;
    }
}
Exemple #17
0
/* Initializes a new tentacle and stores it in the list.
 */
static tentacle *
make_tentacle (ModeInfo *mi, int which, int total)
{
    tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
    tentacle *t = (tentacle *) calloc (1, sizeof (*t));
    double brightness;
    int i;

    t->mi = mi;

    /* position tentacles on a grid */
    {
        int cols = (int) (sqrt(total) + 0.5);
        int rows = (total+cols-1) / cols;
        int xx = which % cols;
        int yy = which / cols;
        double spc = arg_thickness * 0.8;
        if (!intersect_p) cols = 1, xx = 0;
        t->x = (cols * spc / 2) - (spc * (xx + 0.5));
        t->y = (rows * spc / 2) - (spc * (yy + 0.5));
        t->z = 0;
    }

    /* Brighten or darken the colors of this tentacle from the default.
     */
    brightness = 0.6 + frand(3.0);
    memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
    memcpy (t->stripe_color,   tc->stripe_color,   4 * sizeof(*t->stripe_color));
    memcpy (t->sucker_color,   tc->sucker_color,   4 * sizeof(*t->sucker_color));
# define FROB(X) \
    t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
    t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
    t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
    FROB (tentacle_color);
    FROB (stripe_color);
    FROB (sucker_color);
# undef FROB

    t->nsegments = (arg_segments) + BELLRAND(arg_segments);

    t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
    for (i = 0; i < t->nsegments; i++)
    {
        double spin_speed   = 0;
        double spin_accel   = 0;
        double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
        t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
                                           spin_accel, wander_speed, True);
    }

    t->segments[0].thickness = (((arg_thickness * 0.5) +
                                 BELLRAND(arg_thickness * 0.6))
                                / 1.0);

    if (tc->tentacles_size <= tc->ntentacles)
    {
        tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
        tc->tentacles = (tentacle **)
                        realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
        if (! tc->tentacles)
        {
            fprintf (stderr, "%s: out of memory (%d tentacles)\n",
                     progname, tc->tentacles_size);
            exit (1);
        }
    }

    tc->tentacles[tc->ntentacles++] = t;
    return t;
}