ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-1.25.tar.Z
[xscreensaver] / hacks / pedal.c
diff --git a/hacks/pedal.c b/hacks/pedal.c
new file mode 100644 (file)
index 0000000..b39fef4
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * pedal
+ *
+ * Based on a program for some old PDP-11 Graphics Display Processors
+ * at CMU.
+ *
+ * X version by
+ *
+ *  Dale Moore  <Dale.Moore@cs.cmu.edu>
+ *  24-Jun-1994
+ *
+ *  Copyright \(co 1994, by Carnegie Mellon University.  Permission to use,
+ *  copy, modify, distribute, and sell this software and its documentation
+ *  for any purpose is hereby granted without fee, provided fnord that the
+ *  above copyright notice appear in all copies and that both that copyright
+ *  notice and this permission notice appear in supporting documentation.
+ *  No representations are made about the  suitability of fnord this software
+ *  for any purpose.  It is provided "as is" without express or implied
+ *  warranty.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include "screenhack.h"
+
+/* If MAXLINES is too big, we might not be able to get it
+ * to the X server in the 2byte length field. Must be less
+ * than 16k
+ */
+#define MAXLINES (16 * 1024)
+#define MAXPOINTS MAXLINES
+XPoint *points;
+
+/* 
+ * If the pedal has only this many lines, it must be ugly and we dont
+ * want to see it.
+ */
+#define MINLINES 7
+
+static int sizex, sizey;
+static int delay;
+static int fadedelay;
+static int maxlines;
+static GC gc;
+static XColor foreground, background;
+static Colormap cmap;
+
+static Bool fade_p;
+
+
+/*
+ * Routine (Macro actually)
+ *   mysin
+ * Description:
+ *   Assume that degrees is .. oh 360... meaning that
+ *   there are 360 degress in a circle.  Then this function
+ *   would return the sin of the angle in degrees.  But lets
+ *   say that they are really big degrees, with 4 big degrees
+ *   the same as one regular degree.  Then this routine
+ *   would be called mysin(t, 90) and would return sin(t degrees * 4)
+ */
+#define mysin(t, degrees) sin(t * 2 * M_PI / (degrees))
+#define mycos(t, degrees) cos(t * 2 * M_PI / (degrees))
+
+/*
+ * Macro:
+ *   rand_range
+ * Description:
+ *   Return a random number between a inclusive  and b exclusive.
+ *    rand (3, 6) returns 3 or 4 or 5, but not 6.
+ */
+#define rand_range(a, b) (a + random() % (b - a))
+
+
+static int gcd (m, n)
+    int m;
+    int n;
+/* 
+ * Greatest Common Divisor (also Greates common factor).
+ */
+{
+    int r;
+
+    for (;;) {
+        r = m % n;
+        if (r == 0) return (n);
+        m = n;
+        n = r;
+    }
+}
+
+static int numlines (a, b, d)
+    int a;
+    int b;
+    int d;
+/*
+ * Description:
+ *
+ *      Given parameters a and b, how many lines will we have to draw?
+ *
+ * Algorithm:
+ *
+ *      This algorithm assumes that r = sin (theta * a), where we
+ *      evaluate theta on multiples of b.
+ *
+ *      LCM (i, j) = i * j / GCD (i, j);
+ *
+ *      So, at LCM (b, 360) we start over again.  But since we
+ *      got to LCM (b, 360) by steps of b, the number of lines is
+ *      LCM (b, 360) / b.
+ *
+ *      If a is odd, then at 180 we cross over and start the
+ *      negative.  Someone should write up an elegant way of proving
+ *      this.  Why?  Because I'm not convinced of it myself. 
+ *
+ */
+{
+#define odd(x) (x & 1)
+#define even(x) (!odd(x))
+    if ( odd(a) && odd(b) && even(d)) d /= 2;
+    return  (d / gcd (d, b));
+#undef odd
+}
+
+static int
+compute_pedal(points, maxpoints)
+XPoint *points;
+int maxpoints;
+/*
+ * Description:
+ *
+ *    Basically, it's combination spirograph and string art.
+ *    Instead of doing lines, we just use a complex polygon,
+ *    and use an even/odd rule for filling in between.
+ *
+ *    The spirograph, in mathematical terms is a polar
+ *    plot of the form r = sin (theta * c);
+ *    The string art of this is that we evaluate that
+ *    function only on certain multiples of theta.  That is
+ *    we let theta advance in some random increment.  And then
+ *    we draw a straight line between those two adjacent points.
+ *
+ *    Eventually, the lines will start repeating themselves
+ *    if we've evaluated theta on some rational portion of the
+ *    whole.
+ *
+ *    The number of lines generated is limited to the
+ *    ratio of the increment we put on theta to the whole.
+ *    If we say that there are 360 degrees in a circle, then we
+ *    will never have more than 360 lines.   
+ *
+ * Return:
+ *
+ *    The number of points.
+ *
+ */
+{
+    int a, b, d;  /* These describe a unique pedal */
+
+    double r;
+    int theta = 0;
+    XPoint *pp = points;
+    int count;
+    int numpoints;
+
+    /* Just to make sure that this division is not done inside the loop */
+    int h_width = sizex / 2, h_height = sizey / 2 ;
+
+    for (;;) {
+       d = rand_range (MINLINES, maxlines);
+
+       a = rand_range (1, d);
+       b = rand_range (1, d);
+       numpoints = numlines(a, b, d);
+       if (numpoints > MINLINES) break;
+    }
+
+    /* it might be nice to try to move as much sin and cos computing
+     * (or at least the argument computing) out of the loop.
+     */
+    for (count = numpoints; count-- ; )
+    {
+        r = mysin (theta * a, d);
+
+        /* Convert from polar to cartesian coordinates */
+       /* We could round the results, but coercing seems just fine */
+        pp->x = mysin (theta, d) * r * h_width  + h_width;
+        pp->y = mycos (theta, d) * r * h_height + h_height;
+
+        /* Advance index into array */
+        pp++;
+
+        /* Advance theta */
+        theta += b;
+        theta %= d;
+    }
+
+    return(numpoints);
+}
+
+static void
+init_pedal (dpy, window)
+     Display *dpy;
+     Window window;
+{
+  XGCValues gcv;
+  XWindowAttributes xgwa;
+
+  fade_p = !mono_p;
+
+  delay = get_integer_resource ("delay", "Integer");
+  if (delay < 0) delay = 0;
+
+  fadedelay = get_integer_resource ("fadedelay", "Integer");
+  if (fadedelay < 0) fadedelay = 0;
+
+  maxlines = get_integer_resource ("maxlines", "Integer");
+  if (maxlines < MINLINES) maxlines = MINLINES;
+  else if (maxlines > MAXLINES) maxlines = MAXLINES;
+
+  points = (XPoint *)malloc(sizeof(XPoint) * maxlines);
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  sizex = xgwa.width;
+  sizey = xgwa.height;
+
+  if ((xgwa.visual->class != GrayScale) && (xgwa.visual->class != PseudoColor))
+    fade_p = False;
+
+  cmap = xgwa.colormap;
+
+  gcv.function = GXcopy;
+  gcv.subwindow_mode = IncludeInferiors;
+  gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
+  gcv.background = get_pixel_resource ("background", "Background", dpy, cmap);
+  gc = XCreateGC (
+       dpy,
+       window,
+       GCForeground | GCBackground |GCFunction | GCSubwindowMode ,
+       &gcv);
+
+  if (fade_p)
+  {
+      int status;
+      foreground.pixel = gcv.foreground;
+      XQueryColor (dpy, cmap, &foreground);
+
+      status = XAllocColorCells (
+                       dpy,
+                       cmap,
+                       0,
+                       NULL,
+                       0,
+                       &foreground.pixel,
+                       1);
+      if (status)
+      {
+         XStoreColor ( dpy, cmap, &foreground);
+         XSetForeground (dpy, gc, foreground.pixel);
+
+         background.pixel = gcv.background;
+         XQueryColor (dpy, cmap, &background);
+      }
+      else
+      {
+         /* If we cant allocate a color cell, then just forget the
+           * whole fade business.
+           */
+         fade_p = False;
+      }
+  }
+}
+
+static void
+fade_foreground (dpy, cmap, from, to, steps)
+    Display *dpy;
+    Colormap cmap;
+    XColor from;
+    XColor to;
+    int steps;
+/*
+ * This routine assumes that we have a writeable colormap.
+ * That means that the default colormap is not full, and that
+ * the visual class is PseudoColor or GrayScale.
+ */
+{
+    int i;
+    XColor inbetween;
+    int udelay = fadedelay / (steps + 1);
+
+    inbetween = foreground;
+    for (i = 0; i <= steps; i++ )
+    {
+      inbetween.red   = from.red   + (to.red   - from.red)   * i / steps ;
+      inbetween.green = from.green + (to.green - from.green) * i / steps ;
+      inbetween.blue  = from.blue  + (to.blue  - from.blue)  * i / steps ;
+      XStoreColor (dpy, cmap, &inbetween);
+      /* If we don't sync, these can bunch up */
+      XSync(dpy, 0);
+      usleep(udelay);
+    }
+}
+
+static void
+pedal (dpy, window)
+     Display *dpy;
+     Window window;
+/*
+ *    Since the XFillPolygon doesn't require that the last
+ *    point == first point, the number of points is the same
+ *    as the number of lines.  We just let XFillPolygon supply
+ *    the line from the last point to the first point.
+ *
+ */
+{
+   int numpoints;
+
+   numpoints = compute_pedal(points, maxlines);
+
+   /* Fade out, make foreground the same as background */
+   if (fade_p)
+     fade_foreground (dpy, cmap, foreground, background, 32);
+
+    /* Clear the window of previous garbage */
+    XClearWindow (dpy, window);
+
+    XFillPolygon (
+                dpy,
+                window,
+                gc,
+                points,
+                numpoints,
+                Complex,
+                CoordModeOrigin);
+
+   /* Pick a new foreground color (added by jwz) */
+   if (! mono_p)
+     {
+       XColor color;
+       hsv_to_rgb (random()%360, 1.0, 1.0,
+                  &color.red, &color.green, &color.blue);
+       XSync(dpy, 0);
+       if (fade_p)
+        {
+          foreground.red = color.red;
+          foreground.green = color.green;
+          foreground.blue = color.blue;
+          XStoreColor (dpy, cmap, &foreground);
+        }
+       else if (XAllocColor (dpy, cmap, &color))
+        {
+          XSetForeground (dpy, gc, color.pixel);
+          XFreeColors (dpy, cmap, &foreground.pixel, 1, 0);
+          foreground.red = color.red;
+          foreground.green = color.green;
+          foreground.blue = color.blue;
+          foreground.pixel = color.pixel;
+        }
+       XSync(dpy, 0);
+     }
+
+    /* Fade in by bringing the foreground back from background */
+    if (fade_p)
+       fade_foreground (dpy, cmap, background, foreground, 32);
+}
+
+\f
+char *progclass = "Pedal";
+
+/*
+ * If we are trying to save the screen, the background
+ * should be dark.
+ */
+char *defaults [] = {
+  "*delay:                     5",
+  "*fadedelay:                 200000",
+  "*maxlines:                  1000",
+  "*foreground:                 white",
+  "*background:                 black",
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-delay",          ".delay",               XrmoptionSepArg, 0 },
+  { "-fadedelay",      ".fadedelay",           XrmoptionSepArg, 0 },
+  { "-maxlines",       ".maxlines",            XrmoptionSepArg, 0 },
+  { "-foreground",      ".foreground",          XrmoptionSepArg, 0 },
+  { "-background",      ".background",          XrmoptionSepArg, 0 },
+};
+
+int options_size = (sizeof (options) / sizeof (options[0]));
+
+void
+screenhack (dpy, window)
+     Display *dpy;
+     Window window;
+{
+    init_pedal (dpy, window);
+    for (;;) {
+       pedal (dpy, window);
+       XSync(dpy, 0);
+       if (delay) sleep (delay);
+    }
+}