-
Notifications
You must be signed in to change notification settings - Fork 0
/
penrose-internal.h
289 lines (259 loc) · 8.24 KB
/
penrose-internal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#include "penrose.h"
static inline unsigned num_subtriangles(char t)
{
return (t == 'A' || t == 'B' || t == 'X' || t == 'Y') ? 3 : 2;
}
static inline unsigned sibling_edge(char t)
{
switch (t) {
case 'A': case 'U': return 2;
case 'B': case 'V': return 1;
default: return 0;
}
}
/*
* Coordinate system for tracking Penrose-tile half-triangles.
* PenroseCoords simply stores an array of triangle types.
*/
typedef struct PenroseCoords {
char *c;
size_t nc, csize;
} PenroseCoords;
PenroseCoords *penrose_coords_new(void);
void penrose_coords_free(PenroseCoords *pc);
void penrose_coords_make_space(PenroseCoords *pc, size_t size);
PenroseCoords *penrose_coords_copy(PenroseCoords *pc_in);
/*
* Coordinate system for locating Penrose tiles in the plane.
*
* The 'Point' structure represents a single point by means of an
* integer linear combination of {1, t, t^2, t^3}, where t is the
* complex number exp(i pi/5) representing 1/10 of a turn about the
* origin.
*
* The 'PenroseTriangle' structure represents a half-tile triangle,
* giving both the locations of its vertices and its combinatorial
* coordinates. It also contains a linked-list pointer and a boolean
* flag, used during breadth-first search to generate all the tiles in
* an area and report them exactly once.
*/
typedef struct Point {
int coeffs[4];
} Point;
typedef struct PenroseTriangle PenroseTriangle;
struct PenroseTriangle {
Point vertices[3];
PenroseCoords *pc;
PenroseTriangle *next; /* used in breadth-first search */
bool reported;
};
/* Fill in all the coordinates of a triangle starting from any single edge.
* Requires tri->pc to have been filled in, so that we know which shape of
* triangle we're placing. */
void penrose_place(PenroseTriangle *tri, Point u, Point v, int index_of_u);
/* Free a PenroseHalf and its contained coordinates, or a whole PenroseTile */
void penrose_free(PenroseTriangle *tri);
/*
* A Point is really a complex number, so we can add, subtract and
* multiply them.
*/
static inline Point point_add(Point a, Point b)
{
Point r;
size_t i;
for (i = 0; i < 4; i++)
r.coeffs[i] = a.coeffs[i] + b.coeffs[i];
return r;
}
static inline Point point_sub(Point a, Point b)
{
Point r;
size_t i;
for (i = 0; i < 4; i++)
r.coeffs[i] = a.coeffs[i] - b.coeffs[i];
return r;
}
static inline Point point_mul_by_t(Point x)
{
Point r;
/* Multiply by t by using the identity t^4 - t^3 + t^2 - t + 1 = 0,
* so t^4 = t^3 - t^2 + t - 1 */
r.coeffs[0] = -x.coeffs[3];
r.coeffs[1] = x.coeffs[0] + x.coeffs[3];
r.coeffs[2] = x.coeffs[1] - x.coeffs[3];
r.coeffs[3] = x.coeffs[2] + x.coeffs[3];
return r;
}
static inline Point point_mul(Point a, Point b)
{
size_t i, j;
Point r;
/* Initialise r to be a, scaled by b's t^3 term */
for (j = 0; j < 4; j++)
r.coeffs[j] = a.coeffs[j] * b.coeffs[3];
/* Now iterate r = t*r + (next coefficient down), by Horner's rule */
for (i = 3; i-- > 0 ;) {
r = point_mul_by_t(r);
for (j = 0; j < 4; j++)
r.coeffs[j] += a.coeffs[j] * b.coeffs[i];
}
return r;
}
static inline bool point_equal(Point a, Point b)
{
size_t i;
for (i = 0; i < 4; i++)
if (a.coeffs[i] != b.coeffs[i])
return false;
return true;
}
/*
* Return the Point corresponding to a rotation of s steps around the
* origin, i.e. a rotation by 36*s degrees or s*pi/5 radians.
*/
static inline Point point_rot(int s)
{
Point r = {{ 1, 0, 0, 0 }};
Point tpower = {{ 0, 1, 0, 0 }};
/* Reduce to a sensible range */
s = s % 10;
if (s < 0)
s += 10;
while (true) {
if (s & 1)
r = point_mul(r, tpower);
s >>= 1;
if (!s)
break;
tpower = point_mul(tpower, tpower);
}
return r;
}
/*
* PenroseContext is the shared context of a whole run of the
* algorithm. Its 'prototype' PenroseCoords object represents the
* coordinates of the starting triangle, and is extended as necessary;
* any other PenroseCoord that needs extending will copy the
* higher-order values from ctx->prototype as needed, so that once
* each choice has been made, it remains consistent.
*
* When we're inventing a random piece of tiling in the first place,
* we append to ctx->prototype by choosing a random (but legal)
* higher-level metatile for the current topmost one to turn out to be
* part of. When we're replaying a generation whose parameters are
* already stored, we don't have a random_state, and we make fixed
* decisions if not enough coordinates were provided, as in the
* corresponding hat.c system.
*
* For a normal (non-testing) caller, penrosectx_generate() is the
* main useful function. It breadth-first searches a whole area to
* generate all the triangles in it, starting from a (typically
* central) one with the coordinates of ctx->prototype. It takes two
* callback function: one that checks whether a triangle is within the
* bounds of the target area (and therefore the search should continue
* exploring its neighbours), and another that reports a full Penrose
* tile once both of its halves have been found and determined to be
* in bounds.
*/
typedef struct PenroseContext {
random_state *rs;
bool must_free_rs;
unsigned start_vertex; /* which vertex of 'prototype' is at the origin? */
int orientation; /* orientation to put in PenrosePatchParams */
PenroseCoords *prototype;
} PenroseContext;
void penrosectx_init_random(PenroseContext *ctx, random_state *rs, int which);
void penrosectx_init_from_params(
PenroseContext *ctx, const struct PenrosePatchParams *ps);
void penrosectx_cleanup(PenroseContext *ctx);
PenroseCoords *penrosectx_initial_coords(PenroseContext *ctx);
void penrosectx_extend_coords(PenroseContext *ctx, PenroseCoords *pc,
size_t n);
void penrosectx_step(PenroseContext *ctx, PenroseCoords *pc,
unsigned edge, unsigned *outedge);
void penrosectx_generate(
PenroseContext *ctx,
bool (*inbounds)(void *inboundsctx,
const PenroseTriangle *tri), void *inboundsctx,
void (*tile)(void *tilectx, const Point *vertices), void *tilectx);
/* Subroutines that step around the tiling specified by a PenroseCtx,
* delivering both plane and combinatorial coordinates as they go */
PenroseTriangle *penrose_initial(PenroseContext *ctx);
PenroseTriangle *penrose_adjacent(PenroseContext *ctx,
const PenroseTriangle *src_spec,
unsigned src_edge, unsigned *dst_edge);
/* For extracting the point coordinates */
typedef struct Coord {
int c1, cr5; /* coefficients of 1 and sqrt(5) respectively */
} Coord;
static inline Coord point_x(Point p)
{
Coord x = {
4 * p.coeffs[0] + p.coeffs[1] - p.coeffs[2] + p.coeffs[3],
p.coeffs[1] + p.coeffs[2] - p.coeffs[3],
};
return x;
}
static inline Coord point_y(Point p)
{
Coord y = {
2 * p.coeffs[1] + p.coeffs[2] + p.coeffs[3],
p.coeffs[2] + p.coeffs[3],
};
return y;
}
static inline int coord_sign(Coord x)
{
if (x.c1 == 0 && x.cr5 == 0)
return 0;
if (x.c1 >= 0 && x.cr5 >= 0)
return +1;
if (x.c1 <= 0 && x.cr5 <= 0)
return -1;
if (x.c1 * x.c1 > 5 * x.cr5 * x.cr5)
return x.c1 < 0 ? -1 : +1;
else
return x.cr5 < 0 ? -1 : +1;
}
static inline Coord coord_construct(int c1, int cr5)
{
Coord c = { c1, cr5 };
return c;
}
static inline Coord coord_integer(int c1)
{
return coord_construct(c1, 0);
}
static inline Coord coord_add(Coord a, Coord b)
{
Coord sum;
sum.c1 = a.c1 + b.c1;
sum.cr5 = a.cr5 + b.cr5;
return sum;
}
static inline Coord coord_sub(Coord a, Coord b)
{
Coord diff;
diff.c1 = a.c1 - b.c1;
diff.cr5 = a.cr5 - b.cr5;
return diff;
}
static inline Coord coord_mul(Coord a, Coord b)
{
Coord prod;
prod.c1 = a.c1 * b.c1 + 5 * a.cr5 * b.cr5;
prod.cr5 = a.c1 * b.cr5 + a.cr5 * b.c1;
return prod;
}
static inline Coord coord_abs(Coord a)
{
int sign = coord_sign(a);
Coord abs;
abs.c1 = a.c1 * sign;
abs.cr5 = a.cr5 * sign;
return abs;
}
static inline int coord_cmp(Coord a, Coord b)
{
return coord_sign(coord_sub(a, b));
}