[alt.sources] Plasma program for X11

cheeks@arsenic..eds.com (Mark Costlow) (05/26/91)

This is a program for generating/displaying plasma cloud images for X11.  
Here's part of the README:

This is plasma.  It implements "plasma clouds" with X11.  It's not very
sophisticated ... I just saw jjensen@dsg4.dse.beckman.com (John Jensen)'s
post in alt.fractals and coded up his ideas (and used some of his code) with
my Xlib skeleton which I use for prototyping graphics ideas.  It does
colormap cycling, and it lets you mess with a few of the parameters.

Enjoy ...

Mark
cheeks@edsr.eds.com    or     ...uunet!edsr!cheeks

#---------------------------------- cut here ----------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Mark Costlow <cheeks@argon> on Sat May 25 20:30:25 1991
#
# This archive contains:
#	README		Makefile	plasma.c	
#
# Existing files will not be overwritten.
# Error checking via wc(1) will be performed.

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

if test -f README
then
	echo Ok to overwrite existing file README\?
	read answer
	case "$answer" in
	[yY]*)	echo Proceeding;;
	*)	echo Aborting; exit 1;;
	esac
	rm -f README
	if test -f README
	then
		echo Error: could not remove README, aborting
		exit 1
	fi
fi
echo x - README
cat >README <<'@EOF'
This is plasma.  It implements "plasma clouds" with X11.  It's not very
sophisticated ... I just saw jjensen@dsg4.dse.beckman.com (John Jensen)'s
post in alt.fractals and coded up his ideas (and used some of his code) with
my Xlib skeleton which I use for prototyping graphics ideas.  It does
colormap cycling, and it lets you mess with a few of the parameters.

It definitely works with sparcs under SunOS 4.1.x and 8 bit displays.  My
skeleton runs on HPs under hpux as well, even with those gnarly 6-bit
displays, but I haven't tested it with this program.

I don't really have time to "maintain" this thing ... I'm just throwing it
out since it's kinda cute.  I'll fix bugs as time permits though, and I
would like to hear about changes or improvements (like getting it to use
the default color map and running on the root window, etc).  It is hereby
placed in the public domain ... give it away, sell it, whatever.  I'd
appreciate it if my name was left on it though.

Mark Costlow
cheeks@edsr.eds.com    or     ...uunet!edsr!cheeks
@EOF
set `wc -lwc <README`
if test $1$2$3 != 191781038
then
	echo ERROR: wc results of README are $* should be 19 178 1038
fi

chmod 664 README

if test -f Makefile
then
	echo Ok to overwrite existing file Makefile\?
	read answer
	case "$answer" in
	[yY]*)	echo Proceeding;;
	*)	echo Aborting; exit 1;;
	esac
	rm -f Makefile
	if test -f Makefile
	then
		echo Error: could not remove Makefile, aborting
		exit 1
	fi
fi
echo x - Makefile
cat >Makefile <<'@EOF'
CC = gcc
CFLAGS = -g

.c.o:
	$(CC) $(CFLAGS) -c $<


PROGS =  plasma
all: $(PROGS)

plasma: plasma.o
	$(CC) $(CFLAGS) -o plasma plasma.o -lX11 -lm

clean:
	rm -f core *.o $(PROGS)
@EOF
set `wc -lwc <Makefile`
if test $1$2$3 != 1531180
then
	echo ERROR: wc results of Makefile are $* should be 15 31 180
fi

chmod 664 Makefile

if test -f plasma.c
then
	echo Ok to overwrite existing file plasma.c\?
	read answer
	case "$answer" in
	[yY]*)	echo Proceeding;;
	*)	echo Aborting; exit 1;;
	esac
	rm -f plasma.c
	if test -f plasma.c
	then
		echo Error: could not remove plasma.c, aborting
		exit 1
	fi
fi
echo x - plasma.c
cat >plasma.c <<'@EOF'
/*
 *  plasma.c  -- generate/display plasma clouds with X11.  It was written in
 *               about an hour ... and the code shows it :-).
 *
 *  Mark Costlow
 *  cheeks@edsr.eds.com
 *  May 25, 1991
 */
#include <stdio.h>
#include <math.h>
#include <ctype.h>

/* X-windows includes */
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>

typedef unsigned char byte;
double drand48();

