+typedef struct setface_ctx
+{
+ int xmin, xmax, ymin, ymax;
+
+ grid *g;
+ tree234 *points;
+} setface_ctx;
+
+static double round_int_nearest_away(double r)
+{
+ return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
+}
+
+static int set_faces(penrose_state *state, vector *vs, int n, int depth)
+{
+ setface_ctx *sf_ctx = (setface_ctx *)state->ctx;
+ int i;
+ int xs[4], ys[4];
+
+ if (depth < state->max_depth) return 0;
+#ifdef DEBUG_PENROSE
+ if (n != 4) return 0; /* triangles are sent as debugging. */
+#endif
+
+ for (i = 0; i < n; i++) {
+ double tx = v_x(vs, i), ty = v_y(vs, i);
+
+ xs[i] = (int)round_int_nearest_away(tx);
+ ys[i] = (int)round_int_nearest_away(ty);
+
+ if (xs[i] < sf_ctx->xmin || xs[i] > sf_ctx->xmax) return 0;
+ if (ys[i] < sf_ctx->ymin || ys[i] > sf_ctx->ymax) return 0;
+ }
+
+ grid_face_add_new(sf_ctx->g, n);
+ debug(("penrose: new face l=%f gen=%d...",
+ penrose_side_length(state->start_size, depth), depth));
+ for (i = 0; i < n; i++) {
+ grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points,
+ xs[i], ys[i]);
+ grid_face_set_dot(sf_ctx->g, d, i);
+ debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)",
+ d, d->x, d->y, v_x(vs, i), v_y(vs, i)));
+ }
+
+ return 0;
+}
+
+#define PENROSE_TILESIZE 100
+
+static void grid_size_penrose(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ int l = PENROSE_TILESIZE;
+
+ *tilesize = l;
+ *xextent = l * width;
+ *yextent = l * height;
+}
+
+static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
+{
+ int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
+ double outer_radius;
+ int inner_radius;
+ char gd[255];
+ int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
+
+ /* We want to produce a random bit of penrose tiling, so we calculate
+ * a random offset (within the patch that penrose.c calculates for us)
+ * and an angle (multiple of 36) to rotate the patch. */
+
+ penrose_calculate_size(which, tilesize, width, height,
+ &outer_radius, &startsz, &depth);
+
+ /* Calculate radius of (circumcircle of) patch, subtract from
+ * radius calculated. */
+ inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
+
+ /* Pick a random offset (the easy way: choose within outer square,
+ * discarding while it's outside the circle) */
+ do {
+ xoff = random_upto(rs, 2*inner_radius) - inner_radius;
+ yoff = random_upto(rs, 2*inner_radius) - inner_radius;
+ } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
+
+ aoff = random_upto(rs, 360/36) * 36;
+
+ debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
+ tilesize, width, height, outer_radius, inner_radius));
+ debug((" -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
+
+ sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
+
+ return dupstr(gd);
+}
+
+static char *grid_validate_desc_penrose(grid_type type, int width, int height, char *desc)
+{
+ int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
+ double outer_radius;
+ int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
+
+ if (!desc)
+ return "Missing grid description string.";
+
+ penrose_calculate_size(which, tilesize, width, height,
+ &outer_radius, &startsz, &depth);
+ inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
+
+ if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
+ return "Invalid format grid description string.";
+
+ if (sqrt(xoff*xoff + yoff*yoff) > inner_radius)
+ return "Patch offset out of bounds.";
+ if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360)
+ return "Angle offset out of bounds.";
+
+ return NULL;
+}
+
+/*
+ * We're asked for a grid of a particular size, and we generate enough
+ * of the tiling so we can be sure to have enough random grid from which
+ * to pick.
+ */
+
+static grid *grid_new_penrose(int width, int height, int which, char *desc)
+{
+ int max_faces, max_dots, tilesize = PENROSE_TILESIZE;
+ int xsz, ysz, xoff, yoff, aoff;
+ double rradius;
+
+ tree234 *points;
+ grid *g;
+
+ penrose_state ps;
+ setface_ctx sf_ctx;
+
+ penrose_calculate_size(which, tilesize, width, height,
+ &rradius, &ps.start_size, &ps.max_depth);
+
+ debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d",
+ width, height, tilesize, ps.start_size, ps.max_depth));
+
+ ps.new_tile = set_faces;
+ ps.ctx = &sf_ctx;
+
+ max_faces = (width*3) * (height*3); /* somewhat paranoid... */
+ max_dots = max_faces * 4; /* ditto... */
+
+ g = grid_empty();
+ g->tilesize = tilesize;
+ g->faces = snewn(max_faces, grid_face);
+ g->dots = snewn(max_dots, grid_dot);
+
+ points = newtree234(grid_point_cmp_fn);
+
+ memset(&sf_ctx, 0, sizeof(sf_ctx));
+ sf_ctx.g = g;
+ sf_ctx.points = points;
+
+ if (desc != NULL) {
+ if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
+ assert(!"Invalid grid description.");
+ } else {
+ xoff = yoff = 0;
+ }
+
+ xsz = width * tilesize;
+ ysz = height * tilesize;
+
+ sf_ctx.xmin = xoff - xsz/2;
+ sf_ctx.xmax = xoff + xsz/2;
+ sf_ctx.ymin = yoff - ysz/2;
+ sf_ctx.ymax = yoff + ysz/2;
+
+ debug(("penrose: centre (%f, %f) xsz %f ysz %f",
+ 0.0, 0.0, xsz, ysz));
+ debug(("penrose: x range (%f --> %f), y range (%f --> %f)",
+ sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax));
+
+ penrose(&ps, which, aoff);
+
+ freetree234(points);
+ assert(g->num_faces <= max_faces);
+ assert(g->num_dots <= max_dots);
+
+ debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
+ g->num_faces, g->num_faces/height, g->num_faces/width));
+
+ grid_trim_vigorously(g);
+ grid_make_consistent(g);
+
+ /*
+ * Centre the grid in its originally promised rectangle.
+ */
+ g->lowest_x -= ((sf_ctx.xmax - sf_ctx.xmin) -
+ (g->highest_x - g->lowest_x)) / 2;
+ g->highest_x = g->lowest_x + (sf_ctx.xmax - sf_ctx.xmin);
+ g->lowest_y -= ((sf_ctx.ymax - sf_ctx.ymin) -
+ (g->highest_y - g->lowest_y)) / 2;
+ g->highest_y = g->lowest_y + (sf_ctx.ymax - sf_ctx.ymin);
+
+ return g;
+}
+
+static void grid_size_penrose_p2_kite(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ grid_size_penrose(width, height, tilesize, xextent, yextent);
+}
+
+static void grid_size_penrose_p3_thick(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ grid_size_penrose(width, height, tilesize, xextent, yextent);
+}
+
+static grid *grid_new_penrose_p2_kite(int width, int height, char *desc)
+{
+ return grid_new_penrose(width, height, PENROSE_P2, desc);
+}
+
+static grid *grid_new_penrose_p3_thick(int width, int height, char *desc)
+{
+ return grid_new_penrose(width, height, PENROSE_P3, desc);
+}
+