[comp.sources.x] Diffusion Limited Aggregate Fractal

news@sun.Eng.Sun.COM (news) (08/31/90)

Submitted-by: reed!news
Posting-number: Volume 9, Issue 12
Archive-name: dlaf/part01

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\End\Of\Shar\'
This generates a "Diffusion Limited Aggregate Fractal", as described
in some issue of Scientific American last year or so.  Lots of silly
features have been added, and there are some optimizations for the
default case (instead of letting the new particle wander all over an
empty screen, we contrain it to a vicinity of the existing mass of
particles).  Other than being a memory pig and being poorly
documented, it's a rather nice program.  The -shape flag doesn't seem
to work correctly -- something change after keith@expo made a few
changes, and I haven't bothered to track it down.

(Gee, that was great!  I haven't been able to blame Keith for
something in years.  :-)  )

Let's see... -lifetime sets the lifetime of a moving particle, -decay
sets the lifetime of all the other particles.  -expire is a synonym
for one of these.  -border controls the border around the already
existing mass (the optimization I mentioned).  -flash and -lightning
are synonyms as well.

Oh yeah, "q" lets you quit if you aren't running it in the root window.

Comments, problems, and code to trost%reed@cse.ogi.edu.  I haven't
touched the code in months, though, so I'm not certain what the state
of the internals is.

Enjoy!
\End\Of\Shar\
else
  echo "will not over write ./README"
fi
if [ `wc -c ./README | awk '{printf $1}'` -ne 1214 ]
then
echo `wc -c ./README | awk '{print "Got " $1 ", Expected " 1214}'`
fi
if `test ! -s ./Imakefile`
then
echo "writing ./Imakefile"
cat > ./Imakefile << '\End\Of\Shar\'
DEFINES =
INCLUDES = -I$(TOP) -I$(TOP)/X11
DEPLIBS = $(DEPXLIB)
LOCAL_LIBRARIES = $(XLIB)
SYS_LIBRARIES =

SRCS = addpoint.c args.c assert.c bits.c flags.c main.c usage.c
OBJS = addpoint.o args.o assert.o bits.o flags.o main.o usage.o

ComplexProgramTarget(xdla)
\End\Of\Shar\
else
  echo "will not over write ./Imakefile"
fi
if [ `wc -c ./Imakefile | awk '{printf $1}'` -ne 263 ]
then
echo `wc -c ./Imakefile | awk '{print "Got " $1 ", Expected " 263}'`
fi
if `test ! -s ./Makefile.simple`
then
echo "writing ./Makefile.simple"
cat > ./Makefile.simple << '\End\Of\Shar\'
SRCS =         addpoint.c args.c assert.c bits.c flags.c main.c usage.c
OBJS =         addpoint.o args.o assert.o bits.o flags.o main.o usage.o
XLIB =	       -lX11

xdla: $(OBJS)
	$(CC) $(CFLAGS) -o $@ $(OBJS) $(XLIB)

clean:
	rm -f $(OBJS) xdla

addpoint.o: main.h
args.o: main.h
assert.o:
bits.o: main.h
flags.o: main.h
main.o: main.h
usage.o: main.h
\End\Of\Shar\
else
  echo "will not over write ./Makefile.simple"
fi
if [ `wc -c ./Makefile.simple | awk '{printf $1}'` -ne 353 ]
then
echo `wc -c ./Makefile.simple | awk '{print "Got " $1 ", Expected " 353}'`
fi
if `test ! -s ./addpoint.c`
then
echo "writing ./addpoint.c"
cat > ./addpoint.c << '\End\Of\Shar\'
#include "main.h"

static void
DrawLightning(np)
int np;
{
    if (useroot)
	XLowerWindow(dpy, lightningWindow);
    XMapWindow(dpy, lightningWindow);
    XDrawPoints(dpy, lightningWindow, lgc, pl, np, CoordModeOrigin);
}    

