1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* demon --- David Griffeath's cellular automata */
3
4 #if 0
5 static const char sccsid[] = "@(#)demon.c 5.24 2007/01/18 xlockmore";
6
7 #endif
8
9 /*-
10 * Copyright (c) 1995 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 * 18-Jan-2007: Added vertical option.
26 * 01-Nov-2000: Allocation checks
27 * 10-May-1997: Compatible with xscreensaver
28 * 16-Apr-1997: -neighbors 3, 9 (not sound mathematically), 12, and 8 added
29 * 30-May-1996: Ron Hitchens <ron@idiom.com>
30 * Fixed memory management that caused leaks
31 * 14-Apr-1996: -neighbors 6 runtime-time option added
32 * 21-Aug-1995: Coded from A.K. Dewdney's "Computer Recreations", Scientific
33 * American Magazine" Aug 1989 pp 102-105. Also very similar
34 * to hodgepodge machine described in A.K. Dewdney's "Computer
35 * Recreations", Scientific American Magazine" Aug 1988
36 * pp 104-107. Also used life.c as a guide.
37 */
38
39 /*-
40 * A cellular universe of 4 phases debris, droplets, defects, and demons.
41 */
42
43 /*-
44 Grid Number of Neighbors
45 ---- ------------------
46 Square 4 or 8
47 Hexagon 6
48 Triangle 3, 9, or 12
49 Pentagon 5 or 7
50 */
51
52 #ifdef STANDALONE
53 #define MODE_demon
54 #define DEFAULTS "*delay: 50000 \n" \
55 "*count: 0 \n" \
56 "*cycles: 1000 \n" \
57 "*size: -7 \n" \
58 "*ncolors: 64 \n" \
59
60 # define free_demon 0
61 # define reshape_demon 0
62 # define demon_handle_event 0
63 #define UNIFORM_COLORS
64 #include "xlockmore.h" /* in xscreensaver distribution */
65 #else /* STANDALONE */
66 #include "xlock.h" /* in xlockmore distribution */
67 #define DO_STIPPLE
68 #endif /* STANDALONE */
69 #include "automata.h"
70
71 #ifdef MODE_demon
72
73 /*-
74 * neighbors of 0 randomizes it between 3, 4, 5, 6, 7, 8, 9, and 12.
75 */
76 #define DEF_NEIGHBORS "0" /* choose random value */
77 #define DEF_VERTICAL "False"
78 #define DEF_COLORS_ONLY "False"
79
80 static int neighbors;
81 static Bool vertical;
82 static Bool colorsOnly;
83
84 static XrmOptionDescRec opts[] =
85 {
86 {(char *) "-neighbors", (char *) ".demon.neighbors", XrmoptionSepArg, (caddr_t) NULL},
87 {(char *) "-vertical", (char *) ".demon.vertical", XrmoptionNoArg, (caddr_t) "on"},
88 {(char *) "+vertical", (char *) ".demon.vertical", XrmoptionNoArg, (caddr_t) "off"},
89 {(char *) "-colorsonly", (char *) ".demon.colorsonly", XrmoptionNoArg, (caddr_t) "on"},
90 {(char *) "+colorsonly", (char *) ".demon.colorsonly", XrmoptionNoArg, (caddr_t) "off"}
91
92 };
93 static argtype vars[] =
94 {
95 {(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
96 {(void *) & vertical, (char *) "vertical", (char *) "Vertical", (char *) DEF_VERTICAL, t_Bool},
97 {(void *) & colorsOnly, (char *) "colorsonly", (char *) "ColorsOnly", (char *) DEF_COLORS_ONLY, t_Bool}
98 };
99 static OptionStruct desc[] =
100 {
101 {(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12, pentagons 5 or 7"},
102 {(char *) "-/+vertical", (char *) "change orientation for hexagons and triangles"},
103 {(char *) "-/+colorsonly", (char *) "no black cells"}
104 };
105
106 ENTRYPOINT ModeSpecOpt demon_opts =
107 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
108
109 #ifdef USE_MODULES
110 ModStruct demon_description =
111 {"demon", "init_demon", "draw_demon", "release_demon",
112 "refresh_demon", "init_demon", (char *) NULL, &demon_opts,
113 50000, 0, 1000, -7, 64, 1.0, "",
114 "Shows Griffeath's cellular automata", 0, NULL};
115
116 #endif
117
118 #define DEMONBITS(n,w,h)\
119 if ((dp->pixmaps[dp->init_bits]=\
120 XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
121 free_demon_screen(display,dp); return;} else {dp->init_bits++;}
122
123 #define REDRAWSTEP 2000 /* How many cells to draw per cycle */
124 #define MINSTATES 2
125 #define MINGRIDSIZE 24
126 #define MINSIZE 4
127 #define NEIGHBORKINDS 8
128
129 /* Singly linked list */
130 typedef struct _CellList {
131 XPoint pt;
132 struct _CellList *next;
133 } CellList;
134
135 typedef struct {
136 Bool vertical;
137 Bool colorsOnly;
138 int generation;
139 int xs, ys;
140 int xb, yb;
141 int nrows, ncols;
142 int width, height;
143 int states;
144 int state;
145 int redrawing, redrawpos;
146 int *ncells;
147 CellList **cellList;
148 unsigned char *oldcell, *newcell;
149 int neighbors, polygon;
150 int init_bits;
151 GC stippledGC;
152 Pixmap pixmaps[NUMSTIPPLES - 1];
153 union {
154 XPoint hexagon[7];
155 XPoint triangle[2][4];
156 XPoint pentagon[4][6];
157 } shape;
158 } demonstruct;
159
160 static char plots[2][NEIGHBORKINDS] =
161 {
162 {3, 4, 5, 6, 7, 8, 9, 12}, /* Neighborhoods */
163 {12, 16, 17, 18, 19, 20, 22, 24} /* Number of states */
164 };
165
166 static demonstruct *demons = (demonstruct *) NULL;
167
168 static void
drawCell(ModeInfo * mi,int col,int row,unsigned char state)169 drawCell(ModeInfo * mi, int col, int row, unsigned char state)
170 {
171 demonstruct *dp = &demons[MI_SCREEN(mi)];
172 Display *display = MI_DISPLAY(mi);
173 Window window = MI_WINDOW(mi);
174 GC gc;
175
176 if (!state && !dp->colorsOnly) {
177 XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
178 gc = MI_GC(mi);
179 } else if (MI_NPIXELS(mi) >= NUMSTIPPLES) {
180 int offset = (dp->colorsOnly) ? 0 : 1;
181
182 XSetForeground(display, MI_GC(mi),
183 MI_PIXEL(mi, (((int) state - offset) * MI_NPIXELS(mi) /
184 (dp->states - offset)) % MI_NPIXELS(mi)));
185 gc = MI_GC(mi);
186 } else if (!state) {
187 XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
188 gc = MI_GC(mi);
189 } else {
190 XGCValues gcv;
191
192 gcv.stipple = dp->pixmaps[(state - 1) % (NUMSTIPPLES - 1)];
193 gcv.foreground = MI_WHITE_PIXEL(mi);
194 gcv.background = MI_BLACK_PIXEL(mi);
195 XChangeGC(display, dp->stippledGC,
196 GCStipple | GCForeground | GCBackground, &gcv);
197 gc = dp->stippledGC;
198 }
199 if (dp->neighbors == 6) {
200 int ccol = 2 * col + !(row & 1), crow = 2 * row;
201
202 if (dp->vertical) {
203 dp->shape.hexagon[0].x = dp->xb + ccol * dp->xs;
204 dp->shape.hexagon[0].y = dp->yb + crow * dp->ys;
205 } else {
206 dp->shape.hexagon[0].y = dp->xb + ccol * dp->xs;
207 dp->shape.hexagon[0].x = dp->yb + crow * dp->ys;
208 }
209 if (dp->xs == 1 && dp->ys == 1)
210 XDrawPoint(display, window, gc,
211 dp->shape.hexagon[0].x,
212 dp->shape.hexagon[0].y);
213 else
214 XFillPolygon(display, window, gc,
215 dp->shape.hexagon, 6,
216 Convex, CoordModePrevious);
217 } else if (dp->neighbors == 4 || dp->neighbors == 8) {
218 XFillRectangle(display, window, gc,
219 dp->xb + dp->xs * col, dp->yb + dp->ys * row,
220 dp->xs - (dp->xs > 3), dp->ys - (dp->ys > 3));
221 } else if (dp->neighbors == 5 || dp->neighbors == 7) {
222 int map[4] = {2, 0, 1, 3};
223 int orient = ((row & 1) * 2 + col) % 4;
224 int offsetX = 0, offsetY = 0;
225 switch (orient) {
226 case 0: /* up */
227 offsetX = dp->xs;
228 break;
229 case 1: /* down */
230 offsetY = 3 * dp->ys / 2 - 1;
231 break;
232 case 2: /* left */
233 offsetX = -dp->xs / 2;
234 offsetY = 3 * dp->ys / 4;
235 break;
236 case 3: /* right */
237 offsetX = 3 * dp->xs / 2 - 1;
238 offsetY = 3 * dp->ys / 4;
239 break;
240 default:
241 (void) printf("wrong orient %d\n", orient);
242 }
243 orient = map[orient];
244 dp->shape.pentagon[orient][0].x = dp->xb +
245 col * dp->xs + offsetX;
246 dp->shape.pentagon[orient][0].y = dp->yb +
247 row * dp->ys + offsetY;
248 if (dp->xs <= 2 || dp->ys <= 2)
249 XDrawPoint(display, window, gc,
250 dp->shape.pentagon[orient][0].x,
251 dp->shape.pentagon[orient][0].y);
252 else
253 XFillPolygon(display, window, gc,
254 dp->shape.pentagon[orient], 5, Convex, CoordModePrevious);
255 } else { /* TRI */
256 int orient = (col + row) % 2; /* O left 1 right */
257 Bool small = (dp->xs <= 3 || dp->ys <= 3);
258
259 if (dp->vertical) {
260 dp->shape.triangle[orient][0].x = dp->xb + col * dp->xs;
261 dp->shape.triangle[orient][0].y = dp->yb + row * dp->ys;
262 if (small)
263 dp->shape.triangle[orient][0].x +=
264 ((orient) ? -1 : 1);
265 else
266 dp->shape.triangle[orient][0].x +=
267 (dp->xs / 2 - 1) * ((orient) ? 1 : -1);
268 } else {
269 dp->shape.triangle[orient][0].y = dp->xb + col * dp->xs;
270 dp->shape.triangle[orient][0].x = dp->yb + row * dp->ys;
271 if (small)
272 dp->shape.triangle[orient][0].y +=
273 ((orient) ? -1 : 1);
274 else
275 dp->shape.triangle[orient][0].y +=
276 (dp->xs / 2 - 1) * ((orient) ? 1 : -1);
277 }
278 if (small)
279 XDrawPoint(display, window, gc,
280 dp->shape.triangle[orient][0].x,
281 dp->shape.triangle[orient][0].y);
282 else {
283 XFillPolygon(display, window, gc,
284 dp->shape.triangle[orient], 3,
285 Convex, CoordModePrevious);
286 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
287 XDrawLines(display, window, gc,
288 dp->shape.triangle[orient], 4,
289 CoordModePrevious);
290 }
291 }
292 }
293
294 static Bool
addtolist(ModeInfo * mi,int col,int row,unsigned char state)295 addtolist(ModeInfo * mi, int col, int row, unsigned char state)
296 {
297 demonstruct *dp = &demons[MI_SCREEN(mi)];
298 CellList *current;
299
300 current = dp->cellList[state];
301 if ((dp->cellList[state] = (CellList *)
302 malloc(sizeof (CellList))) == NULL) {
303 return False;
304 }
305 dp->cellList[state]->pt.x = col;
306 dp->cellList[state]->pt.y = row;
307 dp->cellList[state]->next = current;
308 dp->ncells[state]++;
309 return True;
310 }
311
312 #ifdef DEBUG
313 static void
print_state(ModeInfo * mi,int state)314 print_state(ModeInfo * mi, int state)
315 {
316 demonstruct *dp = &demons[MI_SCREEN(mi)];
317 CellList *locallist;
318 int i = 0;
319
320 locallist = dp->cellList[state];
321 (void) printf("state %d\n", state);
322 while (locallist) {
323 (void) printf("%d x %d, y %d\n", i,
324 locallist->pt.x, locallist->pt.y);
325 locallist = locallist->next;
326 i++;
327 }
328 }
329
330 #endif
331
332 static void
free_state(demonstruct * dp,int state)333 free_state(demonstruct * dp, int state)
334 {
335 CellList *current;
336
337 while (dp->cellList[state]) {
338 current = dp->cellList[state];
339 dp->cellList[state] = dp->cellList[state]->next;
340 free(current);
341 }
342 dp->cellList[state] = (CellList *) NULL;
343 if (dp->ncells != NULL)
344 dp->ncells[state] = 0;
345 }
346
347
348 static void
free_list(demonstruct * dp)349 free_list(demonstruct * dp)
350 {
351 int state;
352
353 for (state = 0; state < dp->states; state++)
354 free_state(dp, state);
355 free(dp->cellList);
356 dp->cellList = (CellList **) NULL;
357 }
358
359 static void
free_struct(demonstruct * dp)360 free_struct(demonstruct * dp)
361 {
362 if (dp->cellList != NULL) {
363 free_list(dp);
364 }
365 if (dp->ncells != NULL) {
366 free(dp->ncells);
367 dp->ncells = (int *) NULL;
368 }
369 if (dp->oldcell != NULL) {
370 free(dp->oldcell);
371 dp->oldcell = (unsigned char *) NULL;
372 }
373 if (dp->newcell != NULL) {
374 free(dp->newcell);
375 dp->newcell = (unsigned char *) NULL;
376 }
377 }
378
379 static void
free_demon_screen(Display * display,demonstruct * dp)380 free_demon_screen(Display *display, demonstruct *dp)
381 {
382 int shade;
383
384 if (dp == NULL) {
385 return;
386 }
387 if (dp->stippledGC != None) {
388 XFreeGC(display, dp->stippledGC);
389 dp->stippledGC = None;
390 }
391 for (shade = 0; shade < dp->init_bits; shade++) {
392 XFreePixmap(display, dp->pixmaps[shade]);
393 }
394 dp->init_bits = 0;
395 free_struct(dp);
396 dp = NULL;
397 }
398
399 static Bool
draw_state(ModeInfo * mi,int state)400 draw_state(ModeInfo * mi, int state)
401 {
402 demonstruct *dp = &demons[MI_SCREEN(mi)];
403 Display *display = MI_DISPLAY(mi);
404 Window window = MI_WINDOW(mi);
405 GC gc;
406 XRectangle *rects;
407 CellList *current;
408
409 if (!state && !dp->colorsOnly) {
410 XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
411 gc = MI_GC(mi);
412 } else if (MI_NPIXELS(mi) >= NUMSTIPPLES) {
413 int offset = (dp->colorsOnly) ? 0 : 1;
414
415 XSetForeground(display, MI_GC(mi),
416 MI_PIXEL(mi, (((int) state - offset) * MI_NPIXELS(mi) /
417 (dp->states - offset)) % MI_NPIXELS(mi)));
418 gc = MI_GC(mi);
419 } else if (!state) {
420 XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
421 gc = MI_GC(mi);
422 } else {
423 XGCValues gcv;
424
425 gcv.stipple = dp->pixmaps[(state - 1) % (NUMSTIPPLES - 1)];
426 gcv.foreground = MI_WHITE_PIXEL(mi);
427 gcv.background = MI_BLACK_PIXEL(mi);
428 XChangeGC(display, dp->stippledGC,
429 GCStipple | GCForeground | GCBackground, &gcv);
430 gc = dp->stippledGC;
431 }
432 if (dp->neighbors == 6) { /* Draw right away, slow */
433 current = dp->cellList[state];
434 while (current) {
435 int col = current->pt.x;
436 int row = current->pt.y;
437 int ccol = 2 * col + !(row & 1);
438 int crow = 2 * row;
439
440 if (dp->vertical) {
441 dp->shape.hexagon[0].x = dp->xb + ccol * dp->xs;
442 dp->shape.hexagon[0].y = dp->yb + crow * dp->ys;
443 } else {
444 dp->shape.hexagon[0].y = dp->xb + ccol * dp->xs;
445 dp->shape.hexagon[0].x = dp->yb + crow * dp->ys;
446 }
447 if (dp->xs == 1 && dp->ys == 1)
448 XDrawPoint(display, window, gc,
449 dp->shape.hexagon[0].x,
450 dp->shape.hexagon[0].y);
451 else
452 XFillPolygon(display, window, gc,
453 dp->shape.hexagon, 6,
454 Convex, CoordModePrevious);
455 current = current->next;
456 }
457 } else if (dp->neighbors == 4 || dp->neighbors == 8) {
458 /* Take advantage of XDrawRectangles */
459 int ncells = 0;
460
461 /* Create Rectangle list from part of the cellList */
462 if ((rects = (XRectangle *) malloc(dp->ncells[state] *
463 sizeof (XRectangle))) == NULL) {
464 return False;
465 }
466 current = dp->cellList[state];
467 while (current) {
468 rects[ncells].x = dp->xb + current->pt.x * dp->xs;
469 rects[ncells].y = dp->yb + current->pt.y * dp->ys;
470 rects[ncells].width = dp->xs - (dp->xs > 3);
471 rects[ncells].height = dp->ys - (dp->ys > 3);
472 current = current->next;
473 ncells++;
474 }
475 /* Finally get to draw */
476 XFillRectangles(display, window, gc, rects, ncells);
477 /* Free up rects list and the appropriate part of the cellList */
478 free(rects);
479 } else if (dp->neighbors == 5 || dp->neighbors == 7) {
480 int map[4] = {2, 0, 1, 3};
481 current = dp->cellList[state];
482 while (current) {
483 int col = current->pt.x;
484 int row = current->pt.y;
485 int orient = ((row & 1) * 2 + col) % 4;
486 int offsetX = 0, offsetY = 0;
487
488 switch (orient) {
489 case 0: /* up */
490 offsetX = dp->xs;
491 break;
492 case 1: /* down */
493 offsetY = 3 * dp->ys / 2 - 1;
494 break;
495 case 2: /* left */
496 offsetX = -dp->xs / 2;
497 offsetY = 3 * dp->ys / 4;
498 break;
499 case 3: /* right */
500 offsetX = 3 * dp->xs / 2 - 1;
501 offsetY = 3 * dp->ys / 4;
502 break;
503 default:
504 (void) printf("wrong orient %d\n", orient);
505 }
506 orient = map[orient];
507 dp->shape.pentagon[orient][0].x = dp->xb +
508 col * dp->xs + offsetX;
509 dp->shape.pentagon[orient][0].y = dp->yb +
510 row * dp->ys + offsetY;
511 if (dp->xs <= 2 || dp->ys <= 2)
512 XDrawPoint(display, window, gc,
513 dp->shape.pentagon[orient][0].x,
514 dp->shape.pentagon[orient][0].y);
515 else
516 XFillPolygon(display, window, gc,
517 dp->shape.pentagon[orient], 5,
518 Convex, CoordModePrevious);
519 current = current->next;
520 }
521 } else { /* TRI */
522 XGCValues values;
523 GC borderGC = XCreateGC(display, window, 0, &values);
524 Bool small = (dp->xs <= 3 || dp->ys <= 3);
525
526 current = dp->cellList[state];
527 while (current) {
528 int col, row, orient;
529
530 col = current->pt.x;
531 row = current->pt.y;
532 orient = (col + row) % 2; /* O left 1 right */
533 if (dp->vertical) {
534 dp->shape.triangle[orient][0].x = dp->xb + col * dp->xs;
535 dp->shape.triangle[orient][0].y = dp->yb + row * dp->ys;
536 if (small)
537 dp->shape.triangle[orient][0].x +=
538 ((orient) ? -1 : 1);
539 else
540 dp->shape.triangle[orient][0].x +=
541 (dp->xs / 2 - 1) * ((orient) ? 1 : -1);
542 } else {
543 dp->shape.triangle[orient][0].y = dp->xb + col * dp->xs;
544 dp->shape.triangle[orient][0].x = dp->yb + row * dp->ys;
545 if (small)
546 dp->shape.triangle[orient][0].y +=
547 ((orient) ? -1 : 1);
548 else
549 dp->shape.triangle[orient][0].y +=
550 (dp->xs / 2 - 1) * ((orient) ? 1 : -1);
551 }
552 if (small)
553 XDrawPoint(display, window, gc,
554 dp->shape.triangle[orient][0].x,
555 dp->shape.triangle[orient][0].y);
556 else {
557 XFillPolygon(display, window, gc,
558 dp->shape.triangle[orient], 3,
559 Convex, CoordModePrevious);
560 if (borderGC != None) {
561 XSetForeground(display, borderGC, MI_BLACK_PIXEL(mi));
562 XDrawLines(display, window, borderGC,
563 dp->shape.triangle[orient], 4,
564 CoordModePrevious);
565 }
566 }
567 current = current->next;
568 }
569 if (borderGC != None)
570 XFreeGC(display, borderGC);
571 }
572 free_state(dp, state);
573 XFlush(display);
574 return True;
575 }
576
577 static void
RandomSoup(ModeInfo * mi)578 RandomSoup(ModeInfo * mi)
579 {
580 demonstruct *dp = &demons[MI_SCREEN(mi)];
581 int row, col, mrow = 0;
582
583 for (row = 0; row < dp->nrows; ++row) {
584 for (col = 0; col < dp->ncols; ++col) {
585 dp->oldcell[col + mrow] =
586 (unsigned char) LRAND() % ((unsigned char) dp->states);
587 if (!addtolist(mi, col, row, dp->oldcell[col + mrow]))
588 return; /* sparse soup */
589 }
590 mrow += dp->ncols;
591 }
592 }
593
594 static int
positionOfNeighbor(demonstruct * dp,int n,int col,int row)595 positionOfNeighbor(demonstruct * dp, int n, int col, int row)
596 {
597 int dir = n * (360 / dp->neighbors);
598
599 if (dp->polygon == 4 || dp->polygon == 6) {
600 switch (dir) {
601 case 0:
602 col = (col + 1 == dp->ncols) ? 0 : col + 1;
603 break;
604 case 45:
605 col = (col + 1 == dp->ncols) ? 0 : col + 1;
606 row = (!row) ? dp->nrows - 1 : row - 1;
607 break;
608 case 60:
609 if (!(row & 1))
610 col = (col + 1 == dp->ncols) ? 0 : col + 1;
611 row = (!row) ? dp->nrows - 1 : row - 1;
612 break;
613 case 90:
614 row = (!row) ? dp->nrows - 1 : row - 1;
615 break;
616 case 120:
617 if (row & 1)
618 col = (!col) ? dp->ncols - 1 : col - 1;
619 row = (!row) ? dp->nrows - 1 : row - 1;
620 break;
621 case 135:
622 col = (!col) ? dp->ncols - 1 : col - 1;
623 row = (!row) ? dp->nrows - 1 : row - 1;
624 break;
625 case 180:
626 col = (!col) ? dp->ncols - 1 : col - 1;
627 break;
628 case 225:
629 col = (!col) ? dp->ncols - 1 : col - 1;
630 row = (row + 1 == dp->nrows) ? 0 : row + 1;
631 break;
632 case 240:
633 if (row & 1)
634 col = (!col) ? dp->ncols - 1 : col - 1;
635 row = (row + 1 == dp->nrows) ? 0 : row + 1;
636 break;
637 case 270:
638 row = (row + 1 == dp->nrows) ? 0 : row + 1;
639 break;
640 case 300:
641 if (!(row & 1))
642 col = (col + 1 == dp->ncols) ? 0 : col + 1;
643 row = (row + 1 == dp->nrows) ? 0 : row + 1;
644 break;
645 case 315:
646 col = (col + 1 == dp->ncols) ? 0 : col + 1;
647 row = (row + 1 == dp->nrows) ? 0 : row + 1;
648 break;
649 default:
650 (void) fprintf(stderr, "wrong direction %d\n", dir);
651 }
652 } else if (dp->polygon == 3) {
653 if ((col + row) & 1) { /* right */
654 switch (dir) {
655 case 0:
656 col = (!col) ? dp->ncols - 1 : col - 1;
657 break;
658 case 30:
659 case 40:
660 col = (!col) ? dp->ncols - 1 : col - 1;
661 row = (row + 1 == dp->nrows) ? 0 : row + 1;
662 break;
663 case 60:
664 col = (!col) ? dp->ncols - 1 : col - 1;
665 if (row + 1 == dp->nrows)
666 row = 1;
667 else if (row + 2 == dp->nrows)
668 row = 0;
669 else
670 row = row + 2;
671 break;
672 case 80:
673 case 90:
674 if (row + 1 == dp->nrows)
675 row = 1;
676 else if (row + 2 == dp->nrows)
677 row = 0;
678 else
679 row = row + 2;
680 break;
681 case 120:
682 row = (row + 1 == dp->nrows) ? 0 : row + 1;
683 break;
684 case 150:
685 case 160:
686 col = (col + 1 == dp->ncols) ? 0 : col + 1;
687 row = (row + 1 == dp->nrows) ? 0 : row + 1;
688 break;
689 case 180:
690 col = (col + 1 == dp->ncols) ? 0 : col + 1;
691 break;
692 case 200:
693 case 210:
694 col = (col + 1 == dp->ncols) ? 0 : col + 1;
695 row = (!row) ? dp->nrows - 1 : row - 1;
696 break;
697 case 240:
698 row = (!row) ? dp->nrows - 1 : row - 1;
699 break;
700 case 270:
701 case 280:
702 if (!row)
703 row = dp->nrows - 2;
704 else if (!(row - 1))
705 row = dp->nrows - 1;
706 else
707 row = row - 2;
708 break;
709 case 300:
710 col = (!col) ? dp->ncols - 1 : col - 1;
711 if (!row)
712 row = dp->nrows - 2;
713 else if (!(row - 1))
714 row = dp->nrows - 1;
715 else
716 row = row - 2;
717 break;
718 case 320:
719 case 330:
720 col = (!col) ? dp->ncols - 1 : col - 1;
721 row = (!row) ? dp->nrows - 1 : row - 1;
722 break;
723 default:
724 (void) fprintf(stderr, "wrong direction %d\n",
725 dir);
726 }
727 } else { /* left */
728 switch (dir) {
729 case 0:
730 col = (col + 1 == dp->ncols) ? 0 : col + 1;
731 break;
732 case 30:
733 case 40:
734 col = (col + 1 == dp->ncols) ? 0 : col + 1;
735 row = (!row) ? dp->nrows - 1 : row - 1;
736 break;
737 case 60:
738 col = (col + 1 == dp->ncols) ? 0 : col + 1;
739 if (!row)
740 row = dp->nrows - 2;
741 else if (row == 1)
742 row = dp->nrows - 1;
743 else
744 row = row - 2;
745 break;
746 case 80:
747 case 90:
748 if (!row)
749 row = dp->nrows - 2;
750 else if (row == 1)
751 row = dp->nrows - 1;
752 else
753 row = row - 2;
754 break;
755 case 120:
756 row = (!row) ? dp->nrows - 1 : row - 1;
757 break;
758 case 150:
759 case 160:
760 col = (!col) ? dp->ncols - 1 : col - 1;
761 row = (!row) ? dp->nrows - 1 : row - 1;
762 break;
763 case 180:
764 col = (!col) ? dp->ncols - 1 : col - 1;
765 break;
766 case 200:
767 case 210:
768 col = (!col) ? dp->ncols - 1 : col - 1;
769 row = (row + 1 == dp->nrows) ? 0 : row + 1;
770 break;
771 case 240:
772 row = (row + 1 == dp->nrows) ? 0 : row + 1;
773 break;
774 case 270:
775 case 280:
776 if (row + 1 == dp->nrows)
777 row = 1;
778 else if (row + 2 == dp->nrows)
779 row = 0;
780 else
781 row = row + 2;
782 break;
783 case 300:
784 col = (col + 1 == dp->ncols) ? 0 : col + 1;
785 if (row + 1 == dp->nrows)
786 row = 1;
787 else if (row + 2 == dp->nrows)
788 row = 0;
789 else
790 row = row + 2;
791 break;
792 case 320:
793 case 330:
794 col = (col + 1 == dp->ncols) ? 0 : col + 1;
795 row = (row + 1 == dp->nrows) ? 0 : row + 1;
796 break;
797 default:
798 (void) fprintf(stderr, "wrong direction %d\n",
799 dir);
800 }
801 }
802 } else {
803 int orient = ((row & 1) * 2 + col) % 4;
804 switch (orient) { /* up, down, left, right */
805 case 0:
806 switch (dir) {
807 case 0:
808 col++;
809 break;
810 case 51: /* 7 */
811 case 72: /* 5 */
812 col = (col + 2 >= dp->ncols) ? 0 : col + 2;
813 break;
814 case 102: /* 7 corner */
815 col = (col + 3 >= dp->ncols) ? 1 : col + 3;
816 row = (row == 0) ? dp->nrows - 1 : row - 1;
817 break;
818 case 144: /* 5 */
819 case 153: /* 7 */
820 col++;
821 row = (row == 0) ? dp->nrows - 1 : row - 1;
822 break;
823 case 204: /* 7 */
824 case 216: /* 5 */
825 row = (row == 0) ? dp->nrows - 1 : row - 1;
826 break;
827 case 255: /* 7 */
828 col = (col == 0) ? dp->ncols - 1 : col - 1;
829 row = (row == 0) ? dp->nrows - 1 : row - 1;
830 break;
831 case 288: /* 5 */
832 case 306: /* 7 */
833 col = (col == 0) ? dp->ncols - 1 : col - 1;
834 break;
835 default:
836 (void) fprintf(stderr, "wrong direction %d\n",
837 dir);
838 }
839 break;
840 case 1:
841 switch (dir) {
842 case 0:
843 col--;
844 break;
845 case 51: /* 7 */
846 case 72: /* 5 */
847 col = (col == 1) ? dp->ncols - 1 : col - 2;
848 break;
849 case 102: /* 7 */
850 col = (col == 1) ? dp->ncols - 2 : col - 3;
851 row = (row + 1 == dp->nrows) ? 0 : row + 1;
852 break;
853 case 144: /* 5 */
854 case 153: /* 7 */
855 col--;
856 row = (row + 1 == dp->nrows) ? 0 : row + 1;
857 break;
858 case 204: /* 7 */
859 case 216: /* 5 */
860 row = (row + 1 == dp->nrows) ? 0 : row + 1;
861 break;
862 case 255: /* 7 */
863 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
864 row = (row + 1 == dp->nrows) ? 0 : row + 1;
865 break;
866 case 288: /* 5 */
867 case 306: /* 7 */
868 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
869 break;
870 default:
871 (void) fprintf(stderr, "wrong direction %d\n",
872 dir);
873 }
874 break;
875 case 2:
876 switch (dir) {
877 case 0:
878 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
879 break;
880 case 51: /* 7 */
881 case 72: /* 5 */
882 row = (row == 0) ? dp->nrows - 1 : row - 1;
883 col++;
884 break;
885 case 102: /* 7 */
886 col = (col == 0) ? dp->ncols - 1 : col - 1;
887 row = (row == 0) ? dp->nrows - 1 : row - 1;
888 break;
889 case 144: /* 5 */
890 case 153: /* 7 */
891 col = (col == 0) ? dp->ncols - 2 : col - 2;
892 break;
893 case 204: /* 7 */
894 case 216: /* 5 */
895 col = (col == 0) ? dp->ncols - 1 : col - 1;
896 break;
897 case 255: /* 7 */
898 row = (row + 1 == dp->nrows) ? 0 : row + 1;
899 col = (col == 0) ? dp->ncols - 1 : col - 1;
900 break;
901 case 288: /* 5 */
902 case 306: /* 7 */
903 row = (row + 1 == dp->nrows) ? 0 : row + 1;
904 break;
905 default:
906 (void) fprintf(stderr, "wrong direction %d\n",
907 dir);
908 }
909 break;
910 case 3:
911 switch (dir) {
912 case 0:
913 col--;
914 break;
915 case 51: /* 7 */
916 case 72: /* 5 */
917 col = (col == 0) ? dp->ncols - 1 : col - 1;
918 row = (row + 1 == dp->nrows) ? 0 : row + 1;
919 break;
920 case 102: /* 7 */
921 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
922 row = (row + 1 == dp->nrows) ? 0 : row + 1;
923 break;
924 case 144: /* 5 */
925 case 153: /* 7 */
926 col = (col + 2 >= dp->ncols) ? 1 : col + 2;
927 break;
928 case 204: /* 7 */
929 case 216: /* 5 */
930 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
931 break;
932 case 255: /* 7 */
933 col = (col + 1 >= dp->ncols) ? 0 : col + 1;
934 row = (row == 0) ? dp->nrows - 1 : row - 1;
935 break;
936 case 288: /* 5 */
937 case 306: /* 7 */
938 row = (row == 0) ? dp->nrows - 1 : row - 1;
939 break;
940 default:
941 (void) fprintf(stderr, "wrong direction %d\n",
942 dir);
943 }
944 break;
945 default:
946 (void) fprintf(stderr, "wrong orient %d\n",
947 orient);
948 }
949 }
950 return (row * dp->ncols + col);
951 }
952
953 ENTRYPOINT void
init_demon(ModeInfo * mi)954 init_demon(ModeInfo * mi)
955 {
956 Display *display = MI_DISPLAY(mi);
957 Window window = MI_WINDOW(mi);
958 int size = MI_SIZE(mi), nk;
959 demonstruct *dp;
960
961 MI_INIT(mi, demons);
962 dp = &demons[MI_SCREEN(mi)];
963
964 dp->generation = 0;
965 dp->redrawing = 0;
966 if (MI_NPIXELS(mi) < NUMSTIPPLES) {
967 if (dp->stippledGC == None) {
968 XGCValues gcv;
969
970 gcv.fill_style = FillOpaqueStippled;
971 if ((dp->stippledGC = XCreateGC(display, window,
972 GCFillStyle, &gcv)) == None) {
973 free_demon_screen(display, dp);
974 return;
975 }
976 }
977 if (dp->init_bits == 0) {
978 int i;
979
980 for (i = 1; i < NUMSTIPPLES; i++) {
981 DEMONBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
982 }
983 }
984 }
985 free_struct(dp);
986
987 for (nk = 0; nk < NEIGHBORKINDS; nk++) {
988 if (neighbors == plots[0][nk]) {
989 dp->neighbors = plots[0][nk];
990 break;
991 }
992 if (nk == NEIGHBORKINDS - 1) {
993 nk = NRAND(NEIGHBORKINDS);
994 dp->neighbors = plots[0][nk];
995 break;
996 }
997 }
998
999 dp->states = MI_COUNT(mi);
1000 if (dp->states < -MINSTATES)
1001 dp->states = NRAND(-dp->states - MINSTATES + 1) + MINSTATES;
1002 else if (dp->states < MINSTATES)
1003 dp->states = plots[1][nk];
1004 if ((dp->cellList = (CellList **) calloc(dp->states,
1005 sizeof (CellList *))) == NULL) {
1006 free_demon_screen(display, dp);
1007 return;
1008 }
1009 if ((dp->ncells = (int *) calloc(dp->states, sizeof (int))) == NULL) {
1010 free_demon_screen(display, dp);
1011 return;
1012 }
1013
1014 dp->state = 0;
1015 if (MI_IS_FULLRANDOM(mi)) {
1016 dp->vertical = (Bool) (LRAND() & 1);
1017 dp->colorsOnly = (Bool) (LRAND() & 1);
1018 } else {
1019 dp->vertical = vertical;
1020 dp->colorsOnly = colorsOnly;
1021 }
1022 dp->width = MI_WIDTH(mi);
1023 dp->height = MI_HEIGHT(mi);
1024 if (dp->neighbors == 6) {
1025 int nccols, ncrows, side;
1026
1027 dp->polygon = 6;
1028 if (!dp->vertical) {
1029 dp->height = MI_WIDTH(mi);
1030 dp->width = MI_HEIGHT(mi);
1031 }
1032 if (dp->width < 8)
1033 dp->width = 8;
1034 if (dp->height < 8)
1035 dp->height = 8;
1036 if (size < -MINSIZE)
1037 dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1038 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1039 else if (size < MINSIZE) {
1040 if (!size)
1041 dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
1042 else
1043 dp->ys = MINSIZE;
1044 } else
1045 dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1046 MINGRIDSIZE));
1047 dp->xs = dp->ys;
1048 nccols = MAX(dp->width / dp->xs - 2, 2);
1049 ncrows = MAX(dp->height / dp->ys - 1, 4);
1050 dp->ncols = nccols / 2;
1051 dp->nrows = 2 * (ncrows / 4);
1052 dp->xb = (dp->width - dp->xs * nccols) / 2 + dp->xs / 2;
1053 dp->yb = (dp->height - dp->ys * (ncrows / 2) * 2) / 2 +
1054 dp->ys / 3;
1055 for (side = 0; side < 6; side++) {
1056 if (dp->vertical) {
1057 dp->shape.hexagon[side].x =
1058 (dp->xs - 1) * hexagonUnit[side].x;
1059 dp->shape.hexagon[side].y =
1060 ((dp->ys - 1) * hexagonUnit[side].y /
1061 2) * 4 / 3;
1062 } else {
1063 dp->shape.hexagon[side].y =
1064 (dp->xs - 1) * hexagonUnit[side].x;
1065 dp->shape.hexagon[side].x =
1066 ((dp->ys - 1) * hexagonUnit[side].y /
1067 2) * 4 / 3;
1068 }
1069 }
1070 } else if (dp->neighbors == 4 || dp->neighbors == 8) {
1071 dp->polygon = 4;
1072 if (size < -MINSIZE)
1073 dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1074 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1075 else if (size < MINSIZE) {
1076 if (!size)
1077 dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
1078 else
1079 dp->ys = MINSIZE;
1080 } else
1081 dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1082 MINGRIDSIZE));
1083 dp->xs = dp->ys;
1084 dp->ncols = MAX(dp->width / dp->xs, 2);
1085 dp->nrows = MAX(dp->height / dp->ys, 2);
1086 dp->xb = (dp->width - dp->xs * dp->ncols) / 2;
1087 dp->yb = (dp->height - dp->ys * dp->nrows) / 2;
1088 } else if (dp->neighbors == 5 || dp->neighbors == 7) {
1089 int orient, side;
1090 dp->polygon = 5;
1091 if (dp->width < 2)
1092 dp->width = 2;
1093 if (dp->height < 2)
1094 dp->height = 2;
1095 if (size < -MINSIZE)
1096 dp->xs = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1097 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1098 else if (size < MINSIZE) {
1099 if (!size) {
1100 int min = MIN(dp->width, dp->height) / (4 * MINGRIDSIZE);
1101 int max = MIN(dp->width, dp->height) / (MINGRIDSIZE);
1102
1103 dp->xs = MAX(MINSIZE, min + NRAND(max - min + 1));
1104 } else
1105 dp->xs = MINSIZE;
1106 } else
1107 dp->xs = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1108 MINGRIDSIZE));
1109 dp->ys = dp->xs * 2;
1110 dp->ncols = (MAX((dp->width - 4) / dp->xs, 8) / 4) * 4;
1111 dp->nrows = (MAX((dp->height - dp->ys / 2) / dp->ys, 8)) / 2 * 2;
1112 dp->xb = (dp->width - dp->xs * dp->ncols) / 2;
1113 dp->yb = (dp->height - dp->ys * dp->nrows) / 2 - 2;
1114 for (orient = 0; orient < 4; orient++) {
1115 for (side = 0; side < 5; side++) {
1116 dp->shape.pentagon[orient][side].x =
1117 2 * (dp->xs - 1) * pentagonUnit[orient][side].x / 4;
1118 dp->shape.pentagon[orient][side].y =
1119 (dp->ys - 1) * pentagonUnit[orient][side].y / 4;
1120 }
1121 }
1122 } else { /* TRI */
1123 int orient, side;
1124
1125 dp->polygon = 3;
1126 if (!dp->vertical) {
1127 dp->height = MI_WIDTH(mi);
1128 dp->width = MI_HEIGHT(mi);
1129 }
1130 if (dp->width < 2)
1131 dp->width = 2;
1132 if (dp->height < 2)
1133 dp->height = 2;
1134 if (size < -MINSIZE)
1135 dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1136 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1137 else if (size < MINSIZE) {
1138 if (!size)
1139 dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
1140 else
1141 dp->ys = MINSIZE;
1142 } else
1143 dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
1144 MINGRIDSIZE));
1145 dp->xs = (int) (1.52 * dp->ys);
1146 dp->ncols = (MAX(dp->width / dp->xs - 1, 2) / 2) * 2;
1147 dp->nrows = (MAX(dp->height / dp->ys - 1, 2) / 2) * 2;
1148 dp->xb = (dp->width - dp->xs * dp->ncols) / 2 + dp->xs / 2;
1149 dp->yb = (dp->height - dp->ys * dp->nrows) / 2 + dp->ys / 2;
1150 for (orient = 0; orient < 2; orient++) {
1151 for (side = 0; side < 3; side++) {
1152 if (dp->vertical) {
1153 dp->shape.triangle[orient][side].x =
1154 (dp->xs - 2) * triangleUnit[orient][side].x;
1155 dp->shape.triangle[orient][side].y =
1156 (dp->ys - 2) * triangleUnit[orient][side].y;
1157 } else {
1158 dp->shape.triangle[orient][side].y =
1159 (dp->xs - 2) * triangleUnit[orient][side].x;
1160 dp->shape.triangle[orient][side].x =
1161 (dp->ys - 2) * triangleUnit[orient][side].y;
1162 }
1163 }
1164 }
1165 }
1166
1167 MI_CLEARWINDOW(mi);
1168
1169 if ((dp->oldcell = (unsigned char *)
1170 malloc(dp->ncols * dp->nrows * sizeof (unsigned char))) == NULL) {
1171 free_demon_screen(display, dp);
1172 return;
1173 }
1174
1175 if ((dp->newcell = (unsigned char *)
1176 malloc(dp->ncols * dp->nrows * sizeof (unsigned char))) == NULL) {
1177 free_demon_screen(display, dp);
1178 return;
1179 }
1180
1181 RandomSoup(mi);
1182 }
1183
1184 ENTRYPOINT void
draw_demon(ModeInfo * mi)1185 draw_demon(ModeInfo * mi)
1186 {
1187 int i, j, k, l, n, mj = 0;
1188 Bool change = False;
1189 demonstruct *dp;
1190
1191 if (demons == NULL)
1192 return;
1193 dp = &demons[MI_SCREEN(mi)];
1194 if (dp->cellList == NULL)
1195 return;
1196
1197 MI_IS_DRAWN(mi) = True;
1198 if (dp->state >= dp->states) {
1199 (void) memcpy((char *) dp->newcell, (char *) dp->oldcell,
1200 dp->ncols * dp->nrows * sizeof (unsigned char));
1201
1202 for (j = 0; j < dp->nrows; j++) {
1203 mj = dp->ncols * j;
1204 for (i = 0; i < dp->ncols; i++) {
1205 for (n = 0; n < dp->neighbors; n++) {
1206 l = positionOfNeighbor(dp, n, i, j);
1207 k = i + mj;
1208 if (dp->oldcell[l] ==
1209 (int) (dp->oldcell[k] + 1) % dp->states)
1210 dp->newcell[k] = dp->oldcell[l];
1211 }
1212 }
1213 }
1214 mj = 0;
1215 for (j = 0; j < dp->nrows; j++) {
1216 for (i = 0; i < dp->ncols; i++)
1217 if (dp->oldcell[i + mj] != dp->newcell[i + mj]) {
1218 dp->oldcell[i + mj] = dp->newcell[i + mj];
1219 if (!addtolist(mi, i, j, dp->oldcell[i + mj])) {
1220 free_demon_screen(MI_DISPLAY(mi), dp);
1221 return;
1222 }
1223 change = True;
1224 }
1225 mj += dp->ncols;
1226 }
1227 if (++dp->generation > MI_CYCLES(mi) || !change)
1228 init_demon(mi);
1229 dp->state = 0;
1230 } else {
1231 if (dp->ncells[dp->state])
1232 if (!draw_state(mi, dp->state)) {
1233 free_demon_screen(MI_DISPLAY(mi), dp);
1234 return;
1235 }
1236 dp->state++;
1237 }
1238 if (dp->redrawing) {
1239 for (i = 0; i < REDRAWSTEP; i++) {
1240 if (dp->oldcell[dp->redrawpos]) {
1241 drawCell(mi, dp->redrawpos % dp->ncols, dp->redrawpos / dp->ncols,
1242 dp->oldcell[dp->redrawpos]);
1243 }
1244 if (++(dp->redrawpos) >= dp->ncols * dp->nrows) {
1245 dp->redrawing = 0;
1246 break;
1247 }
1248 }
1249 }
1250 }
1251
1252 ENTRYPOINT void
release_demon(ModeInfo * mi)1253 release_demon(ModeInfo * mi)
1254 {
1255 if (demons != NULL) {
1256 int screen;
1257
1258 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1259 free_demon_screen(MI_DISPLAY(mi), &demons[screen]);
1260 free(demons);
1261 demons = (demonstruct *) NULL;
1262 }
1263 }
1264
1265 #ifndef STANDALONE
1266 ENTRYPOINT void
refresh_demon(ModeInfo * mi)1267 refresh_demon(ModeInfo * mi)
1268 {
1269 demonstruct *dp;
1270
1271 if (demons == NULL)
1272 return;
1273 dp = &demons[MI_SCREEN(mi)];
1274
1275 dp->redrawing = 1;
1276 dp->redrawpos = 0;
1277 }
1278 #endif
1279
1280 XSCREENSAVER_MODULE ("Demon", demon)
1281
1282 #endif /* MODE_demon */
1283