1-- GunFu Deadlands
2-- Copyright 2009-2011 Christiaan Janssen, September 2009-October 2011
3--
4-- This file is part of GunFu Deadlands.
5--
6--     GunFu Deadlands is free software: you can redistribute it and/or modify
7--     it under the terms of the GNU General Public License as published by
8--     the Free Software Foundation, either version 3 of the License, or
9--     (at your option) any later version.
10--
11--     GunFu Deadlands is distributed in the hope that it will be useful,
12--     but WITHOUT ANY WARRANTY; without even the implied warranty of
13--     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14--     GNU General Public License for more details.
15--
16--     You should have received a copy of the GNU General Public License
17--     along with GunFu Deadlands.  If not, see <http://www.gnu.org/licenses/>.
18
19
20mymath = {}
21
22-- 2d rotation of a vector, angle given in radians
23function mymath.get_rotation_matrix( angle )
24	local c,s = math.cos(angle), math.sin(angle)
25	return { c, -s, s, c }
26end
27
28function mymath.rotate(vector, angle)
29	local m = mymath.get_rotation_matrix(angle)
30	return {vector[1]*m[1]+vector[2]*m[2],vector[1]*m[3]+vector[2]*m[4]}
31end
32
33function mymath.disturb_vector(vector, deviation)
34	local normal_dist = mymath.randn2()
35	local direction = {vector[1] + normal_dist[1] * deviation, vector[2] + normal_dist[2] * deviation }
36	-- force normalization again
37	return mymath.get_dir_vector( 0, 0, direction[1], direction[2] )
38end
39
40function mymath.sign( a )
41	if a>=0 then return 1 end
42	return -1
43end
44
45function mymath.round( r )
46	return math.floor(r+0.5)
47end
48
49function mymath.normalize_vector( vec )
50	local mag = math.sqrt( vec[1]*vec[1] + vec[2]*vec[2] )
51	return { vec[1] / mag, vec[2] / mag }
52end
53
54function mymath.get_angle( v1, v2 )
55	nv1 = mymath.normalize_vector( v1 )
56	nv2 = mymath.normalize_vector( v2 )
57 	local cosangle = math.acos( nv1[1]*nv2[1] + nv1[2]*nv2[2] )
58	local sinangle = math.asin( nv1[1]*nv2[2] - nv1[2]*nv2[1] )
59
60	return cosangle*mymath.sign(sinangle)
61
62end
63
64
65
66-- returns gaussian random number
67-- from http://www.taygeta.com/random/gaussian.html
68-- Algorithm by Dr. Everett (Skip) Carter, Jr.
69
70function mymath.randn()
71	local x1,x2
72	local w = 1
73	while w >= 1 and w>0 do
74		x1 = 2 * math.random() - 1
75		x2 = 2 * math.random() - 1
76		w = x1*x1 + x2*x2
77	end
78
79	w = math.sqrt( (-2 * math.log( w ) / w ) )
80	return x1 * w
81end
82
83function mymath.randn2()
84	local x1,x2
85	local w = 1
86	while w >= 1 and w>0 do
87		x1 = 2 * math.random() - 1
88		x2 = 2 * math.random() - 1
89		w = x1*x1 + x2*x2
90	end
91
92	w = math.sqrt( (-2 * math.log( w ) / w ) )
93	return {x1 * w, x2 * w}
94end
95
96-- returns a list with the ordinals of a random permutation of length len
97function mymath.permutation( len )
98	used = {}
99	seq = {}
100
101	for i=1,len do
102		used[i]=0
103	end
104
105	for i=1,len do
106		newnum = math.random(len-i+1)
107		j=0
108		while newnum>0 do
109			j=j+1
110			if used[j]==0 then
111				newnum = newnum-1
112			end
113		end
114
115		used[j]=1
116		seq[i]=j
117	end
118
119	return seq
120
121end
122
123function mymath.get_distance(point1, point2)
124	local dx = point2[1] - point1[1]
125	local dy = point2[2] - point1[2]
126	return math.sqrt(dx*dx + dy*dy)
127end
128
129function mymath.get_distanceSq(point1, point2)
130	local dx = point2[1] - point1[1]
131	local dy = point2[2] - point1[2]
132	return (dx*dx + dy*dy)
133end
134
135-- the "public" function
136function mymath.check_intersection(segment, box)
137
138  -- the first point is inside the box?
139  if segment[1]>= box[1] and segment[1]<=box[3] and segment[2]>=box[2] and segment[2]<=box[4] then
140	return true
141  end
142
143  -- the second point is inside the box?
144  if segment[3]>= box[1] and segment[3]<=box[3] and segment[4]>=box[2] and segment[4]<=box[4] then
145	return true
146  end
147
148  -- we sort here the segment coordinates because we treat it as a box
149  if not mymath.check_boxes(
150    { math.min(segment[1],segment[3]), math.min(segment[2],segment[4]),
151	  math.max(segment[1],segment[3]), math.max(segment[2],segment[4]) },
152	box
153  ) then
154	return false
155  end
156
157
158  -- for the real intersection check, the points must not be sorted
159  return mymath.check_intersection_internal( segment, box )
160
161end
162
163
164-- for internal use
165function mymath.check_intersection_internal( segment, box )
166	local dX = segment[3] - segment[1]
167	local dY = segment[4] - segment[2]
168
169	if math.abs(dX) > 0.001 then
170		-- find intersection with left side of the box
171		local tL = (box[1]-segment[1])/dX
172		if tL >= 0  and tL <= 1 -- in the valid range
173		then
174			local yL = segment[2] + tL*dY
175			if yL <= box[4] and yL >= box[2] then
176				return true -- yes they are!
177			end
178		end
179
180
181		-- find intersection with right side of the box
182		local tR = (box[3]-segment[1])/dX
183		if tR >= 0  and tR <= 1 -- in the valid range
184		then
185			local yR = segment[2] + tR*dY
186			if yR <= box[4] and yR >= box[2] then
187				return true -- yes they are!
188			end
189		end
190
191	end
192
193	if math.abs(dY) > 0.001 then
194		-- find intersection with upper side of the box
195		local tU = (box[2]-segment[2])/dY
196		if tU >= 0  and tU <= 1 -- in the valid range
197		then
198			local xU = segment[1] + tU*dX
199			if xU >= box[1] and xU <= box[3] then
200				return true -- yes they are!
201			end
202		end
203
204		-- find intersection with lower side of the box
205		local tD = (box[4]-segment[2])/dY
206		if tD >= 0  and tD <= 1 -- in the valid range
207		then
208			local xD = segment[1] + tD*dX
209			if xD >= box[1] and xD <= box[3] then
210				return true -- yes they are!
211			end
212		end
213	end
214
215	return false
216end
217
218-- for internal use (returns true if boxes intersect
219function mymath.check_boxes(box1, box2)
220	if box1[3]<box2[1] or
221		box1[1]>box2[3] or
222		box1[2]>box2[4] or
223		box1[4]<box2[2] then
224		return false
225	end
226	return true
227end
228
229function mymath.check_boxinbox(small_box, big_box)
230	if small_box[1] < big_box[1] or
231		small_box[2] < big_box[2] or
232		small_box[3] > big_box[3] or
233		small_box[4] > big_box[4] then
234		return false
235	end
236	return true
237end
238
239function mymath.check_pointinbox(point, box)
240	if point[1] >= box[1] and point[1] <= box[3] and
241		point[2] >= box[2] and point[2] <= box[4] then
242		return true
243	else
244		return false
245	end
246end
247
248function mymath.get_dir_vector(origx, origy, destx, desty)
249	-- local destv = { love.mouse.getX() - origx, love.mouse.getY() - origy }
250	local destv = { destx - origx, desty - origy }
251	local modul = math.sqrt( destv[1]*destv[1] + destv[2]*destv[2] )
252	if modul < 0.001 then
253	  modul = 0.001
254	end
255	return { destv[1]/modul, destv[2]/modul }
256end
257
258
259-- more complex collision detection
260function mymath.check_pointinbuilding( point, building )
261	-- for each box...
262	-- first check if the point is inside the box
263	-- if it is, check if it's also inside all the halfspaces
264	for i,colli in ipairs(building.collision) do
265		if mymath.check_pointinbox(point, colli) then
266				return true
267
268		end
269	end
270	return false
271end
272
273function mymath.check_segmentinbuilding( segment, building )
274	-- for each box...
275	-- first check if the segment intersects the bounding box
276	-- if it does, check if at least one of the points is actually inside
277	for i,colli in ipairs(building.collision) do
278		if mymath.check_intersection(segment, colli) then
279				return true
280
281		end
282	end
283	return false
284
285end
286
287-- assuming the point is outside of the box
288-- returns { distance squared, pointx, pointy }
289-- the returned point is the closest point in the box's edge to the input point
290function mymath.distanceSq_to_box( point, box )
291	-- directly above or below?
292	if point[1] >= box[1] and point[1]<= box[3] then
293		if point[2] < box[2] then
294			-- above
295			local diff = box[2] - point[2]
296			return { diff*diff, point[1], box[2] }
297		else
298			-- below
299			local diff = box[4] - point[2]
300			return { diff*diff, point[1], box[4] }
301		end
302	end
303
304	-- directly on the side?
305	if point[2] >= box[2] and point[2]<= box[4] then
306		if point[1] < box[1] then
307			-- left
308			local diff = box[1] - point[1]
309			return { diff*diff, box[1], point[2] }
310		else
311			-- right
312			local diff = box[3] - point[1]
313			return { diff*diff, box[3], point[2] }
314		end
315	end
316
317	-- diagonal...
318	local p1
319	if point[1] < box[1] then
320		p1 = box[1]
321	else
322		p1 = box[3]
323	end
324
325	local p2
326	if point[2] < box[2] then
327		p2 = box[2]
328	else
329		p2 = box[4]
330	end
331
332	local d1,d2 = p1 - point[1], p2 - point[2]
333	return {d1*d1+d2*d2, p1, p2}
334end
335
336function mymath.check_boxinbuilding( box1, building )
337	-- first check mutual box collision
338	-- if it happens, check each individual point and see if at least
339	-- one of them is inside the building
340	for i,colli in ipairs(building.collision) do
341		if mymath.check_boxes(box1, colli) then
342			return true
343		end
344	end
345	return false
346end
347
348
349