| 1 | /* penrose.c |
| 2 | * |
| 3 | * Penrose tile generator. |
| 4 | * |
| 5 | * Uses half-tile technique outlined on: |
| 6 | * |
| 7 | * http://tartarus.org/simon/20110412-penrose/penrose.xhtml |
| 8 | */ |
| 9 | |
| 10 | #include <assert.h> |
| 11 | #include <string.h> |
| 12 | #include <math.h> |
| 13 | #include <stdio.h> |
| 14 | |
| 15 | #include "puzzles.h" /* for malloc routines, and PI */ |
| 16 | #include "penrose.h" |
| 17 | |
| 18 | /* ------------------------------------------------------- |
| 19 | * 36-degree basis vector arithmetic routines. |
| 20 | */ |
| 21 | |
| 22 | /* Imagine drawing a |
| 23 | * ten-point 'clock face' like this: |
| 24 | * |
| 25 | * -E |
| 26 | * -D | A |
| 27 | * \ | / |
| 28 | * -C. \ | / ,B |
| 29 | * `-._\|/_,-' |
| 30 | * ,-' /|\ `-. |
| 31 | * -B' / | \ `C |
| 32 | * / | \ |
| 33 | * -A | D |
| 34 | * E |
| 35 | * |
| 36 | * In case the ASCII art isn't clear, those are supposed to be ten |
| 37 | * vectors of length 1, all sticking out from the origin at equal |
| 38 | * angular spacing (hence 36 degrees). Our basis vectors are A,B,C,D (I |
| 39 | * choose them to be symmetric about the x-axis so that the final |
| 40 | * translation into 2d coordinates will also be symmetric, which I |
| 41 | * think will avoid minor rounding uglinesses), so our vector |
| 42 | * representation sets |
| 43 | * |
| 44 | * A = (1,0,0,0) |
| 45 | * B = (0,1,0,0) |
| 46 | * C = (0,0,1,0) |
| 47 | * D = (0,0,0,1) |
| 48 | * |
| 49 | * The fifth vector E looks at first glance as if it needs to be |
| 50 | * another basis vector, but in fact it doesn't, because it can be |
| 51 | * represented in terms of the other four. Imagine starting from the |
| 52 | * origin and following the path -A, +B, -C, +D: you'll find you've |
| 53 | * traced four sides of a pentagram, and ended up one E-vector away |
| 54 | * from the origin. So we have |
| 55 | * |
| 56 | * E = (-1,1,-1,1) |
| 57 | * |
| 58 | * This tells us that we can rotate any vector in this system by 36 |
| 59 | * degrees: if we start with a*A + b*B + c*C + d*D, we want to end up |
| 60 | * with a*B + b*C + c*D + d*E, and we substitute our identity for E to |
| 61 | * turn that into a*B + b*C + c*D + d*(-A+B-C+D). In other words, |
| 62 | * |
| 63 | * rotate_one_notch_clockwise(a,b,c,d) = (-d, d+a, -d+b, d+c) |
| 64 | * |
| 65 | * and you can verify for yourself that applying that operation |
| 66 | * repeatedly starting with (1,0,0,0) cycles round ten vectors and |
| 67 | * comes back to where it started. |
| 68 | * |
| 69 | * The other operation that may be required is to construct vectors |
| 70 | * with lengths that are multiples of phi. That can be done by |
| 71 | * observing that the vector C-B is parallel to E and has length 1/phi, |
| 72 | * and the vector D-A is parallel to E and has length phi. So this |
| 73 | * tells us that given any vector, we can construct one which points in |
| 74 | * the same direction and is 1/phi or phi times its length, like this: |
| 75 | * |
| 76 | * divide_by_phi(vector) = rotate(vector, 2) - rotate(vector, 3) |
| 77 | * multiply_by_phi(vector) = rotate(vector, 1) - rotate(vector, 4) |
| 78 | * |
| 79 | * where rotate(vector, n) means applying the above |
| 80 | * rotate_one_notch_clockwise primitive n times. Expanding out the |
| 81 | * applications of rotate gives the following direct representation in |
| 82 | * terms of the vector coordinates: |
| 83 | * |
| 84 | * divide_by_phi(a,b,c,d) = (b-d, c+d-b, a+b-c, c-a) |
| 85 | * multiply_by_phi(a,b,c,d) = (a+b-d, c+d, a+b, c+d-a) |
| 86 | * |
| 87 | * and you can verify for yourself that those two operations are |
| 88 | * inverses of each other (as you'd hope!). |
| 89 | * |
| 90 | * Having done all of this, testing for equality between two vectors is |
| 91 | * a trivial matter of comparing the four integer coordinates. (Which |
| 92 | * it _wouldn't_ have been if we'd kept E as a fifth basis vector, |
| 93 | * because then (-1,1,-1,1,0) and (0,0,0,0,1) would have had to be |
| 94 | * considered identical. So leaving E out is vital.) |
| 95 | */ |
| 96 | |
| 97 | struct vector { int a, b, c, d; }; |
| 98 | |
| 99 | static vector v_origin(void) |
| 100 | { |
| 101 | vector v; |
| 102 | v.a = v.b = v.c = v.d = 0; |
| 103 | return v; |
| 104 | } |
| 105 | |
| 106 | /* We start with a unit vector of B: this means we can easily |
| 107 | * draw an isoceles triangle centred on the X axis. */ |
| 108 | #ifdef TEST_VECTORS |
| 109 | |
| 110 | static vector v_unit(void) |
| 111 | { |
| 112 | vector v; |
| 113 | |
| 114 | v.b = 1; |
| 115 | v.a = v.c = v.d = 0; |
| 116 | return v; |
| 117 | } |
| 118 | |
| 119 | #endif |
| 120 | |
| 121 | #define COS54 0.5877852 |
| 122 | #define SIN54 0.8090169 |
| 123 | #define COS18 0.9510565 |
| 124 | #define SIN18 0.3090169 |
| 125 | |
| 126 | /* These two are a bit rough-and-ready for now. Note that B/C are |
| 127 | * 18 degrees from the x-axis, and A/D are 54 degrees. */ |
| 128 | double v_x(vector *vs, int i) |
| 129 | { |
| 130 | return (vs[i].a + vs[i].d) * COS54 + |
| 131 | (vs[i].b + vs[i].c) * COS18; |
| 132 | } |
| 133 | |
| 134 | double v_y(vector *vs, int i) |
| 135 | { |
| 136 | return (vs[i].a - vs[i].d) * SIN54 + |
| 137 | (vs[i].b - vs[i].c) * SIN18; |
| 138 | |
| 139 | } |
| 140 | |
| 141 | static vector v_trans(vector v, vector trans) |
| 142 | { |
| 143 | v.a += trans.a; |
| 144 | v.b += trans.b; |
| 145 | v.c += trans.c; |
| 146 | v.d += trans.d; |
| 147 | return v; |
| 148 | } |
| 149 | |
| 150 | static vector v_rotate_36(vector v) |
| 151 | { |
| 152 | vector vv; |
| 153 | vv.a = -v.d; |
| 154 | vv.b = v.d + v.a; |
| 155 | vv.c = -v.d + v.b; |
| 156 | vv.d = v.d + v.c; |
| 157 | return vv; |
| 158 | } |
| 159 | |
| 160 | static vector v_rotate(vector v, int ang) |
| 161 | { |
| 162 | int i; |
| 163 | |
| 164 | assert((ang % 36) == 0); |
| 165 | while (ang < 0) ang += 360; |
| 166 | ang = 360-ang; |
| 167 | for (i = 0; i < (ang/36); i++) |
| 168 | v = v_rotate_36(v); |
| 169 | return v; |
| 170 | } |
| 171 | |
| 172 | #ifdef TEST_VECTORS |
| 173 | |
| 174 | static vector v_scale(vector v, int sc) |
| 175 | { |
| 176 | v.a *= sc; |
| 177 | v.b *= sc; |
| 178 | v.c *= sc; |
| 179 | v.d *= sc; |
| 180 | return v; |
| 181 | } |
| 182 | |
| 183 | #endif |
| 184 | |
| 185 | static vector v_growphi(vector v) |
| 186 | { |
| 187 | vector vv; |
| 188 | vv.a = v.a + v.b - v.d; |
| 189 | vv.b = v.c + v.d; |
| 190 | vv.c = v.a + v.b; |
| 191 | vv.d = v.c + v.d - v.a; |
| 192 | return vv; |
| 193 | } |
| 194 | |
| 195 | static vector v_shrinkphi(vector v) |
| 196 | { |
| 197 | vector vv; |
| 198 | vv.a = v.b - v.d; |
| 199 | vv.b = v.c + v.d - v.b; |
| 200 | vv.c = v.a + v.b - v.c; |
| 201 | vv.d = v.c - v.a; |
| 202 | return vv; |
| 203 | } |
| 204 | |
| 205 | #ifdef TEST_VECTORS |
| 206 | |
| 207 | static const char *v_debug(vector v) |
| 208 | { |
| 209 | static char buf[255]; |
| 210 | sprintf(buf, |
| 211 | "(%d,%d,%d,%d)[%2.2f,%2.2f]", |
| 212 | v.a, v.b, v.c, v.d, v_x(&v,0), v_y(&v,0)); |
| 213 | return buf; |
| 214 | } |
| 215 | |
| 216 | #endif |
| 217 | |
| 218 | /* ------------------------------------------------------- |
| 219 | * Tiling routines. |
| 220 | */ |
| 221 | |
| 222 | static vector xform_coord(vector vo, int shrink, vector vtrans, int ang) |
| 223 | { |
| 224 | if (shrink < 0) |
| 225 | vo = v_shrinkphi(vo); |
| 226 | else if (shrink > 0) |
| 227 | vo = v_growphi(vo); |
| 228 | |
| 229 | vo = v_rotate(vo, ang); |
| 230 | vo = v_trans(vo, vtrans); |
| 231 | |
| 232 | return vo; |
| 233 | } |
| 234 | |
| 235 | |
| 236 | #define XFORM(n,o,s,a) vs[(n)] = xform_coord(v_edge, (s), vs[(o)], (a)) |
| 237 | |
| 238 | static int penrose_p2_small(penrose_state *state, int depth, int flip, |
| 239 | vector v_orig, vector v_edge); |
| 240 | |
| 241 | static int penrose_p2_large(penrose_state *state, int depth, int flip, |
| 242 | vector v_orig, vector v_edge) |
| 243 | { |
| 244 | vector vv_orig, vv_edge; |
| 245 | |
| 246 | #ifdef DEBUG_PENROSE |
| 247 | { |
| 248 | vector vs[3]; |
| 249 | vs[0] = v_orig; |
| 250 | XFORM(1, 0, 0, 0); |
| 251 | XFORM(2, 0, 0, -36*flip); |
| 252 | |
| 253 | state->new_tile(state, vs, 3, depth); |
| 254 | } |
| 255 | #endif |
| 256 | |
| 257 | if (flip > 0) { |
| 258 | vector vs[4]; |
| 259 | |
| 260 | vs[0] = v_orig; |
| 261 | XFORM(1, 0, 0, -36); |
| 262 | XFORM(2, 0, 0, 0); |
| 263 | XFORM(3, 0, 0, 36); |
| 264 | |
| 265 | state->new_tile(state, vs, 4, depth); |
| 266 | } |
| 267 | if (depth >= state->max_depth) return 0; |
| 268 | |
| 269 | vv_orig = v_trans(v_orig, v_rotate(v_edge, -36*flip)); |
| 270 | vv_edge = v_rotate(v_edge, 108*flip); |
| 271 | |
| 272 | penrose_p2_small(state, depth+1, flip, |
| 273 | v_orig, v_shrinkphi(v_edge)); |
| 274 | |
| 275 | penrose_p2_large(state, depth+1, flip, |
| 276 | vv_orig, v_shrinkphi(vv_edge)); |
| 277 | penrose_p2_large(state, depth+1, -flip, |
| 278 | vv_orig, v_shrinkphi(vv_edge)); |
| 279 | |
| 280 | return 0; |
| 281 | } |
| 282 | |
| 283 | static int penrose_p2_small(penrose_state *state, int depth, int flip, |
| 284 | vector v_orig, vector v_edge) |
| 285 | { |
| 286 | vector vv_orig; |
| 287 | |
| 288 | #ifdef DEBUG_PENROSE |
| 289 | { |
| 290 | vector vs[3]; |
| 291 | vs[0] = v_orig; |
| 292 | XFORM(1, 0, 0, 0); |
| 293 | XFORM(2, 0, -1, -36*flip); |
| 294 | |
| 295 | state->new_tile(state, vs, 3, depth); |
| 296 | } |
| 297 | #endif |
| 298 | |
| 299 | if (flip > 0) { |
| 300 | vector vs[4]; |
| 301 | |
| 302 | vs[0] = v_orig; |
| 303 | XFORM(1, 0, 0, -72); |
| 304 | XFORM(2, 0, -1, -36); |
| 305 | XFORM(3, 0, 0, 0); |
| 306 | |
| 307 | state->new_tile(state, vs, 4, depth); |
| 308 | } |
| 309 | |
| 310 | if (depth >= state->max_depth) return 0; |
| 311 | |
| 312 | vv_orig = v_trans(v_orig, v_edge); |
| 313 | |
| 314 | penrose_p2_large(state, depth+1, -flip, |
| 315 | v_orig, v_shrinkphi(v_rotate(v_edge, -36*flip))); |
| 316 | |
| 317 | penrose_p2_small(state, depth+1, flip, |
| 318 | vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip))); |
| 319 | |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | static int penrose_p3_small(penrose_state *state, int depth, int flip, |
| 324 | vector v_orig, vector v_edge); |
| 325 | |
| 326 | static int penrose_p3_large(penrose_state *state, int depth, int flip, |
| 327 | vector v_orig, vector v_edge) |
| 328 | { |
| 329 | vector vv_orig; |
| 330 | |
| 331 | #ifdef DEBUG_PENROSE |
| 332 | { |
| 333 | vector vs[3]; |
| 334 | vs[0] = v_orig; |
| 335 | XFORM(1, 0, 1, 0); |
| 336 | XFORM(2, 0, 0, -36*flip); |
| 337 | |
| 338 | state->new_tile(state, vs, 3, depth); |
| 339 | } |
| 340 | #endif |
| 341 | |
| 342 | if (flip > 0) { |
| 343 | vector vs[4]; |
| 344 | |
| 345 | vs[0] = v_orig; |
| 346 | XFORM(1, 0, 0, -36); |
| 347 | XFORM(2, 0, 1, 0); |
| 348 | XFORM(3, 0, 0, 36); |
| 349 | |
| 350 | state->new_tile(state, vs, 4, depth); |
| 351 | } |
| 352 | if (depth >= state->max_depth) return 0; |
| 353 | |
| 354 | vv_orig = v_trans(v_orig, v_edge); |
| 355 | |
| 356 | penrose_p3_large(state, depth+1, -flip, |
| 357 | vv_orig, v_shrinkphi(v_rotate(v_edge, 180))); |
| 358 | |
| 359 | penrose_p3_small(state, depth+1, flip, |
| 360 | vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip))); |
| 361 | |
| 362 | vv_orig = v_trans(v_orig, v_growphi(v_edge)); |
| 363 | |
| 364 | penrose_p3_large(state, depth+1, flip, |
| 365 | vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip))); |
| 366 | |
| 367 | |
| 368 | return 0; |
| 369 | } |
| 370 | |
| 371 | static int penrose_p3_small(penrose_state *state, int depth, int flip, |
| 372 | vector v_orig, vector v_edge) |
| 373 | { |
| 374 | vector vv_orig; |
| 375 | |
| 376 | #ifdef DEBUG_PENROSE |
| 377 | { |
| 378 | vector vs[3]; |
| 379 | vs[0] = v_orig; |
| 380 | XFORM(1, 0, 0, 0); |
| 381 | XFORM(2, 0, 0, -36*flip); |
| 382 | |
| 383 | state->new_tile(state, vs, 3, depth); |
| 384 | } |
| 385 | #endif |
| 386 | |
| 387 | if (flip > 0) { |
| 388 | vector vs[4]; |
| 389 | |
| 390 | vs[0] = v_orig; |
| 391 | XFORM(1, 0, 0, -36); |
| 392 | XFORM(3, 0, 0, 0); |
| 393 | XFORM(2, 3, 0, -36); |
| 394 | |
| 395 | state->new_tile(state, vs, 4, depth); |
| 396 | } |
| 397 | if (depth >= state->max_depth) return 0; |
| 398 | |
| 399 | /* NB these two are identical to the first two of p3_large. */ |
| 400 | vv_orig = v_trans(v_orig, v_edge); |
| 401 | |
| 402 | penrose_p3_large(state, depth+1, -flip, |
| 403 | vv_orig, v_shrinkphi(v_rotate(v_edge, 180))); |
| 404 | |
| 405 | penrose_p3_small(state, depth+1, flip, |
| 406 | vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip))); |
| 407 | |
| 408 | return 0; |
| 409 | } |
| 410 | |
| 411 | /* ------------------------------------------------------- |
| 412 | * Utility routines. |
| 413 | */ |
| 414 | |
| 415 | double penrose_side_length(double start_size, int depth) |
| 416 | { |
| 417 | return start_size / pow(PHI, depth); |
| 418 | } |
| 419 | |
| 420 | void penrose_count_tiles(int depth, int *nlarge, int *nsmall) |
| 421 | { |
| 422 | /* Steal sgt's fibonacci thingummy. */ |
| 423 | } |
| 424 | |
| 425 | /* |
| 426 | * It turns out that an acute isosceles triangle with sides in ratio 1:phi:phi |
| 427 | * has an incentre which is conveniently 2*phi^-2 of the way from the apex to |
| 428 | * the base. Why's that convenient? Because: if we situate the incentre of the |
| 429 | * triangle at the origin, then we can place the apex at phi^-2 * (B+C), and |
| 430 | * the other two vertices at apex-B and apex-C respectively. So that's an acute |
| 431 | * triangle with its long sides of unit length, covering a circle about the |
| 432 | * origin of radius 1-(2*phi^-2), which is conveniently enough phi^-3. |
| 433 | * |
| 434 | * (later mail: this is an overestimate by about 5%) |
| 435 | */ |
| 436 | |
| 437 | int penrose(penrose_state *state, int which, int angle) |
| 438 | { |
| 439 | vector vo = v_origin(); |
| 440 | vector vb = v_origin(); |
| 441 | |
| 442 | vo.b = vo.c = -state->start_size; |
| 443 | vo = v_shrinkphi(v_shrinkphi(vo)); |
| 444 | |
| 445 | vb.b = state->start_size; |
| 446 | |
| 447 | vo = v_rotate(vo, angle); |
| 448 | vb = v_rotate(vb, angle); |
| 449 | |
| 450 | if (which == PENROSE_P2) |
| 451 | return penrose_p2_large(state, 0, 1, vo, vb); |
| 452 | else |
| 453 | return penrose_p3_small(state, 0, 1, vo, vb); |
| 454 | } |
| 455 | |
| 456 | /* |
| 457 | * We're asked for a MxN grid, which just means a tiling fitting into roughly |
| 458 | * an MxN space in some kind of reasonable unit - say, the side length of the |
| 459 | * two-arrow edges of the tiles. By some reasoning in a previous email, that |
| 460 | * means we want to pick some subarea of a circle of radius 3.11*sqrt(M^2+N^2). |
| 461 | * To cover that circle, we need to subdivide a triangle large enough that it |
| 462 | * contains a circle of that radius. |
| 463 | * |
| 464 | * Hence: start with those three vectors marking triangle vertices, scale them |
| 465 | * all up by phi repeatedly until the radius of the inscribed circle gets |
| 466 | * bigger than the target, and then recurse into that triangle with the same |
| 467 | * recursion depth as the number of times you scaled up. That will give you |
| 468 | * tiles of unit side length, covering a circle big enough that if you randomly |
| 469 | * choose an orientation and coordinates within the circle, you'll be able to |
| 470 | * get any valid piece of Penrose tiling of size MxN. |
| 471 | */ |
| 472 | #define INCIRCLE_RADIUS 0.22426 /* phi^-3 less 5%: see above */ |
| 473 | |
| 474 | void penrose_calculate_size(int which, int tilesize, int w, int h, |
| 475 | double *required_radius, int *start_size, int *depth) |
| 476 | { |
| 477 | double rradius, size; |
| 478 | int n = 0; |
| 479 | |
| 480 | /* |
| 481 | * Fudge factor to scale P2 and P3 tilings differently. This |
| 482 | * doesn't seem to have much relevance to questions like the |
| 483 | * average number of tiles per unit area; it's just aesthetic. |
| 484 | */ |
| 485 | if (which == PENROSE_P2) |
| 486 | tilesize = tilesize * 3 / 2; |
| 487 | else |
| 488 | tilesize = tilesize * 5 / 4; |
| 489 | |
| 490 | rradius = tilesize * 3.11 * sqrt((double)(w*w + h*h)); |
| 491 | size = tilesize; |
| 492 | |
| 493 | while ((size * INCIRCLE_RADIUS) < rradius) { |
| 494 | n++; |
| 495 | size = size * PHI; |
| 496 | } |
| 497 | |
| 498 | *start_size = (int)size; |
| 499 | *depth = n; |
| 500 | *required_radius = rradius; |
| 501 | } |
| 502 | |
| 503 | /* ------------------------------------------------------- |
| 504 | * Test code. |
| 505 | */ |
| 506 | |
| 507 | #ifdef TEST_PENROSE |
| 508 | |
| 509 | #include <stdio.h> |
| 510 | #include <string.h> |
| 511 | |
| 512 | int show_recursion = 0; |
| 513 | int ntiles, nfinal; |
| 514 | |
| 515 | int test_cb(penrose_state *state, vector *vs, int n, int depth) |
| 516 | { |
| 517 | int i, xoff = 0, yoff = 0; |
| 518 | double l = penrose_side_length(state->start_size, depth); |
| 519 | double rball = l / 10.0; |
| 520 | const char *col; |
| 521 | |
| 522 | ntiles++; |
| 523 | if (state->max_depth == depth) { |
| 524 | col = n == 4 ? "black" : "green"; |
| 525 | nfinal++; |
| 526 | } else { |
| 527 | if (!show_recursion) |
| 528 | return 0; |
| 529 | col = n == 4 ? "red" : "blue"; |
| 530 | } |
| 531 | if (n != 4) yoff = state->start_size; |
| 532 | |
| 533 | printf("<polygon points=\""); |
| 534 | for (i = 0; i < n; i++) { |
| 535 | printf("%s%f,%f", (i == 0) ? "" : " ", |
| 536 | v_x(vs, i) + xoff, v_y(vs, i) + yoff); |
| 537 | } |
| 538 | printf("\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", col, col); |
| 539 | printf("<ellipse cx=\"%f\" cy=\"%f\" rx=\"%f\" ry=\"%f\" fill=\"%s\" />", |
| 540 | v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col); |
| 541 | |
| 542 | return 0; |
| 543 | } |
| 544 | |
| 545 | void usage_exit(void) |
| 546 | { |
| 547 | fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n"); |
| 548 | exit(1); |
| 549 | } |
| 550 | |
| 551 | int main(int argc, char *argv[]) |
| 552 | { |
| 553 | penrose_state ps; |
| 554 | int which = 0; |
| 555 | |
| 556 | while (--argc > 0) { |
| 557 | char *p = *++argv; |
| 558 | if (!strcmp(p, "-h") || !strcmp(p, "--help")) { |
| 559 | usage_exit(); |
| 560 | } else if (!strcmp(p, "--recursion")) { |
| 561 | show_recursion = 1; |
| 562 | } else if (*p == '-') { |
| 563 | fprintf(stderr, "Unrecognised option '%s'\n", p); |
| 564 | exit(1); |
| 565 | } else { |
| 566 | break; |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | if (argc < 3) usage_exit(); |
| 571 | |
| 572 | if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2; |
| 573 | else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3; |
| 574 | else usage_exit(); |
| 575 | |
| 576 | ps.start_size = atoi(argv[1]); |
| 577 | ps.max_depth = atoi(argv[2]); |
| 578 | ps.new_tile = test_cb; |
| 579 | |
| 580 | ntiles = nfinal = 0; |
| 581 | |
| 582 | printf("\ |
| 583 | <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\ |
| 584 | <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\ |
| 585 | \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\ |
| 586 | \n\ |
| 587 | <svg xmlns=\"http://www.w3.org/2000/svg\"\n\ |
| 588 | xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n"); |
| 589 | |
| 590 | printf("<g>\n"); |
| 591 | penrose(&ps, which); |
| 592 | printf("</g>\n"); |
| 593 | |
| 594 | printf("<!-- %d tiles and %d leaf tiles total -->\n", |
| 595 | ntiles, nfinal); |
| 596 | |
| 597 | printf("</svg>"); |
| 598 | |
| 599 | return 0; |
| 600 | } |
| 601 | |
| 602 | #endif |
| 603 | |
| 604 | #ifdef TEST_VECTORS |
| 605 | |
| 606 | static void dbgv(const char *msg, vector v) |
| 607 | { |
| 608 | printf("%s: %s\n", msg, v_debug(v)); |
| 609 | } |
| 610 | |
| 611 | int main(int argc, const char *argv[]) |
| 612 | { |
| 613 | vector v = v_unit(); |
| 614 | |
| 615 | dbgv("unit vector", v); |
| 616 | v = v_rotate(v, 36); |
| 617 | dbgv("rotated 36", v); |
| 618 | v = v_scale(v, 2); |
| 619 | dbgv("scaled x2", v); |
| 620 | v = v_shrinkphi(v); |
| 621 | dbgv("shrunk phi", v); |
| 622 | v = v_rotate(v, -36); |
| 623 | dbgv("rotated -36", v); |
| 624 | |
| 625 | return 0; |
| 626 | } |
| 627 | |
| 628 | #endif |
| 629 | /* vim: set shiftwidth=4 tabstop=8: */ |