int
AddPoint()
{
    int	    x, y, dx = 0, dy = 0;
    int	    top_area, middle_area, bottom_area;
    int	    total_area;
    int	    a;
    int	    i;
    int		np = 0;

    top_area = (curright - curleft) * curtop;
    middle_area = dla_width * (curbottom - curtop);
    bottom_area = (curright - curleft) * (dla_height - curbottom);
    total_area = top_area + middle_area + bottom_area;
    do {
	a = random() % total_area;
	if (a < top_area) {
	    y = a / (curright - curleft);
	    x = a % (curright - curleft) + curleft;
	} else if ((a -= top_area) < middle_area) {
	    y = a / dla_width + curtop;
	    x = a % dla_width;
	} else {
	    a -= middle_area;
	    y = a / (curright - curleft) + curbottom;
	    x = a % (curright - curleft) + curleft;
	}
    } while (GetBit(x, y));

    for (i = 0; (life == 0 || i < life) && !GetBit(x + dx, y + dy); i++) {
	if (lightning && np > 16000) {
	    DrawLightning(np);
	    np = 0;
	}
	if (lightning && (np == 0 || pl[np - 1].x != x || pl[np - 1].y != y)) {
	    pl[np].x = x;
	    pl[np++].y = y;
	}

	if ((x += dx) < curleft)
	    x = curleft;
	else if (x > curright)
	    x = curright;
	if ((y += dy) < curtop)
	    y = curtop;
	else if (y > curbottom)
	    y = curbottom;

	do {
	    a = random () % 9;
	    dx = (a % 3) - 1;
	    dy = (a / 3) - 1;
	} while (folds == 4 && dx && dy);

	if (folds == 6 && dx)
	    dy = 0;
    }
    
    SetBit(x, y);
    if (lightning) {
	DrawLightning(np);
	XFlush(dpy);
	XUnmapWindow(dpy, lightningWindow);
    }

    if (expire) {
	/* yes, this misses the corner, but close enough */
	if (oldpoint[last].x != 0 || oldpoint[last].y != 0) {
	    ResetBit(oldpoint[last].x, oldpoint[last].y);
	    XClearArea(dpy, win, oldpoint[last].x - BORDER,
		       oldpoint[last].y - BORDER, 1, 1, 0);
	}
	oldpoint[last].x = x;
	oldpoint[last++].y = y;
	last %= expire;
    }
	
    XDrawPoint(dpy, win, gc, x - BORDER, y - BORDER);

    /* compute new bounds */
    if (x - BORDER < curleft) {
       curleft = x - BORDER;
       if (curleft <= 0) {
           curleft = 1;
	   return 0;
       }
    }
    else if (x + BORDER > curright) {
       curright = x + BORDER;
       if (curright >= dla_width + 2 * BORDER - 1) {
           curright = dla_width + 2 * BORDER - 2;
	   return 0;
       }
    }

    if (y - BORDER < curtop) {
       curtop = y - BORDER;
       if (curtop <= 0) {
           curtop = 1;
	   return 0;
       }
    }
    else if (y + BORDER > curbottom) {
       curbottom = y + BORDER;
       if (curbottom >= dla_height + 2 * BORDER - 1) {
	    curbottom = dla_height + 2 * BORDER - 2;
	    return 0;
       }
    }
    return 1;
}

char*
Bounds(n)
int n;
{
    static char buf[256];
    int i, l = pl[0].x, r = pl[0].x, t = pl[0].y, b = pl[0].y;

    for (i = 1; i < n; i++) {
	if (pl[i].x < l)
	    l = pl[i].x;
	else if (pl[i].x > r)
	    r = pl[i].x;
	if (pl[i].y < t)
	    t = pl[i].y;
	else if (pl[i].y > b)
	    b = pl[i].y;
    }
    sprintf(buf, "(%d, %d, %d, %d)", l, r, t, b);
    return buf;
}
\End\Of\Shar\
else
  echo "will not over write ./addpoint.c"
