http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[xscreensaver] / hacks / attraction.c
index 8e3f188f3584f8ac9b9e982c2c8d715a97a67f05..0cea509a40406b687b1c8b6a17cfdd80f945cd68 100644 (file)
@@ -1,4 +1,5 @@
-/* xscreensaver, Copyright (c) 1992, 1995 Jamie Zawinski <jwz@netscape.com>
+/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
+ *  Jamie Zawinski <jwz@netscape.com>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
    unless you make about 200 points.... set the viscosity to about .8
    and drag the mouse through it.   it makes a nice wave which travels
    through the field.
+
+   And (always the troublemaker) Joe Keane <jgk@jgk.org> sez:
+
+   Despite what John sez, the field being simulated is always conservative.
+   The real problem is that it uses a simple hack, computing acceleration
+   *based only on the starting position*, instead of a real differential
+   equation solver.  Thus you'll always have energy coming out of nowhere,
+   although it's most blatant when balls get close together.  If it were
+   done right, you wouldn't need viscosity or artificial limits on how
+   close the balls can get.
  */
 
 #include <stdio.h>
 #include "spline.h"
 
 struct ball {
-  float x, y;
-  float vx, vy;
-  float dx, dy;
-  float mass;
+  double x, y;
+  double vx, vy;
+  double dx, dy;
+  double mass;
   int size;
-  XColor color;
+  int pixel_index;
   int hue;
 };
 
-static unsigned int default_fg_pixel;
 static struct ball *balls;
 static int npoints;
 static int threshold;
@@ -68,24 +78,22 @@ static int segments;
 static Bool glow_p;
 static Bool orbit_p;
 static XPoint *point_stack;
-static int point_stack_size, point_stack_fp, pixel_stack_fp, pixel_stack_size;
-static unsigned long *pixel_stack;
-static unsigned int color_shift;
+static int point_stack_size, point_stack_fp;
+static XColor *colors;
+static int ncolors;
+static int fg_index;
+static int color_shift;
 
 /*flip mods for mouse interaction*/
 static Bool mouse_p;
 int mouse_x, mouse_y, mouse_mass, root_x, root_y;
-static float viscosity;
+static double viscosity;
 
 static enum object_mode {
   ball_mode, line_mode, polygon_mode, spline_mode, spline_filled_mode,
   tail_mode
 } mode;
 
-static enum color_mode {
-  cycle_mode, random_mode
-} cmode;
-
 static GC draw_gc, erase_gc;
 
 #define MAX_SIZE 16
@@ -94,9 +102,7 @@ static GC draw_gc, erase_gc;
 #define max(a,b) ((a)>(b)?(a):(b))
 
 static void
-init_balls (dpy, window)
-     Display *dpy;
-     Window window;
+init_balls (Display *dpy, Window window)
 {
   int i;
   XWindowAttributes xgwa;
@@ -131,7 +137,7 @@ init_balls (dpy, window)
   glow_p = get_boolean_resource ("glow", "Boolean");
   orbit_p = get_boolean_resource ("orbit", "Boolean");
   color_shift = get_integer_resource ("colorShift", "Integer");
-  if (color_shift >= 360) color_shift = 5;
+  if (color_shift <= 0) color_shift = 5;
 
   /*flip mods for mouse interaction*/
   mouse_p = get_boolean_resource ("mouse", "Boolean");
@@ -156,34 +162,67 @@ init_balls (dpy, window)
     exit (1);
   }
 
-  mode_str = get_string_resource ("colorMode", "ColorMode");
-  if (! mode_str) cmode = cycle_mode;
-  else if (!strcmp (mode_str, "cycle")) cmode = cycle_mode;
-  else if (!strcmp (mode_str, "random")) cmode = random_mode;
-  else {
-    fprintf (stderr, "%s: colorMode must be cycle or random, not \"%s\"\n",
-            progname, mode_str);
-    exit (1);
-  }
-
   if (mode != ball_mode && mode != tail_mode) glow_p = False;
   
   if (mode == polygon_mode && npoints < 3)
     mode = line_mode;
 
