summaryrefslogtreecommitdiff
path: root/common/coords.lua
blob: f0ca877bd624a9a83e06185122f3b94be4203e14 (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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
local class = require"common.class"
local CHUNK_SIZE = require"common.constants".CHUNK_SIZE

-- 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, ChunkPos

local SR3 = math.sqrt(3)

Hex = class()
function Hex.new(cls)
	return setmetatable({},cls)
end
function Hex.init(self,q,r,s)
	s=s or -q-r
	assert(q+r+s==0,"hex coord doesn't meet invariant")
	self.q=q
	self.r=r
	self.s=s
	return self
end
function Hex.make(cls,...)
	return cls:new():init(...)
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,into)
	into = into or Pos:new()
	local x = self.q*SR3 + self.r*(SR3/2)
	local y = self.r*(3/2)
	return into:init(x,y)
end

function Hex.containing_chunk(self)
	local u = math.floor(self.q/CHUNK_SIZE)
	local v = math.floor(self.r/CHUNK_SIZE)
	return ChunkPos:make(u,v)
end
function Hex.chunk_and_offset(self)
	local cp = self:containing_chunk()
	local tl = cp:extents()
	return cp, (self-tl)
end
function Hex.offset_in_chunk(self)
	local cp,offs = self:chunk_and_offset()
	return offs
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
function Hex.__eq(a,b) return a.q==b.q and a.r==b.r end

Pos = class()
function Pos.new(cls)
	return setmetatable({},cls)
end
function Pos.init(self,x,y)
	self.x = x
	self.y = y
	return self
end
function Pos.make(cls,...)
	return cls:new():init(...)
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.__eq(a,b) return a.x==b.x and a.y==b.y 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,into)
	into = into or Hex:new()
	local q = self.x*(SR3/3) - self.y*(1/3)
	local r = (2/3)*self.y
	return into:init(q,r,-q-r)
end
function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end

-- represents coordinates of a chunk
-- ie pair of integers. the chunk at spawn is C(0,0), the one to the right of that is C(1,0), etc
ChunkPos = class()
function ChunkPos.make(cls,u,v)
	return setmetatable({u=u,v=v},cls)
end
function ChunkPos.__add(self,other) return ChunkPos:make(self.u+other.u,self.v+other.v) end
function ChunkPos.__sub(self,other) return ChunkPos:make(self.u-other.u,self.v-other.v) end
function ChunkPos.__tostring(self) return string.format("C(%d,%d)",self.u,self.v) end
function ChunkPos.__eq(a,b) return a.u==b.u and a.v==b.v end
function ChunkPos.extents(self)
	-- returns Hex of topleft and bottomright
	local tlq,tlr = self.u*CHUNK_SIZE, self.v*CHUNK_SIZE
	local brq,brr = (self.u+1)*CHUNK_SIZE -1, (self.v+1)*CHUNK_SIZE -1
	return Hex:make(tlq,tlr), Hex:make(brq,brr)
end




return {Hex=Hex,Pos=Pos,ChunkPos=ChunkPos}