Display         *display;
char            win_name[64];
Visual          *visual = NULL;
Status          status;
unsigned long   wp, bp;     /* pixel values for black & white */
int screen, nplanes, ncolors;

XEvent         event;
XExposeEvent   *expose;
XSetWindowAttributes attrib;

GC           gc;
XGCValues    gc_val;
XSizeHints   sizehints;
Window       imagewin;        /* window ID for image */
XImage       *image = NULL;
Colormap     *cmaps;

double Futz = 2.0;       /* Global scale of random perturbations. */
int usedist = 1;         /* Use distance to scale perturbations.  */
int sleep_delay = 50000; /* speed of colormap cyling.             */

int global_x, global_y;
byte *buf;
#define BUF(a,b) buf[(global_y * b) + a]
#define NUMCOLORS 255

void MakeColormaps(int nplanes)
{
  XColor colors[256];
  int i, j, mask, tempcolor;
  Colormap cmap;
  double step, f;
      
  fprintf(stderr, "Building color maps . . . ");
  fflush(stderr);
  
  ncolors = 1 << nplanes;
  
  cmaps = (Colormap *) malloc(ncolors * sizeof(Colormap));
  
  step = (double) ( (M_PI_4 * (double)nplanes) / (double) ncolors);
  f = M_PI; /* + M_PI_2; */
#define MAXI 65535

  for (i = 0; i < ncolors; i++) 
    {
      double wank = MAXI / 2.0;
      double sin(double);
      
#define PLAIN

      colors[i].pixel = i;
      colors[i].flags = DoRed | DoGreen | DoBlue;
#ifdef PLAIN
      colors[i].red   = (int)((wank * sin(f)) + wank);
      colors[i].green = (int)((wank * sin(f + M_PI)) + wank);
      colors[i].blue  = (int)((wank * sin(f + M_PI + M_PI_2)) + wank);
#else
      colors[i].red   = (int)((wank * sin(f)) + wank);
      colors[i].green = (int)((wank * sin(f + M_PI_4)) + wank);
      colors[i].blue  = (int)((wank * sin(f + M_PI_2)) + wank);
#endif
      f += step;
    }
  
  for (j = 0; j < ncolors; j++)
    {
      tempcolor = colors[0].pixel;
      for (i = 0; i < ncolors - 1; i++)
	colors[i].pixel = colors[i+1].pixel;
      colors[ncolors-1].pixel = tempcolor;
	  
      cmaps[j] = XCreateColormap(display, RootWindow(display, screen), visual, 
				 AllocAll);
      XStoreColors(display, cmaps[j], colors, ncolors);
    }	  
  fprintf(stderr, "Done\n");
  fflush(stderr);
}

void CycleColormap()
{
  static int current_map = 0;

  XSetWindowColormap(display, imagewin, cmaps[current_map++]);
  current_map = (current_map % ncolors);
  XSync(display, False);
}  
  

