local Pos = require"common.coords".Pos local function sign(x) if x == 0 then return 0 elseif x < 0 then return -1 elseif x > 0 then return 1 end end local function clamp(x,minv,maxv) return math.min(math.max(x,minv),maxv) end -- https://iquilezles.org/articles/distgradfunctions2d/ local function iqz_hex_sdgf(px,py, r) local kx,ky,kz = -0.866025404,0.5,0.577350269 local sx,sy = sign(px),sign(py) px = math.abs(px) py = math.abs(py) local w = kx*px+ky*py local _1 = 2*math.min(w,0) px = px - _1*kx py = py - _1*ky px = px - clamp(px,-kz*r,kz*r) py = py - r local d = math.sqrt(px*px+py*py)*sign(py) local gx,gy if w<0 then gx = -ky*px - kx*py gy = -kx*px + ky*py else gx,gy = px,py end -- dist, gradx, grady return d, sx*gx/d, sy*gy/d end -- rotate by 30 degrees local c30,s30 = math.cos(math.rad(30)), math.sin(math.rad(30)) local function t_in(x,y) return c30*x-s30*y, s30*x+c30*y end local function t_out(x,y) return c30*x+s30*y, -s30*x+c30*y end -- above sdf has flat top not pointy-top hexagons -- so need to rotate -- also, 'r' parameter in the above seems to be distance to side -- but in our unit system, 1 unit is distance to vertex -- conversion factor between these is also cos(30deg) -- this is distance to hex at 0,0 local function hex_sdgf_00(pos) local tpx,tpy = t_in(pos.x,pos.y) local d,gx,gy = iqz_hex_sdgf(tpx,tpy,c30) return d, t_out(gx,gy) end local function hex_sdgf(pos, hex) return hex_sdgf_00(pos-hex:to_pos()) end local PLAYER_SIZE = require"common.constants".PLAYER_SIZE local function collide_with_terrain(old_pos, try_pos, map, tries_remaining) tries_remaining = tries_remaining or 3 if tries_remaining <= 0 then return old_pos end local try_h = try_pos:to_hex():round() for near_h in try_h:iter_neighbours(1) do local tile = map:at(near_h) if tile ~= 0 then local d,gx,gy = hex_sdgf(try_pos, near_h) if d < PLAYER_SIZE then local push_dist = PLAYER_SIZE - d local push_dir = Pos:make(gx,gy) local new_try_pos = try_pos + (push_dist*push_dir) return collide_with_terrain( old_pos, new_try_pos, map, tries_remaining - 1 ) end end end return try_pos end return { collide_with_terrain = collide_with_terrain, hex_sdgf=hex_sdgf }