summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/penrose-internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/penrose-internal.h')
-rw-r--r--apps/plugins/puzzles/src/penrose-internal.h289
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
3static inline unsigned num_subtriangles(char t)
4{
5 return (t == 'A' || t == 'B' || t == 'X' || t == 'Y') ? 3 : 2;
6}
7
8static 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 */
21typedef struct PenroseCoords {
22 char *c;
23 size_t nc, csize;
24} PenroseCoords;
25
26PenroseCoords *penrose_coords_new(void);
27void penrose_coords_free(PenroseCoords *pc);
28void penrose_coords_make_space(PenroseCoords *pc, size_t size);
29PenroseCoords *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 */
45typedef struct Point {
46 int coeffs[4];
47} Point;
48typedef struct PenroseTriangle PenroseTriangle;
49struct 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. */
59void 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 */
62void penrose_free(PenroseTriangle *tri);
63
64/*
65 * A Point is really a complex number, so we can add, subtract and
66 * multiply them.
67 */
68static 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}
76static 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}
84static 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}
95static 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}
113static 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 */
126static 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 */
174typedef 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
182void penrosectx_init_random(PenroseContext *ctx, random_state *rs, int which);
183void penrosectx_init_from_params(
184 PenroseContext *ctx, const struct PenrosePatchParams *ps);
185void penrosectx_cleanup(PenroseContext *ctx);
186PenroseCoords *penrosectx_initial_coords(PenroseContext *ctx);
187void penrosectx_extend_coords(PenroseContext *ctx, PenroseCoords *pc,
188 size_t n);
189void penrosectx_step(PenroseContext *ctx, PenroseCoords *pc,
190 unsigned edge, unsigned *outedge);
191void 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 */
199PenroseTriangle *penrose_initial(PenroseContext *ctx);
200PenroseTriangle *penrose_adjacent(PenroseContext *ctx,
201 const PenroseTriangle *src_spec,
202 unsigned src_edge, unsigned *dst_edge);
203
204/* For extracting the point coordinates */
205typedef struct Coord {
206 int c1, cr5; /* coefficients of 1 and sqrt(5) respectively */
207} Coord;
208
209static 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
218static 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
227static 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
242static inline Coord coord_construct(int c1, int cr5)
243{
244 Coord c = { c1, cr5 };
245 return c;
246}
247
248static inline Coord coord_integer(int c1)
249{
250 return coord_construct(c1, 0);
251}
252
253static 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
261static 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
269static 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
277static 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
286static inline int coord_cmp(Coord a, Coord b)
287{
288 return coord_sign(coord_sub(a, b));
289}