#include "hexagon.h" #include #include #include #include static int inversecantor(int cantor, int *x, int *y); /* * This file is written by Nathan Wagner and dedicated to the public * domain */ double HL_vertexv[12] = { .577350269189625764509148780502, 0.0, .288675134594812882254574390251, 0.5, -.288675134594812882254574390251, 0.5, -.577350269189625764509148780502, 0.0, -.288675134594812882254574390251, -0.5, .288675134594812882254574390251, -0.5}; double HL_fand[16] = { 0.0, 0.0, .577350269189625764509148780502, 0.0, .288675134594812882254574390251, 0.5, -.288675134594812882254574390251, 0.5, -.577350269189625764509148780502, 0.0, -.288675134594812882254574390251, -0.5, .288675134594812882254574390251, -0.5, .577350269189625764509148780502, 0.0 }; double HL_hfand[16] = { 0.0, 0.0, 0.0, .577350269189625764509148780502, 0.5, .288675134594812882254574390251, 0.5, -.288675134594812882254574390251, 0.0, -.577350269189625764509148780502, -0.5, -.288675134594812882254574390251, -0.5, .288675134594812882254574390251, 0.0, .577350269189625764509148780502 }; float HL_fanf[16] = { 0.0f, 0.0f, .577350269189625764509148780502f, 0.0f, .288675134594812882254574390251f, 0.5f, -.288675134594812882254574390251f, 0.5f, -.577350269189625764509148780502f, 0.0f, -.288675134594812882254574390251f, -0.5f, .288675134594812882254574390251f, -0.5f, .577350269189625764509148780502f, 0.0f }; float HL_hfanf[16] = { 0.0f, 0.0f, 0.0f, .577350269189625764509148780502f, 0.5f, .288675134594812882254574390251f, 0.5f, -.288675134594812882254574390251f, 0.0f, -.577350269189625764509148780502f, -0.5f, -.288675134594812882254574390251f, -0.5f, .288675134594812882254574390251f, 0.0f, .577350269189625764509148780502f }; /* size of a square that will exactly fit in a hexagon */ /* 2.0/(1+sqrt(3.0)) */ double HL_square = .73205080756887729352; /* these all are for a hex one unit across */ static double hexptvd[6][2] = { {.577350269189625764509148780502, 0.0}, /* 1.0/sqrt3 */ {.288675134594812882254574390251, 0.5}, /* 0.5/sqrt3 */ {-.288675134594812882254574390251, 0.5}, {-.577350269189625764509148780502, 0.0}, {-.288675134594812882254574390251, -0.5}, {.288675134594812882254574390251, -0.5} }; #if 0 /* TODO how is this related? to the above? */ static double texptvd[6][2] = { {1.154700538379251529018297561004, 0.5}, /* 2.0/sqrt3 */ {.866025403784438646763723170753, 1.0}, /* 1.5/sqrt3 */ {.288675134594812882254574390251, 1.0}, {0.0, 0.5}, {.288675134594812882254574390251, 0.0}, {.866025403784438646763723170753, 0.0} }; static double hexpthd[6][2] = { {0.0, .577350269189625764509148780502}, {0.5, .288675134594812882254574390251}, {0.5, -.288675134594812882254574390251}, {0.0, -.577350269189625764509148780502}, {-0.5, -.288675134594812882254574390251}, {-0.5, .288675134594812882254574390251} }; #endif void HL_vertices(int cantor, double *vc) { int i; double xc, yc; HL_hexcenter(cantor, &xc, &yc); for (i=0; i<6; i++) { *vc++ = hexptvd[i][0] + xc; *vc++ = hexptvd[i][1] + yc; } *vc++ = hexptvd[0][0] + xc; *vc++ = hexptvd[0][1] + yc; } void HL_trianglefan(int cantor, double *vc) { HL_hexcenter(cantor, vc, vc+1); HL_vertices(cantor, vc+2); } double HL_center_x(int cantor) { double x; HL_hexcenter(cantor, &x, 0); return x; } double HL_center_y(int cantor) { double y; HL_hexcenter(cantor, 0, &y); return y; } int HL_hexcenter(int cantor, double *xc, double *yc) { int x, y; double stride = 1.5/sqrt(3.0); inversecantor(cantor, &x, &y); if (xc) *xc = x * stride; if (yc && x >= 0) *yc = y + ((x + 1) % 2) / 2.0 - 0.5; if (yc && x < 0) *yc = y + ((-x + 1) % 2) / 2.0 - 0.5; return cantor; } /* * This function assumes that the hexes are one unit across, and vertically * oriented. If that is not the case, you will need to transform * your input coordinates first. */ int HL_cantor_bin(double x, double y) { return HL_hexbin(1.0, x, y, 0, 0); } static int xy2ijk(int x, int y, int *i, int *j, int *k) { int pi, pj, pk; pi = x; pj = -y; if (x < 0) { pj = pj + (-x + 1) / 2; } else { pj = pj - x/2; } pk = -pi - pj; if (i) *i = pi; if (j) *j = pj; if (k) *k = pk; return HL_cantor_xy(x,y); } static int ijk2xy(int i, int j, int k, int *x, int *y) { int px, py; px = i; /* py = -j - i/2; */ py = -j; if (i < 0) { py += (-i + 1)/2; } else { py -= i/2; } if (x) *x = px; if (y) *y = py; return HL_cantor_xy(px,py); } int HL_cantor_ijk(int i, int j, int k) { return ijk2xy(i,j,k,0,0); } int HL_distance(int from, int to) { int dist = 0, i;; int fc[3], tc[3]; HL_cantor_arrays(from, 0, fc); HL_cantor_arrays(to, 0, tc); for (i=0;i<=2;i++) { dist += abs(fc[i] - tc[i]); } return dist / 2; } int HL_hexes_within_range(int hex, int range, int *list, int size) { int count = 0; int i; if (range == 0) { return HL_hexes_at_range(hex, 0, list, size); } for (i=1;i<=range;i++) { count += HL_hexes_at_range(hex, i, count > size ? 0 : list+count, size-count); } return count; } int HL_hexes_at_range(int hex, int range, int *list, int size) { int q; /* p and q are count/loop vars */ int c[3]; /* ijk coord array */ if (range == 0) { if (list) { list[0] = hex; } return 1; } else if (range < 0) { return 0; } /* TODO don't bother to collect if the list isn't big enough? */ /* i.e. if (!list || size < range * 6) */ if (!list || size < 1) return range * 6; HL_cantor_arrays(hex, 0, c); c[0] += range; c[2] = -c[0] - c[1]; hex = HL_cantor_ijkp(c); for(q=0; q 5) return 0; return HL_adjhex(start, dir); } /* direction 0 is positive X , counter clockwise from there */ int HL_adjhex(int start, int dir) { int c[3]; HL_cantor_arrays(start, 0, c); switch (dir%6) { case 2: c[0]--; c[1]++; break; case 1: c[1]++; c[2]--; break; case 0: c[0]++; c[2]--; break; case 5: c[0]++; c[1]--; break; case 4: c[1]--; c[2]++; break; case 3: c[0]--; ; c[2]++; break; } return HL_cantor_ijkp(c); } int HL_cantor_xyp(int *xy) { return HL_cantor_xy(xy[0], xy[1]); } int HL_cantor_ijkp(int *ijk) { return HL_cantor_ijk(ijk[0], ijk[1], ijk[2]); } int HL_cantor_arrays(int can, int *xy, int *ijk) { return HL_cantor_decode(can, xy, xy ? xy+1 : 0, ijk, ijk ? ijk+1 : 0, ijk ? ijk+2 : 0); } int HL_cantor_decode(int can, int *x, int *y, int *i, int *j, int *k) { int px, py; inversecantor(can, &px, &py); if (x) *x = px; if (y) *y = py; xy2ijk(px, py, i, j, k); return can; } int HL_cantor_i(int cantor) { int i; HL_cantor_decode(cantor, 0,0, &i,0,0); return i; } int HL_cantor_j(int cantor) { int j; HL_cantor_decode(cantor, 0,0, 0,&j,0); return j; } int HL_cantor_k(int cantor) { int k; HL_cantor_decode(cantor, 0,0, 0,0,&k); return k; } int HL_cantor_x(int cantor) { int x; inversecantor(cantor, &x, 0); return x; } int HL_cantor_y(int cantor) { int y; inversecantor(cantor, 0, &y); return y; } /* Determine if a map with these dimensions will overflow */ int HL_map_bounds_ok(int xdim, int ydim) { /* return (x+y) * (x + y + 1) / 2 + y+1; */ if (INT_MAX - xdim - 1 < ydim) return 0; if (INT_MAX / (xdim+ydim) < (xdim+ydim+1)) return 0; if ( (xdim+ydim) * (xdim+ydim+1) / 2 > INT_MAX - ydim - 1) return 0; return 1; } int HL_map_max_dimension(void) { int low, high, try; low = 1; high = INT_MAX/2; while (low != high - 1) { try = (low + high) / 2; if (HL_map_bounds_ok(try,try)) { low = try; } else { high = try; } } return low; } static int inversenatcantor(int cantor, int *x, int *y) { int w, t, py; cantor -= 1; w = (int)floor((sqrt(8.0 * cantor + 1.0) - 1.0)/2.0); t = (w * w + w)/2; py = cantor - t; if (y) *y = py; if (x) *x = w - py; return cantor; } /* * map non negative integer pairs to their cantor pairing function * number, plus one. We add one so that the result is never zero, * leaving zero to be "invalid" or "none" or what have you. */ static int natcantor(int x, int y) { return (x+y) * (x + y + 1) / 2 + y+1; } /* See http://en.wikipedia.org/wiki/Cantor_pairing_function */ /* see also http://szudzik.com/ElegantPairing.pdf */ /* * if either coordinate is negative, map the integers onto the * whole numbers, and then return the negative of the adjusted * cantor number. As for most grids negative coordinates will * be invalid, this will allow for a <= 0 test for invalid * or out of bounds (on the negative side anyway, you'll * still have to test for out of range on the positive side). * * TODO figure out what the maximum supported coordinates are * for given integer sizes. */ int HL_cantor_xy(int x, int y) { if (x < 0 || y < 0) { x = abs(2 * x) - (x < 0); y = abs(2 * y) - (y < 0); return -natcantor(x, y); } return natcantor(x,y); } static int inversecantor(int cantor, int *x, int *y) { if (cantor < 0) { inversenatcantor(-cantor, x, y); if (x) { if (*x % 2) { *x = -(*x+1)/2; } else { *x = *x / 2; } } if (y) { if (*y % 2) { *y = -(*y+1)/2; } else { *y = *y/2; } } } else { inversenatcantor(cantor, x, y); } return cantor; } struct hex { int iso; int x, y, z; }; /* y *must* be positive down as the xy /iso conversion assumes this */ static int hex_xy(struct hex *h) { if (!h->iso) return 1; if (h->x >= 0) { h->y = -h->y - (h->x+1)/2; } else { /* need to round toward -inf, not toward zero, so x-1 */ h->y = -h->y - h->x/2; } h->iso = 0; return 1; } #if 0 static int hex_iso(struct hex *h) { if (h->iso) return 1; if (h->x >= 0) { h->y = (-h->y - (h->x+1)/2); } else { /* need to round toward -inf, not toward zero, so x-1 */ h->y = (-h->y - (h->x)/2); } h->z = -h->x - h->y; h->iso = 1; return 1; } #endif #define COS30 (.866025403784438646763723170752) int HL_hexbin(double width, double x, double y, int *i, int *j) { double z, rx, ry, rz; double abs_dx, abs_dy, abs_dz; int ix, iy, iz, s; struct hex h; /*x = x / cos(30 * M_PI / 180.0); */ /* rotated X coord */ x = x / COS30; y = y - x / 2.0; /* adjustment for rotated X */ /* adjust for actual hexwidth */ x /= width; y /= width; z = -x - y; ix = rx = floor(x + 0.5); iy = ry = floor(y + 0.5); iz = rz = floor(z + 0.5); s = ix + iy + iz; if (s) { abs_dx = fabs(rx - x); abs_dy = fabs(ry - y); abs_dz = fabs(rz - z); if (abs_dx >= abs_dy && abs_dx >= abs_dz) { ix -= s; } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) { iy -= s; } else { iz -= s; } } h.x = ix; h.y = iy; h.z = iz; h.iso = 1; hex_xy(&h); if (i) *i = h.x; if (j) *j = h.y; return HL_cantor_xy(h.x, h.y); }