fi
if [ `wc -c ./addpoint.c | awk '{printf $1}'` -ne 3236 ]
then
echo `wc -c ./addpoint.c | awk '{print "Got " $1 ", Expected " 3236}'`
fi
if `test ! -s ./args.c`
then
echo "writing ./args.c"
cat > ./args.c << '\End\Of\Shar\'
#include "main.h"

void
ParseArgs(argc, argv)
int    argc;
char** argv;
{

    for (progname = *argv++, argc--; argc; argv++, argc--) {
       Argassoc*       a;

       for (a = map; a->name; a++)
           if (**argv == '-' && strcmp(a->name, *argv + 1) == 0) {
               switch (a->type) {

               case Boolean:
                   *((int *) a->ref) = (int) a->value;
                   break;

               case String:
                   *((char **) a->ref) = *++argv;
                   --argc;
                   break;

               case Integer:
                   *((int *) a->ref) = atoi(*++argv);
                   --argc;
                   break;

               case Function:
                   (*(void (*)()) a->ref)();
                   break;
               }
               break;
           }
       if (!a->name)
           Usage();
    }
}

int
ParseShape(s)
char*  s;
{
    static struct {
       char*   s;
       int     i;
    } tbl[] = {
       "dot", DOT,
       "point", DOT,
       "line", LINE,
       0, 0
    }, *here;

    for (here = tbl; here->s; here++)
       if (strcmp(here->s, s) == 0)
           return here->i;
    fprintf (stderr, "valid shape types are:");
    for (here = tbl; here->s; here++)
	fprintf (stderr, " %s", here->s);
    fprintf (stderr, "\n");
    return -1;
}
\End\Of\Shar\
else
  echo "will not over write ./args.c"
fi
if [ `wc -c ./args.c | awk '{printf $1}'` -ne 1340 ]
then
echo `wc -c ./args.c | awk '{print "Got " $1 ", Expected " 1340}'`
fi
if `test ! -s ./assert.c`
then
echo "writing ./assert.c"
cat > ./assert.c << '\End\Of\Shar\'
#include       <stdio.h>
#include       <varargs.h>

/*
 * error routines
 */

void
complain(cond, fmt, args)
int    cond;
char*  fmt;
va_list        args;
{
    if (cond)
       _doprnt(fmt, &args, stderr);
}

void
assert(cond, fmt, args)
int    cond;
char*  fmt;
va_list        args;
{
       if (!cond) {
               _doprnt(fmt, &args, stderr);
               exit(1);
       }
}

void
panic(cond, fmt, args)
int    cond;
char*  fmt;
va_list        args;
{
    if (cond) {
       _doprnt(fmt, &args, stderr);
       abort();
    }
}
\End\Of\Shar\
else
  echo "will not over write ./assert.c"
fi
if [ `wc -c ./assert.c | awk '{printf $1}'` -ne 540 ]
then
echo `wc -c ./assert.c | awk '{print "Got " $1 ", Expected " 540}'`
fi
if `test ! -s ./bits.c`
then
echo "writing ./bits.c"
cat > ./bits.c << '\End\Of\Shar\'
#include "main.h"

#ifndef SetBit
void
SetBit(x, y)
int    x, y;
{
    char*      line = bits[y];

    if (line == 0)
       line = bits[y] = (char *) calloc(dpywidth / 8 + 1, sizeof(char));
    line[x / 8] |= 1 << (x & 7);
}
#endif

#ifndef ResetBit
void
ResetBit(x, y)
int	x, y;
{
    bits[y][x / 8] &= ~(1 << (x & 7));
}
#endif

#ifndef GetBit
int
GetBit(x, y)
int    x, y;
{
    char*      line = bits[y];
    if (line == 0)
       return 0;
    return line[x / 8] & (1 << (x & 7));
}
#endif
\End\Of\Shar\
else
  echo "will not over write ./bits.c"
