|
|
@ -1,81 +1,64 @@ |
|
|
|
--[[ AXIAL/CUBE COORDINATE SYSTEM FOR AMULET/LUA]] |
|
|
|
--[[ |
|
|
|
|
|
|
|
all hexes in functions are assumed to be amulet vectors. |
|
|
|
in amulet, vector arithmetic works already with [ + - * / ] |
|
|
|
things like equality and distance are implemented here. |
|
|
|
|
|
|
|
some algorithms use axial coordinates for hexes: vec2(s, t) |
|
|
|
others use cube coordinates: vec3(s, t, z) where s + t + z = 0 |
|
|
|
this is for simplicity - many algorithms don't care about the |
|
|
|
third coordinate, and if they do, the missing coordinate can |
|
|
|
be calculated from the other two. |
|
|
|
|
|
|
|
-- note on orientation: |
|
|
|
because of the way amulet draws hexagons, it's much easier to assume |
|
|
|
the user wants to use the flat map. rotation after the fact to |
|
|
|
achieve other orienations is probably possible, but might have some |
|
|
|
aliasing issues. TODO work on this. |
|
|
|
|
|
|
|
some of the primary resources used to develop this library: |
|
|
|
- https://redblobgames.com/grid/hexagons - simply amazing. |
|
|
|
- http://amulet.xyz/doc - amulet documentation |
|
|
|
- TODO that place that had the inner circle/outer circle ratio?? |
|
|
|
----- [[ AXIAL/CUBE COORDINATE SYSTEM FOR AMULET/LUA]] ------------------------- |
|
|
|
--[[ author@churchianity.ca |
|
|
|
-- INTRODUCTION |
|
|
|
this is a library for making grids of hexagons using lua. |
|
|
|
it has made use of exclusively standard lua 5.2 functionality, |
|
|
|
making it as portable as possible. it doesn't even use a point |
|
|
|
class, (or classes/metatables at all) simply returning tables |
|
|
|
of integers, which can later be unpacked into your amulet |
|
|
|
vectors, or whatever else you want to use. |
|
|
|
|
|
|
|
this can result in some nasty looking lines with lots of table |
|
|
|
unpacks, but if your graphics library likes traditional lua |
|
|
|
types, you will be better off. |
|
|
|
|
|
|
|
it supports triangular, hexagonal, rectangular, and |
|
|
|
parallelogram map shapes. |
|
|
|
|
|
|
|
it supports non-regular hexagons, though it's trickier to get |
|
|
|
working in amulet. TODO work on this. |
|
|
|
|
|
|
|
-- NOTE ON ORIENTATION + AMULET |
|
|
|
because of the way amulet draws hexagons (amulet essentially |
|
|
|
draws a 6-sided circle from a centerpoint, instead of of a |
|
|
|
series of lines connecting points), the flat orientation is |
|
|
|
default and recommended. other orientations are possible |
|
|
|
with am.rotate, but can cause aliasing issues. TODO work on this. |
|
|
|
|
|
|
|
-- RESOURCES USED TO DEVELOP THIS LIBRARY |
|
|
|
https://redblobgames.com/grid/hexagons - simply amazing. amit is a god. |
|
|
|
http://amulet.xyz/doc - amulet documentation |
|
|
|
TODO that place that had the inner circle/outer circle ratio?? |
|
|
|
|
|
|
|
]] |
|
|
|
|
|
|
|
----- [[ GENERALLY USEFUL FUNCTIONS ]] ----------------------------------------- |
|
|
|
|
|
|
|
-- GENERALLY USEFUL FUNCTIONS -------------------------------------------------- |
|
|
|
|
|
|
|
-- just incase you don't already have a rounding function. |
|
|
|
function round(n) |
|
|
|
return n % 1 >= 0.5 and math.ceil(n) or math.floor(n) |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
-- HEX CONSTANTS --------------------------------------------------------------- |
|
|
|
---- [[ HEX CONSTANTS ]] ------------------------------------------------------- |
|
|
|
|
|
|
|
-- all possible vector directions from a given hex by edge |
|
|
|
HEX_DIRECTIONS = {vec2( 1 , 0), |
|
|
|
vec2( 1 , -1), |
|
|
|
vec2( 0 , -1), |
|
|
|
vec2(-1 , 0), |
|
|
|
vec2(-1 , 1), |
|
|
|
vec2( 0 , 1)} |
|
|
|
HEX_DIRECTIONS = {{ 1 , 0}, |
|
|
|
{ 1 , -1}, |
|
|
|
{ 0 , -1}, |
|
|
|
{-1 , 0}, |
|
|
|
{-1 , 1}, |
|
|
|
{ 0 , 1}} |
|
|
|
|
|
|
|
-- HEX UTILITY FUNCTIONS ------------------------------------------------------- |
|
|
|
|
|
|
|
function hex_equals(a, b) |
|
|
|
return a.s == a.t and b.s == b.t |
|
|
|
end |
|
|
|
|
|
|
|
function hex_nequals(a, b) |
|
|
|
return not hex_equals(a, b) |
|
|
|
end |
|
|
|
|
|
|
|
function hex_length(hex) |
|
|
|
return ((math.abs(hex.s) + math.abs(hex.t) + math.abs(-hex.s - hex.t)) / 2) |
|
|
|
end |
|
|
|
|
|
|
|
function hex_distance(a, b) |
|
|
|
return hex_length(a - b) |
|
|
|
end |
|
|
|
|
|
|
|
function hex_direction(direction) |
|
|
|
return HEX_DIRECTIONS[direction] |
|
|
|
end |
|
|
|
|
|
|
|
function hex_neighbour(hex, direction) |
|
|
|
return hex + HEX_DIRECTIONS[direction] |
|
|
|
end |
|
|
|
|
|
|
|
function hex_round(hex) |
|
|
|
rs = round(hex.s) |
|
|
|
rt = round(hex.t) |
|
|
|
rz = round(-hex.s + -hex.t) |
|
|
|
function hex_round(s, t) |
|
|
|
rs = round(s) |
|
|
|
rt = round(t) |
|
|
|
rz = round(-s - t) |
|
|
|
|
|
|
|
sdelta = math.abs(rs - hex.s) |
|
|
|
tdelta = math.abs(rt - hex.t) |
|
|
|
zdelta = math.abs(rz + hex.s + hex.t) |
|
|
|
sdelta = math.abs(rs - s) |
|
|
|
tdelta = math.abs(rt - t) |
|
|
|
zdelta = math.abs(rz + s + t) |
|
|
|
|
|
|
|
if sdelta > tdelta and sdelta > zdelta then |
|
|
|
rs = -rt - rz |
|
|
@ -85,33 +68,106 @@ function hex_round(hex) |
|
|
|
rz = -rs - rt |
|
|
|
end |
|
|
|
|
|
|
|
return vec2(rs, rt) |
|
|
|
return {rs, rt} |
|
|
|
end |
|
|
|
|
|
|
|
-- COORDINATE CONVERSION FUNCTIONS --------------------------------------------- |
|
|
|
----- [[ LAYOUT, ORIENTATION & COORDINATE CONVERSION ]] ----------------------- |
|
|
|
|
|
|
|
-- forward & inverse matrices used for coordinate conversion |
|
|
|
local M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 ) |
|
|
|
local W = mat2(2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0) |
|
|
|
-- forward & inverse matrices used for the flat orientation. |
|
|
|
FLAT_ORIENTATION = {3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5, |
|
|
|
2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0} |
|
|
|
|
|
|
|
-- forward & inverse matrices used for the pointy orientation. |
|
|
|
POINTY_ORIENTATION = {3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0, |
|
|
|
3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0} |
|
|
|
|
|
|
|
-- layout. |
|
|
|
function layout(size, orientation, origin, width, height, radius) |
|
|
|
return {size = size or {11, 11}, |
|
|
|
orientation = orientation or FLAT_ORIENTATION, |
|
|
|
origin = origin or {0, 0}, |
|
|
|
width = width or 45, |
|
|
|
height = height or 31, |
|
|
|
radius = radius or width or 6} |
|
|
|
end |
|
|
|
|
|
|
|
-- hex to screen |
|
|
|
function hex_to_pixel(hex) |
|
|
|
function hex_to_pixel(s, t, layout) |
|
|
|
M = layout.orientation |
|
|
|
|
|
|
|
x = (M[1][1] * hex.s + M[1][2] * hex.t) * map.size |
|
|
|
y = (M[2][1] * hex.s + M[2][2] * hex.t) * map.size |
|
|
|
x = (M[1] * s + M[2] * t) * layout.size[1] |
|
|
|
y = (M[3] * s + M[4] * t) * layout.size[2] |
|
|
|
|
|
|
|
return vec2(x + map.origin.x, y + map.origin.y) |
|
|
|
return {x + layout.origin[1], y + layout.origin[2]} |
|
|
|
end |
|
|
|
|
|
|
|
-- screen to hex |
|
|
|
function pixel_to_hex(pix) |
|
|
|
pix = vec2(pix.x - map.origin.x) / map.size, |
|
|
|
(pix.y - map.origin.y) / map.size |
|
|
|
function pixel_to_hex(x, y, layout) |
|
|
|
M = layout.orientation |
|
|
|
|
|
|
|
px = {(x - layout.origin[1]) / layout.size[1], |
|
|
|
(y - layout.origin[2]) / layout.size[2]} |
|
|
|
|
|
|
|
s = W[1][1] * pix.x + W[1][2] * pix.y |
|
|
|
t = W[2][1] * pix.x + W[2][2] * pix.y |
|
|
|
s = M[5] * px[1] + M[6] * px[2] |
|
|
|
t = M[7] * px[1] + M[8] * px[2] |
|
|
|
|
|
|
|
return hex_round(vec2(s, t)) |
|
|
|
return hex_round(s, t) |
|
|
|
end |
|
|
|
|
|
|
|
----- [[ MAP STORAGE & RETRIEVAL ]] -------------------------------------------- |
|
|
|
--[[ all functions return a table of tables; a map of points |
|
|
|
storage functions take a range of hex coordinates, and return pixel ones. |
|
|
|
retrieval functions do the opposite. |
|
|
|
everything except map shape is determined by layout. |
|
|
|
pick a pair of functions based on the shape of map you want to use. |
|
|
|
it is not advised to use a single layout instance with multiple shapes. ]] |
|
|
|
|
|
|
|
-- returns parallelogram-shaped map. width and height are used. |
|
|
|
function ogram_map_store(layout) |
|
|
|
map = {} |
|
|
|
for s = 0, layout.width do |
|
|
|
for t = 0, layout.height do |
|
|
|
table.insert(map, hex_to_pixel(s, t, layout)) |
|
|
|
end |
|
|
|
end |
|
|
|
return map |
|
|
|
end |
|
|
|
|
|
|
|
-- returns triangular map. radius is used as the triangle side length. |
|
|
|
function tri_map_store(layout) |
|
|
|
map = {} |
|
|
|
for s = 0, layout.radius do |
|
|
|
for t = layout.radius - s, layout.radius do |
|
|
|
table.insert(map, hex_to_pixel(s, t, layout)) |
|
|
|
end |
|
|
|
end |
|
|
|
return map |
|
|
|
end |
|
|
|
|
|
|
|
-- returns hexagonal map. length of map is radius * 2 + 1 |
|
|
|
function hex_map_store(layout) |
|
|
|
map = {} |
|
|
|
for s = -layout.radius, layout.radius do |
|
|
|
t1 = math.max(-layout.radius, -s - layout.radius) |
|
|
|
t2 = math.min(layout.radius, -s + layout.radius) |
|
|
|
|
|
|
|
for t = t1, t2 do |
|
|
|
table.insert(map, hex_to_pixel(s, t, layout)) |
|
|
|
end |
|
|
|
end |
|
|
|
return map |
|
|
|
end |
|
|
|
|
|
|
|
-- returns rectangular map. width and height are used. |
|
|
|
function rect_map_store(layout) |
|
|
|
map = {} |
|
|
|
for s = 0, layout.width do |
|
|
|
soffset = math.floor(s / 2) |
|
|
|
|
|
|
|
for t = -soffset, layout.height - soffset do |
|
|
|
table.insert(map, hex_to_pixel(s, t, layout)) |
|
|
|
end |
|
|
|
end |
|
|
|
return map |
|
|
|
end |
|
|
|
|
|
|
|
-- MAP FUNCTIONS --------------------------------------------------------------- |