1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* life3d --- Extension to Conway's game of Life, Carter Bays' B5/S45 3d life */
3 
4 #if 0
5 static const char sccsid[] = "@(#)life3d.c	5.27 2008/07/28 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1994 by David Bagley.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 09-Nov-2010: cubic close packing (-neighbors 14)
26  * 31-Jul-2008: 4544 3D life from paper by Carter Bays.
27  * 18-Apr-2004: Added shooting gliders for B6/S567
28  * 13-Mar-2004: Added references
29  * 28-Apr-2003: Randomized "rotation" of life form
30  * 25-Jan-2003: Spawned a life3d.h
31  * 19-Jan-2003: added more gliders from http://www.cse.sc.edu/~bays
32  * 08-Jan-2003: Double buffering
33  * 01-Nov-2000: Allocation checks
34  * 10-May-1997: Compatible with xscreensaver
35  * 18-Apr-1997: Memory leak fixed by Tom Schmidt <tschmidt AT micron.com>
36  * 12-Mar-1995: added LIFE_B6S567 compile-time option
37  * 12-Feb-1995: shooting gliders added
38  * 07-Dec-1994: used life.c and a DOS version of 3dlife
39  * Copyright 1993 Anthony Wesley awesley@canb.auug.org.au found at
40  * life.anu.edu.au /pub/complex_systems/alife/3DLIFE.ZIP
41  *
42  *
43  * References:
44  * Bays, Carter, "A Note About the Discovery of Many New Rules for the
45  *   Game of 3D Life", Complex Systems 16 (2006) 381-386
46  * Bays, Carter, "Candidates for the Game of Life in Three Dimensions",
47  *   1987, Complex Systems 1 373-400
48  * Bays, Carter, "Patterns for Simple Cellular Automata in a Universe of
49  *   Dense-Packed Spheres", 1987, Complex Systems 1 853-875
50  * Bays, Carter, "A Note on the Discovery of a New Game of Three-
51  *   Dimensional Life", 1988, Complex Systems 2 255-258
52  * Bays, Carter, "A New Candidate Rule for the Game of Three-Dimensional
53  *   Life", 1992, Complex Systems 6 433-441
54  * Bays, Carter, "Further Notes on the Game of Three-Dimensional Life",
55  *   1994, Complex Systems 8 67-73
56  * Bays, Carter, "A Note About the Discovery of Many New Rules for the
57      Game of Three-Dimensional Life", 2006, Complex Systems 16 381-386
58  * Dewdney, A.K., "The Armchair Universe, Computer Recreations from the
59  *   Pages of Scientific American Magazine", W.H. Freedman and Company,
60  *   New York, 1988 (February 1987 p 16)
61  * Bays, Carter, "The Game of Three Dimensional Life", 86/11/20
62  *   with (latest?) update from 87/2/1 http://www.cse.sc.edu/~bays/
63  * Meeker, Lee Earl, "Four Dimensional Cellular Automata and the Game
64  *   of Life" 1998 http://home.sc.rr.com/lmeeker/Lee/Home.html
65  */
66 
67 #ifdef STANDALONE
68 #define MODE_life3d
69 #define DEFAULTS "*delay: 1000000 \n" \
70 	"*count: 35 \n" \
71 	"*cycles: 85 \n" \
72 	"*ncolors: 200 \n" \
73 	"*wireframe: False \n" \
74 	"*fullrandom: False \n" \
75 	"*verbose: False \n" \
76 
77 # define reshape_life3d 0
78 # define life3d_handle_event 0
79 #include "xlockmore.h"		/* in xscreensaver distribution */
80 #else /* STANDALONE */
81 #include "xlock.h"		/* in xlockmore distribution */
82 #include "iostuff.h"
83 #endif /* STANDALONE */
84 #define DO_STIPPLE
85 #include "automata.h"
86 
87 #ifdef MODE_life3d
88 #define LIFE_NAMES 1
89 #include "life3d.h"
90 
91 #ifdef LIFE_NAMES
92 #define DEF_LABEL "True"
93 #define FONT_HEIGHT 19
94 #define FONT_WIDTH 15
95 #endif
96 #define DEF_NEIGHBORS  "0"	/* choose best value (26) */
97 #define DEF_SERIAL "False"
98 #define DEF_REPEAT "0"		/* Frequently 2 */
99 
100 #if 1
101 #define DEF_RULE  "G"		/* All rules with gliders */
102 #else
103 #define DEF_RULE  "P"		/* All rules with known patterns */
104 #define DEF_RULE  "B5/S45"
105 #define DEF_RULE  "B5/S56"
106 #define DEF_RULE  "B5/S678"
107 #define DEF_RULE  "B6/S567"
108 #define DEF_RULE  "B67/S67"
109 /* There is a glider for B67/S67 but its kind of big */
110 #endif
111 
112 #define DEF_GLIDERSEARCH  "False"
113 #define DEF_PATTERNSEARCH  "False"
114 #define DEF_DRAW  "True"
115 
116 #ifdef LIFE_NAMES
117 static Bool label;
118 #endif
119 static Bool draw;
120 static int  neighbors;
121 static int  repeat;
122 static char *rule;
123 static char *lifefile;
124 static Bool serial;
125 static Bool glidersearch;
126 static Bool patternsearch;
127 
128 static XrmOptionDescRec opts[] =
129 {
130 #ifdef LIFE_NAMES
131 	{(char *) "-label", (char *) ".life3d.label", XrmoptionNoArg, (caddr_t) "on"},
132 	{(char *) "+label", (char *) ".life3d.label", XrmoptionNoArg, (caddr_t) "off"},
133 #endif
134 	{(char *) "-draw", (char *) ".life3d.draw", XrmoptionNoArg, (caddr_t) "on"},
135 	{(char *) "+draw", (char *) ".life3d.draw", XrmoptionNoArg, (caddr_t) "off"},
136 	{(char *) "-neighbors", (char *) ".life3d.neighbors", XrmoptionSepArg, (caddr_t) NULL},
137 	{(char *) "-repeat", (char *) ".life3d.repeat", XrmoptionSepArg, (caddr_t) NULL},
138 	{(char *) "-rule", (char *) ".life3d.rule", XrmoptionSepArg, (caddr_t) NULL},
139 	{(char *) "-lifefile", (char *) ".life3d.lifefile", XrmoptionSepArg, (caddr_t) NULL},
140 	{(char *) "-serial", (char *) ".life3d.serial", XrmoptionNoArg, (caddr_t) "on"},
141 	{(char *) "+serial", (char *) ".life3d.serial", XrmoptionNoArg, (caddr_t) "off"},
142 	{(char *) "-glidersearch", (char *) ".life3d.glidersearch", XrmoptionNoArg, (caddr_t) "on"},
143 	{(char *) "+glidersearch", (char *) ".life3d.glidersearch", XrmoptionNoArg, (caddr_t) "off"},
144 	{(char *) "-patternsearch", (char *) ".life3d.patternsearch", XrmoptionNoArg, (caddr_t) "on"},
145 	{(char *) "+patternsearch", (char *) ".life3d.patternsearch", XrmoptionNoArg, (caddr_t) "off"},
146 };
147 static argtype vars[] =
148 {
149 #ifdef LIFE_NAMES
150 	{(void *) & label, (char *) "label", (char *) "Label", (char *) DEF_LABEL, t_Bool},
151 #endif
152 	{(void *) & draw, (char *) "draw", (char *) "Draw", (char *) DEF_DRAW, t_Bool},
153 	{(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
154 	{(void *) & repeat, (char *) "repeat", (char *) "Repeat", (char *) DEF_REPEAT, t_Int},
155 	{(void *) & rule, (char *) "rule", (char *) "Rule", (char *) DEF_RULE, t_String},
156 #ifdef STANDALONE
157 	{(void *) & lifefile, (char *) "lifefile", (char *) "LifeFile", (char *) "/dev/null", t_String},
158 #else
159 	{(void *) & lifefile, (char *) "lifefile", (char *) "LifeFile", (char *) "", t_String},
160 #endif
161 	{(void *) & serial, (char *) "serial", (char *) "Serial", (char *) DEF_SERIAL, t_Bool},
162 	{(void *) & glidersearch, (char *) "glidersearch", (char *) "GliderSearch", (char *) DEF_GLIDERSEARCH, t_Bool},
163 	{(void *) & patternsearch, (char *) "patternsearch", (char *) "PatternSearch", (char *) DEF_PATTERNSEARCH, t_Bool},
164 };
165 static OptionStruct desc[] =
166 {
167 #ifdef LIFE_NAMES
168 	{(char *) "-/+label", (char *) "turn on/off name labeling"},
169 #endif
170 	{(char *) "-/+draw", (char *) "turn on/off drawing to speed search"},
171 	{(char *) "-neighbors num", (char *) "cubes 6, 8, 18, 20, 26, rhombic dodecahedron 12, (-)18, truncated octahedron 14, or tetrahedron 22"},
172 	{(char *) "-repeat num", (char *) "repeat for period to exclude in search"}, /* this is not as good as Bays' signature idea */
173 	{(char *) "-rule string", (char *) "B<birth_neighborhood>/S<survival_neighborhood> parameters"},
174 	{(char *) "-lifefile file", (char *) "life file"},
175 	{(char *) "-/+serial", (char *) "turn on/off picking of sequential patterns"},
176 	{(char *) "-/+glidersearch", (char *) "search for gliders"},
177 	{(char *) "-/+patternsearch", (char *) "search for patterns"},
178 };
179 
180 ENTRYPOINT ModeSpecOpt life3d_opts =
181 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
182 
183 #ifdef USE_MODULES
184 ModStruct life3d_description =
185 {"life3d", "init_life3d", "draw_life3d", "release_life3d",
186  "refresh_life3d", "change_life3d", "free_life3d", &life3d_opts,
187  1000000, 35, 85, 1, 64, 1.0, "",
188  "Shows Bays' game of 3D Life", 0, NULL};
189 
190 #endif
191 
192 #define LIFE3DBITS(n,w,h)\
193   if ((lp->pixmaps[lp->init_bits]=\
194   XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
195   free_life3d_screen(display,lp); return;} else {lp->init_bits++;}
196 
197 #define ON 0x40
198 #define OFF 0
199 
200 /* Don't change these numbers without changing the offset() macro below! */
201 #define MAXSTACKS 64
202 #define	MAXROWS 128
203 #define MAXCOLUMNS 128
204 #define BASESIZE ((MAXCOLUMNS*MAXROWS*MAXSTACKS)>>6)
205 #define SOUPPERCENT 30
206 /* #define SOUPSIZE(s)  MAX(s/4,10) */
207 /* 20B4S45 frequently has unlimited growth, which is not pretty */
208 #define SOUPSIZE(s)   (lp->neighbors == 20 && lp->patterned_rule == LIFE_20B4S45) ? 4 : ((lp->neighbors == 18 && lp->patterned_rule == LIFE_18B4S45) ? 6 : 10)
209 
210 #define RT_ANGLE 90
211 #define HALFRT_ANGLE 45
212 
213 /* Store state of cell in top bit. Reserve low bits for count of living nbrs */
214 #define Set3D(x,y,z) SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,ON)
215 #define Reset3D(x,y,z) SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,OFF)
216 
217 #define SetList(x,y,z) if (!SetMem(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z,ON)) return False; \
218 if (!addToList(lp,(unsigned int)x,(unsigned int)y,(unsigned int)z)) return False
219 
220 #define CellState3D(c) ((c)&ON)
221 #define CellNbrs3D(c) ((c)&0x1f)	/* 26 <= 31 */
222 
223 #define EyeToScreen 72.0	/* distance from eye to screen */
224 #define HalfScreenD 14.0	/* 1/2 the diameter of screen */
225 #define BUCKETSIZE 10
226 #define NBUCKETS ((MAXCOLUMNS+MAXROWS+MAXSTACKS)*BUCKETSIZE)
227 #define Distance(x1,y1,z1,x2,y2,z2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2))
228 
229 #define IP (M_PI / 180.0)
230 
231 #define COLORBASE 3
232 #define COLORS (COLORBASE + 2)
233 
234 #define BLACK 0
235 #define RED   1
236 #define GREEN 2
237 #define BLUE  3
238 #define UV    3 /* Place holder... make BLUE for now */
239 #define WHITE 4
240 
241 typedef struct _CellList {
242 	unsigned char x, y, z;	/* Location in world coordinates */
243 	char        visible;	/* 1 if the cube is to be displayed */
244 	short       dist;	/* dist from cell to eye */
245 	struct _CellList *next;	/* pointer to next entry on linked list */
246 	struct _CellList *prev;
247 	struct _CellList *priority;
248 } CellList;
249 
250 typedef struct {
251 	Bool        painted;
252 	int         neighbors;
253 	paramstruct param;
254 	int         pattern, patterned_rule;
255 	int         ox, oy, oz;	/* origin */
256 	double      vx, vy, vz;	/* viewpoint */
257 	int         generation;
258 	int         ncols, nrows, nstacks;
259 	int         memstart;
260 	char        visible;
261 	int         noChangeCount;
262 	int         ncells;
263 	unsigned char *base[BASESIZE];
264 	double      A, B, C, F;
265 	int         width, height;
266 	int         init_bits;
267 	unsigned long colors[COLORS];
268 	Pixmap      pixmaps[NUMSTIPPLES - 1];
269 	GC          stippledGC;
270 	double      azm;
271 	double      metaAlt, metaAzm, metaDist;
272 	CellList   *ptrhead, *ptrend, eraserhead, eraserend;
273 	CellList   *buckethead[NBUCKETS], *bucketend[NBUCKETS];
274 	Bool        wireframe;
275 	Pixmap      dbuf;
276 	int         allPatterns, allGliders;
277 	paramstruct input_param;
278 	int         labelOffsetX, labelOffsetY;
279 	char        ruleString[80], nameString[80];
280 } life3dstruct;
281 
282 static life3dstruct *life3ds = (life3dstruct *) NULL;
283 
284 static char *filePattern = (char *) NULL;
285 
286 #if 0
287 static int
288 invplot(int local_neighbors)
289 {
290 	switch (local_neighbors) {
291 	case 6: /* faces only */
292 		return 0;
293 	case 8: /* corners only */
294 		return 1;
295 	case 12: /* edges only, i.e. only odd #'d cells, hexagonal close packing */
296 		return 2;
297 	case 14: /* cubic close packing */
298 		return 3;
299 	case -14: /* faces and corners */
300 		return 4;
301 	case 16: /* tetrahedral */
302 		return 5;
303 	case 18: /* faces and edges */
304 		return 6;
305 	case -18: /* hexagonal close and corners */
306 		return 7;
307 	case 20: /* corners and edges */
308 		return 8;
309 	case 22: /* tetrahedral */
310 		return 9;
311 	case 26: /* all */
312 		return 10;
313 	default:
314 		(void) fprintf(stderr, "no neighborhood like %d known\n",
315 				local_neighbors);
316 			return 9;
317 	}
318 }
319 #endif
320 
321 #define tetra(x, y, z)  (((x % 2) == 1) && ((y % 2) == 1) && ((z % 2) == 1))
322 #define rTetra(x, y, z) (tetra(x, y, z) && (((x + y + z) % 4) == 1))
323 #define lTetra(x, y, z) (tetra(x, y, z) && (((x + y + z) % 4) == 3))
324 
325 static int
codeToPatternedRule(int local_neighbors,paramstruct param)326 codeToPatternedRule(int local_neighbors, paramstruct param)
327 {
328 	unsigned int i;
329 
330 	switch (local_neighbors) {
331 	case 12:
332 		for (i = 0; i < LIFE_12RULES; i++)
333 			if (param_12rules[i].birth == param.birth &&
334 					param_12rules[i].survival == param.survival)
335 				return i;
336 		return LIFE_12RULES;
337 	case 14:
338 		for (i = 0; i < LIFE_14RULES; i++)
339 			if (param_14rules[i].birth == param.birth &&
340 					param_14rules[i].survival == param.survival)
341 				return i;
342 		return LIFE_14RULES;
343 	case 18:
344 		for (i = 0; i < LIFE_18RULES; i++)
345 			if (param_18rules[i].birth == param.birth &&
346 					param_18rules[i].survival == param.survival)
347 				return i;
348 		return LIFE_18RULES;
349 	case -18:
350 		for (i = 0; i < LIFE__18RULES; i++)
351 			if (param__18rules[i].birth == param.birth &&
352 					param__18rules[i].survival == param.survival)
353 				return i;
354 		return LIFE__18RULES;
355 	case 20:
356 		for (i = 0; i < LIFE_20RULES; i++)
357 			if (param_20rules[i].birth == param.birth &&
358 					param_20rules[i].survival == param.survival)
359 				return i;
360 		return LIFE_20RULES;
361 	case 22:
362 		for (i = 0; i < LIFE_22RULES; i++)
363 			if (param_22rules[i].birth == param.birth &&
364 					param_22rules[i].survival == param.survival)
365 				return i;
366 		return LIFE_22RULES;
367 	case 26:
368 		for (i = 0; i < LIFE_26RULES; i++)
369 			if (param_26rules[i].birth == param.birth &&
370 					param_26rules[i].survival == param.survival)
371 				return i;
372 		return LIFE_26RULES;
373 	}
374 	return 0;
375 }
376 
377 static void
copyFromPatternedRule(int local_neighbors,paramstruct * param,int patterned_rule)378 copyFromPatternedRule(int local_neighbors, paramstruct * param,
379 		int patterned_rule)
380 {
381 	switch (local_neighbors) {
382 	case 12:
383 		param->birth = param_12rules[patterned_rule].birth;
384 		param->survival = param_12rules[patterned_rule].survival;
385 		break;
386 	case 14:
387 		param->birth = param_14rules[patterned_rule].birth;
388 		param->survival = param_14rules[patterned_rule].survival;
389 		break;
390 	case 18:
391 		param->birth = param_18rules[patterned_rule].birth;
392 		param->survival = param_18rules[patterned_rule].survival;
393 		break;
394 	case -18:
395 		param->birth = param__18rules[patterned_rule].birth;
396 		param->survival = param__18rules[patterned_rule].survival;
397 		break;
398 	case 20:
399 		param->birth = param_20rules[patterned_rule].birth;
400 		param->survival = param_20rules[patterned_rule].survival;
401 		break;
402 	case 22:
403 		param->birth = param_22rules[patterned_rule].birth;
404 		param->survival = param_22rules[patterned_rule].survival;
405 		break;
406 	case 26:
407 		param->birth = param_26rules[patterned_rule].birth;
408 		param->survival = param_26rules[patterned_rule].survival;
409 		break;
410 	}
411 }
412 
413 static void
printRule(int local_neighbors,char * string,paramstruct param,Bool verbose)414 printRule(int local_neighbors, char * string, paramstruct param, Bool verbose)
415 {
416 	int i = 1, l;
417 
418 	string[0] = 'B';
419 	if (verbose)
420 		(void) fprintf(stdout,
421 			"rule (Birth/Survival %d neighborhood): ",
422 			local_neighbors);
423 	for (l = 0; l <= ABS(local_neighbors) && l < 10; l++) {
424 		if (param.birth & (1 << l)) {
425 			(void) sprintf(&(string[i]), "%d", l);
426 			i++;
427 		}
428 	}
429 	(void) sprintf(&(string[i]), "/S");
430 	i += 2;
431 	for (l = 0; l <= ABS(local_neighbors) && l < 10; l++) {
432 		if (param.survival & (1 << l)) {
433 			(void) sprintf(&(string[i]), "%d", l);
434 			i++;
435 		}
436 	}
437 	string[i] = '\0';
438 	if (verbose)
439 		(void) fprintf(stdout,
440 			"%s\nbinary rule: Birth 0x%X, Survival 0x%X\n",
441 			string, param.birth, param.survival);
442 }
443 
444 static void
positionOfNeighbor(life3dstruct * lp,int n,unsigned int * col,unsigned int * row,unsigned int * stack)445 positionOfNeighbor(life3dstruct * lp, int n, unsigned int *col,
446 	unsigned int *row, unsigned int *stack)
447 {
448 	/* 16 and 22 are tetrahedral, extra space was given to */
449 	/* allow for octahedron positions not present here */
450 	if (lp->neighbors == 16 || lp->neighbors == 22) {
451 		if (rTetra(*col, *row, *stack)) {
452 			if (lp->neighbors == 16) {
453 				if (n >= 16)
454 					return;
455 				n = n + 10;
456 			} else if (lp->neighbors == 22) {
457 				if (n >= 22)
458 					return;
459 				n = n + 4;
460 			}
461 			switch(n) {
462 			/* edges (tetra) */
463 			case 4:
464 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
465 				break;
466 			case 5:
467 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
468 				break;
469 			case 6:
470 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
471 				break;
472 			case 7:
473 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
474 				break;
475 			case 8:
476 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
477 				break;
478 			case 9:
479 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
480 				break;
481 			/* points (tetra) */
482 			case 10:
483 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
484 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
485 				break;
486 			case 11:
487 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
488 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
489 				break;
490 			case 12:
491 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
492 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
493 				break;
494 			case 13:
495 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
496 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
497 				break;
498 			case 14:
499 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
500 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
501 				break;
502 			case 15:
503 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
504 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
505 				break;
506 			case 16:
507 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
508 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
509 				break;
510 			case 17:
511 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
512 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
513 				break;
514 			case 18:
515 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
516 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
517 				break;
518 			case 19:
519 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
520 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
521 				break;
522 			case 20:
523 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
524 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
525 				break;
526 			case 21:
527 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
528 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
529 				break;
530 			/* far points (tetra) */
531 			case 22:
532 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
533 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
534 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
535 				break;
536 			case 23:
537 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
538 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
539 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
540 				break;
541 			case 24:
542 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
543 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
544 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
545 				break;
546 			case 25:
547 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
548 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
549 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
550 				break;
551 			default:
552 				break;
553 			}
554 		} else if (lTetra(*col, *row, *stack)) {
555 			if (lp->neighbors == 16) {
556 				if (n >= 16)
557 					return;
558 				n = n + 10;
559 			} else if (lp->neighbors == 22) {
560 				if (n >= 22)
561 					return;
562 				n = n + 4;
563 			}
564 			switch(n) {
565 			/* edges (tetra) */
566 			case 4:
567 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
568 				break;
569 			case 5:
570 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
571 				break;
572 			case 6:
573 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
574 				break;
575 			case 7:
576 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
577 				break;
578 			case 8:
579 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
580 				break;
581 			case 9:
582 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
583 				break;
584 			/* points (tetra) */
585 			case 10:
586 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
587 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
588 				break;
589 			case 11:
590 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
591 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
592 				break;
593 			case 12:
594 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
595 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
596 				break;
597 			case 13:
598 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
599 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
600 				break;
601 			case 14:
602 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
603 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
604 				break;
605 			case 15:
606 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
607 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
608 				break;
609 			case 16:
610 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
611 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
612 				break;
613 			case 17:
614 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
615 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
616 				break;
617 			case 18:
618 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
619 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
620 				break;
621 			case 19:
622 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
623 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
624 				break;
625 			case 20:
626 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
627 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
628 				break;
629 			case 21:
630 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
631 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
632 				break;
633 			/* far points (tetra) */
634 			case 22:
635 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
636 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
637 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
638 				break;
639 			case 23:
640 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
641 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
642 				*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
643 				break;
644 			case 24:
645 				*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
646 				*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
647 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
648 				break;
649 			case 25:
650 				*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
651 				*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
652 				*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
653 				break;
654 			default:
655 				break;
656 			}
657 		}
658 		return;
659 	}
660 	/* faces */
661 	if (lp->neighbors == 6 || lp->neighbors == 14 || lp->neighbors == -14 ||
662 				lp->neighbors == 18 || lp->neighbors == 26) {
663 		switch (n) {
664 		case 0:
665 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
666 			break;
667 		case 1:
668 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
669 			break;
670 		case 2:
671 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
672 			break;
673 		case 3:
674 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
675 			break;
676 		case 4:
677 			if (lp->neighbors == 14) {
678 				if (*stack + 1 == lp->nstacks)
679 					*stack = 1;
680 				else if (*stack + 2 == lp->nstacks)
681 					*stack = 0;
682 				else
683 					*stack = *stack + 2;
684 			} else {
685 				*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
686 			}
687 			break;
688 		case 5:
689 			if (lp->neighbors == 14) {
690 				if (*stack == 0)
691 					*stack = lp->nstacks - 2;
692 				else if ((*stack  - 1) == 0)
693 					*stack = lp->nstacks - 1;
694 				else
695 					*stack = *stack - 2;
696 			} else {
697 				*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
698 			}
699 			break;
700 		}
701 	} else {
702 		n += 6;
703 	}
704 	/* edges */
705 	if (lp->neighbors == 12 || lp->neighbors == 18 || lp->neighbors == -18 ||
706 				lp->neighbors == 20 || lp->neighbors == 26) {
707 		switch (n) {
708 		case 6:
709 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
710 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
711 			break;
712 		case 7:
713 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
714 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
715 			break;
716 		case 8:
717 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
718 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
719 			break;
720 		case 9:
721 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
722 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
723 			break;
724 		case 10:
725 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
726 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
727 			break;
728 		case 11:
729 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
730 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
731 			break;
732 		case 12:
733 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
734 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
735 			break;
736 		case 13:
737 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
738 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
739 			break;
740 		case 14:
741 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
742 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
743 			break;
744 		case 15:
745 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
746 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
747 			break;
748 		case 16:
749 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
750 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
751 			break;
752 		case 17:
753 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
754 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
755 			break;
756 		}
757 	} else {
758 		n += 12;
759 	}
760 	/* corners */
761 	if (lp->neighbors == 8 || lp->neighbors == 14 || lp->neighbors == -14 ||
762 			lp->neighbors == 20 || lp->neighbors == 26) {
763 		switch (n) {
764 		case 18:
765 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
766 			if (lp->neighbors != 14 || (*stack & 1) == 0)
767 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
768 			if (lp->neighbors != 14 || (*stack & 1) == 1)
769 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
770 			break;
771 		case 19:
772 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
773 			if (lp->neighbors != 14 || (*stack & 1) == 0)
774 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
775 			if (lp->neighbors != 14 || (*stack & 1) == 0)
776 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
777 			break;
778 		case 20:
779 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
780 			if (lp->neighbors != 14 || (*stack & 1) == 1)
781 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
782 			if (lp->neighbors != 14 || (*stack & 1) == 1)
783 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
784 			break;
785 		case 21:
786 			*stack = (*stack + 1 == lp->nstacks) ? 0 : *stack + 1;
787 			if (lp->neighbors != 14 || (*stack & 1) == 1)
788 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
789 			if (lp->neighbors != 14 || (*stack & 1) == 0)
790 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
791 			break;
792 		case 22:
793 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
794 			if (lp->neighbors != 14 || (*stack & 1) == 0)
795 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
796 			if (lp->neighbors != 14 || (*stack & 1) == 1)
797 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
798 			break;
799 		case 23:
800 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
801 			if (lp->neighbors != 14 || (*stack & 1) == 0)
802 			*col = (*col + 1 == lp->ncols) ? 0 : *col + 1;
803 			if (lp->neighbors != 14 || (*stack & 1) == 0)
804 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
805 			break;
806 		case 24:
807 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
808 			if (lp->neighbors != 14 || (*stack & 1) == 1)
809 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
810 			if (lp->neighbors != 14 || (*stack & 1) == 1)
811 			*row = (*row == 0) ? lp->nrows - 1 : *row - 1;
812 			break;
813 		case 25:
814 			*stack = (*stack == 0) ? lp->nstacks - 1 : *stack - 1;
815 			if (lp->neighbors != 14 || (*stack & 1) == 1)
816 			*col = (*col == 0) ? lp->ncols - 1 : *col - 1;
817 			if (lp->neighbors != 14 || (*stack & 1) == 0)
818 			*row = (*row + 1 == lp->nrows) ? 0 : *row + 1;
819 			break;
820 		}
821 	} else {
822 		n += 8;
823 	}
824 	/* far corners */
825 	if (neighbors == -18) {
826 		switch (n) {
827 		case 26:
828 			*col = (*col < 2) ? lp->ncols - 2 + *col : *col - 2;
829 			break;
830 		case 27:
831 			*col = (*col + 2 >= lp->ncols) ? *col + 2 - lp->ncols : *col + 2;
832 			break;
833 		case 28:
834 			*row = (*row < 2) ? lp->nrows - 2 + *row : *row - 2;
835 			break;
836 		case 29:
837 			*row = (*row + 2 >= lp->nrows) ? *row + 2 - lp->nrows : *row + 2;
838 			break;
839 		case 30:
840 			*stack = (*stack < 2) ? lp->nstacks - 2 + *stack : *stack - 2;
841 			break;
842 		case 31:
843 			*stack = (*stack + 2 >= lp->nstacks) ? *stack + 2 - lp->nstacks : *stack + 2;
844 			break;
845 		default:
846 			break;
847 		}
848 	}
849 }
850 
851 /*-
852  * This stuff is not good for rules above 9 cubes but it is unlikely that
853  * these modes would be much good anyway....  death assumed.
854  */
855 static void
parseRule(ModeInfo * mi,char * string)856 parseRule(ModeInfo * mi, char * string)
857 {
858 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
859 	int n, l = 0;
860 	char serving = 0;
861 	static Bool foundBirth = False;
862 	static Bool foundSurvival = False;
863 
864 	if (foundBirth && foundSurvival)
865 		return;
866 	foundBirth = foundSurvival = False;
867 	lp->input_param.birth = lp->input_param.survival = 0;
868 	if (rule) {
869 		n = 0;
870 		while (rule[n]) {
871 			if (rule[n] == 'P' || rule[n] == 'p') {
872 				lp->allPatterns = True;
873 				foundBirth = foundSurvival = True;
874 				if (MI_IS_VERBOSE(mi))
875 					(void) fprintf(stdout, "rule: All rules with known patterns\n");
876 				return;
877 			} else if (rule[n] == 'G' || rule[n] == 'g') {
878 				lp->allGliders = True;
879 				foundBirth = foundSurvival = True;
880 				if (MI_IS_VERBOSE(mi))
881 					(void) fprintf(stdout, "rule: All rules with known gliders\n");
882 				return;
883 			} else if (rule[n] == 'B' || rule[n] == 'F' ||
884 					rule[n] == 'b' || rule[n] == 'f') {
885 				serving = 'B';
886 			} else if (rule[n] == 'S' || rule[n] == 'E' || rule[n] == 'L' ||
887 					rule[n] == 's' || rule[n] == 'e' || rule[n] == 'l') {
888 				serving = 'S';
889 			} else {
890 				l = rule[n] - '0';
891 				if (l >= 0 && l <= 9 && l <= ABS(lp->neighbors)) {	/* no 10..26 */
892 					if (serving == 'B' || rule[n] == 'b') {
893 						foundBirth = True;
894 						lp->input_param.birth |= (1 << l);
895 					} else if (serving == 'S' || rule[n] == 's') {
896 						foundSurvival = True;
897 						lp->input_param.survival |= (1 << l);
898 					}
899 				}
900 			}
901 			n++;
902 		}
903 	}
904 	if (!foundBirth || !foundSurvival ||
905 			!(lp->input_param.birth || lp->input_param.survival)) {
906 		/* Default to Bays' rules if rule does not make sense */
907 		lp->allGliders = True;
908 		foundBirth = foundSurvival = True;
909 		if (MI_IS_VERBOSE(mi))
910 			(void) fprintf(stdout,
911 				"rule: Defaulting to all rules with known gliders\n");
912 		return;
913 	}
914 	printRule(lp->neighbors, string, lp->input_param, MI_IS_VERBOSE(mi));
915 }
916 
917 #ifndef STANDALONE
918 static void
parseFile(ModeInfo * mi)919 parseFile(ModeInfo *mi)
920 {
921 	FILE *file;
922 	static Bool done = False;
923 	int firstx, firsty, x = 0, y = 0, z = 0, i = 0;
924 	int c, cprev = ' ', size;
925 	char line[256];
926 
927 	if (done)
928 		return;
929 	done = True;
930 	if (MI_IS_FULLRANDOM(mi) || !lifefile || !*lifefile)
931 		return;
932 	if ((file = my_fopenSize(lifefile, "r", &size)) == NULL) {
933 		(void) fprintf(stderr, "could not read file \"%s\"\n", lifefile);
934 		return;
935 	}
936 	for (;;) {
937 		if (!fgets(line, 256, file)) {
938 			(void) fprintf(stderr,
939 				"could not read header of file \"%s\"\n",
940 				lifefile);
941 			(void) fclose(file);
942 			return;
943 		}
944 		if (strncmp(line, "#P", (size_t) 2) == 0 &&
945 				sscanf(line, "#P %d %d %d", &x, &y, &z) == 3)
946 			break;
947 	}
948 	c = getc(file);
949 	while (c != EOF && !(c == '0' || c == 'O' || c == '*' || c == '.')) {
950 		c = getc(file);
951 	}
952 	if (c == EOF || x <= -127 || y <= -127 || z <= -127 ||
953 			x >= 127 || y >= 127 || z >= 127) {
954 		(void) fprintf(stderr,
955 			"corrupt file \"%s\" or file to large\n", lifefile);
956 		(void) fclose(file);
957 		return;
958 	}
959 	firstx = x;
960 	firsty = y;
961 	if ((filePattern = (char *) malloc((3 * size) *
962 			 sizeof (char))) == NULL) {
963 		(void) fprintf(stderr, "not enough memory\n");
964 		(void) fclose(file);
965 	}
966 
967 	while (c != EOF && x < 127 && y < 127 && z < 127 && i < 3 * size) {
968 		if (c == '0' || c == 'O' || c == '*') {
969 			filePattern[i++] = x++;
970 			filePattern[i++] = y;
971 			filePattern[i++] = z;
972 		} else if (c == '.') {
973 			x++;
974 		} else if (c == '\n') {
975 			if (cprev == '\n') {
976 				z++;
977 				y = firsty;
978 			} else {
979 				x = firstx;
980 				y++;
981 			}
982 		}
983 		cprev = c;
984 		c = getc(file);
985 	}
986 	(void) fclose(file);
987 	filePattern[i] = 127;
988 }
989 #endif
990 
991 /*--- list ---*/
992 /* initialise the state of all cells to OFF */
993 static void
initList(life3dstruct * lp)994 initList(life3dstruct * lp)
995 {
996 	lp->ptrhead = lp->ptrend = (CellList *) NULL;
997 	lp->eraserhead.next = &lp->eraserend;
998 	lp->eraserend.prev = &lp->eraserhead;
999 	lp->ncells = 0;
1000 }
1001 
1002 /*-
1003  * Function that adds the cell (assumed live) at (x,y,z) onto the search
1004  * list so that it is scanned in future generations
1005  */
1006 static Bool
addToList(life3dstruct * lp,unsigned int x,unsigned int y,unsigned int z)1007 addToList(life3dstruct * lp, unsigned int x, unsigned int y, unsigned int z)
1008 {
1009 	CellList *tmp;
1010 
1011 	if ((tmp = (CellList *) malloc(sizeof (CellList))) == NULL)
1012 		return False;
1013 	tmp->x = x;
1014 	tmp->y = y;
1015 	tmp->z = z;
1016 	if (lp->ptrhead == NULL) {
1017 		lp->ptrhead = lp->ptrend = tmp;
1018 		tmp->prev = (struct _CellList *) NULL;
1019 	} else {
1020 		lp->ptrend->next = tmp;
1021 		tmp->prev = lp->ptrend;
1022 		lp->ptrend = tmp;
1023 	}
1024 	lp->ncells++;
1025 	lp->ptrend->next = (struct _CellList *) NULL;
1026 	return True;
1027 }
1028 
1029 static void
addToEraseList(life3dstruct * lp,CellList * cell)1030 addToEraseList(life3dstruct * lp, CellList * cell)
1031 {
1032 	cell->next = &lp->eraserend;
1033 	cell->prev = lp->eraserend.prev;
1034 	lp->eraserend.prev->next = cell;
1035 	lp->eraserend.prev = cell;
1036 }
1037 
1038 static void
delFromList(life3dstruct * lp,CellList * cell)1039 delFromList(life3dstruct * lp, CellList * cell)
1040 {
1041 	if (cell != lp->ptrhead) {
1042 		cell->prev->next = cell->next;
1043 	} else {
1044 		lp->ptrhead = cell->next;
1045 		if (lp->ptrhead != NULL)
1046 			lp->ptrhead->prev = (struct _CellList *) NULL;
1047 	}
1048 
1049 	if (cell != lp->ptrend) {
1050 		cell->next->prev = cell->prev;
1051 	} else {
1052 		lp->ptrend = cell->prev;
1053 		if (lp->ptrend != NULL)
1054 			lp->ptrend->next = (struct _CellList *) NULL;
1055 	}
1056 
1057 	lp->ncells--;
1058 	addToEraseList(lp, cell);
1059 }
1060 
1061 static void
delFromEraseList(CellList * cell)1062 delFromEraseList(CellList * cell)
1063 {
1064 	cell->next->prev = cell->prev;
1065 	cell->prev->next = cell->next;
1066 	free(cell);
1067 }
1068 
1069 #ifdef DEBUG
1070 static void
printState(ModeInfo * mi,int state)1071 printState(ModeInfo * mi, int state)
1072 {
1073 /*	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
1074 	CellList *curr;
1075 	int i = 0;
1076 
1077 	curr = lp->first[state]->next;
1078 	(void) printf("state %d\n", state);
1079 	while (curr != lp->last[state]) {
1080 		(void) printf("%d: position %ld,	age %d, state %d, toggle %d\n",
1081 			i, curr->info.position, curr->info.age,
1082 			curr->info.state, curr->info.toggle);
1083 		curr = curr->next;
1084 		i++;
1085 	} */
1086 }
1087 
1088 #endif
1089 
1090 static void
printList(life3dstruct * lp,int i)1091 printList(life3dstruct * lp, int i)
1092 {
1093 	CellList *ptr, *ptrnextcell;
1094 	/* did not make this hack multiscreen ready */
1095 	static int tcol[2], trow[2], tstack[2], tcol2[2], trow2[2], tstack2[2];
1096 	static int tcount[2];
1097 	int hr = lp->nrows / 2;
1098 	int hc = lp->ncols / 2;
1099 	int hs = lp->nstacks / 2;
1100 
1101 	tcol[i] = 0;
1102 	trow[i] = 0;
1103 	tstack[i] = 0;
1104 	tcol2[i] = 0;
1105 	trow2[i] = 0;
1106 	tstack2[i] = 0;
1107 	tcount[i] = lp->ncells;
1108 	if ((glidersearch || patternsearch) && tcount[0] < 72) {
1109 		ptr = lp->ptrend;
1110 		while (ptr != NULL) {
1111 			int col, row, stack;
1112 
1113 			ptrnextcell = ptr->prev;
1114 			col = ptr->x;
1115 			row = ptr->y;
1116 			stack = ptr->z;
1117 			(void) printf("%d, %d, %d,\n", col - hc,
1118 				row - hr, stack - hs);
1119 			/* fudging a signature */
1120 			tcol[i] += col;
1121 			trow[i] += row;
1122 			tstack[i] += stack;
1123 			tcol2[i] += col * col;
1124 			trow2[i] += row * row;
1125 			tstack2[i] += stack * stack;
1126 			ptr = ptrnextcell;
1127 		}
1128 
1129 		(void) printf("%d#%d, pos_sum %d %d %d %d %d %d\n",
1130 			i, tcount[i], tcol[i], trow[i], tstack[i],
1131 			tcol2[i], trow2[i], tstack2[i]);
1132 		if (i == 1 && (tcount[0] <= NUMPTS || tcount[1] <= NUMPTS) &&
1133 				(tcount[0] != tcount[1] ||
1134 				tcol[0] != tcol[1] || trow[0] != trow[1] ||
1135 				tstack[0] != tstack[1] ||
1136 				tcol2[0] != tcol2[1] || trow2[0] != trow2[1] ||
1137 				tstack2[0] != tstack2[1]))
1138 			(void) printf("found\n");
1139 	}
1140 }
1141 
1142 /*--- memory ---*/
1143 /*-
1144  * Simulate a large array by dynamically allocating 4x4x4 size cells when
1145  * needed.
1146  */
1147 static void
MemInit(life3dstruct * lp)1148 MemInit(life3dstruct * lp)
1149 {
1150 	int i;
1151 
1152 	for (i = 0; i < BASESIZE; ++i) {
1153 		if (lp->base[i] != NULL) {
1154 			free(lp->base[i]);
1155 			lp->base[i] = (unsigned char *) NULL;
1156 		}
1157 	}
1158 	lp->memstart = 0;
1159 }
1160 
1161 #define BASE_OFFSET(x,y,z,b,o) \
1162 b = ((x & 0x7c) << 7) + ((y & 0x7c) << 2) + ((z & 0x7c) >> 2); \
1163 o = (x & 3) + ((y & 3) << 2) + ((z & 3) << 4); \
1164 if (lp->base[b] == NULL) {\
1165 if ((lp->base[b] = (unsigned char *) calloc(64, sizeof (unsigned char))) == NULL) {return False;}}
1166 
1167 
1168 static Bool
GetMem(life3dstruct * lp,unsigned int x,unsigned int y,unsigned int z,int * m)1169 GetMem(life3dstruct * lp, unsigned int x, unsigned int y, unsigned int z,
1170 		int *m)
1171 {
1172 	int b, o;
1173 
1174 	if (lp->memstart) {
1175 		MemInit(lp);
1176 	}
1177 	BASE_OFFSET(x, y, z, b, o);
1178 	*m = lp->base[b][o];
1179 	return True;
1180 }
1181 
1182 static Bool
SetMem(life3dstruct * lp,unsigned int x,unsigned int y,unsigned int z,unsigned int val)1183 SetMem(life3dstruct * lp, unsigned int x, unsigned int y, unsigned int z,
1184 		unsigned int val)
1185 {
1186 	int b, o;
1187 
1188 	if (lp->memstart) {
1189 		MemInit(lp);
1190 	}
1191 #define NBUCKETS ((MAXCOLUMNS+MAXROWS+MAXSTACKS)*BUCKETSIZE)
1192 	BASE_OFFSET(x, y, z, b, o);
1193 	lp->base[b][o] = val;
1194 	return True;
1195 }
1196 
1197 static Bool
ChangeMem(life3dstruct * lp,unsigned int x,unsigned int y,unsigned int z,unsigned int val)1198 ChangeMem(life3dstruct * lp,
1199 		unsigned int x, unsigned int y, unsigned int z,
1200 		unsigned int val)
1201 {
1202 	int b, o;
1203 
1204 	if (lp->memstart) {
1205 		MemInit(lp);
1206 	}
1207 	BASE_OFFSET(x, y, z, b, o);
1208 	lp->base[b][o] += val;
1209 	return True;
1210 }
1211 
1212 static void
ClearMem(life3dstruct * lp)1213 ClearMem(life3dstruct * lp)
1214 {
1215 	int i, j, count;
1216 
1217 	for (i = 0; i < BASESIZE; ++i)
1218 		if (lp->base[i] != NULL) {
1219 			for (count = j = 0; j < 64 && count == 0; ++j)
1220 				if (CellState3D(lp->base[i][j]))
1221 					++count;
1222 			if (!count) {
1223 				free(lp->base[i]);
1224 				lp->base[i] = (unsigned char *) NULL;
1225 			}
1226 		}
1227 }
1228 
1229 /* Checks to make sure there is a cell there */
cellCheck(int neighbors,int x,int y,int z)1230 static Bool cellCheck(int neighbors, int x, int y, int z)
1231 {
1232 	if (neighbors == 16 || neighbors == 22)
1233 		return tetra(x, y, z);
1234 	else
1235 		return ((neighbors != 12 && neighbors != -18) ||
1236 			((x + y + z) & 1) == 0);
1237 }
1238 
1239 /* Checks to make sure it wraps well */
1240 /*
1241 static Bool boundsCheck12(int neighbors, int x, int y, int z) {
1242 	return ((neighbors != 12 && neighbors != -18) ||
1243 		((x + y + z) & 1) == 0);
1244 }
1245 
1246 static Bool boundsCheck14(int neighbors, int z) {
1247 	return (neighbors != 14 || (z & 1) == 0);
1248 }
1249 
1250 static Bool boundsCheck22(int neighbors, int x, int y, int z) {
1251 	return (neighbors != 22 || (((x % 4) == 0) &&
1252 		((y % 4) == 0) && ((z % 4) == 0)));
1253 }
1254 */
1255 
1256 /*-
1257  * This routine increments the values stored in the 27 cells centered on
1258  * (x,y,z) Note that the offset() macro implements wrapping - the world is a
1259  * 4d torus
1260  */
1261 static Bool
IncrementNbrs3D(life3dstruct * lp,CellList * cell)1262 IncrementNbrs3D(life3dstruct * lp, CellList * cell)
1263 {
1264 	unsigned int xc, yc, zc, x, y, z, i;
1265 
1266 	for (i = 0; i < ABS(lp->neighbors); i++) {
1267 		x = xc = cell->x;
1268 		y = yc = cell->y;
1269 		z = zc = cell->z;
1270 		if (!cellCheck(lp->neighbors, x, y, z))
1271 			continue;
1272 	/*if (patternsearch &&
1273 		(x == MAXCOLUMNS / 2 - 1 || y == MAXROWS / 2 - 1 || z == MAXSTACKS / 2 - 1))
1274 		continue;*/
1275 		positionOfNeighbor(lp, i, &x, &y, &z);
1276 		if (x != xc || y != yc || z != zc)
1277 			if (!ChangeMem(lp,
1278 				  (unsigned int) x, (unsigned int) y, (unsigned int) z, 1))
1279 				return False;
1280 	}
1281 	return True;
1282 }
1283 
1284 static void
endList(life3dstruct * lp)1285 endList(life3dstruct * lp)
1286 {
1287 	CellList *ptr;
1288 
1289 	while (lp->ptrhead != NULL) {
1290 		/* Reset3D(lp->ptrhead->x, lp->ptrhead->y, lp->ptrhead->z); */
1291 		delFromList(lp, lp->ptrhead);
1292 	}
1293 	ptr = lp->eraserhead.next;
1294 	while (ptr != &lp->eraserend) {
1295 		delFromEraseList(ptr);
1296 		ptr = lp->eraserhead.next;
1297 	}
1298 	MemInit(lp);
1299 }
1300 
1301 static Bool
RunLife3D(life3dstruct * lp)1302 RunLife3D(life3dstruct * lp)
1303 {
1304 	unsigned int i, x, y, z, xc = 0, yc = 0, zc = 0;
1305 	int c;
1306 	CellList *ptr, *ptrnextcell;
1307 	Bool visible = False;
1308 
1309 	/* Step 1 - Add 1 to all neighbors of living cells. */
1310 	ptr = lp->ptrhead;
1311 	while (ptr != NULL) {
1312 		if (!IncrementNbrs3D(lp, ptr))
1313 			return False;
1314 		ptr = ptr->next;
1315 	}
1316 
1317 	/* Step 2 - Scan world and implement Survival rules. We have a list of live
1318 	 * cells, so do the following:
1319 	 * Start at the END of the list and work backwards (so we don't have to worry
1320 	 * about scanning newly created cells since they are appended to the end) and
1321 	 * for every entry, scan its neighbors for new live cells. If found, add them
1322 	 * to the end of the list. If the centre cell is dead, unlink it.
1323 	 * Make sure we do not append multiple copies of cells.
1324 	 */
1325 	ptr = lp->ptrend;
1326 	while (ptr != NULL) {
1327 		ptrnextcell = ptr->prev;
1328 		for (i = 0; i < ABS(lp->neighbors); i++) {
1329 			x = xc = ptr->x;
1330 			y = yc = ptr->y;
1331 			z = zc = ptr->z;
1332 			if (!cellCheck(lp->neighbors, x, y, z))
1333 				continue;
1334 			positionOfNeighbor(lp, i, &x, &y, &z);
1335 			if (x != xc || y != yc || z != zc) {
1336 				if (!GetMem(lp, x, y, z, &c))
1337 					return False;
1338 				if (c) {
1339 					if (CellState3D(c) == OFF) {
1340 						if (lp->param.birth & (1 << CellNbrs3D(c))) {
1341 							visible = True;
1342 							SetList(x, y, z);
1343 						} else {
1344 							if (!Reset3D(x, y, z))
1345 								return False;
1346 						}
1347 					}
1348 				}
1349 			}
1350 		}
1351 		if (!GetMem(lp, xc, yc, zc, &c))
1352 			return False;
1353 		if (lp->param.survival & (1 << CellNbrs3D(c))) {
1354 			if (!Set3D(xc, yc, zc))
1355 				return False;
1356 		} else {
1357 			if (!Reset3D(ptr->x, ptr->y, ptr->z))
1358 				return False;
1359 			delFromList(lp, ptr);
1360 		}
1361 		ptr = ptrnextcell;
1362 	}
1363 	ClearMem(lp);
1364 	if (visible)
1365 		lp->noChangeCount = 0;
1366 	else
1367 		lp->noChangeCount++;
1368 	return True;
1369 }
1370 
1371 #if 0
1372 static int
1373 CountCells3D(life3dstruct * lp)
1374 {
1375 	CellList *ptr;
1376 	int count = 0;
1377 
1378 	ptr = lp->ptrhead;
1379 	while (ptr != NULL) {
1380 		++count;
1381 		ptr = ptr->next;
1382 	}
1383 	return count;
1384 }
1385 
1386 void
1387 DisplayList(life3dstruct * lp)
1388 {
1389 	CellList *ptr;
1390 	int count = 0;
1391 
1392 	ptr = lp->ptrhead;
1393 	while (ptr != NULL) {
1394 		(void) printf("(%x)=[%d,%d,%d] ", (int) ptr,
1395 			ptr->x, ptr->y, ptr->z);
1396 		ptr = ptr->next;
1397 		++count;
1398 	}
1399 	(void) printf("Living cells = %d\n", count);
1400 }
1401 
1402 #endif
1403 
1404 #define NOSYMRAND 0
1405 #define ODDSYMRAND 1
1406 #define EVENSYMRAND 2
1407 #define ODDANTISYMRAND 3
1408 #define EVENANTISYMRAND 4
1409 #define DIAGSYMRAND 5
1410 
1411 static Bool
RandomSoup(ModeInfo * mi,int n,int vx,int vy,int vz)1412 RandomSoup(ModeInfo * mi, int n, int vx, int vy, int vz)
1413 {
1414 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
1415 	int row, col, stack;
1416 	int xrand, yrand, zrand;
1417 	int hr = lp->nrows / 2;
1418 	int hc = lp->ncols / 2;
1419 	int hs = lp->nstacks / 2;
1420 
1421 	if (glidersearch) {
1422 		xrand = NRAND(3);
1423 		yrand = NRAND(3);
1424 		zrand = NRAND(3);
1425 	} else {
1426 		xrand = NRAND(5);
1427 		yrand = NRAND(5);
1428 		zrand = NRAND(5);
1429 	}
1430 	if (glidersearch) {
1431 		/* single sym or else glider pairs will crash into each other,
1432 		hmmm, that may weed out some legitimate cases */
1433 		if (xrand > NOSYMRAND && yrand > NOSYMRAND) {
1434 			if (LRAND() & 1)
1435 				xrand = NOSYMRAND;
1436 			else
1437 				yrand = NOSYMRAND;
1438 		}
1439 		if ((xrand > NOSYMRAND || yrand > NOSYMRAND) &&
1440 				zrand > NOSYMRAND) {
1441 			if (!NRAND(3))
1442 				xrand =  yrand = NOSYMRAND;
1443 			else
1444 				zrand = NOSYMRAND;
1445 		}
1446 	}
1447 	if (!NRAND(4)) {
1448 		xrand = NOSYMRAND;
1449 		yrand = DIAGSYMRAND;
1450 		zrand = NRAND(3);
1451 	}
1452 	if (!NRAND(6)) {
1453 		/* Full diagonal */
1454 		xrand = NOSYMRAND;
1455 		yrand = NOSYMRAND;
1456 		zrand = DIAGSYMRAND;
1457 	}
1458 	if (zrand == DIAGSYMRAND) {
1459 		if ((vx != vy) || (vy != vz))
1460 			vx = vy = vz = MIN(vx, MIN(vy, vz));
1461 	} else if (yrand == DIAGSYMRAND) {
1462 		if (vx != vy)
1463 			vx = vy = MIN(vx, vy);
1464 	}
1465 	vx /= 2;
1466 	vy /= 2;
1467 	vz /= 2;
1468 	if (vx < 1)
1469 		vx = 1;
1470 	if (vy < 1)
1471 		vy = 1;
1472 	if (vz < 1)
1473 		vz = 1;
1474 
1475 	if (xrand == NOSYMRAND && yrand == NOSYMRAND && zrand == NOSYMRAND) {
1476 	    for (stack = 0; stack < 2 * vz; ++stack)
1477 		for (row = 0; row < 2 * vy; ++row)
1478 		    for (col = 0; col < 2 * vx; ++col)
1479 			if (NRAND(100) < n) {
1480 				SetList(col + hc - vx, row + hr - vy, stack + hs - vz);
1481 			}
1482 	    (void) strcpy(lp->nameString, "random pattern");
1483 	    if (MI_IS_VERBOSE(mi)) {
1484 		(void) fprintf(stdout, "%s\n", lp->nameString);
1485 	    }
1486 	    return True;
1487 	}
1488 	if (zrand == DIAGSYMRAND) {
1489 	    for (stack = 0; stack < 2 * vz; ++stack)
1490 		for (row = stack; row < 2 * vy; ++row)
1491 		    for (col = row; col < 2 * vx; ++col)
1492 			if (NRAND(100) < n) {
1493 				SetList(col + hc - vx, row + hr - vy, stack + hs - vz);
1494 				SetList(row + hc - vx, stack + hr - vy, col + hs - vz);
1495 				SetList(stack + hc - vx, col + hr - vy, row + hs - vz);
1496 			}
1497 	    (void) strcpy(lp->nameString, "full diag random pattern");
1498 	    if (MI_IS_VERBOSE(mi)) {
1499 		(void) fprintf(stdout, "%s\n", lp->nameString);
1500 	    }
1501 	    return True;
1502 	}
1503 #if 0
1504 	if (yrand == DIAGSYMRAND) {
1505 	    for (stack = 0; stack < 2 * vz; ++stack)
1506 		for (row = 0; row < 2 * vy; ++row)
1507 		    for (col = row; col < 2 * vx; ++col) {
1508 			if (NRAND(100) < n) {
1509 				SetList(col + hc - vx, row + hr - vy, stack + hs - vz);
1510 				SetList(row + hc - vx, col + hr - vy, stack + hs - vz);
1511 			}
1512 			if (zrand == ODDSYMRAND) {
1513 				SetList(col + hc - vx, row + hr - vy, vz - stack + hs);
1514 				SetList(row + hc - vx, col + hr - vy, vz - stack + hs);
1515 			} else if (zrand == EVENSYMRAND) {
1516 				SetList(col + hc - vx, row + hr - vy, vz - stack + hs - 1);
1517 				SetList(row + hc - vx, col + hr - vy, vz - stack + hs - 1);
1518 			}
1519 		    }
1520 	    (void) strcpy(lp->nameString, "diag random pattern");
1521 	    if (MI_IS_VERBOSE(mi)) {
1522 		(void) fprintf(stdout, "%s\n", lp->nameString);
1523 	    }
1524 	    return True;
1525 	}
1526 #endif
1527 	if (yrand != DIAGSYMRAND)
1528 		zrand = NOSYMRAND; /* change me */
1529 	if (yrand == NOSYMRAND) {
1530 		if (xrand == NOSYMRAND) {
1531 			(void) strcpy(lp->nameString, "full random pattern");
1532 			for (stack = hs - vz; stack < hs + vz; ++stack)
1533 			for (row = hr - vy; row < hr + vy; ++row)
1534 			for (col = hc - vx; col < hc + vx; ++col) {
1535 
1536 					if (NRAND(100) < n) {
1537 						SetList(col, row, stack);
1538 					}
1539 				}
1540 		} else if (xrand == ODDSYMRAND) {
1541 				(void) sprintf(lp->nameString, "%s %srandom pattern", "x", "odd ");
1542 			for (stack = hs - vz; stack < hs + vz; ++stack)
1543 			for (row = hr - vy; row < hr + vy; ++row)
1544 			for (col = hc; col < hc + vx; ++col) {
1545 
1546 					if (NRAND(100) < n / 2) {
1547 						SetList(col, row, stack);
1548 						SetList(2 * hc - col, row, stack);
1549 					}
1550 				}
1551 		} else if (xrand == EVENSYMRAND) {
1552 			(void) sprintf(lp->nameString, "%s even random pattern", "x");
1553 			for (stack = hs - vz; stack < hs + vz; ++stack)
1554 			for (row = hr - vy; row < hr + vy; ++row)
1555 			for (col = hc; col < hc + vx; ++col) {
1556 
1557 					if (NRAND(100) < n / 2) {
1558 						SetList(col, row, stack);
1559 						SetList(2 * hc - col - 1, row, stack);
1560 					}
1561 				}
1562 		} else if (xrand == ODDANTISYMRAND) {
1563 			(void) sprintf(lp->nameString, "%s %srandom pattern", "x", "odd antisym ");
1564 			for (stack = hs - vz; stack < hs + vz; ++stack)
1565 			for (row = hr - vy; row < hr + vy; ++row)
1566 			for (col = hc; col < hc + vx; ++col) {
1567 
1568 					if (NRAND(100) < n / 2) {
1569 						SetList(col, row, stack);
1570 						SetList(2 * hc - col, 2 * hr - row - 1, stack);
1571 					}
1572 				}
1573 		} else if (xrand == EVENANTISYMRAND) {
1574 			(void) sprintf(lp->nameString, "%s even antisym pattern", "x");
1575 			for (stack = hs - vz; stack < hs + vz; ++stack)
1576 			for (row = hr - vy; row < hr + vy; ++row)
1577 			for (col = hc; col < hc + vx; ++col) {
1578 
1579 					if (NRAND(100) < n / 2) {
1580 						SetList(col, row, stack);
1581 						SetList(2 * hc - col - 1, 2 * hr - row - 1, stack);
1582 					}
1583 				}
1584 		}
1585 	} else if (yrand == ODDSYMRAND) {
1586 		if (xrand == NOSYMRAND) {
1587 			(void) sprintf(lp->nameString, "%s odd random pattern", "y");
1588 			for (stack = hs - vz; stack < hs + vz; ++stack)
1589 			for (row = hr; row < hr + vy; ++row)
1590 			for (col = hc - vx; col < hc + vx; ++col) {
1591 
1592 					if (NRAND(100) < n / 2) {
1593 						SetList(col, row, stack);
1594 						SetList(col, 2 * hr - row, stack);
1595 					}
1596 				}
1597 		} else if (xrand == ODDSYMRAND) {
1598 			(void) sprintf(lp->nameString, "x %sand y odd random pattern", "");
1599 			for (stack = hs - vz; stack < hs + vz; ++stack)
1600 			for (row = hr; row < hr + vy; ++row)
1601 			for (col = hc; col < hc + vx; ++col) {
1602 
1603 					if (NRAND(100) < n / 4) {
1604 						SetList(col, row, stack);
1605 						SetList(col, 2 * hr - row, stack);
1606 						SetList(2 * hc - col, row, stack);
1607 						SetList(2 * hc - col, 2 * hr - row, stack);
1608 					}
1609 				}
1610 		} else if (xrand == EVENSYMRAND) {
1611 			(void) sprintf(lp->nameString, "%s random pattern", "x even and y odd");
1612 			for (stack = hs - vz; stack < hs + vz; ++stack)
1613 			for (row = hr; row < hr + vy; ++row)
1614 			for (col = hc; col < hc + vx; ++col) {
1615 					if (NRAND(100) < n / 2) {
1616 						SetList(col, row, stack);
1617 						SetList(2 * hc - col - 1, row, stack);
1618 						SetList(col, 2 * hr - row, stack);
1619 						SetList(2 * hc - col - 1, 2 * hr - row, stack);
1620 					}
1621 				}
1622 		}
1623 	} else if (yrand == EVENSYMRAND) {
1624 		if (xrand == NOSYMRAND) {
1625 			(void) sprintf(lp->nameString, "%s even random pattern", "y");
1626 			for (stack = hs - vz; stack < hs + vz; ++stack)
1627 			for (row = hr; row < hr + vy; ++row)
1628 			for (col = hc - vx; col < hc + vx; ++col) {
1629 					if (NRAND(100) < n / 2) {
1630 						SetList(col, row, stack);
1631 						SetList(col, 2 * hr - row - 1, stack);
1632 					}
1633 				}
1634 		} else if (xrand == ODDSYMRAND) {
1635 			(void) sprintf(lp->nameString, "%s random pattern", "x odd and y even");
1636 			for (stack = hs - vz; stack < hs + vz; ++stack)
1637 			for (row = hr; row < hr + vy; ++row)
1638 			for (col = hc; col < hc + vx; ++col) {
1639 					if (NRAND(100) < n / 4) {
1640 						SetList(col, row, stack);
1641 						SetList(2 * hc - col, row, stack);
1642 						SetList(col, 2 * hr - row - 1, stack);
1643 						SetList(2 * hc - col, 2 * hr - row - 1, stack);
1644 					}
1645 				}
1646 		} else if (xrand == EVENSYMRAND) {
1647 			(void) sprintf(lp->nameString, "x and y even random pattern");
1648 			for (stack = hs - vz; stack < hs + vz; ++stack)
1649 			for (row = hr; row < hr + vy; ++row)
1650 			for (col = hc; col < hc + vx; ++col) {
1651 					if (NRAND(100) < n / 4) {
1652 						SetList(col, row, stack);
1653 						SetList(2 * hc - col - 1, row, stack);
1654 						SetList(col, 2 * hr - row - 1, stack);
1655 						SetList(2 * hc - col - 1, 2 * hr - row - 1, stack);
1656 					}
1657 				}
1658 		}
1659 	} else if (yrand == ODDANTISYMRAND) {
1660 		if (xrand == NOSYMRAND) {
1661 			(void) sprintf(lp->nameString, "%s odd antisym pattern", "y");
1662 			for (stack = hs - vz; stack < hs + vz; ++stack)
1663 			for (row = hr; row < hr + vy; ++row)
1664 			for (col = hc - vx; col < hc + vx; ++col) {
1665 
1666 					if (NRAND(100) < n / 2) {
1667 						SetList(col, row, stack);
1668 						SetList(2 * hc - col - 1, 2 * hr - row, stack);
1669 					}
1670 				}
1671 		}
1672 	} else if (yrand == EVENANTISYMRAND) {
1673 		if (xrand == NOSYMRAND) {
1674 			(void) sprintf(lp->nameString, "%s even antisym pattern", "y");
1675 			for (stack = hs - vz; stack < hs + vz; ++stack)
1676 			for (row = hr; row < hr + vy; ++row)
1677 			for (col = hc - vx; col < hc + vx; ++col) {
1678 
1679 					if (NRAND(100) < n / 2) {
1680 						SetList(col, row, stack);
1681 						SetList(2 * hc - col - 1, 2 * hr - row - 1, stack);
1682 					}
1683 				}
1684 		}
1685 	} else if (yrand == DIAGSYMRAND) {
1686 		if (glidersearch) {
1687 			/* single sym or else glider pairs will crash into each other */
1688 			xrand = NRAND(2);
1689 		} else {
1690 			xrand = NRAND(4);
1691 		}
1692 		vx = MIN(vx, vy);
1693 		if (xrand == 0) {
1694 			(void) strcpy(lp->nameString, "\\ random pattern");
1695 			for (stack = hs - vz; stack < hs + vz; ++stack)
1696 			for (row = hr - vx; row < hr + vx; ++row) {
1697 				for (col = row - hr + hc; col < hc + vx; ++col) {
1698 					if (NRAND(100) < n / 2) {
1699 						SetList(col, row, stack);
1700 						SetList(row + hc - hr, col + hr - hc, stack);
1701 					}
1702 				}
1703 			}
1704 		} else if (xrand == 1) {
1705 			(void) strcpy(lp->nameString, "/ random pattern");
1706 			for (stack = hs - vz; stack < hs + vz; ++stack)
1707 			for (row = hr - vx; row < hr + vx; ++row) {
1708 				for (col = row - hr + hc; col < hc + vx; ++col) {
1709 					if (NRAND(100) < n / 2) {
1710 						SetList(2 * hc - col, row, stack);
1711 						SetList(hc - row + hr, col + hr - hc, stack);
1712 					}
1713 				}
1714 			}
1715 		} else if (xrand == 2) {
1716 			(void) strcpy(lp->nameString, "symmetric diagonal random pattern");
1717 			for (stack = hs - vz; stack < hs + vz; ++stack)
1718 			for (row = hr - vx; row < hr + vx; ++row) {
1719 				for (col = row - hr + hc; col < hc + vx; ++col) {
1720 					if (NRAND(100) < n / 4) {
1721 						SetList(2 * hc - col, row, stack);
1722 						SetList(hc - row + hr, col + hr - hc, stack);
1723 						SetList(col, 2 * hr - row, stack);
1724 						SetList(row + hc - hr, hr - col + hc, stack);
1725 					}
1726 				}
1727 			}
1728 		} else if (xrand == 3) {
1729 			(void) strcpy(lp->nameString, "full symmetric random pattern");
1730 			for (stack = hs - vz; stack < hs + vz; ++stack)
1731 			for (row = hr; row < hr + vx; ++row) {
1732 				for (col = row - hr + hc; col < hc + vx; ++col) {
1733 					if (NRAND(100) < n / 8) {
1734 						SetList(col, row, stack);
1735 						SetList(row + hc - hr, col + hr - hc, stack);
1736 						SetList(2 * hc - col, row, stack);
1737 						SetList(hc - row + hr, col + hr - hc, stack);
1738 						SetList(col, 2 * hr - row, stack);
1739 						SetList(row + hc - hr, hr - col + hc, stack);
1740 						SetList(2 * hc - col, 2 * hr - row, stack);
1741 						SetList(hc - row + hr, hr - col + hc, stack);
1742 					}
1743 				}
1744 			}
1745 		}
1746 	}
1747 
1748 
1749 	if (MI_IS_VERBOSE(mi)) {
1750 		(void) fprintf(stdout, "%s\n", lp->nameString);
1751 	}
1752 	return True;
1753 }
1754 
1755 static Bool
GetPattern(ModeInfo * mi,int pattern_rule,int pattern)1756 GetPattern(ModeInfo * mi, int pattern_rule, int pattern)
1757 {
1758 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
1759 	int x, y, z, orient, temp;
1760 	char *patptr = (char *) NULL;
1761 #ifdef LIFE_NAMES
1762 	int pat = 2 * pattern + 1;
1763 	char * patstrg = (char *) "";
1764 #else
1765 	int pat = pattern;
1766 #endif
1767 
1768 	if (filePattern) {
1769 		patptr = &filePattern[0];
1770 #ifdef LIFE_NAMES
1771 		(void) strcpy(lp->nameString, patstrg);
1772 #endif
1773 	} else {
1774 		switch (lp->neighbors) {
1775 		case 12:
1776 			switch (pattern_rule) {
1777 			case LIFE_12B3S3:
1778 				patptr = &patterns_12B3S3[pat][0];
1779 #ifdef LIFE_NAMES
1780 				patstrg = &patterns_12B3S3[2 * pattern][0];
1781 #endif
1782 				break;
1783 			case LIFE_12B3S456:
1784 				patptr = &patterns_12B3S456[pat][0];
1785 #ifdef LIFE_NAMES
1786 				patstrg = &patterns_12B3S456[2 * pattern][0];
1787 #endif
1788 				break;
1789 			}
1790 			break;
1791 		case 14:
1792 			switch (pattern_rule) {
1793 			case LIFE_14B4S34:
1794 				patptr = &patterns_14B4S34[pat][0];
1795 #ifdef LIFE_NAMES
1796 				patstrg = &patterns_14B4S34[2 * pattern][0];
1797 #endif
1798 				break;
1799 			case LIFE_14B46S34:
1800 				patptr = &patterns_14B46S34[pat][0];
1801 #ifdef LIFE_NAMES
1802 				patstrg = &patterns_14B46S34[2 * pattern][0];
1803 #endif
1804 				break;
1805 			case LIFE_14B45S56:
1806 				patptr = &patterns_14B45S56[pat][0];
1807 #ifdef LIFE_NAMES
1808 				patstrg = &patterns_14B45S56[2 * pattern][0];
1809 #endif
1810 				break;
1811 			}
1812 			break;
1813 		case 18:
1814 			switch (pattern_rule) {
1815 			case LIFE_18B4S45:
1816 				patptr = &patterns_18B4S45[pat][0];
1817 #ifdef LIFE_NAMES
1818 				patstrg = &patterns_18B4S45[2 * pattern][0];
1819 #endif
1820 				break;
1821 			}
1822 			break;
1823 		case -18:
1824 			switch (pattern_rule) {
1825 			case LIFE__18B4S36:
1826 				patptr = &patterns__18B4S36[pat][0];
1827 #ifdef LIFE_NAMES
1828 				patstrg = &patterns__18B4S36[2 * pattern][0];
1829 #endif
1830 				break;
1831 			case LIFE__18B4S45:
1832 				patptr = &patterns__18B4S45[pat][0];
1833 #ifdef LIFE_NAMES
1834 				patstrg = &patterns__18B4S45[2 * pattern][0];
1835 #endif
1836 				break;
1837 			case LIFE__18B4S456:
1838 				patptr = &patterns__18B4S456[pat][0];
1839 #ifdef LIFE_NAMES
1840 				patstrg = &patterns__18B4S456[2 * pattern][0];
1841 #endif
1842 				break;
1843 			case LIFE__18B4S46:
1844 				patptr = &patterns__18B4S46[pat][0];
1845 #ifdef LIFE_NAMES
1846 				patstrg = &patterns__18B4S46[2 * pattern][0];
1847 #endif
1848 				break;
1849 			}
1850 			break;
1851 		case 20:
1852 			switch (pattern_rule) {
1853 			case LIFE_20B4S45:
1854 				patptr = &patterns_20B4S45[pat][0];
1855 #ifdef LIFE_NAMES
1856 				patstrg = &patterns_20B4S45[2 * pattern][0];
1857 #endif
1858 				break;
1859 			}
1860 			break;
1861 		case 22:
1862 			switch (pattern_rule) {
1863 			case LIFE_22B4S3:
1864 				patptr = &patterns_22B4S3[pat][0];
1865 #ifdef LIFE_NAMES
1866 				patstrg = &patterns_22B4S3[2 * pattern][0];
1867 #endif
1868 				break;
1869 			case LIFE_22B4S4:
1870 				patptr = &patterns_22B4S4[pat][0];
1871 #ifdef LIFE_NAMES
1872 				patstrg = &patterns_22B4S4[2 * pattern][0];
1873 #endif
1874 				break;
1875 			case LIFE_22B4S57:
1876 				patptr = &patterns_22B4S57[pat][0];
1877 #ifdef LIFE_NAMES
1878 				patstrg = &patterns_22B4S57[2 * pattern][0];
1879 #endif
1880 				break;
1881 			}
1882 			break;
1883 		case 26:
1884 			switch (pattern_rule) {
1885 			case LIFE_26B5S23:
1886 				patptr = &patterns_26B5S23[pat][0];
1887 #ifdef LIFE_NAMES
1888 				patstrg = &patterns_26B5S23[2 * pattern][0];
1889 #endif
1890 				break;
1891 			case LIFE_26B5S25:
1892 				patptr = &patterns_26B5S25[pat][0];
1893 #ifdef LIFE_NAMES
1894 				patstrg = &patterns_26B5S25[2 * pattern][0];
1895 #endif
1896 				break;
1897 			case LIFE_26B5S27:
1898 				patptr = &patterns_26B5S27[pat][0];
1899 #ifdef LIFE_NAMES
1900 				patstrg = &patterns_26B5S27[2 * pattern][0];
1901 #endif
1902 				break;
1903 			case LIFE_26B5S35:
1904 				patptr = &patterns_26B5S35[pat][0];
1905 #ifdef LIFE_NAMES
1906 				patstrg = &patterns_26B5S35[2 * pattern][0];
1907 #endif
1908 				break;
1909 			case LIFE_26B5S36:
1910 				patptr = &patterns_26B5S36[pat][0];
1911 #ifdef LIFE_NAMES
1912 				patstrg = &patterns_26B5S36[2 * pattern][0];
1913 #endif
1914 				break;
1915 			case LIFE_26B5S37:
1916 				patptr = &patterns_26B5S37[pat][0];
1917 #ifdef LIFE_NAMES
1918 				patstrg = &patterns_26B5S37[2 * pattern][0];
1919 #endif
1920 				break;
1921 			case LIFE_26B5S38:
1922 				patptr = &patterns_26B5S38[pat][0];
1923 #ifdef LIFE_NAMES
1924 				patstrg = &patterns_26B5S38[2 * pattern][0];
1925 #endif
1926 				break;
1927 			case LIFE_26B5S4:
1928 				patptr = &patterns_26B5S4[pat][0];
1929 #ifdef LIFE_NAMES
1930 				patstrg = &patterns_26B5S4[2 * pattern][0];
1931 #endif
1932 				break;
1933 			case LIFE_26B5S45:
1934 				patptr = &patterns_26B5S45[pat][0];
1935 #ifdef LIFE_NAMES
1936 				patstrg = &patterns_26B5S45[2 * pattern][0];
1937 #endif
1938 				break;
1939 			case LIFE_26B5S47:
1940 				patptr = &patterns_26B5S47[pat][0];
1941 #ifdef LIFE_NAMES
1942 				patstrg = &patterns_26B5S47[2 * pattern][0];
1943 #endif
1944 				break;
1945 			case LIFE_26B5S56:
1946 				patptr = &patterns_26B5S56[pat][0];
1947 #ifdef LIFE_NAMES
1948 				patstrg = &patterns_26B5S56[2 * pattern][0];
1949 #endif
1950 				break;
1951 			case LIFE_26B5S58:
1952 				patptr = &patterns_26B5S58[pat][0];
1953 #ifdef LIFE_NAMES
1954 				patstrg = &patterns_26B5S58[2 * pattern][0];
1955 #endif
1956 				break;
1957 			case LIFE_26B5S678:
1958 				patptr = &patterns_26B5S678[pat][0];
1959 #ifdef LIFE_NAMES
1960 				patstrg = &patterns_26B5S678[2 * pattern][0];
1961 #endif
1962 				break;
1963 			case LIFE_26B5S8:
1964 				patptr = &patterns_26B5S8[pat][0];
1965 #ifdef LIFE_NAMES
1966 				patstrg = &patterns_26B5S8[2 * pattern][0];
1967 #endif
1968 				break;
1969 			case LIFE_26B58S58:
1970 				patptr = &patterns_26B58S58[pat][0];
1971 #ifdef LIFE_NAMES
1972 				patstrg = &patterns_26B58S58[2 * pattern][0];
1973 #endif
1974 				break;
1975 			case LIFE_26B6S567:
1976 				patptr = &patterns_26B6S567[pat][0];
1977 #ifdef LIFE_NAMES
1978 				patstrg = &patterns_26B6S567[2 * pattern][0];
1979 #endif
1980 				break;
1981 			case LIFE_26B6S57:
1982 				patptr = &patterns_26B6S57[pat][0];
1983 #ifdef LIFE_NAMES
1984 				patstrg = &patterns_26B6S57[2 * pattern][0];
1985 #endif
1986 				break;
1987 			case LIFE_26B67S57:
1988 				patptr = &patterns_26B67S57[pat][0];
1989 #ifdef LIFE_NAMES
1990 				patstrg = &patterns_26B67S57[2 * pattern][0];
1991 #endif
1992 				break;
1993 			case LIFE_26B67S67:
1994 				patptr = &patterns_26B67S67[pat][0];
1995 #ifdef LIFE_NAMES
1996 				patstrg = &patterns_26B67S67[2 * pattern][0];
1997 #endif
1998 				break;
1999 			}
2000 			break;
2001 		}
2002 #ifdef LIFE_NAMES
2003 		(void) strcpy(lp->nameString, patstrg);
2004 #endif
2005 	}
2006 #ifdef DEBUG
2007 	orient = 0;
2008 #else
2009 	orient = NRAND(24);
2010 #endif
2011 	if (MI_IS_VERBOSE(mi) && !filePattern) {
2012 #ifdef LIFE_NAMES
2013 		(void) fprintf(stdout, "%s, ", patstrg);
2014 #endif
2015 		(void) fprintf(stdout, "table number %d\n", pattern);
2016 	}
2017 	while ((x = *patptr++) != 127) {
2018 		y = *patptr++;
2019 		z = *patptr++;
2020 		if (orient >= 16) {
2021 			temp = x;
2022 			x = y;
2023 			if (lp->neighbors == 14) {
2024 				y = (z >= 0) ? z / 2 : (z - 1) / 2;
2025 				z = (z & 1) + 2 * temp;
2026 			} else {
2027 				y = z;
2028 				z = temp;
2029 			}
2030 		} else if (orient >= 8) {
2031 			temp = x;
2032 			if (lp->neighbors == 14) {
2033 				x = (z >= 0) ? z / 2 : (z - 1) / 2;
2034 				z = (z & 1) + 2 * y;
2035 			} else {
2036 				x = z;
2037 				z = y;
2038 			}
2039 			y = temp;
2040 		}
2041 		if (orient % 8 >= 4) {
2042 			if (lp->neighbors == 14 && (z & 1) == 1)
2043 				x = -x - 1;
2044 			else
2045 				x = -x;
2046 		}
2047 		if (orient % 4 >= 2) {
2048 			if (lp->neighbors == 14 && (z & 1) == 1)
2049 				y = -y - 1;
2050 			else
2051 				y = -y;
2052 		}
2053 		if (orient % 2 == 1) {
2054 			z = -z;
2055 		}
2056 
2057 		x += lp->ncols / 2;
2058 		y += lp->nrows / 2;
2059 		z += lp->nstacks / 2;
2060 		if (x >= 0 && y >= 0 && z >= 0 &&
2061 		    x < lp->ncols && y < lp->nrows && z < lp->nstacks) {
2062 			SetList(x, y, z);
2063 		}
2064 	}
2065 	return True;
2066 }
2067 
2068 static void
NewViewpoint(life3dstruct * lp,double x,double y,double z)2069 NewViewpoint(life3dstruct * lp, double x, double y, double z)
2070 {
2071 	double k, l, d1, d2;
2072 
2073 	k = x * x + y * y;
2074 	l = sqrt(k + z * z);
2075 	k = sqrt(k);
2076 	d1 = (EyeToScreen / HalfScreenD);
2077 	d2 = EyeToScreen / (HalfScreenD * lp->height / lp->width);
2078 	lp->A = d1 * l * (lp->width / 2) / k;
2079 	lp->B = l * l;
2080 	lp->C = d2 * (lp->height / 2) / k;
2081 	lp->F = k * k;
2082 }
2083 
2084 static void
lissajous(life3dstruct * lp)2085 lissajous(life3dstruct * lp)
2086 {
2087 	double alt, azm, dist;
2088 
2089 	alt = 30.0 * sin(lp->metaAlt * IP) + 45.0;
2090 	lp->metaAlt += 1.123;
2091 	if (lp->metaAlt >= 360.0)
2092 		lp->metaAlt -= 360.0;
2093 	if (lp->metaAlt < 0.0)
2094 		lp->metaAlt += 360.0;
2095 	azm = 30.0 * sin(lp->metaAzm * IP) + 45.0;
2096 	lp->metaAzm += 0.987;
2097 	if (lp->metaAzm >= 360.0)
2098 		lp->metaAzm -= 360.0;
2099 	if (lp->metaAzm < 0.0)
2100 		lp->metaAzm += 360.0;
2101 	dist = 10.0 * sin(lp->metaDist * IP) + 50.0;
2102 	lp->metaDist += 1.0;
2103 	if (lp->metaDist >= 360.0)
2104 		lp->metaDist -= 360.0;
2105 	if (lp->metaDist < 0.0)
2106 		lp->metaDist += 360.0;
2107 #if 0
2108 	if (alt >= 90.0)
2109 		alt = 90.0;
2110 	else if (alt < -90.0)
2111 		alt = -90.0;
2112 #endif
2113 	lp->azm = azm;
2114 #ifdef DEBUG
2115 	(void) printf("dist %g, alt %g, azm %g\n", dist, alt, azm);
2116 #endif
2117 	lp->vx = (sin(azm * IP) * cos(alt * IP) * dist);
2118 	lp->vy = (cos(azm * IP) * cos(alt * IP) * dist);
2119 	lp->vz = (sin(alt * IP) * dist);
2120 	NewViewpoint(lp, lp->vx, lp->vy, lp->vz);
2121 }
2122 
2123 static void
NewPoint(life3dstruct * lp,double x,double y,double z,register XPoint * pts)2124 NewPoint(life3dstruct * lp, double x, double y, double z,
2125 		register XPoint * pts)
2126 {
2127 	double p1, E;
2128 
2129 	p1 = x * lp->vx + y * lp->vy;
2130 	E = lp->B - p1 - z * lp->vz;
2131 	pts->x = (int) (lp->width / 2 -
2132 		lp->A * (lp->vx * y - lp->vy * x) / E);
2133 	pts->y = (int) (lp->height / 2 -
2134 		lp->C * (z * lp->F - lp->vz * p1) / E);
2135 }
2136 
2137 
2138 /* Chain together all cells that are at the same distance.
2139  * These cannot mutually overlap. */
2140 static void
SortList(life3dstruct * lp)2141 SortList(life3dstruct * lp)
2142 {
2143 	short dist;
2144 	double d, x, y, z, rsize;
2145 	int i, r;
2146 	XPoint point;
2147 	CellList *ptr;
2148 
2149 	for (i = 0; i < NBUCKETS; ++i)
2150 		lp->buckethead[i] = lp->bucketend[i] = (CellList *) NULL;
2151 
2152 	/* Calculate distances and re-arrange pointers to chain off buckets */
2153 	ptr = lp->ptrhead;
2154 	while (ptr != NULL) {
2155 
2156 		x = (double) ptr->x - lp->ox;
2157 		y = (double) ptr->y - lp->oy;
2158 		z = (double) ptr->z - lp->oz;
2159 		d = Distance(lp->vx, lp->vy, lp->vz, x, y, z);
2160 		if (lp->vx * (lp->vx - x) + lp->vy * (lp->vy - y) +
2161 		    lp->vz * (lp->vz - z) > 0 && d > 1.5)
2162 			ptr->visible = 1;
2163 		else
2164 			ptr->visible = 0;
2165 
2166 		ptr->dist = (short) d;
2167 		dist = (short) (d * BUCKETSIZE);
2168 		if (dist > NBUCKETS - 1)
2169 			dist = NBUCKETS - 1;
2170 
2171 		if (lp->buckethead[dist] == NULL) {
2172 			lp->buckethead[dist] = lp->bucketend[dist] = ptr;
2173 			ptr->priority = (struct _CellList *) NULL;
2174 		} else {
2175 			lp->bucketend[dist]->priority = ptr;
2176 			lp->bucketend[dist] = ptr;
2177 			lp->bucketend[dist]->priority =
2178 				(struct _CellList *) NULL;
2179 		}
2180 		ptr = ptr->next;
2181 	}
2182 
2183 	/* Check for invisibility */
2184 	rsize = 0.47 * lp->width / ((double) HalfScreenD * 2);
2185 	i = (int) lp->azm;
2186 	if (i < 0)
2187 		i = -i;
2188 	i = i % RT_ANGLE;
2189 	if (i > HALFRT_ANGLE)
2190 		i = RT_ANGLE - i;
2191 	rsize /= cos(i * IP);
2192 
2193 	lp->visible = 0;
2194 	for (i = 0; i < NBUCKETS; ++i)
2195 		if (lp->buckethead[i] != NULL) {
2196 			ptr = lp->buckethead[i];
2197 			while (ptr != NULL) {
2198 				if (ptr->visible) {
2199 					x = (double) ptr->x - lp->ox;
2200 					y = (double) ptr->y - lp->oy;
2201 					z = (double) ptr->z - lp->oz;
2202 					NewPoint(lp, x, y, z, &point);
2203 					r = (int) (rsize * (double) EyeToScreen / (double) ptr->dist);
2204 					if (point.x + r >= 0 && point.y + r >= 0 &&
2205 					    point.x - r < lp->width && point.y - r < lp->height)
2206 						lp->visible = 1;
2207 				}
2208 				ptr = ptr->priority;
2209 			}
2210 		}
2211 }
2212 
2213 static void
drawTri(ModeInfo * mi,int color,XPoint * pts,int p1,int p2,int p3)2214 drawTri(ModeInfo * mi, int color, XPoint * pts,
2215 		int p1, int p2, int p3)
2216 {
2217 	Display *display = MI_DISPLAY(mi);
2218 	GC gc = MI_GC(mi);
2219 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2220 	XPoint facepts[4];
2221 
2222 	facepts[0] = pts[p1];
2223 	facepts[1] = pts[p2];
2224 	facepts[2] = pts[p3];
2225 	facepts[3] = pts[p1];
2226 
2227 	if (color != BLACK && MI_NPIXELS(mi) <= 2)
2228 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2229 	else
2230 		XSetForeground(display, gc, lp->colors[color]);
2231 	if (!lp->wireframe) {
2232 		if (color != BLACK && MI_NPIXELS(mi) <= 2) {
2233 			XGCValues gcv;
2234 
2235 			gcv.stipple = lp->pixmaps[lp->colors[color]];
2236 			gcv.foreground = MI_WHITE_PIXEL(mi);
2237 			gcv.background = MI_BLACK_PIXEL(mi);
2238 			XChangeGC(display, lp->stippledGC,
2239 				GCStipple | GCForeground | GCBackground, &gcv);
2240 			gc = lp->stippledGC;
2241 		}
2242 		XFillPolygon(display, (Drawable) lp->dbuf, gc, facepts, 3,
2243 			Convex, CoordModeOrigin);
2244 		gc = MI_GC(mi);
2245 		if (color == BLACK || MI_NPIXELS(mi) <= 2) {
2246 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
2247 		} else {
2248 			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2249 		}
2250 	}
2251 	XDrawLines(display, (Drawable) lp->dbuf, gc, facepts, 4,
2252 		CoordModeOrigin);
2253 }
2254 static void
drawQuad(ModeInfo * mi,int color,XPoint * pts,int p1,int p2,int p3,int p4)2255 drawQuad(ModeInfo * mi, int color, XPoint * pts,
2256 		int p1, int p2, int p3, int p4)
2257 {
2258 	Display *display = MI_DISPLAY(mi);
2259 	GC gc = MI_GC(mi);
2260 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2261 	XPoint facepts[5];
2262 
2263 	facepts[0] = pts[p1];
2264 	facepts[1] = pts[p2];
2265 	facepts[2] = pts[p3];
2266 	facepts[3] = pts[p4];
2267 	facepts[4] = pts[p1];
2268 
2269 	if (color != BLACK && MI_NPIXELS(mi) <= 2)
2270 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2271 	else
2272 		XSetForeground(display, gc, lp->colors[color]);
2273 	if (!lp->wireframe) {
2274 		if (color != BLACK && MI_NPIXELS(mi) <= 2) {
2275 			XGCValues gcv;
2276 
2277 			gcv.stipple = lp->pixmaps[lp->colors[color]];
2278 			gcv.foreground = MI_WHITE_PIXEL(mi);
2279 			gcv.background = MI_BLACK_PIXEL(mi);
2280 			XChangeGC(display, lp->stippledGC,
2281 				GCStipple | GCForeground | GCBackground, &gcv);
2282 			gc = lp->stippledGC;
2283 		}
2284 		XFillPolygon(display, (Drawable) lp->dbuf, gc, facepts, 4,
2285 			Convex, CoordModeOrigin);
2286 		gc = MI_GC(mi);
2287 		if (color == BLACK || MI_NPIXELS(mi) <= 2) {
2288 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
2289 		} else {
2290 			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2291 		}
2292 	}
2293 	XDrawLines(display, (Drawable) lp->dbuf, gc, facepts, 5,
2294 		CoordModeOrigin);
2295 }
2296 
2297 static void
drawHex(ModeInfo * mi,int color,XPoint * pts,int p1,int p2,int p3,int p4,int p5,int p6)2298 drawHex(ModeInfo * mi, int color, XPoint * pts,
2299 		int p1, int p2, int p3, int p4, int p5, int p6)
2300 {
2301 	Display *display = MI_DISPLAY(mi);
2302 	GC gc = MI_GC(mi);
2303 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2304 	XPoint facepts[7];
2305 
2306 	facepts[0] = pts[p1];
2307 	facepts[1] = pts[p2];
2308 	facepts[2] = pts[p3];
2309 	facepts[3] = pts[p4];
2310 	facepts[4] = pts[p5];
2311 	facepts[5] = pts[p6];
2312 	facepts[6] = pts[p1];
2313 
2314 	if (color != BLACK && MI_NPIXELS(mi) <= 2)
2315 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2316 	else
2317 		XSetForeground(display, gc, lp->colors[color]);
2318 	if (!lp->wireframe) {
2319 		if (color != BLACK && MI_NPIXELS(mi) <= 2) {
2320 			XGCValues gcv;
2321 
2322 			gcv.stipple = lp->pixmaps[lp->colors[color]];
2323 			gcv.foreground = MI_WHITE_PIXEL(mi);
2324 			gcv.background = MI_BLACK_PIXEL(mi);
2325 			XChangeGC(display, lp->stippledGC,
2326 				GCStipple | GCForeground | GCBackground, &gcv);
2327 			gc = lp->stippledGC;
2328 		}
2329 		XFillPolygon(display, (Drawable) lp->dbuf, gc, facepts, 6,
2330 			Convex, CoordModeOrigin);
2331 		gc = MI_GC(mi);
2332 		if (color == BLACK || MI_NPIXELS(mi) <= 2) {
2333 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
2334 		} else {
2335 			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
2336 		}
2337 	}
2338 	XDrawLines(display, (Drawable) lp->dbuf, gc, facepts, 7,
2339 		CoordModeOrigin);
2340 }
2341 
2342 #define LEN_2 0.225
2343 #define LEN 0.45
2344 #define LEN2 0.9
2345 
2346 static int
DrawCube(ModeInfo * mi,CellList * cell)2347 DrawCube(ModeInfo * mi, CellList * cell)
2348 {
2349 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2350 	XPoint pts[8];	/* screen coords for point */
2351 	int i = 0, out = 0;
2352 	unsigned int mask;
2353 	double x, y, z;
2354 	double dx, dy, dz;
2355 
2356 	x = (double) cell->x - lp->ox;
2357 	y = (double) cell->y - lp->oy;
2358 	z = (double) cell->z - lp->oz;
2359 	for (dz = z - LEN; dz <= z + LEN2; dz += LEN2)
2360 		for (dy = y - LEN; dy <= y + LEN2; dy += LEN2)
2361 			for (dx = x - LEN; dx <= x + LEN2; dx += LEN2) {
2362 				NewPoint(lp, dx, dy, dz, &pts[i]);
2363 				if (pts[i].x < 0 ||
2364 				    pts[i].x >= lp->width ||
2365 				    pts[i].y < 0 ||
2366 				    pts[i].y >= lp->height)
2367 					++out;
2368 				++i;
2369 			}
2370 	if (out == 8)
2371 		return (0);
2372 
2373 	if (cell->visible)
2374 		mask = 0xFFFF;
2375 	else
2376 		mask = 0x0;
2377 	/* Only draw those faces that are visible */
2378 	dx = lp->vx - x;
2379 	dy = lp->vy - y;
2380 	dz = lp->vz - z;
2381 	/*
2382 		6----7
2383 	       /    /|
2384 	      /    / |
2385 	     2----3  |
2386 	    |  4 |  5
2387 	    |    | /
2388 	    0____1/
2389 	*/
2390 	if (lp->wireframe) {
2391 		if (dz <= LEN)
2392 			drawQuad(mi, (int) (BLUE & mask), pts, 4, 5, 7, 6);
2393 		else if (dz >= -LEN)
2394 			drawQuad(mi, (int) (BLUE & mask), pts, 0, 1, 3, 2);
2395 		if (dx <= LEN)
2396 			drawQuad(mi, (int) (GREEN & mask), pts, 1, 3, 7, 5);
2397 		else if (dx >= -LEN)
2398 			drawQuad(mi, (int) (GREEN & mask), pts, 0, 2, 6, 4);
2399 		if (dy <= LEN)
2400 			drawQuad(mi, (int) (RED & mask), pts, 2, 3, 7, 6);
2401 		else if (dy >= -LEN)
2402 			drawQuad(mi, (int) (RED & mask), pts, 0, 1, 5, 4);
2403 	}
2404 	if (dz > LEN)
2405 		drawQuad(mi, (int) (BLUE & mask), pts, 4, 5, 7, 6);
2406 	else if (dz < -LEN)
2407 		drawQuad(mi, (int) (BLUE & mask), pts, 0, 1, 3, 2);
2408 	if (dx > LEN)
2409 		drawQuad(mi, (int) (GREEN & mask), pts, 1, 3, 7, 5);
2410 	else if (dx < -LEN)
2411 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 2, 6, 4);
2412 	if (dy > LEN)
2413 		drawQuad(mi, (int) (RED & mask), pts, 2, 3, 7, 6);
2414 	else if (dy < -LEN)
2415 		drawQuad(mi, (int) (RED & mask), pts, 0, 1, 5, 4);
2416 	return (1);
2417 }
2418 
2419 static int
DrawTetrahedronTrBl(ModeInfo * mi,CellList * cell)2420 DrawTetrahedronTrBl(ModeInfo * mi, CellList * cell)
2421 {
2422 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2423 	XPoint pts[4];	/* screen coords for point */
2424 	int i = 0, out = 0;
2425 	unsigned int mask;
2426 	double x, y, z;
2427 	double dx, dy, dz;
2428 
2429 	x = (double) cell->x - lp->ox;
2430 	y = (double) cell->y - lp->oy;
2431 	z = (double) cell->z - lp->oz;
2432 	dz = (z - LEN2) / 2;
2433 	dy = (y - LEN2) / 2;
2434 	dx = (x + LEN2) / 2;
2435 	NewPoint(lp, dx, dy, dz, &pts[i]);
2436 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2437 			pts[i].y < 0 || pts[i].y >= lp->height)
2438 		++out;
2439 	++i;
2440 	dz = (z - LEN2) / 2;
2441 	dy = (y + LEN2) / 2;
2442 	dx = (x - LEN2) / 2;
2443 	NewPoint(lp, dx, dy, dz, &pts[i]);
2444 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2445 			pts[i].y < 0 || pts[i].y >= lp->height)
2446 		++out;
2447 	++i;
2448 	dz = (z + LEN2) / 2;
2449 	dy = (y - LEN2) / 2;
2450 	dx = (x - LEN2) / 2;
2451 	NewPoint(lp, dx, dy, dz, &pts[i]);
2452 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2453 			pts[i].y < 0 || pts[i].y >= lp->height)
2454 		++out;
2455 	++i;
2456 	dz = (z + LEN2) / 2;
2457 	dy = (y + LEN2) / 2;
2458 	dx = (x + LEN2) / 2;
2459 	NewPoint(lp, dx, dy, dz, &pts[i]);
2460 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2461 			pts[i].y < 0 || pts[i].y >= lp->height)
2462 		++out;
2463 	++i;
2464 	if (out == 4)
2465 		return (0);
2466 	if (cell->visible)
2467 		mask = 0xFFFF;
2468 	else
2469 		mask = 0x0;
2470 	/* Only draw those faces that are visible */
2471 	dx = lp->vx - x;
2472 	dy = lp->vy - y;
2473 	dz = lp->vz - z;
2474 	/* back points looking through front
2475 	       3
2476 
2477 	   2
2478 	  ----------
2479 	   front points
2480 	   1
2481 
2482 	       0
2483 	*/
2484 	if (lp->wireframe) {
2485 		if (dx + dy + dz > 0) {
2486 			drawTri(mi, (int) (RED & mask), pts, 1, 0, 2);
2487 		}
2488 		if (dx + dy - dz < 0) {
2489 			drawTri(mi, (int) (GREEN & mask), pts, 1, 3, 0);
2490 		}
2491 		if (dx - dy + dz < 0) {
2492 			drawTri(mi, (int) (GREEN & mask), pts, 0, 2, 3);
2493 		}
2494 		if (-dx + dy + dz < 0) {
2495 			drawTri(mi, (int) (BLUE & mask), pts, 1, 3, 2);
2496 		}
2497 	}
2498 	if (dx + dy + dz <= 0) {
2499 		drawTri(mi, (int) (RED & mask), pts, 1, 0, 2);
2500 	}
2501 	if (dx + dy - dz >= 0) {
2502 		drawTri(mi, (int) (GREEN & mask), pts, 1, 3, 0);
2503 	}
2504 	if (dx - dy + dz >= 0) {
2505 		drawTri(mi, (int) (GREEN & mask), pts, 0, 2, 3);
2506 	}
2507 	if (-dx + dy + dz >= 0) {
2508 		drawTri(mi, (int) (BLUE & mask), pts, 1, 3, 2);
2509 	}
2510 	return (1);
2511 }
2512 
2513 static int
DrawTetrahedronTlBr(ModeInfo * mi,CellList * cell)2514 DrawTetrahedronTlBr(ModeInfo * mi, CellList * cell)
2515 {
2516 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2517 	XPoint pts[4];	/* screen coords for point */
2518 	int i = 0, out = 0;
2519 	unsigned int mask;
2520 	double x, y, z;
2521 	double dx, dy, dz;
2522 
2523 	x = (double) cell->x - lp->ox;
2524 	y = (double) cell->y - lp->oy;
2525 	z = (double) cell->z - lp->oz;
2526 	dz = (z - LEN2) / 2;
2527 	dy = (y - LEN2) / 2;
2528 	dx = (x - LEN2) / 2;
2529 	NewPoint(lp, dx, dy, dz, &pts[i]);
2530 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2531 			pts[i].y < 0 || pts[i].y >= lp->height)
2532 		++out;
2533 	++i;
2534 	dz = (z - LEN2) / 2;
2535 	dy = (y + LEN2) / 2;
2536 	dx = (x + LEN2) / 2;
2537 	NewPoint(lp, dx, dy, dz, &pts[i]);
2538 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2539 			pts[i].y < 0 || pts[i].y >= lp->height)
2540 		++out;
2541 	++i;
2542 	dz = (z + LEN2) / 2;
2543 	dy = (y - LEN2) / 2;
2544 	dx = (x + LEN2) / 2;
2545 	NewPoint(lp, dx, dy, dz, &pts[i]);
2546 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2547 			pts[i].y < 0 || pts[i].y >= lp->height)
2548 		++out;
2549 	++i;
2550 	dz = (z + LEN2) / 2;
2551 	dy = (y + LEN2) / 2;
2552 	dx = (x - LEN2) / 2;
2553 	NewPoint(lp, dx, dy, dz, &pts[i]);
2554 	if (pts[i].x < 0 || pts[i].x >= lp->width ||
2555 			pts[i].y < 0 || pts[i].y >= lp->height)
2556 		++out;
2557 	++i;
2558 	if (out == 4)
2559 		return (0);
2560 	if (cell->visible)
2561 		mask = 0xFFFF;
2562 	else
2563 		mask = 0x0;
2564 	/* Only draw those faces that are visible */
2565 	dx = lp->vx - x;
2566 	dy = lp->vy - y;
2567 	dz = lp->vz - z;
2568 	/* back points looking through front
2569 	   1
2570 
2571 	       0
2572 	  ----------
2573 	   front points
2574 	       3
2575 
2576 	   2
2577 	*/
2578 	if (lp->wireframe) {
2579 		if (dx + dy + dz > 0) {
2580 			drawTri(mi, (int) (BLUE & mask), pts, 1, 0, 2);
2581 		}
2582 		if (dx + dy - dz < 0) {
2583 			drawTri(mi, (int) (GREEN & mask), pts, 1, 3, 0);
2584 		}
2585 		if (dx - dy + dz < 0) {
2586 			drawTri(mi, (int) (GREEN & mask), pts, 0, 2, 3);
2587 		}
2588 		if (-dx + dy + dz < 0) {
2589 			drawTri(mi, (int) (RED & mask), pts, 1, 3, 2);
2590 		}
2591 	}
2592 	if (dx + dy + dz <= 0) {
2593 		drawTri(mi, (int) (BLUE & mask), pts, 1, 0, 2);
2594 	}
2595 	if (dx + dy - dz >= 0) {
2596 		drawTri(mi, (int) (GREEN & mask), pts, 1, 3, 0);
2597 	}
2598 	if (dx - dy + dz >= 0) {
2599 		drawTri(mi, (int) (GREEN & mask), pts, 0, 2, 3);
2600 	}
2601 	if (-dx + dy + dz >= 0) {
2602 		drawTri(mi, (int) (RED & mask), pts, 1, 3, 2);
2603 	}
2604 	/* if both 1st and 2nd... something wrong, kludge */
2605 	if (dx + dy - dz >= 0 && dx - dy + dz >= 0) {
2606 		drawTri(mi, (int) (BLUE & mask), pts, 1, 0, 2);
2607 		drawTri(mi, (int) (RED & mask), pts, 1, 3, 2);
2608 	}
2609 	return (1);
2610 }
2611 
2612 static int
DrawTetrahedron(ModeInfo * mi,CellList * cell)2613 DrawTetrahedron(ModeInfo * mi, CellList * cell) {
2614 	if (rTetra(cell->x, cell->y, cell->z)) {
2615 		return DrawTetrahedronTrBl(mi, cell);
2616 	} else if (lTetra(cell->x, cell->y, cell->z)) {
2617 		return DrawTetrahedronTlBr(mi, cell);
2618 	/*} else {
2619 		(void) fprintf(stderr, "bad cell %d %d %d\n",
2620 			cell->x, cell->y, cell->z); */
2621 	}
2622 	return (0);
2623 }
2624 
2625 static int
DrawRhombicDodecahedron(ModeInfo * mi,CellList * cell)2626 DrawRhombicDodecahedron(ModeInfo * mi, CellList * cell)
2627 {
2628 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2629 	XPoint pts[14];	/* screen coords for point */
2630 	int i, out = 0, da, db, sortx, sorty, sortz;
2631 	unsigned int mask;
2632 	double x, y, z;
2633 	double dx, dy, dz;
2634 
2635 	x = (double) cell->x - lp->ox;
2636 	y = (double) cell->y - lp->oy;
2637 	z = (double) cell->z - lp->oz;
2638 	i = 3;
2639 	for (dx = x + LEN; dx >= x - LEN2; dx -= LEN2)
2640 		for (dy = y + LEN; dy >= y - LEN2; dy -= LEN2)
2641 			for (dz = z + LEN; dz >= z - LEN2; dz -= LEN2) {
2642 				NewPoint(lp, dx, dy, dz, &pts[i]);
2643 				if (pts[i].x < 0 ||
2644 				    pts[i].x >= lp->width ||
2645 				    pts[i].y < 0 ||
2646 				    pts[i].y >= lp->height)
2647 					++out;
2648 				++i;
2649 			}
2650 	for (da = 0; da < 2; da++) {
2651 		if (da == 1) {
2652 			i = 0;
2653 		} else
2654 			i = 11; /* 3 + 8 */
2655 		for (db = 0; db < 3; db++) {
2656 			if (db == 0) {
2657 				dx = ((da == 0) ? -LEN2 : LEN2) + x;
2658 				dy = y;
2659 				dz = z;
2660 			} else if (db == 1) {
2661 				dy = ((da == 0) ? -LEN2 : LEN2) + y;
2662 				dz = z;
2663 				dx = x;
2664 			} else {
2665 				dz = ((da == 0) ? -LEN2 : LEN2) + z;
2666 				dx = x;
2667 				dy = y;
2668 			}
2669 			NewPoint(lp, dx, dy, dz, &pts[i]);
2670 			if (pts[i].x < 0 ||
2671 			    pts[i].x >= lp->width ||
2672 			    pts[i].y < 0 ||
2673 			    pts[i].y >= lp->height)
2674 				++out;
2675 			++i;
2676 		}
2677 	}
2678 	if (out == 14)
2679 		return (0);
2680 
2681 	if (cell->visible)
2682 		mask = 0xFFFF;
2683 	else
2684 		mask = 0x0;
2685 
2686 	/* Only draw those faces that are visible */
2687 	dx = lp->vx - x;
2688 	dy = lp->vy - y;
2689 	dz = lp->vz - z;
2690 	/* back points looking through front
2691 	   8   1  4
2692 	     7  3
2693 	  11   2  0
2694 	     9  5
2695 	  10  12  6
2696 	  ---------
2697 	   front points
2698 	  7    1  3
2699 	     8  4
2700 	  11  13  0
2701 	    10  6
2702 	  9   12  5
2703 	*/
2704 	if (lp->wireframe) {
2705 		if (dx <= LEN) {
2706 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2707 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2708 		drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2709 		drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2710 		} else if (dx >= -LEN) {
2711 		drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2712 		drawQuad(mi, (int) (GREEN & mask), pts, 1, 8, 11, 7);
2713 		drawQuad(mi, (int) (RED & mask), pts, 8, 13, 10, 11);
2714 		drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2715 		}
2716 		if (dy <= LEN) {
2717 		drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2718 		drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2719 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2720 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2721 		} else if (dy >= -LEN) {
2722 		drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2723 		drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2724 		drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2725 		drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2726 		}
2727 		if (dz <= LEN) {
2728 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2729 		drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2730 		drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2731 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2732 		} else if (dz >= -LEN) {
2733 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2734 		drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2735 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2736 		drawQuad(mi, (int) (GREEN & mask), pts, 8, 13, 10, 11);
2737 		}
2738 	}
2739 	sortx = sorty = sortz = 0;
2740 	if (ABS(dz - LEN) > ABS(dy - LEN))
2741 		sortz++;
2742 	if (ABS(dz - LEN) > ABS(dx - LEN))
2743 		sortz++;
2744 	if (ABS(dy - LEN) > ABS(dx - LEN))
2745 		sorty++;
2746 	if (ABS(dy - LEN) > ABS(dz - LEN))
2747 		sorty++;
2748 	if (ABS(dx - LEN) > ABS(dz - LEN))
2749 		sortx++;
2750 	if (ABS(dx - LEN) > ABS(dy - LEN))
2751 		sortx++;
2752 	for (i = 0; i < 3; i++) {
2753 	  if (sortx == i) {
2754 	    if (dx > LEN) {
2755 	      if (sortz > sorty) {
2756 		if (dz < 0)
2757 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2758 		else
2759 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2760 		drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2761 		drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2762 		if (dz < 0)
2763 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2764 		else
2765 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2766 	      } else {
2767 		if (dy < 0)
2768 		  drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2769 		else
2770 		  drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2771 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2772 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2773 		if (dy < 0)
2774 		  drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2775 		else
2776 		  drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2777 	      }
2778 	    } else if (dx < -LEN) {
2779 	      if (sortz > sorty) {
2780 		if (dz < 0)
2781 		  drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2782 		else
2783 		  drawQuad(mi, (int) (GREEN & mask), pts, 8, 13, 10, 11);
2784 		drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2785 		drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2786 		if (dz < 0)
2787 		  drawQuad(mi, (int) (GREEN & mask), pts, 8, 13, 10, 11);
2788 		else
2789 		  drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2790 	      } else {
2791 		if (dy < 0)
2792 		  drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2793 		else
2794 	  	  drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2795 		drawQuad(mi, (int) (GREEN & mask), pts, 8, 13, 10, 11);
2796 		drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2797 		if (dy < 0)
2798 	  	  drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2799 		else
2800 		  drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2801 	      }
2802 	    }
2803 	  }
2804 	  if (sorty == i) {
2805 	    if (dy > LEN) {
2806 	      if (sortx > sortz) {
2807 		if (dx < 0)
2808 		  drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2809 		else
2810 		  drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2811 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2812 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2813 		if (dx < 0)
2814 		  drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2815 		else
2816 		  drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2817 	      } else {
2818 		if (dz < 0)
2819 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2820 		else
2821 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2822 		drawQuad(mi, (int) (RED & mask), pts, 1, 8, 11, 7);
2823 		drawQuad(mi, (int) (RED & mask), pts, 0, 4, 1, 3);
2824 		if (dz < 0)
2825 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2826 		else
2827 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2828 	      }
2829 	    } else if (dy < -LEN) {
2830 	      if (sortx > sortz) {
2831 		if (dx < 0)
2832 		  drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2833 		else
2834 		  drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2835 		drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2836 		drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2837 		if (dx < 0)
2838 		  drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2839 		else
2840 		  drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2841 	      } else {
2842 		if (dz < 0)
2843 		  drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2844 		else
2845 		  drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2846 		drawQuad(mi, (int) (RED & mask), pts, 9, 11, 10, 12);
2847 		drawQuad(mi, (int) (RED & mask), pts, 0, 5, 12, 6);
2848 		if (dz < 0)
2849 		  drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2850 		else
2851 		  drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2852 	      }
2853 	    }
2854 	  }
2855 	  if (sortz == i) {
2856 	    if (dz > LEN) {
2857 	      if (sorty > sortx) {
2858 		if (dy < 0)
2859 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2860 		else
2861 		  drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2862 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2863 		drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2864 		if (dy < 0)
2865 		  drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2866 		else
2867 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2868 	      } else {
2869 		if (dx < 0)
2870 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2871 		else
2872 		  drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2873 		drawQuad(mi, (int) (BLUE & mask), pts, 2, 9, 12, 5);
2874 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 7, 2, 3);
2875 		if (dx < 0)
2876 		  drawQuad(mi, (int) (GREEN & mask), pts, 2, 7, 11, 9);
2877 		else
2878 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 3, 2, 5);
2879 	      }
2880 	    } else if (dz < -LEN) {
2881 	      if (sorty > sortx) {
2882 		if (dy < 0)
2883 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2884 		else
2885 		  drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2886 		drawQuad(mi, (int) (GREEN & mask), pts, 1, 8, 13, 10);
2887 		drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2888 		if (dy < 0)
2889 		  drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2890 		else
2891 		  drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2892 	      } else {
2893 		if (dx < 0)
2894 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2895 		else
2896 		  drawQuad(mi, (int) (GREEN & mask), pts, 1, 8, 13, 10);
2897 		drawQuad(mi, (int) (BLUE & mask), pts, 6, 12, 10, 13);
2898 		drawQuad(mi, (int) (BLUE & mask), pts, 1, 4, 13, 8);
2899 		if (dx < 0)
2900 		  drawQuad(mi, (int) (GREEN & mask), pts, 1, 8, 13, 10);
2901 		else
2902 		  drawQuad(mi, (int) (GREEN & mask), pts, 0, 6, 13, 4);
2903 	      }
2904 	    }
2905 	  }
2906 	}
2907 	return (1);
2908 }
2909 
2910 static int
DrawTruncatedOctahedron(ModeInfo * mi,CellList * cell)2911 DrawTruncatedOctahedron(ModeInfo * mi, CellList * cell)
2912 {
2913 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
2914 	XPoint pts[24];	/* screen coords for point */
2915 	int i, out = 0;
2916 	unsigned int mask;
2917 	double x, y, z;
2918 	double dx, dy, dz;
2919 
2920 	x = (double) cell->x - lp->ox;
2921 	y = (double) cell->y - lp->oy;
2922 	z = ((double) cell->z - lp->oz) / 2.0;
2923 	if ((cell->z & 1) == 1) {
2924 		x += 0.5;
2925 		y += 0.5;
2926 	}
2927 	NewPoint(lp, x + LEN_2, y, z + LEN, &pts[0]);
2928 	NewPoint(lp, x, y + LEN_2, z + LEN, &pts[1]);
2929 	NewPoint(lp, x - LEN_2, y, z + LEN, &pts[2]);
2930 	NewPoint(lp, x, y - LEN_2, z + LEN, &pts[3]);
2931 
2932 	NewPoint(lp, x + LEN, y, z + LEN_2, &pts[4]);
2933 	NewPoint(lp, x + LEN, y - LEN_2, z, &pts[15]);
2934 	NewPoint(lp, x + LEN, y, z - LEN_2, &pts[16]);
2935 	NewPoint(lp, x + LEN, y + LEN_2, z, &pts[8]);
2936 
2937 	NewPoint(lp, x, y + LEN, z + LEN_2, &pts[5]);
2938 	NewPoint(lp, x + LEN_2, y + LEN, z, &pts[9]);
2939 	NewPoint(lp, x, y + LEN, z - LEN_2, &pts[17]);
2940 	NewPoint(lp, x - LEN_2, y + LEN, z, &pts[10]);
2941 
2942 	NewPoint(lp, x - LEN, y, z + LEN_2, &pts[6]);
2943 	NewPoint(lp, x - LEN, y + LEN_2, z, &pts[11]);
2944 	NewPoint(lp, x - LEN, y, z - LEN_2, &pts[18]);
2945 	NewPoint(lp, x - LEN, y - LEN_2, z, &pts[12]);
2946 
2947 	NewPoint(lp, x, y - LEN, z + LEN_2, &pts[7]);
2948 	NewPoint(lp, x - LEN_2, y - LEN, z, &pts[13]);
2949 	NewPoint(lp, x, y - LEN, z - LEN_2, &pts[19]);
2950 	NewPoint(lp, x + LEN_2, y - LEN, z, &pts[14]);
2951 
2952 	NewPoint(lp, x + LEN_2, y, z - LEN, &pts[20]);
2953 	NewPoint(lp, x, y + LEN_2, z - LEN, &pts[21]);
2954 	NewPoint(lp, x - LEN_2, y, z - LEN, &pts[22]);
2955 	NewPoint(lp, x, y - LEN_2, z - LEN, &pts[23]);
2956 
2957 	for (i = 0; i < 24; i++) {
2958 		if (pts[i].x < 0 ||
2959 		    pts[i].x >= lp->width ||
2960 		    pts[i].y < 0 ||
2961 		    pts[i].y >= lp->height)
2962 			++out;
2963 	}
2964 	if (out == 24)
2965 		return (0);
2966 
2967 	if (cell->visible)
2968 		mask = 0xFFFF;
2969 	else
2970 		mask = 0x0;
2971 
2972 	/* Only draw those faces that are visible */
2973 	dx = lp->vx - x;
2974 	dy = lp->vy - y;
2975 	dz = lp->vz - z;
2976 	/* back points looking through front
2977 	   10  5  9
2978 	 11    1    8
2979 	  6  2   0  4
2980 	 12    3   15
2981 	   13  7 14
2982 	  ----------
2983 	   front points
2984 	   10 17  9
2985 	 11   21    8
2986 	 18 22  20 16
2987 	 12   23   15
2988 	   13 19 14
2989 	*/
2990 	if (lp->wireframe) {
2991 		if (dz <= LEN)
2992 			drawQuad(mi, (int) (BLUE & mask), pts, 0, 1, 2, 3);
2993 		else if (dz >= -LEN)
2994 			drawQuad(mi, (int) (BLUE & mask), pts, 23, 22, 21, 20);
2995 		if (dy <= LEN)
2996 			drawQuad(mi, (int) (GREEN & mask), pts, 5, 9, 17, 10);
2997 		else if (dy >= -LEN)
2998 			drawQuad(mi, (int) (GREEN & mask), pts, 7, 14, 19, 13);
2999 		if (dx <= LEN)
3000 			drawQuad(mi, (int) (RED & mask), pts, 4, 8, 16, 15);
3001 		else if (dx >= -LEN)
3002 			drawQuad(mi, (int) (RED & mask), pts, 6, 11, 18, 12);
3003 		if (dx + dy + dz < 0) {
3004 			drawHex(mi, (int) (UV & mask), pts, 1, 0, 4, 8, 9, 5);
3005 		} else {
3006 			drawHex(mi, (int) (UV & mask), pts, 22, 23, 19, 13, 12, 18);
3007 		}
3008 		if (dx + dy - dz < 0) {
3009 			drawHex(mi, (int) (GREEN & mask), pts, 20, 21, 17, 9, 8, 16);
3010 		} else {
3011 			drawHex(mi, (int) (GREEN & mask), pts, 3, 2, 6, 12, 13, 7);
3012 		}
3013 		if (dx - dy + dz < 0) {
3014 			drawHex(mi, (int) (RED & mask), pts, 0, 3, 7, 14, 15, 4);
3015 		} else {
3016 			drawHex(mi, (int) (RED & mask), pts, 21, 22, 18, 11, 10, 17);
3017 		}
3018 		if (-dx + dy + dz < 0) {
3019 			drawHex(mi, (int) (BLUE & mask), pts, 2, 1, 5, 10, 11, 6);
3020 		} else {
3021 			drawHex(mi, (int) (BLUE & mask), pts, 23, 20, 16, 15, 14, 19);
3022 		}
3023 	}
3024 	if (dx + dy + dz >= 0) {
3025 		drawHex(mi, (int) (UV & mask), pts, 1, 0, 4, 8, 9, 5);
3026 	} else {
3027 		drawHex(mi, (int) (UV & mask), pts, 22, 23, 19, 13, 12, 18);
3028 	}
3029 	if (dx + dy - dz >= 0) {
3030 		drawHex(mi, (int) (GREEN & mask), pts, 20, 21, 17, 9, 8, 16);
3031 	} else {
3032 		drawHex(mi, (int) (GREEN & mask), pts, 3, 2, 6, 12, 13, 7);
3033 	}
3034 	if (dx - dy + dz >= 0) {
3035 		drawHex(mi, (int) (RED & mask), pts, 0, 3, 7, 14, 15, 4);
3036 	} else {
3037 		drawHex(mi, (int) (RED & mask), pts, 21, 22, 18, 11, 10, 17);
3038 	}
3039 	if (-dx + dy + dz >= 0) {
3040 		drawHex(mi, (int) (BLUE & mask), pts, 2, 1, 5, 10, 11, 6);
3041 	} else {
3042 		drawHex(mi, (int) (BLUE & mask), pts, 23, 20, 16, 15, 14, 19);
3043 	}
3044 	if (dz > LEN)
3045 		drawQuad(mi, (int) (BLUE & mask), pts, 0, 1, 2, 3);
3046 	else if (dz < -LEN)
3047 		drawQuad(mi, (int) (BLUE & mask), pts, 23, 22, 21, 20);
3048 	if (dy > LEN)
3049 		drawQuad(mi, (int) (GREEN & mask), pts, 5, 9, 17, 10);
3050 	else if (dy < -LEN)
3051 		drawQuad(mi, (int) (GREEN & mask), pts, 7, 14, 19, 13);
3052 	if (dx > LEN)
3053 		drawQuad(mi, (int) (RED & mask), pts, 4, 8, 16, 15);
3054 	else if (dx < -LEN)
3055 		drawQuad(mi, (int) (RED & mask), pts, 6, 11, 18, 12);
3056 	return (1);
3057 }
3058 
3059 static void
DrawScreen(ModeInfo * mi)3060 DrawScreen(ModeInfo * mi)
3061 {
3062 	life3dstruct *lp = &life3ds[MI_SCREEN(mi)];
3063 	Display *display = MI_DISPLAY(mi);
3064 	GC gc = MI_GC(mi);
3065 	CellList *ptr;
3066 	CellList *eraserptr;
3067 	int i;
3068 
3069 	SortList(lp);
3070 
3071 	if (draw) {
3072 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
3073 		XFillRectangle(display, (Drawable) lp->dbuf, gc, 0, 0,
3074 			lp->width, lp->height);
3075 	}
3076 	/* Erase dead cubes */
3077 	eraserptr = lp->eraserhead.next;
3078 	while (eraserptr != &lp->eraserend) {
3079 		eraserptr->visible = 0;
3080 		if (draw) {
3081 			if (lp->neighbors == 12 || lp->neighbors == -18)
3082 				(void) DrawRhombicDodecahedron(mi, eraserptr);
3083 			else if (lp->neighbors == 14)
3084 				(void) DrawTruncatedOctahedron(mi, eraserptr);
3085 			else if (lp->neighbors == 22)
3086 				(void) DrawTetrahedron(mi, eraserptr);
3087 			else
3088 				(void) DrawCube(mi, eraserptr);
3089 		}
3090 		delFromEraseList(eraserptr);
3091 		eraserptr = lp->eraserhead.next;
3092 	}
3093 	if (!draw)
3094 		return;
3095 
3096 	/* draw furthest cubes first */
3097 	for (i = NBUCKETS - 1; i >= 0; --i) {
3098 		ptr = lp->buckethead[i];
3099 		while (ptr != NULL) {
3100 			/*if (ptr->visible) */
3101 			if (lp->neighbors == 12 || lp->neighbors == -18)
3102 				(void) DrawRhombicDodecahedron(mi, ptr);
3103 			else if (lp->neighbors == 14)
3104 				(void) DrawTruncatedOctahedron(mi, ptr);
3105 			else if (lp->neighbors == 22)
3106 				(void) DrawTetrahedron(mi, ptr);
3107 			else
3108 			/* v += */ (void) DrawCube(mi, ptr);
3109 			ptr = ptr->priority;
3110 			/* ++count; */
3111 		}
3112 	}
3113 	if (label) {
3114 		int size = MAX(MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) - 1, 1);
3115 
3116 		if (size >= 10 * FONT_WIDTH) {
3117 			char ruleString[120];
3118 
3119 			/* hard code these to corners */
3120 			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
3121 			(void) sprintf(ruleString, "N%d:%s",
3122 				lp->neighbors, lp->ruleString);
3123 			XDrawString(display, (Drawable) lp->dbuf, gc,
3124 				16 + lp->labelOffsetX,
3125 				16 + lp->labelOffsetY + FONT_HEIGHT,
3126 				ruleString, strlen(ruleString));
3127 			XDrawString(display, (Drawable) lp->dbuf, gc,
3128 				16 + lp->labelOffsetX, MI_HEIGHT(mi) - 16 -
3129 				lp->labelOffsetY - FONT_HEIGHT / 2,
3130 				lp->nameString, strlen(lp->nameString));
3131 		}
3132 	}
3133 	XFlush(display);
3134 	XCopyArea(display, (Drawable) lp->dbuf, MI_WINDOW(mi), gc,
3135 		0, 0, lp->width, lp->height, 0, 0);
3136 
3137 #if 0
3138 	{
3139 		int count = 0, v = 0;
3140 
3141 
3142 		(void) printf("Pop=%-4d  Viewpoint (%3d,%3d,%3d)  Origin (%3d,%3d,%3d)  Mode %dx%d\
3143 (%d,%d) %d\n",
3144 		count, (int) (lp->vx + lp->ox), (int) (lp->vy + lp->oy),
3145 			(int) (lp->vz + lp->oz), (int) lp->ox, (int) lp->oy, (int) lp->oz,
3146 			lp->width, lp->height, lp->alt, lp->azm, v);
3147 	}
3148 #endif
3149 }
3150 
3151 static Bool
orthogonal(life3dstruct * lp)3152 orthogonal(life3dstruct * lp) /* or full diagonal */
3153 {
3154 	if (lp->neighbors == 12)
3155 		return (lp->patterned_rule == LIFE_12B3S456);
3156 	else if (lp->neighbors == 18)
3157 		return ((lp->patterned_rule == LIFE_18B4S45) &&
3158 			(LRAND() & 1));
3159 	else if (lp->neighbors == -18)
3160 		return ((lp->patterned_rule == LIFE__18B4S36) ||
3161 			(lp->patterned_rule == LIFE__18B4S46));
3162 	else if (lp->neighbors == 20)
3163 		return (lp->patterned_rule == LIFE_20B4S45);
3164 	else if (lp->neighbors == 22)
3165 		return (((lp->patterned_rule == LIFE_22B4S3) &&
3166 			(NRAND(3) == 0)) ||
3167 			(lp->patterned_rule == LIFE_22B4S4));
3168 	else if (lp->neighbors == 26)
3169 		return ((lp->patterned_rule == LIFE_26B5S23) ||
3170 			(lp->patterned_rule == LIFE_26B5S25) ||
3171 			(lp->patterned_rule == LIFE_26B5S27) ||
3172 			(lp->patterned_rule == LIFE_26B5S35) ||
3173 			(lp->patterned_rule == LIFE_26B5S36) ||
3174 			(lp->patterned_rule == LIFE_26B5S37) ||
3175 			(lp->patterned_rule == LIFE_26B5S4) ||
3176 			(lp->patterned_rule == LIFE_26B5S47) ||
3177 			((lp->patterned_rule == LIFE_26B5S56) &&
3178 			(LRAND() & 1)) ||
3179 			((lp->patterned_rule == LIFE_26B6S57) &&
3180 			(NRAND(4) == 0)) ||
3181 			((lp->patterned_rule == LIFE_26B5S58) &&
3182 			(NRAND(4) == 0)) ||
3183 			((lp->patterned_rule == LIFE_26B5S678) &&
3184 			(LRAND() & 1)) ||
3185 			((lp->patterned_rule == LIFE_26B58S58) &&
3186 			(NRAND(4) != 0)));
3187 	return False;
3188 }
3189 
3190 static Bool
populateGlider(life3dstruct * lp,char * glider,int offsetSwitch,int hsp,int vsp,int asp,int hoff,int voff,int aoff)3191 populateGlider(life3dstruct * lp, char *glider, int offsetSwitch,
3192 		int hsp, int vsp, int asp, int hoff, int voff, int aoff)
3193 {
3194 	int x, y, z, temp;
3195 
3196 	while ((x = *glider++) != 127) {
3197 		int offx = 0, offy = 0, offz;
3198 		y = *glider++;
3199 		z = *glider++;
3200 		if (offsetSwitch == 1) {
3201 			temp = x;
3202 			x = y;
3203 			if (lp->neighbors == 14) {
3204 				y = (z >= 0) ? z / 2 : (z - 1) / 2;
3205 				z = (z & 1) + 2 * temp;
3206 			} else {
3207 				y = z;
3208 				z = temp;
3209 			}
3210 		} else if (offsetSwitch == 2) {
3211 			temp = z;
3212 			if (lp->neighbors == 14) {
3213 				z = (z & 1) + 2 * y;
3214 				y = x;
3215 				x = (temp >= 0) ? temp / 2 : (temp - 1) / 2;
3216 			} else {
3217 				z = y;
3218 				y = x;
3219 				x = temp;
3220 			}
3221 		}
3222 		offz = asp + z * aoff;
3223 		if (lp->neighbors == 14 && (offz & 1) == 1) {
3224 			if (hoff == -1)
3225 				offx = -1;
3226 			if (voff == -1)
3227 				offy = -1;
3228 		}
3229 		SetList(hsp + x * hoff + offx, vsp + y * voff + offy, offz);
3230 	}
3231 	return True;
3232 }
3233 
3234 /* My rule of thumb is if there is an 18 cell (or less) glider add it here */
3235 static void
populateGliders(life3dstruct * lp,int dim,int orth,int hsp,int vsp,int asp,int hoff,int voff,int aoff)3236 populateGliders(life3dstruct * lp, int dim, int orth,
3237 		int hsp, int vsp, int asp, int hoff, int voff, int aoff)
3238 {
3239 	if (lp->neighbors == 12 && lp->patterned_rule == LIFE_12B3S3) {
3240 			(void) populateGlider(lp, glider_12B3S3[2], dim,
3241 				hsp, vsp, asp, hoff, voff, aoff);
3242 		if (NRAND(3)) {
3243 			(void) populateGlider(lp, glider_12B3S3[0], dim,
3244 				hsp, vsp, asp, hoff, voff, aoff);
3245 		} else if (NRAND(3)) {
3246 			(void) populateGlider(lp, glider_12B3S3[1], dim,
3247 				hsp, vsp, asp, hoff, voff, aoff);
3248 		} else if (NRAND(2)) { /* lower probability of big gliders */
3249 			(void) populateGlider(lp, glider_12B3S3[2], dim,
3250 				hsp, vsp, asp, hoff, voff, aoff);
3251 		} else {
3252 			(void) populateGlider(lp, glider_12B3S3[3], dim,
3253 				hsp, vsp, asp, hoff, voff, aoff);
3254 		}
3255 	} else if (lp->neighbors == 12 && lp->patterned_rule == LIFE_12B3S456) {
3256 		(void) populateGlider(lp, glider_12B3S456[0], dim,
3257 			hsp, vsp, asp, hoff, voff, aoff);
3258 	} else if (lp->neighbors == 14 && lp->patterned_rule == LIFE_14B4S34) {
3259 		(void) populateGlider(lp, glider_14B4S34[0], dim,
3260 			hsp, vsp, asp, hoff, voff, aoff);
3261 	} else if (lp->neighbors == 14 && lp->patterned_rule == LIFE_14B46S34) {
3262 		(void) populateGlider(lp, glider_14B46S34[0], dim,
3263 			hsp, vsp, asp, hoff, voff, aoff);
3264 	} else if (lp->neighbors == 18 && lp->patterned_rule == LIFE_18B4S45) {
3265 		if (orth) {
3266 			if (LRAND() & 1) {
3267 				(void) populateGlider(lp, glider_18B4S45[0], dim,
3268 					hsp, vsp, asp, hoff, voff, aoff);
3269 			} else {
3270 				(void) populateGlider(lp, glider_18B4S45[1], dim,
3271 					hsp, vsp, asp, hoff, voff, aoff);
3272 			}
3273 		} else {
3274 			(void) populateGlider(lp, glider_18B4S45[2], dim,
3275 				hsp, vsp, asp, hoff, voff, aoff);
3276 		}
3277 	} else if (lp->neighbors == -18 && lp->patterned_rule == LIFE__18B4S36) {
3278 		(void) populateGlider(lp, glider__18B4S36[0], dim,
3279 			hsp, vsp, asp, hoff, voff, aoff);
3280 	} else if (lp->neighbors == -18 && lp->patterned_rule == LIFE__18B4S46) {
3281 		(void) populateGlider(lp, glider__18B4S46[0], dim,
3282 			hsp, vsp, asp, hoff, voff, aoff);
3283 	} else if (lp->neighbors == 20 && lp->patterned_rule == LIFE_20B4S45) {
3284 		if (LRAND() & 1) {
3285 			(void) populateGlider(lp, glider_20B4S45[0], dim,
3286 				hsp, vsp, asp, hoff, voff, aoff);
3287 		} else {
3288 			(void) populateGlider(lp, glider_20B4S45[1], dim,
3289 				hsp, vsp, asp, hoff, voff, aoff);
3290 		}
3291 	} else if (lp->neighbors == 22 && lp->patterned_rule == LIFE_22B4S3) {
3292 		if (orth) {
3293 			(void) populateGlider(lp, glider_22B4S3[0], dim,
3294 				hsp, vsp, asp, hoff, voff, aoff);
3295 		} else {
3296 			if (NRAND(3)) {
3297 				(void) populateGlider(lp, glider_22B4S3[2], dim,
3298 					hsp, vsp, asp, hoff, voff, aoff);
3299 			} else {
3300 				(void) populateGlider(lp, glider_22B4S3[1], dim,
3301 					hsp, vsp, asp, hoff, voff, aoff);
3302 			}
3303 		}
3304 	} else if (lp->neighbors == 22 && lp->patterned_rule == LIFE_22B4S4) {
3305 		if (NRAND(3)) {
3306 			(void) populateGlider(lp, glider_22B4S4[1], dim,
3307 				hsp, vsp, asp, hoff, voff, aoff);
3308 		} else {
3309 			(void) populateGlider(lp, glider_22B4S4[0], dim,
3310 				hsp, vsp, asp, hoff, voff, aoff);
3311 		}
3312 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S23) {
3313 		(void) populateGlider(lp, glider_26B5S23[0], dim,
3314 			hsp, vsp, asp, hoff, voff, aoff);
3315 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S25) {
3316 		(void) populateGlider(lp, glider_26B5S25[0], dim,
3317 			hsp, vsp, asp, hoff, voff, aoff);
3318 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S27) {
3319 		(void) populateGlider(lp, glider_26B5S27[0], dim,
3320 			hsp, vsp, asp, hoff, voff, aoff);
3321 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S35) {
3322 		(void) populateGlider(lp, glider_26B5S35[0], dim,
3323 			hsp, vsp, asp, hoff, voff, aoff);
3324 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S36) {
3325 		(void) populateGlider(lp, glider_26B5S36[0], dim,
3326 			hsp, vsp, asp, hoff, voff, aoff);
3327 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S37) {
3328 		(void) populateGlider(lp, glider_26B5S37[0], dim,
3329 			hsp, vsp, asp, hoff, voff, aoff);
3330 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S38) {
3331 		if (LRAND() & 1) {
3332 			(void) populateGlider(lp, glider_26B5S38[0], dim,
3333 				hsp, vsp, asp, hoff, voff, aoff);
3334 		} else {
3335 			(void) populateGlider(lp, glider_26B5S38[1], dim,
3336 				hsp, vsp, asp, hoff, voff, aoff);
3337 		}
3338 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S4) {
3339 		(void) populateGlider(lp, glider_26B5S4[0], dim,
3340 			hsp, vsp, asp, hoff, voff, aoff);
3341 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S45) {
3342 		(void) populateGlider(lp, glider_26B5S45[0], dim,
3343 			hsp, vsp, asp, hoff, voff, aoff);
3344 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S47) {
3345 		(void) populateGlider(lp, glider_26B5S47[0], dim,
3346 			hsp, vsp, asp, hoff, voff, aoff);
3347 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S56) {
3348 		if (orth) {
3349 			if (LRAND() & 1) {
3350 				(void) populateGlider(lp, glider_26B5S56[0], dim,
3351 					hsp, vsp, asp, hoff, voff, aoff);
3352 			} else {
3353 				(void) populateGlider(lp, glider_26B5S56[1], dim,
3354 					hsp, vsp, asp, hoff, voff, aoff);
3355 			}
3356 		} else {
3357 			(void) populateGlider(lp, glider_26B5S56[2], dim,
3358 				hsp, vsp, asp, hoff, voff, aoff);
3359 		}
3360 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S58) {
3361 		if (orth)
3362 			(void) populateGlider(lp, glider_26B5S58[4], dim,
3363 				hsp, vsp, asp, hoff, voff, aoff);
3364 		else if (NRAND(3)) {
3365 			if (LRAND() & 1)
3366 				(void) populateGlider(lp, glider_26B5S58[0], dim,
3367 					hsp, vsp, asp, hoff, voff, aoff);
3368 			else if (LRAND() & 1)
3369 				(void) populateGlider(lp, glider_26B5S58[1], dim,
3370 					hsp, vsp, asp, hoff, voff, aoff);
3371 			else
3372 				(void) populateGlider(lp, glider_26B5S58[2], dim,
3373 					hsp, vsp, asp, hoff, voff, aoff);
3374 		} else
3375 			(void) populateGlider(lp, glider_26B5S58[3], dim,
3376 				hsp, vsp, asp, hoff, voff, aoff);
3377 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S678) {
3378 		if (orth) {
3379 			(void) populateGlider(lp, glider_26B5S678[1], dim,
3380 				hsp, vsp, asp, hoff, voff, aoff);
3381 		} else {
3382 			(void) populateGlider(lp, glider_26B5S678[0], dim,
3383 				hsp, vsp, asp, hoff, voff, aoff);
3384 		}
3385 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B5S8) {
3386 		if (LRAND() & 1) {
3387 			(void) populateGlider(lp, glider_26B5S8[0], dim,
3388 				hsp, vsp, asp, hoff, voff, aoff);
3389 		} else {
3390 			if (LRAND() & 1) {
3391 				(void) populateGlider(lp, glider_26B5S8[1], dim,
3392 					hsp, vsp, asp, hoff, voff, aoff);
3393 			} else {
3394 				(void) populateGlider(lp, glider_26B5S8[2], dim,
3395 					hsp, vsp, asp, hoff, voff, aoff);
3396 			}
3397 		}
3398 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B58S58) {
3399 		if (orth) {
3400 			if (LRAND() & 1) {
3401 				if (LRAND() & 1)
3402 					(void) populateGlider(lp, glider_26B58S58[1], dim,
3403 						hsp, vsp, asp, hoff, voff, aoff);
3404 				else
3405 					(void) populateGlider(lp, glider_26B58S58[2], dim,
3406 						hsp, vsp, asp, hoff, voff, aoff);
3407 			} else {
3408 				if (LRAND() & 1)
3409 					(void) populateGlider(lp, glider_26B58S58[3], dim,
3410 						hsp, vsp, asp, hoff, voff, aoff);
3411 				else if (NRAND(3))
3412 					(void) populateGlider(lp, glider_26B58S58[4], dim,
3413 						hsp, vsp, asp, hoff, voff, aoff);
3414 				else if (LRAND() & 1)
3415 					(void) populateGlider(lp, glider_26B58S58[5], dim,
3416 						hsp, vsp, asp, hoff, voff, aoff);
3417 				else
3418 					(void) populateGlider(lp, glider_26B58S58[6], dim,
3419 						hsp, vsp, asp, hoff, voff, aoff);
3420 			}
3421 		} else {
3422 			(void) populateGlider(lp, glider_26B58S58[0], dim,
3423 				hsp, vsp, asp, hoff, voff, aoff);
3424 		}
3425 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B6S567) {
3426 		(void) populateGlider(lp, glider_26B6S567[0], dim,
3427 			hsp, vsp, asp, hoff, voff, aoff);
3428 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B6S57) {
3429 		if (orth) {
3430 			if (LRAND() & 1) {
3431 				(void) populateGlider(lp, glider_26B6S57[1], dim,
3432 					hsp, vsp, asp, hoff, voff, aoff);
3433 			} else {
3434 				(void) populateGlider(lp, glider_26B6S57[2], dim,
3435 					hsp, vsp, asp, hoff, voff, aoff);
3436 			}
3437 		} else {
3438 			(void) populateGlider(lp, glider_26B6S57[0], dim,
3439 				hsp, vsp, asp, hoff, voff, aoff);
3440 		}
3441 	} else if (lp->neighbors == 26 && lp->patterned_rule == LIFE_26B67S57) {
3442 		(void) populateGlider(lp, glider_26B67S57[0], dim,
3443 			hsp, vsp, asp, hoff, voff, aoff);
3444 	}
3445 }
3446 
3447 static Bool
shooter(life3dstruct * lp)3448 shooter(life3dstruct * lp)
3449 {
3450 	int hsp, vsp, asp, hoff = 1, voff = 1, aoff = 1, dim, c2, r2, s2, orth;
3451 
3452 	/* Generate the glider at the edge of the screen */
3453 	int V = (lp->neighbors == 22) ? 20 : 10;
3454 	int V2 = V / 2;
3455 	c2 = lp->ncols / 2;
3456 	r2 = lp->nrows / 2;
3457 	s2 = lp->nstacks / 2;
3458 	dim = NRAND(3);
3459 	orth = orthogonal(lp);
3460 	if (!dim) {
3461 		if (orth) {
3462 			hsp = (LRAND() & 1) ? c2 - V : c2 + V;
3463 			vsp = NRAND(V2) + r2 - V2 / 2;
3464 			asp = NRAND(V2) + s2 - V2 / 2;
3465 		} else {
3466 			hsp = NRAND(V2) + c2 - V2 / 2;
3467 			vsp = (LRAND() & 1) ? r2 - V : r2 + V;
3468 			asp = (LRAND() & 1) ? s2 - V : s2 + V;
3469 		}
3470 		if (asp > s2)
3471 			aoff = -1;
3472 		if (vsp > r2)
3473 			voff = -1;
3474 		if (hsp > c2)
3475 			hoff = -1;
3476 		if (lp->neighbors == 12 || lp->neighbors == -18) {
3477 			/* Must ensure we have only odd cells (x+y+z=odd)
3478 			   for glider, so this must be even */
3479 		    if ((asp + vsp + hsp) & 1)
3480 			hsp--;
3481 		    populateGliders(lp, dim, orth,
3482 			hsp, vsp, asp, hoff, voff, aoff);
3483 		} else if (lp->neighbors == 14) {
3484 		    /* Must ensure we have only z odd cells (z=odd)
3485 		       for glider, so this must be even */
3486 		    if (asp & 1)
3487 			asp--;
3488 		    populateGliders(lp, dim, orth,
3489 			hsp, vsp, asp, hoff, voff, aoff);
3490 		} else if (lp->neighbors == 18 || lp->neighbors == 20 || lp->neighbors == 26) {
3491 		    populateGliders(lp, dim, orth,
3492 			hsp, vsp, asp, hoff, voff, aoff);
3493 		} else if (lp->neighbors == 22) {
3494 		    if (hsp & 1)
3495 			hsp--;
3496 		    if (vsp & 1)
3497 			vsp--;
3498 		    if (asp & 1)
3499 			asp--;
3500 		    if ((hsp + vsp + asp) % 4)
3501 			hsp++, vsp++;
3502 		    populateGliders(lp, dim, orth,
3503 			hsp, vsp, asp, hoff, voff, aoff);
3504 		}
3505 	} else if (dim == 1) {
3506 		if (orth) {
3507 			hsp = NRAND(V2) + c2 - V2 / 2;
3508 			vsp = NRAND(V2) + r2 - V2 / 2;
3509 			asp = (LRAND() & 1) ? s2 - V : s2 + V;
3510 		} else {
3511 			hsp = (LRAND() & 1) ? c2 - V : c2 + V;
3512 			vsp = (LRAND() & 1) ? r2 - V : r2 + V;
3513 			asp = NRAND(V2) + s2 - V2 / 2;
3514 		}
3515 		if (asp > s2)
3516 			aoff = -1;
3517 		if (vsp > r2)
3518 			voff = -1;
3519 		if (hsp > c2)
3520 			hoff = -1;
3521 		if (lp->neighbors == 12 || lp->neighbors == -18) {
3522 			/* Must ensure we have only odd cells (x+y+z=odd)
3523 			   for glider, so this must be even */
3524 		    if ((asp + vsp + hsp) & 1)
3525 			asp--;
3526 		    populateGliders(lp, dim, orth,
3527 			hsp, vsp, asp, hoff, voff, aoff);
3528 		} else if (lp->neighbors == 14) {
3529 		    /* Must ensure we have only z odd cells (z=odd)
3530 		       for glider, so this must be even */
3531 		    if (asp & 1)
3532 			asp--;
3533 		    populateGliders(lp, dim, orth,
3534 			hsp, vsp, asp, hoff, voff, aoff);
3535 		} else if (lp->neighbors == 18 || lp->neighbors == 20 || lp->neighbors == 26) {
3536 		    populateGliders(lp, dim, orth,
3537 			hsp, vsp, asp, hoff, voff, aoff);
3538 		} else if (lp->neighbors == 22) {
3539 		    if (hsp & 1)
3540 			hsp--;
3541 		    if (vsp & 1)
3542 			vsp--;
3543 		    if (asp & 1)
3544 			asp--;
3545 		    if ((hsp + vsp + asp) % 4)
3546 			hsp++, vsp++;
3547 		    populateGliders(lp, dim, orth,
3548 			hsp, vsp, asp, hoff, voff, aoff);
3549 		}
3550 	} else {
3551 		if (orth) {
3552 			hsp = NRAND(V2) + c2 - V2 / 2;
3553 			vsp = (LRAND() & 1) ? r2 - V : r2 + V;
3554 			asp = NRAND(V2) + s2 - V2 / 2;
3555 		} else {
3556 			hsp = (LRAND() & 1) ? c2 - V : c2 + V;
3557 			vsp = NRAND(V2) + r2 - V2 / 2;
3558 			asp = (LRAND() & 1) ? s2 - V : s2 + V;
3559 		}
3560 		if (asp > s2)
3561 			aoff = -1;
3562 		if (vsp > r2)
3563 			voff = -1;
3564 		if (hsp > c2)
3565 			hoff = -1;
3566 		if (lp->neighbors == 12 || lp->neighbors == -18) {
3567 			/* Must ensure we have only odd cells (x+y+z=odd)
3568 			   for glider, so this must be even */
3569 		    if ((asp + vsp + hsp) & 1)
3570 			vsp--;
3571 		    populateGliders(lp, dim, orth,
3572 			hsp, vsp, asp, hoff, voff, aoff);
3573 		} else if (lp->neighbors == 14) {
3574 		    /* Must ensure we have only z odd cells (z=odd)
3575 		       for glider, so this must be even */
3576 		    if (asp & 1)
3577 			asp--;
3578 		    populateGliders(lp, dim, orth,
3579 			hsp, vsp, asp, hoff, voff, aoff);
3580 		} else if (lp->neighbors == 18 || lp->neighbors == 20 || lp->neighbors == 26) {
3581 		    populateGliders(lp, dim, orth,
3582 			hsp, vsp, asp, hoff, voff, aoff);
3583 		} else if (lp->neighbors == 22) {
3584 		    if (hsp & 1)
3585 			hsp--;
3586 		    if (vsp & 1)
3587 			vsp--;
3588 		    if (asp & 1)
3589 			asp--;
3590 		    if ((hsp + vsp + asp) % 4)
3591 			hsp++, vsp++;
3592 		    populateGliders(lp, dim, orth,
3593 			hsp, vsp, asp, hoff, voff, aoff);
3594 		}
3595 	}
3596 	return True;
3597 }
3598 
3599 static void
free_life3d_screen(Display * display,life3dstruct * lp)3600 free_life3d_screen(Display *display, life3dstruct *lp)
3601 {
3602 	int shade;
3603 
3604 	if (lp == NULL) {
3605 		return;
3606 	}
3607 	if (lp->eraserhead.next != NULL)
3608 		endList(lp);
3609 	if (lp->dbuf != None) {
3610 		XFreePixmap(display, lp->dbuf);
3611 		lp->dbuf = None;
3612 	}
3613 	if (lp->stippledGC != None) {
3614 		XFreeGC(display, lp->stippledGC);
3615 		lp->stippledGC = None;
3616 	}
3617 	for (shade = 0; shade < lp->init_bits; shade++) {
3618 		XFreePixmap(display, lp->pixmaps[shade]);
3619 	}
3620 	lp->init_bits = 0;
3621 	lp = NULL;
3622 }
3623 
3624 ENTRYPOINT void
free_life3d(ModeInfo * mi)3625 free_life3d(ModeInfo * mi)
3626 {
3627 	free_life3d_screen(MI_DISPLAY(mi), &life3ds[MI_SCREEN(mi)]);
3628 }
3629 
3630 ENTRYPOINT void
init_life3d(ModeInfo * mi)3631 init_life3d(ModeInfo * mi)
3632 {
3633 	Display *display = MI_DISPLAY(mi);
3634 	Window window = MI_WINDOW(mi);
3635 	life3dstruct *lp;
3636 	int i, npats;
3637 
3638 	MI_INIT(mi, life3ds);
3639 	lp = &life3ds[MI_SCREEN(mi)];
3640 
3641 	if (MI_NPIXELS(mi) <= 2) {
3642 		if (lp->stippledGC == None) {
3643 			XGCValues gcv;
3644 			gcv.fill_style = FillOpaqueStippled;
3645 			if ((lp->stippledGC = XCreateGC(display, window,
3646 					GCFillStyle, &gcv)) == None) {
3647 				free_life3d_screen(display, lp);
3648 				return;
3649 			}
3650 		}
3651 		if (lp->init_bits == 0) {
3652 			for (i = 1; i < NUMSTIPPLES; i++) {
3653 				LIFE3DBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
3654 			}
3655 		}
3656 	}
3657 	lp->generation = 0;
3658 	if (MI_IS_FULLRANDOM(mi)) {
3659 		int r12n1 = patterns_12rules[0];
3660 		int r12n2 = r12n1 + patterns_12rules[1];
3661 		int r14n1 = patterns_14rules[0];
3662 		int r14n2 = r14n1 + patterns_14rules[1];
3663 		int r18n1 = patterns_18rules[0];
3664 		int r_18n1 = patterns__18rules[0];
3665 		int r_18n2 = r_18n1 + patterns__18rules[1];
3666 		int r20n1 = patterns_20rules[0];
3667 		int r22n1 = patterns_22rules[0];
3668 		int r22n2 = r22n1 + patterns_22rules[1];
3669 		int r26n1 = patterns_26rules[0];
3670 		int r26n2 = r26n1 + patterns_26rules[1];
3671 		int r26n3 = r26n2 + patterns_26rules[2];
3672 		int r26n4 = r26n3 + patterns_26rules[3];
3673 #if 1
3674 		lp->neighbors =
3675 			(NRAND(r12n2 + r14n2 + r18n1 + r_18n2 + r20n1 + r22n2 + r26n4) < r12n2) ? 12 :
3676 			(NRAND(r14n2 + r18n1 + r_18n2 + r20n1 + r22n2 + r26n4) < r14n2) ? 14 :
3677 			(NRAND(r18n1 + r_18n2 + r20n1 + r22n2 + r26n4) < r18n1) ? 18 :
3678 			(NRAND(r_18n2 + r20n1 + r22n2 + r26n4) < r18n1) ? -18 :
3679 			(NRAND(r20n1 + r22n2 + r26n4) < r20n1) ? 20 :
3680 			(NRAND(r22n2 + r26n4) < r22n2) ? 22 : 26;
3681 		lp->allGliders = True;
3682 #else
3683 		lp->neighbors = 26;
3684 #endif
3685 	}
3686 	if (!lp->neighbors) {
3687 		for (i = 0; i < NEIGHBORKINDS; i++) {
3688 			if (neighbors == plots[i]) {
3689 				lp->neighbors = neighbors;
3690 				break;
3691 			}
3692 			if (i == NEIGHBORKINDS - 1) {
3693 #if 0
3694 				lp->neighbors = plots[NRAND(NEIGHBORKINDS)];
3695 				lp->neighbors = (LRAND() & 1) ? 12 : 26;
3696 #else
3697 				lp->neighbors = 26;
3698 #endif
3699 				break;
3700 			}
3701 		}
3702 	}
3703 
3704 	lp->labelOffsetX = NRAND(8);
3705 	lp->labelOffsetY = NRAND(8);
3706 	parseRule(mi, lp->ruleString);
3707 #ifndef STANDALONE
3708 	parseFile(mi);
3709 #endif
3710 	if (lp->allPatterns) {
3711 		switch (lp->neighbors) {
3712 		case 12:
3713 			lp->patterned_rule = NRAND(LIFE_12RULES);
3714 			break;
3715 		case 14:
3716 			lp->patterned_rule = NRAND(LIFE_14RULES);
3717 			break;
3718 		case 18:
3719 			lp->patterned_rule = 0; /* only one rule */
3720 			break;
3721 		case -18:
3722 			lp->patterned_rule = NRAND(LIFE__18RULES);
3723 			break;
3724 		case 20:
3725 			lp->patterned_rule = 0; /* only one rule */
3726 			break;
3727 		case 22:
3728 			lp->patterned_rule = NRAND(LIFE_22RULES);
3729 			break;
3730 		case 26:
3731 			lp->patterned_rule = NRAND(LIFE_26RULES);
3732 			break;
3733 		}
3734 		copyFromPatternedRule(lp->neighbors, &lp->param,
3735 			lp->patterned_rule);
3736 		printRule(lp->neighbors, lp->ruleString, lp->param,
3737 			MI_IS_VERBOSE(mi));
3738 	} else if (lp->allGliders) {
3739 		switch (lp->neighbors) {
3740 		case 12:
3741 			lp->patterned_rule = NRAND(LIFE_12GLIDERS);
3742 			break;
3743 		case 14:
3744 			lp->patterned_rule = NRAND(LIFE_14GLIDERS);
3745 			break;
3746 		case 18:
3747 			lp->patterned_rule = 0; /* only one rule */
3748 			break;
3749 		case -18:
3750 			lp->patterned_rule = NRAND(LIFE__18GLIDERS);
3751 			break;
3752 		case 20:
3753 			lp->patterned_rule = 0; /* only one rule */
3754 			break;
3755 		case 22:
3756 			lp->patterned_rule = NRAND(LIFE_22GLIDERS);
3757 			break;
3758 		case 26:
3759 			lp->patterned_rule = NRAND(LIFE_26GLIDERS);
3760 			break;
3761 		}
3762 		copyFromPatternedRule(lp->neighbors, &lp->param,
3763 			lp->patterned_rule);
3764 		printRule(lp->neighbors, lp->ruleString, lp->param,
3765 			MI_IS_VERBOSE(mi));
3766 	} else {
3767 		lp->param.birth = lp->input_param.birth;
3768 		lp->param.survival = lp->input_param.survival;
3769 		lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
3770 		printRule(lp->neighbors, lp->ruleString, lp->param,
3771 			MI_IS_VERBOSE(mi));
3772 	}
3773 	if (!lp->eraserhead.next) {
3774 		lp->metaDist = (double) NRAND(360);
3775 		lp->metaAlt = (double) NRAND(360);
3776 		lp->metaAzm = (double) NRAND(360);
3777 		lp->ncols = MAXCOLUMNS;
3778 		lp->nrows = MAXROWS;
3779 		lp->nstacks = MAXSTACKS;
3780 		lp->ox = lp->ncols / 2;
3781 		lp->oy = lp->nrows / 2;
3782 		lp->oz = lp->nstacks / 2;
3783 
3784 		initList(lp);
3785 	} else {
3786 		endList(lp);
3787 	}
3788 	lp->colors[0] = MI_BLACK_PIXEL(mi);
3789 	if (MI_NPIXELS(mi) > 2) {
3790 #ifdef DISTRIB_COLOR
3791 		i = NRAND(3);
3792 
3793 		lp->colors[i + 1] = MI_PIXEL(mi,
3794 			NRAND(MI_NPIXELS(mi) / COLORBASE));
3795 		lp->colors[(i + 1) % 3 + 1] = MI_PIXEL(mi,
3796 			NRAND(MI_NPIXELS(mi) / COLORBASE) +
3797 			MI_NPIXELS(mi) / COLORBASE);
3798 		lp->colors[(i + 2) % 3 + 1] = MI_PIXEL(mi,
3799 			NRAND(MI_NPIXELS(mi) / COLORBASE) +
3800 			2 * MI_NPIXELS(mi) / COLORBASE);
3801 #else
3802 		int j = NRAND(MI_NPIXELS(mi));
3803 		i = NRAND(3);
3804 		lp->colors[i + 1] =  MI_PIXEL(mi, j);
3805 		lp->colors[(i + 1) % 3 + 1] =
3806 			MI_PIXEL(mi, (j + 2) % MI_NPIXELS(mi));
3807 		lp->colors[(i + 2) % 3 + 1] =
3808 			MI_PIXEL(mi, (j + 4) % MI_NPIXELS(mi));
3809 #endif
3810 	} else {
3811 		/*lp->colors[1] = lp->colors[2] = lp->colors[3] =
3812 			MI_WHITE_PIXEL(mi);*/
3813 		i = NRAND(3);
3814 
3815 		lp->colors[1] = NRAND(NUMSTIPPLES - 1);
3816 		lp->colors[2] = NRAND(NUMSTIPPLES - 1);
3817 		while (lp->colors[2] == lp->colors[1])
3818 			lp->colors[2] = NRAND(NUMSTIPPLES - 1);
3819 		lp->colors[3] = NRAND(NUMSTIPPLES - 1);
3820 		while (lp->colors[3] == lp->colors[1] ||
3821 			lp->colors[3] == lp->colors[2])
3822 			lp->colors[3] = NRAND(NUMSTIPPLES - 1);
3823 	}
3824 	lp->colors[4] = MI_WHITE_PIXEL(mi);
3825 	lp->width = MI_WIDTH(mi);
3826 	lp->height = MI_HEIGHT(mi);
3827 	lp->memstart = 1;
3828 	lp->noChangeCount = False;
3829 	/*lp->tablesMade = 0; */
3830 
3831 	if (MI_IS_FULLRANDOM(mi)) {
3832 		lp->wireframe = (NRAND(16) == 0);
3833 	} else {
3834 		lp->wireframe = MI_IS_WIREFRAME(mi);
3835 	}
3836 
3837 	MI_CLEARWINDOW(mi);
3838 	lp->painted = False;
3839 
3840 	lissajous(lp);
3841 
3842 	lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
3843 	npats = 0;
3844 	switch (lp->neighbors) {
3845 	case 12:
3846 		if ((unsigned) lp->patterned_rule < LIFE_12RULES)
3847 			npats = patterns_12rules[lp->patterned_rule];
3848 		break;
3849 	case 14:
3850 		if ((unsigned) lp->patterned_rule < LIFE_14RULES)
3851 			npats = patterns_14rules[lp->patterned_rule];
3852 		break;
3853 	case 18:
3854 		if ((unsigned) lp->patterned_rule < LIFE_18RULES)
3855 			npats = patterns_18rules[lp->patterned_rule];
3856 		break;
3857 	case -18:
3858 		if ((unsigned) lp->patterned_rule < LIFE__18RULES)
3859 			npats = patterns__18rules[lp->patterned_rule];
3860 		break;
3861 	case 20:
3862 		if ((unsigned) lp->patterned_rule < LIFE_20RULES)
3863 			npats = patterns_20rules[lp->patterned_rule];
3864 		break;
3865 	case 22:
3866 		if ((unsigned) lp->patterned_rule < LIFE_22RULES)
3867 			npats = patterns_22rules[lp->patterned_rule];
3868 		break;
3869 	case 26:
3870 		if ((unsigned) lp->patterned_rule < LIFE_26RULES)
3871 			npats = patterns_26rules[lp->patterned_rule];
3872 		break;
3873 	}
3874 	if (glidersearch || patternsearch) {
3875 		/* trying to find new patterns, formerly 10x10x10 */
3876 		RandomSoup(mi, SOUPPERCENT, SOUPSIZE(lp->ncols),
3877 			SOUPSIZE(lp->nrows), SOUPSIZE(lp->nstacks));
3878 	} else {
3879 		if (!filePattern)
3880 			lp->pattern = NRAND(npats + 2);
3881 		if (lp->pattern >= npats && !filePattern) {
3882 			if (!RandomSoup(mi, SOUPPERCENT, SOUPSIZE(lp->ncols),
3883 			    SOUPSIZE(lp->nrows), SOUPSIZE(lp->nstacks))) {
3884 				if (lp->eraserhead.next != NULL)
3885 					endList(lp);
3886 				return;
3887 			}
3888 		} else {
3889 			if (!GetPattern(mi, lp->patterned_rule, lp->pattern)) {
3890 				if (lp->eraserhead.next != NULL)
3891 					endList(lp);
3892 				return;
3893 			}
3894 		}
3895 	}
3896 
3897 	if (lp->dbuf != None) {
3898 		XFreePixmap(display, lp->dbuf);
3899 		lp->dbuf = None;
3900 	}
3901 	if ((lp->dbuf = XCreatePixmap(display, window,
3902 		lp->width, lp->height, MI_DEPTH(mi))) == None) {
3903 		free_life3d_screen(display, lp);
3904 		return;
3905 	}
3906 	DrawScreen(mi);
3907 }
3908 
3909 ENTRYPOINT void
draw_life3d(ModeInfo * mi)3910 draw_life3d(ModeInfo * mi)
3911 {
3912 	life3dstruct *lp;
3913 
3914 	if (life3ds == NULL)
3915 		return;
3916 	lp = &life3ds[MI_SCREEN(mi)];
3917 	if (lp->eraserhead.next == NULL)
3918 		return;
3919 
3920 	if (!RunLife3D(lp)) {
3921 		if (lp->eraserhead.next != NULL)
3922 			endList(lp);
3923 		return;
3924 	}
3925 	lissajous(lp);
3926 	if (lp->visible) /* kill static life also */
3927 		DrawScreen(mi);
3928 
3929 	MI_IS_DRAWN(mi) = True;
3930 
3931 	if (!lp->visible || lp->noChangeCount >= 8) {
3932 		/*CountCells3D(lp) == 0) */
3933 		init_life3d(mi);
3934 	} else if (++lp->generation > MI_CYCLES(mi)) {
3935 		if (patternsearch || glidersearch)
3936 			printList(lp, 1);
3937 		init_life3d(mi);
3938 	} else {
3939 		if (MI_IS_VERBOSE(mi))
3940 			(void) fprintf(stdout, "%s (%d cells)\n",
3941 				lp->nameString, lp->ncells);
3942 		if (patternsearch || glidersearch) {
3943 			if (lp->generation == MI_CYCLES(mi) - repeat + 1)
3944 				printList(lp, 0);
3945 		}
3946 		lp->painted = True;
3947 	}
3948 
3949 	/*
3950 	 * generate a randomized shooter aimed roughly toward the center of the
3951 	 * screen after batchcount.
3952 	 */
3953 
3954 	if (MI_COUNT(mi)) {
3955 		if (lp->generation && lp->generation %
3956 				((MI_COUNT(mi) < 0) ? 1 : MI_COUNT(mi)) == 0)
3957 			if (!shooter(lp)) {
3958 				if (lp->eraserhead.next != NULL)
3959 					endList(lp);
3960 			}
3961 	}
3962 }
3963 
3964 ENTRYPOINT void
release_life3d(ModeInfo * mi)3965 release_life3d(ModeInfo * mi)
3966 {
3967 	if (life3ds != NULL) {
3968 		int screen;
3969 
3970 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
3971 			free_life3d_screen(MI_DISPLAY(mi), &life3ds[screen]);
3972 		free(life3ds);
3973 		life3ds = (life3dstruct *) NULL;
3974 	}
3975 }
3976 
3977 #ifndef STANDALONE
3978 ENTRYPOINT void
refresh_life3d(ModeInfo * mi)3979 refresh_life3d(ModeInfo * mi)
3980 {
3981 	life3dstruct *lp;
3982 
3983 	if (life3ds == NULL)
3984 		return;
3985 	lp = &life3ds[MI_SCREEN(mi)];
3986 
3987 	if (lp->painted) {
3988 		MI_CLEARWINDOW(mi);
3989 	}
3990 }
3991 
3992 ENTRYPOINT void
change_life3d(ModeInfo * mi)3993 change_life3d(ModeInfo * mi)
3994 {
3995 	int npats;
3996 	life3dstruct *lp;
3997 
3998 	if (life3ds == NULL)
3999 		return;
4000 	lp = &life3ds[MI_SCREEN(mi)];
4001 
4002 	lp->generation = 0;
4003 
4004 	if (lp->eraserhead.next != NULL)
4005 		endList(lp);
4006 	/*lp->tablesMade = 0; */
4007 
4008 	MI_CLEARWINDOW(mi);
4009 
4010 	if (!filePattern)
4011 		lp->pattern++;
4012 	lp->patterned_rule = codeToPatternedRule(lp->neighbors, lp->param);
4013 	npats = 0;
4014 	switch (lp->neighbors) {
4015 	case 12:
4016 		if ((unsigned) lp->patterned_rule < LIFE_12RULES)
4017 			npats = patterns_12rules[lp->patterned_rule];
4018 		break;
4019 	case 14:
4020 		if ((unsigned) lp->patterned_rule < LIFE_14RULES)
4021 			npats = patterns_14rules[lp->patterned_rule];
4022 		break;
4023 	case 18:
4024 		if ((unsigned) lp->patterned_rule < LIFE_18RULES)
4025 			npats = patterns_18rules[lp->patterned_rule];
4026 		break;
4027 	case -18:
4028 		if ((unsigned) lp->patterned_rule < LIFE__18RULES)
4029 			npats = patterns__18rules[lp->patterned_rule];
4030 		break;
4031 	case 20:
4032 		if ((unsigned) lp->patterned_rule < LIFE_20RULES)
4033 			npats = patterns_20rules[lp->patterned_rule];
4034 		break;
4035 	case 22:
4036 		if ((unsigned) lp->patterned_rule < LIFE_22RULES)
4037 			npats = patterns_22rules[lp->patterned_rule];
4038 		break;
4039 	case 26:
4040 		if ((unsigned) lp->patterned_rule < LIFE_26RULES)
4041 			npats = patterns_26rules[lp->patterned_rule];
4042 		break;
4043 	}
4044 	if (lp->pattern >= npats + 2) {
4045 		lp->pattern = 0;
4046 		if (lp->allPatterns) {
4047 			lp->patterned_rule++;
4048 			switch (lp->neighbors) {
4049 			case 12:
4050 				if ((unsigned) lp->patterned_rule >= LIFE_12RULES)
4051 					lp->patterned_rule = 0;
4052 				break;
4053 			case 14:
4054 				if ((unsigned) lp->patterned_rule >= LIFE_14RULES)
4055 					lp->patterned_rule = 0;
4056 				break;
4057 			case 18:
4058 				if ((unsigned) lp->patterned_rule >= LIFE_18RULES)
4059 					lp->patterned_rule = 0;
4060 				break;
4061 			case -18:
4062 				if ((unsigned) lp->patterned_rule >= LIFE__18RULES)
4063 					lp->patterned_rule = 0;
4064 				break;
4065 			case 20:
4066 				if ((unsigned) lp->patterned_rule >= LIFE_20RULES)
4067 					lp->patterned_rule = 0;
4068 				break;
4069 			case 22:
4070 				if ((unsigned) lp->patterned_rule >= LIFE_22RULES)
4071 					lp->patterned_rule = 0;
4072 				break;
4073 			case 26:
4074 				if ((unsigned) lp->patterned_rule >= LIFE_26RULES)
4075 					lp->patterned_rule = 0;
4076 				break;
4077 			}
4078 			copyFromPatternedRule(lp->neighbors, &lp->param,
4079 				lp->patterned_rule);
4080 			printRule(lp->neighbors, lp->ruleString, lp->param,
4081 				MI_IS_VERBOSE(mi));
4082 		} else if (lp->allGliders) {
4083 			lp->patterned_rule++;
4084 			switch (lp->neighbors) {
4085 			case 12:
4086 				if ((unsigned) lp->patterned_rule >= LIFE_12GLIDERS)
4087 					lp->patterned_rule = 0;
4088 				break;
4089 			case 14:
4090 				if ((unsigned) lp->patterned_rule >= LIFE_14GLIDERS)
4091 					lp->patterned_rule = 0;
4092 				break;
4093 			case 18:
4094 				if ((unsigned) lp->patterned_rule >= LIFE_18GLIDERS)
4095 					lp->patterned_rule = 0;
4096 				break;
4097 			case -18:
4098 				if ((unsigned) lp->patterned_rule >= LIFE__18GLIDERS)
4099 					lp->patterned_rule = 0;
4100 				break;
4101 			case 20:
4102 				if ((unsigned) lp->patterned_rule >= LIFE_20GLIDERS)
4103 					lp->patterned_rule = 0;
4104 				break;
4105 			case 22:
4106 				if ((unsigned) lp->patterned_rule >= LIFE_22GLIDERS)
4107 					lp->patterned_rule = 0;
4108 				break;
4109 			case 26:
4110 				if ((unsigned) lp->patterned_rule >= LIFE_26GLIDERS)
4111 					lp->patterned_rule = 0;
4112 				break;
4113 			}
4114 			copyFromPatternedRule(lp->neighbors, &lp->param,
4115 				lp->patterned_rule);
4116 			printRule(lp->neighbors, lp->ruleString, lp->param,
4117 				MI_IS_VERBOSE(mi));
4118 		}
4119 	}
4120 	if (!serial && !filePattern)
4121 		lp->pattern = NRAND(npats + 2);
4122 	if (lp->pattern >= npats) {
4123 		if (!RandomSoup(mi, SOUPPERCENT, SOUPSIZE(lp->ncols),
4124 				SOUPSIZE(lp->nrows), SOUPSIZE(lp->nstacks))) {
4125 			if (lp->eraserhead.next != NULL)
4126 				endList(lp);
4127 			return;
4128 		}
4129 	} else {
4130 		if (!GetPattern(mi, lp->patterned_rule, lp->pattern)) {
4131 			if (lp->eraserhead.next != NULL)
4132 				endList(lp);
4133 			return;
4134 		}
4135 	}
4136 	DrawScreen(mi);
4137 }
4138 #endif
4139 
4140 XSCREENSAVER_MODULE ("Life3d", life3d)
4141 
4142 #endif /* MODE_life3d */
4143