aboutsummaryrefslogtreecommitdiff
path: root/libs/hump/timer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hump/timer.lua')
-rw-r--r--libs/hump/timer.lua245
1 files changed, 152 insertions, 93 deletions
diff --git a/libs/hump/timer.lua b/libs/hump/timer.lua
index 8315f68..54f06f0 100644
--- a/libs/hump/timer.lua
+++ b/libs/hump/timer.lua
@@ -28,6 +28,7 @@ local Timer = {}
Timer.__index = Timer
local function _nothing_() end
+local unpack = unpack or table.unpack
local function updateTimerHandle(handle, dt)
-- handle: {
@@ -90,7 +91,7 @@ function Timer:cancel(handle)
end
function Timer:clear()
- self.functions = {}
+ self.functions = {}
end
function Timer:script(f)
@@ -101,99 +102,157 @@ function Timer:script(f)
end)
end
-Timer.tween = setmetatable({
- -- helper functions
- out = function(f) -- 'rotates' a function
- return function(s, ...) return 1 - f(1-s, ...) end
- end,
- chain = function(f1, f2) -- concatenates two functions
- return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
- end,
-
- -- useful tweening functions
- linear = function(s) return s end,
- quad = function(s) return s*s end,
- cubic = function(s) return s*s*s end,
- quart = function(s) return s*s*s*s end,
- quint = function(s) return s*s*s*s*s end,
- sine = function(s) return 1-math.cos(s*math.pi/2) end,
- expo = function(s) return 2^(10*(s-1)) end,
- circ = function(s) return 1 - math.sqrt(1-s*s) end,
-
- back = function(s,bounciness)
- bounciness = bounciness or 1.70158
- return s*s*((bounciness+1)*s - bounciness)
- end,
-
- bounce = function(s) -- magic numbers ahead
- local a,b = 7.5625, 1/2.75
- return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375)
- end,
-
- elastic = function(s, amp, period)
- amp, period = amp and math.max(1, amp) or 1, period or .3
- return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
- end,
-}, {
-
--- register new tween
-__call = function(tween, self, len, subject, target, method, after, ...)
- -- recursively collects fields that are defined in both subject and target into a flat list
- local function tween_collect_payload(subject, target, out)
- for k,v in pairs(target) do
- local ref = subject[k]
- assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".')
- if type(v) == 'table' then
- tween_collect_payload(ref, v, out)
- else
- local ok, delta = pcall(function() return (v-ref)*1 end)
- assert(ok, 'Field "'..k..'" does not support arithmetic operations')
- out[#out+1] = {subject, k, delta}
- end
- end
- return out
- end
-
- method = tween[method or 'linear'] -- see __index
- local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
-
- local last_s = 0
- return self:during(len, function(dt)
- t = t + dt
- local s = method(math.min(1, t/len), unpack(args))
- local ds = s - last_s
- last_s = s
- for _, info in ipairs(payload) do
- local ref, key, delta = unpack(info)
- ref[key] = ref[key] + delta * ds
- end
- end, after)
-end,
-
--- fetches function and generated compositions for method `key`
-__index = function(tweens, key)
- if type(key) == 'function' then return key end
+local function func_tween(tween, self, len, subject, target, method, after,
+ setters_and_getters, ...)
+ -- recursively collects fields that are defined in both subject and target into a flat list
+ -- re-use of ref is confusing
+ local to_func_tween = {}
+ local function set_and_get(subject, k, v)
+ setters_and_getters = setters_and_getters or {}
+
+ local setter, getter
+ if setters_and_getters[k] then
+ setter, getter = unpack(setters_and_getters[k])
+ else
+ setter = subject['set'..k]
+ getter = subject['get'..k]
+ end
+ assert(setter and getter,
+ "key's value in subject is nil with no set/getter")
+
+ if to_func_tween[subject] == nil then
+ to_func_tween[subject] = {}
+ end
+
+ ref = {getter(subject)}
+ to_func_tween[subject][k] = {ref, setter}
+ if type(v) == 'number' or #ref == 1 then
+ v = {v}
+ end
+ return ref, v
+ end
+
+ local function tween_collect_payload(subject, target, out)
+ for k,v in pairs(target) do
+
+ -- this might not be the smoothest way to do this
+ local ref = subject[k]
+ if ref == nil then
+ ref, v = set_and_get(subject, k, v)
+ end
+ assert(type(v) == type(ref), 'Type mismatch in field "'..k..'". '
+ ..type(v)..' vs '.. type(ref))
+ if type(v) == 'table' then
+ tween_collect_payload(ref, v, out)
+ else
+ local ok, delta = pcall(function() return (v-ref)*1 end)
+ assert(ok, 'Field "'..k..'" does not support arithmetic operations')
+ out[#out+1] = {subject, k, delta}
+ end
+ end
+ return out
+ end
+
+ method = tween[method or 'linear'] -- see __index
+ local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
+
+ local last_s = 0
+ return self:during(len, function(dt)
+ t = t + dt
+ local s = method(math.min(1, t/len), unpack(args))
+ local ds = s - last_s
+ last_s = s
+ for _, info in ipairs(payload) do
+ local ref, key, delta = unpack(info)
+ ref[key] = ref[key] + delta * ds
+ end
+ for ref, t in pairs(to_func_tween) do
+ for key, value in pairs(t) do
+ local setter_args, setter = unpack(value)
+ if not pcall(function() setter(ref, unpack(setter_args)) end) then
+ setter(unpack(setter_args))
+ end
+ end
+ end
+ end, after)
+end
- assert(type(key) == 'string', 'Method must be function or string.')
- if rawget(tweens, key) then return rawget(tweens, key) end
+local function plain_tween(tween, self, len, subject, target, method, after, ...)
+ return func_tween(tween, self, len, subject, target, method, after, nil, ...)
+end
- local function construct(pattern, f)
- local method = rawget(tweens, key:match(pattern))
- if method then return f(method) end
- return nil
- end
- local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
- return construct('^in%-([^-]+)$', function(...) return ... end)
+local function def_tween(func)
+ return setmetatable(
+ {
+ -- helper functions
+ out = function(f) -- 'rotates' a function
+ return function(s, ...) return 1 - f(1-s, ...) end
+ end,
+ chain = function(f1, f2) -- concatenates two functions
+ return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
+ end,
+
+ -- useful tweening functions
+ linear = function(s) return s end,
+ quad = function(s) return s*s end,
+ cubic = function(s) return s*s*s end,
+ quart = function(s) return s*s*s*s end,
+ quint = function(s) return s*s*s*s*s end,
+ sine = function(s) return 1-math.cos(s*math.pi/2) end,
+ expo = function(s) return 2^(10*(s-1)) end,
+ circ = function(s) return 1 - math.sqrt(1-s*s) end,
+
+ back = function(s,bounciness)
+ bounciness = bounciness or 1.70158
+ return s*s*((bounciness+1)*s - bounciness)
+ end,
+
+ bounce = function(s) -- magic numbers ahead
+ local a,b = 7.5625, 1/2.75
+ return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375)
+ end,
+
+ elastic = function(s, amp, period)
+ amp, period = amp and math.max(1, amp) or 1, period or .3
+ return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
+ end,
+
+
+ }, {
+
+ -- register new tween
+ __call = func,
+
+ -- fetches function and generated compositions for method `key`
+ __index = function(tweens, key)
+ if type(key) == 'function' then return key end
+
+ assert(type(key) == 'string', 'Method must be function or string.')
+ if rawget(tweens, key) then return rawget(tweens, key) end
+
+ local function construct(pattern, f)
+ local method = rawget(tweens, key:match(pattern))
+ if method then return f(method) end
+ return nil
+ end
+
+ local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
+ return construct('^in%-([^-]+)$', function(...) return ... end)
or construct('^out%-([^-]+)$', out)
- or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
- or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
+ or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
+ or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
or error('Unknown interpolation method: ' .. key)
-end})
+ end})
+end
+
+
+Timer.tween = def_tween(plain_tween)
+Timer.func_tween = def_tween(func_tween)
-- Timer instancing
function Timer.new()
- return setmetatable({functions = {}, tween = Timer.tween}, Timer)
+ return setmetatable({functions = {}, tween = Timer.tween}, Timer)
end
-- default instance
@@ -202,14 +261,14 @@ local default = Timer.new()
-- module forwards calls to default instance
local module = {}
for k in pairs(Timer) do
- if k ~= "__index" then
- module[k] = function(...) return default[k](default, ...) end
- end
+ if k ~= "__index" then
+ module[k] = function(...) return default[k](default, ...) end
+ end
end
module.tween = setmetatable({}, {
- __index = Timer.tween,
- __newindex = function(k,v) Timer.tween[k] = v end,
- __call = function(t, ...) return default:tween(...) end,
+ __index = Timer.tween,
+ __newindex = function(k,v) Timer.tween[k] = v end,
+ __call = function(t, ...) return default:tween(...) end,
})
-return setmetatable(module, {__call = Timer.new})
+return setmetatable(module, {__call = Timer.new}) \ No newline at end of file