-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloq_util.lua
816 lines (653 loc) · 23.8 KB
/
loq_util.lua
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
-- Copyright 2011 Loqheart
--[[
Module: loq_util
Utility functions used by other modules.
Usage:
require("loq_util")
]]
-- Some utilities for better debugging, print_r taken from Corona's CodeExchange
--[[
Group: Debugging
]]
--[[
Function: atrace
Prints a debugging message with a time stamp and stack trace.
Parameters:
- _msg The message to display, can be a string, function, or table
- _depth The depth of the stack. Defaults to 1.
]]
function atrace(_msg, _depth)
if _msg == nil then
_msg = "nil"
else
if type(_msg) == 'function' then
_msg = functionInfo(_msg)
elseif type(_msg) == 'table' then
_msg = xinspect(_msg)
else
_msg = tostring(_msg)
end
end
_depth = _depth or 1
local sysTime = system and ("(" .. formatMS(system.getTimer()) .. ") ") or "- "
print(" = > atrace " .. sysTime .. _msg)
local res = debug.traceback():split("\n")
local counter = 1
for i, v in ipairs(res) do
counter = counter + 1
if counter > 3 and counter < (3+_depth+1) then
print(tostring(v))
end
end
--print(table.concat(res, "\n", 3, _depth + 2))
-- sometimes gets an error that some table values in res aren't strings
print()
end
--[[
Function: formatMS
Format milliseconds into a string HH:MM:SS:MS
Parameters:
- _ms Milliseconds from a call like system.getTimer()
Returns:
A string in the format HH:MM:SS:MS where HH is hours, MM is minutes, SS is seconds, and MS is milliseconds
]]
function formatMS(_ms)
if not _ms then
return ""
end
local ms = _ms % 1000
local secs = math.floor(_ms / 1000)
local mins = math.floor(secs / 60)
local hours = math.floor(mins / 60)
secs = secs % 60
mins = mins % 60
hours = hours % 24
return string.format("%02d:%02d:%02d:%03d", hours, mins, secs, ms)
end
--[[
Function: functionInfo
Returns the file and line numbers for a function's definition
Parameters:
- _func The function
Returns:
A string with the file and line numbers for the function's definition. If not a valid function then returns nil.
]]
function functionInfo(_func)
if type(_func) == 'function' then
local info = debug.getinfo(_func)
return 'function ' .. info.source .. ':[' .. info.linedefined .. ', ' .. info.lastlinedefined .. ']'
else
return nil
end
end
--[[
Function: loq_declare
Used to declare global values. If loq_DeclareGlobals is true before the loq_util module is loaded,
then global values used without loq_declare() throw an error. From kam187 on the Corona Code Exchange
Parameters:
- _name Name of the global variable.
- _initval Initial value of the variable.
Usage:
(start code)
local loq_DeclareGlobals = true
require('loq_util')
loq_declare("MyGlobal", {})
(end)
]]
function loq_declare (_name, _initval)
rawset(_G, _name, _initval or {})
end
--[[
Function: loq_undeclared
Checks if a string name of a variable has been declared as a global.
Parameters:
- _name Name of the global variable.
Returns:
- true if the variable name is undeclared with loq_declare.
- false if the variable has been declared with loq_declare.
]]
function loq_undeclared(_name)
return rawget(_G, _name) == nil
end
--[[
Function: xinspect
Returns a nicely formated string with the properties and contents listed of the parameter passed in.
Parameters:
- _t A value.
Returns:
A nicely formated string with the properties and contents listed.
Usage:
(start code)
local values = { a = 1, b = 'hello' }
atrace(xinspect(values))
(end)
]]
function xinspect( _t )
local status, retval = pcall(print_r, _t)
return retval
end
function print_r ( t )
local out = {}
local print_r_cache={}
local function sub_print_r(t,indent)
if (print_r_cache[tostring(t)]) then
table.insert(out, indent.."*"..tostring(t))
else
print_r_cache[tostring(t)]=true
if (type(t)=="table") then
for pos,val in pairs(t) do
if (type(val)=="table") then
table.insert(out, indent.."["..pos.."] => "..tostring(t).." {")
sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
table.insert(out, indent..string.rep(" ",string.len(pos)+6).."}")
elseif (type(val)=="string") then
table.insert(out, indent.."["..pos..'] => "'..val..'"')
else
table.insert(out, indent.."["..pos.."] => "..tostring(val))
end
end
else
table.insert(out, indent..tostring(t))
end
end
end
if (t["__tostring"]) then
table.insert(out, tostring(t))
elseif (type(t)=="table") then
table.insert(out, tostring(t).." {")
sub_print_r(t," ")
table.insert(out, "}")
else
sub_print_r(t," ")
end
return table.concat(out, "\n")
end
--[[
Group: String
]]
--[[
Function: string:split
Splits the string using with a separator
Parameters:
- sSeparator A string separator.
Returns:
A table of strings.
]]
function string:split(sSeparator, nMax, bRegexp)
--assert(sSeparator ~= '')
assert(nMax == nil or nMax >= 1)
local aRecord = {}
if sSeparator == '' then
for i = 1, #self do
aRecord[i] = self:sub(i, i)
end
elseif self:len() > 0 then
local bPlain = not bRegexp
nMax = nMax or -1
local nField, nStart = 1, 1
local nFirst,nLast = self:find(sSeparator, nStart, bPlain)
while nFirst and nMax ~= 0 do
aRecord[nField] = self:sub(nStart, nFirst-1)
nField = nField+1
nStart = nLast+1
nFirst,nLast = self:find(sSeparator, nStart, bPlain)
nMax = nMax-1
end
aRecord[nField] = self:sub(nStart)
end
return aRecord
end
--[[
Group: Table
]]
-- supports older versions of Corona
if not table.indexOf then
--[[
Function: table.indexOf
Searches a table for an item and returns its index or nil if not found
Parameters:
- _t The table.
- _item The item to find.
Returns:
The index or nil if not found.
NOTE: This is only defined if table.indexOf does not already exist in the runtime environment.
It is added only for compatibility with older versions of the Corona SDK.
]]
function table.indexOf(_t, _item)
for i, v in ipairs(_t) do
if _item == v then
return i
end
end
return nil
end
end
-- supports older versions of Corona
if not table.copy then
--[[
Function: table.copy
Returns a shallow copy of array, i.e. the portion of the array (table) with integer keys.
A variable number of additional arrays can be passed in as optional arguments.
If an array has a hole (a nil entry), copying in a given source array stops at the
last consecutive item prior to the hole.
Parameters:
- ... Optional, one or more tables.
Returns:
Returns a shallow copy of combined arrays.
NOTE: This is only defined if table.copy does not already exist in the runtime environment.
It is added only for compatibility with older versions of the Corona SDK.
]]
function table.copy(...)
local t = {}
for adx, tb in ipairs(arg) do
for i = 1, #tb do
table.insert(t, tb[i])
end
end
return t
end
end
--[[
Function: table.removeItem
Searches a table for an item and removes it if present.
Parameters:
- _t The table.
- _item The item to remove.
]]
function table.removeItem(_t, _item)
local i = table.indexOf(_t, _item)
if i ~= nil then
table.remove(_t, i)
end
end
--[[
Group: Math
]]
function bezierValue(_t, _a, _b, _c, _d)
return (_t*_t*(_d-_a) + 3*(1-_t)*(_t*(_c-_a) + (1-_t)*(_b-_a)))*_t + _a;
end
--[[
Function: rotateVec2
Rotates vector components x, y through an angle.
Parameters:
- _degrees The angle in degrees
- _x X component of 2D vector
- _y Y component of 2D vector
Returns:
The rotated x and y values.
]]
function rotateVec2(_degrees, _x, _y)
local angle = math.rad(_degrees)
local ct = math.cos(angle)
local st = math.sin(angle)
return (_x*ct - _y*st), (_x*st + _y*ct)
end
loq_declare('socket')
function testNetworkConnection(_url)
assert(_url, "URL cannon be nil")
local nc = require('socket').connect(_url, 80)
if nc == nil then
return false
end
nc:close()
return true
end
function loq_addEventListener(self, _name, _listener)
self:_origAdd( _name, _listener )
if self.__listeners[_name] == nil then
self.__listeners[_name] = { _listener }
else
local index = table.indexOf(self.__listeners[_name], _listener)
if (index == nil) then
table.insert(self.__listeners[_name], _listener)
end
end
end
function loq_removeEventListener(self, _name, _listener)
self:_origRemove( _name, _listener )
if self.__listeners[_name] ~= nil then
table.removeItem(self.__listeners[_name], _listener)
end
end
function loq_clearListeners(self, _name)
if _name == nil then
self.__listeners = {}
elseif type(_name) == 'string' then
self.__listeners[_name] = nil
end
end
function loq_dispatchEvent(self, _e)
local name = _e.name
local ret = false
if self.__listeners[name] ~= nil then
local listeners = self.__listeners[name]
for i = 1, #listeners do
local l = listeners[i]
if type(l) == 'function' then
ret = l(_e)
elseif type(l) == 'table' and type(l[name]) == 'function' then
local f = l[name]
ret = f(l, _e)
end
if ret then
break
end
end
end
return ret
end
--[[
Group: Events
]]
--[[
Function: loq_listeners
Uses adds event dispatching to the object. Can be used to replace the event dispatching
functions (addEventListener, removeEventListener, dispatchEvent) for display objects or
to add event dispatching to regular objects. Also adds clearListeners for remove all
event listeners or event listeners of a certain type.
Parameters:
- _obj A table.
NOTE: loq_listeners is used in SpriteGroups to replace the Corona display object event dispatching
system in order to prevent misbehavior in the Corona event dispatching API.
]]
function loq_listeners(_obj)
_obj.__listeners = {}
if _obj.addEventListener then
_obj:addEventListener('collision', function() end)
end
_obj._origAdd = _obj.addEventListener
_obj._origRemove = _obj.removeEventListener
_obj.addEventListener = loq_addEventListener
_obj.removeEventListener = loq_removeEventListener
_obj.dispatchEvent = loq_dispatchEvent
_obj.clearListeners = loq_clearListeners
end
--[[
Group: Graphics Optimization
]]
function loq_display_cache(_d, _x, _y, _rotation, _xScale, _yScale)
if _x then
_d.x, _d.cx = _x, _x
else
_d.cx = _d.x
end
if _y then
_d.y, _d.cy = _y, _y
else
_d.cy = _d.y
end
if _rotation then
_d.rotation, _d.crotation = _rotation, _rotation
else
_d.crotation = _d.rotation
end
if _xScale then
_d.xScale, _d.cxScale = _xScale, _xScale
else
_d.cxScale = _d.xScale
end
if _yScale then
_d.yScale, _d.cyScale = _yScale, _yScale
else
_d.cyScale = _d.yScale
end
return _d
end
function loq_translateTo(_d, _x, _y)
if _x == _d.cx and _y == _d.cy then
return _d
end
local dx = _x - _d.cx
local dy = _y - _d.cy
_d:_ctranslate(dx, dy)
_d.cx = _x
_d.cy = _y
return _d
end
function loq_rotateTo(_d, _w)
if _w == _d.crotation then
return _d
end
local dw = _w - _d.crotation
_d:_crotate(dw)
_d.crotation = _w
return _d
end
function loq_scaleTo(_d, _xScale, _yScale)
if _xScale == _d.cxScale and _yScale == _d.cyScale then
return _d
end
local dxs = _xScale/_d.cxScale
local dys = _yScale/_d.cyScale
_d:_cscale(dxs, dys)
_d.cxScale = _xScale
_d.cyScale = _yScale
return _d
end
function loq_translate(_d, _dx, _dy)
_d.cx = _d.cx + _dx
_d.cy = _d.cy + _dy
_d:_ctranslate(_dx, _dy)
end
function loq_rotate(_d, _dw)
_d.crotation = _d.crotation + _dw
_d:_crotate(_dw)
end
function loq_scale(_d, _dxScale, _dyScale)
_d.cxScale = _d.cxScale * _dxScale
_d.cyScale = _d.cyScale * _dyScale
_d:_cscale(_dxScale, _dyScale)
end
--[[
Function: loq_display
Adds functions to the display object to optimize transformations: translating, rotating, scaling.
Use the methods translateTo, rotateTo and scaleTo to transform the display object, instead of directly
setting the x, y, rotation, xScale, and yScale values for higher performance.
The speed up is due to caching the x, y, etc properties and using the translate, rotate, and scale
functions to operate on these cached values.
The loq_display function also modifies translate, rotate, and scale to used cached values.
You can directly access the cached values as cx, cy, crotation, cxScale, and cyScale.
Methods:
- translateTo Takes x and y target values to position the object.
- scaleTo Takes xScale and yScale target values to scale the object.
- rotateTo Takes an rotation target value to rotate the object.
- cache Initialize the display object and its cache properties. Optionally takes x, y, rotation, xScale, yScale.
Cached Properties:
- cx Cached x value
- cy Cached y value
- crotation Cached rotation value
- cxScale Cached xScale value
- cyScale Cached yScale value
You can use the cached properties for increased performance.
Note: You should use the actual x, y, and rotation properties if the display object is a physics body.
Check out the example below.
Parameters:
- _d A display object
]]
function loq_display(_d)
if not _d.cache then
_d.cache = loq_display_cache
_d:cache()
-- save the Corona versions
_d._ctranslate = _d.translate
_d._crotate = _d.rotate
_d._cscale = _d.scale
_d.translateTo = loq_translateTo
_d.rotateTo = loq_rotateTo
_d.scaleTo = loq_scaleTo
_d.translate = loq_translate
_d.rotate = loq_rotate
_d.scale = loq_scale
end
return _d
end
--[[
Group: Modules
]]
--[[
Function: unrequire
Removes a required lua file from the loaded packages to free up application memory.
Parameters:
- _filename The name of a lua file to be removed from the loaded packages to clear up application memory.
]]
function unrequire(m)
package.loaded[m] = nil
rawset(_G, m, nil)
-- Search for the shared library handle in the registry and erase it
local registry = debug.getregistry()
local nMatches, mKey, mt = 0, nil, registry['_LOADLIB']
for key, ud in pairs(registry) do
if type(key) == 'string' and type(ud) == 'userdata' and getmetatable(ud) == mt and string.find(key, "LOADLIB: .*" .. m) then
nMatches = nMatches + 1
if nMatches > 1 then
return false, "More than one possible key for module '" .. m .. "'. Can't decide which one to erase."
end
mKey = key
end
end
if mKey then
registry[mKey] = nil
end
return true
end
if loq_DeclareGlobals then
setmetatable(_G, {
__newindex = function(_ENV, var, val)
if var ~= 'tableDict' then
error(("attempt to set undeclared global\"%s\""):format(tostring(var)), 2)
else
rawset(_ENV, var, val)
end
end,
__index = function(_ENV, var)
if var ~= 'tableDict' then
error(("attempt to read undeclared global\"%s\""):format(tostring(var)), 2)
end
end,
})
end
--[[
Section: Examples
Example: Using atrace and xinspect for debugging
Using atrace and xinspect for debugging. You might use print to get debugging info from the Corona terminal.
You can use atrace where you would use print to get extra information like line numbers, timings, and a stack trace.
atrace can check if the value passed in is a function or a table and attempt to print it out. You can use xinspect
on tables and functionInfo on functions if you need to concatenate their values to a string.
(start code)
require('loq_util')
-- print out a message
atrace('This works like print, but you get a time, file, line number, and function')
-- print out a value
local myvar = 10
atrace(myvar)
local mytable = {a = 100}
atrace(mytable) -- that's better!
--atrace('mytable ' .. mytable) -- Throws an error. Can't convert a table to a string
-- Use xinspect on mytable
atrace('mytable ' .. xinspect(mytable)) -- that's better
local function myfunc()
atrace('inside a function')
local function anotherfunc()
atrace("notice that atrace prints the function you're in!")
atrace("let's print the stack 2 deep", 2)
local function funcInFunc()
atrace("let's print the stack 2 deep again", 2)
atrace('now all the way', 10)
end
funcInFunc()
atrace('assigning the function to a variable in lua we lose the original name')
local renameMyFunc = funcInFunc
renameMyFunc()
atrace("we can get some info about the function renameMyFunc points to")
atrace(renameMyFunc)
end
anotherfunc()
end
myfunc()
(end)
]]
--[[
Example: Global declaration of variables before usage
Global declaration of variables before usage.
If you want to force declarations of globals before usage set a variable loq_DeclareGlobals to true before requiring 'loq_util'.
Then you can use the loq_declare declare the global variable before using it. You can also use loq_undeclared to check whether
a variable has been declare before accessing it.
NOTE: With loq_DeclareGlobals enabled, some Corona modules you require must be declared first because they create globals (e.g. 'physics', 'sprite')
main.lua -- using loq_DeclareGlobals in main.lua sets the option for you application
(start code)
loq_DeclareGlobals = true -- if nil or false then globals don't require declarations
require('loq_util')
loq_declare('physics')
require('physics') -- The Corona physics module must be declared because it creates a global.
loq_declare('myglobal')
myglobal = 10
atrace(loq_undeclared('myUndeclaredGlobal'))
atrace(myUndeclaredGlobal) -- throws an error
(end)
yourmodule.lua -- Using the module function creates a global value. You need to declare the module if loq_DeclareGlobals is true.
(start code)
if loq_DeclareGlobals then
loq_declare('yourmodule')
end
module(..., package.seeall) -- this globally defines your module when required
-- the rest of the module
(end)
]]
--[[
Example: Unloading modules from application memory with unrequire.
Unloading modules from application memory with unrequire. Loading a module with the standard 'require' function creates a global variable.
And loads the module into global memory. If you're temporarily using a module, then this is basically a form of a memory leak.
You can reclaim some of the application memory by calling 'unrequire' on the required module. Try commmenting and uncommenting
your require and unrequire calls with the loq_profiler to see the difference in your application memory usage.
(start code)
require('loq_util')
-- instantiate to keep an eye on application memory
require('loq_profiler').createProfiler()
local myModule = require('myModule')
-- ... some processing here, don't need the module anymore
unrequire('myModule') -- removes the module from the global packages and reclaims some app memory
(end)
]]
--[[
Example: Using loq_display to boost performance of transforming display objects
Based on testing as of build 721, accessing the properties of a display object in Corona is very slow.
http://developer.anscamobile.com/forum/2011/12/03/tips-optimization-101#comment-71273
Using the loq_display function on display objects is a workaround to speed up the performance of
transforming display objects. By default, Spriteloq SpriteGroups use loq_display.
With loq_display applied to a display object it gets these new methods and properties which can be used in place
of accessing the x, y, rotation, xScale, yScale for transforms.
Methods:
- translateTo Takes x and y target values to position the object.
- scaleTo Takes xScale and yScale target values to scale the object.
- rotateTo Takes an rotation target value to rotate the object.
- cache Initialize the display object and its cache properties. Optionally takes x, y, rotation, xScale, yScale.
Cached Properties:
- cx Cached x value
- cy Cached y value
- crotation Cached rotation value
- cxScale Cached xScale value
- cyScale Cached yScale value
You can use the cached properties for increased performance.
Note: You should use the actual x, y, and rotation properties if the display object is a physics body.
(start code)
require('loq_util')
local rect = display.newRect(0, 0, 100, 100)
loq_display(rect) -- gains performance transform methods and properties and caches the initial values
rect:cache(200, 200, 45) -- sets the initial cached properties
local angle = 45
local x = 200
local y = 200
local function enterFrame(_e)
-- accessing the x, y, rotation, xScale and yScale properties is slower
rect:rotateTo(angle)
angle = angle + 1
rect:translateTo(x, y)
x = x + 1
-- this access is fast
local cx = rect.cx
-- this access is slow, but if rect is a physics body you must use x
cx = rect.x
end
Runtime:addEventListener('enterFrame', enterFrame)
(end)
]]