535 lines
12 KiB
C
535 lines
12 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* dragon --- Oskar van Deventer's Hexagonal Dragons Maze */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)dragon.c 5.01 2001/03/07 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* Copyright (c) 2001 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.
|
|
*
|
|
* From "Fractal Garden Maze" by Oskar van Deventer, Cubism For Fun
|
|
* Issue 54 February 2001 p16, 17. The maze itself is called a "Hexagonal
|
|
* Dragons Maze", named after its similarity to the Dragon Curve (see
|
|
* turtle.c).
|
|
*
|
|
* Revision History:
|
|
* 07-Mar-2001: Started writing, based on demon mode
|
|
*/
|
|
|
|
/*-
|
|
Grid Number of Neighbors
|
|
---- ------------------
|
|
Hexagon 6
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_dragon
|
|
#define PROGCLASS "Dragon"
|
|
#define HACK_INIT init_dragon
|
|
#define HACK_DRAW draw_dragon
|
|
#define dragon_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 2000000 \n" \
|
|
"*cycles: 16 \n" \
|
|
"*size: -24 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*neighbors: 6 \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_dragon
|
|
|
|
ModeSpecOpt dragon_opts =
|
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct dragon_description =
|
|
{"dragon", "init_dragon", "draw_dragon", "release_dragon",
|
|
"refresh_dragon", "init_dragon", (char *) NULL, &dragon_opts,
|
|
2000000, 1, 16, -24, 64, 1.0, "",
|
|
"Shows Deventer's Hexagonal Dragons Maze", 0, NULL};
|
|
|
|
#endif
|
|
|
|
#include "bitmaps/gray1.xbm"
|
|
|
|
#define STATES 3
|
|
#define LISTS 2
|
|
#define REDRAWSTEP 2000 /* How many cells to draw per cycle */
|
|
#define MINGRIDSIZE 16
|
|
#define MINSIZE 4
|
|
|
|
/* 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 addlist;
|
|
int redrawing, redrawpos;
|
|
int *ncells;
|
|
unsigned long color;
|
|
CellList **cellList;
|
|
unsigned char *oldcell;
|
|
GC stippledGC;
|
|
Pixmap graypix;
|
|
XPoint hexagon[6];
|
|
} dragonstruct;
|
|
|
|
static dragonstruct *dragons = (dragonstruct *) NULL;
|
|
|
|
static void
|
|
drawcell(ModeInfo * mi, int col, int row, unsigned char state)
|
|
{
|
|
dragonstruct *dp = &dragons[MI_SCREEN(mi)];
|
|
Display *display = MI_DISPLAY(mi);
|
|
GC gc;
|
|
|
|
if (!state) {
|
|
XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (state == 1) {
|
|
XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (MI_NPIXELS(mi) > 2) {
|
|
XSetForeground(display, MI_GC(mi), dp->color);
|
|
gc = MI_GC(mi);
|
|
} else {
|
|
XGCValues gcv;
|
|
|
|
gcv.stipple = dp->graypix;
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
XChangeGC(display, dp->stippledGC,
|
|
GCStipple | GCForeground | GCBackground, &gcv);
|
|
gc = dp->stippledGC;
|
|
}
|
|
{
|
|
int ccol = 2 * col + !(row & 1), crow = 2 * row;
|
|
|
|
dp->hexagon[0].x = dp->xb + ccol * dp->xs;
|
|
dp->hexagon[0].y = dp->yb + crow * dp->ys;
|
|
if (dp->xs == 1 && dp->ys == 1)
|
|
XDrawPoint(display, MI_WINDOW(mi),
|
|
gc, dp->hexagon[0].x, dp->hexagon[0].y);
|
|
else
|
|
XFillPolygon(display, MI_WINDOW(mi), gc,
|
|
dp->hexagon, 6, Convex, CoordModePrevious);
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
addtolist(ModeInfo * mi, int col, int row)
|
|
{
|
|
dragonstruct *dp = &dragons[MI_SCREEN(mi)];
|
|
CellList *current;
|
|
|
|
current = dp->cellList[dp->addlist];
|
|
if ((dp->cellList[dp->addlist] = (CellList *)
|
|
malloc(sizeof (CellList))) == NULL) {
|
|
return False;
|
|
}
|
|
dp->cellList[dp->addlist]->pt.x = col;
|
|
dp->cellList[dp->addlist]->pt.y = row;
|
|
dp->cellList[dp->addlist]->next = current;
|
|
dp->ncells[dp->addlist]++;
|
|
return True;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
print_state(ModeInfo * mi, int state)
|
|
{
|
|
dragonstruct *dp = &dragons[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_alist(dragonstruct * dp, int list)
|
|
{
|
|
CellList *current;
|
|
|
|
while (dp->cellList[list]) {
|
|
current = dp->cellList[list];
|
|
dp->cellList[list] = dp->cellList[list]->next;
|
|
free(current);
|
|
}
|
|
dp->cellList[list] = (CellList *) NULL;
|
|
if (dp->ncells != NULL)
|
|
dp->ncells[list] = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
free_list(dragonstruct * dp)
|
|
{
|
|
int list;
|
|
|
|
for (list = 0; list < LISTS; list++)
|
|
free_alist(dp, list);
|
|
free(dp->cellList);
|
|
dp->cellList = (CellList **) NULL;
|
|
if (dp->ncells != NULL) {
|
|
free(dp->ncells);
|
|
dp->ncells = (int *) NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_struct(dragonstruct * dp)
|
|
{
|
|
if (dp->cellList != NULL) {
|
|
free_list(dp);
|
|
}
|
|
if (dp->oldcell != NULL) {
|
|
free(dp->oldcell);
|
|
dp->oldcell = (unsigned char *) NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_dragon(Display *display, dragonstruct *dp)
|
|
{
|
|
if (dp->stippledGC != None) {
|
|
XFreeGC(display, dp->stippledGC);
|
|
dp->stippledGC = None;
|
|
}
|
|
if (dp->graypix != None) {
|
|
XFreePixmap(display, dp->graypix);
|
|
dp->graypix = None;
|
|
}
|
|
free_struct(dp);
|
|
}
|
|
|
|
#if 0
|
|
static Bool
|
|
draw_state(ModeInfo * mi, int state)
|
|
{
|
|
dragonstruct *dp = &dragons[MI_SCREEN(mi)];
|
|
Display *display = MI_DISPLAY(mi);
|
|
GC gc;
|
|
CellList *current;
|
|
|
|
if (!state) {
|
|
XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (state == 1) {
|
|
XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
|
|
gc = MI_GC(mi);
|
|
} else if (MI_NPIXELS(mi) > 2) {
|
|
XSetForeground(display, MI_GC(mi), dp->color);
|
|
gc = MI_GC(mi);
|
|
} else {
|
|
XGCValues gcv;
|
|
|
|
gcv.stipple = dp->graypix;
|
|
gcv.foreground = MI_WHITE_PIXEL(mi);
|
|
gcv.background = MI_BLACK_PIXEL(mi);
|
|
XChangeGC(display, dp->stippledGC,
|
|
GCStipple | GCForeground | GCBackground, &gcv);
|
|
gc = dp->stippledGC;
|
|
}
|
|
{ /* 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->hexagon[0].x = dp->xb + ccol * dp->xs;
|
|
dp->hexagon[0].y = dp->yb + crow * dp->ys;
|
|
if (dp->xs == 1 && dp->ys == 1)
|
|
XDrawPoint(display, MI_WINDOW(mi),
|
|
gc, dp->hexagon[0].x, dp->hexagon[0].y);
|
|
else
|
|
XFillPolygon(display, MI_WINDOW(mi), gc,
|
|
dp->hexagon, 6, Convex, CoordModePrevious);
|
|
current = current->next;
|
|
}
|
|
}
|
|
XFlush(MI_DISPLAY(mi));
|
|
return True;
|
|
}
|
|
#endif
|
|
|
|
static Bool
|
|
SetSoup(ModeInfo * mi)
|
|
{
|
|
dragonstruct *dp = &dragons[MI_SCREEN(mi)];
|
|
int row, col, mrow = 0;
|
|
|
|
for (row = dp->nrows - 1; row >= 0; --row) {
|
|
for (col = dp->ncols - 1; col >= 0; --col) {
|
|
if (!addtolist(mi, col, row))
|
|
return False;
|
|
}
|
|
mrow += dp->ncols;
|
|
}
|
|
dp->addlist = !dp->addlist;
|
|
return True;
|
|
}
|
|
|
|
void
|
|
init_dragon(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
int size = MI_SIZE(mi);
|
|
dragonstruct *dp;
|
|
|
|
if (dragons == NULL) {
|
|
if ((dragons = (dragonstruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (dragonstruct))) == NULL)
|
|
return;
|
|
}
|
|
dp = &dragons[MI_SCREEN(mi)];
|
|
|
|
dp->generation = 0;
|
|
dp->redrawing = 0;
|
|
if (MI_NPIXELS(mi) <= 2) {
|
|
if (dp->stippledGC == None) {
|
|
XGCValues gcv;
|
|
|
|
gcv.fill_style = FillOpaqueStippled;
|
|
if ((dp->stippledGC = XCreateGC(display, window,
|
|
GCFillStyle, &gcv)) == None) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
}
|
|
if (dp->graypix == None) {
|
|
if ((dp->graypix = XCreateBitmapFromData(display, window,
|
|
(char *) gray1_bits, gray1_width, gray1_height)) == None) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
free_struct(dp);
|
|
if (MI_NPIXELS(mi) > 2)
|
|
dp->color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
|
|
|
if ((dp->cellList = (CellList **) calloc(STATES,
|
|
sizeof (CellList *))) == NULL) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
if ((dp->ncells = (int *) calloc(STATES, sizeof (int))) == NULL) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
|
|
dp->addlist = 0;
|
|
|
|
dp->width = MI_WIDTH(mi);
|
|
dp->height = MI_HEIGHT(mi);
|
|
|
|
{
|
|
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->hexagon[i].x = dp->xs * hexagonUnit[i].x;
|
|
dp->hexagon[i].y = ((dp->ys + 1) * hexagonUnit[i].y / 2) * 4 / 3;
|
|
}
|
|
}
|
|
|
|
MI_CLEARWINDOW(mi);
|
|
|
|
if ((dp->oldcell = (unsigned char *)
|
|
calloc(dp->ncols * dp->nrows,
|
|
sizeof (unsigned char))) == NULL) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
if (!SetSoup(mi)) {
|
|
free_dragon(display, dp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
draw_dragon(ModeInfo * mi)
|
|
{
|
|
int white, black;
|
|
int choose_layer, factor, orient, l;
|
|
dragonstruct *dp;
|
|
CellList *locallist;
|
|
Bool detour = False;
|
|
|
|
if (dragons == NULL)
|
|
return;
|
|
dp = &dragons[MI_SCREEN(mi)];
|
|
if (dp->cellList == NULL)
|
|
return;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
choose_layer= NRAND(6);
|
|
if (dp->ncells[!dp->addlist] == 1) {
|
|
/* Since the maze is infinite, it may not get to this last
|
|
* spot for a while. Force it to do it right away so it
|
|
* does not appear to be stuck. */
|
|
detour = True;
|
|
white = black = 0; /* not used but make -Wall happy */
|
|
} else {
|
|
white = (choose_layer / 2);
|
|
black = (choose_layer % 2) ?
|
|
((white + 2) % 3) : ((white + 1) % 3);
|
|
/* gray = (choose_layer % 2) ?
|
|
((white + 1) % 3) : ((white + 2) % 3); */
|
|
}
|
|
locallist = dp->cellList[!dp->addlist];
|
|
orient = dp->generation % 2;
|
|
factor = 1;
|
|
for (l = 0; l < dp->generation / 2; l++) {
|
|
factor *= 3;
|
|
}
|
|
if (!locallist && dp->generation >= MI_CYCLES(mi)) {
|
|
init_dragon(mi);
|
|
return;
|
|
}
|
|
|
|
while (locallist) {
|
|
int i, j, k;
|
|
|
|
i = locallist->pt.x;
|
|
j = locallist->pt.y;
|
|
if (orient) {
|
|
k = (j / factor) % 3;
|
|
} else {
|
|
if (j % 2) {
|
|
/* Had trouble with this line... */
|
|
k = ((i + factor / 2) / factor + 1) % 3;
|
|
} else {
|
|
k = (i / factor) % 3;
|
|
}
|
|
}
|
|
if (detour) {
|
|
k = (LRAND() & 1) + 1;
|
|
dp->oldcell[j * dp->ncols + i] = k;
|
|
drawcell(mi, i, j, k);
|
|
} if (white == k) {
|
|
dp->oldcell[j * dp->ncols + i] = 0;
|
|
drawcell(mi, i, j, 0);
|
|
if (!addtolist(mi, i, j)) {
|
|
free_dragon(MI_DISPLAY(mi), dp);
|
|
return;
|
|
}
|
|
} else if (black == k) {
|
|
dp->oldcell[j * dp->ncols + i] = 1;
|
|
drawcell(mi, i, j, 1);
|
|
} else /* if (gray == k) */ {
|
|
dp->oldcell[j * dp->ncols + i] = 2;
|
|
drawcell(mi, i, j, 2);
|
|
}
|
|
dp->cellList[!dp->addlist] = dp->cellList[!dp->addlist]->next;
|
|
free(locallist);
|
|
dp->ncells[!dp->addlist]--;
|
|
locallist = dp->cellList[!dp->addlist];
|
|
if ((dp->cellList[!dp->addlist] == NULL) &&
|
|
(dp->cellList[dp->addlist] == NULL))
|
|
dp->generation = 0;
|
|
}
|
|
dp->addlist = !dp->addlist;
|
|
if (dp->redrawing) {
|
|
int i;
|
|
|
|
for (i = 0; i < REDRAWSTEP; i++) {
|
|
if (dp->oldcell[dp->redrawpos] != 1) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
dp->generation++;
|
|
}
|
|
void
|
|
release_dragon(ModeInfo * mi)
|
|
{
|
|
if (dragons != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_dragon(MI_DISPLAY(mi), &dragons[screen]);
|
|
free(dragons);
|
|
dragons = (dragonstruct *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_dragon(ModeInfo * mi)
|
|
{
|
|
dragonstruct *dp;
|
|
|
|
if (dragons == NULL)
|
|
return;
|
|
dp = &dragons[MI_SCREEN(mi)];
|
|
|
|
dp->redrawing = 1;
|
|
dp->redrawpos = 0;
|
|
}
|
|
|
|
#endif /* MODE_dragon */
|