Files
puzzles/grid.c
Simon Tatham 7b2b742be8 Another patch from Chris Moore implementing two more grid types, both
involving dodecagons.

[originally from svn r9109]
2011-02-24 19:06:49 +00:00

1558 lines
56 KiB
C

/*
* (c) Lambros Lambrou 2008
*
* Code for working with general grids, which can be any planar graph
* with faces, edges and vertices (dots). Includes generators for a few
* types of grid, including square, hexagonal, triangular and others.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "puzzles.h"
#include "tree234.h"
#include "grid.h"
/* Debugging options */
/*
#define DEBUG_GRID
*/
/* ----------------------------------------------------------------------
* Deallocate or dereference a grid
*/
void grid_free(grid *g)
{
assert(g->refcount);
g->refcount--;
if (g->refcount == 0) {
int i;
for (i = 0; i < g->num_faces; i++) {
sfree(g->faces[i].dots);
sfree(g->faces[i].edges);
}
for (i = 0; i < g->num_dots; i++) {
sfree(g->dots[i].faces);
sfree(g->dots[i].edges);
}
sfree(g->faces);
sfree(g->edges);
sfree(g->dots);
sfree(g);
}
}
/* Used by the other grid generators. Create a brand new grid with nothing
* initialised (all lists are NULL) */
static grid *grid_new(void)
{
grid *g = snew(grid);
g->faces = NULL;
g->edges = NULL;
g->dots = NULL;
g->num_faces = g->num_edges = g->num_dots = 0;
g->refcount = 1;
g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0;
return g;
}
/* Helper function to calculate perpendicular distance from
* a point P to a line AB. A and B mustn't be equal here.
*
* Well-known formula for area A of a triangle:
* / 1 1 1 \
* 2A = determinant of matrix | px ax bx |
* \ py ay by /
*
* Also well-known: 2A = base * height
* = perpendicular distance * line-length.
*
* Combining gives: distance = determinant / line-length(a,b)
*/
static double point_line_distance(long px, long py,
long ax, long ay,
long bx, long by)
{
long det = ax*by - bx*ay + bx*py - px*by + px*ay - ax*py;
double len;
det = max(det, -det);
len = sqrt(SQ(ax - bx) + SQ(ay - by));
return det / len;
}
/* Determine nearest edge to where the user clicked.
* (x, y) is the clicked location, converted to grid coordinates.
* Returns the nearest edge, or NULL if no edge is reasonably
* near the position.
*
* Just judging edges by perpendicular distance is not quite right -
* the edge might be "off to one side". So we insist that the triangle
* with (x,y) has acute angles at the edge's dots.
*
* edge1
* *---------*------
* |
* | *(x,y)
* edge2 |
* | edge2 is OK, but edge1 is not, even though
* | edge1 is perpendicularly closer to (x,y)
* *
*
*/
grid_edge *grid_nearest_edge(grid *g, int x, int y)
{
grid_edge *best_edge;
double best_distance = 0;
int i;
best_edge = NULL;
for (i = 0; i < g->num_edges; i++) {
grid_edge *e = &g->edges[i];
long e2; /* squared length of edge */
long a2, b2; /* squared lengths of other sides */
double dist;
/* See if edge e is eligible - the triangle must have acute angles
* at the edge's dots.
* Pythagoras formula h^2 = a^2 + b^2 detects right-angles,
* so detect acute angles by testing for h^2 < a^2 + b^2 */
e2 = SQ((long)e->dot1->x - (long)e->dot2->x) + SQ((long)e->dot1->y - (long)e->dot2->y);
a2 = SQ((long)e->dot1->x - (long)x) + SQ((long)e->dot1->y - (long)y);
b2 = SQ((long)e->dot2->x - (long)x) + SQ((long)e->dot2->y - (long)y);
if (a2 >= e2 + b2) continue;
if (b2 >= e2 + a2) continue;
/* e is eligible so far. Now check the edge is reasonably close
* to where the user clicked. Don't want to toggle an edge if the
* click was way off the grid.
* There is room for experimentation here. We could check the
* perpendicular distance is within a certain fraction of the length
* of the edge. That amounts to testing a rectangular region around
* the edge.
* Alternatively, we could check that the angle at the point is obtuse.
* That would amount to testing a circular region with the edge as
* diameter. */
dist = point_line_distance((long)x, (long)y,
(long)e->dot1->x, (long)e->dot1->y,
(long)e->dot2->x, (long)e->dot2->y);
/* Is dist more than half edge length ? */
if (4 * SQ(dist) > e2)
continue;
if (best_edge == NULL || dist < best_distance) {
best_edge = e;
best_distance = dist;
}
}
return best_edge;
}
/* ----------------------------------------------------------------------
* Grid generation
*/
#ifdef DEBUG_GRID
/* Show the basic grid information, before doing grid_make_consistent */
static void grid_print_basic(grid *g)
{
/* TODO: Maybe we should generate an SVG image of the dots and lines
* of the grid here, before grid_make_consistent.
* Would help with debugging grid generation. */
int i;
printf("--- Basic Grid Data ---\n");
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
printf("Face %d: dots[", i);
int j;
for (j = 0; j < f->order; j++) {
grid_dot *d = f->dots[j];
printf("%s%d", j ? "," : "", (int)(d - g->dots));
}
printf("]\n");
}
}
/* Show the derived grid information, computed by grid_make_consistent */
static void grid_print_derived(grid *g)
{
/* edges */
int i;
printf("--- Derived Grid Data ---\n");
for (i = 0; i < g->num_edges; i++) {
grid_edge *e = g->edges + i;
printf("Edge %d: dots[%d,%d] faces[%d,%d]\n",
i, (int)(e->dot1 - g->dots), (int)(e->dot2 - g->dots),
e->face1 ? (int)(e->face1 - g->faces) : -1,
e->face2 ? (int)(e->face2 - g->faces) : -1);
}
/* faces */
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
int j;
printf("Face %d: faces[", i);
for (j = 0; j < f->order; j++) {
grid_edge *e = f->edges[j];
grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1;
printf("%s%d", j ? "," : "", f2 ? (int)(f2 - g->faces) : -1);
}
printf("]\n");
}
/* dots */
for (i = 0; i < g->num_dots; i++) {
grid_dot *d = g->dots + i;
int j;
printf("Dot %d: dots[", i);
for (j = 0; j < d->order; j++) {
grid_edge *e = d->edges[j];
grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1;
printf("%s%d", j ? "," : "", (int)(d2 - g->dots));
}
printf("] faces[");
for (j = 0; j < d->order; j++) {
grid_face *f = d->faces[j];
printf("%s%d", j ? "," : "", f ? (int)(f - g->faces) : -1);
}
printf("]\n");
}
}
#endif /* DEBUG_GRID */
/* Helper function for building incomplete-edges list in
* grid_make_consistent() */
static int grid_edge_bydots_cmpfn(void *v1, void *v2)
{
grid_edge *a = v1;
grid_edge *b = v2;
grid_dot *da, *db;
/* Pointer subtraction is valid here, because all dots point into the
* same dot-list (g->dots).
* Edges are not "normalised" - the 2 dots could be stored in any order,
* so we need to take this into account when comparing edges. */
/* Compare first dots */
da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2;
db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2;
if (da != db)
return db - da;
/* Compare last dots */
da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1;
db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1;
if (da != db)
return db - da;
return 0;
}
/* Input: grid has its dots and faces initialised:
* - dots have (optionally) x and y coordinates, but no edges or faces
* (pointers are NULL).
* - edges not initialised at all
* - faces initialised and know which dots they have (but no edges yet). The
* dots around each face are assumed to be clockwise.
*
* Output: grid is complete and valid with all relationships defined.
*/
static void grid_make_consistent(grid *g)
{
int i;
tree234 *incomplete_edges;
grid_edge *next_new_edge; /* Where new edge will go into g->edges */
#ifdef DEBUG_GRID
grid_print_basic(g);
#endif
/* ====== Stage 1 ======
* Generate edges
*/
/* We know how many dots and faces there are, so we can find the exact
* number of edges from Euler's polyhedral formula: F + V = E + 2 .
* We use "-1", not "-2" here, because Euler's formula includes the
* infinite face, which we don't count. */
g->num_edges = g->num_faces + g->num_dots - 1;
g->edges = snewn(g->num_edges, grid_edge);
next_new_edge = g->edges;
/* Iterate over faces, and over each face's dots, generating edges as we
* go. As we find each new edge, we can immediately fill in the edge's
* dots, but only one of the edge's faces. Later on in the iteration, we
* will find the same edge again (unless it's on the border), but we will
* know the other face.
* For efficiency, maintain a list of the incomplete edges, sorted by
* their dots. */
incomplete_edges = newtree234(grid_edge_bydots_cmpfn);
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
int j;
for (j = 0; j < f->order; j++) {
grid_edge e; /* fake edge for searching */
grid_edge *edge_found;
int j2 = j + 1;
if (j2 == f->order)
j2 = 0;
e.dot1 = f->dots[j];
e.dot2 = f->dots[j2];
/* Use del234 instead of find234, because we always want to
* remove the edge if found */
edge_found = del234(incomplete_edges, &e);
if (edge_found) {
/* This edge already added, so fill out missing face.
* Edge is already removed from incomplete_edges. */
edge_found->face2 = f;
} else {
assert(next_new_edge - g->edges < g->num_edges);
next_new_edge->dot1 = e.dot1;
next_new_edge->dot2 = e.dot2;
next_new_edge->face1 = f;
next_new_edge->face2 = NULL; /* potentially infinite face */
add234(incomplete_edges, next_new_edge);
++next_new_edge;
}
}
}
freetree234(incomplete_edges);
/* ====== Stage 2 ======
* For each face, build its edge list.
*/
/* Allocate space for each edge list. Can do this, because each face's
* edge-list is the same size as its dot-list. */
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
int j;
f->edges = snewn(f->order, grid_edge*);
/* Preload with NULLs, to help detect potential bugs. */
for (j = 0; j < f->order; j++)
f->edges[j] = NULL;
}
/* Iterate over each edge, and over both its faces. Add this edge to
* the face's edge-list, after finding where it should go in the
* sequence. */
for (i = 0; i < g->num_edges; i++) {
grid_edge *e = g->edges + i;
int j;
for (j = 0; j < 2; j++) {
grid_face *f = j ? e->face2 : e->face1;
int k, k2;
if (f == NULL) continue;
/* Find one of the dots around the face */
for (k = 0; k < f->order; k++) {
if (f->dots[k] == e->dot1)
break; /* found dot1 */
}
assert(k != f->order); /* Must find the dot around this face */
/* Labelling scheme: as we walk clockwise around the face,
* starting at dot0 (f->dots[0]), we hit:
* (dot0), edge0, dot1, edge1, dot2,...
*
* 0
* 0-----1
* |
* |1
* |
* 3-----2
* 2
*
* Therefore, edgeK joins dotK and dot{K+1}
*/
/* Around this face, either the next dot or the previous dot
* must be e->dot2. Otherwise the edge is wrong. */
k2 = k + 1;
if (k2 == f->order)
k2 = 0;
if (f->dots[k2] == e->dot2) {
/* dot1(k) and dot2(k2) go clockwise around this face, so add
* this edge at position k (see diagram). */
assert(f->edges[k] == NULL);
f->edges[k] = e;
continue;
}
/* Try previous dot */
k2 = k - 1;
if (k2 == -1)
k2 = f->order - 1;
if (f->dots[k2] == e->dot2) {
/* dot1(k) and dot2(k2) go anticlockwise around this face. */
assert(f->edges[k2] == NULL);
f->edges[k2] = e;
continue;
}
assert(!"Grid broken: bad edge-face relationship");
}
}
/* ====== Stage 3 ======
* For each dot, build its edge-list and face-list.
*/
/* We don't know how many edges/faces go around each dot, so we can't
* allocate the right space for these lists. Pre-compute the sizes by
* iterating over each edge and recording a tally against each dot. */
for (i = 0; i < g->num_dots; i++) {
g->dots[i].order = 0;
}
for (i = 0; i < g->num_edges; i++) {
grid_edge *e = g->edges + i;
++(e->dot1->order);
++(e->dot2->order);
}
/* Now we have the sizes, pre-allocate the edge and face lists. */
for (i = 0; i < g->num_dots; i++) {
grid_dot *d = g->dots + i;
int j;
assert(d->order >= 2); /* sanity check */
d->edges = snewn(d->order, grid_edge*);
d->faces = snewn(d->order, grid_face*);
for (j = 0; j < d->order; j++) {
d->edges[j] = NULL;
d->faces[j] = NULL;
}
}
/* For each dot, need to find a face that touches it, so we can seed
* the edge-face-edge-face process around each dot. */
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
int j;
for (j = 0; j < f->order; j++) {
grid_dot *d = f->dots[j];
d->faces[0] = f;
}
}
/* Each dot now has a face in its first slot. Generate the remaining
* faces and edges around the dot, by searching both clockwise and
* anticlockwise from the first face. Need to do both directions,
* because of the possibility of hitting the infinite face, which
* blocks progress. But there's only one such face, so we will
* succeed in finding every edge and face this way. */
for (i = 0; i < g->num_dots; i++) {
grid_dot *d = g->dots + i;
int current_face1 = 0; /* ascends clockwise */
int current_face2 = 0; /* descends anticlockwise */
/* Labelling scheme: as we walk clockwise around the dot, starting
* at face0 (d->faces[0]), we hit:
* (face0), edge0, face1, edge1, face2,...
*
* 0
* |
* 0 | 1
* |
* -----d-----1
* |
* | 2
* |
* 2
*
* So, for example, face1 should be joined to edge0 and edge1,
* and those edges should appear in an anticlockwise sense around
* that face (see diagram). */
/* clockwise search */
while (TRUE) {
grid_face *f = d->faces[current_face1];
grid_edge *e;
int j;
assert(f != NULL);
/* find dot around this face */
for (j = 0; j < f->order; j++) {
if (f->dots[j] == d)
break;
}
assert(j != f->order); /* must find dot */
/* Around f, required edge is anticlockwise from the dot. See
* the other labelling scheme higher up, for why we subtract 1
* from j. */
j--;
if (j == -1)
j = f->order - 1;
e = f->edges[j];
d->edges[current_face1] = e; /* set edge */
current_face1++;
if (current_face1 == d->order)
break;
else {
/* set face */
d->faces[current_face1] =
(e->face1 == f) ? e->face2 : e->face1;
if (d->faces[current_face1] == NULL)
break; /* cannot progress beyond infinite face */
}
}
/* If the clockwise search made it all the way round, don't need to
* bother with the anticlockwise search. */
if (current_face1 == d->order)
continue; /* this dot is complete, move on to next dot */
/* anticlockwise search */
while (TRUE) {
grid_face *f = d->faces[current_face2];
grid_edge *e;
int j;
assert(f != NULL);
/* find dot around this face */
for (j = 0; j < f->order; j++) {
if (f->dots[j] == d)
break;
}
assert(j != f->order); /* must find dot */
/* Around f, required edge is clockwise from the dot. */
e = f->edges[j];
current_face2--;
if (current_face2 == -1)
current_face2 = d->order - 1;
d->edges[current_face2] = e; /* set edge */
/* set face */
if (current_face2 == current_face1)
break;
d->faces[current_face2] =
(e->face1 == f) ? e->face2 : e->face1;
/* There's only 1 infinite face, so we must get all the way
* to current_face1 before we hit it. */
assert(d->faces[current_face2]);
}
}
/* ====== Stage 4 ======
* Compute other grid settings
*/
/* Bounding rectangle */
for (i = 0; i < g->num_dots; i++) {
grid_dot *d = g->dots + i;
if (i == 0) {
g->lowest_x = g->highest_x = d->x;
g->lowest_y = g->highest_y = d->y;
} else {
g->lowest_x = min(g->lowest_x, d->x);
g->highest_x = max(g->highest_x, d->x);
g->lowest_y = min(g->lowest_y, d->y);
g->highest_y = max(g->highest_y, d->y);
}
}
#ifdef DEBUG_GRID
grid_print_derived(g);
#endif
}
/* Helpers for making grid-generation easier. These functions are only
* intended for use during grid generation. */
/* Comparison function for the (tree234) sorted dot list */
static int grid_point_cmp_fn(void *v1, void *v2)
{
grid_dot *p1 = v1;
grid_dot *p2 = v2;
if (p1->y != p2->y)
return p2->y - p1->y;
else
return p2->x - p1->x;
}
/* Add a new face to the grid, with its dot list allocated.
* Assumes there's enough space allocated for the new face in grid->faces */
static void grid_face_add_new(grid *g, int face_size)
{
int i;
grid_face *new_face = g->faces + g->num_faces;
new_face->order = face_size;
new_face->dots = snewn(face_size, grid_dot*);
for (i = 0; i < face_size; i++)
new_face->dots[i] = NULL;
new_face->edges = NULL;
g->num_faces++;
}
/* Assumes dot list has enough space */
static grid_dot *grid_dot_add_new(grid *g, int x, int y)
{
grid_dot *new_dot = g->dots + g->num_dots;
new_dot->order = 0;
new_dot->edges = NULL;
new_dot->faces = NULL;
new_dot->x = x;
new_dot->y = y;
g->num_dots++;
return new_dot;
}
/* Retrieve a dot with these (x,y) coordinates. Either return an existing dot
* in the dot_list, or add a new dot to the grid (and the dot_list) and
* return that.
* Assumes g->dots has enough capacity allocated */
static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y)
{
grid_dot test, *ret;
test.order = 0;
test.edges = NULL;
test.faces = NULL;
test.x = x;
test.y = y;
ret = find234(dot_list, &test, NULL);
if (ret)
return ret;
ret = grid_dot_add_new(g, x, y);
add234(dot_list, ret);
return ret;
}
/* Sets the last face of the grid to include this dot, at this position
* around the face. Assumes num_faces is at least 1 (a new face has
* previously been added, with the required number of dots allocated) */
static void grid_face_set_dot(grid *g, grid_dot *d, int position)
{
grid_face *last_face = g->faces + g->num_faces - 1;
last_face->dots[position] = d;
}
/* ------ Generate various types of grid ------ */
/* General method is to generate faces, by calculating their dot coordinates.
* As new faces are added, we keep track of all the dots so we can tell when
* a new face reuses an existing dot. For example, two squares touching at an
* edge would generate six unique dots: four dots from the first face, then
* two additional dots for the second face, because we detect the other two
* dots have already been taken up. This list is stored in a tree234
* called "points". No extra memory-allocation needed here - we store the
* actual grid_dot* pointers, which all point into the g->dots list.
* For this reason, we have to calculate coordinates in such a way as to
* eliminate any rounding errors, so we can detect when a dot on one
* face precisely lands on a dot of a different face. No floating-point
* arithmetic here!
*/
grid *grid_new_square(int width, int height)
{
int x, y;
/* Side length */
int a = 20;
/* Upper bounds - don't have to be exact */
int max_faces = width * height;
int max_dots = (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = a;
g->faces = snewn(max_faces, grid_face);
g->dots = snewn(max_dots, grid_dot);
points = newtree234(grid_point_cmp_fn);
/* generate square faces */
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
grid_dot *d;
/* face position */
int px = a * x;
int py = a * y;
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + a, py + a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px, py + a);
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;
}
grid *grid_new_honeycomb(int width, int height)
{
int x, y;
/* Vector for side of hexagon - ratio is close to sqrt(3) */
int a = 15;
int b = 26;
/* Upper bounds - don't have to be exact */
int max_faces = width * height;
int max_dots = 2 * (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = 3 * a;
g->faces = snewn(max_faces, grid_face);
g->dots = snewn(max_dots, grid_dot);
points = newtree234(grid_point_cmp_fn);
/* generate hexagonal faces */
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
grid_dot *d;
/* face centre */
int cx = 3 * a * x;
int cy = 2 * b * y;
if (x % 2)
cy += b;
grid_face_add_new(g, 6);
d = grid_get_dot(g, points, cx - a, cy - b);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, cx + a, cy - b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, cx + 2*a, cy);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, cx + a, cy + b);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, cx - a, cy + b);
grid_face_set_dot(g, d, 4);
d = grid_get_dot(g, points, cx - 2*a, cy);
grid_face_set_dot(g, d, 5);
}
}
freetree234(points);
assert(g->num_faces <= max_faces);
assert(g->num_dots <= max_dots);
grid_make_consistent(g);
return g;
}
/* Doesn't use the previous method of generation, it pre-dates it!
* A triangular grid is just about simple enough to do by "brute force" */
grid *grid_new_triangular(int width, int height)
{
int x,y;
/* Vector for side of triangle - ratio is close to sqrt(3) */
int vec_x = 15;
int vec_y = 26;
int index;
/* convenient alias */
int w = width + 1;
grid *g = grid_new();
g->tilesize = 18; /* adjust to your taste */
g->num_faces = width * height * 2;
g->num_dots = (width + 1) * (height + 1);
g->faces = snewn(g->num_faces, grid_face);
g->dots = snewn(g->num_dots, grid_dot);
/* generate dots */
index = 0;
for (y = 0; y <= height; y++) {
for (x = 0; x <= width; x++) {
grid_dot *d = g->dots + index;
/* odd rows are offset to the right */
d->order = 0;
d->edges = NULL;
d->faces = NULL;
d->x = x * 2 * vec_x + ((y % 2) ? vec_x : 0);
d->y = y * vec_y;
index++;
}
}
/* generate faces */
index = 0;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
/* initialise two faces for this (x,y) */
grid_face *f1 = g->faces + index;
grid_face *f2 = f1 + 1;
f1->edges = NULL;
f1->order = 3;
f1->dots = snewn(f1->order, grid_dot*);
f2->edges = NULL;
f2->order = 3;
f2->dots = snewn(f2->order, grid_dot*);
/* face descriptions depend on whether the row-number is
* odd or even */
if (y % 2) {
f1->dots[0] = g->dots + y * w + x;
f1->dots[1] = g->dots + (y + 1) * w + x + 1;
f1->dots[2] = g->dots + (y + 1) * w + x;
f2->dots[0] = g->dots + y * w + x;
f2->dots[1] = g->dots + y * w + x + 1;
f2->dots[2] = g->dots + (y + 1) * w + x + 1;
} else {
f1->dots[0] = g->dots + y * w + x;
f1->dots[1] = g->dots + y * w + x + 1;
f1->dots[2] = g->dots + (y + 1) * w + x;
f2->dots[0] = g->dots + y * w + x + 1;
f2->dots[1] = g->dots + (y + 1) * w + x + 1;
f2->dots[2] = g->dots + (y + 1) * w + x;
}
index += 2;
}
}
grid_make_consistent(g);
return g;
}
grid *grid_new_snubsquare(int width, int height)
{
int x, y;
/* Vector for side of triangle - ratio is close to sqrt(3) */
int a = 15;
int b = 26;
/* Upper bounds - don't have to be exact */
int max_faces = 3 * width * height;
int max_dots = 2 * (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = 18;
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;
/* face position */
int px = (a + b) * x;
int py = (a + b) * y;
/* generate square faces */
grid_face_add_new(g, 4);
if ((x + y) % 2) {
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a + b, py + a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + b, py + a + b);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px, py + b);
grid_face_set_dot(g, d, 3);
} else {
d = grid_get_dot(g, points, px + b, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a + b, py + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + a, py + a + b);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px, py + a);
grid_face_set_dot(g, d, 3);
}
/* generate up/down triangles */
if (x > 0) {
grid_face_add_new(g, 3);
if ((x + y) % 2) {
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px, py + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - a, py);
grid_face_set_dot(g, d, 2);
} else {
d = grid_get_dot(g, points, px, py + a);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py + a + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - a, py + a + b);
grid_face_set_dot(g, d, 2);
}
}
/* generate left/right triangles */
if (y > 0) {
grid_face_add_new(g, 3);
if ((x + y) % 2) {
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a + b, py - a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + a + b, py + a);
grid_face_set_dot(g, d, 2);
} else {
d = grid_get_dot(g, points, px, py - a);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + b, py);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px, py + a);
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;
}
grid *grid_new_cairo(int width, int height)
{
int x, y;
/* Vector for side of pentagon - ratio is close to (4+sqrt(7))/3 */
int a = 14;
int b = 31;
/* Upper bounds - don't have to be exact */
int max_faces = 2 * width * height;
int max_dots = 3 * (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = 40;
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;
/* cell position */
int px = 2 * b * x;
int py = 2 * b * y;
/* horizontal pentagons */
if (y > 0) {
grid_face_add_new(g, 5);
if ((x + y) % 2) {
d = grid_get_dot(g, points, px + a, py - b);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + 2*b - a, py - b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + 2*b, py);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + b, py + a);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 4);
} else {
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + b, py - a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + 2*b, py);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + 2*b - a, py + b);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px + a, py + b);
grid_face_set_dot(g, d, 4);
}
}
/* vertical pentagons */
if (x > 0) {
grid_face_add_new(g, 5);
if ((x + y) % 2) {
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + b, py + a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + b, py + 2*b - a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px, py + 2*b);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px - a, py + b);
grid_face_set_dot(g, d, 4);
} else {
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px, py + 2*b);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px - b, py + 2*b - a);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px - b, py + a);
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;
}
grid *grid_new_greathexagonal(int width, int height)
{
int x, y;
/* Vector for side of triangle - ratio is close to sqrt(3) */
int a = 15;
int b = 26;
/* Upper bounds - don't have to be exact */
int max_faces = 6 * (width + 1) * (height + 1);
int max_dots = 6 * width * height;
tree234 *points;
grid *g = grid_new();
g->tilesize = 18;
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 hexagon */
int px = (3*a + b) * x;
int py = (2*a + 2*b) * y;
if (x % 2)
py += a + b;
/* hexagon */
grid_face_add_new(g, 6);
d = grid_get_dot(g, points, px - a, py - b);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py - b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + 2*a, py);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + a, py + b);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px - a, py + b);
grid_face_set_dot(g, d, 4);
d = grid_get_dot(g, points, px - 2*a, py);
grid_face_set_dot(g, d, 5);
/* square below hexagon */
if (y < height - 1) {
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px - a, py + b);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py + 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);
d = grid_get_dot(g, points, px - a, py + 2*a + b);
grid_face_set_dot(g, d, 3);
}
/* square below right */
if ((x < width - 1) && (((x % 2) == 0) || (y < height - 1))) {
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px + 2*a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + 2*a + b, py + a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + a + b, py + a + b);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + a, py + b);
grid_face_set_dot(g, d, 3);
}
/* square below left */
if ((x > 0) && (((x % 2) == 0) || (y < height - 1))) {
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px - 2*a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px - a, py + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - a - b, py + a + b);
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);
}
/* Triangle below right */
if ((x < width - 1) && (y < height - 1)) {
grid_face_add_new(g, 3);
d = grid_get_dot(g, points, px + a, py + 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 + a, py + 2*a + b);
grid_face_set_dot(g, d, 2);
}
/* Triangle below left */
if ((x > 0) && (y < height - 1)) {
grid_face_add_new(g, 3);
d = grid_get_dot(g, points, px - a, py + b);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px - a, py + 2*a + b);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - a - b, py + 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;
}
grid *grid_new_octagonal(int width, int height)
{
int x, y;
/* b/a approx sqrt(2) */
int a = 29;
int b = 41;
/* Upper bounds - don't have to be exact */
int max_faces = 2 * width * height;
int max_dots = 4 * (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = 40;
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;
/* cell position */
int px = (2*a + b) * x;
int py = (2*a + b) * y;
/* octagon */
grid_face_add_new(g, 8);
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a + b, py);
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 + b);
grid_face_set_dot(g, d, 3);
d = grid_get_dot(g, points, px + a + b, py + 2*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, py + a + b);
grid_face_set_dot(g, d, 6);
d = grid_get_dot(g, points, px, py + a);
grid_face_set_dot(g, d, 7);
/* diamond */
if ((x > 0) && (y > 0)) {
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py - a);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + a, py);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px, py + a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px - a, py);
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;
}
grid *grid_new_kites(int width, int height)
{
int x, y;
/* b/a approx sqrt(3) */
int a = 15;
int b = 26;
/* Upper bounds - don't have to be exact */
int max_faces = 6 * width * height;
int max_dots = 6 * (width + 1) * (height + 1);
tree234 *points;
grid *g = grid_new();
g->tilesize = 40;
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;
/* position of order-6 dot */
int px = 4*b * x;
int py = 6*a * y;
if (y % 2)
px += 2*b;
/* kite pointing up-left */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + 2*b, py);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + 2*b, py + 2*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + b, py + 3*a);
grid_face_set_dot(g, d, 3);
/* kite pointing up */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + b, py + 3*a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px, py + 4*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px - b, py + 3*a);
grid_face_set_dot(g, d, 3);
/* kite pointing up-right */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px - b, py + 3*a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - 2*b, py + 2*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px - 2*b, py);
grid_face_set_dot(g, d, 3);
/* kite pointing down-right */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px - 2*b, py);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px - 2*b, py - 2*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px - b, py - 3*a);
grid_face_set_dot(g, d, 3);
/* kite pointing down */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px - b, py - 3*a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px, py - 4*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + b, py - 3*a);
grid_face_set_dot(g, d, 3);
/* kite pointing down-left */
grid_face_add_new(g, 4);
d = grid_get_dot(g, points, px, py);
grid_face_set_dot(g, d, 0);
d = grid_get_dot(g, points, px + b, py - 3*a);
grid_face_set_dot(g, d, 1);
d = grid_get_dot(g, points, px + 2*b, py - 2*a);
grid_face_set_dot(g, d, 2);
d = grid_get_dot(g, points, px + 2*b, py);
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;
}
grid *grid_new_floret(int width, int height)
{
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 = 75, py = -26; /* |( 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_new();
g->tilesize = 2 * px;
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;
}
grid *grid_new_dodecagonal(int width, int height)
{
int x, y;
/* Vector for side of triangle - ratio is close to sqrt(3) */
int a = 15;
int b = 26;
/* 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_new();
g->tilesize = b;
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;
}
grid *grid_new_greatdodecagonal(int width, int height)
{
int x, y;
/* Vector for side of triangle - ratio is close to sqrt(3) */
int a = 15;
int b = 26;
/* 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_new();
g->tilesize = b;
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;
}
/* ----------- End of grid generators ------------- */