source: trunk/third/xscreensaver/hacks/goop.c @ 15683

Revision 15683, 15.4 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15682, which included commits to RCS files with non-trunk default branches.
Line 
1/* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation.  No representations are made about the suitability of this
8 * software for any purpose.  It is provided "as is" without express or
9 * implied warranty.
10 */
11
12#include <math.h>
13#include "screenhack.h"
14#include "spline.h"
15#include "alpha.h"
16
17
18/* This is pretty compute-intensive, probably due to the large number of
19   polygon fills.  I tried introducing a scaling factor to make the spline
20   code emit fewer line segments, but that made the edges very rough.
21   However, tuning *maxVelocity, *elasticity and *delay can result in much
22   smoother looking animation.  I tuned these for a 1280x1024 Indy display,
23   but I don't know whether these values will be reasonable for a slower
24   machine...
25
26   The more planes the better -- SGIs have a 12-bit pseudocolor display
27   (4096 colormap cells) which is mostly useless, except for this program,
28   where it means you can have 11 or 12 mutually-transparent objects instead
29   of only 7 or 8.  But, if you are using the 12-bit visual, you should crank
30   down the velocity and elasticity, or server slowness will cause the
31   animation to look jerky (yes, it's sad but true, SGI's X server is
32   perceptibly slower when using plane masks on a 12-bit visual than on an
33   8-bit visual.)  Using -max-velocity 0.5 -elasticity 0.9 seems to work ok
34   on my Indy R5k with visual 0x27 and the bottom-of-the-line 24-bit graphics
35   board.
36
37   It might look better if each blob had an outline, which was a *slightly*
38   darker color than the center, to give them a bit more definition -- but
39   that would mean using two planes per blob.  (Or maybe allocating the
40   outline colors outside of the plane-space?  Then the outlines wouldn't be
41   transparent, but maybe that wouldn't be so noticeable?)
42
43   Oh, for an alpha channel... maybe I should rewrite this in GL.  Then the
44   blobs could have thickness, and curved edges with specular reflections...
45 */
46
47
48#define SCALE       10000  /* fixed-point math, for sub-pixel motion */
49#define DEF_COUNT   12     /* When planes and count are 0, how many blobs. */
50
51
52#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
53#define RANDSIGN() ((random() & 1) ? 1 : -1)
54
55struct blob {
56  long x, y;            /* position of midpoint */
57  long dx, dy;          /* velocity and direction */
58  double torque;        /* rotational speed */
59  double th;            /* angle of rotation */
60  long elasticity;      /* how fast they deform */
61  long max_velocity;    /* speed limit */
62  long min_r, max_r;    /* radius range */
63  int npoints;          /* control points */
64  long *r;              /* radii */
65  spline *spline;
66};
67
68struct layer {
69  int nblobs;
70  struct blob **blobs;
71  Pixmap pixmap;
72  unsigned long pixel;
73  GC gc;
74};
75
76enum goop_mode {
77  transparent,
78  opaque,
79  xor,
80  outline
81};
82
83struct goop {
84  enum goop_mode mode;
85  int width, height;
86  int nlayers;
87  struct layer **layers;
88  unsigned long background;
89  Pixmap pixmap;
90  GC pixmap_gc;
91  GC window_gc;
92  Bool additive_p;
93  Bool cmap_p;
94};
95
96
97static struct blob *
98make_blob (int maxx, int maxy, int size)
99{
100  struct blob *b = (struct blob *) calloc(1, sizeof(*b));
101  int i;
102  int mid;
103
104  maxx *= SCALE;
105  maxy *= SCALE;
106  size *= SCALE;
107
108  b->max_r = size/2;
109  b->min_r = size/10;
110
111  if (b->min_r < (5*SCALE)) b->min_r = (5*SCALE);
112  mid = ((b->min_r + b->max_r) / 2);
113
114  b->torque       = get_float_resource ("torque", "Torque");
115  b->elasticity   = SCALE * get_float_resource ("elasticity", "Elasticity");
116  b->max_velocity = SCALE * get_float_resource ("maxVelocity", "MaxVelocity");
117
118  b->x = RAND(maxx);
119  b->y = RAND(maxy);
120
121  b->dx = RAND(b->max_velocity) * RANDSIGN();
122  b->dy = RAND(b->max_velocity) * RANDSIGN();
123  b->th = frand(M_PI+M_PI) * RANDSIGN();
124  b->npoints = (random() % 5) + 5;
125
126  b->spline = make_spline (b->npoints);
127  b->r = (long *) malloc (sizeof(*b->r) * b->npoints);
128  for (i = 0; i < b->npoints; i++)
129    b->r[i] = ((random() % mid) + (mid/2)) * RANDSIGN();
130  return b;
131}
132
133static void
134throb_blob (struct blob *b)
135{
136  int i;
137  double frac = ((M_PI+M_PI) / b->npoints);
138
139  for (i = 0; i < b->npoints; i++)
140    {
141      long r = b->r[i];
142      long ra = (r > 0 ? r : -r);
143      double th = (b->th > 0 ? b->th : -b->th);
144      long x, y;
145
146      /* place control points evenly around perimiter, shifted by theta */
147      x = b->x + ra * cos (i * frac + th);
148      y = b->y + ra * sin (i * frac + th);
149
150      b->spline->control_x[i] = x / SCALE;
151      b->spline->control_y[i] = y / SCALE;
152
153      /* alter the radius by a random amount, in the direction in which
154         it had been going (the sign of the radius indicates direction.) */
155      ra += (RAND(b->elasticity) * (r > 0 ? 1 : -1));
156      r = ra * (r >= 0 ? 1 : -1);
157
158      /* If we've reached the end (too long or too short) reverse direction. */
159      if ((ra > b->max_r && r >= 0) ||
160          (ra < b->min_r && r < 0))
161        r = -r;
162      /* And reverse direction in mid-course once every 50 times. */
163      else if (! (random() % 50))
164        r = -r;
165
166      b->r[i] = r;
167    }
168}
169
170static void
171move_blob (struct blob *b, int maxx, int maxy)
172{
173  maxx *= SCALE;
174  maxy *= SCALE;
175
176  b->x += b->dx;
177  b->y += b->dy;
178
179  /* If we've reached the edge of the box, reverse direction. */
180  if ((b->x > maxx && b->dx >= 0) ||
181      (b->x < 0    && b->dx < 0))
182    {
183      b->dx = -b->dx;
184    }
185  if ((b->y > maxy && b->dy >= 0) ||
186      (b->y < 0    && b->dy < 0))
187    {
188      b->dy = -b->dy;
189    }
190
191  /* Alter velocity randomly. */
192  if (! (random() % 10))
193    {
194      b->dx += (RAND(b->max_velocity/2) * RANDSIGN());
195      b->dy += (RAND(b->max_velocity/2) * RANDSIGN());
196
197      /* Throttle velocity */
198      if (b->dx > b->max_velocity || b->dx < -b->max_velocity)
199        b->dx /= 2;
200      if (b->dy > b->max_velocity || b->dy < -b->max_velocity)
201        b->dy /= 2;
202    }
203
204  {
205    double th = b->th;
206    double d = (b->torque == 0 ? 0 : frand(b->torque));
207    if (th < 0)
208      th = -(th + d);
209    else
210      th += d;
211
212    if (th > (M_PI+M_PI))
213      th -= (M_PI+M_PI);
214    else if (th < 0)
215      th += (M_PI+M_PI);
216
217    b->th = (b->th > 0 ? th : -th);
218  }
219
220  /* Alter direction of rotation randomly. */
221  if (! (random() % 100))
222    b->th *= -1;
223}
224
225static void
226draw_blob (Display *dpy, Drawable drawable, GC gc, struct blob *b,
227           Bool fill_p)
228{
229  compute_closed_spline (b->spline);
230#ifdef DEBUG
231  {
232    int i;
233    for (i = 0; i < b->npoints; i++)
234      XDrawLine (dpy, drawable, gc, b->x/SCALE, b->y/SCALE,
235                 b->spline->control_x[i], b->spline->control_y[i]);
236  }
237#else
238  if (fill_p)
239    XFillPolygon (dpy, drawable, gc, b->spline->points, b->spline->n_points,
240                  Nonconvex, CoordModeOrigin);
241  else
242#endif
243    XDrawLines (dpy, drawable, gc, b->spline->points, b->spline->n_points,
244                CoordModeOrigin);
245}
246
247
248static struct layer *
249make_layer (Display *dpy, Window window, int width, int height, int nblobs)
250{
251  int i;
252  struct layer *layer = (struct layer *) calloc(1, sizeof(*layer));
253  int blob_min, blob_max;
254  XGCValues gcv;
255  layer->nblobs = nblobs;
256
257  layer->blobs = (struct blob **) malloc(sizeof(*layer->blobs)*layer->nblobs);
258
259  blob_max = (width < height ? width : height) / 2;
260  blob_min = (blob_max * 2) / 3;
261  for (i = 0; i < layer->nblobs; i++)
262    layer->blobs[i] = make_blob (width, height,
263                                 (random() % (blob_max-blob_min)) + blob_min);
264
265  layer->pixmap = XCreatePixmap (dpy, window, width, height, 1);
266  layer->gc = XCreateGC (dpy, layer->pixmap, 0, &gcv);
267
268  return layer;
269}
270
271static void
272draw_layer_plane (Display *dpy, struct layer *layer, int width, int height)
273{
274  int i;
275  for (i = 0; i < layer->nblobs; i++)
276    {
277      throb_blob (layer->blobs[i]);
278      move_blob (layer->blobs[i], width, height);
279      draw_blob (dpy, layer->pixmap, layer->gc, layer->blobs[i], True);
280    }
281}
282
283
284static void
285draw_layer_blobs (Display *dpy, Drawable drawable, GC gc,
286                  struct layer *layer, int width, int height,
287                  Bool fill_p)
288{
289  int i;
290  for (i = 0; i < layer->nblobs; i++)
291    {
292      draw_blob (dpy, drawable, gc, layer->blobs[i], fill_p);
293      throb_blob (layer->blobs[i]);
294      move_blob (layer->blobs[i], width, height);
295    }
296}
297
298
299static struct goop *
300make_goop (Screen *screen, Visual *visual, Window window, Colormap cmap,
301           int width, int height, long depth)
302{
303  Display *dpy = DisplayOfScreen (screen);
304  int i;
305  struct goop *goop = (struct goop *) calloc(1, sizeof(*goop));
306  XGCValues gcv;
307  int nblobs = get_integer_resource ("count", "Count");
308
309  unsigned long *plane_masks = 0;
310  unsigned long base_pixel = 0;
311
312  goop->mode = (get_boolean_resource("xor", "Xor")
313                ? xor
314                : (get_boolean_resource("transparent", "Transparent")
315                   ? transparent
316                   : opaque));
317
318  goop->width = width;
319  goop->height = height;
320
321
322  goop->nlayers = get_integer_resource ("planes", "Planes");
323  if (goop->nlayers <= 0)
324    goop->nlayers = (random() % (depth-2)) + 2;
325  goop->layers = (struct layer **) malloc(sizeof(*goop->layers)*goop->nlayers);
326
327  goop->additive_p = get_boolean_resource ("additive", "Additive");
328  goop->cmap_p = has_writable_cells (screen, visual);
329
330  if (mono_p && goop->mode == transparent)
331    goop->mode = opaque;
332
333  /* Try to allocate some color planes before committing to nlayers.
334   */
335  if (goop->mode == transparent)
336    {
337      int nplanes = goop->nlayers;
338      allocate_alpha_colors (screen, visual, cmap,
339                             &nplanes, goop->additive_p, &plane_masks,
340                             &base_pixel);
341      if (nplanes > 1)
342        goop->nlayers = nplanes;
343      else
344        {
345          fprintf (stderr,
346         "%s: couldn't allocate any color planes; turning transparency off.\n",
347                   progname);
348          goop->mode = opaque;
349        }
350    }
351
352  {
353    int lblobs[32];
354    int total = DEF_COUNT;
355    memset (lblobs, 0, sizeof(lblobs));
356    if (nblobs <= 0)
357      while (total)
358        for (i = 0; total && i < goop->nlayers; i++)
359          lblobs[i]++, total--;
360    for (i = 0; i < goop->nlayers; i++)
361      goop->layers[i] = make_layer (dpy, window, width, height,
362                                    (nblobs > 0 ? nblobs : lblobs[i]));
363  }
364
365  if (goop->mode == transparent && plane_masks)
366    {
367      for (i = 0; i < goop->nlayers; i++)
368        goop->layers[i]->pixel = base_pixel | plane_masks[i];
369      goop->background = base_pixel;
370    }
371  if (plane_masks)
372    free (plane_masks);
373
374  if (goop->mode != transparent)
375    {
376      XColor color;
377      color.flags = DoRed|DoGreen|DoBlue;
378
379      goop->background =
380        get_pixel_resource ("background", "Background", dpy,cmap);
381
382      for (i = 0; i < goop->nlayers; i++)
383        {
384          int H = random() % 360;                          /* range 0-360    */
385          double S = ((double) (random()%70) + 30)/100.0;  /* range 30%-100% */
386          double V = ((double) (random()%34) + 66)/100.0;  /* range 66%-100% */
387          hsv_to_rgb (H, S, V, &color.red, &color.green, &color.blue);
388          if (XAllocColor (dpy, cmap, &color))
389            goop->layers[i]->pixel = color.pixel;
390          else
391            goop->layers[i]->pixel =
392              WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
393        }
394    }
395
396  goop->pixmap = XCreatePixmap (dpy, window, width, height,
397                                (goop->mode == xor ? 1L : depth));
398
399  gcv.background = goop->background;
400  gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
401  gcv.line_width = get_integer_resource ("thickness","Thickness");
402  goop->pixmap_gc = XCreateGC (dpy, goop->pixmap, GCLineWidth, &gcv);
403  goop->window_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
404
405  return goop;
406}
407
408static struct goop *
409init_goop (Display *dpy, Window window)
410{
411  XWindowAttributes xgwa;
412  XGetWindowAttributes (dpy, window, &xgwa);
413
414  return make_goop (xgwa.screen, xgwa.visual, window, xgwa.colormap,
415                    xgwa.width, xgwa.height, xgwa.depth);
416}
417
418static void
419run_goop (Display *dpy, Window window, struct goop *goop)
420{
421  int i, j;
422
423  switch (goop->mode)
424    {
425    case transparent:
426
427      for (i = 0; i < goop->nlayers; i++)
428        draw_layer_plane (dpy, goop->layers[i], goop->width, goop->height);
429
430      XSetForeground (dpy, goop->pixmap_gc, goop->background);
431      XSetFunction (dpy, goop->pixmap_gc, GXcopy);
432      XSetPlaneMask (dpy, goop->pixmap_gc, AllPlanes);
433      XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
434                      goop->width, goop->height);
435
436      XSetForeground (dpy, goop->pixmap_gc, ~0L);
437
438      if (!goop->cmap_p && !goop->additive_p)
439        {
440          for (i = 0; i < goop->nlayers; i++)
441            for (j = 0; j < goop->layers[i]->nblobs; j++)
442              draw_blob (dpy, goop->pixmap, goop->pixmap_gc,
443                         goop->layers[i]->blobs[j], True);
444          XSetFunction (dpy, goop->pixmap_gc, GXclear);
445        }
446
447      for (i = 0; i < goop->nlayers; i++)
448        {
449          XSetPlaneMask (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
450          draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
451                            goop->layers[i], goop->width, goop->height,
452                            True);
453        }
454      XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
455                 goop->width, goop->height, 0, 0);
456      break;
457
458    case xor:
459      XSetFunction (dpy, goop->pixmap_gc, GXcopy);
460      XSetForeground (dpy, goop->pixmap_gc, 0);
461      XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
462                      goop->width, goop->height);
463      XSetFunction (dpy, goop->pixmap_gc, GXxor);
464      XSetForeground (dpy, goop->pixmap_gc, 1);
465      for (i = 0; i < goop->nlayers; i++)
466        draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
467                          goop->layers[i], goop->width, goop->height,
468                          (goop->mode != outline));
469      XCopyPlane (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
470                  goop->width, goop->height, 0, 0, 1L);
471      break;
472
473    case opaque:
474    case outline:
475      XSetForeground (dpy, goop->pixmap_gc, goop->background);
476      XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
477                      goop->width, goop->height);
478      for (i = 0; i < goop->nlayers; i++)
479        {
480          XSetForeground (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
481          draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
482                            goop->layers[i], goop->width, goop->height,
483                            (goop->mode != outline));
484        }
485      XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
486                 goop->width, goop->height, 0, 0);
487      break;
488
489    default:
490      abort ();
491      break;
492    }
493}
494
495
496char *progclass = "Goop";
497
498char *defaults [] = {
499  ".background:         black",
500  ".foreground:         white",
501  "*delay:              12000",
502  "*transparent:        true",
503  "*additive:           true",
504  "*xor:                false",
505  "*count:              0",
506  "*planes:             0",
507  "*thickness:          5",
508  "*torque:             0.0075",
509  "*elasticity:         1.8",
510  "*maxVelocity:        1.2",
511  0
512};
513
514XrmOptionDescRec options [] = {
515  { "-delay",           ".delay",       XrmoptionSepArg, 0 },
516  { "-count",           ".count",       XrmoptionSepArg, 0 },
517  { "-planes",          ".planes",      XrmoptionSepArg, 0 },
518  { "-transparent",     ".transparent", XrmoptionNoArg, "True" },
519  { "-non-transparent", ".transparent", XrmoptionNoArg, "False" },
520  { "-additive",        ".additive",    XrmoptionNoArg, "True" },
521  { "-subtractive",     ".additive",    XrmoptionNoArg, "false" },
522  { "-xor",             ".xor",         XrmoptionNoArg, "true" },
523  { "-no-xor",          ".xor",         XrmoptionNoArg, "false" },
524  { "-thickness",       ".thickness",   XrmoptionSepArg, 0 },
525  { "-torque",          ".torque",      XrmoptionSepArg, 0 },
526  { "-elasticity",      ".elasticity",  XrmoptionSepArg, 0 },
527  { "-max-velocity",    ".maxVelocity", XrmoptionSepArg, 0 },
528  { 0, 0, 0, 0 }
529};
530
531void
532screenhack (Display *dpy, Window window)
533{
534  struct goop *g = init_goop (dpy, window);
535  int delay = get_integer_resource ("delay", "Integer");
536  while (1)
537    {
538      run_goop (dpy, window, g);
539      XSync (dpy, False);
540      screenhack_handle_events (dpy);
541      if (delay) usleep (delay);
542    }
543}
Note: See TracBrowser for help on using the repository browser.