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

#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);
}