19
18
http://www.nine.org/notw/notw.html
23
#if !defined( lint ) && !defined( SABER )
24
static const char sccsid[] = "@(#)penrose.c 4.00 97/01/01 xlockmore";
22
static const char sccsid[] = "@(#)penrose.c 5.00 2000/11/01 xlockmore";
27
/* Copyright (c) 1996 by Timo Korvola <tkorvola@dopey.hut.fi>
26
* Copyright (c) 1996 by Timo Korvola <tkorvola@dopey.hut.fi>
29
28
* Permission to use, copy, modify, and distribute this software and its
30
29
* documentation for any purpose and without fee is hereby granted,
63
64
* untiled area. Whenever this is in danger of happening, we just
64
65
* do not add the tile, hoping for a better random choice the next
65
66
* time. Second, when choosing a vertex randomly, we will take
66
* one that lies withing the viewport if available. If this seems to
67
* one that lies within the viewport if available. If this seems to
67
68
* cause enclosures in the forced rule case, we will allow invisible
68
69
* vertices to be chosen.
73
74
* horizontally or vertically or forced rule choice has failed 100
74
75
* times due to areas about to become enclosed.
78
* Science News March 23 1985 Vol 127, No. 12
79
* Science News July 16 1988 Vol 134, No. 3
80
* The Economist Sept 17 1988 pg. 100
79
# define PROGCLASS "Penrose"
80
# define HACK_INIT init_penrose
81
# define HACK_DRAW draw_penrose
82
# define penrose_opts xlockmore_opts
83
# define DEFAULTS "*delay: 10000 \n" \
86
# include "xlockmore.h" /* from the xscreensaver distribution */
87
#else /* !STANDALONE */
88
# include "xlock.h" /* from the xlockmore distribution */
86
#define PROGCLASS "Penrose"
87
#define HACK_INIT init_penrose
88
#define HACK_DRAW draw_penrose
89
#define penrose_opts xlockmore_opts
90
#define DEFAULTS "*delay: 10000 \n" \
93
#include "xlockmore.h" /* from the xscreensaver distribution */
94
#else /* !STANDALONE */
95
#include "xlock.h" /* from the xlockmore distribution */
89
96
#endif /* !STANDALONE */
100
#define DEF_AMMANN "False"
104
static XrmOptionDescRec opts[] =
106
{"-ammann", ".penrose.ammann", XrmoptionNoArg, "on"},
107
{"+ammann", ".penrose.ammann", XrmoptionNoArg, "off"}
109
static argtype vars[] =
111
{&ammann, "ammann", "Ammann", DEF_AMMANN, t_Bool}
113
static OptionStruct desc[] =
115
{"-/+ammann", "turn on/off Ammann lines"}
118
ModeSpecOpt penrose_opts =
119
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
122
ModStruct penrose_description =
123
{"penrose", "init_penrose", "draw_penrose", "release_penrose",
124
"init_penrose", "init_penrose", (char *) NULL, &penrose_opts,
125
10000, 1, 1, -40, 64, 1.0, "",
126
"Shows Penrose's quasiperiodic tilings", 0, NULL};
93
131
* Annoyingly the ANSI C library people have reserved all identifiers
102
140
* vertex rules only allow at most seven tiles to meet at a vertex.
143
#define CELEBRATE 31415 /* This causes a pause, an error occurred. */
144
#define COMPLETION 3141 /* This causes a pause, tiles filled up screen. */
105
146
#define MAX_TILES_PER_VERTEX 7
106
147
#define N_VERTEX_RULES 8
107
#define ALLOC_NODE( type) ((type *)malloc( sizeof( type)))
108
#define DEF_AMMANN "False"
112
/* How long in seconds should we wait before starting a new tiling? */
113
static long redo_delay = 3;
114
static long redo_delay_usec;
116
static XrmOptionDescRec opts[] =
118
{"-ammann", ".penrose.ammann", XrmoptionNoArg, (caddr_t) "on"},
119
{"+ammann", ".penrose.ammann", XrmoptionNoArg, (caddr_t) "off"},
120
{"-redoDelay", ".penrose.redoDelay", XrmoptionSepArg, NULL}
122
static argtype vars[] =
124
{(caddr_t *) & ammann, "ammann", "Ammann", DEF_AMMANN, t_Bool},
125
{(caddr_t *) & redo_delay, "redoDelay", "RedoDelay", "3", t_Int}
127
static OptionStruct desc[] =
129
{"-/+ammann", "turn on/off Ammann lines"},
130
{"-redoDelay", "delay between new tilings"}
133
ModeSpecOpt penrose_opts = { 3, opts, 2, vars, desc };
148
#define ALLOC_NODE(type) (type *)malloc(sizeof (type))
137
151
* These are used to specify directions. They can also be used in bit
182
196
* Here we use a doubly chained ring-like structure as vertices often need
183
* to be removed or inserted (they are kept in geometrical order
197
* to be removed or inserted (they are kept in geometrical order
184
198
* circling the tiled area counterclockwise). The ring is refered to by
185
199
* a pointer to one more or less random node. When deleting nodes one
186
200
* must make sure that this pointer continues to refer to a valid
323
338
return (2 * i + 5) % 10;
326
if (MI_WIN_IS_VERBOSE(mi)) {
341
if (MI_IS_VERBOSE(mi)) {
327
342
(void) fprintf(stderr,
328
"Weirdness in vertex_dir (this has been reported)\n");
343
"Weirdness in vertex_dir (this has been reported)\n");
329
344
for (i = 0; i < 5; i++)
330
345
(void) fprintf(stderr, "v2->fived[%d]=%d, vertex->fived[%d]=%d\n",
331
i, v2->fived[i], i, vertex->fived[i]);
346
i, v2->fived[i], i, vertex->fived[i]);
333
MI_PAUSE(mi) = redo_delay_usec;
348
tp->busyLoop = CELEBRATE;
379
395
offset.x += r * fived_table[i].x;
380
396
offset.y -= r * fived_table[i].y;
382
pt.x += (int) (offset.x + .5);
383
pt.y += (int) (offset.y + .5);
398
(*pt).x += (int) (offset.x + .5);
399
(*pt).y += (int) (offset.y + .5);
388
403
/* Mop up dynamic data for one screen. */
390
release_screen(tiling_c * tp)
405
free_penrose(tiling_c * tp)
392
407
register fringe_node_c *fp1, *fp2;
393
408
register forced_node_c *lp1, *lp2;
395
if (tp->fringe.nodes == 0)
410
if (tp->fringe.nodes == NULL)
397
412
fp1 = tp->fringe.nodes;
401
(void) free((char *) fp2);
416
(void) free((void *) fp2);
402
417
} while (fp1 != tp->fringe.nodes);
403
tp->fringe.nodes = 0;
418
tp->fringe.nodes = (fringe_node_c *) NULL;
404
419
for (lp1 = tp->forced.first; lp1 != 0;) {
407
(void) free((char *) lp2);
422
(void) free((void *) lp2);
409
424
tp->forced.first = 0;
418
433
fringe_node_c *fp;
421
redo_delay_usec = redo_delay * 1000000;
423
436
if (tilings == NULL) {
424
437
if ((tilings = (tiling_c *) calloc(MI_NUM_SCREENS(mi),
425
438
sizeof (tiling_c))) == NULL)
428
441
tp = &tilings[MI_SCREEN(mi)];
443
#if 0 /* if you do this, then the -ammann and -no-ammann options don't work.
445
if (MI_IS_FULLRANDOM(mi))
446
tp->ammann = (Bool) (LRAND() & 1);
429
451
tp->done = False;
430
453
tp->failures = 0;
431
tp->width = MI_WIN_WIDTH(mi);
432
tp->height = MI_WIN_HEIGHT(mi);
454
tp->width = MI_WIDTH(mi);
455
tp->height = MI_HEIGHT(mi);
433
456
if (MI_NPIXELS(mi) > 2) {
434
457
tp->thick_color = NRAND(MI_NPIXELS(mi));
435
458
/* Insure good contrast */
451
474
tp->origin.x = (tp->width / 2 + NRAND(tp->width)) / 2;
452
475
tp->origin.y = (tp->height / 2 + NRAND(tp->height)) / 2;
453
476
tp->fringe.n_nodes = 2;
454
if (tp->fringe.nodes != 0)
456
if (tp->fringe.nodes != 0 || tp->forced.first != 0) {
457
if (MI_WIN_IS_VERBOSE(mi)) {
477
if (tp->fringe.nodes != NULL)
479
if (tp->fringe.nodes != NULL || tp->forced.first != 0) {
480
if (MI_IS_VERBOSE(mi)) {
458
481
(void) fprintf(stderr, "Weirdness in init_penrose()\n");
459
(void) fprintf(stderr, "tp->fringe.nodes = 0 && tp->forced.first = 0\n");
482
(void) fprintf(stderr, "tp->fringe.nodes = NULL && tp->forced.first = 0\n");
461
release_screen(tp); /* Try again */
484
free_penrose(tp); /* Try again */
464
487
tp->forced.n_nodes = tp->forced.n_visible = 0;
465
fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c);
488
if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) {
467
if (MI_WIN_IS_VERBOSE(mi)) {
493
if (MI_IS_VERBOSE(mi)) {
468
494
(void) fprintf(stderr, "Weirdness in init_penrose()\n");
469
495
(void) fprintf(stderr, "fp = 0\n");
471
fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c);
497
if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) {
474
503
/* First vertex. */
475
504
fp->rule_mask = (1 << N_VERTEX_RULES) - 1;
476
505
fp->list_ptr = 0;
477
fp->prev = fp->next = ALLOC_NODE(fringe_node_c);
506
if ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) {
478
510
if (fp->next == 0) {
479
if (MI_WIN_IS_VERBOSE(mi)) {
511
if (MI_IS_VERBOSE(mi)) {
480
512
(void) fprintf(stderr, "Weirdness in init_penrose()\n");
481
513
(void) fprintf(stderr, "fp->next = 0\n");
483
fp->prev = fp->next = ALLOC_NODE(fringe_node_c);
515
if ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) {
647
682
XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color));
649
XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
684
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
650
685
XFillPolygon(display, window, gc, pts, 4, Convex, CoordModeOrigin);
651
XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
686
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
652
687
XDrawLines(display, window, gc, pts, 5, CoordModeOrigin);
655
690
/* Draw some Ammann lines for debugging purposes. This will probably
656
691
fail miserably on a b&w display. */
666
701
if (MI_NPIXELS(mi) > 2)
667
702
XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color));
669
XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
704
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
670
705
XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter);
672
707
XDrawLine(display, window, gc,
680
715
if (MI_NPIXELS(mi) > 2)
681
716
XSetForeground(display, gc, MI_PIXEL(mi, tp->thick_color));
683
XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
718
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
684
719
XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter);
686
721
XDrawLine(display, window, gc,
719
754
if (vertex->rule_mask == 0) {
721
if (MI_WIN_IS_VERBOSE(mi)) {
722
(void) fprintf(stderr, "Dislocation occured!\n");
756
if (MI_IS_VERBOSE(mi)) {
757
(void) fprintf(stderr, "Dislocation occurred!\n");
724
MI_PAUSE(mi) = redo_delay_usec; /* Should be able to recover */
759
tp->busyLoop = CELEBRATE; /* Should be able to recover */
726
761
if (1 == find_completions(vertex, hits, n_hits, S_LEFT, 0 /*, False */ ))
727
762
forced_sides |= S_LEFT;
773
809
if (tp->fringe.nodes == vertex) {
775
if (MI_WIN_IS_VERBOSE(mi)) {
811
if (MI_IS_VERBOSE(mi)) {
776
812
(void) fprintf(stderr, "Weirdness in delete_penrose()\n");
777
813
(void) fprintf(stderr, "tp->fringe.nodes == vertex\n");
779
MI_PAUSE(mi) = redo_delay_usec;
815
tp->busyLoop = CELEBRATE;
781
817
if (vertex->list_ptr != 0) {
782
818
forced_node_c *node = *vertex->list_ptr;
784
820
*vertex->list_ptr = node->next;
785
821
if (node->next != 0)
786
822
node->next->vertex->list_ptr = vertex->list_ptr;
823
(void) free((void *) node);
788
824
tp->forced.n_nodes--;
789
825
if (!vertex->off_screen)
790
826
tp->forced.n_visible--;
792
828
if (!vertex->off_screen)
793
829
tp->fringe.n_nodes--;
830
(void) free((void *) vertex);
798
/* Check whether the addition of a tile of type vtype would completely fill *
799
the space available at vertex. */
835
* Check whether the addition of a tile of type vtype would completely fill
836
* the space available at vertex.
801
839
fills_vertex(ModeInfo * mi, vertex_type_c vtype, fringe_node_c * vertex)
908
946
static fringe_node_c *
909
947
alloc_vertex(ModeInfo * mi, angle_c dir, fringe_node_c * from, tiling_c * tp)
911
fringe_node_c *v = ALLOC_NODE(fringe_node_c);
951
if ((v = ALLOC_NODE(fringe_node_c)) == NULL) {
915
if (MI_WIN_IS_VERBOSE(mi)) {
916
(void) fprintf(stderr, "Weirdness in alloc_vertex()\n");
917
(void) fprintf(stderr, "v = 0\n");
953
if (MI_IS_VERBOSE(mi)) {
954
(void) fprintf(stderr, "No memory in alloc_vertex()\n");
919
MI_PAUSE(mi) = redo_delay_usec;
956
tp->busyLoop = CELEBRATE;
922
960
add_unit_vec(dir, v->fived);
923
v->loc = fived_to_loc(v->fived, tp);
961
fived_to_loc(v->fived, tp, &(v->loc));
924
962
if (v->loc.x < 0 || v->loc.y < 0
925
963
|| v->loc.x >= tp->width || v->loc.y >= tp->height) {
926
964
v->off_screen = True;
954
992
tiling_c *tp = &tilings[MI_SCREEN(mi)];
995
*left = (fringe_node_c *) NULL,
996
*right = (fringe_node_c *) NULL,
997
*far = (fringe_node_c *) NULL,
961
999
unsigned fc = fringe_changes(mi, vertex, side, vtype, &right, &far, &left);
971
1009
/* This should never occur. */
972
1010
if (fc & FC_BAG) {
973
1011
tp->done = True;
974
if (MI_WIN_IS_VERBOSE(mi)) {
1012
if (MI_IS_VERBOSE(mi)) {
975
1013
(void) fprintf(stderr, "Weirdness in add_tile()\n");
976
1014
(void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG);
979
1017
if (side == S_LEFT) {
981
right = alloc_vertex(mi,
982
vertex_dir(mi, vertex, S_LEFT) - vtype_angle(vtype), vertex, tp);
984
far = alloc_vertex(mi,
985
vertex_dir(mi, left, S_RIGHT) + vtype_angle(ltype), left, tp);
1019
if ((right = alloc_vertex(mi, vertex_dir(mi, vertex, S_LEFT) -
1020
vtype_angle(vtype), vertex, tp)) == NULL)
1023
if ((far = alloc_vertex(mi, vertex_dir(mi, left, S_RIGHT) +
1024
vtype_angle(ltype), left, tp)) == NULL)
988
left = alloc_vertex(mi,
989
vertex_dir(mi, vertex, S_RIGHT) + vtype_angle(vtype), vertex, tp);
991
far = alloc_vertex(mi,
992
vertex_dir(mi, right, S_LEFT) - vtype_angle(rtype), right, tp);
1028
if ((left = alloc_vertex(mi, vertex_dir(mi, vertex, S_RIGHT) +
1029
vtype_angle(vtype), vertex, tp)) == NULL)
1032
if ((far = alloc_vertex(mi, vertex_dir(mi, right, S_LEFT) -
1033
vtype_angle(rtype), right, tp)) == NULL)
995
1037
/* Having allocated the new vertices, but before joining them with
1092
1134
n = find_completions(node->vertex, hits, n, side, &vtype /*, True */ );
1094
1136
tp->done = True;
1095
if (MI_WIN_IS_VERBOSE(mi)) {
1137
if (MI_IS_VERBOSE(mi)) {
1096
1138
(void) fprintf(stderr, "Weirdness in add_forced_tile()\n");
1097
1139
(void) fprintf(stderr, "n = %d\n", n);
1145
1187
tp->thin_color = (NRAND(2 * MI_NPIXELS(mi) / 3) + tp->thick_color +
1146
1188
MI_NPIXELS(mi) / 6) % MI_NPIXELS(mi);
1148
tp->thick_color = tp->thin_color = MI_WIN_WHITE_PIXEL(mi);
1190
tp->thick_color = tp->thin_color = MI_WHITE_PIXEL(mi);
1149
1191
n_hits = match_rules(vertex, hits, False);
1150
1192
side = NRAND(2) ? S_LEFT : S_RIGHT;
1151
1193
n = find_completions(vertex, hits, n_hits, side, vtypes /*, False */ );
1152
1194
/* One answer would mean a forced tile. */
1154
1196
tp->done = True;
1155
if (MI_WIN_IS_VERBOSE(mi)) {
1197
if (MI_IS_VERBOSE(mi)) {
1156
1198
(void) fprintf(stderr, "Weirdness in add_random_tile()\n");
1157
1199
(void) fprintf(stderr, "n = %d\n", n);
1163
1205
fc = fringe_changes(mi, vertex, side, vtypes[i], &right, &far, &left);
1164
1206
if (fc & FC_BAG) {
1165
1207
tp->done = True;
1166
if (MI_WIN_IS_VERBOSE(mi)) {
1208
if (MI_IS_VERBOSE(mi)) {
1167
1209
(void) fprintf(stderr, "Weirdness in add_random_tile()\n");
1168
1210
(void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG);
1204
1246
while (no_good & (1 << j))
1207
i = add_tile(mi, vertex, side, vtypes[j - 1]);
1249
if (!add_tile(mi, vertex, side, vtypes[j - 1])) {
1209
1250
tp->done = True;
1210
if (MI_WIN_IS_VERBOSE(mi)) {
1251
if (MI_IS_VERBOSE(mi)) {
1211
1252
(void) fprintf(stderr, "Weirdness in add_random_tile()\n");
1212
(void) fprintf(stderr, "i = %d\n", i);
1219
1260
draw_penrose(ModeInfo * mi)
1221
tiling_c *tp = &tilings[MI_SCREEN(mi)];
1223
forced_node_c *p = tp->forced.first;
1266
if (tilings == NULL)
1268
tp = &tilings[MI_SCREEN(mi)];
1269
if (tp->fringe.nodes == NULL)
1272
MI_IS_DRAWN(mi) = True;
1273
p = tp->forced.first;
1274
if (tp->busyLoop > 0) {
1225
1278
if (tp->done || tp->failures >= 100) {
1226
1279
init_penrose(mi);
1229
1282
/* Check for the initial "2-gon". */
1230
1283
if (tp->fringe.nodes->prev == tp->fringe.nodes->next) {
1231
vertex_type_c vtype = VT_TOTAL_MASK & LRAND();
1233
XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
1234
(void) add_tile(mi, tp->fringe.nodes, S_LEFT, vtype);
1284
vertex_type_c vtype = (unsigned char) (VT_TOTAL_MASK & LRAND());
1288
if (!add_tile(mi, tp->fringe.nodes, S_LEFT, vtype))
1237
1292
/* No visible nodes left. */
1238
1293
if (tp->fringe.n_nodes == 0) {
1239
1294
tp->done = True;
1240
MI_PAUSE(mi) = redo_delay_usec; /* Just finished drawing */
1295
tp->busyLoop = COMPLETION; /* Just finished drawing */
1243
1298
if (tp->forced.n_visible > 0 && tp->failures < 10) {
1281
1336
if (tilings != NULL) {
1284
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1285
tiling_c *tp = &tilings[screen];
1339
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1340
free_penrose(&tilings[screen]);
1289
1341
(void) free((void *) tilings);
1342
tilings = (tiling_c *) NULL;
1346
#endif /* MODE_penrose */