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

1036 lines
26 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* wire --- logical circuits based on simple state-changes (wireworld) */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)wire.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1996 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
* 05-Dec-1997: neighbors option added.
* 10-May-1997: Compatible with xscreensaver
* 14-Jun-1996: Coded from A.K. Dewdney's "Computer Recreations", Scientific
* American Magazine" Jan 1990 pp 146-148. Used ant.c as an
* example. do_gen() based on code by Kevin Dahlhausen
* <ap096@po.cwru.edu> and Stefan Strack
* <stst@vuse.vanderbilt.edu>.
*/
/*-
* # Rules file for Wireworld
*
* # 0 is space, 1 is wire, 2 is tail, 3 is head
* states 4
*
* passive 2
* 0[0123][0123][0123][0123]0 # No way to make a space into signal or wire
*
* # Signal propagation
* 2[0123][0123][0123][0123]1 # tail -> wire
* 3[0123][0123][0123][0123]2 # head -> tail
*
* # 1 or 2 heads adjacent to a wire makes it a head
* 1(3*1)3 # wire with 1 head adjacent -> head
* 1(3*2)3 # wire with 2 heads adjacent -> head
*
*
* " " is space, X is wire, o is tail, O is head
*
* ->XXXoOXXX-> Electron moving in a wire
*
* Diode -- permits an electron to pass right to left, but not the other way.
* XX
* XX XXX
* XX
*
* OR gate or fan-in where inputs are protected by diodes
* XX XX
* Input ->XXX XX XX XXX<- Input
* XX X XX
* X
* X
* |
* V
* Output
*
* Dewdney's synchronous-logic flipflop.
* Bottom left input is write 1, top left is remember 0.
* When gate is on, 1 electron goes to output at right every 13 cycles
* memory element, about to forget 1 and remember 0
* Remember 0
* o
* X O XX XX Memory Loop
* X XX X XXXX
* X XX XX X oOX-> 1 Output
* Inputs ->XX X X
* X X XX o
* Inputs ->XXXXXXX XX X XO Memory of 1
* XX XX
* Remember 1
*/
#ifdef STANDALONE
#define MODE_wire
#define PROGCLASS "Wire"
#define HACK_INIT init_wire
#define HACK_DRAW draw_wire
#define wire_opts xlockmore_opts
#define DEFAULTS "*delay: 500000 \n" \
"*count: 1000 \n" \
"*cycles: 150 \n" \
"*size: -8 \n" \
"*ncolors: 64 \n" \
"*neighbors: 0 \n"
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#include "automata.h"
#ifdef MODE_wire
/*-
* 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 *) ".wire.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 wire_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct wire_description =
{"wire", "init_wire", "draw_wire", "release_wire",
"refresh_wire", "init_wire", (char *) NULL, &wire_opts,
500000, 1000, 150, -8, 64, 1.0, "",
"Shows a random circuit with 2 electrons", 0, NULL};
#endif
#define WIREBITS(n,w,h)\
if ((wp->pixmaps[wp->init_bits]=\
XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
free_wire(display,wp); return;} else {wp->init_bits++;}
#define COLORS 4
#define MINWIRES 32
#define MINGRIDSIZE 24
#define MINSIZE 3
#define ANGLES 360
#define NEIGHBORKINDS 6
#define SPACE 0
#define WIRE 1 /* Normal wire */
#define HEAD 2 /* electron head */
#define TAIL 3 /* electron tail */
#define REDRAWSTEP 2000 /* How much wire to draw per cycle */
/* Singly linked list */
typedef struct _CellList {
XPoint pt;
struct _CellList *next;
} CellList;
typedef struct {
int init_bits;
int neighbors;
int generation;
int xs, ys;
int xb, yb;
int nrows, ncols;
int bnrows, bncols;
int mincol, minrow, maxcol, maxrow;
int width, height;
int redrawing, redrawpos;
unsigned char *oldcells, *newcells;
int ncells[COLORS - 1];
CellList *cellList[COLORS - 1];
unsigned char colors[COLORS - 1];
GC stippledGC;
Pixmap pixmaps[COLORS - 1];
int prob_array[12];
union {
XPoint hexagon[6];
XPoint triangle[2][3];
} shape;
} circuitstruct;
static char plots[NEIGHBORKINDS] =
{3, 4, 6, 8, 9, 12}; /* Neighborhoods */
static circuitstruct *circuits = (circuitstruct *) NULL;
static void
position_of_neighbor(circuitstruct * wp, int dir, int *pcol, int *prow)
{
int col = *pcol, row = *prow;
/* NO WRAPING */
if (wp->neighbors == 4 || wp->neighbors == 6 || wp->neighbors == 8) {
switch (dir) {
case 0:
col = col + 1;
break;
case 45:
col = col + 1;
row = row - 1;
break;
case 60:
if (!(row & 1))
col = col + 1;
row = row - 1;
break;
case 90:
row = row - 1;
break;
case 120:
if (row & 1)
col = col - 1;
row = row - 1;
break;
case 135:
col = col - 1;
row = row - 1;
break;
case 180:
col = col - 1;
break;
case 225:
col = col - 1;
row = row + 1;
break;
case 240:
if (row & 1)
col = col - 1;
row = row + 1;
break;
case 270:
row = row + 1;
break;
case 300:
if (!(row & 1))
col = col + 1;
row = row + 1;
break;
case 315:
col = col + 1;
row = row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else { /* TRI */
if ((col + row) % 2) { /* right */
switch (dir) {
case 0:
col = col - 1;
break;
case 30:
case 40:
col = col - 1;
row = row - 1;
break;
case 60:
col = col - 1;
row = row - 2;
break;
case 80:
case 90:
row = row - 2;
break;
case 120:
row = row - 1;
break;
case 150:
case 160:
col = col + 1;
row = row - 1;
break;
case 180:
col = col + 1;
break;
case 200:
case 210:
col = col + 1;
row = row + 1;
break;
case 240:
row = row + 1;
break;
case 270:
case 280:
row = row + 2;
break;
case 300:
col = col - 1;
row = row + 2;
break;
case 320:
case 330:
col = col - 1;
row = row + 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
} else { /* left */
switch (dir) {
case 0:
col = col + 1;
break;
case 30:
case 40:
col = col + 1;
row = row + 1;
break;
case 60:
col = col + 1;
row = row + 2;
break;
case 80:
case 90:
row = row + 2;
break;
case 120:
row = row + 1;
break;
case 150:
case 160:
col = col - 1;
row = row + 1;
break;
case 180:
col = col - 1;
break;
case 200:
case 210:
col = col - 1;
row = row - 1;
break;
case 240:
row = row - 1;
break;
case 270:
case 280:
row = row - 2;
break;
case 300:
col = col + 1;
row = row - 2;
break;
case 320:
case 330:
col = col + 1;
row = row - 1;
break;
default:
(void) fprintf(stderr, "wrong direction %d\n", dir);
}
}
}
*pcol = col;
*prow = row;
}
static Bool
withinBounds(circuitstruct * wp, int col, int row)
{
return (row >= 2 && row < wp->bnrows - 2 &&
col >= 2 && col < wp->bncols - 2 - (wp->neighbors == 6 && !(row % 2)));
}
static void
fillcell(ModeInfo * mi, GC gc, int col, int row)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
if (wp->neighbors == 6) {
int ccol = 2 * col + !(row & 1), crow = 2 * row;
wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
if (wp->xs <= 3 || wp->ys <= 3)
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->shape.hexagon[0].x, wp->shape.hexagon[0].y);
else
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->shape.hexagon, 6, Convex, CoordModePrevious);
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
wp->xs - (wp->xs > 3), wp->ys - (wp->ys > 3));
} else { /* TRI */
int orient = (col + row) % 2; /* O left 1 right */
wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
if (wp->xs <= 3 || wp->ys <= 3)
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
((orient) ? -1 : 1) + wp->shape.triangle[orient][0].x,
wp->shape.triangle[orient][0].y);
else {
if (orient)
wp->shape.triangle[orient][0].x += (wp->xs / 2 - 1);
else
wp->shape.triangle[orient][0].x -= (wp->xs / 2 - 1);
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->shape.triangle[orient], 3, Convex, CoordModePrevious);
}
}
}
static void
drawcell(ModeInfo * mi, int col, int row, unsigned char state)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
GC gc;
if (MI_NPIXELS(mi) > 2) {
gc = MI_GC(mi);
XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
} else {
XGCValues gcv;
gcv.stipple = wp->pixmaps[state];
gcv.foreground = MI_WHITE_PIXEL(mi);
gcv.background = MI_BLACK_PIXEL(mi);
XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
GCStipple | GCForeground | GCBackground, &gcv);
gc = wp->stippledGC;
}
fillcell(mi, gc, col, row);
}
#if 0
static void
drawcell_notused(ModeInfo * mi, int col, int row, unsigned char state)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
XGCValues gcv;
GC gc;
if (MI_NPIXELS(mi) > 2) {
gc = MI_GC(mi);
XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
} else {
gcv.stipple = wp->pixmaps[state];
gcv.foreground = MI_WHITE_PIXEL(mi);
gcv.background = MI_BLACK_PIXEL(mi);
XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
GCStipple | GCForeground | GCBackground, &gcv);
gc = wp->stippledGC;
}
XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
wp->xb + wp->xs * col, wp->yb + wp->ys * row,
wp->xs - (wp->xs > 3), wp->ys - (wp->ys > 3));
}
#endif
static Bool
addtolist(ModeInfo * mi, int col, int row, unsigned char state)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
CellList *current = wp->cellList[state];
wp->cellList[state] = (CellList *) NULL;
if ((wp->cellList[state] = (CellList *) malloc(sizeof (CellList))) ==
NULL) {
return False;
}
wp->cellList[state]->pt.x = col;
wp->cellList[state]->pt.y = row;
wp->cellList[state]->next = current;
wp->ncells[state]++;
return True;
}
#ifdef DEBUG
static void
print_state(ModeInfo * mi, int state)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
CellList *locallist = wp->cellList[state];
int i = 0;
(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(circuitstruct * wp, int state)
{
CellList *current;
while (wp->cellList[state]) {
current = wp->cellList[state];
wp->cellList[state] = wp->cellList[state]->next;
free(current);
}
wp->ncells[state] = 0;
}
static Bool
draw_state(ModeInfo * mi, int state)
{
circuitstruct *wp = &circuits[MI_SCREEN(mi)];
GC gc;
XGCValues gcv;
CellList *current = wp->cellList[state];
if (MI_NPIXELS(mi) > 2) {
gc = MI_GC(mi);
XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
} else {
gcv.stipple = wp->pixmaps[state];
gcv.foreground = MI_WHITE_PIXEL(mi);
gcv.background = MI_BLACK_PIXEL(mi);
XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
GCStipple | GCForeground | GCBackground, &gcv);
gc = wp->stippledGC;
}
if (wp->neighbors == 6) { /* Draw right away, slow */
while (current) {
int col, row, ccol, crow;
col = current->pt.x;
row = current->pt.y;
ccol = 2 * col + !(row & 1), crow = 2 * row;
wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
if (wp->xs <= 1 || wp->ys <= 1)
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
gc, wp->shape.hexagon[0].x, wp->shape.hexagon[0].y);
else
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->shape.hexagon, 6, Convex, CoordModePrevious);
current = current->next;
}
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
XRectangle *rects;
/* Take advantage of XFillRectangles */
int nrects = 0;
/* Create Rectangle list from part of the cellList */
if ((rects = (XRectangle *) malloc(wp->ncells[state] *
sizeof (XRectangle))) == NULL) {
return False;
}
while (current) {
rects[nrects].x = wp->xb + current->pt.x * wp->xs;
rects[nrects].y = wp->yb + current->pt.y * wp->ys;
rects[nrects].width = wp->xs - (wp->xs > 3);
rects[nrects].height = wp->ys - (wp->ys > 3);
current = current->next;
nrects++;
}
/* Finally get to draw */
XFillRectangles(MI_DISPLAY(mi), MI_WINDOW(mi), gc, rects, nrects);
/* Free up rects list and the appropriate part of the cellList */
free(rects);
} else { /* TRI */
while (current) {
int col, row, orient;
col = current->pt.x;
row = current->pt.y;
orient = (col + row) % 2; /* O left 1 right */
wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
if (wp->xs <= 3 || wp->ys <= 3)
XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
((orient) ? -1 : 1) + wp->shape.triangle[orient][0].x,
wp->shape.triangle[orient][0].y);
else {
if (orient)
wp->shape.triangle[orient][0].x += (wp->xs / 2 - 1);
else
wp->shape.triangle[orient][0].x -= (wp->xs / 2 - 1);
XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
wp->shape.triangle[orient], 3, Convex, CoordModePrevious);
}
current = current->next;
}
}
free_state(wp, state);
XFlush(MI_DISPLAY(mi));
return True;
}
#if 0
static void
RandomSoup(circuitstruct * wp)
{
int i, j;
for (j = 2; j < wp->bnrows - 2; j++)
for (i = 2; i < wp->bncols - 2; i++) {
*(wp->newcells + i + j * wp->bncols) =
(NRAND(100) > wp->n) ? SPACE : (NRAND(4)) ? WIRE : (NRAND(2)) ?
HEAD : TAIL;
}
}
#endif
static void
create_path(circuitstruct * wp, int n)
{
int col, row;
int count = 0;
int dir, prob;
int nextcol, nextrow, i;
#ifdef RANDOMSTART
/* Path usually "mushed" in a corner */
col = NRAND(wp->ncols) + 1;
row = NRAND(wp->nrows) + 1;
#else
/* Start from center */
col = wp->ncols / 2;
row = wp->nrows / 2;
#endif
wp->mincol = col - 1, wp->minrow = row - 2;
wp->maxcol = col + 1, wp->maxrow = row + 2;
dir = NRAND(wp->neighbors) * ANGLES / wp->neighbors;
*(wp->newcells + col + row * wp->bncols) = HEAD;
while (++count < n) {
prob = NRAND(wp->prob_array[wp->neighbors - 1]);
i = 0;
while (prob > wp->prob_array[i])
i++;
dir = ((dir * wp->neighbors / ANGLES + i) %
wp->neighbors) * ANGLES / wp->neighbors;
nextcol = col;
nextrow = row;
position_of_neighbor(wp, dir, &nextcol, &nextrow);
if (withinBounds(wp, nextcol, nextrow)) {
col = nextcol;
row = nextrow;
if (col == wp->mincol && col > 2)
wp->mincol--;
if (row == wp->minrow && row > 2)
wp->minrow--;
else if (row == wp->minrow - 1 && row > 3)
wp->minrow -= 2;
if (col == wp->maxcol && col < wp->bncols - 3)
wp->maxcol++;
if (row == wp->maxrow && row < wp->bnrows - 3)
wp->maxrow++;
else if (row == wp->maxrow + 1 && row < wp->bnrows - 4)
wp->maxrow += 2;
if (!*(wp->newcells + col + row * wp->bncols))
*(wp->newcells + col + row * wp->bncols) = WIRE;
} else {
if (wp->neighbors == 3)
break; /* There is no reverse step */
dir = ((dir * wp->neighbors / ANGLES + wp->neighbors / 2) %
wp->neighbors) * ANGLES / wp->neighbors;
}
}
*(wp->newcells + col + row * wp->bncols) = HEAD;
}
static void
do_gen(circuitstruct * wp)
{
int i, j, k;
unsigned char *z;
int count;
#define LOC(X, Y) (*(wp->oldcells + (X) + ((Y) * wp->bncols)))
#define ADD(X, Y) if (LOC((X), (Y)) == HEAD) count++
for (j = wp->minrow; j <= wp->maxrow; j++) {
for (i = wp->mincol; i <= wp->maxcol; i++) {
z = wp->newcells + i + j * wp->bncols;
switch (LOC(i, j)) {
case SPACE:
*z = SPACE;
break;
case TAIL:
*z = WIRE;
break;
case HEAD:
*z = TAIL;
break;
case WIRE:
count = 0;
for (k = 0; k < wp->neighbors; k++) {
int newi = i, newj = j;
position_of_neighbor(wp, k * ANGLES / wp->neighbors, &newi, &newj);
ADD(newi, newj);
}
if (count == 1 || count == 2)
*z = HEAD;
else
*z = WIRE;
break;
default:
{
(void) fprintf(stderr,
"bad internal character %d at %d,%d\n",
(int) LOC(i, j), i, j);
}
}
}
}
}
static void
free_list(circuitstruct * wp)
{
int state;
for (state = 0; state < COLORS - 1; state++)
free_state(wp, state);
}
static void
free_wire(Display *display, circuitstruct *wp)
{
int shade;
for (shade = 0; shade < wp->init_bits; shade++)
XFreePixmap(display, wp->pixmaps[shade]);
wp->init_bits = 0;
if (wp->stippledGC != None) {
XFreeGC(display, wp->stippledGC);
wp->stippledGC = None;
}
if (wp->oldcells != NULL) {
free(wp->oldcells);
wp->oldcells = (unsigned char *) NULL;
}
if (wp->newcells != NULL) {
free(wp->newcells);
wp->newcells = (unsigned char *) NULL;
}
free_list(wp);
}
void
release_wire(ModeInfo * mi)
{
if (circuits != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_wire(MI_DISPLAY(mi), &circuits[screen]);
free(circuits);
circuits = (circuitstruct *) NULL;
}
}
void
init_wire(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
int i, size = MI_SIZE(mi), n;
circuitstruct *wp;
XGCValues gcv;
if (circuits == NULL) {
if ((circuits = (circuitstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (circuitstruct))) == NULL)
return;
}
wp = &circuits[MI_SCREEN(mi)];
wp->redrawing = 0;
if ((MI_NPIXELS(mi) <= 2) && (wp->init_bits == 0)) {
if (wp->stippledGC == None) {
gcv.fill_style = FillOpaqueStippled;
if ((wp->stippledGC = XCreateGC(display, window, GCFillStyle,
&gcv)) == None) {
free_wire(display, wp);
return;
}
}
WIREBITS(stipples[NUMSTIPPLES - 1], STIPPLESIZE, STIPPLESIZE);
WIREBITS(stipples[NUMSTIPPLES - 3], STIPPLESIZE, STIPPLESIZE);
WIREBITS(stipples[2], STIPPLESIZE, STIPPLESIZE);
}
if (MI_NPIXELS(mi) > 2) {
wp->colors[0] = (NRAND(MI_NPIXELS(mi)));
wp->colors[1] = (wp->colors[0] + MI_NPIXELS(mi) / 6 +
NRAND(MI_NPIXELS(mi) / 4 + 1)) % MI_NPIXELS(mi);
wp->colors[2] = (wp->colors[1] + MI_NPIXELS(mi) / 6 +
NRAND(MI_NPIXELS(mi) / 4 + 1)) % MI_NPIXELS(mi);
}
free_list(wp);
wp->generation = 0;
wp->width = MI_WIDTH(mi);
wp->height = MI_HEIGHT(mi);
for (i = 0; i < NEIGHBORKINDS; i++) {
if (neighbors == plots[i]) {
wp->neighbors = plots[i];
break;
}
if (i == NEIGHBORKINDS - 1) {
i = NRAND(NEIGHBORKINDS - 3) + 1; /* Skip triangular ones */
wp->neighbors = plots[i];
break;
}
}
wp->prob_array[wp->neighbors - 1] = 100;
if (wp->neighbors == 3) {
wp->prob_array[1] = 67;
wp->prob_array[0] = 33;
} else {
int incr = 24 / wp->neighbors;
for (i = wp->neighbors - 2; i >= 0; i--) {
wp->prob_array[i] = wp->prob_array[i + 1] - incr -
incr * ((i + 1) != wp->neighbors / 2);
}
}
if (wp->neighbors == 6) {
int nccols, ncrows;
if (wp->width < 8)
wp->width = 8;
if (wp->height < 8)
wp->height = 8;
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
else
wp->ys = MINSIZE;
} else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = wp->ys;
nccols = MAX(wp->width / wp->xs - 2, 16);
ncrows = MAX(wp->height / wp->ys - 1, 16);
wp->ncols = nccols / 2;
wp->nrows = ncrows / 2;
wp->nrows -= !(wp->nrows & 1); /* Must be odd */
wp->xb = (wp->width - wp->xs * nccols) / 2 + wp->xs;
wp->yb = (wp->height - wp->ys * ncrows) / 2 + wp->ys;
for (i = 0; i < 6; i++) {
wp->shape.hexagon[i].x = (wp->xs - 1) * hexagonUnit[i].x;
wp->shape.hexagon[i].y = ((wp->ys - 1) * hexagonUnit[i].y / 2) * 4 / 3;
}
} else if (wp->neighbors == 4 || wp->neighbors == 8) {
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
else
wp->ys = MINSIZE;
} else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = wp->ys;
wp->ncols = MAX(wp->width / wp->xs, 8);
wp->nrows = MAX(wp->height / wp->ys, 8);
wp->xb = (wp->width - wp->xs * wp->ncols) / 2;
wp->yb = (wp->height - wp->ys * wp->nrows) / 2;
} else { /* TRI */
int orient;
if (wp->width < 4)
wp->width = 4;
if (wp->height < 2)
wp->height = 2;
if (size < -MINSIZE)
wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE) {
if (!size)
wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
else
wp->ys = MINSIZE;
} else
wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
MINGRIDSIZE));
wp->xs = (int) (1.52 * wp->ys);
wp->ncols = (MAX(wp->width / wp->xs - 1, 8) / 2) * 2;
wp->nrows = (MAX(wp->height / wp->ys - 1, 8) / 2) * 2 - 1;
wp->xb = (wp->width - wp->xs * wp->ncols) / 2 + wp->xs / 2;
wp->yb = (wp->height - wp->ys * wp->nrows) / 2 + wp->ys;
for (orient = 0; orient < 2; orient++) {
for (i = 0; i < 3; i++) {
wp->shape.triangle[orient][i].x =
(wp->xs - 2) * triangleUnit[orient][i].x;
wp->shape.triangle[orient][i].y =
(wp->ys - 2) * triangleUnit[orient][i].y;
}
}
}
/*
* I am being a bit naughty here wasting a little bit of memory
* but it will give me a real headache to figure out the logic
* and to refigure the mappings to save a few bytes
* ncols should only need a border of 2 and nrows should only need
* a border of 4 when in the neighbors = 9 or 12
*/
wp->bncols = wp->ncols + 4;
wp->bnrows = wp->nrows + 4;
if (MI_IS_VERBOSE(mi))
(void) fprintf(stdout,
"neighbors %d, ncols %d, nrows %d\n",
wp->neighbors, wp->ncols, wp->nrows);
MI_CLEARWINDOW(mi);
if (wp->oldcells != NULL) {
free(wp->oldcells);
wp->oldcells = (unsigned char *) NULL;
}
if ((wp->oldcells = (unsigned char *) calloc(wp->bncols * wp->bnrows,
sizeof (unsigned char))) == NULL) {
free_wire(display, wp);
return;
}
if (wp->newcells != NULL) {
free(wp->newcells);
wp->newcells = (unsigned char *) NULL;
}
if ((wp->newcells = (unsigned char *) calloc(wp->bncols * wp->bnrows,
sizeof (unsigned char))) == NULL) {
free_wire(display, wp);
return;
}
n = MI_COUNT(mi);
i = (1 + (wp->neighbors == 6)) * wp->ncols * wp->nrows / 4;
if (n < -MINWIRES && i > MINWIRES) {
n = NRAND(MIN(-n, i) - MINWIRES + 1) + MINWIRES;
} else if (n < MINWIRES) {
n = MINWIRES;
} else if (n > i) {
n = MAX(MINWIRES, i);
}
create_path(wp, n);
}
void
draw_wire(ModeInfo * mi)
{
int offset, i, j, found = 0;
unsigned char *z, *znew;
circuitstruct *wp;
if (circuits == NULL)
return;
wp = &circuits[MI_SCREEN(mi)];
if (wp->newcells == NULL)
return;
MI_IS_DRAWN(mi) = True;
/* wires do not grow so min max stuff does not change */
for (j = wp->minrow; j <= wp->maxrow; j++) {
for (i = wp->mincol; i <= wp->maxcol; i++) {
offset = j * wp->bncols + i;
z = wp->oldcells + offset;
znew = wp->newcells + offset;
if (*z != *znew) { /* Counting on once a space always a space */
found = 1;
*z = *znew;
if (!addtolist(mi, i - 2, j - 2, *znew - 1)) {
free_wire(MI_DISPLAY(mi), wp);
return;
}
}
}
}
for (i = 0; i < COLORS - 1; i++)
if (!draw_state(mi, i)) {
free_wire(MI_DISPLAY(mi), wp);
return;
}
if (++wp->generation > MI_CYCLES(mi) || !found) {
init_wire(mi);
return;
} else
do_gen(wp);
if (wp->redrawing) {
for (i = 0; i < REDRAWSTEP; i++) {
if ((*(wp->oldcells + wp->redrawpos))) {
drawcell(mi, wp->redrawpos % wp->bncols - 2,
wp->redrawpos / wp->bncols - 2, *(wp->oldcells + wp->redrawpos) - 1);
}
if (++(wp->redrawpos) >= wp->bncols * (wp->bnrows - 2)) {
wp->redrawing = 0;
break;
}
}
}
}
void
refresh_wire(ModeInfo * mi)
{
circuitstruct *wp;
if (circuits == NULL)
return;
wp = &circuits[MI_SCREEN(mi)];
MI_CLEARWINDOW(mi);
wp->redrawing = 1;
wp->redrawpos = 2 * wp->ncols + 2;
}
#endif /* MODE_wire */