mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Files

This fixes a build failure introduced by commit 2e48ce132e011e8 yesterday. When I saw that commit I expected the most likely problem would be in the NestedVM build, which is currently the thing with the most most out-of-date C implementation. And indeed the NestedVM toolchain doesn't have <tgmath.h> - but much more surprisingly, our _Windows_ builds failed too, with a compile error inside <tgmath.h> itself! I haven't looked closely into the problem yet. Our Windows builds are done with clang, which comes with its own <tgmath.h> superseding the standard Windows one. So you'd _hope_ that clang could make sense of its own header! But perhaps the problem is that this is an unusual compile mode and hasn't been tested. My fix is to simply add a cmake check for <tgmath.h> - which doesn't just check the file's existence, it actually tries compiling a file that #includes it, so it will detect 'file exists but is mysteriously broken' just as easily as 'not there at all'. So this makes the builds start working again, precisely on Ben's theory of opportunistically using <tgmath.h> where possible and falling back to <math.h> otherwise. It looks ugly, though! I'm half tempted to make a new header file whose job is to include a standard set of system headers, just so that that nasty #ifdef doesn't have to sit at the top of almost all the source files. But for the moment this at least gets the build working again.
671 lines
20 KiB
C
671 lines
20 KiB
C
/*
|
|
* Standalone test program for hat.c, which generates patches of hat
|
|
* tiling in multiple output formats without also generating a Loopy
|
|
* puzzle around them.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#ifdef NO_TGMATH_H
|
|
# include <math.h>
|
|
#else
|
|
# include <tgmath.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "hat-internal.h"
|
|
|
|
static HatCoords *hat_coords_construct_v(TileType type, va_list ap)
|
|
{
|
|
HatCoords *hc = hat_coords_new();
|
|
while (true) {
|
|
int index = va_arg(ap, int);
|
|
|
|
hat_coords_make_space(hc, hc->nc + 1);
|
|
hc->c[hc->nc].type = type;
|
|
hc->c[hc->nc].index = index;
|
|
hc->nc++;
|
|
|
|
if (index < 0)
|
|
return hc;
|
|
|
|
type = va_arg(ap, TileType);
|
|
}
|
|
}
|
|
|
|
static HatCoords *hat_coords_construct(TileType type, ...)
|
|
{
|
|
HatCoords *hc;
|
|
va_list ap;
|
|
|
|
va_start(ap, type);
|
|
hc = hat_coords_construct_v(type, ap);
|
|
va_end(ap);
|
|
|
|
return hc;
|
|
}
|
|
|
|
static bool hat_coords_equal(HatCoords *hc1, HatCoords *hc2)
|
|
{
|
|
size_t i;
|
|
|
|
if (hc1->nc != hc2->nc)
|
|
return false;
|
|
|
|
for (i = 0; i < hc1->nc; i++) {
|
|
if (hc1->c[i].type != hc2->c[i].type ||
|
|
hc1->c[i].index != hc2->c[i].index)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool hat_coords_expect(const char *file, int line, HatCoords *hc,
|
|
TileType type, ...)
|
|
{
|
|
bool equal;
|
|
va_list ap;
|
|
HatCoords *hce;
|
|
|
|
va_start(ap, type);
|
|
hce = hat_coords_construct_v(type, ap);
|
|
va_end(ap);
|
|
|
|
equal = hat_coords_equal(hc, hce);
|
|
|
|
if (!equal) {
|
|
fprintf(stderr, "%s:%d: coordinate mismatch\n", file, line);
|
|
hat_coords_debug(" expected: ", hce, "\n");
|
|
hat_coords_debug(" actual: ", hc, "\n");
|
|
}
|
|
|
|
hat_coords_free(hce);
|
|
return equal;
|
|
}
|
|
|
|
#define EXPECT(hc, ...) do { \
|
|
if (!hat_coords_expect(__FILE__, __LINE__, hc, __VA_ARGS__)) \
|
|
fails++; \
|
|
} while (0)
|
|
|
|
/*
|
|
* For four-colouring the tiling: these tables give a colouring of
|
|
* each kitemap, with colour 3 assigned to the reflected tiles in the
|
|
* middle of the H, and 0,1,2 chosen arbitrarily.
|
|
*/
|
|
|
|
static const int fourcolours_H[] = {
|
|
/* 0 */ 0, 2, 1, 3,
|
|
/* 1 */ 1, 0, 2, 3,
|
|
/* 2 */ 0, 2, 1, 3,
|
|
/* 3 */ 1, -1, -1, -1,
|
|
/* 4 */ 1, 2, -1, -1,
|
|
/* 5 */ 1, 2, -1, -1,
|
|
/* 6 */ 2, 1, -1, -1,
|
|
/* 7 */ 0, 1, -1, -1,
|
|
/* 8 */ 2, 0, -1, -1,
|
|
/* 9 */ 2, 0, -1, -1,
|
|
/* 10 */ 0, 1, -1, -1,
|
|
/* 11 */ 0, 1, -1, -1,
|
|
/* 12 */ 2, 0, -1, -1,
|
|
};
|
|
static const int fourcolours_T[] = {
|
|
/* 0 */ 1, 2, 0, 3,
|
|
/* 1 */ 2, 1, -1, -1,
|
|
/* 2 */ 0, 1, -1, -1,
|
|
/* 3 */ 0, 2, -1, -1,
|
|
/* 4 */ 2, 0, -1, -1,
|
|
/* 5 */ 0, 1, -1, -1,
|
|
/* 6 */ 1, 2, -1, -1,
|
|
};
|
|
static const int fourcolours_P[] = {
|
|
/* 0 */ 2, 1, 0, 3,
|
|
/* 1 */ 1, 2, 0, 3,
|
|
/* 2 */ 2, 1, -1, -1,
|
|
/* 3 */ 0, 2, -1, -1,
|
|
/* 4 */ 0, 1, -1, -1,
|
|
/* 5 */ 1, 2, -1, -1,
|
|
/* 6 */ 2, 0, -1, -1,
|
|
/* 7 */ 0, 1, -1, -1,
|
|
/* 8 */ 1, 0, -1, -1,
|
|
/* 9 */ 2, 1, -1, -1,
|
|
/* 10 */ 0, 2, -1, -1,
|
|
};
|
|
static const int fourcolours_F[] = {
|
|
/* 0 */ 2, 0, 1, 3,
|
|
/* 1 */ 0, 2, 1, 3,
|
|
/* 2 */ 1, 2, -1, -1,
|
|
/* 3 */ 1, 0, -1, -1,
|
|
/* 4 */ 0, 2, -1, -1,
|
|
/* 5 */ 2, 1, -1, -1,
|
|
/* 6 */ 2, 0, -1, -1,
|
|
/* 7 */ 0, 1, -1, -1,
|
|
/* 8 */ 0, 1, -1, -1,
|
|
/* 9 */ 2, 0, -1, -1,
|
|
/* 10 */ 1, 2, -1, -1,
|
|
};
|
|
static const int *const fourcolours[] = {
|
|
fourcolours_H, fourcolours_T, fourcolours_P, fourcolours_F,
|
|
};
|
|
|
|
static bool unit_tests(void)
|
|
{
|
|
int fails = 0;
|
|
HatContext ctx[1];
|
|
HatCoords *hc_in, *hc_out;
|
|
|
|
ctx->rs = NULL;
|
|
ctx->prototype = hat_coords_construct(TT_KITE, 0, TT_HAT, 0, TT_H, -1);
|
|
|
|
/* Simple steps within a hat */
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 6, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_LEFT);
|
|
EXPECT(hc_out, TT_KITE, 5, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 6, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_RIGHT);
|
|
EXPECT(hc_out, TT_KITE, 7, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 5, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_F_LEFT);
|
|
EXPECT(hc_out, TT_KITE, 2, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 5, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_F_RIGHT);
|
|
EXPECT(hc_out, TT_KITE, 1, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
/* Step between hats in the same kitemap, which can change the
|
|
* metatile type at layer 2 */
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 6, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_F_LEFT);
|
|
EXPECT(hc_out, TT_KITE, 3, TT_HAT, 0, TT_H, 0, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 7, TT_HAT, 2, TT_H, 1, TT_H, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_F_RIGHT);
|
|
EXPECT(hc_out, TT_KITE, 4, TT_HAT, 0, TT_T, 3, TT_H, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
/* Step off the edge of one kitemap, necessitating a metamap
|
|
* rewrite of layers 2,3 to get into a different kitemap where
|
|
* that step can be made */
|
|
|
|
hc_in = hat_coords_construct(TT_KITE, 6, TT_HAT, 0, TT_P, 2, TT_P, 3,
|
|
TT_P, -1);
|
|
hc_out = hatctx_step(ctx, hc_in, KS_F_RIGHT);
|
|
/* Working:
|
|
* kite 6 . hat 0 . P 2 . P 3 . P ?
|
|
* -> kite 6 . hat 0 . P 6 . H 0 . P ? (P metamap says 2.3 = 6.0)
|
|
*/
|
|
EXPECT(hc_out, TT_KITE, 7, TT_HAT, 1, TT_H, 1, TT_H, 0, TT_P, -1);
|
|
hat_coords_free(hc_in);
|
|
hat_coords_free(hc_out);
|
|
|
|
hatctx_cleanup(ctx);
|
|
return fails == 0;
|
|
}
|
|
|
|
/*
|
|
* Structure that describes how the colours in the above maps are
|
|
* translated to output colours. This will vary with each kitemap our
|
|
* coordinates pass through, in order to maintain consistency.
|
|
*/
|
|
typedef struct FourColourMap {
|
|
unsigned char map[4];
|
|
} FourColourMap;
|
|
|
|
/*
|
|
* Make an initial FourColourMap by choosing the initial permutation
|
|
* of the three 'normal' hat colours randomly.
|
|
*/
|
|
static inline FourColourMap fourcolourmap_initial(random_state *rs)
|
|
{
|
|
FourColourMap f;
|
|
unsigned i;
|
|
|
|
/* Start with the identity mapping */
|
|
for (i = 0; i < 4; i++)
|
|
f.map[i] = i;
|
|
|
|
/* Randomly permute colours 0,1,2, leaving 3 as the distinguished
|
|
* colour for reflected hats */
|
|
shuffle(f.map, 3, sizeof(f.map[0]), rs);
|
|
|
|
return f;
|
|
}
|
|
|
|
static inline FourColourMap fourcolourmap_update(
|
|
FourColourMap prevm, HatCoords *prevc, HatCoords *currc, KiteStep step,
|
|
HatContext *ctx)
|
|
{
|
|
size_t i, m1, m2;
|
|
const int *f1, *f2;
|
|
unsigned sum;
|
|
int missing;
|
|
FourColourMap newm;
|
|
HatCoords *prev2c;
|
|
|
|
/*
|
|
* If prevc and currc are in the same kitemap anyway, that's the
|
|
* easy case: the colour map for the new kitemap is the same as
|
|
* for the old one, because they're the same kitemap.
|
|
*/
|
|
hatctx_extend_coords(ctx, prevc, currc->nc);
|
|
hatctx_extend_coords(ctx, currc, prevc->nc);
|
|
for (i = 3; i < prevc->nc; i++)
|
|
if (currc->c[i].index != prevc->c[i].index)
|
|
goto mismatch;
|
|
return prevm;
|
|
mismatch:
|
|
|
|
/*
|
|
* The hatctx_step algorithm guarantees that the _new_ coordinate
|
|
* currc is expected to be in a kitemap containing both this kite
|
|
* and the previous one (because it first transformed the previous
|
|
* coordinate until it _could_ take a step within the same
|
|
* kitemap, and then did).
|
|
*
|
|
* So if we reverse the last step we took, we should get a second
|
|
* HatCoords describing the same kite as prevc but showing its
|
|
* position in the _new_ kitemap. This lets us figure out a pair
|
|
* of corresponding metatile indices within the old and new
|
|
* kitemaps (by looking at which metatile prevc and prev2c claim
|
|
* to be in).
|
|
*
|
|
* That metatile will also always be a P or an F (because all
|
|
* metatiles overlapping the next kitemap are of those types),
|
|
* which means it will have two hats in it. And those hats will be
|
|
* adjacent, so differently coloured. Hence, we have enough
|
|
* information to decide how two of the new kitemap's three normal
|
|
* colours map to the colours we were using in the old kitemap -
|
|
* and then the third is determined by process of elimination.
|
|
*/
|
|
prev2c = hatctx_step(
|
|
ctx, currc, (step == KS_LEFT ? KS_RIGHT :
|
|
step == KS_RIGHT ? KS_LEFT :
|
|
step == KS_F_LEFT ? KS_F_RIGHT : KS_F_LEFT));
|
|
|
|
/* Metatile indices within the old and new kitemaps */
|
|
m1 = prevc->c[2].index;
|
|
m2 = prev2c->c[2].index;
|
|
|
|
/* The colourings of those metatiles' hats in our fixed fourcolours[] */
|
|
f1 = fourcolours[prevc->c[3].type] + 4*m1;
|
|
f2 = fourcolours[prev2c->c[3].type] + 4*m2;
|
|
|
|
/*
|
|
* Start making our new output map, filling in all three normal
|
|
* colours to 255 = "don't know yet".
|
|
*/
|
|
newm.map[3] = 3;
|
|
newm.map[0] = newm.map[1] = newm.map[2] = 255;
|
|
|
|
/*
|
|
* Iterate over the tile colourings in fourcolours[] for these
|
|
* metatiles, matching up our mappings.
|
|
*/
|
|
for (i = 0; i < 4; i++) {
|
|
/* They should be the same metatile, so have same number of hats! */
|
|
if (f1[i] == -1 && f2[i] == -1)
|
|
continue;
|
|
assert(f1[i] != -1 && f2[i] != -1);
|
|
|
|
if (f1[i] != 255)
|
|
newm.map[f2[i]] = prevm.map[f1[i]];
|
|
}
|
|
|
|
/*
|
|
* We expect to have filled in exactly two of the three normal
|
|
* colours. Find the missing index, and fill in its colour by
|
|
* arithmetic (using the fact that the three colours add up to 3).
|
|
*/
|
|
sum = 0;
|
|
missing = -1;
|
|
for (i = 0; i < 3; i++) {
|
|
if (newm.map[i] == 255) {
|
|
assert(missing == -1); /* shouldn't have two missing colours */
|
|
missing = i;
|
|
} else {
|
|
sum += newm.map[i];
|
|
}
|
|
}
|
|
assert(missing != -1);
|
|
assert(0 < sum && sum <= 3);
|
|
newm.map[missing] = 3 - sum;
|
|
|
|
return newm;
|
|
}
|
|
|
|
typedef struct pspoint {
|
|
float x, y;
|
|
} pspoint;
|
|
|
|
typedef struct psbbox {
|
|
bool started;
|
|
pspoint bl, tr;
|
|
} psbbox;
|
|
|
|
static inline void psbbox_add(psbbox *bbox, pspoint p)
|
|
{
|
|
if (!bbox->started || bbox->bl.x > p.x)
|
|
bbox->bl.x = p.x;
|
|
if (!bbox->started || bbox->tr.x < p.x)
|
|
bbox->tr.x = p.x;
|
|
if (!bbox->started || bbox->bl.y > p.y)
|
|
bbox->bl.y = p.y;
|
|
if (!bbox->started || bbox->tr.y < p.y)
|
|
bbox->tr.y = p.y;
|
|
bbox->started = true;
|
|
}
|
|
|
|
typedef enum OutFmt { OF_POSTSCRIPT, OF_PYTHON } OutFmt;
|
|
typedef enum ColourMode { CM_SEMANTIC, CM_FOURCOLOUR } ColourMode;
|
|
|
|
typedef struct drawctx {
|
|
OutFmt outfmt;
|
|
ColourMode colourmode;
|
|
psbbox *bbox;
|
|
KiteEnum *kiteenum;
|
|
FourColourMap fourcolourmap[KE_NKEEP];
|
|
bool natural_scale, clip;
|
|
} drawctx;
|
|
|
|
static void bbox_add_hat(void *vctx, Kite kite0, HatCoords *hc, int *coords)
|
|
{
|
|
drawctx *ctx = (drawctx *)vctx;
|
|
pspoint p;
|
|
size_t i;
|
|
|
|
for (i = 0; i < 14; i++) {
|
|
p.x = coords[2*i] * 1.5;
|
|
p.y = coords[2*i+1] * sqrt(0.75);
|
|
psbbox_add(ctx->bbox, p);
|
|
}
|
|
}
|
|
|
|
static void header(drawctx *ctx)
|
|
{
|
|
switch (ctx->outfmt) {
|
|
case OF_POSTSCRIPT: {
|
|
float scale, ox, oy;
|
|
|
|
/* Optionally clip to an inner rectangle that guarantees
|
|
* the whole visible area is covered in hats. */
|
|
if (ctx->clip) {
|
|
ctx->bbox->bl.x += 9;
|
|
ctx->bbox->tr.x -= 9;
|
|
ctx->bbox->bl.y += 12 * sqrt(0.75);
|
|
ctx->bbox->tr.y -= 12 * sqrt(0.75);
|
|
}
|
|
|
|
if (!ctx->natural_scale) {
|
|
/* Scale the output to fit on an A4 page, for test prints. */
|
|
float w = 595, h = 842, margin = 12;
|
|
float xext = ctx->bbox->tr.x - ctx->bbox->bl.x;
|
|
float yext = ctx->bbox->tr.y - ctx->bbox->bl.y;
|
|
float xscale = (w - 2*margin) / xext;
|
|
float yscale = (h - 2*margin) / yext;
|
|
scale = xscale < yscale ? xscale : yscale;
|
|
ox = (w - scale * (ctx->bbox->bl.x + ctx->bbox->tr.x)) / 2;
|
|
oy = (h - scale * (ctx->bbox->bl.y + ctx->bbox->tr.y)) / 2;
|
|
} else {
|
|
/* Leave the patch at its natural scale. */
|
|
scale = 1.0;
|
|
|
|
/* And translate the lower left corner of the bounding box to 0. */
|
|
ox = -ctx->bbox->bl.x;
|
|
oy = -ctx->bbox->bl.y;
|
|
}
|
|
|
|
printf("%%!PS-Adobe-2.0\n%%%%Creator: hat-test from Simon Tatham's "
|
|
"Portable Puzzle Collection\n%%%%Pages: 1\n"
|
|
"%%%%BoundingBox: %f %f %f %f\n"
|
|
"%%%%EndComments\n%%%%Page: 1 1\n",
|
|
ox + scale * ctx->bbox->bl.x,
|
|
oy + scale * ctx->bbox->bl.y,
|
|
ox + scale * ctx->bbox->tr.x,
|
|
oy + scale * ctx->bbox->tr.y);
|
|
|
|
if (ctx->clip) {
|
|
printf("%f %f moveto %f %f lineto %f %f lineto %f %f lineto "
|
|
"closepath clip\n",
|
|
ox + scale * ctx->bbox->bl.x,
|
|
oy + scale * ctx->bbox->bl.y,
|
|
ox + scale * ctx->bbox->bl.x,
|
|
oy + scale * ctx->bbox->tr.y,
|
|
ox + scale * ctx->bbox->tr.x,
|
|
oy + scale * ctx->bbox->tr.y,
|
|
ox + scale * ctx->bbox->tr.x,
|
|
oy + scale * ctx->bbox->bl.y);
|
|
}
|
|
printf("%f %f translate %f dup scale\n", ox, oy, scale);
|
|
printf("%f setlinewidth\n", 0.06);
|
|
printf("0 setgray 1 setlinejoin 1 setlinecap\n");
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void draw_hat(void *vctx, Kite kite0, HatCoords *hc, int *coords)
|
|
{
|
|
drawctx *ctx = (drawctx *)vctx;
|
|
pspoint p;
|
|
size_t i;
|
|
int orientation;
|
|
|
|
/*
|
|
* Determine an index for the hat's orientation, based on the axis
|
|
* of symmetry of its kite #0.
|
|
*/
|
|
{
|
|
int dx = kite0.outer.x - kite0.centre.x;
|
|
int dy = kite0.outer.y - kite0.centre.y;
|
|
orientation = 0;
|
|
while (dx < 0 || dy < 0) {
|
|
int newdx = dx + dy;
|
|
int newdy = -dx;
|
|
dx = newdx;
|
|
dy = newdy;
|
|
orientation++;
|
|
assert(orientation < 6);
|
|
}
|
|
}
|
|
|
|
switch (ctx->outfmt) {
|
|
case OF_POSTSCRIPT: {
|
|
const char *colour;
|
|
|
|
printf("newpath");
|
|
for (i = 0; i < 14; i++) {
|
|
p.x = coords[2*i] * 1.5;
|
|
p.y = coords[2*i+1] * sqrt(0.75);
|
|
printf(" %f %f %s", p.x, p.y, i ? "lineto" : "moveto");
|
|
}
|
|
printf(" closepath gsave");
|
|
|
|
switch (ctx->colourmode) {
|
|
case CM_SEMANTIC:
|
|
if (hc->c[2].type == TT_H) {
|
|
colour = (hc->c[1].index == 3 ? "0 0.5 0.8 setrgbcolor" :
|
|
"0.6 0.8 1 setrgbcolor");
|
|
} else if (hc->c[2].type == TT_F) {
|
|
colour = "0.7 setgray";
|
|
} else {
|
|
colour = "1 setgray";
|
|
}
|
|
break;
|
|
|
|
default /* case CM_FOURCOLOUR */: {
|
|
/*
|
|
* Determine the colour of this tile by translating the
|
|
* fixed colour from fourcolours[] through our current
|
|
* FourColourMap.
|
|
*/
|
|
FourColourMap f = ctx->fourcolourmap[ctx->kiteenum->curr_index];
|
|
const int *m = fourcolours[hc->c[3].type];
|
|
static const char *const colours[] = {
|
|
"1 0.7 0.7 setrgbcolor",
|
|
"1 1 0.7 setrgbcolor",
|
|
"0.7 1 0.7 setrgbcolor",
|
|
"0.6 0.6 1 setrgbcolor",
|
|
};
|
|
colour = colours[f.map[m[hc->c[2].index * 4 + hc->c[1].index]]];
|
|
break;
|
|
}
|
|
}
|
|
printf(" %s fill grestore", colour);
|
|
printf(" stroke\n");
|
|
break;
|
|
}
|
|
case OF_PYTHON: {
|
|
printf("hat('%c', %d, %d, [", "HTPF"[hc->c[2].type], hc->c[1].index,
|
|
orientation);
|
|
for (i = 0; i < 14; i++)
|
|
printf("%s(%d,%d)", i ? ", " : "", coords[2*i], coords[2*i+1]);
|
|
printf("])\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void trailer(drawctx *dctx)
|
|
{
|
|
switch (dctx->outfmt) {
|
|
case OF_POSTSCRIPT: {
|
|
printf("showpage\n");
|
|
printf("%%%%Trailer\n");
|
|
printf("%%%%EOF\n");
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
psbbox bbox[1];
|
|
KiteEnum s[1];
|
|
HatContext ctx[1];
|
|
HatCoords *coords[KE_NKEEP];
|
|
random_state *rs;
|
|
const char *random_seed = "12345";
|
|
int w = 10, h = 10;
|
|
int argpos = 0;
|
|
size_t i;
|
|
drawctx dctx[1];
|
|
|
|
dctx->outfmt = OF_POSTSCRIPT;
|
|
dctx->colourmode = CM_SEMANTIC;
|
|
dctx->natural_scale = false;
|
|
dctx->clip = false;
|
|
dctx->kiteenum = s;
|
|
|
|
while (--argc > 0) {
|
|
const char *arg = *++argv;
|
|
if (!strcmp(arg, "--help")) {
|
|
printf(" usage: hat-test [options] [<width>] [<height>]\n"
|
|
"options: --python write a Python function call per hat\n"
|
|
" --seed=STR vary the starting random seed\n"
|
|
" also: hat-test --test\n");
|
|
return 0;
|
|
} else if (!strcmp(arg, "--test")) {
|
|
return unit_tests() ? 0 : 1;
|
|
} else if (!strcmp(arg, "--python")) {
|
|
dctx->outfmt = OF_PYTHON;
|
|
} else if (!strcmp(arg, "--fourcolour")) {
|
|
dctx->colourmode = CM_FOURCOLOUR;
|
|
} else if (!strcmp(arg, "--unscaled")) {
|
|
dctx->natural_scale = true;
|
|
} else if (!strcmp(arg, "--clip")) {
|
|
dctx->clip = true;
|
|
} else if (!strncmp(arg, "--seed=", 7)) {
|
|
random_seed = arg+7;
|
|
} else if (arg[0] == '-') {
|
|
fprintf(stderr, "unrecognised option '%s'\n", arg);
|
|
return 1;
|
|
} else {
|
|
switch (argpos++) {
|
|
case 0:
|
|
w = atoi(arg);
|
|
break;
|
|
case 1:
|
|
h = atoi(arg);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "unexpected extra argument '%s'\n", arg);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < lenof(coords); i++)
|
|
coords[i] = NULL;
|
|
|
|
rs = random_new(random_seed, strlen(random_seed));
|
|
hatctx_init_random(ctx, rs);
|
|
|
|
bbox->started = false;
|
|
dctx->bbox = bbox;
|
|
|
|
hat_kiteenum_first(s, w, h);
|
|
coords[s->curr_index] = hatctx_initial_coords(ctx);
|
|
maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
|
|
bbox_add_hat, dctx);
|
|
while (hat_kiteenum_next(s)) {
|
|
hat_coords_free(coords[s->curr_index]);
|
|
coords[s->curr_index] = hatctx_step(
|
|
ctx, coords[s->last_index], s->last_step);
|
|
maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
|
|
bbox_add_hat, dctx);
|
|
}
|
|
for (i = 0; i < lenof(coords); i++) {
|
|
hat_coords_free(coords[i]);
|
|
coords[i] = NULL;
|
|
}
|
|
|
|
header(dctx);
|
|
|
|
hat_kiteenum_first(s, w, h);
|
|
coords[s->curr_index] = hatctx_initial_coords(ctx);
|
|
dctx->fourcolourmap[s->curr_index] = fourcolourmap_initial(rs);
|
|
maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
|
|
draw_hat, dctx);
|
|
while (hat_kiteenum_next(s)) {
|
|
hat_coords_free(coords[s->curr_index]);
|
|
coords[s->curr_index] = hatctx_step(
|
|
ctx, coords[s->last_index], s->last_step);
|
|
dctx->fourcolourmap[s->curr_index] = fourcolourmap_update(
|
|
dctx->fourcolourmap[s->last_index], coords[s->last_index],
|
|
coords[s->curr_index], s->last_step, ctx);
|
|
maybe_report_hat(w, h, *s->curr, coords[s->curr_index],
|
|
draw_hat, dctx);
|
|
}
|
|
for (i = 0; i < lenof(coords); i++) {
|
|
hat_coords_free(coords[i]);
|
|
coords[i] = NULL;
|
|
}
|
|
|
|
trailer(dctx);
|
|
|
|
hatctx_cleanup(ctx);
|
|
|
|
return 0;
|
|
}
|