http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.04.tar.gz
[xscreensaver] / hacks / deco.c
index 6e3612fa23ef11a8c92e81037b07e817a3176c6e..69085525a029f9ece95185a471c761628e6b7a95 100644 (file)
  *
  * Concept snarfed from Michael D. Bayne in
  * http://www.go2net.com/internet/deep/1997/04/16/body.html
+ *
+ * Changes by Lars Huttar, http://www.huttar.net:
+ * - allow use of golden ratio for dividing rectangles instead of 1/2.
+ * - allow smooth colors instead of random
+ * - added line thickness setting
+ * - added "Mondrian" mode
+ * Other ideas:
+ * - allow recomputing the colormap on each new frame (especially useful
+ *   when ncolors is low)
  */
 
 #include "screenhack.h"
@@ -22,12 +31,131 @@ struct state {
   int max_depth;
   int min_height;
   int min_width;
+  int line_width;
+  int old_line_width;
+  Bool goldenRatio;
+  Bool mondrian;
+  Bool smoothColors;
+
   int delay;
   XWindowAttributes xgwa;
   GC fgc, bgc;
   int current_color;
 };
 
+/* Golden Ratio
+ *   Suppose you're dividing a rectangle of length A+B
+ *   into two parts, of length A and B respectively. You want the ratio of
+ *   A to B to be the same as the ratio of the whole (A+B) to A. The golden
+ *   ratio (phi) is that ratio. Supposed to be visually pleasing. */
+#define PHI 1.61803
+#define PHI1 (1.0/PHI)
+#define PHI2 (1.0 - PHI1)
+
+/* copied from make_random_colormap in colors.c */
+static void
+make_mondrian_colormap (Display *dpy, Visual *visual, Colormap cmap,
+                     XColor *colors, int *ncolorsP,
+                     Bool allocate_p,
+                     Bool *writable_pP,
+                     Bool verbose_p)
+{
+  Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
+  int ncolors = 8;
+  int i;
+  Screen *screen = (dpy ? DefaultScreenOfDisplay(dpy) : 0); /* #### WRONG! */
+
+  if (*ncolorsP <= 0) return;
+
+  /* If this visual doesn't support writable cells, don't bother trying. */
+  if (wanted_writable && !has_writable_cells(screen, visual))
+    *writable_pP = False;
+
+  for (i = 0; i < ncolors; i++)
+  {
+      colors[i].flags = DoRed|DoGreen|DoBlue;
+      colors[i].red = 0;
+      colors[i].green = 0;
+      colors[i].blue = 0;
+
+      switch(i) {
+         case 0: case 1: case 2: case 3: case 7: /* white */
+             colors[i].red = 0xE800;
+             colors[i].green = 0xE800;
+             colors[i].blue  = 0xE800;
+             break;
+         case 4:
+             colors[i].red = 0xCFFF; break; /* red */
+         case 5:
+             colors[i].red = 0x2000;
+             colors[i].blue = 0xCFFF; break; /* blue */
+         case 6:
+             colors[i].red = 0xDFFF; /* yellow */
+             colors[i].green = 0xCFFF; break;
+      }
+  }
+
+  if (!allocate_p)
+    return;
+
+ RETRY_NON_WRITABLE:
+  if (writable_pP && *writable_pP)
+    {
+      unsigned long *pixels = (unsigned long *)
+       malloc(sizeof(*pixels) * (ncolors + 1));
+
+      allocate_writable_colors (dpy, cmap, pixels, &ncolors);
+      if (ncolors > 0)
+       for (i = 0; i < ncolors; i++)
+         colors[i].pixel = pixels[i];
+      free (pixels);
+      if (ncolors > 0)
+       XStoreColors (dpy, cmap, colors, ncolors);
+    }
+  else
+    {
+      for (i = 0; i < ncolors; i++)
+       {
+         XColor color;
+         color = colors[i];
+         if (!XAllocColor (dpy, cmap, &color))
+           break;
+         colors[i].pixel = color.pixel;
+       }
+      ncolors = i;
+    }
+
+  /* If we tried for writable cells and got none, try for non-writable. */
+  if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
+    {
+      ncolors = *ncolorsP;
+      *writable_pP = False;
+      goto RETRY_NON_WRITABLE;
+    }
+
+#if 0
+  /* I don't think we need to bother copying or linking to the complain
+     function. */
+  if (verbose_p)
+    complain(*ncolorsP, ncolors, wanted_writable,
+            wanted_writable && *writable_pP);
+#endif
+
+  *ncolorsP = ncolors;
+}
+
+static void
+mondrian_set_sizes (struct state *st, int w, int h)
+{
+    if (w > h) {
+       st->line_width = w/50;
+       st->min_height = st->min_width = w/8;
+    } else {
+       st->line_width = h/50;
+       st->min_height = st->min_width = h/8;
+    }
+}
 static void
 deco (Display *dpy, Window window, struct state *st,
       int x, int y, int w, int h, int depth)
