xenocara/app/xlockmore/modes/dragon.c
2006-11-26 11:07:42 +00:00

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 */