/zoeplat

To get this branch, use:
bzr branch http://9ix.org/bzr/zoeplat
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
1
-- Copyright (c) 2009 Marcus Irven
2
--  
3
-- Permission is hereby granted, free of charge, to any person
4
-- obtaining a copy of this software and associated documentation
5
-- files (the "Software"), to deal in the Software without
6
-- restriction, including without limitation the rights to use,
7
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
8
-- copies of the Software, and to permit persons to whom the
9
-- Software is furnished to do so, subject to the following
10
-- conditions:
11
--  
12
-- The above copyright notice and this permission notice shall be
13
-- included in all copies or substantial portions of the Software.
14
--  
15
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
-- OTHER DEALINGS IN THE SOFTWARE.
23
24
--- Underscore is a set of utility functions for dealing with 
25
-- iterators, arrays, tables, and functions.
26
27
local Underscore = { funcs = {} }
28
Underscore.__index = Underscore
29
30
function Underscore.__call(_, value)
31
	return Underscore:new(value)
32
end
33
34
function Underscore:new(value, chained)
35
	return setmetatable({ _val = value, chained = chained or false }, self)
36
end
37
38
function Underscore.iter(list_or_iter)
39
	if type(list_or_iter) == "function" then return list_or_iter end
40
	
41
	return coroutine.wrap(function() 
42
		for i=1,#list_or_iter do
43
			coroutine.yield(list_or_iter[i])
44
		end
45
	end)
46
end
47
48
function Underscore.range(start_i, end_i, step)
49
	if end_i == nil then
50
		end_i = start_i
51
		start_i = 1
52
	end
53
	step = step or 1
54
	local range_iter = coroutine.wrap(function() 
55
		for i=start_i, end_i, step do
56
			coroutine.yield(i)
57
		end
58
	end)
59
	return Underscore:new(range_iter)
60
end
61
62
--- Identity function. This function looks useless, but is used throughout Underscore as a default.
63
-- @name _.identity
64
-- @param value any object
65
-- @return value
66
-- @usage _.identity("foo")
67
-- => "foo"
68
function Underscore.identity(value)
69
	return value
70
end
71
72
-- chaining
73
74
function Underscore:chain()
75
	self.chained = true
76
	return self
77
end
78
79
function Underscore:value()
80
	return self._val
81
end
82
83
-- iter
84
85
function Underscore.funcs.each(list, func)
86
	for i in Underscore.iter(list) do
87
		func(i)
88
	end
89
	return list
90
end
91
92
function Underscore.funcs.map(list, func)
93
	local mapped = {}
94
	for i in Underscore.iter(list) do