@@ -45,15 +173,17 @@ deco (Display *dpy, Window window, struct state *st,
     }
   else
     {
-      if (random() & 1)
-       {
-         deco (dpy, window, st, x, y, w/2, h, depth+1);
-         deco (dpy, window, st, x+w/2, y, w/2, h, depth+1);
+      if ((st->goldenRatio || st->mondrian) ? (w > h) : (random() & 1))
+        { /* Divide the rectangle side-by-side */
+          int wnew = (st->goldenRatio ? (w * (random() & 1 ? PHI1 : PHI2)) : w/2);
+         deco (dpy, window, st, x, y, wnew, h, depth+1);
+         deco (dpy, window, st, x+wnew, y, w-wnew, h, depth+1);
        }
       else
-       {
-         deco (dpy, window, st, x, y, w, h/2, depth+1);
-         deco (dpy, window, st, x, y+h/2, w, h/2, depth+1);
+        { /* Divide the rectangle top-to-bottom */
+          int hnew = (st->goldenRatio ? (h * (random() & 1 ? PHI1 : PHI2)) : h/2);
+         deco (dpy, window, st, x, y, w, hnew, depth+1);
+         deco (dpy, window, st, x, y+hnew, w, h-hnew, depth+1);
        }
     }
 }
@@ -66,6 +196,11 @@ deco_init (Display *dpy, Window window)
 
   st->delay = get_integer_resource (dpy, "delay", "Integer");
 
+  st->smoothColors = get_boolean_resource(dpy, "smoothColors", "Boolean");
+  st->old_line_width = 1;
+
+  st->goldenRatio = get_boolean_resource (dpy, "goldenRatio", "Boolean");
+
   st->max_depth = get_integer_resource (dpy, "maxDepth", "Integer");
   if (st->max_depth < 1) st->max_depth = 1;
   else if (st->max_depth > 1000) st->max_depth = 1000;
@@ -75,8 +210,12 @@ deco_init (Display *dpy, Window window)
   st->min_height = get_integer_resource (dpy, "minHeight", "Integer");
   if (st->min_height < 2) st->min_height = 2;
 
+  st->line_width = get_integer_resource (dpy, "lineWidth", "Integer");
+
   XGetWindowAttributes (dpy, window, &st->xgwa);
 
+  st->ncolors = get_integer_resource (dpy, "ncolors", "Integer");
+
   gcv.foreground = get_pixel_resource(dpy, st->xgwa.colormap,
                                       "foreground", "Foreground");
   st->fgc = XCreateGC (dpy, window, GCForeground, &gcv);
@@ -85,11 +224,6 @@ deco_init (Display *dpy, Window window)
                                       "background", "Background");
   st->bgc = XCreateGC (dpy, window, GCForeground, &gcv);
 