fi
if [ `wc -c ./bits.c | awk '{printf $1}'` -ne 496 ]
then
echo `wc -c ./bits.c | awk '{print "Got " $1 ", Expected " 496}'`
fi
if `test ! -s ./flags.c`
then
echo "writing ./flags.c"
cat > ./flags.c << '\End\Of\Shar\'
#include "main.h"

Argassoc       map[] = {
    { String, "display", (char *) &displayname },
    { String, "fg", (char *) &foreground },
    { String, "bg", (char *) &background },
    { Integer, "border", (char *) &BORDER },
    { Integer, "decay", (char*) &expire },
    { Integer, "expire", (char *) &expire },
    { Integer, "folds", (char *) &folds },
    { Boolean, "flash", (char *) &lightning, (char *) 1},
    { Integer, "lifetime", (char *) &life },
    { Boolean, "lightning", (char *) &lightning, (char *) 1},
    { String, "lightningcolor", (char *) &lightningColor },
    { String, "shape", (char *) &shape },
    { Boolean, "root", (char *) &useroot, (char*) 1 },
    { Function, "help", (char *) Usage },
    { Integer, "update", (char *) &update },
    { String, "geometry", (char *) &geometry },
    { Boolean, 0, 0 },
};
\End\Of\Shar\
else
  echo "will not over write ./flags.c"
fi
if [ `wc -c ./flags.c | awk '{printf $1}'` -ne 841 ]
then
echo `wc -c ./flags.c | awk '{print "Got " $1 ", Expected " 841}'`
fi
if `test ! -s ./main.c`
then
echo "writing ./main.c"
cat > ./main.c << '\End\Of\Shar\'
#include "main.h"
#include <X11/keysym.h>

int		screen;
unsigned	dla_width, dla_height;
unsigned	curleft, curright, curbottom, curtop;
Display*	dpy;
Window		win;
char**		bits;
unsigned long	fg, bg;
char*		displayname = 0;
char*		progname;
char*		foreground;
char*		background;
char*		foldstr = 0;
char*		shape = "dot";
char*		geometry = "500x500";
int		useroot = 0;
int		BORDER;
int		folds;
int		life;
int		lightning;
Window		lightningWindow;
XPoint		pl[32767];
char*		lightningColor;
int		update = 20;
GC		gc, lgc;
Pixmap		pic;
XGCValues	gcv;
int		expire, last;
XPoint*		oldpoint;