+  ncolors = get_integer_resource ("colors", "Colors");
+  if (ncolors < 2) ncolors = 2;
+  if (ncolors <= 2) mono_p = True;
+  colors = 0;
+
+  if (!mono_p)
+    {
+      fg_index = 0;
+      switch (mode)
+       {
+       case ball_mode:
+         if (glow_p)
+           {
+             int H = random() % 360;
+             double S1 = 0.25;
+             double S2 = 1.00;
+             double V = frand(0.25) + 0.75;
+             colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
+             make_color_ramp (dpy, cmap, H, S1, V, H, S2, V, colors, &ncolors,
+                              False, True, False);
+           }
+         else
+           {
+             ncolors = npoints;
+             colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
+             make_random_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
+                                   True, True, False, True);
+           }
+         break;
+       case line_mode:
+       case polygon_mode:
+       case spline_mode:
+       case spline_filled_mode:
+       case tail_mode:
+         colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
+         make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
+                               True, False, True);
+         break;
+       default:
+         abort ();
+       }
+    }
+
+  if (!mono_p && ncolors <= 2)
+    {
+      if (colors) free (colors);
+      colors = 0;
+      mono_p = True;
+    }
+
   if (mode != ball_mode)
     {
       int size = (segments ? segments : 1);
       point_stack_size = size * (npoints + 1);
       point_stack = (XPoint *) calloc (point_stack_size, sizeof (XPoint));
       point_stack_fp = 0;
-      if (segments > 0)
-       pixel_stack_size = segments;
-      else
-       pixel_stack_size = (360 / color_shift);
-      pixel_stack = (unsigned long *)
-       calloc (pixel_stack_size, sizeof (unsigned int));
-      pixel_stack_fp = 0;
     }
 
   gcv.line_width = (mode == tail_mode
@@ -191,21 +230,15 @@ init_balls (dpy, window)
                    : 1);
   gcv.cap_style = (mode == tail_mode ? CapRound : CapButt);
 
-  gcv.foreground = default_fg_pixel =
-    get_pixel_resource ("foreground", "Foreground", dpy, cmap);
+  if (mono_p)
+    gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
+  else
+    gcv.foreground = colors[fg_index].pixel;
   draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
-  gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
+
+  gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
   erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
 
-  if (!mono_p && mode != ball_mode)
-    for (i = 0; i < pixel_stack_size; i++)
-      {
-       XColor color;
-       color.pixel = default_fg_pixel;
-       XQueryColor (dpy, cmap, &color);
-       if (!XAllocColor (dpy, cmap, &color)) abort ();
-       pixel_stack [i] = color.pixel;
-      }
 
 #define rand_size() min (MAX_SIZE, 8 + (random () % (MAX_SIZE - 9)))
 
@@ -229,20 +262,12 @@ init_balls (dpy, window)
          balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
          balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
        }
-      balls [i].color.pixel = default_fg_pixel;
-      balls [i].color.flags = DoRed | DoGreen | DoBlue;
-      if (!mono_p)
-       {
-         if (i != 0 && (glow_p || mode != ball_mode))
-           balls [i].hue = balls [0].hue;
-         else
-           balls [i].hue = random () % 360;
-         hsv_to_rgb (balls [i].hue, 1.0, 1.0,
-                     &balls [i].color.red, &balls [i].color.green,
-                     &balls [i].color.blue);
-         if (!XAllocColor (dpy, cmap, &balls [i].color))
-           mono_p = True; /* just give up */
-       }
+      if (mono_p || mode != ball_mode)
+       balls [i].pixel_index = -1;
+      else if (glow_p)
+       balls [i].pixel_index = 0;
+      else
+       balls [i].pixel_index = random() % ncolors;
     }
 
   if (orbit_p)
@@ -285,18 +310,14 @@ init_balls (dpy, window)
 }
 
 static void
