Another patch from Chris Moore implementing two more grid types, both
[sgt/puzzles] / grid.c
1 /*
2 * (c) Lambros Lambrou 2008
3 *
4 * Code for working with general grids, which can be any planar graph
5 * with faces, edges and vertices (dots). Includes generators for a few
6 * types of grid, including square, hexagonal, triangular and others.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <ctype.h>
14 #include <math.h>
15
16 #include "puzzles.h"
17 #include "tree234.h"
18 #include "grid.h"
19
20 /* Debugging options */
21
22 /*
23 #define DEBUG_GRID
24 */
25
26 /* ----------------------------------------------------------------------
27 * Deallocate or dereference a grid
28 */
29 void grid_free(grid *g)
30 {
31 assert(g->refcount);
32
33 g->refcount--;
34 if (g->refcount == 0) {
35 int i;
36 for (i = 0; i < g->num_faces; i++) {
37 sfree(g->faces[i].dots);
38 sfree(g->faces[i].edges);
39 }
40 for (i = 0; i < g->num_dots; i++) {
41 sfree(g->dots[i].faces);
42 sfree(g->dots[i].edges);
43 }
44 sfree(g->faces);
45 sfree(g->edges);
46 sfree(g->dots);
47 sfree(g);
48 }
49 }
50
51 /* Used by the other grid generators. Create a brand new grid with nothing
52 * initialised (all lists are NULL) */
53 static grid *grid_new(void)
54 {
55 grid *g = snew(grid);
56 g->faces = NULL;
57 g->edges = NULL;
58 g->dots = NULL;
59 g->num_faces = g->num_edges = g->num_dots = 0;
60 g->refcount = 1;
61 g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0;
62 return g;
63 }
64
65 /* Helper function to calculate perpendicular distance from
66 * a point P to a line AB. A and B mustn't be equal here.
67 *
68 * Well-known formula for area A of a triangle:
69 * / 1 1 1 \
70 * 2A = determinant of matrix | px ax bx |
71 * \ py ay by /
72 *
73 * Also well-known: 2A = base * height
74 * = perpendicular distance * line-length.
75 *
76 * Combining gives: distance = determinant / line-length(a,b)
77 */
78 static double point_line_distance(long px, long py,
79 long ax, long ay,
80 long bx, long by)
81 {
82 long det = ax*by - bx*ay + bx*py - px*by + px*ay - ax*py;
83 double len;
84 det = max(det, -det);
85 len = sqrt(SQ(ax - bx) + SQ(ay - by));
86 return det / len;
87 }
88
89 /* Determine nearest edge to where the user clicked.
90 * (x, y) is the clicked location, converted to grid coordinates.
91 * Returns the nearest edge, or NULL if no edge is reasonably
92 * near the position.
93 *
94 * Just judging edges by perpendicular distance is not quite right -
95 * the edge might be "off to one side". So we insist that the triangle
96 * with (x,y) has acute angles at the edge's dots.
97 *
98 * edge1
99 * *---------*------
100 * |
101 * | *(x,y)
102 * edge2 |
103 * | edge2 is OK, but edge1 is not, even though
104 * | edge1 is perpendicularly closer to (x,y)
105 * *
106 *
107 */
108 grid_edge *grid_nearest_edge(grid *g, int x, int y)
109 {
110 grid_edge *best_edge;
111 double best_distance = 0;
112 int i;
113
114 best_edge = NULL;
115
116 for (i = 0; i < g->num_edges; i++) {
117 grid_edge *e = &g->edges[i];
118 long e2; /* squared length of edge */
119 long a2, b2; /* squared lengths of other sides */
120 double dist;
121
122 /* See if edge e is eligible - the triangle must have acute angles
123 * at the edge's dots.
124 * Pythagoras formula h^2 = a^2 + b^2 detects right-angles,
125 * so detect acute angles by testing for h^2 < a^2 + b^2 */
126 e2 = SQ((long)e->dot1->x - (long)e->dot2->x) + SQ((long)e->dot1->y - (long)e->dot2->y);
127 a2 = SQ((long)e->dot1->x - (long)x) + SQ((long)e->dot1->y - (long)y);
128 b2 = SQ((long)e->dot2->x - (long)x) + SQ((long)e->dot2->y - (long)y);
129 if (a2 >= e2 + b2) continue;
130 if (b2 >= e2 + a2) continue;
131
132 /* e is eligible so far. Now check the edge is reasonably close
133 * to where the user clicked. Don't want to toggle an edge if the
134 * click was way off the grid.
135 * There is room for experimentation here. We could check the
136 * perpendicular distance is within a certain fraction of the length
137 * of the edge. That amounts to testing a rectangular region around
138 * the edge.
139 * Alternatively, we could check that the angle at the point is obtuse.
140 * That would amount to testing a circular region with the edge as
141 * diameter. */
142 dist = point_line_distance((long)x, (long)y,
143 (long)e->dot1->x, (long)e->dot1->y,
144 (long)e->dot2->x, (long)e->dot2->y);
145 /* Is dist more than half edge length ? */
146 if (4 * SQ(dist) > e2)
147 continue;
148
149 if (best_edge == NULL || dist < best_distance) {
150 best_edge = e;
151 best_distance = dist;
152 }
153 }
154 return best_edge;
155 }
156
157 /* ----------------------------------------------------------------------
158 * Grid generation
159 */
160
161 #ifdef DEBUG_GRID
162 /* Show the basic grid information, before doing grid_make_consistent */
163 static void grid_print_basic(grid *g)
164 {
165 /* TODO: Maybe we should generate an SVG image of the dots and lines
166 * of the grid here, before grid_make_consistent.
167 * Would help with debugging grid generation. */
168 int i;
169 printf("--- Basic Grid Data ---\n");
170 for (i = 0; i < g->num_faces; i++) {
171 grid_face *f = g->faces + i;
172 printf("Face %d: dots[", i);
173 int j;
174 for (j = 0; j < f->order; j++) {
175 grid_dot *d = f->dots[j];
176 printf("%s%d", j ? "," : "", (int)(d - g->dots));
177 }
178 printf("]\n");
179 }
180 }
181 /* Show the derived grid information, computed by grid_make_consistent */
182 static void grid_print_derived(grid *g)
183 {
184 /* edges */
185 int i;
186 printf("--- Derived Grid Data ---\n");
187 for (i = 0; i < g->num_edges; i++) {
188 grid_edge *e = g->edges + i;
189 printf("Edge %d: dots[%d,%d] faces[%d,%d]\n",
190 i, (int)(e->dot1 - g->dots), (int)(e->dot2 - g->dots),
191 e->face1 ? (int)(e->face1 - g->faces) : -1,
192 e->face2 ? (int)(e->face2 - g->faces) : -1);
193 }
194 /* faces */
195 for (i = 0; i < g->num_faces; i++) {
196 grid_face *f = g->faces + i;
197 int j;
198 printf("Face %d: faces[", i);
199 for (j = 0; j < f->order; j++) {
200 grid_edge *e = f->edges[j];
201 grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1;
202 printf("%s%d", j ? "," : "", f2 ? (int)(f2 - g->faces) : -1);
203 }
204 printf("]\n");
205 }
206 /* dots */
207 for (i = 0; i < g->num_dots; i++) {
208 grid_dot *d = g->dots + i;
209 int j;
210 printf("Dot %d: dots[", i);
211 for (j = 0; j < d->order; j++) {
212 grid_edge *e = d->edges[j];
213 grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1;
214 printf("%s%d", j ? "," : "", (int)(d2 - g->dots));
215 }
216 printf("] faces[");
217 for (j = 0; j < d->order; j++) {
218 grid_face *f = d->faces[j];
219 printf("%s%d", j ? "," : "", f ? (int)(f - g->faces) : -1);
220 }
221 printf("]\n");
222 }
223 }
224 #endif /* DEBUG_GRID */
225
226 /* Helper function for building incomplete-edges list in
227 * grid_make_consistent() */
228 static int grid_edge_bydots_cmpfn(void *v1, void *v2)
229 {
230 grid_edge *a = v1;
231 grid_edge *b = v2;
232 grid_dot *da, *db;
233
234 /* Pointer subtraction is valid here, because all dots point into the
235 * same dot-list (g->dots).
236 * Edges are not "normalised" - the 2 dots could be stored in any order,
237 * so we need to take this into account when comparing edges. */
238
239 /* Compare first dots */
240 da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2;
241 db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2;
242 if (da != db)
243 return db - da;
244 /* Compare last dots */
245 da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1;
246 db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1;
247 if (da != db)
248 return db - da;
249
250 return 0;
251 }
252
253 /* Input: grid has its dots and faces initialised:
254 * - dots have (optionally) x and y coordinates, but no edges or faces
255 * (pointers are NULL).
256 * - edges not initialised at all
257 * - faces initialised and know which dots they have (but no edges yet). The
258 * dots around each face are assumed to be clockwise.
259 *
260 * Output: grid is complete and valid with all relationships defined.
261 */
262 static void grid_make_consistent(grid *g)
263 {
264 int i;
265 tree234 *incomplete_edges;
266 grid_edge *next_new_edge; /* Where new edge will go into g->edges */
267
268 #ifdef DEBUG_GRID
269 grid_print_basic(g);
270 #endif
271
272 /* ====== Stage 1 ======
273 * Generate edges
274 */
275
276 /* We know how many dots and faces there are, so we can find the exact
277 * number of edges from Euler's polyhedral formula: F + V = E + 2 .
278 * We use "-1", not "-2" here, because Euler's formula includes the
279 * infinite face, which we don't count. */
280 g->num_edges = g->num_faces + g->num_dots - 1;
281 g->edges = snewn(g->num_edges, grid_edge);
282 next_new_edge = g->edges;
283
284 /* Iterate over faces, and over each face's dots, generating edges as we
285 * go. As we find each new edge, we can immediately fill in the edge's
286 * dots, but only one of the edge's faces. Later on in the iteration, we
287 * will find the same edge again (unless it's on the border), but we will
288 * know the other face.
289 * For efficiency, maintain a list of the incomplete edges, sorted by
290 * their dots. */
291 incomplete_edges = newtree234(grid_edge_bydots_cmpfn);
292 for (i = 0; i < g->num_faces; i++) {
293 grid_face *f = g->faces + i;
294 int j;
295 for (j = 0; j < f->order; j++) {
296 grid_edge e; /* fake edge for searching */
297 grid_edge *edge_found;
298 int j2 = j + 1;
299 if (j2 == f->order)
300 j2 = 0;
301 e.dot1 = f->dots[j];
302 e.dot2 = f->dots[j2];
303 /* Use del234 instead of find234, because we always want to
304 * remove the edge if found */
305 edge_found = del234(incomplete_edges, &e);
306 if (edge_found) {
307 /* This edge already added, so fill out missing face.
308 * Edge is already removed from incomplete_edges. */
309 edge_found->face2 = f;
310 } else {
311 assert(next_new_edge - g->edges < g->num_edges);
312 next_new_edge->dot1 = e.dot1;
313 next_new_edge->dot2 = e.dot2;
314 next_new_edge->face1 = f;
315 next_new_edge->face2 = NULL; /* potentially infinite face */
316 add234(incomplete_edges, next_new_edge);
317 ++next_new_edge;
318 }
319 }
320 }
321 freetree234(incomplete_edges);
322
323 /* ====== Stage 2 ======
324 * For each face, build its edge list.
325 */
326
327 /* Allocate space for each edge list. Can do this, because each face's
328 * edge-list is the same size as its dot-list. */
329 for (i = 0; i < g->num_faces; i++) {
330 grid_face *f = g->faces + i;
331 int j;
332 f->edges = snewn(f->order, grid_edge*);
333 /* Preload with NULLs, to help detect potential bugs. */
334 for (j = 0; j < f->order; j++)
335 f->edges[j] = NULL;
336 }
337
338 /* Iterate over each edge, and over both its faces. Add this edge to
339 * the face's edge-list, after finding where it should go in the
340 * sequence. */
341 for (i = 0; i < g->num_edges; i++) {
342 grid_edge *e = g->edges + i;
343 int j;
344 for (j = 0; j < 2; j++) {
345 grid_face *f = j ? e->face2 : e->face1;
346 int k, k2;
347 if (f == NULL) continue;
348 /* Find one of the dots around the face */
349 for (k = 0; k < f->order; k++) {
350 if (f->dots[k] == e->dot1)
351 break; /* found dot1 */
352 }
353 assert(k != f->order); /* Must find the dot around this face */
354
355 /* Labelling scheme: as we walk clockwise around the face,
356 * starting at dot0 (f->dots[0]), we hit:
357 * (dot0), edge0, dot1, edge1, dot2,...
358 *
359 * 0
360 * 0-----1
361 * |
362 * |1
363 * |
364 * 3-----2
365 * 2
366 *
367 * Therefore, edgeK joins dotK and dot{K+1}
368 */
369
370 /* Around this face, either the next dot or the previous dot
371 * must be e->dot2. Otherwise the edge is wrong. */
372 k2 = k + 1;
373 if (k2 == f->order)
374 k2 = 0;
375 if (f->dots[k2] == e->dot2) {
376 /* dot1(k) and dot2(k2) go clockwise around this face, so add
377 * this edge at position k (see diagram). */
378 assert(f->edges[k] == NULL);
379 f->edges[k] = e;
380 continue;
381 }
382 /* Try previous dot */
383 k2 = k - 1;
384 if (k2 == -1)
385 k2 = f->order - 1;
386 if (f->dots[k2] == e->dot2) {
387 /* dot1(k) and dot2(k2) go anticlockwise around this face. */
388 assert(f->edges[k2] == NULL);
389 f->edges[k2] = e;
390 continue;
391 }
392 assert(!"Grid broken: bad edge-face relationship");
393 }
394 }
395
396 /* ====== Stage 3 ======
397 * For each dot, build its edge-list and face-list.
398 */
399
400 /* We don't know how many edges/faces go around each dot, so we can't
401 * allocate the right space for these lists. Pre-compute the sizes by
402 * iterating over each edge and recording a tally against each dot. */
403 for (i = 0; i < g->num_dots; i++) {
404 g->dots[i].order = 0;
405 }
406 for (i = 0; i < g->num_edges; i++) {
407 grid_edge *e = g->edges + i;
408 ++(e->dot1->order);
409 ++(e->dot2->order);
410 }
411 /* Now we have the sizes, pre-allocate the edge and face lists. */
412 for (i = 0; i < g->num_dots; i++) {
413 grid_dot *d = g->dots + i;
414 int j;
415 assert(d->order >= 2); /* sanity check */
416 d->edges = snewn(d->order, grid_edge*);
417 d->faces = snewn(d->order, grid_face*);
418 for (j = 0; j < d->order; j++) {
419 d->edges[j] = NULL;
420 d->faces[j] = NULL;
421 }
422 }
423 /* For each dot, need to find a face that touches it, so we can seed
424 * the edge-face-edge-face process around each dot. */
425 for (i = 0; i < g->num_faces; i++) {
426 grid_face *f = g->faces + i;
427 int j;
428 for (j = 0; j < f->order; j++) {
429 grid_dot *d = f->dots[j];
430 d->faces[0] = f;
431 }
432 }
433 /* Each dot now has a face in its first slot. Generate the remaining
434 * faces and edges around the dot, by searching both clockwise and
435 * anticlockwise from the first face. Need to do both directions,
436 * because of the possibility of hitting the infinite face, which
437 * blocks progress. But there's only one such face, so we will
438 * succeed in finding every edge and face this way. */
439 for (i = 0; i < g->num_dots; i++) {
440 grid_dot *d = g->dots + i;
441 int current_face1 = 0; /* ascends clockwise */
442 int current_face2 = 0; /* descends anticlockwise */
443
444 /* Labelling scheme: as we walk clockwise around the dot, starting
445 * at face0 (d->faces[0]), we hit:
446 * (face0), edge0, face1, edge1, face2,...
447 *
448 * 0
449 * |
450 * 0 | 1
451 * |
452 * -----d-----1
453 * |
454 * | 2
455 * |
456 * 2
457 *
458 * So, for example, face1 should be joined to edge0 and edge1,
459 * and those edges should appear in an anticlockwise sense around
460 * that face (see diagram). */
461
462 /* clockwise search */
463 while (TRUE) {
464 grid_face *f = d->faces[current_face1];
465 grid_edge *e;
466 int j;
467 assert(f != NULL);
468 /* find dot around this face */
469 for (j = 0; j < f->order; j++) {
470 if (f->dots[j] == d)
471 break;
472 }
473 assert(j != f->order); /* must find dot */
474
475 /* Around f, required edge is anticlockwise from the dot. See
476 * the other labelling scheme higher up, for why we subtract 1
477 * from j. */
478 j--;
479 if (j == -1)
480 j = f->order - 1;
481 e = f->edges[j];
482 d->edges[current_face1] = e; /* set edge */
483 current_face1++;
484 if (current_face1 == d->order)
485 break;
486 else {
487 /* set face */
488 d->faces[current_face1] =
489 (e->face1 == f) ? e->face2 : e->face1;
490 if (d->faces[current_face1] == NULL)
491 break; /* cannot progress beyond infinite face */
492 }
493 }
494 /* If the clockwise search made it all the way round, don't need to
495 * bother with the anticlockwise search. */
496 if (current_face1 == d->order)
497 continue; /* this dot is complete, move on to next dot */
498
499 /* anticlockwise search */
500 while (TRUE) {
501 grid_face *f = d->faces[current_face2];
502 grid_edge *e;
503 int j;
504 assert(f != NULL);
505 /* find dot around this face */
506 for (j = 0; j < f->order; j++) {
507 if (f->dots[j] == d)
508 break;
509 }
510 assert(j != f->order); /* must find dot */
511
512 /* Around f, required edge is clockwise from the dot. */
513 e = f->edges[j];
514
515 current_face2--;
516 if (current_face2 == -1)
517 current_face2 = d->order - 1;
518 d->edges[current_face2] = e; /* set edge */
519
520 /* set face */
521 if (current_face2 == current_face1)
522 break;
523 d->faces[current_face2] =
524 (e->face1 == f) ? e->face2 : e->face1;
525 /* There's only 1 infinite face, so we must get all the way
526 * to current_face1 before we hit it. */
527 assert(d->faces[current_face2]);
528 }
529 }
530
531 /* ====== Stage 4 ======
532 * Compute other grid settings
533 */
534
535 /* Bounding rectangle */
536 for (i = 0; i < g->num_dots; i++) {
537 grid_dot *d = g->dots + i;
538 if (i == 0) {
539 g->lowest_x = g->highest_x = d->x;
540 g->lowest_y = g->highest_y = d->y;
541 } else {
542 g->lowest_x = min(g->lowest_x, d->x);
543 g->highest_x = max(g->highest_x, d->x);
544 g->lowest_y = min(g->lowest_y, d->y);
545 g->highest_y = max(g->highest_y, d->y);
546 }
547 }
548
549 #ifdef DEBUG_GRID
550 grid_print_derived(g);
551 #endif
552 }
553
554 /* Helpers for making grid-generation easier. These functions are only
555 * intended for use during grid generation. */
556
557 /* Comparison function for the (tree234) sorted dot list */
558 static int grid_point_cmp_fn(void *v1, void *v2)
559 {
560 grid_dot *p1 = v1;
561 grid_dot *p2 = v2;
562 if (p1->y != p2->y)
563 return p2->y - p1->y;
564 else
565 return p2->x - p1->x;
566 }
567 /* Add a new face to the grid, with its dot list allocated.
568 * Assumes there's enough space allocated for the new face in grid->faces */
569 static void grid_face_add_new(grid *g, int face_size)
570 {
571 int i;
572 grid_face *new_face = g->faces + g->num_faces;
573 new_face->order = face_size;
574 new_face->dots = snewn(face_size, grid_dot*);
575 for (i = 0; i < face_size; i++)
576 new_face->dots[i] = NULL;
577 new_face->edges = NULL;
578 g->num_faces++;
579 }
580 /* Assumes dot list has enough space */
581 static grid_dot *grid_dot_add_new(grid *g, int x, int y)
582 {
583 grid_dot *new_dot = g->dots + g->num_dots;
584 new_dot->order = 0;
585 new_dot->edges = NULL;
586 new_dot->faces = NULL;
587 new_dot->x = x;
588 new_dot->y = y;
589 g->num_dots++;
590 return new_dot;
591 }
592 /* Retrieve a dot with these (x,y) coordinates. Either return an existing dot
593 * in the dot_list, or add a new dot to the grid (and the dot_list) and
594 * return that.
595 * Assumes g->dots has enough capacity allocated */
596 static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y)
597 {
598 grid_dot test, *ret;
599
600 test.order = 0;
601 test.edges = NULL;
602 test.faces = NULL;
603 test.x = x;
604 test.y = y;
605 ret = find234(dot_list, &test, NULL);
606 if (ret)
607 return ret;
608
609 ret = grid_dot_add_new(g, x, y);
610 add234(dot_list, ret);
611 return ret;
612 }
613
614 /* Sets the last face of the grid to include this dot, at this position
615 * around the face. Assumes num_faces is at least 1 (a new face has
616 * previously been added, with the required number of dots allocated) */
617 static void grid_face_set_dot(grid *g, grid_dot *d, int position)
618 {
619 grid_face *last_face = g->faces + g->num_faces - 1;
620 last_face->dots[position] = d;
621 }
622
623 /* ------ Generate various types of grid ------ */
624
625 /* General method is to generate faces, by calculating their dot coordinates.
626 * As new faces are added, we keep track of all the dots so we can tell when
627 * a new face reuses an existing dot. For example, two squares touching at an
628 * edge would generate six unique dots: four dots from the first face, then
629 * two additional dots for the second face, because we detect the other two
630 * dots have already been taken up. This list is stored in a tree234
631 * called "points". No extra memory-allocation needed here - we store the
632 * actual grid_dot* pointers, which all point into the g->dots list.
633 * For this reason, we have to calculate coordinates in such a way as to
634 * eliminate any rounding errors, so we can detect when a dot on one
635 * face precisely lands on a dot of a different face. No floating-point
636 * arithmetic here!
637 */
638
639 grid *grid_new_square(int width, int height)
640 {
641 int x, y;
642 /* Side length */
643 int a = 20;
644
645 /* Upper bounds - don't have to be exact */
646 int max_faces = width * height;
647 int max_dots = (width + 1) * (height + 1);
648
649 tree234 *points;
650
651 grid *g = grid_new();
652 g->tilesize = a;
653 g->faces = snewn(max_faces, grid_face);
654 g->dots = snewn(max_dots, grid_dot);
655
656 points = newtree234(grid_point_cmp_fn);
657
658 /* generate square faces */
659 for (y = 0; y < height; y++) {
660 for (x = 0; x < width; x++) {
661 grid_dot *d;
662 /* face position */
663 int px = a * x;
664 int py = a * y;
665
666 grid_face_add_new(g, 4);
667 d = grid_get_dot(g, points, px, py);
668 grid_face_set_dot(g, d, 0);
669 d = grid_get_dot(g, points, px + a, py);
670 grid_face_set_dot(g, d, 1);
671 d = grid_get_dot(g, points, px + a, py + a);
672 grid_face_set_dot(g, d, 2);
673 d = grid_get_dot(g, points, px, py + a);
674 grid_face_set_dot(g, d, 3);
675 }
676 }
677
678 freetree234(points);
679 assert(g->num_faces <= max_faces);
680 assert(g->num_dots <= max_dots);
681
682 grid_make_consistent(g);
683 return g;
684 }
685
686 grid *grid_new_honeycomb(int width, int height)
687 {
688 int x, y;
689 /* Vector for side of hexagon - ratio is close to sqrt(3) */
690 int a = 15;
691 int b = 26;
692
693 /* Upper bounds - don't have to be exact */
694 int max_faces = width * height;
695 int max_dots = 2 * (width + 1) * (height + 1);
696
697 tree234 *points;
698
699 grid *g = grid_new();
700 g->tilesize = 3 * a;
701 g->faces = snewn(max_faces, grid_face);
702 g->dots = snewn(max_dots, grid_dot);
703
704 points = newtree234(grid_point_cmp_fn);
705
706 /* generate hexagonal faces */
707 for (y = 0; y < height; y++) {
708 for (x = 0; x < width; x++) {
709 grid_dot *d;
710 /* face centre */
711 int cx = 3 * a * x;
712 int cy = 2 * b * y;
713 if (x % 2)
714 cy += b;
715 grid_face_add_new(g, 6);
716
717 d = grid_get_dot(g, points, cx - a, cy - b);
718 grid_face_set_dot(g, d, 0);
719 d = grid_get_dot(g, points, cx + a, cy - b);
720 grid_face_set_dot(g, d, 1);
721 d = grid_get_dot(g, points, cx + 2*a, cy);
722 grid_face_set_dot(g, d, 2);
723 d = grid_get_dot(g, points, cx + a, cy + b);
724 grid_face_set_dot(g, d, 3);
725 d = grid_get_dot(g, points, cx - a, cy + b);
726 grid_face_set_dot(g, d, 4);
727 d = grid_get_dot(g, points, cx - 2*a, cy);
728 grid_face_set_dot(g, d, 5);
729 }
730 }
731
732 freetree234(points);
733 assert(g->num_faces <= max_faces);
734 assert(g->num_dots <= max_dots);
735
736 grid_make_consistent(g);
737 return g;
738 }
739
740 /* Doesn't use the previous method of generation, it pre-dates it!
741 * A triangular grid is just about simple enough to do by "brute force" */
742 grid *grid_new_triangular(int width, int height)
743 {
744 int x,y;
745
746 /* Vector for side of triangle - ratio is close to sqrt(3) */
747 int vec_x = 15;
748 int vec_y = 26;
749
750 int index;
751
752 /* convenient alias */
753 int w = width + 1;
754
755 grid *g = grid_new();
756 g->tilesize = 18; /* adjust to your taste */
757
758 g->num_faces = width * height * 2;
759 g->num_dots = (width + 1) * (height + 1);
760 g->faces = snewn(g->num_faces, grid_face);
761 g->dots = snewn(g->num_dots, grid_dot);
762
763 /* generate dots */
764 index = 0;
765 for (y = 0; y <= height; y++) {
766 for (x = 0; x <= width; x++) {
767 grid_dot *d = g->dots + index;
768 /* odd rows are offset to the right */
769 d->order = 0;
770 d->edges = NULL;
771 d->faces = NULL;
772 d->x = x * 2 * vec_x + ((y % 2) ? vec_x : 0);
773 d->y = y * vec_y;
774 index++;
775 }
776 }
777
778 /* generate faces */
779 index = 0;
780 for (y = 0; y < height; y++) {
781 for (x = 0; x < width; x++) {
782 /* initialise two faces for this (x,y) */
783 grid_face *f1 = g->faces + index;
784 grid_face *f2 = f1 + 1;
785 f1->edges = NULL;
786 f1->order = 3;
787 f1->dots = snewn(f1->order, grid_dot*);
788 f2->edges = NULL;
789 f2->order = 3;
790 f2->dots = snewn(f2->order, grid_dot*);
791
792 /* face descriptions depend on whether the row-number is
793 * odd or even */
794 if (y % 2) {
795 f1->dots[0] = g->dots + y * w + x;
796 f1->dots[1] = g->dots + (y + 1) * w + x + 1;
797 f1->dots[2] = g->dots + (y + 1) * w + x;
798 f2->dots[0] = g->dots + y * w + x;
799 f2->dots[1] = g->dots + y * w + x + 1;
800 f2->dots[2] = g->dots + (y + 1) * w + x + 1;
801 } else {
802 f1->dots[0] = g->dots + y * w + x;
803 f1->dots[1] = g->dots + y * w + x + 1;
804 f1->dots[2] = g->dots + (y + 1) * w + x;
805 f2->dots[0] = g->dots + y * w + x + 1;
806 f2->dots[1] = g->dots + (y + 1) * w + x + 1;
807 f2->dots[2] = g->dots + (y + 1) * w + x;
808 }
809 index += 2;
810 }
811 }
812
813 grid_make_consistent(g);
814 return g;
815 }
816
817 grid *grid_new_snubsquare(int width, int height)
818 {
819 int x, y;
820 /* Vector for side of triangle - ratio is close to sqrt(3) */
821 int a = 15;
822 int b = 26;
823
824 /* Upper bounds - don't have to be exact */
825 int max_faces = 3 * width * height;
826 int max_dots = 2 * (width + 1) * (height + 1);
827
828 tree234 *points;
829
830 grid *g = grid_new();
831 g->tilesize = 18;
832 g->faces = snewn(max_faces, grid_face);
833 g->dots = snewn(max_dots, grid_dot);
834
835 points = newtree234(grid_point_cmp_fn);
836
837 for (y = 0; y < height; y++) {
838 for (x = 0; x < width; x++) {
839 grid_dot *d;
840 /* face position */
841 int px = (a + b) * x;
842 int py = (a + b) * y;
843
844 /* generate square faces */
845 grid_face_add_new(g, 4);
846 if ((x + y) % 2) {
847 d = grid_get_dot(g, points, px + a, py);
848 grid_face_set_dot(g, d, 0);
849 d = grid_get_dot(g, points, px + a + b, py + a);
850 grid_face_set_dot(g, d, 1);
851 d = grid_get_dot(g, points, px + b, py + a + b);
852 grid_face_set_dot(g, d, 2);
853 d = grid_get_dot(g, points, px, py + b);
854 grid_face_set_dot(g, d, 3);
855 } else {
856 d = grid_get_dot(g, points, px + b, py);
857 grid_face_set_dot(g, d, 0);
858 d = grid_get_dot(g, points, px + a + b, py + b);
859 grid_face_set_dot(g, d, 1);
860 d = grid_get_dot(g, points, px + a, py + a + b);
861 grid_face_set_dot(g, d, 2);
862 d = grid_get_dot(g, points, px, py + a);
863 grid_face_set_dot(g, d, 3);
864 }
865
866 /* generate up/down triangles */
867 if (x > 0) {
868 grid_face_add_new(g, 3);
869 if ((x + y) % 2) {
870 d = grid_get_dot(g, points, px + a, py);
871 grid_face_set_dot(g, d, 0);
872 d = grid_get_dot(g, points, px, py + b);
873 grid_face_set_dot(g, d, 1);
874 d = grid_get_dot(g, points, px - a, py);
875 grid_face_set_dot(g, d, 2);
876 } else {
877 d = grid_get_dot(g, points, px, py + a);
878 grid_face_set_dot(g, d, 0);
879 d = grid_get_dot(g, points, px + a, py + a + b);
880 grid_face_set_dot(g, d, 1);
881 d = grid_get_dot(g, points, px - a, py + a + b);
882 grid_face_set_dot(g, d, 2);
883 }
884 }
885
886 /* generate left/right triangles */
887 if (y > 0) {
888 grid_face_add_new(g, 3);
889 if ((x + y) % 2) {
890 d = grid_get_dot(g, points, px + a, py);
891 grid_face_set_dot(g, d, 0);
892 d = grid_get_dot(g, points, px + a + b, py - a);
893 grid_face_set_dot(g, d, 1);
894 d = grid_get_dot(g, points, px + a + b, py + a);
895 grid_face_set_dot(g, d, 2);
896 } else {
897 d = grid_get_dot(g, points, px, py - a);
898 grid_face_set_dot(g, d, 0);
899 d = grid_get_dot(g, points, px + b, py);
900 grid_face_set_dot(g, d, 1);
901 d = grid_get_dot(g, points, px, py + a);
902 grid_face_set_dot(g, d, 2);
903 }
904 }
905 }
906 }
907
908 freetree234(points);
909 assert(g->num_faces <= max_faces);
910 assert(g->num_dots <= max_dots);
911
912 grid_make_consistent(g);
913 return g;
914 }
915
916 grid *grid_new_cairo(int width, int height)
917 {
918 int x, y;
919 /* Vector for side of pentagon - ratio is close to (4+sqrt(7))/3 */
920 int a = 14;
921 int b = 31;
922
923 /* Upper bounds - don't have to be exact */
924 int max_faces = 2 * width * height;
925 int max_dots = 3 * (width + 1) * (height + 1);
926
927 tree234 *points;
928
929 grid *g = grid_new();
930 g->tilesize = 40;
931 g->faces = snewn(max_faces, grid_face);
932 g->dots = snewn(max_dots, grid_dot);
933
934 points = newtree234(grid_point_cmp_fn);
935
936 for (y = 0; y < height; y++) {
937 for (x = 0; x < width; x++) {
938 grid_dot *d;
939 /* cell position */
940 int px = 2 * b * x;
941 int py = 2 * b * y;
942
943 /* horizontal pentagons */
944 if (y > 0) {
945 grid_face_add_new(g, 5);
946 if ((x + y) % 2) {
947 d = grid_get_dot(g, points, px + a, py - b);
948 grid_face_set_dot(g, d, 0);
949 d = grid_get_dot(g, points, px + 2*b - a, py - b);
950 grid_face_set_dot(g, d, 1);
951 d = grid_get_dot(g, points, px + 2*b, py);
952 grid_face_set_dot(g, d, 2);
953 d = grid_get_dot(g, points, px + b, py + a);
954 grid_face_set_dot(g, d, 3);
955 d = grid_get_dot(g, points, px, py);
956 grid_face_set_dot(g, d, 4);
957 } else {
958 d = grid_get_dot(g, points, px, py);
959 grid_face_set_dot(g, d, 0);
960 d = grid_get_dot(g, points, px + b, py - a);
961 grid_face_set_dot(g, d, 1);
962 d = grid_get_dot(g, points, px + 2*b, py);
963 grid_face_set_dot(g, d, 2);
964 d = grid_get_dot(g, points, px + 2*b - a, py + b);
965 grid_face_set_dot(g, d, 3);
966 d = grid_get_dot(g, points, px + a, py + b);
967 grid_face_set_dot(g, d, 4);
968 }
969 }
970 /* vertical pentagons */
971 if (x > 0) {
972 grid_face_add_new(g, 5);
973 if ((x + y) % 2) {
974 d = grid_get_dot(g, points, px, py);
975 grid_face_set_dot(g, d, 0);
976 d = grid_get_dot(g, points, px + b, py + a);
977 grid_face_set_dot(g, d, 1);
978 d = grid_get_dot(g, points, px + b, py + 2*b - a);
979 grid_face_set_dot(g, d, 2);
980 d = grid_get_dot(g, points, px, py + 2*b);
981 grid_face_set_dot(g, d, 3);
982 d = grid_get_dot(g, points, px - a, py + b);
983 grid_face_set_dot(g, d, 4);
984 } else {
985 d = grid_get_dot(g, points, px, py);
986 grid_face_set_dot(g, d, 0);
987 d = grid_get_dot(g, points, px + a, py + b);
988 grid_face_set_dot(g, d, 1);
989 d = grid_get_dot(g, points, px, py + 2*b);
990 grid_face_set_dot(g, d, 2);
991 d = grid_get_dot(g, points, px - b, py + 2*b - a);
992 grid_face_set_dot(g, d, 3);
993 d = grid_get_dot(g, points, px - b, py + a);
994 grid_face_set_dot(g, d, 4);
995 }
996 }
997 }
998 }
999
1000 freetree234(points);
1001 assert(g->num_faces <= max_faces);
1002 assert(g->num_dots <= max_dots);
1003
1004 grid_make_consistent(g);
1005 return g;
1006 }
1007
1008 grid *grid_new_greathexagonal(int width, int height)
1009 {
1010 int x, y;
1011 /* Vector for side of triangle - ratio is close to sqrt(3) */
1012 int a = 15;
1013 int b = 26;
1014
1015 /* Upper bounds - don't have to be exact */
1016 int max_faces = 6 * (width + 1) * (height + 1);
1017 int max_dots = 6 * width * height;
1018
1019 tree234 *points;
1020
1021 grid *g = grid_new();
1022 g->tilesize = 18;
1023 g->faces = snewn(max_faces, grid_face);
1024 g->dots = snewn(max_dots, grid_dot);
1025
1026 points = newtree234(grid_point_cmp_fn);
1027
1028 for (y = 0; y < height; y++) {
1029 for (x = 0; x < width; x++) {
1030 grid_dot *d;
1031 /* centre of hexagon */
1032 int px = (3*a + b) * x;
1033 int py = (2*a + 2*b) * y;
1034 if (x % 2)
1035 py += a + b;
1036
1037 /* hexagon */
1038 grid_face_add_new(g, 6);
1039 d = grid_get_dot(g, points, px - a, py - b);
1040 grid_face_set_dot(g, d, 0);
1041 d = grid_get_dot(g, points, px + a, py - b);
1042 grid_face_set_dot(g, d, 1);
1043 d = grid_get_dot(g, points, px + 2*a, py);
1044 grid_face_set_dot(g, d, 2);
1045 d = grid_get_dot(g, points, px + a, py + b);
1046 grid_face_set_dot(g, d, 3);
1047 d = grid_get_dot(g, points, px - a, py + b);
1048 grid_face_set_dot(g, d, 4);
1049 d = grid_get_dot(g, points, px - 2*a, py);
1050 grid_face_set_dot(g, d, 5);
1051
1052 /* square below hexagon */
1053 if (y < height - 1) {
1054 grid_face_add_new(g, 4);
1055 d = grid_get_dot(g, points, px - a, py + b);
1056 grid_face_set_dot(g, d, 0);
1057 d = grid_get_dot(g, points, px + a, py + b);
1058 grid_face_set_dot(g, d, 1);
1059 d = grid_get_dot(g, points, px + a, py + 2*a + b);
1060 grid_face_set_dot(g, d, 2);
1061 d = grid_get_dot(g, points, px - a, py + 2*a + b);
1062 grid_face_set_dot(g, d, 3);
1063 }
1064
1065 /* square below right */
1066 if ((x < width - 1) && (((x % 2) == 0) || (y < height - 1))) {
1067 grid_face_add_new(g, 4);
1068 d = grid_get_dot(g, points, px + 2*a, py);
1069 grid_face_set_dot(g, d, 0);
1070 d = grid_get_dot(g, points, px + 2*a + b, py + a);
1071 grid_face_set_dot(g, d, 1);
1072 d = grid_get_dot(g, points, px + a + b, py + a + b);
1073 grid_face_set_dot(g, d, 2);
1074 d = grid_get_dot(g, points, px + a, py + b);
1075 grid_face_set_dot(g, d, 3);
1076 }
1077
1078 /* square below left */
1079 if ((x > 0) && (((x % 2) == 0) || (y < height - 1))) {
1080 grid_face_add_new(g, 4);
1081 d = grid_get_dot(g, points, px - 2*a, py);
1082 grid_face_set_dot(g, d, 0);
1083 d = grid_get_dot(g, points, px - a, py + b);
1084 grid_face_set_dot(g, d, 1);
1085 d = grid_get_dot(g, points, px - a - b, py + a + b);
1086 grid_face_set_dot(g, d, 2);
1087 d = grid_get_dot(g, points, px - 2*a - b, py + a);
1088 grid_face_set_dot(g, d, 3);
1089 }
1090
1091 /* Triangle below right */
1092 if ((x < width - 1) && (y < height - 1)) {
1093 grid_face_add_new(g, 3);
1094 d = grid_get_dot(g, points, px + a, py + b);
1095 grid_face_set_dot(g, d, 0);
1096 d = grid_get_dot(g, points, px + a + b, py + a + b);
1097 grid_face_set_dot(g, d, 1);
1098 d = grid_get_dot(g, points, px + a, py + 2*a + b);
1099 grid_face_set_dot(g, d, 2);
1100 }
1101
1102 /* Triangle below left */
1103 if ((x > 0) && (y < height - 1)) {
1104 grid_face_add_new(g, 3);
1105 d = grid_get_dot(g, points, px - a, py + b);
1106 grid_face_set_dot(g, d, 0);
1107 d = grid_get_dot(g, points, px - a, py + 2*a + b);
1108 grid_face_set_dot(g, d, 1);
1109 d = grid_get_dot(g, points, px - a - b, py + a + b);
1110 grid_face_set_dot(g, d, 2);
1111 }
1112 }
1113 }
1114
1115 freetree234(points);
1116 assert(g->num_faces <= max_faces);
1117 assert(g->num_dots <= max_dots);
1118
1119 grid_make_consistent(g);
1120 return g;
1121 }
1122
1123 grid *grid_new_octagonal(int width, int height)
1124 {
1125 int x, y;
1126 /* b/a approx sqrt(2) */
1127 int a = 29;
1128 int b = 41;
1129
1130 /* Upper bounds - don't have to be exact */
1131 int max_faces = 2 * width * height;
1132 int max_dots = 4 * (width + 1) * (height + 1);
1133
1134 tree234 *points;
1135
1136 grid *g = grid_new();
1137 g->tilesize = 40;
1138 g->faces = snewn(max_faces, grid_face);
1139 g->dots = snewn(max_dots, grid_dot);
1140
1141 points = newtree234(grid_point_cmp_fn);
1142
1143 for (y = 0; y < height; y++) {
1144 for (x = 0; x < width; x++) {
1145 grid_dot *d;
1146 /* cell position */
1147 int px = (2*a + b) * x;
1148 int py = (2*a + b) * y;
1149 /* octagon */
1150 grid_face_add_new(g, 8);
1151 d = grid_get_dot(g, points, px + a, py);
1152 grid_face_set_dot(g, d, 0);
1153 d = grid_get_dot(g, points, px + a + b, py);
1154 grid_face_set_dot(g, d, 1);
1155 d = grid_get_dot(g, points, px + 2*a + b, py + a);
1156 grid_face_set_dot(g, d, 2);
1157 d = grid_get_dot(g, points, px + 2*a + b, py + a + b);
1158 grid_face_set_dot(g, d, 3);
1159 d = grid_get_dot(g, points, px + a + b, py + 2*a + b);
1160 grid_face_set_dot(g, d, 4);
1161 d = grid_get_dot(g, points, px + a, py + 2*a + b);
1162 grid_face_set_dot(g, d, 5);
1163 d = grid_get_dot(g, points, px, py + a + b);
1164 grid_face_set_dot(g, d, 6);
1165 d = grid_get_dot(g, points, px, py + a);
1166 grid_face_set_dot(g, d, 7);
1167
1168 /* diamond */
1169 if ((x > 0) && (y > 0)) {
1170 grid_face_add_new(g, 4);
1171 d = grid_get_dot(g, points, px, py - a);
1172 grid_face_set_dot(g, d, 0);
1173 d = grid_get_dot(g, points, px + a, py);
1174 grid_face_set_dot(g, d, 1);
1175 d = grid_get_dot(g, points, px, py + a);
1176 grid_face_set_dot(g, d, 2);
1177 d = grid_get_dot(g, points, px - a, py);
1178 grid_face_set_dot(g, d, 3);
1179 }
1180 }
1181 }
1182
1183 freetree234(points);
1184 assert(g->num_faces <= max_faces);
1185 assert(g->num_dots <= max_dots);
1186
1187 grid_make_consistent(g);
1188 return g;
1189 }
1190
1191 grid *grid_new_kites(int width, int height)
1192 {
1193 int x, y;
1194 /* b/a approx sqrt(3) */
1195 int a = 15;
1196 int b = 26;
1197
1198 /* Upper bounds - don't have to be exact */
1199 int max_faces = 6 * width * height;
1200 int max_dots = 6 * (width + 1) * (height + 1);
1201
1202 tree234 *points;
1203
1204 grid *g = grid_new();
1205 g->tilesize = 40;
1206 g->faces = snewn(max_faces, grid_face);
1207 g->dots = snewn(max_dots, grid_dot);
1208
1209 points = newtree234(grid_point_cmp_fn);
1210
1211 for (y = 0; y < height; y++) {
1212 for (x = 0; x < width; x++) {
1213 grid_dot *d;
1214 /* position of order-6 dot */
1215 int px = 4*b * x;
1216 int py = 6*a * y;
1217 if (y % 2)
1218 px += 2*b;
1219
1220 /* kite pointing up-left */
1221 grid_face_add_new(g, 4);
1222 d = grid_get_dot(g, points, px, py);
1223 grid_face_set_dot(g, d, 0);
1224 d = grid_get_dot(g, points, px + 2*b, py);
1225 grid_face_set_dot(g, d, 1);
1226 d = grid_get_dot(g, points, px + 2*b, py + 2*a);
1227 grid_face_set_dot(g, d, 2);
1228 d = grid_get_dot(g, points, px + b, py + 3*a);
1229 grid_face_set_dot(g, d, 3);
1230
1231 /* kite pointing up */
1232 grid_face_add_new(g, 4);
1233 d = grid_get_dot(g, points, px, py);
1234 grid_face_set_dot(g, d, 0);
1235 d = grid_get_dot(g, points, px + b, py + 3*a);
1236 grid_face_set_dot(g, d, 1);
1237 d = grid_get_dot(g, points, px, py + 4*a);
1238 grid_face_set_dot(g, d, 2);
1239 d = grid_get_dot(g, points, px - b, py + 3*a);
1240 grid_face_set_dot(g, d, 3);
1241
1242 /* kite pointing up-right */
1243 grid_face_add_new(g, 4);
1244 d = grid_get_dot(g, points, px, py);
1245 grid_face_set_dot(g, d, 0);
1246 d = grid_get_dot(g, points, px - b, py + 3*a);
1247 grid_face_set_dot(g, d, 1);
1248 d = grid_get_dot(g, points, px - 2*b, py + 2*a);
1249 grid_face_set_dot(g, d, 2);
1250 d = grid_get_dot(g, points, px - 2*b, py);
1251 grid_face_set_dot(g, d, 3);
1252
1253 /* kite pointing down-right */
1254 grid_face_add_new(g, 4);
1255 d = grid_get_dot(g, points, px, py);
1256 grid_face_set_dot(g, d, 0);
1257 d = grid_get_dot(g, points, px - 2*b, py);
1258 grid_face_set_dot(g, d, 1);
1259 d = grid_get_dot(g, points, px - 2*b, py - 2*a);
1260 grid_face_set_dot(g, d, 2);
1261 d = grid_get_dot(g, points, px - b, py - 3*a);
1262 grid_face_set_dot(g, d, 3);
1263
1264 /* kite pointing down */
1265 grid_face_add_new(g, 4);
1266 d = grid_get_dot(g, points, px, py);
1267 grid_face_set_dot(g, d, 0);
1268 d = grid_get_dot(g, points, px - b, py - 3*a);
1269 grid_face_set_dot(g, d, 1);
1270 d = grid_get_dot(g, points, px, py - 4*a);
1271 grid_face_set_dot(g, d, 2);
1272 d = grid_get_dot(g, points, px + b, py - 3*a);
1273 grid_face_set_dot(g, d, 3);
1274
1275 /* kite pointing down-left */
1276 grid_face_add_new(g, 4);
1277 d = grid_get_dot(g, points, px, py);
1278 grid_face_set_dot(g, d, 0);
1279 d = grid_get_dot(g, points, px + b, py - 3*a);
1280 grid_face_set_dot(g, d, 1);
1281 d = grid_get_dot(g, points, px + 2*b, py - 2*a);
1282 grid_face_set_dot(g, d, 2);
1283 d = grid_get_dot(g, points, px + 2*b, py);
1284 grid_face_set_dot(g, d, 3);
1285 }
1286 }
1287
1288 freetree234(points);
1289 assert(g->num_faces <= max_faces);
1290 assert(g->num_dots <= max_dots);
1291
1292 grid_make_consistent(g);
1293 return g;
1294 }
1295
1296 grid *grid_new_floret(int width, int height)
1297 {
1298 int x, y;
1299 /* Vectors for sides; weird numbers needed to keep puzzle aligned with window
1300 * -py/px is close to tan(30 - atan(sqrt(3)/9))
1301 * using py=26 makes everything lean to the left, rather than right
1302 */
1303 int px = 75, py = -26; /* |( 75, -26)| = 79.43 */
1304 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
1305 int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */
1306
1307 /* Upper bounds - don't have to be exact */
1308 int max_faces = 6 * width * height;
1309 int max_dots = 9 * (width + 1) * (height + 1);
1310
1311 tree234 *points;
1312
1313 grid *g = grid_new();
1314 g->tilesize = 2 * px;
1315 g->faces = snewn(max_faces, grid_face);
1316 g->dots = snewn(max_dots, grid_dot);
1317
1318 points = newtree234(grid_point_cmp_fn);
1319
1320 /* generate pentagonal faces */
1321 for (y = 0; y < height; y++) {
1322 for (x = 0; x < width; x++) {
1323 grid_dot *d;
1324 /* face centre */
1325 int cx = (6*px+3*qx)/2 * x;
1326 int cy = (4*py-5*qy) * y;
1327 if (x % 2)
1328 cy -= (4*py-5*qy)/2;
1329 else if (y && y == height-1)
1330 continue; /* make better looking grids? try 3x3 for instance */
1331
1332 grid_face_add_new(g, 5);
1333 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1334 d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 1);
1335 d = grid_get_dot(g, points, cx+2*rx+qx, cy+2*ry+qy); grid_face_set_dot(g, d, 2);
1336 d = grid_get_dot(g, points, cx+2*qx+rx, cy+2*qy+ry); grid_face_set_dot(g, d, 3);
1337 d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 4);
1338
1339 grid_face_add_new(g, 5);
1340 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1341 d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 1);
1342 d = grid_get_dot(g, points, cx+2*qx+px, cy+2*qy+py); grid_face_set_dot(g, d, 2);
1343 d = grid_get_dot(g, points, cx+2*px+qx, cy+2*py+qy); grid_face_set_dot(g, d, 3);
1344 d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 4);
1345
1346 grid_face_add_new(g, 5);
1347 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1348 d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 1);
1349 d = grid_get_dot(g, points, cx+2*px-rx, cy+2*py-ry); grid_face_set_dot(g, d, 2);
1350 d = grid_get_dot(g, points, cx-2*rx+px, cy-2*ry+py); grid_face_set_dot(g, d, 3);
1351 d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 4);
1352
1353 grid_face_add_new(g, 5);
1354 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1355 d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 1);
1356 d = grid_get_dot(g, points, cx-2*rx-qx, cy-2*ry-qy); grid_face_set_dot(g, d, 2);
1357 d = grid_get_dot(g, points, cx-2*qx-rx, cy-2*qy-ry); grid_face_set_dot(g, d, 3);
1358 d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 4);
1359
1360 grid_face_add_new(g, 5);
1361 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1362 d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 1);
1363 d = grid_get_dot(g, points, cx-2*qx-px, cy-2*qy-py); grid_face_set_dot(g, d, 2);
1364 d = grid_get_dot(g, points, cx-2*px-qx, cy-2*py-qy); grid_face_set_dot(g, d, 3);
1365 d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 4);
1366
1367 grid_face_add_new(g, 5);
1368 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
1369 d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 1);
1370 d = grid_get_dot(g, points, cx-2*px+rx, cy-2*py+ry); grid_face_set_dot(g, d, 2);
1371 d = grid_get_dot(g, points, cx+2*rx-px, cy+2*ry-py); grid_face_set_dot(g, d, 3);
1372 d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 4);
1373 }
1374 }
1375
1376 freetree234(points);
1377 assert(g->num_faces <= max_faces);
1378 assert(g->num_dots <= max_dots);
1379
1380 grid_make_consistent(g);
1381 return g;
1382 }
1383
1384 grid *grid_new_dodecagonal(int width, int height)
1385 {
1386 int x, y;
1387 /* Vector for side of triangle - ratio is close to sqrt(3) */
1388 int a = 15;
1389 int b = 26;
1390
1391 /* Upper bounds - don't have to be exact */
1392 int max_faces = 3 * width * height;
1393 int max_dots = 14 * width * height;
1394
1395 tree234 *points;
1396
1397 grid *g = grid_new();
1398 g->tilesize = b;
1399 g->faces = snewn(max_faces, grid_face);
1400 g->dots = snewn(max_dots, grid_dot);
1401
1402 points = newtree234(grid_point_cmp_fn);
1403
1404 for (y = 0; y < height; y++) {
1405 for (x = 0; x < width; x++) {
1406 grid_dot *d;
1407 /* centre of dodecagon */
1408 int px = (4*a + 2*b) * x;
1409 int py = (3*a + 2*b) * y;
1410 if (y % 2)
1411 px += 2*a + b;
1412
1413 /* dodecagon */
1414 grid_face_add_new(g, 12);
1415 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
1416 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
1417 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
1418 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
1419 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
1420 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
1421 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
1422 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
1423 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
1424 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
1425 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
1426 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
1427
1428 /* triangle below dodecagon */
1429 if ((y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
1430 grid_face_add_new(g, 3);
1431 d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
1432 d = grid_get_dot(g, points, px , py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
1433 d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 2);
1434 }
1435
1436 /* triangle above dodecagon */
1437 if ((y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
1438 grid_face_add_new(g, 3);
1439 d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
1440 d = grid_get_dot(g, points, px , py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
1441 d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 2);
1442 }
1443 }
1444 }
1445
1446 freetree234(points);
1447 assert(g->num_faces <= max_faces);
1448 assert(g->num_dots <= max_dots);
1449
1450 grid_make_consistent(g);
1451 return g;
1452 }
1453
1454 grid *grid_new_greatdodecagonal(int width, int height)
1455 {
1456 int x, y;
1457 /* Vector for side of triangle - ratio is close to sqrt(3) */
1458 int a = 15;
1459 int b = 26;
1460
1461 /* Upper bounds - don't have to be exact */
1462 int max_faces = 30 * width * height;
1463 int max_dots = 200 * width * height;
1464
1465 tree234 *points;
1466
1467 grid *g = grid_new();
1468 g->tilesize = b;
1469 g->faces = snewn(max_faces, grid_face);
1470 g->dots = snewn(max_dots, grid_dot);
1471
1472 points = newtree234(grid_point_cmp_fn);
1473
1474 for (y = 0; y < height; y++) {
1475 for (x = 0; x < width; x++) {
1476 grid_dot *d;
1477 /* centre of dodecagon */
1478 int px = (6*a + 2*b) * x;
1479 int py = (3*a + 3*b) * y;
1480 if (y % 2)
1481 px += 3*a + b;
1482
1483 /* dodecagon */
1484 grid_face_add_new(g, 12);
1485 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
1486 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
1487 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
1488 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
1489 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
1490 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
1491 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
1492 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
1493 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
1494 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
1495 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
1496 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
1497
1498 /* hexagon below dodecagon */
1499 if (y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
1500 grid_face_add_new(g, 6);
1501 d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
1502 d = grid_get_dot(g, points, px + 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
1503 d = grid_get_dot(g, points, px + a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 2);
1504 d = grid_get_dot(g, points, px - a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 3);
1505 d = grid_get_dot(g, points, px - 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 4);
1506 d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 5);
1507 }
1508
1509 /* hexagon above dodecagon */
1510 if (y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
1511 grid_face_add_new(g, 6);
1512 d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
1513 d = grid_get_dot(g, points, px - 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
1514 d = grid_get_dot(g, points, px - a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 2);
1515 d = grid_get_dot(g, points, px + a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 3);
1516 d = grid_get_dot(g, points, px + 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 4);
1517 d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 5);
1518 }
1519
1520 /* square on right of dodecagon */
1521 if (x < width - 1) {
1522 grid_face_add_new(g, 4);
1523 d = grid_get_dot(g, points, px + 2*a + b, py - a); grid_face_set_dot(g, d, 0);
1524 d = grid_get_dot(g, points, px + 4*a + b, py - a); grid_face_set_dot(g, d, 1);
1525 d = grid_get_dot(g, points, px + 4*a + b, py + a); grid_face_set_dot(g, d, 2);
1526 d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 3);
1527 }
1528
1529 /* square on top right of dodecagon */
1530 if (y && (x < width - 1 || !(y % 2))) {
1531 grid_face_add_new(g, 4);
1532 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
1533 d = grid_get_dot(g, points, px + (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
1534 d = grid_get_dot(g, points, px + (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 2);
1535 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 3);
1536 }
1537
1538 /* square on top left of dodecagon */
1539 if (y && (x || (y % 2))) {
1540 grid_face_add_new(g, 4);
1541 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 0);
1542 d = grid_get_dot(g, points, px - (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 1);
1543 d = grid_get_dot(g, points, px - (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 2);
1544 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 3);
1545 }
1546 }
1547 }
1548
1549 freetree234(points);
1550 assert(g->num_faces <= max_faces);
1551 assert(g->num_dots <= max_dots);
1552
1553 grid_make_consistent(g);
1554 return g;
1555 }
1556
1557 /* ----------- End of grid generators ------------- */