main(argc, argv)
int    argc;
char*  argv[];
{
    int		noquit;
    int		mask;
    int		screen;
    int		x, y;
    int		i;
    Window	gunk, parent;
    XColor	cdef;
    Colormap	dcm;
    XSizeHints	xsh;
    unsigned int	width, height;
    XSetWindowAttributes	attrs;

    ParseArgs(argc, argv);

    if (BORDER <= 0)
	BORDER = 10;		/* very arbitrary */

    assert(dpy = XOpenDisplay(displayname),
	   "unable to open display \"%s\".\n", XDisplayName (displayname));

    screen = XDefaultScreen (dpy);
    dcm = DefaultColormap(dpy, screen);

    if (background && XParseColor(dpy, dcm, background, &cdef) &&
	XAllocColor(dpy, dcm, &cdef))
	bg = cdef.pixel;
    else
	bg = BlackPixel(dpy, screen);

    if (foreground && XParseColor(dpy, dcm, foreground, &cdef) &&
	XAllocColor(dpy, dcm, &cdef))
	fg = cdef.pixel;
    else
	fg = WhitePixel(dpy, screen);

    if (useroot) {
	win = XRootWindow(dpy, screen);
	width = DisplayWidth (dpy, screen);
	height = DisplayHeight (dpy, screen);
    } else {
	mask = XParseGeometry (geometry, &x, &y, &width, &height);
	xsh.flags = PSize;
	xsh.width = width;
	xsh.height = height;
	parent = XCreateSimpleWindow(dpy, XRootWindow(dpy, screen),
				     x, y, width, height, 1, fg, bg);
	win =  XCreateSimpleWindow(dpy, parent,
				   0, 0, width, height, 0, fg, bg);
	XSetStandardProperties (dpy, parent, "Diffusion Limited Aggregates",
				"xdla", None, argv, argc, &xsh);
	XSelectInput (dpy, win, KeyPressMask|KeyReleaseMask);
    }
    dla_width = width + 2 * BORDER;
    dla_height = height + 2 * BORDER;

    attrs.backing_store = Always;
    attrs.win_gravity = CenterGravity;
    XChangeWindowAttributes (dpy, win, CWBackingStore|CWWinGravity, &attrs);

    gcv.foreground = fg;
    gc = XCreateGC(dpy, win, GCForeground, &gcv);

    if (lightningColor)
	lightning = 1;

    if (lightning) {
	attrs.override_redirect = 1;
	lightningWindow = XCreateWindow(dpy, win, -BORDER, -BORDER,
					width, height, 0, CopyFromParent,
					InputOutput, CopyFromParent,
					CWOverrideRedirect, &attrs);

	if (lightningColor && XParseColor(dpy, dcm, lightningColor, &cdef) &&
	    XAllocColor(dpy, dcm, &cdef))
	    gcv.foreground = cdef.pixel;
	lgc = XCreateGC(dpy, lightningWindow, GCForeground, &gcv);
    }

    if (expire) {
	noquit = 1;
	oldpoint = (XPoint*) calloc(expire, sizeof(oldpoint[0]));
    }

    srandom(getpid());

    bits = (char **) calloc(dla_height, sizeof(*bits));

    switch (ParseShape(shape)) {
    case -1:
	Usage ();
	break;
    case DOT:
	(void) SetBit(dla_width / 2, dla_height / 2);
	(void) XDrawPoint(dpy, win, gc,
			  dla_width / 2 - BORDER,
			  dla_height / 2 - BORDER);
	if (life) {
	    curleft = curtop = 1;
	    curright = width - 1;
	    curbottom = height - 1;
	}
	else {
	    curleft = dla_width / 2 - BORDER;
	    curright = dla_width / 2 + BORDER;
	    curtop = dla_height / 2 - BORDER;
	    curbottom = dla_height / 2 + BORDER;
	}
	break;

    case LINE:
	noquit = 1;
	for (i = 0; i < dla_width; i++) {
	    SetBit(i, dla_height - 1);
	    XDrawPoint(dpy, win, gc, i, dla_height - 1 - BORDER);
	}
	curleft = 0;
	curright = dla_width;
	curbottom = dla_height - 1;
	curtop = dla_height - BORDER - 1;
	break;
    }

    if (folds == 0)
	folds = 8;
    assert(folds == 4 || folds == 6 || folds == 8,
	   "folds must be one of 4, 6, or 8, not %d.\n", folds);

    if (useroot) {
	XSetWindowBackground (dpy, win, bg);
	XClearWindow(dpy, win);
    }
    else {
	XMapWindow(dpy, win);
	XMapWindow (dpy, parent);
    }

    if (life)
	noquit = 1;
    i = 0;
    while (AddPoint() || noquit) {
	if (++i >= update) {
	    XEvent      ev;
	    i = XEventsQueued (dpy, QueuedAfterFlush);
	    while (i--) {
		XNextEvent (dpy, &ev);
		Dispatch (&ev);
	    }
	}
    }
    for (;;) {
	XEvent  ev;
	XNextEvent (dpy, &ev);
	Dispatch (&ev);
    }
}

Dispatch (ev)
XEvent *ev;
{
       switch (ev->type) {
       case KeyPress:
           switch (XLookupKeysym (ev, 0)) {
           case XK_Q:
           case XK_q:
           case XK_Cancel:
           case XK_Break:
               exit (0);
           }
       }
}

