You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
5.5 KiB
233 lines
5.5 KiB
|
|
#include "hexlib.h" |
|
|
|
// Generated code -- http://www.redblobgames.com/grids/hexagons/ |
|
|
|
#include <cstdlib> |
|
#include <algorithm> |
|
#include <iterator> |
|
using std::abs; |
|
using std::max; |
|
using std::vector; |
|
|
|
|
|
struct OffsetCoord |
|
{ |
|
const int col; |
|
const int row; |
|
OffsetCoord(int col_, int row_): col(col_), row(row_) {} |
|
}; |
|
|
|
Hex hex_add(Hex a, Hex b) |
|
{ |
|
return Hex(a.q + b.q, a.r + b.r, a.s + b.s); |
|
} |
|
|
|
Hex hex_subtract(Hex a, Hex b) |
|
{ |
|
return Hex(a.q - b.q, a.r - b.r, a.s - b.s); |
|
} |
|
|
|
Hex hex_scale(Hex a, int k) |
|
{ |
|
return Hex(a.q * k, a.r * k, a.s * k); |
|
} |
|
|
|
const vector<Hex> hex_directions = {Hex(1, 0, -1), Hex(1, -1, 0), Hex(0, -1, 1), Hex(-1, 0, 1), Hex(-1, 1, 0), Hex(0, 1, -1)}; |
|
Hex hex_direction(int direction) |
|
{ |
|
return hex_directions[direction]; |
|
} |
|
|
|
Hex hex_neighbor(Hex hex, int direction) |
|
{ |
|
return hex_add(hex, hex_direction(direction)); |
|
} |
|
|
|
const vector<Hex> hex_diagonals = {Hex(2, -1, -1), Hex(1, -2, 1), Hex(-1, -1, 2), Hex(-2, 1, 1), Hex(-1, 2, -1), Hex(1, 1, -2)}; |
|
Hex hex_diagonal_neighbor(Hex hex, int direction) |
|
{ |
|
return hex_add(hex, hex_diagonals[direction]); |
|
} |
|
|
|
int hex_length(Hex hex) |
|
{ |
|
return int((abs(hex.q) + abs(hex.r) + abs(hex.s)) / 2); |
|
} |
|
|
|
int hex_distance(Hex a, Hex b) |
|
{ |
|
return hex_length(hex_subtract(a, b)); |
|
} |
|
|
|
Hex hex_round(FractionalHex h) |
|
{ |
|
int q = int(round(h.q)); |
|
int r = int(round(h.r)); |
|
int s = int(round(h.s)); |
|
double q_diff = abs(q - h.q); |
|
double r_diff = abs(r - h.r); |
|
double s_diff = abs(s - h.s); |
|
if (q_diff > r_diff && q_diff > s_diff) |
|
{ |
|
q = -r - s; |
|
} |
|
else |
|
if (r_diff > s_diff) |
|
{ |
|
r = -q - s; |
|
} |
|
else |
|
{ |
|
s = -q - r; |
|
} |
|
return Hex(q, r, s); |
|
} |
|
|
|
FractionalHex hex_lerp(FractionalHex a, FractionalHex b, double t) |
|
{ |
|
return FractionalHex(a.q * (1 - t) + b.q * t, a.r * (1 - t) + b.r * t, a.s * (1 - t) + b.s * t); |
|
} |
|
|
|
vector<Hex> hex_linedraw(Hex a, Hex b) |
|
{ |
|
int N = hex_distance(a, b); |
|
FractionalHex a_nudge = FractionalHex(a.q + 0.000001, a.r + 0.000001, a.s - 0.000002); |
|
FractionalHex b_nudge = FractionalHex(b.q + 0.000001, b.r + 0.000001, b.s - 0.000002); |
|
vector<Hex> results = {}; |
|
double step = 1.0 / max(N, 1); |
|
for (int i = 0; i <= N; i++) |
|
{ |
|
results.push_back(hex_round(hex_lerp(a_nudge, b_nudge, step * i))); |
|
} |
|
return results; |
|
} |
|
|
|
const int EVEN = 1; |
|
const int ODD = -1; |
|
OffsetCoord qoffset_from_cube(int offset, Hex h) |
|
{ |
|
int col = h.q; |
|
int row = h.r + int((h.q + offset * (h.q & 1)) / 2); |
|
return OffsetCoord(col, row); |
|
} |
|
|
|
Hex qoffset_to_cube(int offset, OffsetCoord h) |
|
{ |
|
int q = h.col; |
|
int r = h.row - int((h.col + offset * (h.col & 1)) / 2); |
|
int s = -q - r; |
|
return Hex(q, r, s); |
|
} |
|
|
|
OffsetCoord roffset_from_cube(int offset, Hex h) |
|
{ |
|
int col = h.q + int((h.r + offset * (h.r & 1)) / 2); |
|
int row = h.r; |
|
return OffsetCoord(col, row); |
|
} |
|
|
|
Hex roffset_to_cube(int offset, OffsetCoord h) |
|
{ |
|
int q = h.col - int((h.row + offset * (h.row & 1)) / 2); |
|
int r = h.row; |
|
int s = -q - r; |
|
return Hex(q, r, s); |
|
} |
|
|
|
Point hex_to_pixel(Layout layout, Hex h) |
|
{ |
|
Orientation M = layout.orientation; |
|
Point size = layout.size; |
|
Point origin = layout.origin; |
|
double x = (M.f0 * h.q + M.f1 * h.r) * size.x; |
|
double y = (M.f2 * h.q + M.f3 * h.r) * size.y; |
|
return Point(x + origin.x, y + origin.y); |
|
} |
|
|
|
FractionalHex pixel_to_hex(Layout layout, Point p) |
|
{ |
|
Orientation M = layout.orientation; |
|
Point size = layout.size; |
|
Point origin = layout.origin; |
|
Point pt = Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y); |
|
double q = M.b0 * pt.x + M.b1 * pt.y; |
|
double r = M.b2 * pt.x + M.b3 * pt.y; |
|
return FractionalHex(q, r, -q - r); |
|
} |
|
|
|
Point hex_corner_offset(Layout layout, int corner) |
|
{ |
|
Orientation M = layout.orientation; |
|
Point size = layout.size; |
|
double angle = 2.0 * M_PI * (M.start_angle - corner) / 6; |
|
return Point(size.x * cos(angle), size.y * sin(angle)); |
|
} |
|
|
|
vector<Point> polygon_corners(Layout layout, Hex h) |
|
{ |
|
vector<Point> corners = {}; |
|
Point center = hex_to_pixel(layout, h); |
|
for (int i = 0; i < 6; i++) |
|
{ |
|
Point offset = hex_corner_offset(layout, i); |
|
corners.push_back(Point(center.x + offset.x, center.y + offset.y)); |
|
} |
|
return corners; |
|
} |
|
|
|
// custom hex functions |
|
|
|
bool |
|
hex_equal(Hex a, Hex b) |
|
{ |
|
return (a.q == b.q && a.r == b.r && a.s == b.s); |
|
} |
|
|
|
// NOTE: implementation of this line crossing test |
|
// https://graphics.stanford.edu/pub/Graphics/RTNews/html/rtnv5n3.html#art3 |
|
// NOTE: assumes use of convex polygons with vertices in CCW layout |
|
bool |
|
crossingTest(vector<Point> vertices, Point p) |
|
{ |
|
int numVertices = vertices.size(), intersect_count = 0; |
|
Point vert0, vert1; |
|
double m, x; |
|
|
|
if (numVertices < 3) |
|
return false; |
|
|
|
vert0 = vertices[0]; |
|
|
|
for (int i = 1; i < numVertices + 1; i++) |
|
{ |
|
// use first vertex for the last edge |
|
if (i == numVertices) |
|
vert1 = vertices[0]; |
|
else |
|
vert1 = vertices[i]; |
|
|
|
// check if edge can intersect the +X ray |
|
// NOTE: upward/downward crossing excludes one vertex in the case that |
|
// the ray intersects a vertex |
|
if ((vert0.y > p.y && vert1.y <= p.y) || // downward crossing |
|
(vert0.y <= p.y && vert1.y > p.y)) // upward crossing |
|
{ |
|
m = (vert1.y - vert0.y) / (vert1.x - vert0.x); |
|
x = (p.y - vert0.y) / m + vert0.x; |
|
// if x is right of p.x it must intersect |
|
if (x >= p.x) |
|
intersect_count++; |
|
|
|
// 2 intersections of convex polygon means we started outside |
|
if (intersect_count > 1) |
|
return false; |
|
} |
|
|
|
// start with previous vertex on next loop |
|
vert0 = vert1; |
|
} |
|
|
|
return (intersect_count == 1); |
|
} |
|
|
|
|