-  st->ncolors = get_integer_resource (dpy, "ncolors", "Integer");
-
-  make_random_colormap (dpy, st->xgwa.visual, st->xgwa.colormap, st->colors, &st->ncolors,
-                       False, True, 0, True);
-
   if (st->ncolors <= 2)
     mono_p = True;
 
@@ -100,6 +234,28 @@ deco_init (Display *dpy, Window window)
       st->bgc = tmp;
     }
 
+  st->mondrian = get_boolean_resource(dpy, "mondrian", "Boolean");
+  if (st->mondrian) {
+      /* Mondrian, if true, overrides several other options. */
+      mondrian_set_sizes(st, st->xgwa.width, st->xgwa.height);
+
+      /** set up red-yellow-blue-black-white colormap and fgc **/
+      make_mondrian_colormap(dpy, st->xgwa.visual, st->xgwa.colormap,
+                            st->colors, &st->ncolors, True, 0, True);
+
+      /** put white in several cells **/
+      /** set min-height and min-width to about 10% of total w/h **/
+  }
+  else if (st->smoothColors)
+      make_smooth_colormap (dpy, st->xgwa.visual, st->xgwa.colormap,
+                           st->colors, &st->ncolors, True, 0, True);
+  else
+      make_random_colormap (dpy, st->xgwa.visual, st->xgwa.colormap,
+                           st->colors, &st->ncolors, False, True, 0, True);
+
+  gcv.line_width = st->old_line_width = st->line_width;
+  XChangeGC(dpy, st->fgc, GCLineWidth, &gcv);
+
   return st;
 }
 
@@ -108,6 +264,14 @@ deco_draw (Display *dpy, Window window, void *closure)
 {
   struct state *st = (struct state *) closure;
   XFillRectangle (dpy, window, st->bgc, 0, 0, st->xgwa.width, st->xgwa.height);
+  if (st->mondrian) {
+      mondrian_set_sizes(st, st->xgwa.width, st->xgwa.height);
+      if (st->line_width != st->old_line_width) {
+         XSetLineAttributes(dpy, st->fgc, st->line_width,
+                            LineSolid, CapButt, JoinBevel);
+         st->old_line_width = st->line_width;
+      }
+  }
   deco (dpy, window, st, 0, 0, st->xgwa.width, st->xgwa.height, 0);
   return 1000000 * st->delay;
 }
@@ -141,8 +305,12 @@ static const char *deco_defaults [] = {
   "*maxDepth:          12",
   "*minWidth:          20",
   "*minHeight:         20",
+  "*lineWidth:          1",
   "*delay:             5",
   "*ncolors:           64",
+  "*goldenRatio:        False",
+  "*smoothColors:       False",
+  "*mondrian:           False",
   0
 };
 
@@ -150,8 +318,15 @@ static XrmOptionDescRec deco_options [] = {
   { "-max-depth",      ".maxDepth",    XrmoptionSepArg, 0 },
   { "-min-width",      ".minWidth",    XrmoptionSepArg, 0 },
   { "-min-height",     ".minHeight",   XrmoptionSepArg, 0 },
+  { "-line-width",     ".lineWidth",   XrmoptionSepArg, 0 },
   { "-delay",          ".delay",       XrmoptionSepArg, 0 },
   { "-ncolors",                ".ncolors",     XrmoptionSepArg, 0 },
+  { "-golden-ratio",    ".goldenRatio", XrmoptionNoArg, "True" },
+  { "-no-golden-ratio", ".goldenRatio", XrmoptionNoArg, "False" },
+  { "-smooth-colors",   ".smoothColors",XrmoptionNoArg, "True" },
+  { "-no-smooth-colors",".smoothColors",XrmoptionNoArg, "False" },
+  { "-mondrian",        ".mondrian",    XrmoptionNoArg, "True" },
+  { "-no-mondrian",     ".mondrian",    XrmoptionNoArg, "False" },
   { 0, 0, 0, 0 }
 };