void InitX(char *disp, int x, int y)
{
  int i, j;
  
  strcpy(win_name, "Plasma");
  
  /*  
   *  Open the connection to the X11 server and set defaults 
   */
  if ((display = XOpenDisplay(disp)) == NULL) 
    {
      fprintf(stderr,"Could not open display.\n");
      exit(1); 
    }
  screen = XDefaultScreen(display);
  visual = XDefaultVisual(display, screen);
  nplanes = XDisplayPlanes(display, screen);

  bp = BlackPixel(display, screen);
  wp = WhitePixel(display, screen);

  /*
   * Set window manager hints
   */
  sizehints.flags = PPosition | PSize | PMinSize | PMaxSize;
  sizehints.width = sizehints.min_width = x;
  sizehints.max_width = x;
  sizehints.height = sizehints.min_height = y;
  sizehints.max_height = x;
  sizehints.x = 0;
  sizehints.y = 0;
  
  MakeColormaps(nplanes);
  attrib.background_pixel = wp;
  attrib.border_pixel     = bp;
  attrib.event_mask       = (ExposureMask | LeaveWindowMask | 
			     ButtonPressMask | ButtonReleaseMask |
			     ColormapChangeMask | Button1MotionMask |
			     EnterWindowMask);
  attrib.cursor = XCreateFontCursor(display, XC_top_left_arrow);
  
  /*
   * Create the window.
   */
  imagewin = XCreateWindow(display, RootWindow(display, screen), 0, 0,
			   x, y, 5, XDefaultDepth(display, screen),
			   InputOutput, visual, 
			   (CWBackPixel|CWEventMask|CWCursor|CWBorderPixel), 
			   &attrib);

  /* 
   * Install properties for the window.
   */
  XSetStandardProperties(display, imagewin, win_name, win_name, NULL,
			 NULL, NULL, &sizehints);
  XSetIconName(display, imagewin, "Plasma");

  /*
   *  Install the colormap
   */
  XSetWindowColormap(display, imagewin, cmaps[0]);


/* #define DEBUG */
#ifdef DEBUG
  /*
   *  See what's in the colormap
   */
  for (j = 0; j < (1<<nplanes); j++)
    {
      for (i=0; i < (1 << nplanes); i++) 
	{
	  XColor qcolor;
	  
	  qcolor.pixel = (u_long)i;
	  XQueryColor(display, cmaps[j], &qcolor);
	  fprintf(stderr,"color[%2d]: pix %3u r= %5u g= %5u b= %5u\n",i,
		  qcolor.pixel, qcolor.red, qcolor.green, qcolor.blue);
	}
    }
  fprintf(stderr, "\n\n");
#endif
  
  /* 
   * Now finally map the window ... 
   */
  XMapWindow(display, imagewin);

  /* 
   * Create a Graphics Context.
   */
  gc_val.foreground = bp;
  gc_val.background = wp;
  gc = XCreateGC(display, imagewin, GCForeground | GCBackground , &gc_val);
  
  /* 
   * select which input events to honor  
   */
  XSelectInput(display, imagewin, (ExposureMask | KeyPressMask));

  /* 
   * Wait for first expose event
   */
  XNextEvent(display, &event);
  while (event.type != Expose)
    {
      XNextEvent(display, &event);
    } 
}  


show_pic(byte *buf, int x, int y) 
{
  char  ch, tmp[80];
  static int ngen = 0;
  
  image = XCreateImage(display, visual, nplanes, ZPixmap, 0, (char *)buf, 
		       x, y, 8, 0);

  XPutImage(display, imagewin, gc, image, 0, 0, 0, 0, x, y);
  XSync(display, False);
}

/* 
 * Enter infinite loop to sevice events.
 */
void manage_window()
{
  int Finished;
  XKeyEvent *keypress;
  KeySym keysym;
  char keybuf[64];
  XComposeStatus cstat;
  
  Finished = 0;
  expose = (XExposeEvent *)&event;
  keypress = (XKeyEvent *)&event;

  while (!Finished) 
    {
      if (XPending(display))
	{
	  XNextEvent(display, &event);
	  switch ((int)event.type) 
	    {
	    case Expose:
	      if (expose->x % 4 != 0) 
		{
		  expose->x -= (expose->x % 4);
		  expose->width += (expose->x % 4);
		}
	      if (expose->width % 4 != 0) 
		expose->width += 4 - (expose->width % 4);
	      XPutImage(display, imagewin, gc, image, expose->x, expose->y, 
			expose->x, expose->y, expose->width, expose->height);
	      break;
	    case ButtonPress:
	      Finished = 1;
	      break;
	    case KeyPress:
	      XLookupString(keypress, keybuf, sizeof(keybuf), &keysym, &cstat);
	      if(*keybuf == 'q' || *keybuf == 'Q')
		Finished = 1;
	      break;
	    default:
	      break;
	    }
	}
      usleep(sleep_delay);
      CycleColormap();
    }
}

adjust(int xa, int ya, int x, int y, int xb, int yb)
{
  int d;
  double v;

  if (usedist)
    d = abs(xa - xb) + abs(ya - yb);
  else
    d = 1.0;
  
  v = (BUF(xa, ya) + BUF(xb, yb)) / 2 + (drand48() - 0.5) * d * Futz;

  if (v < 1.0)
    v = 1.0;
  else if (v > (double)NUMCOLORS)
    v = (double)NUMCOLORS;
  BUF(x, y) = (int)v;
}