95
		mapped[#mapped+1] = func(i)
96
	end	
97
	return mapped
98
end
99
100
function Underscore.funcs.reduce(list, memo, func)	
101
	for i in Underscore.iter(list) do
102
		memo = func(memo, i)
103
	end	
104
	return memo
105
end
106
107
function Underscore.funcs.detect(list, func)
108
	for i in Underscore.iter(list) do
109
		if func(i) then return i end
110
	end	
111
	return nil	
112
end
113
114
function Underscore.funcs.select(list, func)
115
	local selected = {}
116
	for i in Underscore.iter(list) do
117
		if func(i) then selected[#selected+1] = i end
118
	end
119
	return selected
120
end
121
122
function Underscore.funcs.reject(list, func)
123
	local selected = {}
124
	for i in Underscore.iter(list) do
125
		if not func(i) then selected[#selected+1] = i end
126
	end
127
	return selected
128
end
129
130
function Underscore.funcs.all(list, func)
131
	func = func or Underscore.identity
132
	
133
	-- TODO what should happen with an empty list?
134
	for i in Underscore.iter(list) do
135
		if not func(i) then return false end
136
	end
137
	return true
138
end
139
140
function Underscore.funcs.any(list, func)
141
	func = func or Underscore.identity
142
143
	-- TODO what should happen with an empty list?	
144
	for i in Underscore.iter(list) do
145
		if func(i) then return true end
146
	end	
147
	return false
148
end
149
150
function Underscore.funcs.include(list, value)
151
	for i in Underscore.iter(list) do
152
		if i == value then return true end
153
	end	
154
	return false
155
end
156
157
function Underscore.funcs.invoke(list, function_name, ...)
158
	local args = {...}
159
	Underscore.funcs.each(list, function(i) i[function_name](i, unpack(args)) end)
160
	return list
161
end
162
163
function Underscore.funcs.pluck(list, propertyName)
164
	return Underscore.funcs.map(list, function(i) return i[propertyName] end)
165
end
166
167
function Underscore.funcs.min(list, func)
168
	func = func or Underscore.identity
169
	
170
	return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(min, item) 
171
		if min.item == nil then
172
			min.item = item
173
			min.value = func(item)
174
		else
175
			local value = func(item)
176
			if value < min.value then
177
				min.item = item
178
				min.value = value
179
			end
180
		end
181
		return min
182
	end).item
183
end
184
185
function Underscore.funcs.max(list, func)
186
	func = func or Underscore.identity
187
	
188
	return Underscore.funcs.reduce(list, { item = nil, value = nil }, function(max, item) 
189
		if max.item == nil then
190
			max.item = item
191
			max.value = func(item)
192
		else
193
			local value = func(item)
194
			if value > max.value then
195
				max.item = item
196
				max.value = value
197
			end
198
		end
199
		return max
200
	end).item
201
end
202
203
function Underscore.funcs.to_array(list)
204
	local array = {}
205
	for i in Underscore.iter(list) do
206
		array[#array+1] = i
207
	end	
208
	return array
209
end
210
211
function Underscore.funcs.reverse(list)
212
	local reversed = {}
213
	for i in Underscore.iter(list) do
214
		table.insert(reversed, 1, i)
215
	end	
216
	return reversed
217
end
218
219
function Underscore.funcs.sort(iter, comparison_func)
220
	local array = iter
221
	if type(iter) == "function" then
222
		array = Underscore.funcs.to_array(iter)
223
	end
224
	table.sort(array, comparison_func)
225
	return array
226
end
227
228
-- arrays
229
230
function Underscore.funcs.first(array, n)
231
	if n == nil then
232
		return array[1]
233
	else
234
		local first = {}
235
		n = math.min(n,#array)
236
		for i=1,n do
237
			first[i] = array[i]			
238
		end
239
		return first
240
	end
241
end
242
243
function Underscore.funcs.rest(array, index)
244
	index = index or 2
245
	local rest = {}
246
	for i=index,#array do
247
		rest[#rest+1] = array[i]
248
	end
249
	return rest
250
end
251
252
function Underscore.funcs.slice(array, start_index, length)
253
	local sliced_array = {}
254
	
255
	start_index = math.max(start_index, 1)
256
	local end_index = math.min(start_index+length-1, #array)
257
	for i=start_index, end_index do
258
		sliced_array[#sliced_array+1] = array[i]
259
	end
260
	return sliced_array
261
end
262
263
function Underscore.funcs.flatten(array)
264
	local all = {}
265
	
266
	for ele in Underscore.iter(array) do
267
		if type(ele) == "table" then
268
			local flattened_element = Underscore.funcs.flatten(ele)
269
			Underscore.funcs.each(flattened_element, function(e) all[#all+1] = e end)
270
		else
271
			all[#all+1] = ele
272
		end
273
	end
274
	return all
275
end
276
277
function Underscore.funcs.push(array, item)
278
	table.insert(array, item)
279
	return array
280
end
281
282
function Underscore.funcs.pop(array)
283
	return table.remove(array)
284
end
285
286
function Underscore.funcs.shift(array)
287
	return table.remove(array, 1)
288
end
289
290
function Underscore.funcs.unshift(array, item)
291
	table.insert(array, 1, item)
292
	return array
293
end
294
295
function Underscore.funcs.join(array, separator)
296
	return table.concat(array, separator)
297
end
298
299
-- objects
300
301
function Underscore.funcs.keys(obj)
302
	local keys = {}
303
	for k,v in pairs(obj) do
304
		keys[#keys+1] = k
305
	end
306
	return keys
307
end
308
309
function Underscore.funcs.values(obj)
310
	local values = {}
311
	for k,v in pairs(obj) do
312
		values[#values+1] = v
313
	end
314
	return values
315
end
316
317
function Underscore.funcs.extend(destination, source)
318
	for k,v in pairs(source) do
319
		destination[k] = v
320
	end	
321
	return destination
322
end
323
324
function Underscore.funcs.is_empty(obj)
325
	return next(obj) == nil
326
end
327
328
-- Originally based on penlight's deepcompare() -- http://luaforge.net/projects/penlight/
329
function Underscore.funcs.is_equal(o1, o2, ignore_mt)
330
	local ty1 = type(o1)
331
	local ty2 = type(o2)
332
	if ty1 ~= ty2 then return false end
333
	
334
	-- non-table types can be directly compared
335
	if ty1 ~= 'table' then return o1 == o2 end
336
	
337
	-- as well as tables which have the metamethod __eq
338
	local mt = getmetatable(o1)
339
	if not ignore_mt and mt and mt.__eq then return o1 == o2 end
340
	
341
	local is_equal = Underscore.funcs.is_equal
342
	
343
	for k1,v1 in pairs(o1) do
344
		local v2 = o2[k1]
345
		if v2 == nil or not is_equal(v1,v2, ignore_mt) then return false end
346
	end
347
	for k2,v2 in pairs(o2) do
348
		local v1 = o1[k2]
349
		if v1 == nil then return false end
350
	end
351
	return true
352
end
353
354
-- functions
355
356
function Underscore.funcs.compose(...)
357
	local function call_funcs(funcs, ...)
358
		if #funcs > 1 then
359
			return funcs[1](call_funcs(_.rest(funcs), ...))
360
		else
361
			return funcs[1](...)
362
		end
363
	end
364
	
365
	local funcs = {...}
366
	return function(...)
367
		return call_funcs(funcs, ...)
368
	end
369
end
370
371
function Underscore.funcs.wrap(func, wrapper)
372
	return function(...)
373
		return wrapper(func, ...)
374
	end
375
end
376
377
function Underscore.funcs.curry(func, argument)
378
	return function(...)
379
		return func(argument, ...)
380
	end
381
end
382
383
function Underscore.functions() 
384
	return Underscore.keys(Underscore.funcs)
385
end
386
387
-- add aliases
388
Underscore.methods = Underscore.functions
389
390
Underscore.funcs.for_each = Underscore.funcs.each
391
Underscore.funcs.collect = Underscore.funcs.map
392
Underscore.funcs.inject = Underscore.funcs.reduce
393
Underscore.funcs.foldl = Underscore.funcs.reduce
394
Underscore.funcs.filter = Underscore.funcs.select
395
Underscore.funcs.every = Underscore.funcs.all
396
Underscore.funcs.some = Underscore.funcs.any
397
Underscore.funcs.head = Underscore.funcs.first
398
Underscore.funcs.tail = Underscore.funcs.rest
399
400
local function wrap_functions_for_oo_support()
401
	local function value_and_chained(value_or_self)
402
		local chained = false
403
		if getmetatable(value_or_self) == Underscore then 
404
			chained = value_or_self.chained
405
			value_or_self = value_or_self._val 
406
		end
407
		return value_or_self, chained
408
	end
409
410
	local function value_or_wrap(value, chained)
411
		if chained then value = Underscore:new(value, true) end
412
		return value
413
	end
414
415
	for fn, func in pairs(Underscore.funcs) do
416
		Underscore[fn] = function(obj_or_self, ...)
417
			local obj, chained = value_and_chained(obj_or_self)	
418
			return value_or_wrap(func(obj, ...), chained)		
419
		end	 
420
	end
421
end
422
423
wrap_functions_for_oo_support()
424
425
return Underscore:new()