Edit

IABSD.fr/xenocara/app/xlockmore/modes/life3d.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2006-11-26 11:07:42
    Hash : 110b2a92
    Message : Importing xlockmore 5.22

  • app/xlockmore/modes/life3d.c
  • /* -*- Mode: C; tab-width: 4 -*- */
    /* life3d --- Extension to Conway's game of Life, Carter Bays' S45/B5 3d life */
    
    #if !defined( lint ) && !defined( SABER )
    static const char sccsid[] = "@(#)life3d.c	5.07 2003/01/08 xlockmore";
    
    #endif
    
    /*-
     * Copyright (c) 1994 by David Bagley.
     *
     * Permission to use, copy, modify, and distribute this software and its
     * documentation for any purpose and without fee is hereby granted,
     * provided that the above copyright notice appear in all copies and that
     * both that copyright notice and this permission notice appear in
     * supporting documentation.
     *
     * This file is provided AS IS with no warranties of any kind.  The author
     * shall have no liability with respect to the infringement of copyrights,
     * trade secrets or any patents by this file or any part thereof.  In no
     * event will the author be liable for any lost revenue or profits or
     * other special, indirect and consequential damages.
     *
     * Revision History:
     * 18-Apr-2004: Added shooting gliders for S567B6
     * 13-Mar-2004: Added references
     * 28-Apr-2003: Randomized "rotation" of life form
     * 25-Jan-2003: Spawned a life3d.h
     * 19-Jan-2003: added more gliders from http://www.cse.sc.edu/~bays
     * 08-Jan-2003: Double buffering
     * 01-Nov-2000: Allocation checks
     * 10-May-1997: Compatible with xscreensaver
     * 18-Apr-1997: Memory leak fixed by Tom Schmidt <tschmidt@micron.com>
     * 12-Mar-1995: added LIFE_S567B6 compile-time option
     * 12-Feb-1995: shooting gliders added
     * 07-Dec-1994: used life.c and a DOS version of 3dlife
     * Copyright 1993 Anthony Wesley awesley@canb.auug.org.au found at
     * life.anu.edu.au /pub/complex_systems/alife/3DLIFE.ZIP
     *
     *
     * References:
     * Dewdney, A.K., "The Armchair Universe, Computer Recreations from the
     *   Pages of Scientific American Magazine", W.H. Freedman and Company,
     *   New York, 1988 (February 1987 p 16)
     * Bays, Carter, "The Game of Three Dimensional Life", 86/11/20
     *   with (latest?) update from 87/2/1 http://www.cse.sc.edu/~bays/
     * Meeker, Lee Earl, "Four Dimensional Cellular Automata and the Game
     *   of Life" 1998 http://home.sc.rr.com/lmeeker/Lee/Home.html
     */
    
    #ifdef STANDALONE
    #define MODE_life3d
    #define PROGCLASS "Life3D"
    #define HACK_INIT init_life3d
    #define HACK_DRAW draw_life3d
    #define life3d_opts xlockmore_opts
    #define DEFAULTS "*delay: 1000000 \n" \
     "*count: 35 \n" \
     "*cycles: 85 \n" \
     "*ncolors: 200 \n" \
     "*wireframe: False \n" \
     "*fullrandom: False \n" \
     "*verbose: False \n"
    #include "xlockmore.h"		/* in xscreensaver distribution */
    #else /* STANDALONE */
    #include "xlock.h"		/* in xlockmore distribution */
    
    #endif /* STANDALONE */
    #include "iostuff.h"
    
    #ifdef MODE_life3d
    #define LIFE_NAMES 1
    #include "life3d.h"
    
    #ifdef LIFE_NAMES
    #define DEF_LABEL "True"
    #define FONT_HEIGHT 19
    #define FONT_WIDTH 15
    #endif
    #define DEF_SERIAL "False"
    
    #if 1
    #define DEF_RULE  "G"		/* All rules with gliders */
    #else
    #define DEF_RULE  "P"		/* All rules with known patterns */
    #define DEF_RULE  "S45/B5"	/* "B5/S45" */
    #define DEF_RULE  "S567/B6"	/* "B6/S567" */
    #define DEF_RULE  "S56/B5"	/* "B5/S56" */
    #define DEF_RULE  "S678/B5"	/* "B5/S678" */
    #define DEF_RULE  "S67/B67"	/* "B67/S67" */
    /* There are no known gliders for S67/B67 */
    #endif
    
    static char *rule;
    static char *lifefile;
    #ifdef LIFE_NAMES
    static Bool label;
    #endif
    static Bool serial;
    
    static XrmOptionDescRec opts[] =
    {
    #ifdef LIFE_NAMES
    	{(char *) "-label", (char *) ".life3d.label", XrmoptionNoArg, (caddr_t) "on"},
    	{(char *) "+label", (char *) ".life3d.label", XrmoptionNoArg, (caddr_t) "off"},
    #endif
    	{(char *) "-rule", (char *) ".life3d.rule", XrmoptionSepArg, (caddr_t) NULL},
    	{(char *) "-lifefile", (char *) ".life3d.lifefile", XrmoptionSepArg, (caddr_t) NULL},
    	{(char *) "-serial", (char *) ".life3d.serial", XrmoptionNoArg, (caddr_t) "on"},
    	{(char *) "+serial", (char *) ".life3d.serial", XrmoptionNoArg, (caddr_t) "off"}
    };
    static argtype vars[] =
    {
    #ifdef LIFE_NAMES
    	{(void *) & label, (char *) "label", (char *) "Label", (char *) DEF_LABEL, t_Bool},
    #endif
    	{(void *) & rule, (char *) "rule", (char *) "Rule", (char *) DEF_RULE, t_String},
    	{(void *) & lifefile, (char *) "lifefile", (char *) "LifeFile", (char *) "", t_String},
    	{(void *) & serial, (char *) "serial", (char *) "Serial", (char *) DEF_SERIAL, t_Bool}
    
    };
    static OptionStruct desc[] =
    {
    #ifdef LIFE_NAMES
    	{(char *) "-/+label", (char *) "turn on/off name labeling"},
    #endif
    	{(char *) "-rule string", (char *) "S<survial_neighborhood>/B<birth_neighborhood> parameters"},
    	{(char *) "-lifefile file", (char *) "life file"},
    	{(char *) "-/+serial", (char *) "turn on/off picking of sequential patterns"}
    };
    
    ModeSpecOpt life3d_opts =
    {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
    
    #ifdef USE_MODULES
    ModStruct   life3d_description =
    {"life3d", "init_life3d", "draw_life3d", "release_life3d",
     "refresh_life3d", "change_life3d", (char *) NULL, &life3d_opts,
     1000000, 35, 85, 1, 64, 1.0, "",
     "Shows Bays' game of 3D Life", 0, NULL};
    
    #endif
    
    #define ON 0x40
    #define OFF 0
    
    /* Don't change these numbers without changing the offset() macro below! */
    #define MAXSTACKS 64
    #define	MAXROWS 128
    #define MAXCOLUMNS 128
    #define BASESIZE ((MAXCOLUMNS*MAXROWS*MAXSTACKS)>>6)
    
    #define RT_ANGLE 90
    #define HALFRT_ANGLE 45
    
    /* Store state of cell in top bit. Reserve low bits for count of living nbrs */
    #define Set3D(x,y,z) SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,ON)
    #define Reset3D(x,y,z) SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,OFF)
    
    #define SetList3D(x,y,z) if (!SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,ON)) return False; \
    if (!AddToList(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z)) return False
    
    #define CellState3D(c) ((c)&ON)
    #define CellNbrs3D(c) ((c)&0x1f)	/* 26 <= 31 */
    
    #define EyeToScreen 72.0	/* distance from eye to screen */
    #define HalfScreenD 14.0	/* 1/2 the diameter of screen */
    #define BUCKETSIZE 10
    #define NBUCKETS ((MAXCOLUMNS+MAXROWS+MAXSTACKS)*BUCKETSIZE)
    #define Distance(x1,y1,z1,x2,y2,z2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2))
    
    #define IP (M_PI / 180.0)
    
    #define COLORBASE 3
    #define COLORS (COLORBASE + 2)
    
    #define BLACK 0
    #define RED   1
    #define GREEN 2
    #define BLUE  3
    #define WHITE 4
    
    typedef struct _CellList {
    	unsigned char x, y, z;	/* Location in world coordinates */
    	char        visible;	/* 1 if the cube is to be displayed */
    	short       dist;	/* dist from cell to eye */
    	struct _CellList *next;	/* pointer to next entry on linked list */
    	struct _CellList *prev;
    	struct _CellList *priority;
    } CellList;
    
    typedef struct {
    	Bool        painted;
    	paramstruct param;
    	int         pattern, patterned_rule;
    	int         ox, oy, oz;	/* origin */
    	double      vx, vy, vz;	/* viewpoint */
    	int         generation;
    	int         ncolumns, nrows, nstacks;
    	int         memstart;
    	char        visible;
    	int         noChangeCount;
    	unsigned char *base[BASESIZE];
    	double      A, B, C, F;
    	int         width, height;
    	unsigned long colors[COLORS];
    	double      azm;
    	double      metaAlt, metaAzm, metaDist;
    	CellList   *ptrhead, *ptrend, eraserhead, eraserend;
    	CellList   *buckethead[NBUCKETS], *bucketend[NBUCKETS];
    	Bool        wireframe;
    	Pixmap      dbuf;
    	int         labelOffsetX, labelOffsetY;
    	char        ruleString[80], nameString[80];
    } life3dstruct;
    
    static life3dstruct *life3ds = (life3dstruct *) NULL;
    
    static paramstruct input_param;
    static Bool allPatterns = False, allGliders = False;
    static char *filePattern = (char *) NULL;
    
    static int
    codeToPatternedRule(paramstruct param)
    {
    	unsigned int i;
    
    	for (i = 0; i < LIFE_RULES; i++)
    		if (param_rules[i].survival == param.survival &&
    		    param_rules[i].birth == param.birth)
    			return i;
    	return LIFE_RULES;
    }
    
    static void
    copyFromPatternedRule(paramstruct * param, int patterned_rule)
    {
    	param->survival = param_rules[patterned_rule].survival;
    	param->birth = param_rules[patterned_rule].birth;
    }
    
    static void
    printRule(char * string, paramstruct param, Bool verbose)
    {
    	int         i = 1, l;
    
    	string[0] = 'S';
    	if (verbose)
    		(void) fprintf(stdout, "rule (Survival/Birth neighborhood): ");
    	for (l = 0; l <= MAXNEIGHBORS && l < 10; l++) {
    
    		if (param.survival & (1 << l)) {
    			(void) sprintf(&(string[i]), "%d", l);
    			i++;
    		}
    	}
    	(void) sprintf(&(string[i]), "/B");
    	i += 2;
    	for (l = 0; l <= MAXNEIGHBORS && l < 10; l++) {
    		if (param.birth & (1 << l)) {
    			(void) sprintf(&(string[i]), "%d", l);
    			i++;
    		}
    	}
    	string[i] = '\0';
    	if (verbose)
    	  (void) fprintf(stdout, "%s\nbinary rule: Survival 0x%X, Birth 0x%X\n",
    		       string, param.survival, param.birth);
    }
    
    /*-
     * This stuff is not good for rules above 9 cubes but it is unlikely that
     * these modes would be much good anyway....  death assumed.
     */
    static void
    parseRule(ModeInfo * mi, char * string)
    {
    	int         n, l;
    	char        serving = 0;
    	static Bool found = False;
    
    	if (found)
    		return;
    	input_param.survival = input_param.birth = 0;
    	if (rule) {
    		n = 0;
    		while (rule[n]) {
    			if (rule[n] == 'P' || rule[n] == 'p') {
    				allPatterns = True;
    				found = True;
    				if (MI_IS_VERBOSE(mi))
    					(void) fprintf(stdout, "rule: All rules with known patterns\n");
    				return;
    			} else if (rule[n] == 'G' || rule[n] == 'g') {
    				allGliders = True;
    				found = True;
    				if (MI_IS_VERBOSE(mi))
    					(void) fprintf(stdout, "rule: All rules with known gliders\n");
    				return;
    			} else if (rule[n] == 'S' || rule[n] == 'E' || rule[n] == 'L' || rule[n] == 's' || rule[n] == 'e' || rule[n] == 'l') {
    				serving = 'S';
    			} else if (rule[n] == 'B' || rule[n] == 'b') {
    				serving = 'B';
    			} else {
    				l = rule[n] - '0';
    				if (l >= 0 && l <= 9 /*&& l <= MAXNEIGHBORS */ ) {	/* no 10..27 */
    					if (serving == 'S' || rule[n] == 's') {
    						found = True;
    						input_param.survival |= (1 << l);
    					} else if (serving == 'B' || rule[n] == 'b') {
    						found = True;
    						input_param.birth |= (1 << l);
    					}
    				}
    			}
    			n++;
    		}
    	}
    	if (!found || !(input_param.survival || input_param.birth)) {
    		/* Default to Bays' rules if rule does not make sense */
    		allGliders = True;
    		found = True;
    		if (MI_IS_VERBOSE(mi))
    			(void) fprintf(stdout,
    				"rule: Defaulting to all rules with known gliders\n");
    		return;
    	}
    	printRule(string, input_param, MI_IS_VERBOSE(mi));
    }
    
    static void
    parseFile(ModeInfo *mi)
    {
    	FILE       *file;
    	static Bool done = False;
    	int         firstx, firsty, x = 0, y = 0, z = 0, i = 0;
    	int         c, cprev = ' ', size;
    	char        line[256];
    
    	if (done)
    		return;
    	done = True;
    	if (MI_IS_FULLRANDOM(mi) || !lifefile || !*lifefile)
    		return;
    	if ((file = my_fopenSize(lifefile, "r", &size)) == NULL) {
    		(void) fprintf(stderr, "could not read file \"%s\"\n", lifefile);
    		return;
    	}
    	for (;;) {
    		if (!fgets(line, 256, file)) {
    			(void) fprintf(stderr, "could not read header of file \"%s\"\n",
    				       lifefile);
    			(void) fclose(file);
    			return;
    		}
    		if (strncmp(line, "#P", (size_t) 2) == 0 &&
    		    sscanf(line, "#P %d %d %d", &x, &y, &z) == 3)
    			break;
    	}
    	c = getc(file);
    	while (c != EOF && !(c == '0' || c == 'O' || c == '*' || c == '.')) {
    		c = getc(file);
    	}
    	if (c == EOF || x <= -127 || y <= -127 || z <= -127 ||
    	    x >= 127 || y >= 127 || z >= 127) {
    		(void) fprintf(stderr, "corrupt file \"%s\" or file to large\n",
    			       lifefile);
    		(void) fclose(file);
    		return;
    	}
    	firstx = x;
    	firsty = y;
    	if ((filePattern = (char *) malloc((3 * size) *
    			 sizeof (char))) == NULL) {
    		(void) fprintf(stderr, "not enough memory\n");
    		(void) fclose(file);
    	}
    
    	while (c != EOF && x < 127 && y < 127 && z < 127 && i < 3 * size) {
    		if (c == '0' || c == 'O' || c == '*') {
    			filePattern[i++] = x++;
    			filePattern[i++] = y;
    			filePattern[i++] = z;
    		} else if (c == '.') {
    			x++;
    		} else if (c == '\n') {
    			if (cprev == '\n') {
    				z++;
    				y = firsty;
    			} else {
    				x = firstx;
    				y++;
    			}
    		}
    		cprev = c;
    		c = getc(file);
    	}
    	(void) fclose(file);
    	filePattern[i] = 127;
    }
    
    /*--- list ---*/
    /* initialise the state of all cells to OFF */
    static void
    Init3D(life3dstruct * lp)
    {
    	lp->ptrhead = lp->ptrend = (CellList *) NULL;
    	lp->eraserhead.next = &lp->eraserend;
    	lp->eraserend.prev = &lp->eraserhead;
    }
    
    /*-
     * Function that adds the cell (assumed live) at (x,y,z) onto the search
     * list so that it is scanned in future generations
     */
    static Bool
    AddToList(life3dstruct * lp, unsigned int x, unsigned int y, unsigned int z)
    {
    	CellList   *tmp;
    
    	if ((tmp = (CellList *) malloc(sizeof (CellList))) == NULL)
    		return False;
    	tmp->x = x;
    	tmp->y = y;
    	tmp->z = z;
    	if (lp->ptrhead == NULL) {
    		lp->ptrhead = lp->ptrend = tmp;
    		tmp->prev = (struct _CellList *) NULL;
    	} else {
    		lp->ptrend->next = tmp;
    		tmp->prev = lp->ptrend;
    		lp->ptrend = tmp;
    	}
    	lp->ptrend->next = (struct _CellList *) NULL;
    	return True;
    }
    
    static void
    AddToEraseList(life3dstruct * lp, CellList * cell)
    {
    	cell->next = &lp->eraserend;
    	cell->prev = lp->eraserend.prev;
    	lp->eraserend.prev->next = cell;
    	lp->eraserend.prev = cell;
    }
    
    static void
    DelFromList(life3dstruct * lp, CellList * cell)
    {
    	if (cell != lp->ptrhead) {
    		cell->prev->next = cell->next;
    	} else {
    		lp->ptrhead = cell->next;
    		if (lp->ptrhead != NULL)
    			lp->ptrhead->prev = (struct _CellList *) NULL;
    	}
    
    	if (cell != lp->ptrend) {
    		cell->next->prev = cell->prev;
    	} else {
    		lp->ptrend = cell->prev;
    		if (lp->ptrend != NULL)
    			lp->ptrend->next = (struct _CellList *) NULL;
    	}
    
    	AddToEraseList(lp, cell);
    }
    
    static void
    DelFromEraseList(CellList * cell)
    {
    	cell->next->prev = cell->prev;
    	cell->prev->next = cell->next;
    	free(cell);
    }
    
    /*--- memory ---*/
    /*-
     * Simulate a large array by dynamically allocating 4x4x4 size cells when
     * needed.
     */
    static void
    MemInit(life3dstruct * lp)
    {
    	int         i;
    
    	for (i = 0; i < BASESIZE; ++i) {
    		if (lp->base[i] != NULL) {
    			free(lp->base[i]);
    			lp->base[i] = (unsigned char *) NULL;
    		}
    	}
    	lp->memstart = 0;
    }
    
    #define BASE_OFFSET(x,y,z,b,o) \
    b = ((x & 0x7c) << 7) + ((y & 0x7c) << 2) + ((z & 0x7c) >> 2); \
    o = (x & 3) + ((y & 3) << 2) + ((z & 3) << 4); \
    if (lp->base[b] == NULL) {\
    if ((lp->base[b] = (unsigned char *) calloc(64, sizeof (unsigned char))) == NULL) {return False;}}
    
    
    static Bool
    GetMem(life3dstruct * lp, unsigned int x, unsigned int y, unsigned int z,
    		int *m)
    {
    	int         b, o;
    
    	if (lp->memstart) {
    		MemInit(lp);
    	}
    	BASE_OFFSET(x, y, z, b, o);
    	*m = lp->base[b][o];
    	return True;
    }
    
    static Bool
    SetMem(life3dstruct * lp,
           unsigned int x, unsigned int y, unsigned int z, unsigned int val)
    {
    	int         b, o;
    
    	if (lp->memstart) {
    		MemInit(lp);
    	}
    	BASE_OFFSET(x, y, z, b, o);
    	lp->base[b][o] = val;
    	return True;
    }
    
    static Bool
    ChangeMem(life3dstruct * lp,
    		unsigned int x, unsigned int y, unsigned int z,
    		unsigned int val)
    {
    	int         b, o;
    
    	if (lp->memstart) {
    		MemInit(lp);
    	}
    	BASE_OFFSET(x, y, z, b, o);
    	lp->base[b][o] += val;
    	return True;
    }
    
    static void
    ClearMem(life3dstruct * lp)
    {
    	int         i, j, count;
    
    	for (i = 0; i < BASESIZE; ++i)
    		if (lp->base[i] != NULL) {
    			for (count = j = 0; j < 64 && count == 0; ++j)
    				if (CellState3D(lp->base[i][j]))
    					++count;
    			if (!count) {
    				free(lp->base[i]);
    				lp->base[i] = (unsigned char *) NULL;
    			}
    		}
    }
    
    
    /*-
     * This routine increments the values stored in the 27 cells centred on
     * (x,y,z) Note that the offset() macro implements wrapping - the world is a
     * 4d torus
     */
    static Bool
    IncrementNbrs3D(life3dstruct * lp, CellList * cell)
    {
    	int         xc, yc, zc, x, y, z;
    
    	xc = cell->x;
    	yc = cell->y;
    	zc = cell->z;
    	for (z = zc - 1; z != zc + 2; ++z)
    		for (y = yc - 1; y != yc + 2; ++y)
    			for (x = xc - 1; x != xc + 2; ++x)
    				if (x != xc || y != yc || z != zc)
    					if (!ChangeMem(lp,
    						  (unsigned int) x, (unsigned int) y, (unsigned int) z, 1))
    						return False;
    	return True;
    }
    
    static void
    End3D(life3dstruct * lp)
    {
    	CellList   *ptr;
    
    	while (lp->ptrhead != NULL) {
    		/* Reset3D(lp->ptrhead->x, lp->ptrhead->y, lp->ptrhead->z); */
    		DelFromList(lp, lp->ptrhead);
    	}
    	ptr = lp->eraserhead.next;
    	while (ptr != &lp->eraserend) {
    		DelFromEraseList(ptr);
    		ptr = lp->eraserhead.next;
    	}
    	MemInit(lp);
    }
    
    static Bool
    RunLife3D(life3dstruct * lp)
    {
    	unsigned int x, y, z, xc, yc, zc;
    	int         c;
    	CellList   *ptr, *ptrnextcell;
    	Bool visible = False;
    
    	/* Step 1 - Add 1 to all neighbours of living cells. */
    	ptr = lp->ptrhead;
    	while (ptr != NULL) {
    		if (!IncrementNbrs3D(lp, ptr))
    			return False;
    		ptr = ptr->next;
    	}
    
    	/* Step 2 - Scan world and implement Survival rules. We have a list of live
    	 * cells, so do the following:
    	 * Start at the END of the list and work backwards (so we don't have to worry
    	 * about scanning newly created cells since they are appended to the end) and
    	 * for every entry, scan its neighbours for new live cells. If found, add them
    	 * to the end of the list. If the centre cell is dead, unlink it.
    	 * Make sure we do not append multiple copies of cells.
    	 */
    	ptr = lp->ptrend;
    	while (ptr != NULL) {
    		ptrnextcell = ptr->prev;
    		xc = ptr->x;
    		yc = ptr->y;
    		zc = ptr->z;
    		for (z = zc - 1; z != zc + 2; ++z)
    			for (y = yc - 1; y != yc + 2; ++y)
    				for (x = xc - 1; x != xc + 2; ++x)
    					if (x != xc || y != yc || z != zc) {
    						if (!GetMem(lp, x, y, z, &c))
    							return False;
    						if (c) {
    							if (CellState3D(c) == OFF) {
    								if (lp->param.birth & (1 << CellNbrs3D(c))) {
    									visible = True;
    									SetList3D(x, y, z);
    								} else {
    									if (!Reset3D(x, y, z))
    										return False;
    
    								}
    							}
    						}
    					}
    		if (!GetMem(lp, xc, yc, zc, &c))
    			return False;
    		if (lp->param.survival & (1 << CellNbrs3D(c))) {
    			if (!Set3D(xc, yc, zc))
    				return False;
    		} else {
    			if (!Reset3D(ptr->x, ptr->y, ptr->z))
    				return False;
    			DelFromList(lp, ptr);
    		}
    		ptr = ptrnextcell;
    	}
    	ClearMem(lp);
    	if (visible)
    		lp->noChangeCount = 0;
    	else
    		lp->noChangeCount++;
    	return True;
    }
    
    #if 0
    static int
    CountCells3D(life3dstruct * lp)
    {
    	CellList   *ptr;
    	int         count = 0;
    
    	ptr = lp->ptrhead;
    	while (ptr != NULL) {
    		++count;
    		ptr = ptr->next;
    	}
    	return count;
    }
    
    void
    DisplayList(life3dstruct * lp)
    {
    	CellList   *ptr;
    	int         count = 0;
    
    	ptr = lp->ptrhead;
    	while (ptr != NULL) {
    		(void) printf("(%x)=[%d,%d,%d] ", (int) ptr,
    			ptr->x, ptr->y, ptr->z);
    		ptr = ptr->next;
    		++count;
    	}
    	(void) printf("Living cells = %d\n", count);
    }
    
    #endif
    
    static Bool
    RandomSoup(ModeInfo * mi, int n, int v)
    {
    	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
    	int         x, y, z;
    
    	v /= 2;
    	if (v < 1)
    		v = 1;
    	for (z = lp->nstacks / 2 - v; z < lp->nstacks / 2 + v; ++z)
    		for (y = lp->nrows / 2 - v; y < lp->nrows / 2 + v; ++y)
    			for (x = lp->ncolumns / 2 - v; x < lp->ncolumns / 2 + v; ++x)
    				if (NRAND(100) < n) {
    					SetList3D(x, y, z);
    				}
    	(void) strcpy(lp->nameString, "random pattern");
    	if (MI_IS_VERBOSE(mi)) {
    		(void) fprintf(stdout, "%s\n", lp->nameString);
    	}
    	return True;
    }
    
    static Bool
    GetPattern(ModeInfo * mi, int pattern_rule, int pattern)
    {
    	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
    	int         x, y, z, orient, temp;
    	char       *patptr = (char *) NULL;
    #ifdef LIFE_NAMES
    	int pat = 2 * pattern + 1;
    	char * patstrg = (char *) "";
    #else
    	int pat = pattern;
    #endif
    
    	if (filePattern) {
    		patptr = &filePattern[0];
    	} else {
    		switch (pattern_rule) {
    			case LIFE_S45B5:
    				patptr = &patterns_S45B5[pat][0];
    #ifdef LIFE_NAMES
    				patstrg = &patterns_S45B5[2 * pattern][0];
    #endif
    				break;
    			case LIFE_S567B6:
    				patptr = &patterns_S567B6[pat][0];
    #ifdef LIFE_NAMES
    				patstrg = &patterns_S567B6[2 * pattern][0];
    #endif
    				break;
    			case LIFE_S56B5:
    				patptr = &patterns_S56B5[pat][0];
    #ifdef LIFE_NAMES
    				patstrg = &patterns_S56B5[2 * pattern][0];
    #endif
    				break;
    			case LIFE_S678B5:
    				patptr = &patterns_S678B5[pat][0];
    #ifdef LIFE_NAMES
    				patstrg = &patterns_S678B5[2 * pattern][0];
    #endif
    				break;
    			case LIFE_S67B67:
    				patptr = &patterns_S67B67[pat][0];
    #ifdef LIFE_NAMES
    				patstrg = &patterns_S67B67[2 * pattern][0];
    #endif
    				break;
    		}
    #ifdef LIFE_NAMES
    		(void) strcpy(lp->nameString, patstrg);
    #endif
    	}
    #ifdef DEBUG
    	orient = 0;
    #else
    	orient = NRAND(24);
    #endif
    	if (MI_IS_VERBOSE(mi) && !filePattern) {
    #ifdef LIFE_NAMES
    		(void) fprintf(stdout, "%s, ", patstrg);
    #endif
    		(void) fprintf(stdout, "table number %d\n", pattern);
    	}
    	while ((x = *patptr++) != 127) {
    		y = *patptr++;
    		z = *patptr++;
    		if (orient >= 16) {
    			temp = x;
    			x = y;
    			y = z;
    			z = temp;
    		} else if (orient >= 8) {
    			temp = x;
    			x = z;
    			z = y;
    			y = temp;
    		}
    		if (orient % 8 >= 4) {
    			x = -x;
    		}
    		if (orient % 4 >= 2) {
    			y = -y;
    		}
    		if (orient % 2) {
    			z = -z;
    		}
    
    		x += lp->ncolumns / 2;
    		y += lp->nrows / 2;
    		z += lp->nstacks / 2;
    		if (x >= 0 && y >= 0 && z >= 0 &&
    		    x < lp->ncolumns && y < lp->nrows && z < lp->nstacks) {
    			SetList3D(x, y, z);
    		}
    	}
    	return True;
    }
    
    static void
    NewViewpoint(life3dstruct * lp, double x, double y, double z)
    {
    	double      k, l, d1, d2;
    
    	k = x * x + y * y;
    	l = sqrt(k + z * z);
    	k = sqrt(k);
    	d1 = (EyeToScreen / HalfScreenD);
    	d2 = EyeToScreen / (HalfScreenD * lp->height / lp->width);
    	lp->A = d1 * l * (lp->width / 2) / k;
    	lp->B = l * l;
    	lp->C = d2 * (lp->height / 2) / k;
    	lp->F = k * k;
    }
    
    static void
    lissajous(life3dstruct * lp)
    {
    	double alt, azm, dist;
    
    	alt = 30.0 * sin(lp->metaAlt * IP) + 45.0;
    	lp->metaAlt += 1.123;
    	if (lp->metaAlt >= 360.0)
    		lp->metaAlt -= 360.0;
    	if (lp->metaAlt < 0.0)
    		lp->metaAlt += 360.0;
    	azm = 30.0 * sin(lp->metaAzm * IP) + 45.0;
    	lp->metaAzm += 0.987;
    	if (lp->metaAzm >= 360.0)
    		lp->metaAzm -= 360.0;
    	if (lp->metaAzm < 0.0)
    		lp->metaAzm += 360.0;
    	dist = 10.0 * sin(lp->metaDist * IP) + 50.0;
    	lp->metaDist += 1.0;
    	if (lp->metaDist >= 360.0)
    		lp->metaDist -= 360.0;
    	if (lp->metaDist < 0.0)
    		lp->metaDist += 360.0;
    #if 0
    	if (alt >= 90.0)
    		alt = 90.0;
    	else if (alt < -90.0)
    		alt = -90.0;
    #endif
    	lp->azm = azm;
    #ifdef DEBUG
    	(void) printf("dist %g, alt %g, azm %g\n", dist, alt, azm);
    #endif
    	lp->vx = (sin(azm * IP) * cos(alt * IP) * dist);
    	lp->vy = (cos(azm * IP) * cos(alt * IP) * dist);
    	lp->vz = (sin(alt * IP) * dist);
    	NewViewpoint(lp, lp->vx, lp->vy, lp->vz);
    }
    
    static void
    NewPoint(life3dstruct * lp, double x, double y, double z,
    	 register XPoint * cubepts)
    {
    	double      p1, E;
    
    	p1 = x * lp->vx + y * lp->vy;
    	E = lp->B - p1 - z * lp->vz;
    	cubepts->x = (int) (lp->width / 2 -
    		lp->A * (lp->vx * y - lp->vy * x) / E);
    	cubepts->y = (int) (lp->height / 2 -
    		lp->C * (z * lp->F - lp->vz * p1) / E);
    }
    
    
    /* Chain together all cells that are at the same distance.
     * These cannot mutually overlap. */
    static void
    SortList(life3dstruct * lp)
    {
    	short       dist;
    	double      d, x, y, z, rsize;
    	int         i, r;
    	XPoint      point;
    	CellList   *ptr;
    
    	for (i = 0; i < NBUCKETS; ++i)
    		lp->buckethead[i] = lp->bucketend[i] = (CellList *) NULL;
    
    	/* Calculate distances and re-arrange pointers to chain off buckets */
    	ptr = lp->ptrhead;
    	while (ptr != NULL) {
    
    		x = (double) ptr->x - lp->ox;
    		y = (double) ptr->y - lp->oy;
    		z = (double) ptr->z - lp->oz;
    		d = Distance(lp->vx, lp->vy, lp->vz, x, y, z);
    		if (lp->vx * (lp->vx - x) + lp->vy * (lp->vy - y) +
    		    lp->vz * (lp->vz - z) > 0 && d > 1.5)
    			ptr->visible = 1;
    		else
    			ptr->visible = 0;
    
    		ptr->dist = (short) d;
    		dist = (short) (d * BUCKETSIZE);
    		if (dist > NBUCKETS - 1)
    			dist = NBUCKETS - 1;
    
    		if (lp->buckethead[dist] == NULL) {
    			lp->buckethead[dist] = lp->bucketend[dist] = ptr;
    			ptr->priority = (struct _CellList *) NULL;
    		} else {
    			lp->bucketend[dist]->priority = ptr;
    			lp->bucketend[dist] = ptr;
    			lp->bucketend[dist]->priority =
    				(struct _CellList *) NULL;
    		}
    		ptr = ptr->next;
    	}
    
    	/* Check for invisibility */
    	rsize = 0.47 * lp->width / ((double) HalfScreenD * 2);
    	i = (int) lp->azm;
    	if (i < 0)
    		i = -i;
    	i = i % RT_ANGLE;
    	if (i > HALFRT_ANGLE)
    		i = RT_ANGLE - i;
    	rsize /= cos(i * IP);
    
    	lp->visible = 0;
    	for (i = 0; i < NBUCKETS; ++i)
    		if (lp->buckethead[i] != NULL) {
    			ptr = lp->buckethead[i];
    			while (ptr != NULL) {
    				if (ptr->visible) {
    					x = (double) ptr->x - lp->ox;
    					y = (double) ptr->y - lp->oy;
    					z = (double) ptr->z - lp->oz;
    					NewPoint(lp, x, y, z, &point);
    					r = (int) (rsize * (double) EyeToScreen / (double) ptr->dist);
    					if (point.x + r >= 0 && point.y + r >= 0 &&
    					    point.x - r < lp->width && point.y - r < lp->height)
    						lp->visible = 1;
    				}
    				ptr = ptr->priority;
    			}
    		}
    }
    
    static void
    DrawFace(ModeInfo * mi, int color, XPoint * cubepts,
    		int p1, int p2, int p3, int p4)
    {
    	Display    *display = MI_DISPLAY(mi);
    	GC          gc = MI_GC(mi);
    	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
    	XPoint      facepts[5];
    
    	facepts[0] = cubepts[p1];
    	facepts[1] = cubepts[p2];
    	facepts[2] = cubepts[p3];
    	facepts[3] = cubepts[p4];
    	facepts[4] = cubepts[p1];
    
    	XSetForeground(display, gc, lp->colors[color]);
    	if (!lp->wireframe) {
    		XFillPolygon(display, (Drawable) lp->dbuf, gc, facepts, 4,
    			Convex, CoordModeOrigin);
    		if (color == BLACK || MI_NPIXELS(mi) <= 2) {
    			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
    		} else {
    			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
    		}
    	}
    	XDrawLines(display, (Drawable) lp->dbuf, gc, facepts, 5, CoordModeOrigin);
    }
    
    #define LEN 0.45
    #define LEN2 0.9
    
    static int
    DrawCube(ModeInfo * mi, CellList * cell)
    {
    	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
    	XPoint      cubepts[8];	/* screen coords for point */
    	int         i = 0, out;
    	unsigned int mask;
    	double      x, y, z;
    	double      dx, dy, dz;
    
    	x = (double) cell->x - lp->ox;
    	y = (double) cell->y - lp->oy;
    	z = (double) cell->z - lp->oz;
    	out = 0;
    	for (dz = z - LEN; dz <= z + LEN2; dz += LEN2)
    		for (dy = y - LEN; dy <= y + LEN2; dy += LEN2)
    			for (dx = x - LEN; dx <= x + LEN2; dx += LEN2) {
    				NewPoint(lp, dx, dy, dz, &cubepts[i]);
    				if (cubepts[i].x < 0 ||
    				    cubepts[i].x >= lp->width ||
    				    cubepts[i].y < 0 ||
    				    cubepts[i].y >= lp->height)
    					++out;
    				++i;
    			}
    	if (out == 8)
    		return (0);
    
    	if (cell->visible)
    		mask = 0xFFFF;
    	else
    		mask = 0x0;
    
    	/* Only draw those faces that are visible */
    	dx = lp->vx - x;
    	dy = lp->vy - y;
    	dz = lp->vz - z;
    	if (lp->wireframe) {
    		if (dz <= LEN)
    			DrawFace(mi, (int) (BLUE & mask), cubepts, 4, 5, 7, 6);
    		else if (dz >= -LEN)
    			DrawFace(mi, (int) (BLUE & mask), cubepts, 0, 1, 3, 2);
    		if (dx <= LEN)
    			DrawFace(mi, (int) (GREEN & mask), cubepts, 1, 3, 7, 5);
    		else if (dx >= -LEN)
    			DrawFace(mi, (int) (GREEN & mask), cubepts, 0, 2, 6, 4);
    		if (dy <= LEN)
    			DrawFace(mi, (int) (RED & mask), cubepts, 2, 3, 7, 6);
    		else if (dy >= -LEN)
    			DrawFace(mi, (int) (RED & mask), cubepts, 0, 1, 5, 4);
    	}
    	if (dz > LEN)
    		DrawFace(mi, (int) (BLUE & mask), cubepts, 4, 5, 7, 6);
    	else if (dz < -LEN)
    		DrawFace(mi, (int) (BLUE & mask), cubepts, 0, 1, 3, 2);
    	if (dx > LEN)
    		DrawFace(mi, (int) (GREEN & mask), cubepts, 1, 3, 7, 5);
    	else if (dx < -LEN)
    		DrawFace(mi, (int) (GREEN & mask), cubepts, 0, 2, 6, 4);
    	if (dy > LEN)
    		DrawFace(mi, (int) (RED & mask), cubepts, 2, 3, 7, 6);
    	else if (dy < -LEN)
    		DrawFace(mi, (int) (RED & mask), cubepts, 0, 1, 5, 4);
    	return (1);
    }
    
    static void
    DrawScreen(ModeInfo * mi)
    {
    	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
    	Display    *display = MI_DISPLAY(mi);
    	GC          gc = MI_GC(mi);
    	CellList   *ptr;
    	CellList   *eraserptr;
    	int         i;
    
    	SortList(lp);
    
    	XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
    	XFillRectangle(display, (Drawable) lp->dbuf, gc, 0, 0,
    		lp->width, lp->height);
    
    	/* Erase dead cubes */
    	eraserptr = lp->eraserhead.next;
    	while (eraserptr != &lp->eraserend) {
    		eraserptr->visible = 0;
    		(void) DrawCube(mi, eraserptr);
    		DelFromEraseList(eraserptr);
    		eraserptr = lp->eraserhead.next;
    	}
    
    	/* draw furthest cubes first */
    	for (i = NBUCKETS - 1; i >= 0; --i) {
    		ptr = lp->buckethead[i];
    		while (ptr != NULL) {
    			/*if (ptr->visible) */
    			/* v += */ (void) DrawCube(mi, ptr);
    			ptr = ptr->priority;
    			/* ++count; */
    		}
    	}
    	if (label) {
    		int size = MAX(MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) - 1, 1);
    		if (size >= 10 * FONT_WIDTH) {
    			/* hard code these to corners */
    			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
    			XDrawString(display, (Drawable) lp->dbuf, gc,
    				16 + lp->labelOffsetX,
    				16 + lp->labelOffsetY + FONT_HEIGHT,
    				lp->ruleString, strlen(lp->ruleString));
    			XDrawString(display, (Drawable) lp->dbuf, gc,
    				16 + lp->labelOffsetX,
    				MI_HEIGHT(mi) - 16 -
    			       	lp->labelOffsetY - FONT_HEIGHT / 2,
    				lp->nameString, strlen(lp->nameString));
    		}
    	}
    	XFlush(display);
    	XCopyArea(display, (Drawable) lp->dbuf, MI_WINDOW(mi), gc,
    		0, 0, lp->width, lp->height, 0, 0);
    
    #if 0
    	{
    		int         count = 0, v = 0;
    
    
    		(void) printf("Pop=%-4d  Viewpoint (%3d,%3d,%3d)  Origin (%3d,%3d,%3d)  Mode %dx%d\
    (%d,%d) %d\n",
    		     count, (int) (lp->vx + lp->ox), (int) (lp->vy + lp->oy),
    			      (int) (lp->vz + lp->oz), (int) lp->ox, (int) lp->oy, (int) lp->oz,
    			      lp->width, lp->height, lp->alt, lp->azm, v);
    	}
    #endif
    }
    
    static Bool
    shooter(life3dstruct * lp)
    {
    	int         hsp, vsp, asp, hoff = 1, voff = 1, aoff = 1, r, c2,
    	            r2, s2;
    
    	/* Generate the glider at the edge of the screen */
    #define V 10
    #define V2 (V/2)
    	c2 = lp->ncolumns / 2;
    	r2 = lp->nrows / 2;
    	s2 = lp->nstacks / 2;
    	r = NRAND(3);
    	if (!r) {
    		hsp = NRAND(V2) + c2 - V2 / 2;
    		vsp = (LRAND() & 1) ? r2 - V : r2 + V;
    		asp = (LRAND() & 1) ? s2 - V : s2 + V;
    		if (asp > s2)
    			aoff = -1;
    		if (vsp > r2)
    			voff = -1;
    		if (hsp > c2)
    			hoff = -1;
    		if (lp->patterned_rule == LIFE_S45B5) {
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    		} else if (lp->patterned_rule == LIFE_S567B6) {
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 2 * aoff);
    		} else if (lp->patterned_rule == LIFE_S56B5) {
    			SetList3D(hsp + 2 * hoff, vsp + 4 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 4 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 4 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 4 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 3 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 4 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 3 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 4 * aoff);
    		} else if (lp->patterned_rule == LIFE_S678B5) {
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    		}
    	} else if (r == 1) {
    		hsp = (LRAND() & 1) ? c2 - V : c2 + V;
    		vsp = (LRAND() & 1) ? r2 - V : r2 + V;
    		asp = NRAND(V2) + s2 - V2 / 2;
    		if (asp > s2)
    			aoff = -1;
    		if (vsp > r2)
    			voff = -1;
    		if (hsp > c2)
    			hoff = -1;
    		if (lp->patterned_rule == LIFE_S45B5) {
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    		} else if (lp->patterned_rule == LIFE_S567B6) {
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    		} else if (lp->patterned_rule == LIFE_S56B5) {
    			SetList3D(hsp + 4 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 3 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 3 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 4 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 4 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 4 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 4 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 4 * voff, asp + 0 * aoff);
    		} else if (lp->patterned_rule == LIFE_S678B5) {
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    		}
    	} else {
    		hsp = (LRAND() & 1) ? c2 - V : c2 + V;
    		vsp = NRAND(V2) + r2 - V2 / 2;
    		asp = (LRAND() & 1) ? s2 - V : s2 + V;
    		if (asp > s2)
    			aoff = -1;
    		if (vsp > r2)
    			voff = -1;
    		if (hsp > c2)
    			hoff = -1;
    		if (lp->patterned_rule == LIFE_S45B5) {
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 3 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 3 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    		} else if (lp->patterned_rule == LIFE_S567B6) {
    			SetList3D(hsp + 0 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 2 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 2 * aoff);
    		} else if (lp->patterned_rule == LIFE_S56B5) {
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 4 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 0 * voff, asp + 3 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 3 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 4 * hoff, vsp + 0 * voff, asp + 1 * aoff);
    		} else if (lp->patterned_rule == LIFE_S678B5) {
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 0 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 2 * hoff, vsp + 3 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 1 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 1 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 1 * voff, asp + 0 * aoff);
    			SetList3D(hsp + 0 * hoff, vsp + 2 * voff, asp + 0 * aoff);
    		}
    	}
    	return True;
    }
    
    static void
    free_life3d(Display *display, life3dstruct *lp)
    {
    	if (lp->eraserhead.next != NULL)
    		End3D(lp);
            if (lp->dbuf != None) {
    		XFreePixmap(display, lp->dbuf);
    		lp->dbuf = None;
    	}
    }
    
    void
    init_life3d(ModeInfo * mi)
    {
    	Display *display = MI_DISPLAY(mi);
    	life3dstruct *lp;
    	int i, npats;
    
    	if (life3ds == NULL) {
    		if ((life3ds = (life3dstruct *) calloc(MI_NUM_SCREENS(mi),
    				sizeof (life3dstruct))) == NULL)
    			return;
    	}
    	lp = &life3ds[MI_SCREEN(mi)];
    
    	lp->generation = 0;
    
    	lp->labelOffsetX = NRAND(8);
    	lp->labelOffsetY = NRAND(8);
    	parseRule(mi, lp->ruleString);
    	parseFile(mi);
    	if (allPatterns) {
    		lp->patterned_rule = NRAND(LIFE_RULES);
    		copyFromPatternedRule(&lp->param, lp->patterned_rule);
    		printRule(lp->ruleString, lp->param, MI_IS_VERBOSE(mi));
    	} else if (allGliders) {
    		lp->patterned_rule = NRAND(LIFE_GLIDERS);
    		copyFromPatternedRule(&lp->param, lp->patterned_rule);
    		printRule(lp->ruleString, lp->param, MI_IS_VERBOSE(mi));
    	} else {
    		lp->param.survival = input_param.survival;
    		lp->param.birth = input_param.birth;
    	}
    	if (!lp->eraserhead.next) {
    		lp->metaDist = (double) NRAND(360);
                    lp->metaAlt = (double) NRAND(360);
                    lp->metaAzm = (double) NRAND(360);
    		lp->ncolumns = MAXCOLUMNS;
    		lp->nrows = MAXROWS;
    		lp->nstacks = MAXSTACKS;
    		lp->ox = lp->ncolumns / 2;
    		lp->oy = lp->nrows / 2;
    		lp->oz = lp->nstacks / 2;
    
    		Init3D(lp);
    	} else {
    		End3D(lp);
    	}
    	lp->colors[0] = MI_BLACK_PIXEL(mi);
    	if (MI_NPIXELS(mi) > 2) {
    		i = NRAND(3);
    
    		lp->colors[i + 1] = MI_PIXEL(mi,
    			NRAND(MI_NPIXELS(mi) / COLORBASE));
    		lp->colors[(i + 1) % 3 + 1] = MI_PIXEL(mi,
    			NRAND(MI_NPIXELS(mi) / COLORBASE) +
    			MI_NPIXELS(mi) / COLORBASE);
    		lp->colors[(i + 2) % 3 + 1] = MI_PIXEL(mi,
    			NRAND(MI_NPIXELS(mi) / COLORBASE) +
    			2 * MI_NPIXELS(mi) / COLORBASE);
    	} else {
    		lp->colors[1] = lp->colors[2] = lp->colors[3] =
    			MI_WHITE_PIXEL(mi);
    	}
    	lp->colors[4] = MI_WHITE_PIXEL(mi);
    	lp->width = MI_WIDTH(mi);
    	lp->height = MI_HEIGHT(mi);
    	lp->memstart = 1;
    	lp->noChangeCount = False;
    	/*lp->tablesMade = 0; */
    
    	if (MI_IS_FULLRANDOM(mi)) {
    		lp->wireframe = (NRAND(8) == 0);
    	} else {
    		lp->wireframe = MI_IS_WIREFRAME(mi);
    	}
    
    	MI_CLEARWINDOW(mi);
    	lp->painted = False;
    
    	lissajous(lp);
    
    	lp->patterned_rule = codeToPatternedRule(lp->param);
    	if ((unsigned) lp->patterned_rule < LIFE_RULES)
    		npats = patterns_rules[lp->patterned_rule];
    	else
    		npats = 0;
    	lp->pattern = NRAND(npats + 2);
    	if (lp->pattern >= npats && !filePattern) {
    		if (!RandomSoup(mi, 30, 10)) {
    			if (lp->eraserhead.next != NULL)
    				End3D(lp);
    			return;
    		}
    	} else {
    		if (!GetPattern(mi, lp->patterned_rule, lp->pattern)) {
    			if (lp->eraserhead.next != NULL)
    				End3D(lp);
    			return;
    		}
    	}
    
    	if (lp->dbuf != None) {
    		XFreePixmap(display, lp->dbuf);
    		lp->dbuf = None;
    	}
    	if ((lp->dbuf = XCreatePixmap(display, MI_WINDOW(mi),
    		lp->width, lp->height, MI_DEPTH(mi))) == None) {
    		free_life3d(display, lp);
    		return;
    	}
    
    	DrawScreen(mi);
    }
    
    void
    draw_life3d(ModeInfo * mi)
    {
    	life3dstruct *lp;
    
    	if (life3ds == NULL)
    		return;
    	lp = &life3ds[MI_SCREEN(mi)];
    	if (lp->eraserhead.next == NULL)
    		return;
    
    	if (!RunLife3D(lp)) {
    		if (lp->eraserhead.next != NULL)
    			End3D(lp);
    		return;
    	}
    	lissajous(lp);
    	if (lp->visible) /* kill static life also */
    		DrawScreen(mi);
    
    	MI_IS_DRAWN(mi) = True;
    
    	if (++lp->generation > MI_CYCLES(mi) || !lp->visible || lp->noChangeCount >= 8) {
    		/*CountCells3D(lp) == 0) */
    		init_life3d(mi);
    	} else
    		lp->painted = True;
    
    	/*
    	 * generate a randomized shooter aimed roughly toward the center of the
    	 * screen after batchcount.
    	 */
    
    	if (MI_COUNT(mi)) {
    		if (lp->generation && lp->generation %
    				((MI_COUNT(mi) < 0) ? 1 : MI_COUNT(mi)) == 0)
    			if (!shooter(lp)) {
    				if (lp->eraserhead.next != NULL)
    					End3D(lp);
    			}
    	}
    }
    
    void
    release_life3d(ModeInfo * mi)
    {
    	if (life3ds != NULL) {
    		int         screen;
    
    		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
    			free_life3d(MI_DISPLAY(mi), &life3ds[screen]);
    		free(life3ds);
    		life3ds = (life3dstruct *) NULL;
    	}
    }
    
    void
    refresh_life3d(ModeInfo * mi)
    {
    	life3dstruct *lp;
    
    	if (life3ds == NULL)
    		return;
    	lp = &life3ds[MI_SCREEN(mi)];
    
    	if (lp->painted) {
    		MI_CLEARWINDOW(mi);
    	}
    }
    
    void
    change_life3d(ModeInfo * mi)
    {
    	int         npats;
    	life3dstruct *lp;
    
    	if (life3ds == NULL)
    		return;
    	lp = &life3ds[MI_SCREEN(mi)];
    
    	lp->generation = 0;
    
    	if (lp->eraserhead.next != NULL)
    		End3D(lp);
    	/*lp->tablesMade = 0; */
    
    	MI_CLEARWINDOW(mi);
    
    	lp->pattern++;
    	lp->patterned_rule = codeToPatternedRule(lp->param);
    	if ((unsigned) lp->patterned_rule < LIFE_RULES)
    		npats = patterns_rules[lp->patterned_rule];
    	else
    		npats = 0;
    	if (lp->pattern >= npats + 2) {
    		lp->pattern = 0;
    		if (allPatterns) {
    			lp->patterned_rule++;
    			if ((unsigned) lp->patterned_rule >= LIFE_RULES)
    				lp->patterned_rule = 0;
    			copyFromPatternedRule(&lp->param, lp->patterned_rule);
    			printRule(lp->ruleString, lp->param, MI_IS_VERBOSE(mi));
    		} else if (allGliders) {
    			lp->patterned_rule++;
    			if (lp->patterned_rule >= LIFE_GLIDERS)
    				lp->patterned_rule = 0;
    			copyFromPatternedRule(&lp->param, lp->patterned_rule);
    			printRule(lp->ruleString, lp->param, MI_IS_VERBOSE(mi));
    		}
    	}
    	if (!serial)
    		lp->pattern = NRAND(npats + 2);
    	if (lp->pattern >= npats) {
    		if (!RandomSoup(mi, 30, 10)) {
    			if (lp->eraserhead.next != NULL)
    				End3D(lp);
    			return;
    		}
    	} else {
    		if (!GetPattern(mi, lp->patterned_rule, lp->pattern)) {
    			if (lp->eraserhead.next != NULL)
    				End3D(lp);
    			return;
    		}
    	}
    
    	DrawScreen(mi);
    }
    
    #endif /* MODE_life3d */