summaryrefslogtreecommitdiff
path: root/common/coords.lua
blob: 351aa5340c41460fa3ecc19dfb618c5cf5676071 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
local class = require"common.class"

-- Hex: q,r,s. invariant that q+r+s=0
-- add, subtract
-- constructor takes 3 positions and rounds to closest hex centre.

-- round to nearest int, rounding .5 away from 0.
local function round(x)
	if x<0 then
		return math.ceil(x-0.5)
	else
		return math.floor(x+0.5)
	end
end

local Pos, Hex

local SR3 = math.sqrt(3)

local Hex = class()
function Hex.make(cls,q,r,s)
	s=s or -q-r
	assert(q+r+s==0,"hex coord doesn't meet invariant")
	return setmetatable({q=q,r=r,s=s},cls)
end
function Hex.round(self)
	-- return a new Hex rounded to integer coordinates
	local fq,fr,fs = self.q,self.r,self.s
	-- round all to nearest integer
	-- find which was changed the most, reset that one to be coherent with the other two.
	local abs = math.abs

	local rq,rr,rs = round(fq),round(fr),round(fs)
	local dq,dr,ds = abs(fq-rq), abs(fr-rr), abs(fs-rs)

	if dq>dr and dq>ds then
		rq = -rr-rs
	elseif dr>ds then
		rr = -rq-rs
	else
		rs = -rq-rr
	end

	return Hex:make(rq,rr,rs)
end

function Hex.to_pos(self)
	local x = self.q*SR3 + self.r*(SR3/2)
	local y = self.r*(3/2)
	return Pos:make(x,y)
end

function Hex.__add(self,other) return Hex:make(self.q+other.q, self.r+other.r, self.s+other.s) end
function Hex.__sub(self,other) return Hex:make(self.q-other.q, self.r-other.r, self.s-other.s) end
function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end


Pos = class()
function Pos.make(cls,x,y)
	return setmetatable({x=x,y=y},cls)
end
function Pos.__add(self,other) return Pos:make(self.x+other.x,self.y+other.y) end
function Pos.__sub(self,other) return Pos:make(self.x-other.x,self.y-other.y) end
function Pos.__mul(a,b)
	if type(a) == "number" then
		return Pos:make(a*b.x,a*b.y)
	elseif type(b) == "number" then
		return Pos:make(a.x*b,a.y*b)
	else
		error("can only multiply Pos by scalar")
	end
end
function Pos.__div(a,b)
	assert(type(b) == "number","can only divide Pos by scalar, and can't divide scalar by Pos")
	return a*(1/b)
end
function Pos.lensq(self) return self.x^2 + self.y^2 end
function Pos.len(self) return math.sqrt(self:lensq()) end
function Pos.norm(self) return self/self:len() end
function Pos.dot(self,other) return self.x*other.x + self.y*other.y end
function Pos.to_hex(self)
	local q = self.x*(SR3/3) - self.y*(1/3)
	local r = (2/3)*self.y
	return Hex:make(q,r,-q-r)
end
function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end



return {Hex=Hex,Pos=Pos}