1 | /* -*- Mode: C; tab-width: 4 -*- */ |
---|
2 | /* penrose --- quasiperiodic tilings */ |
---|
3 | |
---|
4 | /* As reported in News of the Weird: |
---|
5 | |
---|
6 | In April, Sir Roger Penrose, a British math professor who has worked |
---|
7 | with Stephen Hawking on such topics as relativity, black holes, and |
---|
8 | whether time has a beginning, filed a copyright-infringement lawsuit |
---|
9 | against the Kimberly-Clark Corporation, which Penrose said copied a |
---|
10 | pattern he created (a pattern demonstrating that "a nonrepeating |
---|
11 | pattern could exist in nature") for its Kleenex quilted toilet paper. |
---|
12 | Penrose said he doesn't like litigation but, "When it comes to the |
---|
13 | population of Great Britain being invited by a multinational to wipe |
---|
14 | their bottoms on what appears to be the work of a Knight of the |
---|
15 | Realm, then a last stand must be taken." |
---|
16 | |
---|
17 | NOTW #491, 4-jul-1997, by Chuck Shepherd. |
---|
18 | http://www.nine.org/notw/notw.html |
---|
19 | */ |
---|
20 | |
---|
21 | #if 0 |
---|
22 | static const char sccsid[] = "@(#)penrose.c 5.00 2000/11/01 xlockmore"; |
---|
23 | #endif |
---|
24 | |
---|
25 | /*- |
---|
26 | * Copyright (c) 1996 by Timo Korvola <tkorvola@dopey.hut.fi> |
---|
27 | * |
---|
28 | * Permission to use, copy, modify, and distribute this software and its |
---|
29 | * documentation for any purpose and without fee is hereby granted, |
---|
30 | * provided that the above copyright notice appear in all copies and that |
---|
31 | * both that copyright notice and this permission notice appear in |
---|
32 | * supporting documentation. |
---|
33 | * |
---|
34 | * This file is provided AS IS with no warranties of any kind. The author |
---|
35 | * shall have no liability with respect to the infringement of copyrights, |
---|
36 | * trade secrets or any patents by this file or any part thereof. In no |
---|
37 | * event will the author be liable for any lost revenue or profits or |
---|
38 | * other special, indirect and consequential damages. |
---|
39 | * |
---|
40 | * Revision History: |
---|
41 | * 01-Nov-2000: Allocation checks |
---|
42 | * 10-May-1997: Jamie Zawinski <jwz@jwz.org> compatible with xscreensaver |
---|
43 | * 09-Sep-1996: Written. |
---|
44 | */ |
---|
45 | |
---|
46 | /*- |
---|
47 | Be careful, this probably still has a few bugs (many of which may only |
---|
48 | appear with a very low probability). These are seen with -verbose . |
---|
49 | If one of these are hit penrose will reinitialize. |
---|
50 | */ |
---|
51 | |
---|
52 | /*- |
---|
53 | * See Onoda, Steinhardt, DiVincenzo and Socolar in |
---|
54 | * Phys. Rev. Lett. 60, #25, 1988 or |
---|
55 | * Strandburg in Computers in Physics, Sep/Oct 1991. |
---|
56 | * |
---|
57 | * This implementation uses the simpler version of the growth |
---|
58 | * algorithm, i.e., if there are no forced vertices, a randomly chosen |
---|
59 | * tile is added to a randomly chosen vertex (no preference for those |
---|
60 | * 108 degree angles). |
---|
61 | * |
---|
62 | * There are two essential differences to the algorithm presented in |
---|
63 | * the literature: First, we do not allow the tiling to enclose an |
---|
64 | * untiled area. Whenever this is in danger of happening, we just |
---|
65 | * do not add the tile, hoping for a better random choice the next |
---|
66 | * time. Second, when choosing a vertex randomly, we will take |
---|
67 | * one that lies within the viewport if available. If this seems to |
---|
68 | * cause enclosures in the forced rule case, we will allow invisible |
---|
69 | * vertices to be chosen. |
---|
70 | * |
---|
71 | * Tiling is restarted whenever one of the following happens: there |
---|
72 | * are no incomplete vertices within the viewport or the tiling has |
---|
73 | * extended a window's length beyond the edge of the window |
---|
74 | * horizontally or vertically or forced rule choice has failed 100 |
---|
75 | * times due to areas about to become enclosed. |
---|
76 | * |
---|
77 | * Introductory info: |
---|
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 |
---|
81 | * |
---|
82 | */ |
---|
83 | |
---|
84 | #ifdef STANDALONE |
---|
85 | #define MODE_penrose |
---|
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" \ |
---|
91 | "*size: 40 \n" \ |
---|
92 | "*ncolors: 64 \n" |
---|
93 | #include "xlockmore.h" /* from the xscreensaver distribution */ |
---|
94 | #else /* !STANDALONE */ |
---|
95 | #include "xlock.h" /* from the xlockmore distribution */ |
---|
96 | #endif /* !STANDALONE */ |
---|
97 | |
---|
98 | #ifdef MODE_penrose |
---|
99 | |
---|
100 | #define DEF_AMMANN "False" |
---|
101 | |
---|
102 | static Bool ammann; |
---|
103 | |
---|
104 | static XrmOptionDescRec opts[] = |
---|
105 | { |
---|
106 | {(char *) "-ammann", (char *) ".penrose.ammann", XrmoptionNoArg, (caddr_t) "on"}, |
---|
107 | {(char *) "+ammann", (char *) ".penrose.ammann", XrmoptionNoArg, (caddr_t) "off"} |
---|
108 | }; |
---|
109 | static argtype vars[] = |
---|
110 | { |
---|
111 | {(caddr_t *) & ammann, (char *) "ammann", (char *) "Ammann", (char *) DEF_AMMANN, t_Bool} |
---|
112 | }; |
---|
113 | static OptionStruct desc[] = |
---|
114 | { |
---|
115 | {(char *) "-/+ammann", (char *) "turn on/off Ammann lines"} |
---|
116 | }; |
---|
117 | |
---|
118 | ModeSpecOpt penrose_opts = |
---|
119 | {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; |
---|
120 | |
---|
121 | #ifdef USE_MODULES |
---|
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}; |
---|
127 | |
---|
128 | #endif |
---|
129 | |
---|
130 | /*- |
---|
131 | * Annoyingly the ANSI C library people have reserved all identifiers |
---|
132 | * ending with _t for future use. Hence we use _c as a suffix for |
---|
133 | * typedefs (c for class, although this is not C++). |
---|
134 | */ |
---|
135 | |
---|
136 | #define MINSIZE 5 |
---|
137 | |
---|
138 | /*- |
---|
139 | * In theory one could fit 10 tiles to a single vertex. However, the |
---|
140 | * vertex rules only allow at most seven tiles to meet at a vertex. |
---|
141 | */ |
---|
142 | |
---|
143 | #define CELEBRATE 31415 /* This causes a pause, an error occurred. */ |
---|
144 | #define COMPLETION 3141 /* This causes a pause, tiles filled up screen. */ |
---|
145 | |
---|
146 | #define MAX_TILES_PER_VERTEX 7 |
---|
147 | #define N_VERTEX_RULES 8 |
---|
148 | #define ALLOC_NODE(type) (type *)malloc(sizeof (type)) |
---|
149 | |
---|
150 | /*- |
---|
151 | * These are used to specify directions. They can also be used in bit |
---|
152 | * masks to specify a combination of directions. |
---|
153 | */ |
---|
154 | #define S_LEFT 1 |
---|
155 | #define S_RIGHT 2 |
---|
156 | |
---|
157 | |
---|
158 | /*- |
---|
159 | * We do not actually maintain objects corresponding to the tiles since |
---|
160 | * we do not really need them and they would only consume memory and |
---|
161 | * cause additional bookkeeping. Instead we only have vertices, and |
---|
162 | * each vertex lists the type of each adjacent tile as well as the |
---|
163 | * position of the vertex on the tile (hereafter refered to as |
---|
164 | * "corner"). These positions are numbered in counterclockwise order |
---|
165 | * so that 0 is where two double arrows meet (see one of the |
---|
166 | * articles). The tile type and vertex number are stored in a single |
---|
167 | * integer (we use char, and even most of it remains unused). |
---|
168 | * |
---|
169 | * The primary use of tile objects would be draw traversal, but we do |
---|
170 | * not currently do redraws at all (we just start over). |
---|
171 | */ |
---|
172 | #define VT_CORNER_MASK 0x3 |
---|
173 | #define VT_TYPE_MASK 0x4 |
---|
174 | #define VT_THIN 0 |
---|
175 | #define VT_THICK 0x4 |
---|
176 | #define VT_BITS 3 |
---|
177 | #define VT_TOTAL_MASK 0x7 |
---|
178 | |
---|
179 | typedef unsigned char vertex_type_c; |
---|
180 | |
---|
181 | /*- |
---|
182 | * These allow one to compute the types of the other corners of the tile. If |
---|
183 | * you are standing at a vertex of type vt looking towards the middle of the |
---|
184 | * tile, VT_LEFT( vt) is the vertex on your left etc. |
---|
185 | */ |
---|
186 | #define VT_LEFT( vt) ((((vt) - 1) & VT_CORNER_MASK) | (((vt) & VT_TYPE_MASK))) |
---|
187 | #define VT_RIGHT( vt) ((((vt) + 1) & VT_CORNER_MASK) | (((vt) & VT_TYPE_MASK))) |
---|
188 | #define VT_FAR( vt) ((vt) ^ 2) |
---|
189 | |
---|
190 | |
---|
191 | /*- |
---|
192 | * Since we do not do redraws, we only store the vertices we need. These are |
---|
193 | * the ones with still some empty space around them for the growth algorithm |
---|
194 | * to fill. |
---|
195 | * |
---|
196 | * Here we use a doubly chained ring-like structure as vertices often need |
---|
197 | * to be removed or inserted (they are kept in geometrical order |
---|
198 | * circling the tiled area counterclockwise). The ring is refered to by |
---|
199 | * a pointer to one more or less random node. When deleting nodes one |
---|
200 | * must make sure that this pointer continues to refer to a valid |
---|
201 | * node. A vertex count is maintained to make it easier to pick |
---|
202 | * vertices randomly. |
---|
203 | */ |
---|
204 | typedef struct forced_node forced_node_c; |
---|
205 | |
---|
206 | typedef struct fringe_node { |
---|
207 | struct fringe_node *prev; |
---|
208 | struct fringe_node *next; |
---|
209 | /* These are numbered counterclockwise. The gap, if any, lies |
---|
210 | between the last and first tiles. */ |
---|
211 | vertex_type_c tiles[MAX_TILES_PER_VERTEX]; |
---|
212 | int n_tiles; |
---|
213 | /* A bit mask used to indicate vertex rules that are still applicable for |
---|
214 | completing this vertex. Initialize this to (1 << N_VERTEX_RULES) - 1, |
---|
215 | i.e., all ones, and the rule matching functions will automatically mask |
---|
216 | out rules that no longer match. */ |
---|
217 | unsigned char rule_mask; |
---|
218 | /* If the vertex is on the forced vertex list, this points to the |
---|
219 | pointer to the appropriate node in the list. To remove the |
---|
220 | vertex from the list just set *list_ptr to the next node, |
---|
221 | deallocate and decrement node count. */ |
---|
222 | struct forced_node **list_ptr; |
---|
223 | /* Screen coordinates. */ |
---|
224 | XPoint loc; |
---|
225 | /* We also keep track of 5D coordinates to avoid rounding errors. |
---|
226 | These are in units of edge length. */ |
---|
227 | int fived[5]; |
---|
228 | /* This is used to quickly check if a vertex is visible. */ |
---|
229 | unsigned char off_screen; |
---|
230 | } fringe_node_c; |
---|
231 | |
---|
232 | typedef struct { |
---|
233 | fringe_node_c *nodes; |
---|
234 | /* This does not count off-screen nodes. */ |
---|
235 | int n_nodes; |
---|
236 | } fringe_c; |
---|
237 | |
---|
238 | |
---|
239 | /*- |
---|
240 | * The forced vertex pool contains vertices where at least one |
---|
241 | * side of the tiled region can only be extended in one way. Note |
---|
242 | * that this does not necessarily mean that there would only be one |
---|
243 | * applicable rule. forced_sides are specified using S_LEFT and |
---|
244 | * S_RIGHT as if looking at the untiled region from the vertex. |
---|
245 | */ |
---|
246 | struct forced_node { |
---|
247 | fringe_node_c *vertex; |
---|
248 | unsigned forced_sides; |
---|
249 | struct forced_node *next; |
---|
250 | }; |
---|
251 | |
---|
252 | typedef struct { |
---|
253 | forced_node_c *first; |
---|
254 | int n_nodes, n_visible; |
---|
255 | } forced_pool_c; |
---|
256 | |
---|
257 | |
---|
258 | /* This is the data related to the tiling of one screen. */ |
---|
259 | typedef struct { |
---|
260 | int width, height; |
---|
261 | XPoint origin; |
---|
262 | int edge_length; |
---|
263 | fringe_c fringe; |
---|
264 | forced_pool_c forced; |
---|
265 | int done, failures; |
---|
266 | unsigned long thick_color, thin_color; |
---|
267 | int busyLoop; |
---|
268 | Bool ammann; |
---|
269 | } tiling_c; |
---|
270 | |
---|
271 | static tiling_c *tilings = (tiling_c *) NULL; |
---|
272 | |
---|
273 | /* The tiles are listed in counterclockwise order. */ |
---|
274 | typedef struct { |
---|
275 | vertex_type_c tiles[MAX_TILES_PER_VERTEX]; |
---|
276 | int n_tiles; |
---|
277 | } vertex_rule_c; |
---|
278 | |
---|
279 | static vertex_rule_c vertex_rules[N_VERTEX_RULES] = |
---|
280 | { |
---|
281 | { |
---|
282 | {VT_THICK | 2, VT_THICK | 2, VT_THICK | 2, VT_THICK | 2, VT_THICK | 2}, 5}, |
---|
283 | { |
---|
284 | {VT_THICK | 0, VT_THICK | 0, VT_THICK | 0, VT_THICK | 0, VT_THICK | 0}, 5}, |
---|
285 | { |
---|
286 | {VT_THICK | 0, VT_THICK | 0, VT_THICK | 0, VT_THIN | 0}, 4}, |
---|
287 | { |
---|
288 | {VT_THICK | 2, VT_THICK | 2, VT_THIN | 1, VT_THIN | 3, VT_THICK | 2, |
---|
289 | VT_THIN | 1, VT_THIN | 3}, 7}, |
---|
290 | { |
---|
291 | {VT_THICK | 2, VT_THICK | 2, VT_THICK | 2, VT_THICK | 2, |
---|
292 | VT_THIN | 1, VT_THIN | 3}, 6}, |
---|
293 | { |
---|
294 | {VT_THICK | 1, VT_THICK | 3, VT_THIN | 2}, 3}, |
---|
295 | { |
---|
296 | {VT_THICK | 0, VT_THIN | 0, VT_THIN | 0}, 3}, |
---|
297 | { |
---|
298 | {VT_THICK | 2, VT_THIN | 1, VT_THICK | 3, VT_THICK | 1, VT_THIN | 3}, 5} |
---|
299 | }; |
---|
300 | |
---|
301 | |
---|
302 | /* Match information returned by match_rules. */ |
---|
303 | typedef struct { |
---|
304 | int rule; |
---|
305 | int pos; |
---|
306 | } rule_match_c; |
---|
307 | |
---|
308 | |
---|
309 | /* Occasionally floating point coordinates are needed. */ |
---|
310 | typedef struct { |
---|
311 | float x, y; |
---|
312 | } fcoord_c; |
---|
313 | |
---|
314 | |
---|
315 | /* All angles are measured in multiples of 36 degrees. */ |
---|
316 | typedef int angle_c; |
---|
317 | |
---|
318 | static angle_c vtype_angles[] = |
---|
319 | {4, 1, 4, 1, 2, 3, 2, 3}; |
---|
320 | |
---|
321 | #define vtype_angle( v) (vtype_angles[ v]) |
---|
322 | |
---|
323 | |
---|
324 | /* Direction angle of an edge. */ |
---|
325 | static angle_c |
---|
326 | vertex_dir(ModeInfo * mi, fringe_node_c * vertex, unsigned side) |
---|
327 | { |
---|
328 | tiling_c *tp = &tilings[MI_SCREEN(mi)]; |
---|
329 | fringe_node_c *v2 = |
---|
330 | (side == S_LEFT ? vertex->next : vertex->prev); |
---|
331 | register int i; |
---|
332 | |
---|
333 | for (i = 0; i < 5; i++) |
---|
334 | switch (v2->fived[i] - vertex->fived[i]) { |
---|
335 | case 1: |
---|
336 | return 2 * i; |
---|
337 | case -1: |
---|
338 | return (2 * i + 5) % 10; |
---|
339 | } |
---|
340 | tp->done = True; |
---|
341 | if (MI_IS_VERBOSE(mi)) { |
---|
342 | (void) fprintf(stderr, |
---|
343 | "Weirdness in vertex_dir (this has been reported)\n"); |
---|
344 | for (i = 0; i < 5; i++) |
---|
345 | (void) fprintf(stderr, "v2->fived[%d]=%d, vertex->fived[%d]=%d\n", |
---|
346 | i, v2->fived[i], i, vertex->fived[i]); |
---|
347 | } |
---|
348 | tp->busyLoop = CELEBRATE; |
---|
349 | return 0; |
---|
350 | } |
---|
351 | |
---|
352 | |
---|
353 | /* Move one step to a given direction. */ |
---|
354 | static void |
---|
355 | add_unit_vec(angle_c dir, int *fived) |
---|
356 | { |
---|
357 | static int dir2i[] = |
---|
358 | {0, 3, 1, 4, 2}; |
---|
359 | |
---|
360 | while (dir < 0) |
---|
361 | dir += 10; |
---|
362 | fived[dir2i[dir % 5]] += (dir % 2 ? -1 : 1); |
---|
363 | } |
---|
364 | |
---|
365 | |
---|
366 | /* For comparing coordinates. */ |
---|
367 | #define fived_equal( f1, f2) (!memcmp( (f1), (f2), 5 * sizeof( int))) |
---|
368 | |
---|
369 | |
---|
370 | /*- |
---|
371 | * This computes screen coordinates from 5D representation. Note that X |
---|
372 | * uses left-handed coordinates (y increases downwards). |
---|
373 | */ |
---|
374 | static void |
---|
375 | fived_to_loc(int fived[], tiling_c * tp, XPoint *pt) |
---|
376 | { |
---|
377 | static fcoord_c fived_table[5] = |
---|
378 | { |
---|
379 | {.0, .0}}; |
---|
380 | float fifth = 8 * atan(1.) / 5; |
---|
381 | register int i; |
---|
382 | register float r; |
---|
383 | register fcoord_c offset; |
---|
384 | |
---|
385 | *pt = tp->origin; |
---|
386 | offset.x = 0.0; |
---|
387 | offset.y = 0.0; |
---|
388 | if (fived_table[0].x == .0) |
---|
389 | for (i = 0; i < 5; i++) { |
---|
390 | fived_table[i].x = cos(fifth * i); |
---|
391 | fived_table[i].y = sin(fifth * i); |
---|
392 | } |
---|
393 | for (i = 0; i < 5; i++) { |
---|
394 | r = fived[i] * tp->edge_length; |
---|
395 | offset.x += r * fived_table[i].x; |
---|
396 | offset.y -= r * fived_table[i].y; |
---|
397 | } |
---|
398 | (*pt).x += (int) (offset.x + .5); |
---|
399 | (*pt).y += (int) (offset.y + .5); |
---|
400 | } |
---|
401 | |
---|
402 | |
---|
403 | /* Mop up dynamic data for one screen. */ |
---|
404 | static void |
---|
405 | free_penrose(tiling_c * tp) |
---|
406 | { |
---|
407 | register fringe_node_c *fp1, *fp2; |
---|
408 | register forced_node_c *lp1, *lp2; |
---|
409 | |
---|
410 | if (tp->fringe.nodes == NULL) |
---|
411 | return; |
---|
412 | fp1 = tp->fringe.nodes; |
---|
413 | do { |
---|
414 | fp2 = fp1; |
---|
415 | fp1 = fp1->next; |
---|
416 | (void) free((void *) fp2); |
---|
417 | } while (fp1 != tp->fringe.nodes); |
---|
418 | tp->fringe.nodes = (fringe_node_c *) NULL; |
---|
419 | for (lp1 = tp->forced.first; lp1 != 0;) { |
---|
420 | lp2 = lp1; |
---|
421 | lp1 = lp1->next; |
---|
422 | (void) free((void *) lp2); |
---|
423 | } |
---|
424 | tp->forced.first = 0; |
---|
425 | } |
---|
426 | |
---|
427 | |
---|
428 | /* Called to init the mode. */ |
---|
429 | void |
---|
430 | init_penrose(ModeInfo * mi) |
---|
431 | { |
---|
432 | tiling_c *tp; |
---|
433 | fringe_node_c *fp; |
---|
434 | int i, size; |
---|
435 | |
---|
436 | if (tilings == NULL) { |
---|
437 | if ((tilings = (tiling_c *) calloc(MI_NUM_SCREENS(mi), |
---|
438 | sizeof (tiling_c))) == NULL) |
---|
439 | return; |
---|
440 | } |
---|
441 | tp = &tilings[MI_SCREEN(mi)]; |
---|
442 | |
---|
443 | #if 0 /* if you do this, then the -ammann and -no-ammann options don't work. |
---|
444 | -- jwz */ |
---|
445 | if (MI_IS_FULLRANDOM(mi)) |
---|
446 | tp->ammann = (Bool) (LRAND() & 1); |
---|
447 | else |
---|
448 | #endif /* 0 */ |
---|
449 | tp->ammann = ammann; |
---|
450 | |
---|
451 | tp->done = False; |
---|
452 | tp->busyLoop = 0; |
---|
453 | tp->failures = 0; |
---|
454 | tp->width = MI_WIDTH(mi); |
---|
455 | tp->height = MI_HEIGHT(mi); |
---|
456 | if (MI_NPIXELS(mi) > 2) { |
---|
457 | tp->thick_color = NRAND(MI_NPIXELS(mi)); |
---|
458 | /* Insure good contrast */ |
---|
459 | tp->thin_color = (NRAND(2 * MI_NPIXELS(mi) / 3) + tp->thick_color + |
---|
460 | MI_NPIXELS(mi) / 6) % MI_NPIXELS(mi); |
---|
461 | } |
---|
462 | size = MI_SIZE(mi); |
---|
463 | if (size < -MINSIZE) |
---|
464 | tp->edge_length = NRAND(MIN(-size, MAX(MINSIZE, |
---|
465 | MIN(tp->width, tp->height) / 2)) - MINSIZE + 1) + MINSIZE; |
---|
466 | else if (size < MINSIZE) { |
---|
467 | if (!size) |
---|
468 | tp->edge_length = MAX(MINSIZE, MIN(tp->width, tp->height) / 2); |
---|
469 | else |
---|
470 | tp->edge_length = MINSIZE; |
---|
471 | } else |
---|
472 | tp->edge_length = MIN(size, MAX(MINSIZE, |
---|
473 | MIN(tp->width, tp->height) / 2)); |
---|
474 | tp->origin.x = (tp->width / 2 + NRAND(tp->width)) / 2; |
---|
475 | tp->origin.y = (tp->height / 2 + NRAND(tp->height)) / 2; |
---|
476 | tp->fringe.n_nodes = 2; |
---|
477 | if (tp->fringe.nodes != NULL) |
---|
478 | free_penrose(tp); |
---|
479 | if (tp->fringe.nodes != NULL || tp->forced.first != 0) { |
---|
480 | if (MI_IS_VERBOSE(mi)) { |
---|
481 | (void) fprintf(stderr, "Weirdness in init_penrose()\n"); |
---|
482 | (void) fprintf(stderr, "tp->fringe.nodes = NULL && tp->forced.first = 0\n"); |
---|
483 | } |
---|
484 | free_penrose(tp); /* Try again */ |
---|
485 | tp->done = True; |
---|
486 | } |
---|
487 | tp->forced.n_nodes = tp->forced.n_visible = 0; |
---|
488 | if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) { |
---|
489 | free_penrose(tp); |
---|
490 | return; |
---|
491 | } |
---|
492 | if (fp == 0) { |
---|
493 | if (MI_IS_VERBOSE(mi)) { |
---|
494 | (void) fprintf(stderr, "Weirdness in init_penrose()\n"); |
---|
495 | (void) fprintf(stderr, "fp = 0\n"); |
---|
496 | } |
---|
497 | if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) { |
---|
498 | free_penrose(tp); |
---|
499 | return; |
---|
500 | } |
---|
501 | tp->done = True; |
---|
502 | } |
---|
503 | /* First vertex. */ |
---|
504 | fp->rule_mask = (1 << N_VERTEX_RULES) - 1; |
---|
505 | fp->list_ptr = 0; |
---|
506 | if ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) { |
---|
507 | free_penrose(tp); |
---|
508 | return; |
---|
509 | } |
---|
510 | if (fp->next == 0) { |
---|
511 | if (MI_IS_VERBOSE(mi)) { |
---|
512 | (void) fprintf(stderr, "Weirdness in init_penrose()\n"); |
---|
513 | (void) fprintf(stderr, "fp->next = 0\n"); |
---|
514 | } |
---|
515 | if ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) { |
---|
516 | free_penrose(tp); |
---|
517 | return; |
---|
518 | } |
---|
519 | tp->done = True; |
---|
520 | } |
---|
521 | fp->n_tiles = 0; |
---|
522 | fp->loc = tp->origin; |
---|
523 | fp->off_screen = False; |
---|
524 | for (i = 0; i < 5; i++) |
---|
525 | fp->fived[i] = 0; |
---|
526 | |
---|
527 | /* Second vertex. */ |
---|
528 | *(fp->next) = *fp; |
---|
529 | fp->next->prev = fp->next->next = fp; |
---|
530 | fp = fp->next; |
---|
531 | i = NRAND(5); |
---|
532 | fp->fived[i] = 2 * NRAND(2) - 1; |
---|
533 | fived_to_loc(fp->fived, tp, &(fp->loc)); |
---|
534 | /* That's it! We have created our first edge. */ |
---|
535 | } |
---|
536 | |
---|
537 | /*- |
---|
538 | * This attempts to match the configuration of vertex with the vertex |
---|
539 | * rules. The return value is a total match count. If matches is |
---|
540 | * non-null, it will be used to store information about the matches |
---|
541 | * and must be large enough to contain it. To play it absolutely |
---|
542 | * safe, allocate room for MAX_TILES_PER_VERTEX * N_VERTEX_RULES |
---|
543 | * entries when searching all matches. The rule mask of vertex will |
---|
544 | * be applied and rules masked out will not be searched. Only strict |
---|
545 | * subsequences match. If first_only is true, the search stops when |
---|
546 | * the first match is found. Otherwise all matches will be found and |
---|
547 | * the rule_mask of vertex will be updated, which also happens in |
---|
548 | * single-match mode if no match is found. |
---|
549 | */ |
---|
550 | static int |
---|
551 | match_rules(fringe_node_c * vertex, rule_match_c * matches, int first_only) |
---|
552 | { |
---|
553 | /* I will assume that I can fit all the relevant bits in vertex->tiles |
---|
554 | into one unsigned long. With 3 bits per element and at most 7 |
---|
555 | elements this means 21 bits, which should leave plenty of room. |
---|
556 | After packing the bits the rest is just integer comparisons and |
---|
557 | some bit shuffling. This is essentially Rabin-Karp without |
---|
558 | congruence arithmetic. */ |
---|
559 | register int i, j; |
---|
560 | int hits = 0, good_rules[N_VERTEX_RULES], n_good = 0; |
---|
561 | unsigned long |
---|
562 | vertex_hash = 0, lower_bits_mask = ~(VT_TOTAL_MASK << VT_BITS * (vertex->n_tiles - 1)); |
---|
563 | unsigned new_rule_mask = 0; |
---|
564 | |
---|
565 | for (i = 0; i < N_VERTEX_RULES; i++) |
---|
566 | if (vertex->n_tiles >= vertex_rules[i].n_tiles) |
---|
567 | vertex->rule_mask &= ~(1 << i); |
---|
568 | else if (vertex->rule_mask & 1 << i) |
---|
569 | good_rules[n_good++] = i; |
---|
570 | for (i = 0; i < vertex->n_tiles; i++) |
---|
571 | vertex_hash |= (unsigned long) vertex->tiles[i] << (VT_BITS * i); |
---|
572 | |
---|
573 | for (j = 0; j < n_good; j++) { |
---|
574 | unsigned long rule_hash = 0; |
---|
575 | vertex_rule_c *vr = vertex_rules + good_rules[j]; |
---|
576 | |
---|
577 | for (i = 0; i < vertex->n_tiles; i++) |
---|
578 | rule_hash |= (unsigned long) vr->tiles[i] << (VT_BITS * i); |
---|
579 | if (rule_hash == vertex_hash) { |
---|
580 | if (matches != 0) { |
---|
581 | matches[hits].rule = good_rules[j]; |
---|
582 | matches[hits].pos = 0; |
---|
583 | } |
---|
584 | hits++; |
---|
585 | if (first_only) |
---|
586 | return hits; |
---|
587 | else |
---|
588 | new_rule_mask |= 1 << good_rules[j]; |
---|
589 | } |
---|
590 | for (i = vr->n_tiles - 1; i > 0; i--) { |
---|
591 | rule_hash = vr->tiles[i] | (rule_hash & lower_bits_mask) << VT_BITS; |
---|
592 | if (vertex_hash == rule_hash) { |
---|
593 | if (matches != 0) { |
---|
594 | matches[hits].rule = good_rules[j]; |
---|
595 | matches[hits].pos = i; |
---|
596 | } |
---|
597 | hits++; |
---|
598 | if (first_only) |
---|
599 | return hits; |
---|
600 | else |
---|
601 | new_rule_mask |= 1 << good_rules[j]; |
---|
602 | } |
---|
603 | } |
---|
604 | } |
---|
605 | vertex->rule_mask = new_rule_mask; |
---|
606 | return hits; |
---|
607 | } |
---|
608 | |
---|
609 | |
---|
610 | /*- |
---|
611 | * find_completions finds the possible ways to add a tile to a vertex. |
---|
612 | * The return values is the number of such possibilities. You must |
---|
613 | * first call match_rules to produce matches and n_matches. sides |
---|
614 | * specifies which side of the vertex to extend and can be S_LEFT or |
---|
615 | * S_RIGHT. If results is non-null, it should point to an array large |
---|
616 | * enough to contain the results, which will be stored there. |
---|
617 | * MAX_COMPL elements will always suffice. If first_only is true we |
---|
618 | * stop as soon as we find one possibility (NOT USED). |
---|
619 | */ |
---|
620 | #define MAX_COMPL 2 |
---|
621 | |
---|
622 | static int |
---|
623 | find_completions(fringe_node_c * vertex, rule_match_c * matches, int n_matches, |
---|
624 | unsigned side, vertex_type_c * results /*, int first_only */ ) |
---|
625 | { |
---|
626 | int n_res = 0, cont; |
---|
627 | register int i, j; |
---|
628 | vertex_type_c buf[MAX_COMPL]; |
---|
629 | |
---|
630 | if (results == 0) |
---|
631 | results = buf; |
---|
632 | if (n_matches <= 0) |
---|
633 | return 0; |
---|
634 | for (i = 0; i < n_matches; i++) { |
---|
635 | vertex_rule_c *rule = vertex_rules + matches[i].rule; |
---|
636 | int pos = (matches[i].pos |
---|
637 | + (side == S_RIGHT ? vertex->n_tiles : rule->n_tiles - 1)) |
---|
638 | % rule->n_tiles; |
---|
639 | vertex_type_c vtype = rule->tiles[pos]; |
---|
640 | |
---|
641 | cont = 1; |
---|
642 | for (j = 0; j < n_res; j++) |
---|
643 | if (vtype == results[j]) { |
---|
644 | cont = 0; |
---|
645 | break; |
---|
646 | } |
---|
647 | if (cont) |
---|
648 | results[n_res++] = vtype; |
---|
649 | } |
---|
650 | return n_res; |
---|
651 | } |
---|
652 | |
---|
653 | |
---|
654 | /*- |
---|
655 | * Draw a tile on the display. Vertices must be given in a |
---|
656 | * counterclockwise order. vtype is the vertex type of v1 (and thus |
---|
657 | * also gives the tile type). |
---|
658 | */ |
---|
659 | static void |
---|
660 | draw_tile(fringe_node_c * v1, fringe_node_c * v2, |
---|
661 | fringe_node_c * v3, fringe_node_c * v4, |
---|
662 | vertex_type_c vtype, ModeInfo * mi) |
---|
663 | { |
---|
664 | Display *display = MI_DISPLAY(mi); |
---|
665 | Window window = MI_WINDOW(mi); |
---|
666 | GC gc = MI_GC(mi); |
---|
667 | tiling_c *tp = &tilings[MI_SCREEN(mi)]; |
---|
668 | XPoint pts[5]; |
---|
669 | vertex_type_c corner = vtype & VT_CORNER_MASK; |
---|
670 | |
---|
671 | if (v1->off_screen && v2->off_screen && v3->off_screen && v4->off_screen) |
---|
672 | return; |
---|
673 | pts[corner] = v1->loc; |
---|
674 | pts[VT_RIGHT(corner)] = v2->loc; |
---|
675 | pts[VT_FAR(corner)] = v3->loc; |
---|
676 | pts[VT_LEFT(corner)] = v4->loc; |
---|
677 | pts[4] = pts[0]; |
---|
678 | if (MI_NPIXELS(mi) > 2) { |
---|
679 | if ((vtype & VT_TYPE_MASK) == VT_THICK) |
---|
680 | XSetForeground(display, gc, MI_PIXEL(mi, tp->thick_color)); |
---|
681 | else |
---|
682 | XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color)); |
---|
683 | } else |
---|
684 | XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); |
---|
685 | XFillPolygon(display, window, gc, pts, 4, Convex, CoordModeOrigin); |
---|
686 | XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); |
---|
687 | XDrawLines(display, window, gc, pts, 5, CoordModeOrigin); |
---|
688 | |
---|
689 | if (tp->ammann) { |
---|
690 | /* Draw some Ammann lines for debugging purposes. This will probably |
---|
691 | fail miserably on a b&w display. */ |
---|
692 | |
---|
693 | if ((vtype & VT_TYPE_MASK) == VT_THICK) { |
---|
694 | static float r = .0; |
---|
695 | |
---|
696 | if (r == .0) { |
---|
697 | float pi10 = 2 * atan(1.) / 5; |
---|
698 | |
---|
699 | r = 1 - sin(pi10) / (2 * sin(3 * pi10)); |
---|
700 | } |
---|
701 | if (MI_NPIXELS(mi) > 2) |
---|
702 | XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color)); |
---|
703 | else { |
---|
704 | XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); |
---|
705 | XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter); |
---|
706 | } |
---|
707 | XDrawLine(display, window, gc, |
---|
708 | (int) (r * pts[3].x + (1 - r) * pts[0].x + .5), |
---|
709 | (int) (r * pts[3].y + (1 - r) * pts[0].y + .5), |
---|
710 | (int) (r * pts[1].x + (1 - r) * pts[0].x + .5), |
---|
711 | (int) (r * pts[1].y + (1 - r) * pts[0].y + .5)); |
---|
712 | if (MI_NPIXELS(mi) <= 2) |
---|
713 | XSetLineAttributes(display, gc, 1, LineSolid, CapNotLast, JoinMiter); |
---|
714 | } else { |
---|
715 | if (MI_NPIXELS(mi) > 2) |
---|
716 | XSetForeground(display, gc, MI_PIXEL(mi, tp->thick_color)); |
---|
717 | else { |
---|
718 | XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); |
---|
719 | XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter); |
---|
720 | } |
---|
721 | XDrawLine(display, window, gc, |
---|
722 | (int) ((pts[3].x + pts[2].x) / 2 + .5), |
---|
723 | (int) ((pts[3].y + pts[2].y) / 2 + .5), |
---|
724 | (int) ((pts[1].x + pts[2].x) / 2 + .5), |
---|
725 | (int) ((pts[1].y + pts[2].y) / 2 + .5)); |
---|
726 | if (MI_NPIXELS(mi) <= 2) |
---|
727 | XSetLineAttributes(display, gc, 1, LineSolid, CapNotLast, JoinMiter); |
---|
728 | } |
---|
729 | } |
---|
730 | } |
---|
731 | |
---|
732 | /*- |
---|
733 | * Update the status of this vertex on the forced vertex queue. If |
---|
734 | * the vertex has become untileable set tp->done. This is supposed |
---|
735 | * to detect dislocations -- never call this routine with a completely |
---|
736 | * tiled vertex. |
---|
737 | * |
---|
738 | * Check for untileable vertices in check_vertex and stop tiling as |
---|
739 | * soon as one finds one. I don't know if it is possible to run out |
---|
740 | * of forced vertices while untileable vertices exist (or will |
---|
741 | * cavities inevitably appear). If this can happen, add_random_tile |
---|
742 | * might get called with an untileable vertex, causing ( n <= 1). |
---|
743 | * (This is what the tp->done checks for). |
---|
744 | * |
---|
745 | * A delayLoop celebrates the dislocation. |
---|
746 | */ |
---|
747 | static void |
---|
748 | check_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp) |
---|
749 | { |
---|
750 | rule_match_c hits[MAX_TILES_PER_VERTEX * N_VERTEX_RULES]; |
---|
751 | int n_hits = match_rules(vertex, hits, False); |
---|
752 | unsigned forced_sides = 0; |
---|
753 | |
---|
754 | if (vertex->rule_mask == 0) { |
---|
755 | tp->done = True; |
---|
756 | if (MI_IS_VERBOSE(mi)) { |
---|
757 | (void) fprintf(stderr, "Dislocation occurred!\n"); |
---|
758 | } |
---|
759 | tp->busyLoop = CELEBRATE; /* Should be able to recover */ |
---|
760 | } |
---|
761 | if (1 == find_completions(vertex, hits, n_hits, S_LEFT, 0 /*, False */ )) |
---|
762 | forced_sides |= S_LEFT; |
---|
763 | if (1 == find_completions(vertex, hits, n_hits, S_RIGHT, 0 /*, False */ )) |
---|
764 | forced_sides |= S_RIGHT; |
---|
765 | if (forced_sides == 0) { |
---|
766 | if (vertex->list_ptr != 0) { |
---|
767 | forced_node_c *node = *vertex->list_ptr; |
---|
768 | |
---|
769 | *vertex->list_ptr = node->next; |
---|
770 | if (node->next != 0) |
---|
771 | node->next->vertex->list_ptr = vertex->list_ptr; |
---|
772 | (void) free((void *) node); |
---|
773 | tp->forced.n_nodes--; |
---|
774 | if (!vertex->off_screen) |
---|
775 | tp->forced.n_visible--; |
---|
776 | vertex->list_ptr = 0; |
---|
777 | } |
---|
778 | } else { |
---|
779 | forced_node_c *node; |
---|
780 | |
---|
781 | if (vertex->list_ptr == 0) { |
---|
782 | if ((node = ALLOC_NODE(forced_node_c)) == NULL) |
---|
783 | return; |
---|
784 | node->vertex = vertex; |
---|
785 | node->next = tp->forced.first; |
---|
786 | if (tp->forced.first != 0) |
---|
787 | tp->forced.first->vertex->list_ptr = &(node->next); |
---|
788 | tp->forced.first = node; |
---|
789 | vertex->list_ptr = &(tp->forced.first); |
---|
790 | tp->forced.n_nodes++; |
---|
791 | if (!vertex->off_screen) |
---|
792 | tp->forced.n_visible++; |
---|
793 | } else |
---|
794 | node = *vertex->list_ptr; |
---|
795 | node->forced_sides = forced_sides; |
---|
796 | } |
---|
797 | } |
---|
798 | |
---|
799 | |
---|
800 | /*- |
---|
801 | * Delete this vertex. If the vertex is a member of the forced vertex queue, |
---|
802 | * also remove that entry. We assume that the vertex is no longer |
---|
803 | * connected to the fringe. Note that tp->fringe.nodes must not point to |
---|
804 | * the vertex being deleted. |
---|
805 | */ |
---|
806 | static void |
---|
807 | delete_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp) |
---|
808 | { |
---|
809 | if (tp->fringe.nodes == vertex) { |
---|
810 | tp->done = True; |
---|
811 | if (MI_IS_VERBOSE(mi)) { |
---|
812 | (void) fprintf(stderr, "Weirdness in delete_penrose()\n"); |
---|
813 | (void) fprintf(stderr, "tp->fringe.nodes == vertex\n"); |
---|
814 | } |
---|
815 | tp->busyLoop = CELEBRATE; |
---|
816 | } |
---|
817 | if (vertex->list_ptr != 0) { |
---|
818 | forced_node_c *node = *vertex->list_ptr; |
---|
819 | |
---|
820 | *vertex->list_ptr = node->next; |
---|
821 | if (node->next != 0) |
---|
822 | node->next->vertex->list_ptr = vertex->list_ptr; |
---|
823 | (void) free((void *) node); |
---|
824 | tp->forced.n_nodes--; |
---|
825 | if (!vertex->off_screen) |
---|
826 | tp->forced.n_visible--; |
---|
827 | } |
---|
828 | if (!vertex->off_screen) |
---|
829 | tp->fringe.n_nodes--; |
---|
830 | (void) free((void *) vertex); |
---|
831 | } |
---|
832 | |
---|
833 | |
---|
834 | /*- |
---|
835 | * Check whether the addition of a tile of type vtype would completely fill |
---|
836 | * the space available at vertex. |
---|
837 | */ |
---|
838 | static int |
---|
839 | fills_vertex(ModeInfo * mi, vertex_type_c vtype, fringe_node_c * vertex) |
---|
840 | { |
---|
841 | return |
---|
842 | (vertex_dir(mi, vertex, S_LEFT) - vertex_dir(mi, vertex, S_RIGHT) |
---|
843 | - vtype_angle(vtype)) % 10 == 0; |
---|
844 | } |
---|
845 | |
---|
846 | |
---|
847 | /*- |
---|
848 | * If you were to add a tile of type vtype to a specified side of |
---|
849 | * vertex, fringe_changes tells you which other vertices it would |
---|
850 | * attach to. The addresses of these vertices will be stored in the |
---|
851 | * last three arguments. Null is stored if the corresponding vertex |
---|
852 | * would need to be allocated. |
---|
853 | * |
---|
854 | * The function also analyzes which vertices would be swallowed by the tiling |
---|
855 | * and thus cut off from the fringe. The result is returned as a bit pattern. |
---|
856 | */ |
---|
857 | #define FC_BAG 1 /* Total enclosure. Should never occur. */ |
---|
858 | #define FC_NEW_RIGHT 2 |
---|
859 | #define FC_NEW_FAR 4 |
---|
860 | #define FC_NEW_LEFT 8 |
---|
861 | #define FC_NEW_MASK 0xe |
---|
862 | #define FC_CUT_THIS 0x10 |
---|
863 | #define FC_CUT_RIGHT 0x20 |
---|
864 | #define FC_CUT_FAR 0x40 |
---|
865 | #define FC_CUT_LEFT 0x80 |
---|
866 | #define FC_CUT_MASK 0xf0 |
---|
867 | #define FC_TOTAL_MASK 0xff |
---|
868 | |
---|
869 | static unsigned |
---|
870 | fringe_changes(ModeInfo * mi, fringe_node_c * vertex, |
---|
871 | unsigned side, vertex_type_c vtype, |
---|
872 | fringe_node_c ** right, fringe_node_c ** far, |
---|
873 | fringe_node_c ** left) |
---|
874 | { |
---|
875 | fringe_node_c *v, *f = (fringe_node_c *) NULL; |
---|
876 | unsigned result = FC_NEW_FAR; /* We clear this later if necessary. */ |
---|
877 | |
---|
878 | if (far) |
---|
879 | *far = 0; |
---|
880 | if (fills_vertex(mi, vtype, vertex)) { |
---|
881 | result |= FC_CUT_THIS; |
---|
882 | } else if (side == S_LEFT) { |
---|
883 | result |= FC_NEW_RIGHT; |
---|
884 | if (right) |
---|
885 | *right = 0; |
---|
886 | } else { |
---|
887 | result |= FC_NEW_LEFT; |
---|
888 | if (left) |
---|
889 | *left = 0; |
---|
890 | } |
---|
891 | |
---|
892 | if (!(result & FC_NEW_LEFT)) { |
---|
893 | v = vertex->next; |
---|
894 | if (left) |
---|
895 | *left = v; |
---|
896 | if (fills_vertex(mi, VT_LEFT(vtype), v)) { |
---|
897 | result = (result & ~FC_NEW_FAR) | FC_CUT_LEFT; |
---|
898 | f = v->next; |
---|
899 | if (far) |
---|
900 | *far = f; |
---|
901 | } |
---|
902 | } |
---|
903 | if (!(result & FC_NEW_RIGHT)) { |
---|
904 | v = vertex->prev; |
---|
905 | if (right) |
---|
906 | *right = v; |
---|
907 | if (fills_vertex(mi, VT_RIGHT(vtype), v)) { |
---|
908 | result = (result & ~FC_NEW_FAR) | FC_CUT_RIGHT; |
---|
909 | f = v->prev; |
---|
910 | if (far) |
---|
911 | *far = f; |
---|
912 | } |
---|
913 | } |
---|
914 | if (!(result & FC_NEW_FAR) |
---|
915 | && fills_vertex(mi, VT_FAR(vtype), f)) { |
---|
916 | result |= FC_CUT_FAR; |
---|
917 | result &= (~FC_NEW_LEFT & ~FC_NEW_RIGHT); |
---|
918 | if (right && (result & FC_CUT_LEFT)) |
---|
919 | *right = f->next; |
---|
920 | if (left && (result & FC_CUT_RIGHT)) |
---|
921 | *left = f->prev; |
---|
922 | } |
---|
923 | if (((result & FC_CUT_LEFT) && (result & FC_CUT_RIGHT)) |
---|
924 | || ((result & FC_CUT_THIS) && (result & FC_CUT_FAR))) |
---|
925 | result |= FC_BAG; |
---|
926 | return result; |
---|
927 | } |
---|
928 | |
---|
929 | |
---|
930 | /* A couple of lesser helper functions for add_tile. */ |
---|
931 | static void |
---|
932 | add_vtype(fringe_node_c * vertex, unsigned side, vertex_type_c vtype) |
---|
933 | { |
---|
934 | if (side == S_RIGHT) |
---|
935 | vertex->tiles[vertex->n_tiles++] = vtype; |
---|
936 | else { |
---|
937 | register int i; |
---|
938 | |
---|
939 | for (i = vertex->n_tiles; i > 0; i--) |
---|
940 | vertex->tiles[i] = vertex->tiles[i - 1]; |
---|
941 | vertex->tiles[0] = vtype; |
---|
942 | vertex->n_tiles++; |
---|
943 | } |
---|
944 | } |
---|
945 | |
---|
946 | static fringe_node_c * |
---|
947 | alloc_vertex(ModeInfo * mi, angle_c dir, fringe_node_c * from, tiling_c * tp) |
---|
948 | { |
---|
949 | fringe_node_c *v; |
---|
950 | |
---|
951 | if ((v = ALLOC_NODE(fringe_node_c)) == NULL) { |
---|
952 | tp->done = True; |
---|
953 | if (MI_IS_VERBOSE(mi)) { |
---|
954 | (void) fprintf(stderr, "No memory in alloc_vertex()\n"); |
---|
955 | } |
---|
956 | tp->busyLoop = CELEBRATE; |
---|
957 | return v; |
---|
958 | } |
---|
959 | *v = *from; |
---|
960 | add_unit_vec(dir, v->fived); |
---|
961 | fived_to_loc(v->fived, tp, &(v->loc)); |
---|
962 | if (v->loc.x < 0 || v->loc.y < 0 |
---|
963 | || v->loc.x >= tp->width || v->loc.y >= tp->height) { |
---|
964 | v->off_screen = True; |
---|
965 | if (v->loc.x < -tp->width || v->loc.y < -tp->height |
---|
966 | || v->loc.x >= 2 * tp->width || v->loc.y >= 2 * tp->height) |
---|
967 | tp->done = True; |
---|
968 | } else { |
---|
969 | v->off_screen = False; |
---|
970 | tp->fringe.n_nodes++; |
---|
971 | } |
---|
972 | v->n_tiles = 0; |
---|
973 | v->rule_mask = (1 << N_VERTEX_RULES) - 1; |
---|
974 | v->list_ptr = 0; |
---|
975 | return v; |
---|
976 | } |
---|
977 | |
---|
978 | /*- |
---|
979 | * Add a tile described by vtype to the side of vertex. This must be |
---|
980 | * allowed by the rules -- we do not check it here. New vertices are |
---|
981 | * allocated as necessary. The fringe and the forced vertex pool are updated. |
---|
982 | * The new tile is drawn on the display. |
---|
983 | * |
---|
984 | * One thing we do check here is whether the new tile causes an untiled |
---|
985 | * area to become enclosed by the tiling. If this would happen, the tile |
---|
986 | * is not added. The return value is true iff a tile was added. |
---|
987 | */ |
---|
988 | static int |
---|
989 | add_tile(ModeInfo * mi, |
---|
990 | fringe_node_c * vertex, unsigned side, vertex_type_c vtype) |
---|
991 | { |
---|
992 | tiling_c *tp = &tilings[MI_SCREEN(mi)]; |
---|
993 | |
---|
994 | fringe_node_c |
---|
995 | *left = (fringe_node_c *) NULL, |
---|
996 | *right = (fringe_node_c *) NULL, |
---|
997 | *far = (fringe_node_c *) NULL, |
---|
998 | *node; |
---|
999 | unsigned fc = fringe_changes(mi, vertex, side, vtype, &right, &far, &left); |
---|
1000 | |
---|
1001 | vertex_type_c |
---|
1002 | ltype = VT_LEFT(vtype), |
---|
1003 | rtype = VT_RIGHT(vtype), |
---|
1004 | ftype = VT_FAR(vtype); |
---|
1005 | |
---|
1006 | /* By our conventions vertex->next lies to the left of vertex and |
---|
1007 | vertex->prev to the right. */ |
---|
1008 | |
---|
1009 | /* This should never occur. */ |
---|
1010 | if (fc & FC_BAG) { |
---|
1011 | tp->done = True; |
---|
1012 | if (MI_IS_VERBOSE(mi)) { |
---|
1013 | (void) fprintf(stderr, "Weirdness in add_tile()\n"); |
---|
1014 | (void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG); |
---|
1015 | } |
---|
1016 | } |
---|
1017 | if (side == S_LEFT) { |
---|
1018 | if (right == NULL) |
---|
1019 | if ((right = alloc_vertex(mi, vertex_dir(mi, vertex, S_LEFT) - |
---|
1020 | vtype_angle(vtype), vertex, tp)) == NULL) |
---|
1021 | return False; |
---|
1022 | if (far == NULL) |
---|
1023 | if ((far = alloc_vertex(mi, vertex_dir(mi, left, S_RIGHT) + |
---|
1024 | vtype_angle(ltype), left, tp)) == NULL) |
---|
1025 | return False; |
---|
1026 | } else { |
---|
1027 | if (left == NULL) |
---|
1028 | if ((left = alloc_vertex(mi, vertex_dir(mi, vertex, S_RIGHT) + |
---|
1029 | vtype_angle(vtype), vertex, tp)) == NULL) |
---|
1030 | return False; |
---|
1031 | if (far == NULL) |
---|
1032 | if ((far = alloc_vertex(mi, vertex_dir(mi, right, S_LEFT) - |
---|
1033 | vtype_angle(rtype), right, tp)) == NULL) |
---|
1034 | return False; |
---|
1035 | } |
---|
1036 | |
---|
1037 | /* Having allocated the new vertices, but before joining them with |
---|
1038 | the rest of the fringe, check if vertices with same coordinates |
---|
1039 | already exist. If any such are found, give up. */ |
---|
1040 | node = tp->fringe.nodes; |
---|
1041 | do { |
---|
1042 | if (((fc & FC_NEW_LEFT) && fived_equal(node->fived, left->fived)) |
---|
1043 | || ((fc & FC_NEW_RIGHT) && fived_equal(node->fived, right->fived)) |
---|
1044 | || ((fc & FC_NEW_FAR) && fived_equal(node->fived, far->fived))) { |
---|
1045 | /* Better luck next time. */ |
---|
1046 | if (fc & FC_NEW_LEFT) |
---|
1047 | delete_vertex(mi, left, tp); |
---|
1048 | if (fc & FC_NEW_RIGHT) |
---|
1049 | delete_vertex(mi, right, tp); |
---|
1050 | if (fc & FC_NEW_FAR) |
---|
1051 | delete_vertex(mi, far, tp); |
---|
1052 | return False; |
---|
1053 | } |
---|
1054 | node = node->next; |
---|
1055 | } while (node != tp->fringe.nodes); |
---|
1056 | |
---|
1057 | /* Rechain. */ |
---|
1058 | if (!(fc & FC_CUT_THIS)) { |
---|
1059 | if (side == S_LEFT) { |
---|
1060 | vertex->next = right; |
---|
1061 | right->prev = vertex; |
---|
1062 | } else { |
---|
1063 | vertex->prev = left; |
---|
1064 | left->next = vertex; |
---|
1065 | } |
---|
1066 | } |
---|
1067 | if (!(fc & FC_CUT_FAR)) { |
---|
1068 | if (!(fc & FC_CUT_LEFT)) { |
---|
1069 | far->next = left; |
---|
1070 | left->prev = far; |
---|
1071 | } |
---|
1072 | if (!(fc & FC_CUT_RIGHT)) { |
---|
1073 | far->prev = right; |
---|
1074 | right->next = far; |
---|
1075 | } |
---|
1076 | } |
---|
1077 | draw_tile(vertex, right, far, left, vtype, mi); |
---|
1078 | |
---|
1079 | /* Delete vertices that are no longer on the fringe. Check the others. */ |
---|
1080 | if (fc & FC_CUT_THIS) { |
---|
1081 | tp->fringe.nodes = far; |
---|
1082 | delete_vertex(mi, vertex, tp); |
---|
1083 | } else { |
---|
1084 | add_vtype(vertex, side, vtype); |
---|
1085 | check_vertex(mi, vertex, tp); |
---|
1086 | tp->fringe.nodes = vertex; |
---|
1087 | } |
---|
1088 | if (fc & FC_CUT_FAR) |
---|
1089 | delete_vertex(mi, far, tp); |
---|
1090 | else { |
---|
1091 | add_vtype(far, fc & FC_CUT_RIGHT ? S_LEFT : S_RIGHT, ftype); |
---|
1092 | check_vertex(mi, far, tp); |
---|
1093 | } |
---|
1094 | if (fc & FC_CUT_LEFT) |
---|
1095 | delete_vertex(mi, left, tp); |
---|
1096 | else { |
---|
1097 | add_vtype(left, fc & FC_CUT_FAR ? S_LEFT : S_RIGHT, ltype); |
---|
1098 | check_vertex(mi, left, tp); |
---|
1099 | } |
---|
1100 | if (fc & FC_CUT_RIGHT) |
---|
1101 | delete_vertex(mi, right, tp); |
---|
1102 | else { |
---|
1103 | add_vtype(right, fc & FC_CUT_FAR ? S_RIGHT : S_LEFT, rtype); |
---|
1104 | check_vertex(mi, right, tp); |
---|
1105 | } |
---|
1106 | return True; |
---|
1107 | } |
---|
1108 | |
---|
1109 | |
---|
1110 | /*- |
---|
1111 | * Add a forced tile to a given forced vertex. Basically an easy job, |
---|
1112 | * since we know what to add. But it might fail if adding the tile |
---|
1113 | * would cause some untiled area to become enclosed. There is also another |
---|
1114 | * more exotic culprit: we might have a dislocation. Fortunately, they |
---|
1115 | * are very rare (the PRL article reported that perfect tilings of over |
---|
1116 | * 2^50 tiles had been generated). There is a version of the algorithm |
---|
1117 | * that doesn't produce dislocations, but it's a lot hairier than the |
---|
1118 | * simpler version I used. |
---|
1119 | */ |
---|
1120 | static int |
---|
1121 | add_forced_tile(ModeInfo * mi, forced_node_c * node) |
---|
1122 | { |
---|
1123 | tiling_c *tp = &tilings[MI_SCREEN(mi)]; |
---|
1124 | unsigned side; |
---|
1125 | vertex_type_c vtype; |
---|
1126 | rule_match_c hits[MAX_TILES_PER_VERTEX * N_VERTEX_RULES]; |
---|
1127 | int n; |
---|
1128 | |
---|
1129 | if (node->forced_sides == (S_LEFT | S_RIGHT)) |
---|
1130 | side = NRAND(2) ? S_LEFT : S_RIGHT; |
---|
1131 | else |
---|
1132 | side = node->forced_sides; |
---|
1133 | n = match_rules(node->vertex, hits, True); |
---|
1134 | n = find_completions(node->vertex, hits, n, side, &vtype /*, True */ ); |
---|
1135 | if (n <= 0) { |
---|
1136 | tp->done = True; |
---|
1137 | if (MI_IS_VERBOSE(mi)) { |
---|
1138 | (void) fprintf(stderr, "Weirdness in add_forced_tile()\n"); |
---|
1139 | (void) fprintf(stderr, "n = %d\n", n); |
---|
1140 | } |
---|
1141 | } |
---|
1142 | return add_tile(mi, node->vertex, side, vtype); |
---|
1143 | } |
---|
1144 | |
---|
1145 | |
---|
1146 | /*- |
---|
1147 | * Whether the addition of a tile of vtype on the given side of vertex |
---|
1148 | * would conform to the rules. The efficient way to do this would be |
---|
1149 | * to add the new tile and then use the same type of search as in |
---|
1150 | * match_rules. However, this function is not a performance |
---|
1151 | * bottleneck (only needed for random tile additions, which are |
---|
1152 | * relatively infrequent), so I will settle for a simpler implementation. |
---|
1153 | */ |
---|
1154 | static int |
---|
1155 | legal_move(fringe_node_c * vertex, unsigned side, vertex_type_c vtype) |
---|
1156 | { |
---|
1157 | rule_match_c hits[MAX_TILES_PER_VERTEX * N_VERTEX_RULES]; |
---|
1158 | vertex_type_c legal_vt[MAX_COMPL]; |
---|
1159 | int n_hits, n_legal, i; |
---|
1160 | |
---|
1161 | n_hits = match_rules(vertex, hits, False); |
---|
1162 | n_legal = find_completions(vertex, hits, n_hits, side, legal_vt /*, False */ ); |
---|
1163 | for (i = 0; i < n_legal; i++) |
---|
1164 | if (legal_vt[i] == vtype) |
---|
1165 | return True; |
---|
1166 | return False; |
---|
1167 | } |
---|
1168 | |
---|
1169 | |
---|
1170 | /*- |
---|
1171 | * Add a randomly chosen tile to a given vertex. This requires more checking |
---|
1172 | * as we must make sure the new tile conforms to the vertex rules at every |
---|
1173 | * vertex it touches. */ |
---|
1174 | static void |
---|
1175 | add_random_tile(fringe_node_c * vertex, ModeInfo * mi) |
---|
1176 | { |
---|
1177 | fringe_node_c *right, *left, *far; |
---|
1178 | int i, j, n, n_hits, n_good; |
---|
1179 | unsigned side, fc, no_good, s; |
---|
1180 | vertex_type_c vtypes[MAX_COMPL]; |
---|
1181 | rule_match_c hits[MAX_TILES_PER_VERTEX * N_VERTEX_RULES]; |
---|
1182 | tiling_c *tp = &tilings[MI_SCREEN(mi)]; |
---|
1183 | |
---|
1184 | if (MI_NPIXELS(mi) > 2) { |
---|
1185 | tp->thick_color = NRAND(MI_NPIXELS(mi)); |
---|
1186 | /* Insure good contrast */ |
---|
1187 | tp->thin_color = (NRAND(2 * MI_NPIXELS(mi) / 3) + tp->thick_color + |
---|
1188 | MI_NPIXELS(mi) / 6) % MI_NPIXELS(mi); |
---|
1189 | } else |
---|
1190 | tp->thick_color = tp->thin_color = MI_WHITE_PIXEL(mi); |
---|
1191 | n_hits = match_rules(vertex, hits, False); |
---|
1192 | side = NRAND(2) ? S_LEFT : S_RIGHT; |
---|
1193 | n = find_completions(vertex, hits, n_hits, side, vtypes /*, False */ ); |
---|
1194 | /* One answer would mean a forced tile. */ |
---|
1195 | if (n <= 0) { |
---|
1196 | tp->done = True; |
---|
1197 | if (MI_IS_VERBOSE(mi)) { |
---|
1198 | (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); |
---|
1199 | (void) fprintf(stderr, "n = %d\n", n); |
---|
1200 | } |
---|
1201 | } |
---|
1202 | no_good = 0; |
---|
1203 | n_good = n; |
---|
1204 | for (i = 0; i < n; i++) { |
---|
1205 | fc = fringe_changes(mi, vertex, side, vtypes[i], &right, &far, &left); |
---|
1206 | if (fc & FC_BAG) { |
---|
1207 | tp->done = True; |
---|
1208 | if (MI_IS_VERBOSE(mi)) { |
---|
1209 | (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); |
---|
1210 | (void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG); |
---|
1211 | } |
---|
1212 | } |
---|
1213 | if (right) { |
---|
1214 | s = (((fc & FC_CUT_FAR) && (fc & FC_CUT_LEFT)) ? S_RIGHT : S_LEFT); |
---|
1215 | if (!legal_move(right, s, VT_RIGHT(vtypes[i]))) { |
---|
1216 | no_good |= (1 << i); |
---|
1217 | n_good--; |
---|
1218 | continue; |
---|
1219 | } |
---|
1220 | } |
---|
1221 | if (left) { |
---|
1222 | s = (((fc & FC_CUT_FAR) && (fc & FC_CUT_RIGHT)) ? S_LEFT : S_RIGHT); |
---|
1223 | if (!legal_move(left, s, VT_LEFT(vtypes[i]))) { |
---|
1224 | no_good |= (1 << i); |
---|
1225 | n_good--; |
---|
1226 | continue; |
---|
1227 | } |
---|
1228 | } |
---|
1229 | if (far) { |
---|
1230 | s = ((fc & FC_CUT_LEFT) ? S_RIGHT : S_LEFT); |
---|
1231 | if (!legal_move(far, s, VT_FAR(vtypes[i]))) { |
---|
1232 | no_good |= (1 << i); |
---|
1233 | n_good--; |
---|
1234 | } |
---|
1235 | } |
---|
1236 | } |
---|
1237 | if (n_good <= 0) { |
---|
1238 | tp->done = True; |
---|
1239 | if (MI_IS_VERBOSE(mi)) { |
---|
1240 | (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); |
---|
1241 | (void) fprintf(stderr, "n_good = %d\n", n_good); |
---|
1242 | } |
---|
1243 | } |
---|
1244 | n = NRAND(n_good); |
---|
1245 | for (i = j = 0; i <= n; i++, j++) |
---|
1246 | while (no_good & (1 << j)) |
---|
1247 | j++; |
---|
1248 | |
---|
1249 | if (!add_tile(mi, vertex, side, vtypes[j - 1])) { |
---|
1250 | tp->done = True; |
---|
1251 | if (MI_IS_VERBOSE(mi)) { |
---|
1252 | (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); |
---|
1253 | } |
---|
1254 | free_penrose(tp); |
---|
1255 | } |
---|
1256 | } |
---|
1257 | |
---|
1258 | /* One step of the growth algorithm. */ |
---|
1259 | void |
---|
1260 | draw_penrose(ModeInfo * mi) |
---|
1261 | { |
---|
1262 | int i = 0, n; |
---|
1263 | forced_node_c *p; |
---|
1264 | tiling_c *tp; |
---|
1265 | |
---|
1266 | if (tilings == NULL) |
---|
1267 | return; |
---|
1268 | tp = &tilings[MI_SCREEN(mi)]; |
---|
1269 | if (tp->fringe.nodes == NULL) |
---|
1270 | return; |
---|
1271 | |
---|
1272 | MI_IS_DRAWN(mi) = True; |
---|
1273 | p = tp->forced.first; |
---|
1274 | if (tp->busyLoop > 0) { |
---|
1275 | tp->busyLoop--; |
---|
1276 | return; |
---|
1277 | } |
---|
1278 | if (tp->done || tp->failures >= 100) { |
---|
1279 | init_penrose(mi); |
---|
1280 | return; |
---|
1281 | } |
---|
1282 | /* Check for the initial "2-gon". */ |
---|
1283 | if (tp->fringe.nodes->prev == tp->fringe.nodes->next) { |
---|
1284 | vertex_type_c vtype = (unsigned char) (VT_TOTAL_MASK & LRAND()); |
---|
1285 | |
---|
1286 | MI_CLEARWINDOW(mi); |
---|
1287 | |
---|
1288 | if (!add_tile(mi, tp->fringe.nodes, S_LEFT, vtype)) |
---|
1289 | free_penrose(tp); |
---|
1290 | return; |
---|
1291 | } |
---|
1292 | /* No visible nodes left. */ |
---|
1293 | if (tp->fringe.n_nodes == 0) { |
---|
1294 | tp->done = True; |
---|
1295 | tp->busyLoop = COMPLETION; /* Just finished drawing */ |
---|
1296 | return; |
---|
1297 | } |
---|
1298 | if (tp->forced.n_visible > 0 && tp->failures < 10) { |
---|
1299 | n = NRAND(tp->forced.n_visible); |
---|
1300 | for (;;) { |
---|
1301 | while (p->vertex->off_screen) |
---|
1302 | p = p->next; |
---|
1303 | if (i++ < n) |
---|
1304 | p = p->next; |
---|
1305 | else |
---|
1306 | break; |
---|
1307 | } |
---|
1308 | } else if (tp->forced.n_nodes > 0) { |
---|
1309 | n = NRAND(tp->forced.n_nodes); |
---|
1310 | while (i++ < n) |
---|
1311 | p = p->next; |
---|
1312 | } else { |
---|
1313 | fringe_node_c *fringe_p = tp->fringe.nodes; |
---|
1314 | |
---|
1315 | n = NRAND(tp->fringe.n_nodes); |
---|
1316 | i = 0; |
---|
1317 | for (; i <= n; i++) |
---|
1318 | do { |
---|
1319 | fringe_p = fringe_p->next; |
---|
1320 | } while (fringe_p->off_screen); |
---|
1321 | add_random_tile(fringe_p, mi); |
---|
1322 | tp->failures = 0; |
---|
1323 | return; |
---|
1324 | } |
---|
1325 | if (add_forced_tile(mi, p)) |
---|
1326 | tp->failures = 0; |
---|
1327 | else |
---|
1328 | tp->failures++; |
---|
1329 | } |
---|
1330 | |
---|
1331 | |
---|
1332 | /* Total clean-up. */ |
---|
1333 | void |
---|
1334 | release_penrose(ModeInfo * mi) |
---|
1335 | { |
---|
1336 | if (tilings != NULL) { |
---|
1337 | int screen; |
---|
1338 | |
---|
1339 | for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) |
---|
1340 | free_penrose(&tilings[screen]); |
---|
1341 | (void) free((void *) tilings); |
---|
1342 | tilings = (tiling_c *) NULL; |
---|
1343 | } |
---|
1344 | } |
---|
1345 | |
---|
1346 | #endif /* MODE_penrose */ |
---|