-
-
Save bazzargh/961b6765042b17c0c25eadcc98b080e6 to your computer and use it in GitHub Desktop.
| // this version lets the canvas take care of all the matrix operations. | |
| let canvas = document.getElementById('canvas'); | |
| let ctx = canvas.getContext('2d'); | |
| let A60 = Math.PI/3; | |
| let A90 = Math.PI/2; | |
| let S3 = Math.sqrt(3); | |
| let ANGLES = [0, -2*A60, -A60, 0, 2*A60, A60, 0]; | |
| // Centres of patches in H8 at each substitution level. | |
| let cache = [ | |
| [[0, 0], [0, 0]], [[3, 3*S3], [3, 5*S3]], | |
| [[-3, 3*S3], [-6, 4*S3]], [[-6, 0], [-9, -S3]], | |
| [[0, -4*S3], [-6, -6*S3]], [[-6, -4*S3], [-15, -7*S3]]] | |
| function origin(part, z) { | |
| if (part == 6) { | |
| return origin(3, z + 1); | |
| } | |
| if (cache[part].length <= z) { | |
| let r1 = origin(part, z - 1); | |
| let r2 = origin(part, z - 2); | |
| cache[part][z] = [3*r1[0]-r2[0], 3*r1[1]-r2[1]]; | |
| } | |
| return cache[part][z]; | |
| } | |
| function monotile(ctx) { | |
| let u = 1; | |
| let v = Math.sqrt(4-u*u); | |
| ctx.save(); | |
| ctx.beginPath(); | |
| ctx.moveTo(0,0); | |
| for(let [r, a] of [ | |
| [v, A90], | |
| [u, A60],[u, -A90], | |
| [v, A60],[v, A90], | |
| [u,-A60],[u, A90], | |
| [v,-A60],[v, A90], | |
| [u, A60],[2*u, A60], | |
| [u, A90] | |
| ]) { | |
| ctx.lineTo(0, r); | |
| ctx.translate(0, r); | |
| ctx.rotate(-a); | |
| } | |
| ctx.closePath(); | |
| ctx.stroke(); | |
| ctx.restore(); | |
| } | |
| // h(7,...) and h(8,...) supertiles. | |
| function h(type, ctx, z, x, y, a) { | |
| ctx.save(); | |
| ctx.translate(x, y); | |
| ctx.rotate(a); | |
| if (z == 0) { | |
| monotile(ctx); | |
| } else { | |
| for(let part=0; part < type - 1; part++) { | |
| h(part == 0 ? 7 : 8, ctx, z - 1, ...origin(part, z - 1), ANGLES[part]); | |
| } | |
| } | |
| ctx.restore(); | |
| } | |
| ctx.save(); | |
| ctx.translate(500, 400); | |
| ctx.scale(14, 14); | |
| ctx.lineWidth = 1/14; | |
| h(8, ctx, 2, 0, 0, 0); | |
| ctx.restore(); | |
| ctx.save(); | |
| ctx.translate(500, 400); | |
| ctx.scale(2, 2); | |
| ctx.lineWidth = 1/4; | |
| ctx.strokeStyle = 'red' | |
| h(8, ctx, 4, 0, 0, 0); | |
| ctx.restore(); |
Tried to simplify the mess by using matrix transforms, letting the canvas handle scaling, and shifting all the magic numbers into tables. It's less eye-hurting to read, anyway. (thanks to Craig Kaplan for pointing out the bugs in this version)
Since I wrote this I also came up with an L-system for drawing the tiling as a single line, which needs a lot less code (it's a lot slower and more repetitive tho) https://hachyderm.io/@bazzargh/110293551149890811
And in case that site disappears, the code inline:
import turtle
import math
def expand(order, a, s0, s1):
for op in s0 if order <= 0 else s1:
mono_op_map[op](order - 1, a)
# an earlier version of this used a stack, but it's unnecessary since
# I can just complete the loop round a tile to return the turtle to the branch point
mono_op_map = {
"a": lambda o, a: turtle.forward(a),
"b": lambda o, a: turtle.forward(a*math.sqrt(3)),
# U is drawn as "W--X--W", but I don't see how the rules would fit if I used that
"U": lambda o, a: expand(o, a, "a+++b--b+++a--a+++b", "V++R++U++V++W++V++R--U++V++W++V++R++U--V"),
"V": lambda o, a: expand(o, a, "b---a", "W++V++R++U++V++W++V++R--W"),
"W": lambda o, a: expand(o, a, "a+++b", "V++R++U++V++W++V++R--W++V++R++U++V++W--V"),
"R": lambda o, a: expand(o, a, "aa", "V++R++U++V++W++V++R--W"),
"+": lambda o, a: turtle.right(30),
"-": lambda o, a: turtle.left(30),
}
start = "U++V++W++V++R"
size = 6
order = 3
turtle.speed("fastest")
expand(order, size, start, start)
it does share with the above js code the opinion that the flipped tile is a hole; but it's better in that the a/b lengths can be changed
and this will still just work. This code is small enough that a variation (using single-character symbols for the turns) fits the BBC Basic code of it into a single toot https://hachyderm.io/@bazzargh/110295590546847505
the recursion is https://oeis.org/A001906 (with varying starting points). As mentioned there,
Limit_{n->infinity} a(n)/a(n-1) = 1 + phi = (3 + sqrt(5))/2- 1 + phi is phi^2, this is exactly the expansion ratio mentioned in the monotile paper.