/* critical -- Self-organizing-criticality display hack for XScreenSaver
- * Copyright (C) 1998, 1999 Martin Pool <mbp@humbug.org.au>
+ * Copyright (C) 1998, 1999, 2000 Martin Pool <mbp@humbug.org.au>
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
*
* Revision history:
* 13 Nov 1998: Initial version, Martin Pool <mbp@humbug.org.au>
- */
+ * 08 Feb 2000: Change to keeping and erasing a trail, <mbp>
+ *
+ * It would be nice to draw curvy shapes rather than just straight
+ * lines, but X11 doesn't have spline primitives (?) so we'd have to
+ * do all the work ourselves */
#include "screenhack.h"
#include "erase.h"
#include <stdio.h>
#include <stdlib.h>
+#include <assert.h>
char *progclass = "Critical";
unsigned short *cells;
} CriticalModel;
+typedef struct {
+ int trail; /* length of trail */
+ int cell_size;
+} CriticalSettings;
+
CriticalModel * model_allocate (int w, int h);
void model_initialize (CriticalModel *model);
-static void model_step (CriticalModel *model, int *top_x, int *top_y);
-
/* Options this module understands. */
XrmOptionDescRec options[] = {
{ "-restart", ".restart", XrmoptionSepArg, 0 },
{ "-cellsize", ".cellsize", XrmoptionSepArg, 0 },
{ "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
+ { "-trail", ".trail", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 } /* end */
};
"*delay: 10000",
"*ncolors: 64",
"*restart: 8",
- "*cellsize: 9",
"*batchcount: 1500",
+ "*trail: 50",
0 /* end */
};
+int
+clip (int low, int val, int high)
+{
+ if (val < low)
+ return low;
+ else if (val > high)
+ return high;
+ else
+ return val;
+}
+
+
/* Allocate an return a new simulation model datastructure.
*/
Neighbours that fall off the edge of the model are simply
ignored. */
static void
-model_step (CriticalModel *model, int *top_x, int *top_y)
+model_step (CriticalModel *model, XPoint *ptop)
{
int x, y, i;
int dx, dy;
- unsigned short top_value;
+ unsigned short top_value = 0;
+ int top_x = 0, top_y = 0;
/* Find the top cell */
top_value = 0;
if (model->cells[i] >= top_value)
{
top_value = model->cells[i];
- *top_x = x;
- *top_y = y;
+ top_x = x;
+ top_y = y;
}
i++;
}
/* Replace it and its neighbours with new random values */
for (dy = -1; dy <= 1; dy++)
{
- int y = *top_y + dy;
+ int y = top_y + dy;
if (y < 0 || y >= model->height)
continue;
for (dx = -1; dx <= 1; dx++)
{
- int x = *top_x + dx;
+ int x = top_x + dx;
if (x < 0 || x >= model->width)
continue;
model->cells[y * model->width + x] = (unsigned short) random();
}
}
+
+ ptop->x = top_x;
+ ptop->y = top_y;
}
}
+/* Draw one step of the hack. Positions are cell coordinates. */
+static void
+draw_step (CriticalSettings *settings,
+ Display *dpy, Window window, GC gc,
+ int pos, XPoint *history)
+{
+ int cell_size = settings->cell_size;
+ int half = cell_size/2;
+ int old_pos = (pos + settings->trail - 1) % settings->trail;
+
+ pos = pos % settings->trail;
+
+ XDrawLine (dpy, window, gc,
+ history[pos].x * cell_size + half,
+ history[pos].y * cell_size + half,
+ history[old_pos].x * cell_size + half,
+ history[old_pos].y * cell_size + half);
+}
+
+
/* Display a self-organizing criticality screen hack. The program
runs indefinately on the root window. */
void
screenhack (Display *dpy, Window window)
{
- GC fgc, bgc;
- XGCValues gcv;
- XWindowAttributes wattr;
int n_colors;
XColor *colors;
int model_w, model_h;
CriticalModel *model;
int lines_per_color = 10;
int i_color = 0;
- int x1, y1, x2, y2;
long delay_usecs;
- int cell_size;
int batchcount;
+ XPoint *history; /* in cell coords */
+ int pos = 0;
+ int wrapped = 0;
+ GC fgc, bgc;
+ XGCValues gcv;
+ XWindowAttributes wattr;
+ CriticalSettings settings;
/* Number of screens that should be drawn before reinitializing the
model, and count of the number of screens done so far. */
/* Find window attributes */
XGetWindowAttributes (dpy, window, &wattr);
- /* Construct the initial model state. */
- cell_size = get_integer_resource ("cellsize", "Integer");
- if (cell_size < 1)
- cell_size = 1;
- if (cell_size >= 100)
- cell_size = 99;
-
batchcount = get_integer_resource ("batchcount", "Integer");
if (batchcount < 5)
batchcount = 5;
+
+ /* For the moment the model size is just fixed -- making it vary
+ with the screen size just makes the hack boring on large
+ screens. */
+ model_w = 80;
+ settings.cell_size = wattr.width / model_w;
+ model_h = wattr.height / settings.cell_size;
+
+ /* Construct the initial model state. */
+
+ settings.trail = clip(2, get_integer_resource ("trail", "Integer"), 1000);
- model_w = wattr.width / cell_size;
- model_h = wattr.height / cell_size;
-
+ history = malloc (sizeof history[0] * settings.trail);
+ if (!history)
+ {
+ fprintf (stderr, "critical: "
+ "couldn't allocate trail history of %d cells\n",
+ settings.trail);
+ return;
+ }
+
model = model_allocate (model_w, model_h);
if (!model)
{
fgc = XCreateGC (dpy, window, 0, &gcv);
- x2 = rand() % model_w;
- y2 = rand() % model_h;
-
delay_usecs = get_integer_resource ("delay", "Integer");
n_restart = get_integer_resource ("restart", "Integer");
while (1) {
int i_batch;
- if (!i_restart)
+ if (i_restart == 0)
{
+ /* Time to start a new simulation, this one has probably got
+ to be a bit boring. */
setup_colormap (dpy, &wattr, &colors, &n_colors);
+ erase_full_window (dpy, window);
model_initialize (model);
+ pos = 1;
+ wrapped = 0;
}
for (i_batch = batchcount; i_batch; i_batch--)
XChangeGC (dpy, fgc, GCForeground, &gcv);
}
- /* draw a line */
- x1 = x2;
- y1 = y2;
+ assert(pos >= 0 && pos < settings.trail);
+ model_step (model, &history[pos]);
- model_step (model, &x2, &y2);
+ draw_step (&settings, dpy, window, fgc, pos, history);
- XDrawLine (dpy, window, fgc,
- x1 * cell_size + cell_size/2,
- y1 * cell_size + cell_size/2,
- x2 * cell_size + cell_size/2,
- y2 * cell_size + cell_size/2);
+ /* we use the history as a ring buffer, but don't start erasing until
+ we've wrapped around once. */
+ if (++pos >= settings.trail)
+ {
+ pos -= settings.trail;
+ wrapped = 1;
+ }
- XSync (dpy, False);
- screenhack_handle_events (dpy);
+ if (wrapped)
+ {
+ draw_step (&settings, dpy, window, bgc, pos+1, history);
+ }
- if (delay_usecs)
- usleep (delay_usecs);
- }
+ XSync (dpy, False);
+ screenhack_handle_events (dpy);
+
+ if (delay_usecs)
+ usleep (delay_usecs);
+ }
+
i_restart = (i_restart + 1) % n_restart;
- erase_full_window (dpy, window);
}
}