-compute_force (i, dx_ret, dy_ret)
-     int i;
-     float *dx_ret, *dy_ret;
+compute_force (int i, double *dx_ret, double *dy_ret)
 {
   int j;
-  float x_dist, y_dist, dist, dist2;
+  double x_dist, y_dist, dist, dist2;
   *dx_ret = 0;
   *dy_ret = 0;
   for (j = 0; j < npoints; j++)
     {
-      float x_dist, y_dist, dist, dist2;
-
       if (i == j) continue;
       x_dist = balls [j].x - balls [i].x;
       y_dist = balls [j].y - balls [i].y;
@@ -305,9 +326,9 @@ compute_force (i, dx_ret, dy_ret)
              
       if (dist > 0.1) /* the balls are not overlapping */
        {
-         float new_acc = ((balls[j].mass / dist2) *
-                          ((dist < threshold) ? -1.0 : 1.0));
-         float new_acc_dist = new_acc / dist;
+         double new_acc = ((balls[j].mass / dist2) *
+                           ((dist < threshold) ? -1.0 : 1.0));
+         double new_acc_dist = new_acc / dist;
          *dx_ret += new_acc_dist * x_dist;
          *dy_ret += new_acc_dist * y_dist;
        }
@@ -327,9 +348,9 @@ compute_force (i, dx_ret, dy_ret)
        
       if (dist > 0.1) /* the balls are not overlapping */
        {
-         float new_acc = ((mouse_mass / dist2) *
-                          ((dist < threshold) ? -1.0 : 1.0));
-         float new_acc_dist = new_acc / dist;
+         double new_acc = ((mouse_mass / dist2) *
+                           ((dist < threshold) ? -1.0 : 1.0));
+         double new_acc_dist = new_acc / dist;
          *dx_ret += new_acc_dist * x_dist;
          *dy_ret += new_acc_dist * y_dist;
        }
@@ -342,9 +363,7 @@ compute_force (i, dx_ret, dy_ret)
 }
 
 static void
-run_balls (dpy, window)
-     Display *dpy;
-     Window window;
+run_balls (Display *dpy, Window window)
 {
   int last_point_stack_fp = point_stack_fp;
   static int tick = 500, xlim, ylim;
@@ -353,7 +372,7 @@ run_balls (dpy, window)
 
   /*flip mods for mouse interaction*/
   Window  root1, child1;
-  int mask;
+  unsigned int mask;
   if (mouse_p)
     {
       XQueryPointer(dpy, window, &root1, &child1,
@@ -377,9 +396,9 @@ run_balls (dpy, window)
   /* move the balls according to the forces now in effect */
   for (i = 0; i < npoints; i++)
     {
-      float old_x = balls[i].x;
-      float old_y = balls[i].y;
-      float new_x, new_y;
+      double old_x = balls[i].x;
+      double old_y = balls[i].y;
+      double new_x, new_y;
       int size = balls[i].size;
       balls[i].vx += balls[i].dx;
       balls[i].vy += balls[i].dy;
@@ -438,43 +457,39 @@ run_balls (dpy, window)
       new_x = balls[i].x;
       new_y = balls[i].y;
 
-      /* make color saturation be related to particle acceleration. */
-      if (glow_p)
+      if (!mono_p)
        {
-         float limit = 0.5;
-         double s, v, fraction;
-         float vx = balls [i].dx;
-         float vy = balls [i].dy;
-         XColor new_color;
-         if (vx < 0) vx = -vx;
-         if (vy < 0) vy = -vy;
-         fraction = vx + vy;
-         if (fraction > limit) fraction = limit;
-
-         s = 1 - (fraction / limit);
-         v = 1.0;
-
-         s = (s * 0.75) + 0.25;
-
-         hsv_to_rgb (balls [i].hue, s, v, 
-                     &new_color.red, &new_color.green, &new_color.blue);
-         if (XAllocColor (dpy, cmap, &new_color))
+         if (mode == ball_mode)
            {
-             XFreeColors (dpy, cmap, &balls [i].color.pixel, 1, 0);
-             balls [i].color = new_color;
+             if (glow_p)
+               {
+                 /* make color saturation be related to particle
+                    acceleration. */
+                 double limit = 0.5;
+                 double s, fraction;
+                 double vx = balls [i].dx;
+                 double vy = balls [i].dy;
+                 if (vx < 0) vx = -vx;
+                 if (vy < 0) vy = -vy;
+                 fraction = vx + vy;
+                 if (fraction > limit) fraction = limit;
+
+                 s = 1 - (fraction / limit);
+                 balls[i].pixel_index = (ncolors * s);
+               }
+             XSetForeground (dpy, draw_gc,
+                             colors[balls[i].pixel_index].pixel);
            }
        }
 
       if (mode == ball_mode)
        {
-         if (!mono_p)
-           XSetForeground (dpy, draw_gc, balls [i].color.pixel);
          XFillArc (dpy, window, erase_gc, (int) old_x, (int) old_y,
                    size, size, 0, 360*64);
          XFillArc (dpy, window, draw_gc,  (int) new_x, (int) new_y,
                    size, size, 0, 360*64);
        }
-      if (mode != ball_mode)
+      else
        {
          point_stack [point_stack_fp].x = new_x;
          point_stack [point_stack_fp].y = new_y;
@@ -494,45 +509,13 @@ run_balls (dpy, window)
        abort ();
       if (!mono_p)
        {
-         XColor color2, desired;
-         color2 = balls [0].color;
-         switch (cmode)
-           {
-           case cycle_mode:
-             cycle_hue (&color2, color_shift);
-             break;
-           case random_mode:
-             color2.red =   random () % 65535;
-             color2.green = random () % 65535;
-             color2.blue =  random () % 65535;
-             break;
-           default:
-             abort ();
-           }
-             
-         desired = color2;
-         if (XAllocColor (dpy, cmap, &color2))
+         static int tick = 0;
+         if (tick++ == color_shift)
            {
-             /* XAllocColor returns the actual RGB that the hardware let us
-                allocate.  Restore the requested values into the XColor struct
-                so that limited-resolution hardware doesn't cause cycle_hue to
-                get "stuck". */
-             color2.red = desired.red;
-             color2.green = desired.green;
-             color2.blue = desired.blue;
+             tick = 0;
+             fg_index = (fg_index + 1) % ncolors;
+             XSetForeground (dpy, draw_gc, colors[fg_index].pixel);
            }
-         else
-           {
-             color2 = balls [0].color;
-             if (!XAllocColor (dpy, cmap, &balls [0].color))
-               abort ();
-           }
-         pixel_stack [pixel_stack_fp++] = balls [0].color.pixel;
-         if (pixel_stack_fp >= pixel_stack_size)
-           pixel_stack_fp = 0;
-         XFreeColors (dpy, cmap, pixel_stack + pixel_stack_fp, 1, 0);
-         balls [0].color = color2;
-         XSetForeground (dpy, draw_gc, balls [0].color.pixel);
        }
     }
 
@@ -558,7 +541,6 @@ run_balls (dpy, window)
       break;
     case tail_mode:
       {
-       int i;
        for (i = 0; i < npoints; i++)
          {
            int index = point_stack_fp + i;
@@ -586,7 +568,6 @@ run_balls (dpy, window)
     case spline_mode:
     case spline_filled_mode:
       {
-       int i;
        static spline *s = 0;
        if (! s) s = make_spline (npoints);
        if (segments > 0)
@@ -631,11 +612,12 @@ run_balls (dpy, window)
 char *progclass = "Attraction";
 
 char *defaults [] = {
-  "Attraction.background:      black",         /* to placate SGI */
-  "Attraction.foreground:      white",
+  ".background:        black",
+  ".foreground:        white",
   "*mode:      balls",
   "*points:    0",
   "*size:      0",
+  "*colors:    200",
   "*threshold: 100",
   "*delay:     10000",
   "*glow:      false",
@@ -644,19 +626,20 @@ char *defaults [] = {
   "*viscosity: 1",
   "*orbit:     false",
   "*colorShift:        3",
-  "*segments:  100",
+  "*segments:  500",
+  "*vMult:     0.9",
   0
 };
 
 XrmOptionDescRec options [] = {
   { "-mode",           ".mode",        XrmoptionSepArg, 0 },
+  { "-colors",         ".colors",      XrmoptionSepArg, 0 },
   { "-points",         ".points",      XrmoptionSepArg, 0 },
+  { "-color-shift",    ".colorShift",  XrmoptionSepArg, 0 },
   { "-threshold",      ".threshold",   XrmoptionSepArg, 0 },
   { "-segments",       ".segments",    XrmoptionSepArg, 0 },
   { "-delay",          ".delay",       XrmoptionSepArg, 0 },
   { "-size",           ".size",        XrmoptionSepArg, 0 },
-  { "-color-mode",     ".colorMode",   XrmoptionSepArg, 0 },
-  { "-color-shift",    ".colorShift",  XrmoptionSepArg, 0 },
   { "-radius",         ".radius",      XrmoptionSepArg, 0 },
   { "-vx",             ".vx",          XrmoptionSepArg, 0 },
   { "-vy",             ".vy",          XrmoptionSepArg, 0 },
@@ -667,14 +650,12 @@ XrmOptionDescRec options [] = {
   { "-viscosity",      ".viscosity",   XrmoptionSepArg, 0 },
   { "-glow",           ".glow",        XrmoptionNoArg, "true" },
   { "-noglow",         ".glow",        XrmoptionNoArg, "false" },
-  { "-orbit",          ".orbit",       XrmoptionNoArg, "true" }
+  { "-orbit",          ".orbit",       XrmoptionNoArg, "true" },
+  { 0, 0, 0, 0 }
 };
-int options_size = (sizeof (options) / sizeof (options[0]));
 
 void
-screenhack (dpy, window)
-     Display *dpy;
-     Window window;
+screenhack (Display *dpy, Window window)
 {
   init_balls (dpy, window);
   while (1)