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