\End\Of\Shar\
else
  echo "will not over write ./main.c"
fi
if [ `wc -c ./main.c | awk '{printf $1}'` -ne 4659 ]
then
echo `wc -c ./main.c | awk '{print "Got " $1 ", Expected " 4659}'`
fi
if `test ! -s ./main.h`
then
echo "writing ./main.h"
cat > ./main.h << '\End\Of\Shar\'
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>

#define DOT    0
#define LINE   1

typedef enum { Boolean, String, Integer, Function } ArgType;

typedef struct {
    ArgType    type;
    char*      name;
    char*      ref;
    char*      value;  /* wildcard thingie */
} Argassoc;

extern int		screen;
extern unsigned		dla_width, dla_height;
extern unsigned		curleft, curright, curbottom, curtop;
extern Display*		dpy;
extern Window		win;
extern char**		bits;
extern unsigned long	fg;
extern char*		displayname;
extern char*		progname;
extern char*		foreground;
extern char*		background;
extern char*		foldstr;
extern char*		shape;
extern char*		geometry;
extern int		useroot;
extern int		BORDER;
extern int		folds;
extern int		life;
extern int		lightning;
extern Window		lightningWindow;
extern XPoint		pl[];
extern char*		lightningColor;
extern int		update;
extern GC		gc, gcr, lgc;
extern Pixmap		pic;
extern XGCValues	gcv;
extern Argassoc		map[];
extern int		expire, last;
extern XPoint*		oldpoint;

int AddPoint();
void ParseArgs();
void SetBit();
void Usage();

char*  calloc();
char*  getenv();

#define SetBit(x,y) (\
    (bits[y] ? 0 : (bits[y] = (char *) calloc (dla_width / 8 + 1,sizeof (char)))),\
    (bits[y][(x)>>3] |= 1 << (x & 7))\
)

#define ResetBit(x,y) (bits[y][x / 8] &= ~(1 << (x & 7)))

#define GetBit(x,y) (bits[y] ? bits[y][(x) >> 3] & (1 << (x & 7)) : 0)
\End\Of\Shar\
else
  echo "will not over write ./main.h"
fi
if [ `wc -c ./main.h | awk '{printf $1}'` -ne 1442 ]
then
echo `wc -c ./main.h | awk '{print "Got " $1 ", Expected " 1442}'`
fi
if `test ! -s ./patchlevel.h`
then
echo "writing ./patchlevel.h"
cat > ./patchlevel.h << '\End\Of\Shar\'
#define PATCHLEVEL 0
\End\Of\Shar\
else
  echo "will not over write ./patchlevel.h"
fi
if [ `wc -c ./patchlevel.h | awk '{printf $1}'` -ne 21 ]
then
echo `wc -c ./patchlevel.h | awk '{print "Got " $1 ", Expected " 21}'`
fi
if `test ! -s ./usage.c`
then
echo "writing ./usage.c"
cat > ./usage.c << '\End\Of\Shar\'
#include "main.h"

extern char *progname;
void
Usage()
{
    Argassoc*  here;

    fprintf(stderr, "usage: %s", progname);

    for (here = map; here->name; here++) {
       fprintf(stderr, " [-%s", here->name);
       switch (here->type) {
       case String:
       case Integer:
           fprintf(stderr, " %s", here->name);
           break;
       }
       fprintf(stderr, "]");
    }
    fprintf(stderr, "\n");
    exit(1);
}
\End\Of\Shar\
else
  echo "will not over write ./usage.c"
fi
if [ `wc -c ./usage.c | awk '{printf $1}'` -ne 433 ]
then
echo `wc -c ./usage.c | awk '{print "Got " $1 ", Expected " 433}'`
fi
echo "Finished archive 1 of 1"
exit

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.