/* -*- 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 * and Stefan Strack * . */ /*- * # 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 */