+#define FLORET_TILESIZE 150
+/* -py/px is close to tan(30 - atan(sqrt(3)/9))
+ * using py=26 makes everything lean to the left, rather than right
+ */
+#define FLORET_PX 75
+#define FLORET_PY -26
+
+static void grid_size_floret(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
+ int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
+ int ry = qy-py;
+ /* rx unused in determining grid size. */
+
+ *tilesize = FLORET_TILESIZE;
+ *xextent = (6*px+3*qx)/2 * (width-1) + 4*qx + 2*px;
+ *yextent = (5*qy-4*py) * (height-1) + 4*qy + 2*ry;
+}
+
+static grid *grid_new_floret(int width, int height, char *desc)
+{
+ int x, y;
+ /* Vectors for sides; weird numbers needed to keep puzzle aligned with window
+ * -py/px is close to tan(30 - atan(sqrt(3)/9))
+ * using py=26 makes everything lean to the left, rather than right
+ */
+ int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
+ int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
+ int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */
+
+ /* Upper bounds - don't have to be exact */
+ int max_faces = 6 * width * height;
+ int max_dots = 9 * (width + 1) * (height + 1);
+
+ tree234 *points;
+
+ grid *g = grid_empty();
+ g->tilesize = FLORET_TILESIZE;
+ g->faces = snewn(max_faces, grid_face);
+ g->dots = snewn(max_dots, grid_dot);
+
+ points = newtree234(grid_point_cmp_fn);
+
+ /* generate pentagonal faces */
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ grid_dot *d;
+ /* face centre */
+ int cx = (6*px+3*qx)/2 * x;
+ int cy = (4*py-5*qy) * y;
+ if (x % 2)
+ cy -= (4*py-5*qy)/2;
+ else if (y && y == height-1)
+ continue; /* make better looking grids? try 3x3 for instance */
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx+2*rx+qx, cy+2*ry+qy); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx+2*qx+rx, cy+2*qy+ry); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 4);
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx+2*qx+px, cy+2*qy+py); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx+2*px+qx, cy+2*py+qy); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 4);
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx+2*px-rx, cy+2*py-ry); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx-2*rx+px, cy-2*ry+py); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 4);
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx-2*rx-qx, cy-2*ry-qy); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx-2*qx-rx, cy-2*qy-ry); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 4);
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx-2*qx-px, cy-2*qy-py); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx-2*px-qx, cy-2*py-qy); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 4);
+
+ grid_face_add_new(g, 5);
+ d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, cx-2*px+rx, cy-2*py+ry); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, cx+2*rx-px, cy+2*ry-py); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 4);
+ }
+ }
+
+ freetree234(points);
+ assert(g->num_faces <= max_faces);
+ assert(g->num_dots <= max_dots);
+
+ grid_make_consistent(g);
+ return g;
+}
+
+/* DODEC_* are used for dodecagonal and great-dodecagonal grids. */
+#define DODEC_TILESIZE 26
+/* Vector for side of triangle - ratio is close to sqrt(3) */
+#define DODEC_A 15
+#define DODEC_B 26
+
+static void grid_size_dodecagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ int a = DODEC_A;
+ int b = DODEC_B;
+
+ *tilesize = DODEC_TILESIZE;
+ *xextent = (4*a + 2*b) * (width-1) + 3*(2*a + b);
+ *yextent = (3*a + 2*b) * (height-1) + 2*(2*a + b);
+}
+
+static grid *grid_new_dodecagonal(int width, int height, char *desc)
+{
+ int x, y;
+ int a = DODEC_A;
+ int b = DODEC_B;
+
+ /* Upper bounds - don't have to be exact */
+ int max_faces = 3 * width * height;
+ int max_dots = 14 * width * height;
+
+ tree234 *points;
+
+ grid *g = grid_empty();
+ g->tilesize = DODEC_TILESIZE;
+ g->faces = snewn(max_faces, grid_face);
+ g->dots = snewn(max_dots, grid_dot);
+
+ points = newtree234(grid_point_cmp_fn);
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ grid_dot *d;
+ /* centre of dodecagon */
+ int px = (4*a + 2*b) * x;
+ int py = (3*a + 2*b) * y;
+ if (y % 2)
+ px += 2*a + b;
+
+ /* dodecagon */
+ grid_face_add_new(g, 12);
+ d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
+ d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
+ d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
+ d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
+ d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
+ d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
+ d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
+ d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
+
+ /* triangle below dodecagon */
+ if ((y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
+ grid_face_add_new(g, 3);
+ d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px , py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 2);
+ }
+
+ /* triangle above dodecagon */
+ if ((y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
+ grid_face_add_new(g, 3);
+ d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px , py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 2);
+ }
+ }
+ }
+
+ freetree234(points);
+ assert(g->num_faces <= max_faces);
+ assert(g->num_dots <= max_dots);
+
+ grid_make_consistent(g);
+ return g;
+}
+
+static void grid_size_greatdodecagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+{
+ int a = DODEC_A;
+ int b = DODEC_B;
+
+ *tilesize = DODEC_TILESIZE;
+ *xextent = (6*a + 2*b) * (width-1) + 2*(2*a + b) + 3*a + b;
+ *yextent = (3*a + 3*b) * (height-1) + 2*(2*a + b);
+}
+
+static grid *grid_new_greatdodecagonal(int width, int height, char *desc)
+{
+ int x, y;
+ /* Vector for side of triangle - ratio is close to sqrt(3) */
+ int a = DODEC_A;
+ int b = DODEC_B;
+
+ /* Upper bounds - don't have to be exact */
+ int max_faces = 30 * width * height;
+ int max_dots = 200 * width * height;
+
+ tree234 *points;
+
+ grid *g = grid_empty();
+ g->tilesize = DODEC_TILESIZE;
+ g->faces = snewn(max_faces, grid_face);
+ g->dots = snewn(max_dots, grid_dot);
+
+ points = newtree234(grid_point_cmp_fn);
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ grid_dot *d;
+ /* centre of dodecagon */
+ int px = (6*a + 2*b) * x;
+ int py = (3*a + 3*b) * y;
+ if (y % 2)
+ px += 3*a + b;
+
+ /* dodecagon */
+ grid_face_add_new(g, 12);
+ d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
+ d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
+ d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
+ d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
+ d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
+ d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
+ d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
+ d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
+
+ /* hexagon below dodecagon */
+ if (y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
+ grid_face_add_new(g, 6);
+ d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px + 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px - a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, px - 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 4);
+ d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 5);
+ }
+
+ /* hexagon above dodecagon */
+ if (y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
+ grid_face_add_new(g, 6);
+ d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px - 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px - a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px + a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 3);
+ d = grid_get_dot(g, points, px + 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 4);
+ d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 5);
+ }
+
+ /* square on right of dodecagon */
+ if (x < width - 1) {
+ grid_face_add_new(g, 4);
+ d = grid_get_dot(g, points, px + 2*a + b, py - a); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px + 4*a + b, py - a); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + 4*a + b, py + a); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 3);
+ }
+
+ /* square on top right of dodecagon */
+ if (y && (x < width - 1 || !(y % 2))) {
+ grid_face_add_new(g, 4);
+ d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px + (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px + (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 3);
+ }
+
+ /* square on top left of dodecagon */
+ if (y && (x || (y % 2))) {
+ grid_face_add_new(g, 4);
+ d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 0);
+ d = grid_get_dot(g, points, px - (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 1);
+ d = grid_get_dot(g, points, px - (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 2);
+ d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 3);
+ }
+ }
+ }
+
+ freetree234(points);
+ assert(g->num_faces <= max_faces);
+ assert(g->num_dots <= max_dots);
+
+ grid_make_consistent(g);
+ return g;
+}
+
+typedef struct setface_ctx
+{
+ int xmin, xmax, ymin, ymax;
+ int aoff;
+
+ 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];
+ double cosa = cos(sf_ctx->aoff * PI / 180.0);
+ double sina = sin(sf_ctx->aoff * PI / 180.0);
+
+ 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*cosa + ty*sina);
+ ys[i] = (int)round_int_nearest_away(-tx*sina + ty*cosa);
+
+ 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;
+ 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, &sf_ctx.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);
+
+ 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);
+}
+