1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* wire --- logical circuits based on simple state-changes (wireworld) */
3
4 #if 0
5 static const char sccsid[] = "@(#)wire.c 5.24 2007/01/18 xlockmore";
6
7 #endif
8
9 /*-
10 * Copyright (c) 1996 by David Bagley.
11 *
12 * Permission to use, copy, modify, and distribute this software and its
13 * documentation for any purpose and without fee is hereby granted,
14 * provided that the above copyright notice appear in all copies and that
15 * both that copyright notice and this permission notice appear in
16 * supporting documentation.
17 *
18 * This file is provided AS IS with no warranties of any kind. The author
19 * shall have no liability with respect to the infringement of copyrights,
20 * trade secrets or any patents by this file or any part thereof. In no
21 * event will the author be liable for any lost revenue or profits or
22 * other special, indirect and consequential damages.
23 *
24 * Revision History:
25 * 18-Jan-2007: Added vertical option.
26 * 01-Nov-2000: Allocation checks
27 * 05-Dec-1997: neighbors option added.
28 * 10-May-1997: Compatible with xscreensaver
29 * 14-Jun-1996: Coded from A.K. Dewdney's "Computer Recreations", Scientific
30 * American Magazine" Jan 1990 pp 146-148. Used ant.c as an
31 * example. do_gen() based on code by Kevin Dahlhausen
32 * <ap096@po.cwru.edu> and Stefan Strack
33 * <stst@vuse.vanderbilt.edu>.
34 */
35
36 /*-
37 * # Rules file for Wireworld
38 *
39 * # 0 is space, 1 is wire, 2 is tail, 3 is head
40 * states 4
41 *
42 * passive 2
43 * 0[0123][0123][0123][0123]0 # No way to make a space into signal or wire
44 *
45 * # Signal propagation
46 * 2[0123][0123][0123][0123]1 # tail -> wire
47 * 3[0123][0123][0123][0123]2 # head -> tail
48 *
49 * # 1 or 2 heads adjacent to a wire makes it a head
50 * 1(3*1)3 # wire with 1 head adjacent -> head
51 * 1(3*2)3 # wire with 2 heads adjacent -> head
52 *
53 *
54 * " " is space, X is wire, o is tail, O is head
55 *
56 * ->XXXoOXXX-> Electron moving in a wire
57 *
58 * Diode -- permits an electron to pass right to left, but not the other way.
59 * XX
60 * XX XXX
61 * XX
62 *
63 * OR gate or fan-in where inputs are protected by diodes
64 * XX XX
65 * Input ->XXX XX XX XXX<- Input
66 * XX X XX
67 * X
68 * X
69 * |
70 * V
71 * Output
72 *
73 * Dewdney's synchronous-logic flipflop.
74 * Bottom left input is write 1, top left is remember 0.
75 * When gate is on, 1 electron goes to output at right every 13 cycles
76 * memory element, about to forget 1 and remember 0
77 * Remember 0
78 * o
79 * X O XX XX Memory Loop
80 * X XX X XXXX
81 * X XX XX X oOX-> 1 Output
82 * Inputs ->XX X X
83 * X X XX o
84 * Inputs ->XXXXXXX XX X XO Memory of 1
85 * XX XX
86 * Remember 1
87 */
88
89 #ifdef STANDALONE
90 #define MODE_wire
91 #define DEFAULTS "*delay: 500000 \n" \
92 "*count: 1000 \n" \
93 "*cycles: 150 \n" \
94 "*size: -8 \n" \
95 "*ncolors: 64 \n" \
96
97 # define free_wire 0
98 # define reshape_wire 0
99 # define wire_handle_event 0
100 #include "xlockmore.h" /* in xscreensaver distribution */
101 #else /* STANDALONE */
102 #include "xlock.h" /* in xlockmore distribution */
103 #define DO_STIPPLE
104 #endif /* STANDALONE */
105 #include "automata.h"
106
107 #ifdef MODE_wire
108
109 /*-
110 * neighbors of 0 randomizes it between 3, 4, 6, 8, 9, and 12.
111 */
112 #define DEF_NEIGHBORS "0" /* choose random value */
113 #define DEF_VERTICAL "False"
114
115 static int neighbors;
116 static Bool vertical;
117
118 static XrmOptionDescRec opts[] =
119 {
120 {(char *) "-neighbors", (char *) ".wire.neighbors", XrmoptionSepArg, (caddr_t) NULL},
121 {(char *) "-vertical", (char *) ".wire.vertical", XrmoptionNoArg, (caddr_t) "on"},
122 {(char *) "+vertical", (char *) ".wire.vertical", XrmoptionNoArg, (caddr_t) "off"}
123 };
124
125 static argtype vars[] =
126 {
127 {(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int},
128 {(void *) & vertical, (char *) "vertical", (char *) "Vertical", (char *) DEF_VERTICAL, t_Bool}
129 };
130 static OptionStruct desc[] =
131 {
132 {(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6, triangles 3, 9 or 12"},
133 {(char *) "-/+vertical", (char *) "change orientation for hexagons and triangles"}
134 };
135
136 ENTRYPOINT ModeSpecOpt wire_opts =
137 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
138
139 #ifdef USE_MODULES
140 ModStruct wire_description =
141 {"wire", "init_wire", "draw_wire", "release_wire",
142 "refresh_wire", "init_wire", (char *) NULL, &wire_opts,
143 500000, 1000, 150, -8, 64, 1.0, "",
144 "Shows a random circuit with 2 electrons", 0, NULL};
145
146 #endif
147
148 #define WIREBITS(n,w,h)\
149 if ((wp->pixmaps[wp->init_bits]=\
150 XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
151 free_wire_screen(display,wp); return;} else {wp->init_bits++;}
152
153 #define COLORS 4
154 #define MINWIRES 32
155 #define MINGRIDSIZE 24
156 #define MINSIZE 3
157 #define ANGLES 360
158 #define NEIGHBORKINDS 6
159
160 #define SPACE 0
161 #define WIRE 1 /* Normal wire */
162 #define HEAD 2 /* electron head */
163 #define TAIL 3 /* electron tail */
164
165 #define REDRAWSTEP 2000 /* How much wire to draw per cycle */
166
167 /* Singly linked list */
168 typedef struct _CellList {
169 XPoint pt;
170 struct _CellList *next;
171 } CellList;
172
173 typedef struct {
174 Bool vertical;
175 int init_bits;
176 int neighbors, polygon;
177 int generation;
178 int xs, ys;
179 int xb, yb;
180 int nrows, ncols;
181 int bnrows, bncols;
182 int mincol, minrow, maxcol, maxrow;
183 int width, height;
184 int redrawing, redrawpos;
185 unsigned char *oldcells, *newcells;
186 int ncells[COLORS - 1];
187 CellList *cellList[COLORS - 1];
188 unsigned char colors[COLORS - 1];
189 GC stippledGC;
190 Pixmap pixmaps[COLORS - 1];
191 int prob_array[12];
192 union {
193 XPoint hexagon[6];
194 XPoint triangle[2][3];
195 } shape;
196 } circuitstruct;
197
198 static char plots[NEIGHBORKINDS] =
199 {3, 4, 6, 8, 9, 12}; /* Neighborhoods */
200
201 static circuitstruct *circuits = (circuitstruct *) NULL;
202
203 static void
positionOfNeighbor(circuitstruct * wp,int dir,int * pcol,int * prow)204 positionOfNeighbor(circuitstruct * wp, int dir, int *pcol, int *prow)
205 {
206 int col = *pcol, row = *prow;
207
208 /* NO WRAPPING */
209
210 if (wp->polygon == 4 || wp->polygon == 6) {
211 switch (dir) {
212 case 0:
213 col = col + 1;
214 break;
215 case 45:
216 col = col + 1;
217 row = row - 1;
218 break;
219 case 60:
220 if (!(row & 1))
221 col = col + 1;
222 row = row - 1;
223 break;
224 case 90:
225 row = row - 1;
226 break;
227 case 120:
228 if (row & 1)
229 col = col - 1;
230 row = row - 1;
231 break;
232 case 135:
233 col = col - 1;
234 row = row - 1;
235 break;
236 case 180:
237 col = col - 1;
238 break;
239 case 225:
240 col = col - 1;
241 row = row + 1;
242 break;
243 case 240:
244 if (row & 1)
245 col = col - 1;
246 row = row + 1;
247 break;
248 case 270:
249 row = row + 1;
250 break;
251 case 300:
252 if (!(row & 1))
253 col = col + 1;
254 row = row + 1;
255 break;
256 case 315:
257 col = col + 1;
258 row = row + 1;
259 break;
260 default:
261 (void) fprintf(stderr, "wrong direction %d\n", dir);
262 }
263 } else if (wp->polygon == 3) {
264 if ((col + row) & 1) { /* right */
265 switch (dir) {
266 case 0:
267 col = col - 1;
268 break;
269 case 30:
270 case 40:
271 col = col - 1;
272 row = row - 1;
273 break;
274 case 60:
275 col = col - 1;
276 row = row - 2;
277 break;
278 case 80:
279 case 90:
280 row = row - 2;
281 break;
282 case 120:
283 row = row - 1;
284 break;
285 case 150:
286 case 160:
287 col = col + 1;
288 row = row - 1;
289 break;
290 case 180:
291 col = col + 1;
292 break;
293 case 200:
294 case 210:
295 col = col + 1;
296 row = row + 1;
297 break;
298 case 240:
299 row = row + 1;
300 break;
301 case 270:
302 case 280:
303 row = row + 2;
304 break;
305 case 300:
306 col = col - 1;
307 row = row + 2;
308 break;
309 case 320:
310 case 330:
311 col = col - 1;
312 row = row + 1;
313 break;
314 default:
315 (void) fprintf(stderr, "wrong direction %d\n", dir);
316 }
317 } else { /* left */
318 switch (dir) {
319 case 0:
320 col = col + 1;
321 break;
322 case 30:
323 case 40:
324 col = col + 1;
325 row = row + 1;
326 break;
327 case 60:
328 col = col + 1;
329 row = row + 2;
330 break;
331 case 80:
332 case 90:
333 row = row + 2;
334 break;
335 case 120:
336 row = row + 1;
337 break;
338 case 150:
339 case 160:
340 col = col - 1;
341 row = row + 1;
342 break;
343 case 180:
344 col = col - 1;
345 break;
346 case 200:
347 case 210:
348 col = col - 1;
349 row = row - 1;
350 break;
351 case 240:
352 row = row - 1;
353 break;
354 case 270:
355 case 280:
356 row = row - 2;
357 break;
358 case 300:
359 col = col + 1;
360 row = row - 2;
361 break;
362 case 320:
363 case 330:
364 col = col + 1;
365 row = row - 1;
366 break;
367 default:
368 (void) fprintf(stderr, "wrong direction %d\n", dir);
369 }
370 }
371 #if 0
372 } else {
373 int orient = ((row & 1) * 2 + col) % 4;
374 switch (orient) { /* up, down, left, right */
375 case 0:
376 switch (dir) {
377 case 0:
378 col++;
379 break;
380 case 51: /* 7 */
381 case 72: /* 5 */
382 col = col + 2;
383 break;
384 case 102: /* 7 corner */
385 col = col + 3;
386 row = row - 1;
387 break;
388 case 144: /* 5 */
389 case 153: /* 7 */
390 col++;
391 row = row - 1;
392 break;
393 case 204: /* 7 */
394 case 216: /* 5 */
395 row = row - 1;
396 break;
397 case 255: /* 7 */
398 col = col - 1;
399 row = row - 1;
400 break;
401 case 288: /* 5 */
402 case 306: /* 7 */
403 col = col - 1;
404 break;
405 default:
406 (void) fprintf(stderr, "wrong direction %d\n",
407 dir);
408 }
409 break;
410 case 1:
411 switch (dir) {
412 case 0:
413 col--;
414 break;
415 case 51: /* 7 */
416 case 72: /* 5 */
417 col = col - 2;
418 break;
419 case 102: /* 7 */
420 col = col - 3;
421 row = row + 1;
422 break;
423 case 144: /* 5 */
424 case 153: /* 7 */
425 col--;
426 row = row + 1;
427 break;
428 case 204: /* 7 */
429 case 216: /* 5 */
430 row = row + 1;
431 break;
432 case 255: /* 7 */
433 col = col + 1;
434 row = row + 1;
435 break;
436 case 288: /* 5 */
437 case 306: /* 7 */
438 col = col + 1;
439 break;
440 default:
441 (void) fprintf(stderr, "wrong direction %d\n",
442 dir);
443 }
444 break;
445 case 2:
446 switch (dir) {
447 case 0:
448 col = col + 1;
449 break;
450 case 51: /* 7 */
451 case 72: /* 5 */
452 row = row - 1;
453 col++;
454 break;
455 case 102: /* 7 */
456 col = col - 1;
457 row = row - 1;
458 break;
459 case 144: /* 5 */
460 case 153: /* 7 */
461 col = col - 2;
462 break;
463 case 204: /* 7 */
464 case 216: /* 5 */
465 col = col - 1;
466 break;
467 case 255: /* 7 */
468 row = row + 1;
469 col = col - 1;
470 break;
471 case 288: /* 5 */
472 case 306: /* 7 */
473 row = row + 1;
474 break;
475 default:
476 (void) fprintf(stderr, "wrong direction %d\n",
477 dir);
478 }
479 break;
480 case 3:
481 switch (dir) {
482 case 0:
483 col--;
484 break;
485 case 51: /* 7 */
486 case 72: /* 5 */
487 col = col - 1;
488 row = row + 1;
489 break;
490 case 102: /* 7 */
491 col = col + 1;
492 row = row + 1;
493 break;
494 case 144: /* 5 */
495 case 153: /* 7 */
496 col = col + 2;
497 break;
498 case 204: /* 7 */
499 case 216: /* 5 */
500 col = col + 1;
501 break;
502 case 255: /* 7 */
503 col = col + 1;
504 row = row - 1;
505 break;
506 case 288: /* 5 */
507 case 306: /* 7 */
508 row = row - 1;
509 break;
510 default:
511 (void) fprintf(stderr, "wrong direction %d\n",
512 dir);
513 }
514 break;
515 default:
516 (void) fprintf(stderr, "wrong orient %d\n",
517 orient);
518 }
519 #endif
520 }
521 *pcol = col;
522 *prow = row;
523 }
524
525 static Bool
withinBounds(circuitstruct * wp,int col,int row)526 withinBounds(circuitstruct * wp, int col, int row)
527 {
528 return (row >= 2 && row < wp->bnrows - 2 &&
529 col >= 2 && col < wp->bncols - 2 - (wp->polygon == 6 && !(row % 2)));
530 }
531
532 static void
fillcell(ModeInfo * mi,GC gc,int col,int row)533 fillcell(ModeInfo * mi, GC gc, int col, int row)
534 {
535 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
536
537 if (wp->neighbors == 6) {
538 int ccol = 2 * col + !(row & 1), crow = 2 * row;
539
540 if (wp->vertical) {
541 wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
542 wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
543 } else {
544 wp->shape.hexagon[0].y = wp->xb + ccol * wp->xs;
545 wp->shape.hexagon[0].x = wp->yb + crow * wp->ys;
546 }
547 if (wp->xs <= 3 || wp->ys <= 3)
548 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
549 wp->shape.hexagon[0].x,
550 wp->shape.hexagon[0].y);
551 else
552 XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
553 wp->shape.hexagon, 6,
554 Convex, CoordModePrevious);
555 } else if (wp->neighbors == 4 || wp->neighbors == 8) {
556 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
557 wp->xb + wp->xs * col, wp->yb + wp->ys * row,
558 wp->xs - (wp->xs > 3), wp->ys - (wp->ys > 3));
559 } else { /* TRI */
560 int orient = (col + row) % 2; /* O left 1 right */
561 Bool small = (wp->xs <= 3 || wp->ys <= 3);
562
563 if (wp->vertical) {
564 wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
565 wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
566 if (small)
567 wp->shape.triangle[orient][0].x +=
568 ((orient) ? -1 : 1);
569 else
570 wp->shape.triangle[orient][0].x +=
571 (wp->xs / 2 - 1) * ((orient) ? 1 : -1);
572 } else {
573 wp->shape.triangle[orient][0].y = wp->xb + col * wp->xs;
574 wp->shape.triangle[orient][0].x = wp->yb + row * wp->ys;
575 if (small)
576 wp->shape.triangle[orient][0].y +=
577 ((orient) ? -1 : 1);
578 else
579 wp->shape.triangle[orient][0].y +=
580 (wp->xs / 2 - 1) * ((orient) ? 1 : -1);
581 }
582 if (small)
583 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
584 wp->shape.triangle[orient][0].x,
585 wp->shape.triangle[orient][0].y);
586 else {
587 XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
588 wp->shape.triangle[orient], 3,
589 Convex, CoordModePrevious);
590 }
591 }
592 }
593
594 static void
drawCell(ModeInfo * mi,int col,int row,unsigned char state)595 drawCell(ModeInfo * mi, int col, int row, unsigned char state)
596 {
597 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
598 GC gc;
599
600 if (MI_NPIXELS(mi) > 2) {
601 gc = MI_GC(mi);
602 XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
603 } else {
604 XGCValues gcv;
605
606 gcv.stipple = wp->pixmaps[state];
607 gcv.foreground = MI_WHITE_PIXEL(mi);
608 gcv.background = MI_BLACK_PIXEL(mi);
609 XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
610 GCStipple | GCForeground | GCBackground, &gcv);
611 gc = wp->stippledGC;
612 }
613 fillcell(mi, gc, col, row);
614 }
615
616 #if 0
617 static void
618 drawCell_notused(ModeInfo * mi, int col, int row, unsigned char state)
619 {
620 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
621 XGCValues gcv;
622 GC gc;
623
624 if (MI_NPIXELS(mi) > 2) {
625 gc = MI_GC(mi);
626 XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
627 } else {
628 gcv.stipple = wp->pixmaps[state];
629 gcv.foreground = MI_WHITE_PIXEL(mi);
630 gcv.background = MI_BLACK_PIXEL(mi);
631 XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
632 GCStipple | GCForeground | GCBackground, &gcv);
633 gc = wp->stippledGC;
634 }
635 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
636 wp->xb + wp->xs * col, wp->yb + wp->ys * row,
637 wp->xs - (wp->xs > 3), wp->ys - (wp->ys > 3));
638 }
639 #endif
640
641 static Bool
addtolist(ModeInfo * mi,int col,int row,unsigned char state)642 addtolist(ModeInfo * mi, int col, int row, unsigned char state)
643 {
644 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
645 CellList *current = wp->cellList[state];
646
647 wp->cellList[state] = (CellList *) NULL;
648 if ((wp->cellList[state] = (CellList *) malloc(sizeof (CellList))) ==
649 NULL) {
650 return False;
651 }
652 wp->cellList[state]->pt.x = col;
653 wp->cellList[state]->pt.y = row;
654 wp->cellList[state]->next = current;
655 wp->ncells[state]++;
656 return True;
657 }
658
659 #ifdef DEBUG
660 static void
print_state(ModeInfo * mi,int state)661 print_state(ModeInfo * mi, int state)
662 {
663 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
664 CellList *locallist = wp->cellList[state];
665 int i = 0;
666
667 (void) printf("state %d\n", state);
668 while (locallist) {
669 (void) printf("%d x %d, y %d\n", i,
670 locallist->pt.x, locallist->pt.y);
671 locallist = locallist->next;
672 i++;
673 }
674 }
675
676 #endif
677
678 static void
free_state(circuitstruct * wp,int state)679 free_state(circuitstruct * wp, int state)
680 {
681 CellList *current;
682
683 while (wp->cellList[state]) {
684 current = wp->cellList[state];
685 wp->cellList[state] = wp->cellList[state]->next;
686 free(current);
687 }
688 wp->ncells[state] = 0;
689 }
690
691 static Bool
draw_state(ModeInfo * mi,int state)692 draw_state(ModeInfo * mi, int state)
693 {
694 circuitstruct *wp = &circuits[MI_SCREEN(mi)];
695 GC gc;
696 XGCValues gcv;
697 CellList *current = wp->cellList[state];
698
699 if (MI_NPIXELS(mi) > 2) {
700 gc = MI_GC(mi);
701 XSetForeground(MI_DISPLAY(mi), gc, MI_PIXEL(mi, wp->colors[state]));
702 } else {
703 gcv.stipple = wp->pixmaps[state];
704 gcv.foreground = MI_WHITE_PIXEL(mi);
705 gcv.background = MI_BLACK_PIXEL(mi);
706 XChangeGC(MI_DISPLAY(mi), wp->stippledGC,
707 GCStipple | GCForeground | GCBackground, &gcv);
708 gc = wp->stippledGC;
709 }
710
711 if (wp->neighbors == 6) { /* Draw right away, slow */
712 while (current) {
713 int col, row, ccol, crow;
714
715 col = current->pt.x;
716 row = current->pt.y;
717 ccol = 2 * col + !(row & 1), crow = 2 * row;
718 if (wp->vertical) {
719 wp->shape.hexagon[0].x = wp->xb + ccol * wp->xs;
720 wp->shape.hexagon[0].y = wp->yb + crow * wp->ys;
721 } else {
722 wp->shape.hexagon[0].y = wp->xb + ccol * wp->xs;
723 wp->shape.hexagon[0].x = wp->yb + crow * wp->ys;
724 }
725 if (wp->xs <= 1 || wp->ys <= 1)
726 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
727 wp->shape.hexagon[0].x,
728 wp->shape.hexagon[0].y);
729 else
730 XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
731 wp->shape.hexagon, 6,
732 Convex, CoordModePrevious);
733 current = current->next;
734 }
735 } else if (wp->neighbors == 4 || wp->neighbors == 8) {
736 XRectangle *rects;
737 /* Take advantage of XFillRectangles */
738 int nrects = 0;
739
740 /* Create Rectangle list from part of the cellList */
741 if ((rects = (XRectangle *) malloc(wp->ncells[state] *
742 sizeof (XRectangle))) == NULL) {
743 return False;
744 }
745
746 while (current) {
747 rects[nrects].x = wp->xb + current->pt.x * wp->xs;
748 rects[nrects].y = wp->yb + current->pt.y * wp->ys;
749 rects[nrects].width = wp->xs - (wp->xs > 3);
750 rects[nrects].height = wp->ys - (wp->ys > 3);
751 current = current->next;
752 nrects++;
753 }
754 /* Finally get to draw */
755 XFillRectangles(MI_DISPLAY(mi), MI_WINDOW(mi), gc, rects, nrects);
756 /* Free up rects list and the appropriate part of the cellList */
757 free(rects);
758 } else { /* TRI */
759 while (current) {
760 int col, row, orient;
761 Bool small = (wp->xs <= 3 || wp->ys <= 3);
762
763 col = current->pt.x;
764 row = current->pt.y;
765 orient = (col + row) % 2; /* O left 1 right */
766 if (wp->vertical) {
767 wp->shape.triangle[orient][0].x = wp->xb + col * wp->xs;
768 wp->shape.triangle[orient][0].y = wp->yb + row * wp->ys;
769 if (small)
770 wp->shape.triangle[orient][0].x +=
771 ((orient) ? -1 : 1);
772 else
773 wp->shape.triangle[orient][0].x +=
774 (wp->xs / 2 - 1) * ((orient) ? 1 : -1);
775 } else {
776 wp->shape.triangle[orient][0].y = wp->xb + col * wp->xs;
777 wp->shape.triangle[orient][0].x = wp->yb + row * wp->ys;
778 if (small)
779 wp->shape.triangle[orient][0].y +=
780 ((orient) ? -1 : 1);
781 else
782 wp->shape.triangle[orient][0].y +=
783 (wp->xs / 2 - 1) * ((orient) ? 1 : -1);
784 }
785 if (small)
786 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
787 wp->shape.triangle[orient][0].x,
788 wp->shape.triangle[orient][0].y);
789 else {
790 XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), gc,
791 wp->shape.triangle[orient], 3,
792 Convex, CoordModePrevious);
793 }
794 current = current->next;
795 }
796 }
797 free_state(wp, state);
798 XFlush(MI_DISPLAY(mi));
799 return True;
800 }
801
802 #if 0
803 static void
804 RandomSoup(circuitstruct * wp)
805 {
806 int i, j;
807
808 for (j = 2; j < wp->bnrows - 2; j++)
809 for (i = 2; i < wp->bncols - 2; i++) {
810 *(wp->newcells + i + j * wp->bncols) =
811 (NRAND(100) > wp->n) ? SPACE : (NRAND(4)) ? WIRE : (NRAND(2)) ?
812 HEAD : TAIL;
813 }
814 }
815
816 #endif
817
818 static void
create_path(circuitstruct * wp,int n)819 create_path(circuitstruct * wp, int n)
820 {
821 int col, row;
822 int count = 0;
823 int dir, prob;
824 int nextcol, nextrow, i;
825
826 #ifdef RANDOMSTART
827 /* Path usually "mushed" in a corner */
828 col = NRAND(wp->ncols) + 1;
829 row = NRAND(wp->nrows) + 1;
830 #else
831 /* Start from center */
832 col = wp->ncols / 2;
833 row = wp->nrows / 2;
834 #endif
835 wp->mincol = col - 1, wp->minrow = row - 2;
836 wp->maxcol = col + 1, wp->maxrow = row + 2;
837 dir = NRAND(wp->neighbors) * ANGLES / wp->neighbors;
838 *(wp->newcells + col + row * wp->bncols) = HEAD;
839 while (++count < n) {
840 prob = NRAND(wp->prob_array[wp->neighbors - 1]);
841 i = 0;
842 while (prob > wp->prob_array[i])
843 i++;
844 dir = ((dir * wp->neighbors / ANGLES + i) %
845 wp->neighbors) * ANGLES / wp->neighbors;
846 nextcol = col;
847 nextrow = row;
848 positionOfNeighbor(wp, dir, &nextcol, &nextrow);
849 if (withinBounds(wp, nextcol, nextrow)) {
850 col = nextcol;
851 row = nextrow;
852 if (col == wp->mincol && col > 2)
853 wp->mincol--;
854 if (row == wp->minrow && row > 2)
855 wp->minrow--;
856 else if (row == wp->minrow - 1 && row > 3)
857 wp->minrow -= 2;
858 if (col == wp->maxcol && col < wp->bncols - 3)
859 wp->maxcol++;
860 if (row == wp->maxrow && row < wp->bnrows - 3)
861 wp->maxrow++;
862 else if (row == wp->maxrow + 1 && row < wp->bnrows - 4)
863 wp->maxrow += 2;
864
865 if (!*(wp->newcells + col + row * wp->bncols))
866 *(wp->newcells + col + row * wp->bncols) = WIRE;
867 } else {
868 if (wp->neighbors == 3)
869 break; /* There is no reverse step */
870 dir = ((dir * wp->neighbors / ANGLES + wp->neighbors / 2) %
871 wp->neighbors) * ANGLES / wp->neighbors;
872 }
873 }
874 *(wp->newcells + col + row * wp->bncols) = HEAD;
875 }
876
877 static void
do_gen(circuitstruct * wp)878 do_gen(circuitstruct * wp)
879 {
880 int i, j, k;
881 unsigned char *z;
882 int count;
883
884 #define LOC(X, Y) (*(wp->oldcells + (X) + ((Y) * wp->bncols)))
885 #define ADD(X, Y) if (LOC((X), (Y)) == HEAD) count++
886
887 for (j = wp->minrow; j <= wp->maxrow; j++) {
888 for (i = wp->mincol; i <= wp->maxcol; i++) {
889 z = wp->newcells + i + j * wp->bncols;
890 switch (LOC(i, j)) {
891 case SPACE:
892 *z = SPACE;
893 break;
894 case TAIL:
895 *z = WIRE;
896 break;
897 case HEAD:
898 *z = TAIL;
899 break;
900 case WIRE:
901 count = 0;
902 for (k = 0; k < wp->neighbors; k++) {
903 int newi = i, newj = j;
904
905 positionOfNeighbor(wp, k * ANGLES / wp->neighbors, &newi, &newj);
906 ADD(newi, newj);
907 }
908 if (count == 1 || count == 2)
909 *z = HEAD;
910 else
911 *z = WIRE;
912 break;
913 default:
914 {
915 (void) fprintf(stderr,
916 "bad internal character %d at %d,%d\n",
917 (int) LOC(i, j), i, j);
918 }
919 }
920 }
921 }
922 }
923
924 static void
free_list(circuitstruct * wp)925 free_list(circuitstruct * wp)
926 {
927 int state;
928
929 for (state = 0; state < COLORS - 1; state++)
930 free_state(wp, state);
931 }
932
933 static void
free_wire_screen(Display * display,circuitstruct * wp)934 free_wire_screen(Display *display, circuitstruct *wp)
935 {
936 int shade;
937
938 if (wp == NULL) {
939 return;
940 }
941 for (shade = 0; shade < wp->init_bits; shade++)
942 XFreePixmap(display, wp->pixmaps[shade]);
943 wp->init_bits = 0;
944 if (wp->stippledGC != None) {
945 XFreeGC(display, wp->stippledGC);
946 wp->stippledGC = None;
947 }
948 if (wp->oldcells != NULL) {
949 free(wp->oldcells);
950 wp->oldcells = (unsigned char *) NULL;
951 }
952 if (wp->newcells != NULL) {
953 free(wp->newcells);
954 wp->newcells = (unsigned char *) NULL;
955 }
956 free_list(wp);
957 wp = NULL;
958 }
959
960 ENTRYPOINT void
init_wire(ModeInfo * mi)961 init_wire(ModeInfo * mi)
962 {
963 Display *display = MI_DISPLAY(mi);
964 Window window = MI_WINDOW(mi);
965 int i, size = MI_SIZE(mi), n;
966 circuitstruct *wp;
967 XGCValues gcv;
968
969 MI_INIT(mi, circuits);
970 wp = &circuits[MI_SCREEN(mi)];
971
972 wp->redrawing = 0;
973
974 if ((MI_NPIXELS(mi) <= 2) && (wp->init_bits == 0)) {
975 if (wp->stippledGC == None) {
976 gcv.fill_style = FillOpaqueStippled;
977 if ((wp->stippledGC = XCreateGC(display, window, GCFillStyle,
978 &gcv)) == None) {
979 free_wire_screen(display, wp);
980 return;
981 }
982 }
983 WIREBITS(stipples[NUMSTIPPLES - 1], STIPPLESIZE, STIPPLESIZE);
984 WIREBITS(stipples[NUMSTIPPLES - 3], STIPPLESIZE, STIPPLESIZE);
985 WIREBITS(stipples[2], STIPPLESIZE, STIPPLESIZE);
986 }
987 if (MI_NPIXELS(mi) > 2) {
988 wp->colors[0] = (NRAND(MI_NPIXELS(mi)));
989 wp->colors[1] = (wp->colors[0] + MI_NPIXELS(mi) / 6 +
990 NRAND(MI_NPIXELS(mi) / 4 + 1)) % MI_NPIXELS(mi);
991 wp->colors[2] = (wp->colors[1] + MI_NPIXELS(mi) / 6 +
992 NRAND(MI_NPIXELS(mi) / 4 + 1)) % MI_NPIXELS(mi);
993 }
994 free_list(wp);
995 wp->generation = 0;
996 if (MI_IS_FULLRANDOM(mi)) {
997 wp->vertical = (Bool) (LRAND() & 1);
998 } else {
999 wp->vertical = vertical;
1000 }
1001 wp->width = MI_WIDTH(mi);
1002 wp->height = MI_HEIGHT(mi);
1003
1004 for (i = 0; i < NEIGHBORKINDS; i++) {
1005 if (neighbors == plots[i]) {
1006 wp->neighbors = plots[i];
1007 break;
1008 }
1009 if (i == NEIGHBORKINDS - 1) {
1010 i = NRAND(NEIGHBORKINDS - 3) + 1; /* Skip triangular ones */
1011 wp->neighbors = plots[i];
1012 break;
1013 }
1014 }
1015
1016 wp->prob_array[wp->neighbors - 1] = 100;
1017 if (wp->neighbors == 3) {
1018 wp->prob_array[1] = 67;
1019 wp->prob_array[0] = 33;
1020 } else {
1021 int incr = 24 / wp->neighbors;
1022
1023 for (i = wp->neighbors - 2; i >= 0; i--) {
1024 wp->prob_array[i] = wp->prob_array[i + 1] - incr -
1025 incr * ((i + 1) != wp->neighbors / 2);
1026 }
1027 }
1028
1029 if (wp->neighbors == 6) {
1030 int nccols, ncrows;
1031
1032 wp->polygon = 6;
1033 if (!wp->vertical) {
1034 wp->height = MI_WIDTH(mi);
1035 wp->width = MI_HEIGHT(mi);
1036 }
1037 if (wp->width < 8)
1038 wp->width = 8;
1039 if (wp->height < 8)
1040 wp->height = 8;
1041 if (size < -MINSIZE)
1042 wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1043 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1044 else if (size < MINSIZE) {
1045 if (!size)
1046 wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
1047 else
1048 wp->ys = MINSIZE;
1049 } else
1050 wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1051 MINGRIDSIZE));
1052 wp->xs = wp->ys;
1053 nccols = MAX(wp->width / wp->xs - 2, 16);
1054 ncrows = MAX(wp->height / wp->ys - 1, 16);
1055 wp->ncols = nccols / 2;
1056 wp->nrows = ncrows / 2;
1057 wp->nrows -= !(wp->nrows & 1); /* Must be odd */
1058 wp->xb = (wp->width - wp->xs * nccols) / 2 + wp->xs;
1059 wp->yb = (wp->height - wp->ys * ncrows) / 2 + wp->ys;
1060 for (i = 0; i < 6; i++) {
1061 if (wp->vertical) {
1062 wp->shape.hexagon[i].x =
1063 (wp->xs - 1) * hexagonUnit[i].x;
1064 wp->shape.hexagon[i].y =
1065 ((wp->ys - 1) * hexagonUnit[i].y /
1066 2) * 4 / 3;
1067 } else {
1068 wp->shape.hexagon[i].y =
1069 (wp->xs - 1) * hexagonUnit[i].x;
1070 wp->shape.hexagon[i].x =
1071 ((wp->ys - 1) * hexagonUnit[i].y /
1072 2) * 4 / 3;
1073 }
1074 }
1075 } else if (wp->neighbors == 4 || wp->neighbors == 8) {
1076 wp->polygon = 4;
1077 if (size < -MINSIZE)
1078 wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1079 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1080 else if (size < MINSIZE) {
1081 if (!size)
1082 wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
1083 else
1084 wp->ys = MINSIZE;
1085 } else
1086 wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1087 MINGRIDSIZE));
1088 wp->xs = wp->ys;
1089 wp->ncols = MAX(wp->width / wp->xs, 8);
1090 wp->nrows = MAX(wp->height / wp->ys, 8);
1091 wp->xb = (wp->width - wp->xs * wp->ncols) / 2;
1092 wp->yb = (wp->height - wp->ys * wp->nrows) / 2;
1093 } else { /* TRI */
1094 int orient;
1095
1096 wp->polygon = 3;
1097 if (!wp->vertical) {
1098 wp->height = MI_WIDTH(mi);
1099 wp->width = MI_HEIGHT(mi);
1100 }
1101 if (wp->width < 4)
1102 wp->width = 4;
1103 if (wp->height < 2)
1104 wp->height = 2;
1105 if (size < -MINSIZE)
1106 wp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1107 MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
1108 else if (size < MINSIZE) {
1109 if (!size)
1110 wp->ys = MAX(MINSIZE, MIN(wp->width, wp->height) / MINGRIDSIZE);
1111 else
1112 wp->ys = MINSIZE;
1113 } else
1114 wp->ys = MIN(size, MAX(MINSIZE, MIN(wp->width, wp->height) /
1115 MINGRIDSIZE));
1116 wp->xs = (int) (1.52 * wp->ys);
1117 wp->ncols = (MAX(wp->width / wp->xs - 1, 8) / 2) * 2;
1118 wp->nrows = (MAX(wp->height / wp->ys - 1, 8) / 2) * 2 - 1;
1119 wp->xb = (wp->width - wp->xs * wp->ncols) / 2 + wp->xs / 2;
1120 wp->yb = (wp->height - wp->ys * wp->nrows) / 2 + wp->ys;
1121 for (orient = 0; orient < 2; orient++) {
1122 for (i = 0; i < 3; i++) {
1123 if (wp->vertical) {
1124 wp->shape.triangle[orient][i].x =
1125 (wp->xs - 2) * triangleUnit[orient][i].x;
1126 wp->shape.triangle[orient][i].y =
1127 (wp->ys - 2) * triangleUnit[orient][i].y;
1128 } else {
1129 wp->shape.triangle[orient][i].y =
1130 (wp->xs - 2) * triangleUnit[orient][i].x;
1131 wp->shape.triangle[orient][i].x =
1132 (wp->ys - 2) * triangleUnit[orient][i].y;
1133 }
1134 }
1135 }
1136 }
1137
1138 /*
1139 * I am being a bit naughty here wasting a little bit of memory
1140 * but it will give me a real headache to figure out the logic
1141 * and to refigure the mappings to save a few bytes
1142 * ncols should only need a border of 2 and nrows should only need
1143 * a border of 4 when in the neighbors = 9 or 12
1144 */
1145 wp->bncols = wp->ncols + 4;
1146 wp->bnrows = wp->nrows + 4;
1147
1148 if (MI_IS_VERBOSE(mi))
1149 (void) fprintf(stdout,
1150 "neighbors %d, ncols %d, nrows %d\n",
1151 wp->neighbors, wp->ncols, wp->nrows);
1152 MI_CLEARWINDOW(mi);
1153
1154 if (wp->oldcells != NULL) {
1155 free(wp->oldcells);
1156 wp->oldcells = (unsigned char *) NULL;
1157 }
1158 if ((wp->oldcells = (unsigned char *) calloc(wp->bncols * wp->bnrows,
1159 sizeof (unsigned char))) == NULL) {
1160 free_wire_screen(display, wp);
1161 return;
1162 }
1163
1164 if (wp->newcells != NULL) {
1165 free(wp->newcells);
1166 wp->newcells = (unsigned char *) NULL;
1167 }
1168 if ((wp->newcells = (unsigned char *) calloc(wp->bncols * wp->bnrows,
1169 sizeof (unsigned char))) == NULL) {
1170 free_wire_screen(display, wp);
1171 return;
1172 }
1173
1174 n = MI_COUNT(mi);
1175 i = (1 + (wp->neighbors == 6)) * wp->ncols * wp->nrows / 4;
1176 if (n < -MINWIRES && i > MINWIRES) {
1177 n = NRAND(MIN(-n, i) - MINWIRES + 1) + MINWIRES;
1178 } else if (n < MINWIRES) {
1179 n = MINWIRES;
1180 } else if (n > i) {
1181 n = MAX(MINWIRES, i);
1182 }
1183 create_path(wp, n);
1184 }
1185
1186 ENTRYPOINT void
draw_wire(ModeInfo * mi)1187 draw_wire(ModeInfo * mi)
1188 {
1189 int offset, i, j, found = 0;
1190 unsigned char *z, *znew;
1191 circuitstruct *wp;
1192
1193 if (circuits == NULL)
1194 return;
1195 wp = &circuits[MI_SCREEN(mi)];
1196 if (wp->newcells == NULL)
1197 return;
1198
1199 MI_IS_DRAWN(mi) = True;
1200
1201 /* wires do not grow so min max stuff does not change */
1202 for (j = wp->minrow; j <= wp->maxrow; j++) {
1203 for (i = wp->mincol; i <= wp->maxcol; i++) {
1204 offset = j * wp->bncols + i;
1205 z = wp->oldcells + offset;
1206 znew = wp->newcells + offset;
1207 if (*z != *znew) { /* Counting on once a space always a space */
1208 found = 1;
1209 *z = *znew;
1210 if (!addtolist(mi, i - 2, j - 2, *znew - 1)) {
1211 free_wire_screen(MI_DISPLAY(mi), wp);
1212 return;
1213 }
1214 }
1215 }
1216 }
1217 for (i = 0; i < COLORS - 1; i++)
1218 if (!draw_state(mi, i)) {
1219 free_wire_screen(MI_DISPLAY(mi), wp);
1220 return;
1221 }
1222 if (++wp->generation > MI_CYCLES(mi) || !found) {
1223 init_wire(mi);
1224 return;
1225 } else
1226 do_gen(wp);
1227
1228 if (wp->redrawing) {
1229 for (i = 0; i < REDRAWSTEP; i++) {
1230 if ((*(wp->oldcells + wp->redrawpos))) {
1231 drawCell(mi, wp->redrawpos % wp->bncols - 2,
1232 wp->redrawpos / wp->bncols - 2, *(wp->oldcells + wp->redrawpos) - 1);
1233 }
1234 if (++(wp->redrawpos) >= wp->bncols * (wp->bnrows - 2)) {
1235 wp->redrawing = 0;
1236 break;
1237 }
1238 }
1239 }
1240 }
1241
1242 ENTRYPOINT void
release_wire(ModeInfo * mi)1243 release_wire(ModeInfo * mi)
1244 {
1245 if (circuits != NULL) {
1246 int screen;
1247
1248 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1249 free_wire_screen(MI_DISPLAY(mi), &circuits[screen]);
1250 free(circuits);
1251 circuits = (circuitstruct *) NULL;
1252 }
1253 }
1254
1255 #ifndef STANDALONE
1256 void
refresh_wire(ModeInfo * mi)1257 refresh_wire(ModeInfo * mi)
1258 {
1259 circuitstruct *wp;
1260
1261 if (circuits == NULL)
1262 return;
1263 wp = &circuits[MI_SCREEN(mi)];
1264
1265 MI_CLEARWINDOW(mi);
1266 wp->redrawing = 1;
1267 wp->redrawpos = 2 * wp->ncols + 2;
1268 }
1269 #endif
1270
1271 XSCREENSAVER_MODULE ("Wire", wire)
1272
1273 #endif /* MODE_wire */
1274