hats.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <html>
  2. <head>
  3. <meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
  4. <title>Generating hat tilings for Loopy</title>
  5. <style type="text/css">
  6. table, tbody, tr, td, th {
  7. border: 1px solid rgba(0, 0, 0, 0.3);
  8. border-collapse: collapse;
  9. }
  10. table.noborder, table.noborder tbody, table.noborder td,
  11. table.noborder th, table.noborder tr { border: none; }
  12. table {
  13. margin: 1em;
  14. }
  15. th, td {
  16. padding: 0.5em;
  17. }
  18. </style>
  19. </head>
  20. <body>
  21. <h1>Generating hat tilings for Loopy</h1>
  22. <p>The <a href="https://arxiv.org/abs/2303.10798">original paper</a>
  23. describes a method for generating hat tilings from a system of four
  24. 'metatiles'. You can start with any one of the four tiles, and then
  25. recursively apply a set of expansion rules that turn each tile into
  26. a collection of smaller tiles from the same set.</p>
  27. <p>This table shows the four tiles, with their one-letter names as
  28. given in the paper, and how each one is expanded.</p>
  29. <p>All the tiles have a significant orientation. The H, T and P
  30. tiles are marked with arrows to indicate the orientation. The F tile
  31. is asymmetric, so no arrow is needed.</p>
  32. <p>I've assigned each tile in each expansion a number, which Loopy
  33. will use for its coordinate system.</p>
  34. <table>
  35. <tr>
  36. <th>Tile name</th>
  37. <th>Single tile</th>
  38. <th>Expansion</th>
  39. <tr>
  40. <td>H</td>
  41. <td><img src="single-H.svg"></td>
  42. <td><img src="expanded-H.svg"></td>
  43. </tr>
  44. <tr>
  45. <td>T</td>
  46. <td><img src="single-T.svg"></td>
  47. <td><img src="expanded-T.svg"></td>
  48. </tr>
  49. <tr>
  50. <td>P</td>
  51. <td><img src="single-P.svg"></td>
  52. <td><img src="expanded-P.svg"></td>
  53. </tr>
  54. <tr>
  55. <td>F</td>
  56. <td><img src="single-F.svg"></td>
  57. <td><img src="expanded-F.svg"></td>
  58. </tr>
  59. </table>
  60. <p><strong>Note that these expansions overlap</strong>. When two
  61. adjacent metatiles are expanded, the outer layer of P and F tiles in
  62. their expansions must be placed so that they overlap each other. The
  63. original paper suggests a set of tiles to remove from these
  64. expansions so that each metatile expands to a <em>disjoint</em> set
  65. of smaller tiles. In our implementation, however, we prefer to keep
  66. the overlap, because our coordinate system will use it.</p>
  67. <p>Once you've generated a large enough patch of metatiles for your
  68. needs, the final step is to convert it into the actual hat tiles.
  69. The expansion of each metatile into hats is shown here. Again, I've
  70. assigned numbers to each hat for coordinate-system purposes:</p>
  71. <table>
  72. <tr>
  73. <th>Tile name</th>
  74. <th>Conversion into hats</th>
  75. <tr>
  76. <td>H</td>
  77. <td><img src="single-H.svg"></td>
  78. <td><img src="hats-single-H.svg"></td>
  79. </tr>
  80. <tr>
  81. <td>T</td>
  82. <td><img src="single-T.svg"></td>
  83. <td><img src="hats-single-T.svg"></td>
  84. </tr>
  85. <tr>
  86. <td>P</td>
  87. <td><img src="single-P.svg"></td>
  88. <td><img src="hats-single-P.svg"></td>
  89. </tr>
  90. <tr>
  91. <td>F</td>
  92. <td><img src="single-F.svg"></td>
  93. <td><img src="hats-single-F.svg"></td>
  94. </tr>
  95. </table>
  96. <p>(The hat in the middle of the H is shaded to indicate that it's
  97. one of the rare reflected ones. All the other hats are rotations of
  98. each other.)</p>
  99. <p>Given all of this, an obvious approach to generating a random
  100. patch of hat tiling would be to start with a single metatile,
  101. iterate the expansion process a few times until you have a tiled
  102. area much larger than you need, and then pick a subrectangle of
  103. it at random.</p>
  104. <p>Loopy's algorithm for generating Penrose tilings (which admit a
  105. similar, though less complicated, expansion system) works in exactly
  106. this way.</p>
  107. <p>One problem with that algorithm is that it spends a lot of effort
  108. on generating huge areas of tiles that aren't actually needed. So
  109. you'd prefer to adjust the algorithm so that at every stage of
  110. expansion it spots tiles completely outside the target rectangle,
  111. and throws them away <em>before</em> spending 5 iterations on
  112. exponentially expanding them into huge amounts of detail that will
  113. only be thrown away anyway later.</p>
  114. <p>That works well for Penrose tilings, because there, the expansion
  115. procedure is geometrically precise: coordinates in the expanded
  116. tiling are scaled up by an exact factor from coordinates in the
  117. original tiling. So at every stage it's easy to know exactly where
  118. your target rectangle <em>is</em>, and discard things that don't
  119. overlap it.</p>
  120. <p>But the metatiles shown here don't have that property. The tiles
  121. distort slightly as they expand. The <em>topological</em> properties
  122. of the expanded tiling match the original (which expanded tiles
  123. connect to each other), but the geometry (precise distances) is
  124. different. So it would be harder to implement the pruning for this
  125. tiling. The target rectangle might not even be rectangular in every
  126. iteration!</p>
  127. <p>Instead, I came up with a completely different mechanism, by
  128. devising a coordinate system to track our position within multiple
  129. layers of tile expansion. This allows us to generate precisely the
  130. area of tiling we need, and waste no effort at all on anything
  131. outside the target region.</p>
  132. <p>We begin by assigning an integer index to each kite making up an
  133. individual hat:</p>
  134. <img src="hat-kites.svg">
  135. <p>(For a reflected hat, these indices work in mirror image, so that
  136. for example 5 is still the kite in the middle.)</p>
  137. <p>Together with the indices we've assigned to hats within each
  138. metatile, and to metatiles in the expansion of another metatile,
  139. this gives us a coordinate system that can identify an individual
  140. kite in an n-times-expanded metatile. For each large metatile
  141. expansion, you can give the index of the smaller metatile selected
  142. from its expansion; when we reach the last layer of metatiles and
  143. expand them into hats, we can give the index of the hat in that
  144. metatile; finally we can index the kite in that hat.</p>
  145. <p><strong>But note that a kite can have multiple
  146. coordinates</strong>, because of the overlap between the expansions
  147. of adjacent metatiles. This will be useful!</p>
  148. <p>Our next step is to unambiguously name the four directions in
  149. which you can move from one kite to an adjacent kite. The directions
  150. should be independent of the orientation of the kite. I've chosen to
  151. name them from the viewpoint of someone standing at the pointy end
  152. of the kite and looking towards the blunt end:</p>
  153. <dl>
  154. <dt><strong>Left</strong></dt>
  155. <dd>Rotate 60° anticlockwise about the pointy end of the kite. For
  156. example, in the above hat, going 'left' from kite 5 takes you to
  157. kite 4.</dd>
  158. <dt><strong>Right</strong></dt>
  159. <dd>Rotate 60° clockwise about the pointy end. From kite 5, this
  160. would take you to kite 6.</dd>
  161. <dt><strong>Forward left</strong></dt>
  162. <dd>Head forwards and slightly left, to the kite sharing the
  163. left-hand one of this kite's short edges (as seen from the
  164. centre). Equivalently, rotate 120° <em>clockwise</em> about the
  165. blunt end. From kite 5, this takes you to kite 2.</dd>
  166. <dt><strong>Forward right</strong></dt>
  167. <dd>Head forwards and slightly right. Or rotate 120° anticlockwise
  168. about the blunt end, if you prefer to think of it that way. From
  169. kite 5, this takes you to kite 1.</dd>
  170. </dl>
  171. <p>The idea is that if we know how to transform the coordinates of a
  172. single kite into the coordinates of each of those four adjacent
  173. kites, then we can iterate that over a whole area and determine the
  174. coordinates of every kite in the whole tiling.</p>
  175. <p>Having done that, it's easy to identify each individual kite, by
  176. several different methods. For example, you could iterate over edges
  177. of the tiling to see whether the kites on each side have coordinates
  178. differing only in the kite index; if so, they're part of the same
  179. hat, and if not, not. Or a completely different approach (in fact
  180. the one Loopy actually uses) would be to trace round the boundary of
  181. each hat by starting from its kite #0 and just knowing what shape a
  182. hat is.</p>
  183. <p>So now we have to come up with an algorithm that lets us
  184. transform a kite coordinate by making one of the four permitted
  185. moves. To do this, we use two multilevel types of map.</p>
  186. <p>The <strong>kitemap</strong> for a given metatile type is made by
  187. expanding the metatile once into more metatiles, and then into hats.
  188. For example, the T tile:</p>
  189. <table class="noborder">
  190. <tr>
  191. <td><img src="single-T.svg"></td>
  192. <td><img src="arrow.svg"></td>
  193. <td><img src="expanded-T.svg" height="200px"></td>
  194. <td><img src="arrow.svg"></td>
  195. <td><img src="kitemap-T.svg" height="500px"></td>
  196. </tr>
  197. </table>
  198. <p>In each kite, we show a three-part coordinate, in little-endian
  199. fashion (because that matches the order the coordinates are stored
  200. in an array in the code that actually generates the tilings). For
  201. example, 7.3.0 means kite 7 in hat 3 of metatile 0 of the
  202. expansion.</p>
  203. <p>This map can be converted into a lookup table, indexed by those
  204. three-part coordinates and also the four move directions, which
  205. allows you to look up that (for example) going Left from kite 7.3.0
  206. goes to 0.0.0, or going Forward Left from 7.3.0 goes to 3.1.3.</p>
  207. <p>But if you're at the very edge of the kitemap, this isn't enough.
  208. For example, kite 0.0.4 right at the top can go Left to 1.0.4, but
  209. if it wants to go in any of the other three directions, this map
  210. doesn't help at all.</p>
  211. <p>This is where the overlap between the metatile expansions comes
  212. in. If you're in kite 0.0.4, then in particular, you're in the F
  213. tile numbered 4 in the expansion of a larger T metatile. And that F
  214. tile is <em>also</em> part of the expansion of at least one other
  215. second-order metatile – maybe two of them – which means that there
  216. are other equivalent coordinates describing the same kite, which
  217. will place it in a different kitemap where it <em>isn't</em> right
  218. on the edge,</p>
  219. <p>In order to find those equivalent coordinates, we create a second
  220. map for each metatile type, called the <strong>metamap</strong>.
  221. This one arises from expanding the metatile twice into other
  222. metatiles, instead of into hats:</p>
  223. <table class="noborder">
  224. <tr>
  225. <td><img src="single-T.svg"></td>
  226. <td><img src="arrow.svg"></td>
  227. <td><img src="expanded-T.svg" height="200px"></td>
  228. <td><img src="arrow.svg"></td>
  229. <td><img src="metamap-T.svg" height="500px"></td>
  230. </tr>
  231. </table>
  232. <p>Again, the coordinates are little-end first, so that 7.4 means
  233. the 7th smallest-size tile expanded from the 4th medium-sized tile
  234. expanded from the original single large tile.</p>
  235. <p>Unlike the kitemap, the metamap is not used for <em>moving
  236. around</em> the tiling to a different kite. It's used for rewriting
  237. the coordinates of the current kite into equivalent forms. So each
  238. the small tile in the metamap that's part of the expansion
  239. of <em>more than one</em> medium-sized tile has more than one
  240. coordinate pair. For example, tile 5.2 is also tile 5.4, and tile
  241. 7.0 is also 8.3 <em>and</em> also 4.5 (because it's where three
  242. medium-tile expansions meet).</p>
  243. <p>Using both of these maps (converted into appropriate lookup
  244. tables in the code), you can always eventually find a valid
  245. coordinate representation of whichever kite you like adjacent to
  246. your current one. If the kitemap corresponding to the current
  247. coordinates doesn't tell you the coordinates of the next kite, then
  248. you can try rewriting the two least-significant metatile indices
  249. (using the metamap corresponding to the type of the next-larger
  250. metatile still) and then see if that gives you a new kitemap that
  251. works. If even that doesn't work, you can move another level up, and
  252. try a metamap rewrite on the 2nd and 3rd smallest metatile levels,
  253. or the 3rd and 4th, etc. And eventually, you find something you can
  254. do.</p>
  255. <p>The full set of kitemaps and metamaps for all the tile
  256. types is in <a href="hatmaps.html">hatmaps.html</a>.</p>
  257. </body>
  258. </html>