subdivide(int x1, int y1, int x2, int y2)
{
  int x, y, v;

  if ((x2 - x1 > 1) || (y2 - y1 > 1)) 
    {
      x = (x1 + x2) / 2;
      y = (y1 + y2) / 2;

      if (!BUF(x,y1))
	  adjust(x1, y1, x, y1, x2, y1);
      if (!BUF(x2,y))
	  adjust(x2, y1, x2, y, x2, y2);
      if (!BUF(x,y2))
	  adjust(x1, y2, x, y2, x2, y2);
      if (!BUF(x1,y))
	  adjust(x1, y1, x1, y, x1, y2);
      
      if (!BUF(x, y)) 
	{
	  v = (BUF(x1, y1) + BUF(x2, y1) + BUF(x2, y2) + BUF(x1, y2)) / 4;
	  if (v < 1)
	    v = 1;
	  else if (v > NUMCOLORS)
	    v = NUMCOLORS;
	  BUF(x, y) = v;
	}
      
      subdivide(x1, y1, x, y);
      subdivide(x, y1, x2, y);
      subdivide(x, y, x2, y2);
      subdivide(x1, y, x, y2);
      /* show_pic(buf, global_x, global_y); */
    }
}

void Usage(char *prog, char *opt)
{
  fprintf(stderr, "%s:  bad option \"%s\"\n\n", prog, opt);
  fprintf(stderr, "Usage:  %s [options]\n\nWhere options include:\n", prog);
  fprintf(stderr,"    -f <num>\t\t Set global futz factor (default = 2.0)\n");
  fprintf(stderr,"    -dist\t\t Don't make perturbations dependent on dist.\n");
  fprintf(stderr,"    -delay <num>\t Delay in colormap rotation. (default = 1.0).\n");
  fprintf(stderr,"    -display <host:#>\t Specify alternate X server\n");
  fprintf(stderr,"    -s <size>\t\t Use a <size>-square area\n\n");
  exit(1);
}

main (int argc, char **argv)
{
  char *display = NULL;
  int x, y, X, Y, i, j;
  int size = 0, testcolor = 0, matrix_size = 4;
  int xsize = 100, ysize = 100;
  
  /* Initialize the random number generator  */
  srand48(getpid());
  
  i = 0;
  
  for(i = 1; i < argc; i++)
    {
      if (! strncmp("-s", argv[i], 2) && i+1 < argc)
	size = atoi(argv[++i]);
      else if (! strncmp("-x", argv[i], 2) && i+1 < argc)
	xsize = atoi(argv[++i]);
      else if (! strncmp("-y", argv[i], 2) && i+1 < argc)
	ysize = atoi(argv[++i]);
      else if (! strncmp("-f", argv[i], 2) && i+1 < argc)
	Futz = atof(argv[++i]);
      else if (! strncmp("-display", argv[i], 8) && i+1 < argc)
	display = argv[++i];
      else if (! strncmp("-delay", argv[i], 6) && i+1 < argc)
	sleep_delay = (int) ((double)sleep_delay * atof(argv[++i]));
      else if (! strncmp("-dist", argv[i], 5))
	usedist = 0;
      else 
        Usage(argv[0], argv[i]);
    }

  if (size)
    global_x = global_y = x = y = size;
  else
    {
      global_x = x = xsize;
      global_y = y = ysize;
    }
           
    buf    = (byte *)malloc(x * y);
  if (!buf) 
    {
      fprintf(stderr, "%s: malloc failure.\n", argv[0]);
      exit(1);
    }
  
  memset(buf, 0, x * y);

  /*
   *  Prime the 4 corners with random colors.
   */
  BUF(0,0)     = (int)( (drand48() * NUMCOLORS) + 1.0 );
  BUF(x-1,0)   = (int)( (drand48() * NUMCOLORS) + 1.0 );
  BUF(x-1,y-1) = (int)( (drand48() * NUMCOLORS) + 1.0 );
  BUF(0,y-1)   = (int)( (drand48() * NUMCOLORS) + 1.0 );
  fprintf(stderr, "Subdividing ...");  fflush(stderr);
  subdivide(0, 0, x-1, y-1);
  fprintf(stderr, " done\n");  fflush(stderr);
  
  InitX(display, x, y);
  show_pic(buf, x, y);
  manage_window();
  exit(0);
}
@EOF
set `wc -lwc <plasma.c`
if test $1$2$3 != 418135110205
then
	echo ERROR: wc results of plasma.c are $* should be 418 1351 10205
fi

chmod 664 plasma.c

exit 0
-- 
cheeks@edsr.eds.com    or     ...uunet!edsr!cheeks