968 lines
27 KiB
C
968 lines
27 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* demon --- David Griffeath's cellular automata */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)demon.c 5.00 2000/11/01 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* Copyright (c) 1995 by David Bagley.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose and without fee is hereby granted,
|
|
* provided that the above copyright notice appear in all copies and that
|
|
* both that copyright notice and this permission notice appear in
|
|
* supporting documentation.
|
|
*
|
|
* This file is provided AS IS with no warranties of any kind. The author
|
|
* shall have no liability with respect to the infringement of copyrights,
|
|
* trade secrets or any patents by this file or any part thereof. In no
|
|
* event will the author be liable for any lost revenue or profits or
|
|
* other special, indirect and consequential damages.
|
|
*
|
|
* Revision History:
|
|
* 01-Nov-2000: Allocation checks
|
|
* 10-May-1997: Compatible with xscreensaver
|
|
* 16-Apr-1997: -neighbors 3, 9 (not sound mathematically), 12, and 8 added
|
|
* 30-May-1996: Ron Hitchens <ron@idiom.com>
|
|
* Fixed memory management that caused leaks
|
|
* 14-Apr-1996: -neighbors 6 runtime-time option added
|
|
* 21-Aug-1995: Coded from A.K. Dewdney's "Computer Recreations", Scientific
|
|
* American Magazine" Aug 1989 pp 102-105. Also very similar
|
|
* to hodgepodge machine described in A.K. Dewdney's "Computer
|
|
* Recreations", Scientific American Magazine" Aug 1988
|
|
* pp 104-107. Also used life.c as a guide.
|
|
*/
|
|
|
|
/*-
|
|
* A cellular universe of 4 phases debris, droplets, defects, and demons.
|
|
*/
|
|
|
|
/*-
|
|
Grid Number of Neighbors
|
|
---- ------------------
|
|
Square 4 or 8
|
|
Hexagon 6
|
|
Triangle 3, 9, or 12
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_demon
|
|
#define PROGCLASS "Demon"
|
|
#define HACK_INIT init_demon
|
|
#define HACK_DRAW draw_demon
|
|
#define demon_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 50000 \n" \
|
|
"*count: 0 \n" \
|
|
"*cycles: 1000 \n" \
|
|
"*size: -7 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*neighbors: 0 \n"
|
|
#define UNIFORM_COLORS
|
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
|
#else /* STANDALONE */
|
|
#include "xlock.h" /* in xlockmore distribution */
|
|
|
|
#endif /* STANDALONE */
|
|
#include "automata.h"
|
|
|
|
#ifdef MODE_demon
|
|
|
|
/*-
|
|
* neighbors of 0 randomizes it between 3, 4, 6, 8, 9, and 12.
|
|
*/
|
|
#define DEF_NEIGHBORS "0" /* choose random value */
|
|
|
|
static int neighbors;
|
|
|
|
static XrmOptionDescRec opts[] =
|
|
{
|
|
{(char *) "-neighbors", (char *) ".demon.neighbors", XrmoptionSepArg, (caddr_t) NULL}
|
|
};
|
|
|
|
static argtype vars[] =
|
|
{
|
|
{(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int}
|
|
};
|
|
static OptionStruct desc[] =
|
|
{
|
|
{(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"}
|
|
};
|
|
|
|
ModeSpecOpt demon_opts =
|
|
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct demon_description =
|
|
{"demon", "init_demon", "draw_demon", "release_demon",
|
|
"refresh_demon", "init_demon", (char *) NULL, &demon_opts,
|
|
50000, 0, 1000, -7, 64, 1.0, "",
|
|
"Shows Griffeath's cellular automata", 0, NULL};
|
|
|
|
#endif
|
|
|
|
#define DEMONBITS(n,w,h)\
|
|
if ((dp->pixmaps[dp->init_bits]=\
|
|
XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
|
|
free_demon(display,dp); return;} else {dp->init_bits++;}
|
|
|
|
#define REDRAWSTEP 2000 /* How many cells to draw per cycle */
|
|
#define MINSTATES 2
|
|
#define MINGRIDSIZE 24
|
|
#define MINSIZE 4
|
|
#define NEIGHBORKINDS 6
|
|
|
|
/* Singly linked list */
|
|
typedef struct _CellList {
|
|
XPoint pt;
|
|
struct _CellList *next;
|
|
} CellList;
|
|
|
|
typedef struct {
|
|
int generation;
|
|
int xs, ys;
|
|
int xb, yb;
|
|
int nrows, ncols;
|
|
int width, height;
|
|
int states;
|
|
int state;
|
|
int redrawing, redrawpos;
|
|
int *ncells;
|
|
CellList **cellList;
|
|
unsigned char *oldcell, *newcell;
|
|
int neighbors;
|
|
int init_bits;
|
|
GC stippledGC;
|
|
Pixmap pixmaps[NUMSTIPPLES - 1];
|
|
union {
|
|
XPoint hexagon[6];
|
|
XPoint triangle[2][3];
|
|
} shape;
|
|
} demonstruct;
|
|
|
|
static char plots[2][NEIGHBORKINDS] =
|
|
{
|
|
{3, 4, 6, 8, 9, 12}, /* Neighborhoods */
|
|
{12, 16, 18, 20, 22, 24} /* Number of states */
|
|
};
|
|
|
|
static demonstruct *demons = (demonstruct *) NULL;
|
|
|
|
static void
|
|
drawcell(ModeInfo * mi, int col, int row, unsigned char state)
|
|
{
|
|
demonstruct *dp = &demons[MI_SCREEN(mi)];
|
|
GC gc;
|
|
|
|
if (!state) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (MI_NPIXELS(mi) >= NUMSTIPPLES) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
|
|
MI_PIXEL(mi, (((int) state - 1) * MI_NPIXELS(mi) /
|
|
(dp->states - 1)) % MI_NPIXELS(mi)));
|
|
gc = MI_GC(mi);
|
|
} else {
|
|
XGCValues gcv;
|
|
|
|
gcv.stipple = dp->pixmaps[(state - 1) % (NUMSTIPPLES - 1)];
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
XChangeGC(MI_DISPLAY(mi), dp->stippledGC,
|
|
GCStipple | GCForeground | GCBackground, &gcv);
|
|
gc = dp->stippledGC;
|
|
}
|
|
if (dp->neighbors == 6) {
|
|
int ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
|
|
dp->shape.hexagon[0].x = dp->xb + ccol * dp->xs;
|
|
dp->shape.hexagon[0].y = dp->yb + crow * dp->ys;
|
|
if (dp->xs == 1 && dp->ys == 1)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
|
|
gc, dp->shape.hexagon[0].x, dp->shape.hexagon[0].y);
|
|
else
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
dp->shape.hexagon, 6, Convex, CoordModePrevious);
|
|
} else if (dp->neighbors == 4 || dp->neighbors == 8) {
|
|
XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
dp->xb + dp->xs * col, dp->yb + dp->ys * row,
|
|
dp->xs - (dp->xs > 3), dp->ys - (dp->ys > 3));
|
|
} else { /* TRI */
|
|
int orient = (col + row) % 2; /* O left 1 right */
|
|
|
|
dp->shape.triangle[orient][0].x = dp->xb + col * dp->xs;
|
|
dp->shape.triangle[orient][0].y = dp->yb + row * dp->ys;
|
|
if (dp->xs <= 3 || dp->ys <= 3)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
((orient) ? -1 : 1) + dp->shape.triangle[orient][0].x,
|
|
dp->shape.triangle[orient][0].y);
|
|
else {
|
|
if (orient)
|
|
dp->shape.triangle[orient][0].x += (dp->xs / 2 - 1);
|
|
else
|
|
dp->shape.triangle[orient][0].x -= (dp->xs / 2 - 1);
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
dp->shape.triangle[orient], 3, Convex, CoordModePrevious);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
addtolist(ModeInfo * mi, int col, int row, unsigned char state)
|
|
{
|
|
demonstruct *dp = &demons[MI_SCREEN(mi)];
|
|
CellList *current;
|
|
|
|
current = dp->cellList[state];
|
|
if ((dp->cellList[state] = (CellList *)
|
|
malloc(sizeof (CellList))) == NULL) {
|
|
return False;
|
|
}
|
|
dp->cellList[state]->pt.x = col;
|
|
dp->cellList[state]->pt.y = row;
|
|
dp->cellList[state]->next = current;
|
|
dp->ncells[state]++;
|
|
return True;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
print_state(ModeInfo * mi, int state)
|
|
{
|
|
demonstruct *dp = &demons[MI_SCREEN(mi)];
|
|
CellList *locallist;
|
|
int i = 0;
|
|
|
|
locallist = dp->cellList[state];
|
|
(void) printf("state %d\n", state);
|
|
while (locallist) {
|
|
(void) printf("%d x %d, y %d\n", i,
|
|
locallist->pt.x, locallist->pt.y);
|
|
locallist = locallist->next;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
free_state(demonstruct * dp, int state)
|
|
{
|
|
CellList *current;
|
|
|
|
while (dp->cellList[state]) {
|
|
current = dp->cellList[state];
|
|
dp->cellList[state] = dp->cellList[state]->next;
|
|
free(current);
|
|
}
|
|
dp->cellList[state] = (CellList *) NULL;
|
|
if (dp->ncells != NULL)
|
|
dp->ncells[state] = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
free_list(demonstruct * dp)
|
|
{
|
|
int state;
|
|
|
|
for (state = 0; state < dp->states; state++)
|
|
free_state(dp, state);
|
|
free(dp->cellList);
|
|
dp->cellList = (CellList **) NULL;
|
|
}
|
|
|
|
static void
|
|
free_struct(demonstruct * dp)
|
|
{
|
|
if (dp->cellList != NULL) {
|
|
free_list(dp);
|
|
}
|
|
if (dp->ncells != NULL) {
|
|
free(dp->ncells);
|
|
dp->ncells = (int *) NULL;
|
|
}
|
|
if (dp->oldcell != NULL) {
|
|
free(dp->oldcell);
|
|
dp->oldcell = (unsigned char *) NULL;
|
|
}
|
|
if (dp->newcell != NULL) {
|
|
free(dp->newcell);
|
|
dp->newcell = (unsigned char *) NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_demon(Display *display, demonstruct *dp)
|
|
{
|
|
int shade;
|
|
|
|
if (dp->stippledGC != None) {
|
|
XFreeGC(display, dp->stippledGC);
|
|
dp->stippledGC = None;
|
|
}
|
|
for (shade = 0; shade < dp->init_bits; shade++) {
|
|
XFreePixmap(display, dp->pixmaps[shade]);
|
|
}
|
|
dp->init_bits = 0;
|
|
free_struct(dp);
|
|
}
|
|
|
|
static Bool
|
|
draw_state(ModeInfo * mi, int state)
|
|
{
|
|
demonstruct *dp = &demons[MI_SCREEN(mi)];
|
|
GC gc;
|
|
XRectangle *rects;
|
|
CellList *current;
|
|
|
|
if (!state) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (MI_NPIXELS(mi) >= NUMSTIPPLES) {
|
|
XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
|
|
MI_PIXEL(mi, (((int) state - 1) * MI_NPIXELS(mi) /
|
|
(dp->states - 1)) % MI_NPIXELS(mi)));
|
|
gc = MI_GC(mi);
|
|
} else {
|
|
XGCValues gcv;
|
|
|
|
gcv.stipple = dp->pixmaps[(state - 1) % (NUMSTIPPLES - 1)];
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
XChangeGC(MI_DISPLAY(mi), dp->stippledGC,
|
|
GCStipple | GCForeground | GCBackground, &gcv);
|
|
gc = dp->stippledGC;
|
|
}
|
|
if (dp->neighbors == 6) { /* Draw right away, slow */
|
|
current = dp->cellList[state];
|
|
while (current) {
|
|
int col, row, ccol, crow;
|
|
|
|
col = current->pt.x;
|
|
row = current->pt.y;
|
|
ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
dp->shape.hexagon[0].x = dp->xb + ccol * dp->xs;
|
|
dp->shape.hexagon[0].y = dp->yb + crow * dp->ys;
|
|
if (dp->xs == 1 && dp->ys == 1)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
|
|
gc, dp->shape.hexagon[0].x, dp->shape.hexagon[0].y);
|
|
else
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
dp->shape.hexagon, 6, Convex, CoordModePrevious);
|
|
current = current->next;
|
|
}
|
|
} else if (dp->neighbors == 4 || dp->neighbors == 8) {
|
|
/* Take advantage of XDrawRectangles */
|
|
int ncells = 0;
|
|
|
|
/* Create Rectangle list from part of the cellList */
|
|
if ((rects = (XRectangle *) malloc(dp->ncells[state] *
|
|
sizeof (XRectangle))) == NULL) {
|
|
return False;
|
|
}
|
|
current = dp->cellList[state];
|
|
while (current) {
|
|
rects[ncells].x = dp->xb + current->pt.x * dp->xs;
|
|
rects[ncells].y = dp->yb + current->pt.y * dp->ys;
|
|
rects[ncells].width = dp->xs - (dp->xs > 3);
|
|
rects[ncells].height = dp->ys - (dp->ys > 3);
|
|
current = current->next;
|
|
ncells++;
|
|
}
|
|
/* Finally get to draw */
|
|
XFillRectangles(MI_DISPLAY(mi), MI_WINDOW(mi), gc, rects, ncells);
|
|
/* Free up rects list and the appropriate part of the cellList */
|
|
free(rects);
|
|
} else { /* TRI */
|
|
current = dp->cellList[state];
|
|
while (current) {
|
|
int col, row, orient;
|
|
|
|
col = current->pt.x;
|
|
row = current->pt.y;
|
|
orient = (col + row) % 2; /* O left 1 right */
|
|
dp->shape.triangle[orient][0].x = dp->xb + col * dp->xs;
|
|
dp->shape.triangle[orient][0].y = dp->yb + row * dp->ys;
|
|
if (dp->xs <= 3 || dp->ys <= 3)
|
|
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
((orient) ? -1 : 1) + dp->shape.triangle[orient][0].x,
|
|
dp->shape.triangle[orient][0].y);
|
|
else {
|
|
if (orient)
|
|
dp->shape.triangle[orient][0].x += (dp->xs / 2 - 1);
|
|
else
|
|
dp->shape.triangle[orient][0].x -= (dp->xs / 2 - 1);
|
|
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
|
|
dp->shape.triangle[orient], 3, Convex, CoordModePrevious);
|
|
}
|
|
current = current->next;
|
|
}
|
|
}
|
|
free_state(dp, state);
|
|
XFlush(MI_DISPLAY(mi));
|
|
return True;
|
|
}
|
|
|
|
static void
|
|
RandomSoup(ModeInfo * mi)
|
|
{
|
|
demonstruct *dp = &demons[MI_SCREEN(mi)];
|
|
int row, col, mrow = 0;
|
|
|
|
for (row = 0; row < dp->nrows; ++row) {
|
|
for (col = 0; col < dp->ncols; ++col) {
|
|
dp->oldcell[col + mrow] =
|
|
(unsigned char) LRAND() % ((unsigned char) dp->states);
|
|
if (!addtolist(mi, col, row, dp->oldcell[col + mrow]))
|
|
return; /* sparse soup */
|
|
}
|
|
mrow += dp->ncols;
|
|
}
|
|
}
|
|
|
|
void
|
|
init_demon(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
int size = MI_SIZE(mi), nk;
|
|
demonstruct *dp;
|
|
|
|
if (demons == NULL) {
|
|
if ((demons = (demonstruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (demonstruct))) == NULL)
|
|
return;
|
|
}
|
|
dp = &demons[MI_SCREEN(mi)];
|
|
|
|
dp->generation = 0;
|
|
dp->redrawing = 0;
|
|
if (MI_NPIXELS(mi) < NUMSTIPPLES) {
|
|
if (dp->stippledGC == None) {
|
|
XGCValues gcv;
|
|
|
|
gcv.fill_style = FillOpaqueStippled;
|
|
if ((dp->stippledGC = XCreateGC(display, window,
|
|
GCFillStyle, &gcv)) == None) {
|
|
free_demon(display, dp);
|
|
return;
|
|
}
|
|
}
|
|
if (dp->init_bits == 0) {
|
|
int i;
|
|
|
|
for (i = 1; i < NUMSTIPPLES; i++) {
|
|
DEMONBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
|
|
}
|
|
}
|
|
}
|
|
free_struct(dp);
|
|
|
|
for (nk = 0; nk < NEIGHBORKINDS; nk++) {
|
|
if (neighbors == plots[0][nk]) {
|
|
dp->neighbors = plots[0][nk];
|
|
break;
|
|
}
|
|
if (nk == NEIGHBORKINDS - 1) {
|
|
nk = NRAND(NEIGHBORKINDS);
|
|
dp->neighbors = plots[0][nk];
|
|
break;
|
|
}
|
|
}
|
|
|
|
dp->states = MI_COUNT(mi);
|
|
if (dp->states < -MINSTATES)
|
|
dp->states = NRAND(-dp->states - MINSTATES + 1) + MINSTATES;
|
|
else if (dp->states < MINSTATES)
|
|
dp->states = plots[1][nk];
|
|
if ((dp->cellList = (CellList **) calloc(dp->states,
|
|
sizeof (CellList *))) == NULL) {
|
|
free_demon(display, dp);
|
|
return;
|
|
}
|
|
if ((dp->ncells = (int *) calloc(dp->states, sizeof (int))) == NULL) {
|
|
free_demon(display, dp);
|
|
return;
|
|
}
|
|
|
|
dp->state = 0;
|
|
|
|
dp->width = MI_WIDTH(mi);
|
|
dp->height = MI_HEIGHT(mi);
|
|
if (dp->neighbors == 6) {
|
|
int nccols, ncrows, i;
|
|
|
|
if (dp->width < 8)
|
|
dp->width = 8;
|
|
if (dp->height < 8)
|
|
dp->height = 8;
|
|
if (size < -MINSIZE)
|
|
dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
else if (size < MINSIZE) {
|
|
if (!size)
|
|
dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
|
|
else
|
|
dp->ys = MINSIZE;
|
|
} else
|
|
dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE));
|
|
dp->xs = dp->ys;
|
|
nccols = MAX(dp->width / dp->xs - 2, 2);
|
|
ncrows = MAX(dp->height / dp->ys - 1, 4);
|
|
dp->ncols = nccols / 2;
|
|
dp->nrows = 2 * (ncrows / 4);
|
|
dp->xb = (dp->width - dp->xs * nccols) / 2 + dp->xs / 2;
|
|
dp->yb = (dp->height - dp->ys * (ncrows / 2) * 2) / 2 + dp->ys / 4;
|
|
for (i = 0; i < 6; i++) {
|
|
dp->shape.hexagon[i].x = (dp->xs - 1) * hexagonUnit[i].x;
|
|
dp->shape.hexagon[i].y = ((dp->ys - 1) * hexagonUnit[i].y / 2) * 4 / 3;
|
|
}
|
|
} else if (dp->neighbors == 4 || dp->neighbors == 8) {
|
|
if (size < -MINSIZE)
|
|
dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
else if (size < MINSIZE) {
|
|
if (!size)
|
|
dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
|
|
else
|
|
dp->ys = MINSIZE;
|
|
} else
|
|
dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE));
|
|
dp->xs = dp->ys;
|
|
dp->ncols = MAX(dp->width / dp->xs, 2);
|
|
dp->nrows = MAX(dp->height / dp->ys, 2);
|
|
dp->xb = (dp->width - dp->xs * dp->ncols) / 2;
|
|
dp->yb = (dp->height - dp->ys * dp->nrows) / 2;
|
|
} else { /* TRI */
|
|
int orient, i;
|
|
|
|
if (dp->width < 2)
|
|
dp->width = 2;
|
|
if (dp->height < 2)
|
|
dp->height = 2;
|
|
if (size < -MINSIZE)
|
|
dp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
|
|
else if (size < MINSIZE) {
|
|
if (!size)
|
|
dp->ys = MAX(MINSIZE, MIN(dp->width, dp->height) / MINGRIDSIZE);
|
|
else
|
|
dp->ys = MINSIZE;
|
|
} else
|
|
dp->ys = MIN(size, MAX(MINSIZE, MIN(dp->width, dp->height) /
|
|
MINGRIDSIZE));
|
|
dp->xs = (int) (1.52 * dp->ys);
|
|
dp->ncols = (MAX(dp->width / dp->xs - 1, 2) / 2) * 2;
|
|
dp->nrows = (MAX(dp->height / dp->ys - 1, 2) / 2) * 2;
|
|
dp->xb = (dp->width - dp->xs * dp->ncols) / 2 + dp->xs / 2;
|
|
dp->yb = (dp->height - dp->ys * dp->nrows) / 2 + dp->ys / 2;
|
|
for (orient = 0; orient < 2; orient++) {
|
|
for (i = 0; i < 3; i++) {
|
|
dp->shape.triangle[orient][i].x =
|
|
(dp->xs - 2) * triangleUnit[orient][i].x;
|
|
dp->shape.triangle[orient][i].y =
|
|
(dp->ys - 2) * triangleUnit[orient][i].y;
|
|
}
|
|
}
|
|
}
|
|
|
|
MI_CLEARWINDOW(mi);
|
|
|
|
if ((dp->oldcell = (unsigned char *)
|
|
malloc(dp->ncols * dp->nrows * sizeof (unsigned char))) == NULL) {
|
|
free_demon(display, dp);
|
|
return;
|
|
}
|
|
|
|
if ((dp->newcell = (unsigned char *)
|
|
malloc(dp->ncols * dp->nrows * sizeof (unsigned char))) == NULL) {
|
|
free_demon(display, dp);
|
|
return;
|
|
}
|
|
|
|
RandomSoup(mi);
|
|
}
|
|
|
|
void
|
|
draw_demon(ModeInfo * mi)
|
|
{
|
|
int i, j, k, l, mj = 0, ml;
|
|
demonstruct *dp;
|
|
|
|
if (demons == NULL)
|
|
return;
|
|
dp = &demons[MI_SCREEN(mi)];
|
|
if (dp->cellList == NULL)
|
|
return;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
if (dp->state >= dp->states) {
|
|
(void) memcpy((char *) dp->newcell, (char *) dp->oldcell,
|
|
dp->ncols * dp->nrows * sizeof (unsigned char));
|
|
|
|
if (dp->neighbors == 6) {
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
/* NE */
|
|
if (!(j & 1))
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
else
|
|
k = i;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* E */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SE */
|
|
if (!(j & 1))
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
else
|
|
k = i;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SW */
|
|
if (j & 1)
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
else
|
|
k = i;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* W */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* NW */
|
|
if (j & 1)
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
else
|
|
k = i;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
} else if (dp->neighbors == 4 || dp->neighbors == 8) {
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
/* N */
|
|
k = i;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* E */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* S */
|
|
k = i;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* W */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = j;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
if (dp->neighbors == 8) {
|
|
mj = 0;
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
/* NE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* NW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
}
|
|
} else if (dp->neighbors == 3 || dp->neighbors == 9 ||
|
|
dp->neighbors == 12) {
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
if ((i + j) % 2) { /* right */
|
|
/* W */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
} else { /* left */
|
|
/* E */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
/* N */
|
|
k = i;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* S */
|
|
k = i;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
if (dp->neighbors == 9 || dp->neighbors == 12) {
|
|
mj = 0;
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
/* NN */
|
|
k = i;
|
|
if (!j)
|
|
l = dp->nrows - 2;
|
|
else if (!(j - 1))
|
|
l = dp->nrows - 1;
|
|
else
|
|
l = j - 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SS */
|
|
k = i;
|
|
if (j + 1 == dp->nrows)
|
|
l = 1;
|
|
else if (j + 2 == dp->nrows)
|
|
l = 0;
|
|
else
|
|
l = j + 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* NW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* NE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
l = (!j) ? dp->nrows - 1 : j - 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
l = (j + 1 == dp->nrows) ? 0 : j + 1;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
if (dp->neighbors == 12) {
|
|
mj = 0;
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++) {
|
|
if ((i + j) % 2) { /* right */
|
|
/* NNW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
if (!j)
|
|
l = dp->nrows - 2;
|
|
else if (!(j - 1))
|
|
l = dp->nrows - 1;
|
|
else
|
|
l = j - 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SSW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
if (j + 1 == dp->nrows)
|
|
l = 1;
|
|
else if (j + 2 == dp->nrows)
|
|
l = 0;
|
|
else
|
|
l = j + 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* EE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
l = j;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
} else { /* left */
|
|
/* NNE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
if (!j)
|
|
l = dp->nrows - 2;
|
|
else if (!(j - 1))
|
|
l = dp->nrows - 1;
|
|
else
|
|
l = j - 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* SSE */
|
|
k = (i + 1 == dp->ncols) ? 0 : i + 1;
|
|
if (j + 1 == dp->nrows)
|
|
l = 1;
|
|
else if (j + 2 == dp->nrows)
|
|
l = 0;
|
|
else
|
|
l = j + 2;
|
|
ml = l * dp->ncols;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
/* WW */
|
|
k = (!i) ? dp->ncols - 1 : i - 1;
|
|
l = j;
|
|
ml = mj;
|
|
if (dp->oldcell[k + ml] ==
|
|
(int) (dp->oldcell[i + mj] + 1) % dp->states)
|
|
dp->newcell[i + mj] = dp->oldcell[k + ml];
|
|
}
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mj = 0;
|
|
for (j = 0; j < dp->nrows; j++) {
|
|
for (i = 0; i < dp->ncols; i++)
|
|
if (dp->oldcell[i + mj] != dp->newcell[i + mj]) {
|
|
dp->oldcell[i + mj] = dp->newcell[i + mj];
|
|
if (!addtolist(mi, i, j, dp->oldcell[i + mj])) {
|
|
free_demon(MI_DISPLAY(mi), dp);
|
|
return;
|
|
}
|
|
}
|
|
mj += dp->ncols;
|
|
}
|
|
if (++dp->generation > MI_CYCLES(mi))
|
|
init_demon(mi);
|
|
dp->state = 0;
|
|
} else {
|
|
if (dp->ncells[dp->state])
|
|
if (!draw_state(mi, dp->state)) {
|
|
free_demon(MI_DISPLAY(mi), dp);
|
|
return;
|
|
}
|
|
dp->state++;
|
|
}
|
|
if (dp->redrawing) {
|
|
for (i = 0; i < REDRAWSTEP; i++) {
|
|
if (dp->oldcell[dp->redrawpos]) {
|
|
drawcell(mi, dp->redrawpos % dp->ncols, dp->redrawpos / dp->ncols,
|
|
dp->oldcell[dp->redrawpos]);
|
|
}
|
|
if (++(dp->redrawpos) >= dp->ncols * dp->nrows) {
|
|
dp->redrawing = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
release_demon(ModeInfo * mi)
|
|
{
|
|
if (demons != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_demon(MI_DISPLAY(mi), &demons[screen]);
|
|
free(demons);
|
|
demons = (demonstruct *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_demon(ModeInfo * mi)
|
|
{
|
|
demonstruct *dp;
|
|
|
|
if (demons == NULL)
|
|
return;
|
|
dp = &demons[MI_SCREEN(mi)];
|
|
|
|
dp->redrawing = 1;
|
|
dp->redrawpos = 0;
|
|
}
|
|
|
|
#endif /* MODE_demon */
|