A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 894 lines 27 kB view raw
1/* 2 * Generate Penrose tilings via combinatorial coordinates. 3 * 4 * For general explanation of the algorithm: 5 * https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/ 6 * 7 * I use exactly the same indexing system here that's described in the 8 * article. For the P2 tiling, acute isosceles triangles (half-kites) 9 * are assigned letters A,B, and obtuse ones (half-darts) U,V; for P3, 10 * acute triangles (half of a thin rhomb) are C,D and obtuse ones 11 * (half a thick rhomb) are X,Y. Edges of all triangles are indexed 12 * anticlockwise around the triangle, with 0 being the base and 1,2 13 * being the two equal legs. 14 */ 15 16#include <assert.h> 17#include <stddef.h> 18#include <string.h> 19 20#include "puzzles.h" 21#include "penrose.h" 22#include "penrose-internal.h" 23#include "tree234.h" 24 25bool penrose_valid_letter(char c, int which) 26{ 27 switch (c) { 28 case 'A': case 'B': case 'U': case 'V': 29 return which == PENROSE_P2; 30 case 'C': case 'D': case 'X': case 'Y': 31 return which == PENROSE_P3; 32 default: 33 return false; 34 } 35} 36 37/* 38 * Result of attempting a transition within the coordinate system. 39 * INTERNAL means we've moved to a different child of the same parent, 40 * so the 'internal' substructure gives the type of the new triangle 41 * and which edge of it we came in through; EXTERNAL means we've moved 42 * out of the parent entirely, and the 'external' substructure tells 43 * us which edge of the parent triangle we left by, and if it's 44 * divided in two, which end of that edge (-1 for the left end or +1 45 * for the right end). If the parent edge is undivided, end == 0. 46 * 47 * The type FAIL _shouldn't_ ever come up! It occurs if you try to 48 * compute an incoming transition with an illegal value of 'end' (i.e. 49 * having the wrong idea of whether the edge is divided), or if you 50 * refer to a child triangle type that doesn't exist in that parent. 51 * If it ever happens in the production code then an assertion will 52 * fail. But it might be useful to other users of the same code. 53 */ 54typedef struct TransitionResult { 55 enum { INTERNAL, EXTERNAL, FAIL } type; 56 union { 57 struct { 58 char new_child; 59 unsigned char new_edge; 60 } internal; 61 struct { 62 unsigned char parent_edge; 63 signed char end; 64 } external; 65 } u; 66} TransitionResult; 67 68/* Construction function to make an INTERNAL-type TransitionResult */ 69static inline TransitionResult internal(char new_child, unsigned new_edge) 70{ 71 TransitionResult tr; 72 tr.type = INTERNAL; 73 tr.u.internal.new_child = new_child; 74 tr.u.internal.new_edge = new_edge; 75 return tr; 76} 77 78/* Construction function to make an EXTERNAL-type TransitionResult */ 79static inline TransitionResult external(unsigned parent_edge, int end) 80{ 81 TransitionResult tr; 82 tr.type = EXTERNAL; 83 tr.u.external.parent_edge = parent_edge; 84 tr.u.external.end = end; 85 return tr; 86} 87 88/* Construction function to make a FAIL-type TransitionResult */ 89static inline TransitionResult fail(void) 90{ 91 TransitionResult tr; 92 tr.type = FAIL; 93 return tr; 94} 95 96/* 97 * Compute a transition out of a triangle. Can return either INTERNAL 98 * or EXTERNAL types (or FAIL if it gets invalid data). 99 */ 100static TransitionResult transition(char parent, char child, unsigned edge) 101{ 102 switch (parent) { 103 case 'A': 104 switch (child) { 105 case 'A': 106 switch (edge) { 107 case 0: return external(2, -1); 108 case 1: return external(0, 0); 109 case 2: return internal('B', 1); 110 } 111 case 'B': 112 switch (edge) { 113 case 0: return internal('U', 1); 114 case 1: return internal('A', 2); 115 case 2: return external(1, +1); 116 } 117 case 'U': 118 switch (edge) { 119 case 0: return external(2, +1); 120 case 1: return internal('B', 0); 121 case 2: return external(1, -1); 122 } 123 default: 124 return fail(); 125 } 126 case 'B': 127 switch (child) { 128 case 'A': 129 switch (edge) { 130 case 0: return internal('V', 2); 131 case 1: return external(2, -1); 132 case 2: return internal('B', 1); 133 } 134 case 'B': 135 switch (edge) { 136 case 0: return external(1, +1); 137 case 1: return internal('A', 2); 138 case 2: return external(0, 0); 139 } 140 case 'V': 141 switch (edge) { 142 case 0: return external(1, -1); 143 case 1: return external(2, +1); 144 case 2: return internal('A', 0); 145 } 146 default: 147 return fail(); 148 } 149 case 'U': 150 switch (child) { 151 case 'B': 152 switch (edge) { 153 case 0: return internal('U', 1); 154 case 1: return external(2, 0); 155 case 2: return external(0, +1); 156 } 157 case 'U': 158 switch (edge) { 159 case 0: return external(1, 0); 160 case 1: return internal('B', 0); 161 case 2: return external(0, -1); 162 } 163 default: 164 return fail(); 165 } 166 case 'V': 167 switch (child) { 168 case 'A': 169 switch (edge) { 170 case 0: return internal('V', 2); 171 case 1: return external(0, -1); 172 case 2: return external(1, 0); 173 } 174 case 'V': 175 switch (edge) { 176 case 0: return external(2, 0); 177 case 1: return external(0, +1); 178 case 2: return internal('A', 0); 179 } 180 default: 181 return fail(); 182 } 183 case 'C': 184 switch (child) { 185 case 'C': 186 switch (edge) { 187 case 0: return external(1, +1); 188 case 1: return internal('Y', 1); 189 case 2: return external(0, 0); 190 } 191 case 'Y': 192 switch (edge) { 193 case 0: return external(2, 0); 194 case 1: return internal('C', 1); 195 case 2: return external(1, -1); 196 } 197 default: 198 return fail(); 199 } 200 case 'D': 201 switch (child) { 202 case 'D': 203 switch (edge) { 204 case 0: return external(2, -1); 205 case 1: return external(0, 0); 206 case 2: return internal('X', 2); 207 } 208 case 'X': 209 switch (edge) { 210 case 0: return external(1, 0); 211 case 1: return external(2, +1); 212 case 2: return internal('D', 2); 213 } 214 default: 215 return fail(); 216 } 217 case 'X': 218 switch (child) { 219 case 'C': 220 switch (edge) { 221 case 0: return external(2, +1); 222 case 1: return internal('Y', 1); 223 case 2: return internal('X', 1); 224 } 225 case 'X': 226 switch (edge) { 227 case 0: return external(1, 0); 228 case 1: return internal('C', 2); 229 case 2: return external(0, -1); 230 } 231 case 'Y': 232 switch (edge) { 233 case 0: return external(0, +1); 234 case 1: return internal('C', 1); 235 case 2: return external(2, -1); 236 } 237 default: 238 return fail(); 239 } 240 case 'Y': 241 switch (child) { 242 case 'D': 243 switch (edge) { 244 case 0: return external(1, -1); 245 case 1: return internal('Y', 2); 246 case 2: return internal('X', 2); 247 } 248 case 'X': 249 switch (edge) { 250 case 0: return external(0, -1); 251 case 1: return external(1, +1); 252 case 2: return internal('D', 2); 253 } 254 case 'Y': 255 switch (edge) { 256 case 0: return external(2, 0); 257 case 1: return external(0, +1); 258 case 2: return internal('D', 1); 259 } 260 default: 261 return fail(); 262 } 263 default: 264 return fail(); 265 } 266} 267 268/* 269 * Compute a transition into a parent triangle, after the above 270 * function reported an EXTERNAL transition out of a neighbouring 271 * parent and we had to recurse. Because we're coming inwards, this 272 * should always return an INTERNAL TransitionResult (or FAIL if it 273 * gets invalid data). 274 */ 275static TransitionResult transition_in(char parent, unsigned edge, int end) 276{ 277 #define EDGEEND(edge, end) (3 * (edge) + 1 + (end)) 278 279 switch (parent) { 280 case 'A': 281 switch (EDGEEND(edge, end)) { 282 case EDGEEND(0, 0): return internal('A', 1); 283 case EDGEEND(1, -1): return internal('B', 2); 284 case EDGEEND(1, +1): return internal('U', 2); 285 case EDGEEND(2, -1): return internal('U', 0); 286 case EDGEEND(2, +1): return internal('A', 0); 287 default: 288 return fail(); 289 } 290 case 'B': 291 switch (EDGEEND(edge, end)) { 292 case EDGEEND(0, 0): return internal('B', 2); 293 case EDGEEND(1, -1): return internal('B', 0); 294 case EDGEEND(1, +1): return internal('V', 0); 295 case EDGEEND(2, -1): return internal('V', 1); 296 case EDGEEND(2, +1): return internal('A', 1); 297 default: 298 return fail(); 299 } 300 case 'U': 301 switch (EDGEEND(edge, end)) { 302 case EDGEEND(0, -1): return internal('B', 2); 303 case EDGEEND(0, +1): return internal('U', 2); 304 case EDGEEND(1, 0): return internal('U', 0); 305 case EDGEEND(2, 0): return internal('B', 1); 306 default: 307 return fail(); 308 } 309 case 'V': 310 switch (EDGEEND(edge, end)) { 311 case EDGEEND(0, -1): return internal('V', 1); 312 case EDGEEND(0, +1): return internal('A', 1); 313 case EDGEEND(1, 0): return internal('A', 2); 314 case EDGEEND(2, 0): return internal('V', 0); 315 default: 316 return fail(); 317 } 318 case 'C': 319 switch (EDGEEND(edge, end)) { 320 case EDGEEND(0, 0): return internal('C', 2); 321 case EDGEEND(1, -1): return internal('C', 0); 322 case EDGEEND(1, +1): return internal('Y', 2); 323 case EDGEEND(2, 0): return internal('Y', 0); 324 default: 325 return fail(); 326 } 327 case 'D': 328 switch (EDGEEND(edge, end)) { 329 case EDGEEND(0, 0): return internal('D', 1); 330 case EDGEEND(1, 0): return internal('X', 0); 331 case EDGEEND(2, -1): return internal('X', 1); 332 case EDGEEND(2, +1): return internal('D', 0); 333 default: 334 return fail(); 335 } 336 case 'X': 337 switch (EDGEEND(edge, end)) { 338 case EDGEEND(0, -1): return internal('Y', 0); 339 case EDGEEND(0, +1): return internal('X', 2); 340 case EDGEEND(1, 0): return internal('X', 0); 341 case EDGEEND(2, -1): return internal('C', 0); 342 case EDGEEND(2, +1): return internal('Y', 2); 343 default: 344 return fail(); 345 } 346 case 'Y': 347 switch (EDGEEND(edge, end)) { 348 case EDGEEND(0, +1): return internal('X', 0); 349 case EDGEEND(0, -1): return internal('Y', 1); 350 case EDGEEND(1, -1): return internal('X', 1); 351 case EDGEEND(1, +1): return internal('D', 0); 352 case EDGEEND(2, 0): return internal('Y', 0); 353 default: 354 return fail(); 355 } 356 default: 357 return fail(); 358 } 359 360 #undef EDGEEND 361} 362 363PenroseCoords *penrose_coords_new(void) 364{ 365 PenroseCoords *pc = snew(PenroseCoords); 366 pc->nc = pc->csize = 0; 367 pc->c = NULL; 368 return pc; 369} 370 371void penrose_coords_free(PenroseCoords *pc) 372{ 373 if (pc) { 374 sfree(pc->c); 375 sfree(pc); 376 } 377} 378 379void penrose_coords_make_space(PenroseCoords *pc, size_t size) 380{ 381 if (pc->csize < size) { 382 pc->csize = pc->csize * 5 / 4 + 16; 383 if (pc->csize < size) 384 pc->csize = size; 385 pc->c = sresize(pc->c, pc->csize, char); 386 } 387} 388 389PenroseCoords *penrose_coords_copy(PenroseCoords *pc_in) 390{ 391 PenroseCoords *pc_out = penrose_coords_new(); 392 penrose_coords_make_space(pc_out, pc_in->nc); 393 memcpy(pc_out->c, pc_in->c, pc_in->nc * sizeof(*pc_out->c)); 394 pc_out->nc = pc_in->nc; 395 return pc_out; 396} 397 398/* 399 * The main recursive function for computing the next triangle's 400 * combinatorial coordinates. 401 */ 402static void penrosectx_step_recurse( 403 PenroseContext *ctx, PenroseCoords *pc, size_t depth, 404 unsigned edge, unsigned *outedge) 405{ 406 TransitionResult tr; 407 408 penrosectx_extend_coords(ctx, pc, depth+2); 409 410 /* Look up the transition out of the starting triangle */ 411 tr = transition(pc->c[depth+1], pc->c[depth], edge); 412 413 /* If we've left the parent triangle, recurse to find out what new 414 * triangle we've landed in at the next size up, and then call 415 * transition_in to find out which child of that parent we're 416 * going to */ 417 if (tr.type == EXTERNAL) { 418 unsigned parent_outedge; 419 penrosectx_step_recurse( 420 ctx, pc, depth+1, tr.u.external.parent_edge, &parent_outedge); 421 tr = transition_in(pc->c[depth+1], parent_outedge, tr.u.external.end); 422 } 423 424 /* Now we should definitely have ended up in a child of the 425 * (perhaps rewritten) parent triangle */ 426 assert(tr.type == INTERNAL); 427 pc->c[depth] = tr.u.internal.new_child; 428 *outedge = tr.u.internal.new_edge; 429} 430 431void penrosectx_step(PenroseContext *ctx, PenroseCoords *pc, 432 unsigned edge, unsigned *outedge) 433{ 434 /* Allow outedge to be NULL harmlessly, just in case */ 435 unsigned dummy_outedge; 436 if (!outedge) 437 outedge = &dummy_outedge; 438 439 penrosectx_step_recurse(ctx, pc, 0, edge, outedge); 440} 441 442static Point penrose_triangle_post_edge(char c, unsigned edge) 443{ 444 static const Point acute_post_edge[3] = { 445 {{-1, 1, 0, 1}}, /* phi * t^3 */ 446 {{-1, 1, -1, 1}}, /* t^4 */ 447 {{-1, 1, 0, 0}}, /* 1/phi * t^3 */ 448 }; 449 static const Point obtuse_post_edge[3] = { 450 {{0, -1, 1, 0}}, /* 1/phi * t^4 */ 451 {{0, 0, 1, 0}}, /* t^2 */ 452 {{-1, 0, 0, 1}}, /* phi * t^4 */ 453 }; 454 455 switch (c) { 456 case 'A': case 'B': case 'C': case 'D': 457 return acute_post_edge[edge]; 458 default: /* case 'U': case 'V': case 'X': case 'Y': */ 459 return obtuse_post_edge[edge]; 460 } 461} 462 463void penrose_place(PenroseTriangle *tri, Point u, Point v, int index_of_u) 464{ 465 unsigned i; 466 Point here = u, delta = point_sub(v, u); 467 468 for (i = 0; i < 3; i++) { 469 unsigned edge = (index_of_u + i) % 3; 470 tri->vertices[edge] = here; 471 here = point_add(here, delta); 472 delta = point_mul(delta, penrose_triangle_post_edge( 473 tri->pc->c[0], edge)); 474 } 475} 476 477void penrose_free(PenroseTriangle *tri) 478{ 479 penrose_coords_free(tri->pc); 480 sfree(tri); 481} 482 483static unsigned long penrose_relative_probability(char c) 484{ 485 /* Penrose tile probability ratios are always phi, so we can 486 * approximate that very well using two consecutive Fibonacci 487 * numbers */ 488 switch (c) { 489 case 'A': case 'B': case 'X': case 'Y': 490 return 63245986; 491 case 'C': case 'D': case 'U': case 'V': 492 return 39088169; 493 default: 494 return 0; 495 } 496} 497 498static char penrose_choose_random(const char *possibilities, random_state *rs) 499{ 500 const char *p; 501 unsigned long value, limit = 0; 502 503 for (p = possibilities; *p; p++) 504 limit += penrose_relative_probability(*p); 505 value = random_upto(rs, limit); 506 for (p = possibilities; *p; p++) { 507 unsigned long curr = penrose_relative_probability(*p); 508 if (value < curr) 509 return *p; 510 value -= curr; 511 } 512 assert(false && "Probability overflow!"); 513 return possibilities[0]; 514} 515 516static const char *penrose_starting_tiles(int which) 517{ 518 return which == PENROSE_P2 ? "ABUV" : "CDXY"; 519} 520 521static const char *penrose_valid_parents(char tile) 522{ 523 switch (tile) { 524 case 'A': return "ABV"; 525 case 'B': return "ABU"; 526 case 'U': return "AU"; 527 case 'V': return "BV"; 528 case 'C': return "CX"; 529 case 'D': return "DY"; 530 case 'X': return "DXY"; 531 case 'Y': return "CXY"; 532 default: return NULL; 533 } 534} 535 536void penrosectx_init_random(PenroseContext *ctx, random_state *rs, int which) 537{ 538 ctx->rs = rs; 539 ctx->must_free_rs = false; 540 ctx->prototype = penrose_coords_new(); 541 penrose_coords_make_space(ctx->prototype, 1); 542 ctx->prototype->c[0] = penrose_choose_random( 543 penrose_starting_tiles(which), rs); 544 ctx->prototype->nc = 1; 545 ctx->start_vertex = random_upto(rs, 3); 546 ctx->orientation = random_upto(rs, 10); 547} 548 549void penrosectx_init_from_params( 550 PenroseContext *ctx, const struct PenrosePatchParams *ps) 551{ 552 size_t i; 553 554 ctx->rs = NULL; 555 ctx->must_free_rs = false; 556 ctx->prototype = penrose_coords_new(); 557 penrose_coords_make_space(ctx->prototype, ps->ncoords); 558 for (i = 0; i < ps->ncoords; i++) 559 ctx->prototype->c[i] = ps->coords[i]; 560 ctx->prototype->nc = ps->ncoords; 561 ctx->start_vertex = ps->start_vertex; 562 ctx->orientation = ps->orientation; 563} 564 565void penrosectx_cleanup(PenroseContext *ctx) 566{ 567 if (ctx->must_free_rs) 568 random_free(ctx->rs); 569 penrose_coords_free(ctx->prototype); 570} 571 572PenroseCoords *penrosectx_initial_coords(PenroseContext *ctx) 573{ 574 return penrose_coords_copy(ctx->prototype); 575} 576 577void penrosectx_extend_coords(PenroseContext *ctx, PenroseCoords *pc, 578 size_t n) 579{ 580 if (ctx->prototype->nc < n) { 581 penrose_coords_make_space(ctx->prototype, n); 582 while (ctx->prototype->nc < n) { 583 if (!ctx->rs) { 584 /* 585 * For safety, similarly to spectre.c, we respond to a 586 * lack of available random_state by making a 587 * deterministic one. 588 */ 589 ctx->rs = random_new("dummy", 5); 590 ctx->must_free_rs = true; 591 } 592 593 ctx->prototype->c[ctx->prototype->nc] = penrose_choose_random( 594 penrose_valid_parents(ctx->prototype->c[ctx->prototype->nc-1]), 595 ctx->rs); 596 ctx->prototype->nc++; 597 } 598 } 599 600 penrose_coords_make_space(pc, n); 601 while (pc->nc < n) { 602 pc->c[pc->nc] = ctx->prototype->c[pc->nc]; 603 pc->nc++; 604 } 605} 606 607static Point penrose_triangle_edge_0_length(char c) 608{ 609 static const Point one = {{ 1, 0, 0, 0 }}; 610 static const Point phi = {{ 1, 0, 1, -1 }}; 611 static const Point invphi = {{ 0, 0, 1, -1 }}; 612 613 switch (c) { 614 /* P2 tiling: unit-length edges are the long edges, i.e. edges 615 * 1,2 of AB and edge 0 of UV. So AB have edge 0 short. */ 616 case 'A': case 'B': 617 return invphi; 618 case 'U': case 'V': 619 return one; 620 621 /* P3 tiling: unit-length edges are edges 1,2 of everything, 622 * so CD have edge 0 short and XY have it long. */ 623 case 'C': case 'D': 624 return invphi; 625 default: /* case 'X': case 'Y': */ 626 return phi; 627 } 628} 629 630PenroseTriangle *penrose_initial(PenroseContext *ctx) 631{ 632 char type = ctx->prototype->c[0]; 633 Point origin = {{ 0, 0, 0, 0 }}; 634 Point edge0 = penrose_triangle_edge_0_length(type); 635 Point negoffset; 636 size_t i; 637 PenroseTriangle *tri = snew(PenroseTriangle); 638 639 /* Orient the triangle by deciding what vector edge #0 should traverse */ 640 edge0 = point_mul(edge0, point_rot(ctx->orientation)); 641 642 /* Place the triangle at an arbitrary position, in that orientation */ 643 tri->pc = penrose_coords_copy(ctx->prototype); 644 penrose_place(tri, origin, edge0, 0); 645 646 /* Now translate so that the appropriate vertex is at the origin */ 647 negoffset = tri->vertices[ctx->start_vertex]; 648 for (i = 0; i < 3; i++) 649 tri->vertices[i] = point_sub(tri->vertices[i], negoffset); 650 651 return tri; 652} 653 654PenroseTriangle *penrose_adjacent(PenroseContext *ctx, 655 const PenroseTriangle *src_tri, 656 unsigned src_edge, unsigned *dst_edge_out) 657{ 658 unsigned dst_edge; 659 PenroseTriangle *dst_tri = snew(PenroseTriangle); 660 dst_tri->pc = penrose_coords_copy(src_tri->pc); 661 penrosectx_step(ctx, dst_tri->pc, src_edge, &dst_edge); 662 penrose_place(dst_tri, src_tri->vertices[(src_edge+1) % 3], 663 src_tri->vertices[src_edge], dst_edge); 664 if (dst_edge_out) 665 *dst_edge_out = dst_edge; 666 return dst_tri; 667} 668 669static int penrose_cmp(void *av, void *bv) 670{ 671 PenroseTriangle *a = (PenroseTriangle *)av, *b = (PenroseTriangle *)bv; 672 size_t i, j; 673 674 /* We should only ever need to compare the first two vertices of 675 * any triangle, because those force the rest */ 676 for (i = 0; i < 2; i++) { 677 for (j = 0; j < 4; j++) { 678 int ac = a->vertices[i].coeffs[j], bc = b->vertices[i].coeffs[j]; 679 if (ac < bc) 680 return -1; 681 if (ac > bc) 682 return +1; 683 } 684 } 685 686 return 0; 687} 688 689static unsigned penrose_sibling_edge_index(char c) 690{ 691 switch (c) { 692 case 'A': case 'U': return 2; 693 case 'B': case 'V': return 1; 694 default: return 0; 695 } 696} 697 698void penrosectx_generate( 699 PenroseContext *ctx, 700 bool (*inbounds)(void *inboundsctx, 701 const PenroseTriangle *tri), void *inboundsctx, 702 void (*tile)(void *tilectx, const Point *vertices), void *tilectx) 703{ 704 tree234 *placed = newtree234(penrose_cmp); 705 PenroseTriangle *qhead = NULL, *qtail = NULL; 706 707 { 708 PenroseTriangle *tri = penrose_initial(ctx); 709 710 add234(placed, tri); 711 712 tri->next = NULL; 713 tri->reported = false; 714 715 if (inbounds(inboundsctx, tri)) 716 qhead = qtail = tri; 717 } 718 719 while (qhead) { 720 PenroseTriangle *tri = qhead; 721 unsigned edge; 722 unsigned sibling_edge = penrose_sibling_edge_index(tri->pc->c[0]); 723 724 for (edge = 0; edge < 3; edge++) { 725 PenroseTriangle *new_tri, *found_tri; 726 727 new_tri = penrose_adjacent(ctx, tri, edge, NULL); 728 729 if (!inbounds(inboundsctx, new_tri)) { 730 penrose_free(new_tri); 731 continue; 732 } 733 734 found_tri = find234(placed, new_tri, NULL); 735 if (found_tri) { 736 if (edge == sibling_edge && !tri->reported && 737 !found_tri->reported) { 738 /* 739 * found_tri and tri are opposite halves of the 740 * same tile; both are in the tree, and haven't 741 * yet been reported as a completed tile. 742 */ 743 unsigned new_sibling_edge = penrose_sibling_edge_index( 744 found_tri->pc->c[0]); 745 Point tilevertices[4] = { 746 tri->vertices[(sibling_edge + 1) % 3], 747 tri->vertices[(sibling_edge + 2) % 3], 748 found_tri->vertices[(new_sibling_edge + 1) % 3], 749 found_tri->vertices[(new_sibling_edge + 2) % 3], 750 }; 751 tile(tilectx, tilevertices); 752 753 tri->reported = true; 754 found_tri->reported = true; 755 } 756 757 penrose_free(new_tri); 758 continue; 759 } 760 761 add234(placed, new_tri); 762 qtail->next = new_tri; 763 qtail = new_tri; 764 new_tri->next = NULL; 765 new_tri->reported = false; 766 } 767 768 qhead = qhead->next; 769 } 770 771 { 772 PenroseTriangle *tri; 773 while ((tri = delpos234(placed, 0)) != NULL) 774 penrose_free(tri); 775 freetree234(placed); 776 } 777} 778 779const char *penrose_tiling_params_invalid( 780 const struct PenrosePatchParams *params, int which) 781{ 782 size_t i; 783 784 if (params->ncoords == 0) 785 return "expected at least one coordinate"; 786 787 for (i = 0; i < params->ncoords; i++) { 788 if (!penrose_valid_letter(params->coords[i], which)) 789 return "invalid coordinate letter"; 790 if (i > 0 && !strchr(penrose_valid_parents(params->coords[i-1]), 791 params->coords[i])) 792 return "invalid pair of consecutive coordinates"; 793 } 794 795 return NULL; 796} 797 798struct PenroseOutputCtx { 799 int xoff, yoff; 800 Coord xmin, xmax, ymin, ymax; 801 802 penrose_tile_callback_fn external_cb; 803 void *external_cbctx; 804}; 805 806static bool inbounds(void *vctx, const PenroseTriangle *tri) 807{ 808 struct PenroseOutputCtx *octx = (struct PenroseOutputCtx *)vctx; 809 size_t i; 810 811 for (i = 0; i < 3; i++) { 812 Coord x = point_x(tri->vertices[i]); 813 Coord y = point_y(tri->vertices[i]); 814 815 if (coord_cmp(x, octx->xmin) < 0 || coord_cmp(x, octx->xmax) > 0 || 816 coord_cmp(y, octx->ymin) < 0 || coord_cmp(y, octx->ymax) > 0) 817 return false; 818 } 819 820 return true; 821} 822 823static void null_output_tile(void *vctx, const Point *vertices) 824{ 825} 826 827static void really_output_tile(void *vctx, const Point *vertices) 828{ 829 struct PenroseOutputCtx *octx = (struct PenroseOutputCtx *)vctx; 830 size_t i; 831 int coords[16]; 832 833 for (i = 0; i < 4; i++) { 834 Coord x = point_x(vertices[i]); 835 Coord y = point_y(vertices[i]); 836 837 coords[4*i + 0] = x.c1 + octx->xoff; 838 coords[4*i + 1] = x.cr5; 839 coords[4*i + 2] = y.c1 + octx->yoff; 840 coords[4*i + 3] = y.cr5; 841 } 842 843 octx->external_cb(octx->external_cbctx, coords); 844} 845 846static void penrose_set_bounds(struct PenroseOutputCtx *octx, int w, int h) 847{ 848 octx->xoff = w/2; 849 octx->yoff = h/2; 850 octx->xmin.c1 = -octx->xoff; 851 octx->xmax.c1 = -octx->xoff + w; 852 octx->ymin.c1 = octx->yoff - h; 853 octx->ymax.c1 = octx->yoff; 854 octx->xmin.cr5 = 0; 855 octx->xmax.cr5 = 0; 856 octx->ymin.cr5 = 0; 857 octx->ymax.cr5 = 0; 858} 859 860void penrose_tiling_randomise(struct PenrosePatchParams *params, int which, 861 int w, int h, random_state *rs) 862{ 863 PenroseContext ctx[1]; 864 struct PenroseOutputCtx octx[1]; 865 866 penrose_set_bounds(octx, w, h); 867 868 penrosectx_init_random(ctx, rs, which); 869 penrosectx_generate(ctx, inbounds, octx, null_output_tile, NULL); 870 871 params->orientation = ctx->orientation; 872 params->start_vertex = ctx->start_vertex; 873 params->ncoords = ctx->prototype->nc; 874 params->coords = snewn(params->ncoords, char); 875 memcpy(params->coords, ctx->prototype->c, params->ncoords); 876 877 penrosectx_cleanup(ctx); 878} 879 880void penrose_tiling_generate( 881 const struct PenrosePatchParams *params, int w, int h, 882 penrose_tile_callback_fn cb, void *cbctx) 883{ 884 PenroseContext ctx[1]; 885 struct PenroseOutputCtx octx[1]; 886 887 penrose_set_bounds(octx, w, h); 888 octx->external_cb = cb; 889 octx->external_cbctx = cbctx; 890 891 penrosectx_init_from_params(ctx, params); 892 penrosectx_generate(ctx, inbounds, octx, really_output_tile, octx); 893 penrosectx_cleanup(ctx); 894}