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