aboutsummaryrefslogtreecommitdiff
path: root/libs/sti/utils.lua
blob: 95e857ad0c17df66dc0ffe45d5952b40f720ebe7 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
-- Some utility functions that shouldn't be exposed.
local utils = {}

-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
function utils.format_path(path)
	local np_gen1,np_gen2  = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
	local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
	local k

	repeat -- /./ -> /
		path,k = path:gsub(np_pat2,'/',1)
	until k == 0

	repeat -- A/../ -> (empty)
		path,k = path:gsub(np_pat1,'',1)
	until k == 0

	if path == '' then path = '.' end

	return path
end

-- Compensation for scale/rotation shift
function utils.compensate(tile, tileX, tileY, tileW, tileH)
	local compx = 0
	local compy = 0

	if tile.sx < 0 then compx = tileW end
	if tile.sy < 0 then compy = tileH end

	if tile.r > 0 then
		tileX = tileX + tileH - compy
		tileY = tileY + tileH + compx - tileW
	elseif tile.r < 0 then
		tileX = tileX + compy
		tileY = tileY - compx + tileH
	else
		tileX = tileX + compx
		tileY = tileY + compy
	end

	return tileX, tileY
end

-- Cache images in main STI module
function utils.cache_image(sti, path, image)
	image = image or love.graphics.newImage(path)
	image:setFilter("nearest", "nearest")
	sti.cache[path] = image
end

-- We just don't know.
function utils.get_tiles(imageW, tileW, margin, spacing)
	imageW  = imageW - margin
	local n = 0

	while imageW >= tileW do
		imageW = imageW - tileW
		if n ~= 0 then imageW = imageW - spacing end
		if imageW >= 0 then n  = n + 1 end
	end

	return n
end

-- Decompress tile layer data
function utils.get_decompressed_data(data)
	local ffi     = require "ffi"
	local d       = {}
	local decoded = ffi.cast("uint32_t*", data)

	for i = 0, data:len() / ffi.sizeof("uint32_t") do
		table.insert(d, tonumber(decoded[i]))
	end

	return d
end

-- Convert a Tiled ellipse object to a LOVE polygon
function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
	local ceil = math.ceil
	local cos  = math.cos
	local sin  = math.sin

	local function calc_segments(segments)
		local function vdist(a, b)
			local c = {
				x = a.x - b.x,
				y = a.y - b.y,
			}

			return c.x * c.x + c.y * c.y
		end

		segments = segments or 64
		local vertices = {}

		local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }

		local m
		if love and love.physics then
			m = love.physics.getMeter()
		else
			m = 32
		end

		for _, i in ipairs(v) do
			local angle = (i / segments) * math.pi * 2
			local px    = x + w / 2 + cos(angle) * w / 2
			local py    = y + h / 2 + sin(angle) * h / 2

			table.insert(vertices, { x = px / m, y = py / m })
		end

		local dist1 = vdist(vertices[1], vertices[2])
		local dist2 = vdist(vertices[3], vertices[4])

		-- Box2D threshold
		if dist1 < 0.0025 or dist2 < 0.0025 then
			return calc_segments(segments-2)
		end

		return segments
	end

	local segments = calc_segments(max_segments)
	local vertices = {}

	table.insert(vertices, { x = x + w / 2, y = y + h / 2 })

	for i = 0, segments do
		local angle = (i / segments) * math.pi * 2
		local px    = x + w / 2 + cos(angle) * w / 2
		local py    = y + h / 2 + sin(angle) * h / 2

		table.insert(vertices, { x = px, y = py })
	end

	return vertices
end

function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
	if map.orientation == "isometric" then
		x, y               = utils.convert_isometric_to_screen(map, x, y)
		vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
	end

	vertex.x = vertex.x - x
	vertex.y = vertex.y - y

	return
		x + cos * vertex.x - sin * vertex.y,
		y + sin * vertex.x + cos * vertex.y - (oy or 0)
end

--- Project isometric position to cartesian position
function utils.convert_isometric_to_screen(map, x, y)
	local mapW    = map.width
	local tileW   = map.tilewidth
	local tileH   = map.tileheight
	local tileX   = x / tileH
	local tileY   = y / tileH
	local offsetX = mapW * tileW / 2

	return
		(tileX - tileY) * tileW / 2 + offsetX,
		(tileX + tileY) * tileH / 2
end

function utils.hex_to_color(hex)
	if hex:sub(1, 1) == "#" then
		hex = hex:sub(2)
	end

	return {
		r = tonumber(hex:sub(1, 2), 16) / 255,
		g = tonumber(hex:sub(3, 4), 16) / 255,
		b = tonumber(hex:sub(5, 6), 16) / 255
	}
end

function utils.pixel_function(_, _, r, g, b, a)
	local mask = utils._TC

	if r == mask.r and
		g == mask.g and
		b == mask.b then
		return r, g, b, 0
	end

	return r, g, b, a
end

function utils.fix_transparent_color(tileset, path)
	local image_data = love.image.newImageData(path)
	tileset.image = love.graphics.newImage(image_data)

	if tileset.transparentcolor then
		utils._TC = utils.hex_to_color(tileset.transparentcolor)

		image_data:mapPixel(utils.pixel_function)
		tileset.image = love.graphics.newImage(image_data)
	end
end

function utils.deepCopy(t)
	local copy = {}
	for k,v in pairs(t) do
		if type(v) == "table" then
			v = utils.deepCopy(v)
		end
		copy[k] = v
	end
	return copy
end

return utils