Changed indentation to the one true style

This commit is contained in:
Mikko Ahlroth 2014-10-17 22:47:28 +03:00
parent b914b5217c
commit b377c9805d
6 changed files with 353 additions and 353 deletions

View file

@ -31,87 +31,87 @@ local camera = {}
camera.__index = camera camera.__index = camera
local function new(x,y, zoom, rot) local function new(x,y, zoom, rot)
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2 x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
zoom = zoom or 1 zoom = zoom or 1
rot = rot or 0 rot = rot or 0
return setmetatable({x = x, y = y, scale = zoom, rot = rot}, camera) return setmetatable({x = x, y = y, scale = zoom, rot = rot}, camera)
end end
function camera:lookAt(x,y) function camera:lookAt(x,y)
self.x, self.y = x,y self.x, self.y = x,y
return self return self
end end
function camera:move(x,y) function camera:move(x,y)
self.x, self.y = self.x + x, self.y + y self.x, self.y = self.x + x, self.y + y
return self return self
end end
function camera:pos() function camera:pos()
return self.x, self.y return self.x, self.y
end end
function camera:rotate(phi) function camera:rotate(phi)
self.rot = self.rot + phi self.rot = self.rot + phi
return self return self
end end
function camera:rotateTo(phi) function camera:rotateTo(phi)
self.rot = phi self.rot = phi
return self return self
end end
function camera:zoom(mul) function camera:zoom(mul)
self.scale = self.scale * mul self.scale = self.scale * mul
return self return self
end end
function camera:zoomTo(zoom) function camera:zoomTo(zoom)
self.scale = zoom self.scale = zoom
return self return self
end end
function camera:attach() function camera:attach()
local cx,cy = love.graphics.getWidth()/(2*self.scale), love.graphics.getHeight()/(2*self.scale) local cx,cy = love.graphics.getWidth()/(2*self.scale), love.graphics.getHeight()/(2*self.scale)
love.graphics.push() love.graphics.push()
love.graphics.scale(self.scale) love.graphics.scale(self.scale)
love.graphics.translate(cx, cy) love.graphics.translate(cx, cy)
love.graphics.rotate(self.rot) love.graphics.rotate(self.rot)
love.graphics.translate(-self.x, -self.y) love.graphics.translate(-self.x, -self.y)
end end
function camera:detach() function camera:detach()
love.graphics.pop() love.graphics.pop()
end end
function camera:draw(func) function camera:draw(func)
self:attach() self:attach()
func() func()
self:detach() self:detach()
end end
function camera:cameraCoords(x,y) function camera:cameraCoords(x,y)
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center -- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
local w,h = love.graphics.getWidth(), love.graphics.getHeight() local w,h = love.graphics.getWidth(), love.graphics.getHeight()
local c,s = cos(self.rot), sin(self.rot) local c,s = cos(self.rot), sin(self.rot)
x,y = x - self.x, y - self.y x,y = x - self.x, y - self.y
x,y = c*x - s*y, s*x + c*y x,y = c*x - s*y, s*x + c*y
return x*self.scale + w/2, y*self.scale + h/2 return x*self.scale + w/2, y*self.scale + h/2
end end
function camera:worldCoords(x,y) function camera:worldCoords(x,y)
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y) -- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
local w,h = love.graphics.getWidth(), love.graphics.getHeight() local w,h = love.graphics.getWidth(), love.graphics.getHeight()
local c,s = cos(-self.rot), sin(-self.rot) local c,s = cos(-self.rot), sin(-self.rot)
x,y = (x - w/2) / self.scale, (y - h/2) / self.scale x,y = (x - w/2) / self.scale, (y - h/2) / self.scale
x,y = c*x - s*y, s*x + c*y x,y = c*x - s*y, s*x + c*y
return x+self.x, y+self.y return x+self.x, y+self.y
end end
function camera:mousepos() function camera:mousepos()
return self:worldCoords(love.mouse.getPosition()) return self:worldCoords(love.mouse.getPosition())
end end
-- the module -- the module
return setmetatable({new = new}, return setmetatable({new = new},
{__call = function(_, ...) return new(...) end}) {__call = function(_, ...) return new(...) end})

View file

@ -28,70 +28,70 @@ local function __NULL__() end
-- default gamestate produces error on every callback -- default gamestate produces error on every callback
local state_init = setmetatable({leave = __NULL__}, local state_init = setmetatable({leave = __NULL__},
{__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end}) {__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end})
local stack = {state_init} local stack = {state_init}
local GS = {} local GS = {}
function GS.new(t) return t or {} end -- constructor - deprecated! function GS.new(t) return t or {} end -- constructor - deprecated!
function GS.switch(to, ...) function GS.switch(to, ...)
assert(to, "Missing argument: Gamestate to switch to") assert(to, "Missing argument: Gamestate to switch to")
assert(to ~= GS, "Can't call switch with colon operator") assert(to ~= GS, "Can't call switch with colon operator")
local pre = stack[#stack] local pre = stack[#stack]
;(pre.leave or __NULL__)(pre) ;(pre.leave or __NULL__)(pre)
;(to.init or __NULL__)(to) ;(to.init or __NULL__)(to)
to.init = nil to.init = nil
stack[#stack] = to stack[#stack] = to
return (to.enter or __NULL__)(to, pre, ...) return (to.enter or __NULL__)(to, pre, ...)
end end
function GS.push(to, ...) function GS.push(to, ...)
assert(to, "Missing argument: Gamestate to switch to") assert(to, "Missing argument: Gamestate to switch to")
assert(to ~= GS, "Can't call push with colon operator") assert(to ~= GS, "Can't call push with colon operator")
local pre = stack[#stack] local pre = stack[#stack]
;(to.init or __NULL__)(to) ;(to.init or __NULL__)(to)
to.init = nil to.init = nil
stack[#stack+1] = to stack[#stack+1] = to
return (to.enter or __NULL__)(to, pre, ...) return (to.enter or __NULL__)(to, pre, ...)
end end
function GS.pop(...) function GS.pop(...)
assert(#stack > 1, "No more states to pop!") assert(#stack > 1, "No more states to pop!")
local pre = stack[#stack] local pre = stack[#stack]
stack[#stack] = nil stack[#stack] = nil
;(pre.leave or __NULL__)(pre) ;(pre.leave or __NULL__)(pre)
return (stack[#stack].resume or __NULL__)(pre, ...) return (stack[#stack].resume or __NULL__)(pre, ...)
end end
function GS.current() function GS.current()
return stack[#stack] return stack[#stack]
end end
local all_callbacks = { local all_callbacks = {
'draw', 'errhand', 'focus', 'keypressed', 'keyreleased', 'mousefocus', 'draw', 'errhand', 'focus', 'keypressed', 'keyreleased', 'mousefocus',
'mousepressed', 'mousereleased', 'quit', 'resize', 'textinput', 'mousepressed', 'mousereleased', 'quit', 'resize', 'textinput',
'threaderror', 'update', 'visible', 'gamepadaxis', 'gamepadpressed', 'threaderror', 'update', 'visible', 'gamepadaxis', 'gamepadpressed',
'gamepadreleased', 'joystickadded', 'joystickaxis', 'joystickhat', 'gamepadreleased', 'joystickadded', 'joystickaxis', 'joystickhat',
'joystickpressed', 'joystickreleased', 'joystickremoved' 'joystickpressed', 'joystickreleased', 'joystickremoved'
} }
function GS.registerEvents(callbacks) function GS.registerEvents(callbacks)
local registry = {} local registry = {}
callbacks = callbacks or all_callbacks callbacks = callbacks or all_callbacks
for _, f in ipairs(callbacks) do for _, f in ipairs(callbacks) do
registry[f] = love[f] or __NULL__ registry[f] = love[f] or __NULL__
love[f] = function(...) love[f] = function(...)
registry[f](...) registry[f](...)
return GS[f](...) return GS[f](...)
end end
end end
end end
-- forward any undefined functions -- forward any undefined functions
setmetatable(GS, {__index = function(_, func) setmetatable(GS, {__index = function(_, func)
return function(...) return function(...)
return (stack[#stack][func] or __NULL__)(stack[#stack], ...) return (stack[#stack][func] or __NULL__)(stack[#stack], ...)
end end
end}) end})
return GS return GS

View file

@ -26,68 +26,68 @@ THE SOFTWARE.
local Registry = {} local Registry = {}
Registry.__index = function(self, key) Registry.__index = function(self, key)
return Registry[key] or (function() return Registry[key] or (function()
local t = {} local t = {}
rawset(self, key, t) rawset(self, key, t)
return t return t
end)() end)()
end end
function Registry:register(s, f) function Registry:register(s, f)
self[s][f] = f self[s][f] = f
return f return f
end end
function Registry:emit(s, ...) function Registry:emit(s, ...)
for f in pairs(self[s]) do for f in pairs(self[s]) do
f(...) f(...)
end end
end end
function Registry:remove(s, ...) function Registry:remove(s, ...)
local f = {...} local f = {...}
for i = 1,select('#', ...) do for i = 1,select('#', ...) do
self[s][f[i]] = nil self[s][f[i]] = nil
end end
end end
function Registry:clear(...) function Registry:clear(...)
local s = {...} local s = {...}
for i = 1,select('#', ...) do for i = 1,select('#', ...) do
self[s[i]] = {} self[s[i]] = {}
end end
end end
function Registry:emit_pattern(p, ...) function Registry:emit_pattern(p, ...)
for s in pairs(self) do for s in pairs(self) do
if s:match(p) then self:emit(s, ...) end if s:match(p) then self:emit(s, ...) end
end end
end end
function Registry:remove_pattern(p, ...) function Registry:remove_pattern(p, ...)
for s in pairs(self) do for s in pairs(self) do
if s:match(p) then self:remove(s, ...) end if s:match(p) then self:remove(s, ...) end
end end
end end
function Registry:clear_pattern(p) function Registry:clear_pattern(p)
for s in pairs(self) do for s in pairs(self) do
if s:match(p) then self[s] = {} end if s:match(p) then self[s] = {} end
end end
end end
local function new() local function new()
return setmetatable({}, Registry) return setmetatable({}, Registry)
end end
local default = new() local default = new()
return setmetatable({ return setmetatable({
new = new, new = new,
register = function(...) return default:register(...) end, register = function(...) return default:register(...) end,
emit = function(...) default:emit(...) end, emit = function(...) default:emit(...) end,
remove = function(...) default:remove(...) end, remove = function(...) default:remove(...) end,
clear = function(...) default:clear(...) end, clear = function(...) default:clear(...) end,
emit_pattern = function(...) default:emit_pattern(...) end, emit_pattern = function(...) default:emit_pattern(...) end,
remove_pattern = function(...) default:remove_pattern(...) end, remove_pattern = function(...) default:remove_pattern(...) end,
clear_pattern = function(...) default:clear_pattern(...) end, clear_pattern = function(...) default:clear_pattern(...) end,
}, {__call = new}) }, {__call = new})

View file

@ -30,144 +30,144 @@ Timer.__index = Timer
local function _nothing_() end local function _nothing_() end
local function new() local function new()
return setmetatable({functions = {}, tween = Timer.tween}, Timer) return setmetatable({functions = {}, tween = Timer.tween}, Timer)
end end
function Timer:update(dt) function Timer:update(dt)
local to_remove = {} local to_remove = {}
for handle, delay in pairs(self.functions) do for handle, delay in pairs(self.functions) do
delay = delay - dt delay = delay - dt
if delay <= 0 then if delay <= 0 then
to_remove[#to_remove+1] = handle to_remove[#to_remove+1] = handle
end end
self.functions[handle] = delay self.functions[handle] = delay
handle.func(dt, delay) handle.func(dt, delay)
end end
for _,handle in ipairs(to_remove) do for _,handle in ipairs(to_remove) do
self.functions[handle] = nil self.functions[handle] = nil
handle.after(handle.after) handle.after(handle.after)
end end
end end
function Timer:do_for(delay, func, after) function Timer:do_for(delay, func, after)
local handle = {func = func, after = after or _nothing_} local handle = {func = func, after = after or _nothing_}
self.functions[handle] = delay self.functions[handle] = delay
return handle return handle
end end
function Timer:add(delay, func) function Timer:add(delay, func)
return self:do_for(delay, _nothing_, func) return self:do_for(delay, _nothing_, func)
end end
function Timer:addPeriodic(delay, func, count) function Timer:addPeriodic(delay, func, count)
local count, handle = count or math.huge -- exploit below: math.huge - 1 = math.huge local count, handle = count or math.huge -- exploit below: math.huge - 1 = math.huge
handle = self:add(delay, function(f) handle = self:add(delay, function(f)
if func(func) == false then return end if func(func) == false then return end
count = count - 1 count = count - 1
if count > 0 then if count > 0 then
self.functions[handle] = delay self.functions[handle] = delay
end end
end) end)
return handle return handle
end end
function Timer:cancel(handle) function Timer:cancel(handle)
self.functions[handle] = nil self.functions[handle] = nil
end end
function Timer:clear() function Timer:clear()
self.functions = {} self.functions = {}
end end
Timer.tween = setmetatable({ Timer.tween = setmetatable({
-- helper functions -- helper functions
out = function(f) -- 'rotates' a function out = function(f) -- 'rotates' a function
return function(s, ...) return 1 - f(1-s, ...) end return function(s, ...) return 1 - f(1-s, ...) end
end, end,
chain = function(f1, f2) -- concatenates two functions 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 return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
end, end,
-- useful tweening functions -- useful tweening functions
linear = function(s) return s end, linear = function(s) return s end,
quad = function(s) return s*s end, quad = function(s) return s*s end,
cubic = function(s) return s*s*s end, cubic = function(s) return s*s*s end,
quart = function(s) return s*s*s*s end, quart = function(s) return s*s*s*s end,
quint = function(s) return s*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, sine = function(s) return 1-math.cos(s*math.pi/2) end,
expo = function(s) return 2^(10*(s-1)) end, expo = function(s) return 2^(10*(s-1)) end,
circ = function(s) return 1 - math.sqrt(1-s*s) end, circ = function(s) return 1 - math.sqrt(1-s*s) end,
back = function(s,bounciness) back = function(s,bounciness)
bounciness = bounciness or 1.70158 bounciness = bounciness or 1.70158
return s*s*((bounciness+1)*s - bounciness) return s*s*((bounciness+1)*s - bounciness)
end, end,
bounce = function(s) -- magic numbers ahead bounce = function(s) -- magic numbers ahead
local a,b = 7.5625, 1/2.75 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) 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, end,
elastic = function(s, amp, period) elastic = function(s, amp, period)
amp, period = amp and math.max(1, amp) or 1, period or .3 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)) return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
end, end,
}, { }, {
-- register new tween -- register new tween
__call = function(tween, self, len, subject, target, method, after, ...) __call = function(tween, self, len, subject, target, method, after, ...)
-- recursively collects fields that are defined in both subject and target into a flat list -- recursively collects fields that are defined in both subject and target into a flat list
local function tween_collect_payload(subject, target, out) local function tween_collect_payload(subject, target, out)
for k,v in pairs(target) do for k,v in pairs(target) do
local ref = subject[k] local ref = subject[k]
assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".') assert(type(v) == type(ref), 'Type mismatch in field "'..k..'".')
if type(v) == 'table' then if type(v) == 'table' then
tween_collect_payload(ref, v, out) tween_collect_payload(ref, v, out)
else else
local ok, delta = pcall(function() return (v-ref)*1 end) local ok, delta = pcall(function() return (v-ref)*1 end)
assert(ok, 'Field "'..k..'" does not support arithmetic operations') assert(ok, 'Field "'..k..'" does not support arithmetic operations')
out[#out+1] = {subject, k, delta} out[#out+1] = {subject, k, delta}
end end
end end
return out return out
end end
method = tween[method or 'linear'] -- see __index method = tween[method or 'linear'] -- see __index
local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...} local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
local last_s = 0 local last_s = 0
return self:do_for(len, function(dt) return self:do_for(len, function(dt)
t = t + dt t = t + dt
local s = method(math.min(1, t/len), unpack(args)) local s = method(math.min(1, t/len), unpack(args))
local ds = s - last_s local ds = s - last_s
last_s = s last_s = s
for _, info in ipairs(payload) do for _, info in ipairs(payload) do
local ref, key, delta = unpack(info) local ref, key, delta = unpack(info)
ref[key] = ref[key] + delta * ds ref[key] = ref[key] + delta * ds
end end
end, after) end, after)
end, end,
-- fetches function and generated compositions for method `key` -- fetches function and generated compositions for method `key`
__index = function(tweens, key) __index = function(tweens, key)
if type(key) == 'function' then return key end if type(key) == 'function' then return key end
assert(type(key) == 'string', 'Method must be function or string.') assert(type(key) == 'string', 'Method must be function or string.')
if rawget(tweens, key) then return rawget(tweens, key) end if rawget(tweens, key) then return rawget(tweens, key) end
local function construct(pattern, f) local function construct(pattern, f)
local method = rawget(tweens, key:match(pattern)) local method = rawget(tweens, key:match(pattern))
if method then return f(method) end if method then return f(method) end
return nil return nil
end end
local out, chain = rawget(tweens,'out'), rawget(tweens,'chain') local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
return construct('^in%-([^-]+)$', function(...) return ... end) return construct('^in%-([^-]+)$', function(...) return ... end)
or construct('^out%-([^-]+)$', out) or construct('^out%-([^-]+)$', out)
or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(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 construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
or error('Unknown interpolation method: ' .. key) or error('Unknown interpolation method: ' .. key)
end}) end})
-- default timer -- default timer
@ -175,16 +175,16 @@ local default = new()
-- the module -- the module
return setmetatable({ return setmetatable({
new = new, new = new,
update = function(...) return default:update(...) end, update = function(...) return default:update(...) end,
do_for = function(...) return default:do_for(...) end, do_for = function(...) return default:do_for(...) end,
add = function(...) return default:add(...) end, add = function(...) return default:add(...) end,
addPeriodic = function(...) return default:addPeriodic(...) end, addPeriodic = function(...) return default:addPeriodic(...) end,
cancel = function(...) return default:cancel(...) end, cancel = function(...) return default:cancel(...) end,
clear = function(...) return default:clear(...) end, clear = function(...) return default:clear(...) end,
tween = setmetatable({}, { tween = setmetatable({}, {
__index = Timer.tween, __index = Timer.tween,
__newindex = function(_,k,v) Timer.tween[k] = v end, __newindex = function(_,k,v) Timer.tween[k] = v end,
__call = function(t,...) return default:tween(...) end, __call = function(t,...) return default:tween(...) end,
}) })
}, {__call = new}) }, {__call = new})

View file

@ -27,135 +27,135 @@ THE SOFTWARE.
local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2 local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
local function str(x,y) local function str(x,y)
return "("..tonumber(x)..","..tonumber(y)..")" return "("..tonumber(x)..","..tonumber(y)..")"
end end
local function mul(s, x,y) local function mul(s, x,y)
return s*x, s*y return s*x, s*y
end end
local function div(s, x,y) local function div(s, x,y)
return x/s, y/s return x/s, y/s
end end
local function add(x1,y1, x2,y2) local function add(x1,y1, x2,y2)
return x1+x2, y1+y2 return x1+x2, y1+y2
end end
local function sub(x1,y1, x2,y2) local function sub(x1,y1, x2,y2)
return x1-x2, y1-y2 return x1-x2, y1-y2
end end
local function permul(x1,y1, x2,y2) local function permul(x1,y1, x2,y2)
return x1*x2, y1*y2 return x1*x2, y1*y2
end end
local function dot(x1,y1, x2,y2) local function dot(x1,y1, x2,y2)
return x1*x2 + y1*y2 return x1*x2 + y1*y2
end end
local function det(x1,y1, x2,y2) local function det(x1,y1, x2,y2)
return x1*y2 - y1*x2 return x1*y2 - y1*x2
end end
local function eq(x1,y1, x2,y2) local function eq(x1,y1, x2,y2)
return x1 == x2 and y1 == y2 return x1 == x2 and y1 == y2
end end
local function lt(x1,y1, x2,y2) local function lt(x1,y1, x2,y2)
return x1 < x2 or (x1 == x2 and y1 < y2) return x1 < x2 or (x1 == x2 and y1 < y2)
end end
local function le(x1,y1, x2,y2) local function le(x1,y1, x2,y2)
return x1 <= x2 and y1 <= y2 return x1 <= x2 and y1 <= y2
end end
local function len2(x,y) local function len2(x,y)
return x*x + y*y return x*x + y*y
end end
local function len(x,y) local function len(x,y)
return sqrt(x*x + y*y) return sqrt(x*x + y*y)
end end
local function dist2(x1,y1, x2,y2) local function dist2(x1,y1, x2,y2)
return len2(x1-x2, y1-y2) return len2(x1-x2, y1-y2)
end end
local function dist(x1,y1, x2,y2) local function dist(x1,y1, x2,y2)
return len(x1-x2, y1-y2) return len(x1-x2, y1-y2)
end end
local function normalize(x,y) local function normalize(x,y)
local l = len(x,y) local l = len(x,y)
if l > 0 then if l > 0 then
return x/l, y/l return x/l, y/l
end end
return x,y return x,y
end end
local function rotate(phi, x,y) local function rotate(phi, x,y)
local c, s = cos(phi), sin(phi) local c, s = cos(phi), sin(phi)
return c*x - s*y, s*x + c*y return c*x - s*y, s*x + c*y
end end
local function perpendicular(x,y) local function perpendicular(x,y)
return -y, x return -y, x
end end
local function project(x,y, u,v) local function project(x,y, u,v)
local s = (x*u + y*v) / (u*u + v*v) local s = (x*u + y*v) / (u*u + v*v)
return s*u, s*v return s*u, s*v
end end
local function mirror(x,y, u,v) local function mirror(x,y, u,v)
local s = 2 * (x*u + y*v) / (u*u + v*v) local s = 2 * (x*u + y*v) / (u*u + v*v)
return s*u - x, s*v - y return s*u - x, s*v - y
end end
-- ref.: http://blog.signalsondisplay.com/?p=336 -- ref.: http://blog.signalsondisplay.com/?p=336
local function trim(maxLen, x, y) local function trim(maxLen, x, y)
local s = maxLen * maxLen / len2(x, y) local s = maxLen * maxLen / len2(x, y)
s = s > 1 and 1 or math.sqrt(s) s = s > 1 and 1 or math.sqrt(s)
return x * s, y * s return x * s, y * s
end end
local function angleTo(x,y, u,v) local function angleTo(x,y, u,v)
if u and v then if u and v then
return atan2(y, x) - atan2(v, u) return atan2(y, x) - atan2(v, u)
end end
return atan2(y, x) return atan2(y, x)
end end
-- the module -- the module
return { return {
str = str, str = str,
-- arithmetic -- arithmetic
mul = mul, mul = mul,
div = div, div = div,
add = add, add = add,
sub = sub, sub = sub,
permul = permul, permul = permul,
dot = dot, dot = dot,
det = det, det = det,
cross = det, cross = det,
-- relation -- relation
eq = eq, eq = eq,
lt = lt, lt = lt,
le = le, le = le,
-- misc operations -- misc operations
len2 = len2, len2 = len2,
len = len, len = len,
dist2 = dist2, dist2 = dist2,
dist = dist, dist = dist,
normalize = normalize, normalize = normalize,
rotate = rotate, rotate = rotate,
perpendicular = perpendicular, perpendicular = perpendicular,
project = project, project = project,
mirror = mirror, mirror = mirror,
trim = trim, trim = trim,
angleTo = angleTo, angleTo = angleTo,
} }

View file

@ -31,158 +31,158 @@ local vector = {}
vector.__index = vector vector.__index = vector
local function new(x,y) local function new(x,y)
return setmetatable({x = x or 0, y = y or 0}, vector) return setmetatable({x = x or 0, y = y or 0}, vector)
end end
local zero = new(0,0) local zero = new(0,0)
local function isvector(v) local function isvector(v)
return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number' return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number'
end end
function vector:clone() function vector:clone()
return new(self.x, self.y) return new(self.x, self.y)
end end
function vector:unpack() function vector:unpack()
return self.x, self.y return self.x, self.y
end end
function vector:__tostring() function vector:__tostring()
return "("..tonumber(self.x)..","..tonumber(self.y)..")" return "("..tonumber(self.x)..","..tonumber(self.y)..")"
end end
function vector.__unm(a) function vector.__unm(a)
return new(-a.x, -a.y) return new(-a.x, -a.y)
end end
function vector.__add(a,b) function vector.__add(a,b)
assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)") assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
return new(a.x+b.x, a.y+b.y) return new(a.x+b.x, a.y+b.y)
end end
function vector.__sub(a,b) function vector.__sub(a,b)
assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)") assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
return new(a.x-b.x, a.y-b.y) return new(a.x-b.x, a.y-b.y)
end end
function vector.__mul(a,b) function vector.__mul(a,b)
if type(a) == "number" then if type(a) == "number" then
return new(a*b.x, a*b.y) return new(a*b.x, a*b.y)
elseif type(b) == "number" then elseif type(b) == "number" then
return new(b*a.x, b*a.y) return new(b*a.x, b*a.y)
else else
assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)") assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
return a.x*b.x + a.y*b.y return a.x*b.x + a.y*b.y
end end
end end
function vector.__div(a,b) function vector.__div(a,b)
assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)") assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
return new(a.x / b, a.y / b) return new(a.x / b, a.y / b)
end end
function vector.__eq(a,b) function vector.__eq(a,b)
return a.x == b.x and a.y == b.y return a.x == b.x and a.y == b.y
end end
function vector.__lt(a,b) function vector.__lt(a,b)
return a.x < b.x or (a.x == b.x and a.y < b.y) return a.x < b.x or (a.x == b.x and a.y < b.y)
end end
function vector.__le(a,b) function vector.__le(a,b)
return a.x <= b.x and a.y <= b.y return a.x <= b.x and a.y <= b.y
end end
function vector.permul(a,b) function vector.permul(a,b)
assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)") assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
return new(a.x*b.x, a.y*b.y) return new(a.x*b.x, a.y*b.y)
end end
function vector:len2() function vector:len2()
return self.x * self.x + self.y * self.y return self.x * self.x + self.y * self.y
end end
function vector:len() function vector:len()
return sqrt(self.x * self.x + self.y * self.y) return sqrt(self.x * self.x + self.y * self.y)
end end
function vector.dist(a, b) function vector.dist(a, b)
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)") assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
local dx = a.x - b.x local dx = a.x - b.x
local dy = a.y - b.y local dy = a.y - b.y
return sqrt(dx * dx + dy * dy) return sqrt(dx * dx + dy * dy)
end end
function vector.dist2(a, b) function vector.dist2(a, b)
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)") assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
local dx = a.x - b.x local dx = a.x - b.x
local dy = a.y - b.y local dy = a.y - b.y
return (dx * dx + dy * dy) return (dx * dx + dy * dy)
end end
function vector:normalize_inplace() function vector:normalize_inplace()
local l = self:len() local l = self:len()
if l > 0 then if l > 0 then
self.x, self.y = self.x / l, self.y / l self.x, self.y = self.x / l, self.y / l
end end
return self return self
end end
function vector:normalized() function vector:normalized()
return self:clone():normalize_inplace() return self:clone():normalize_inplace()
end end
function vector:rotate_inplace(phi) function vector:rotate_inplace(phi)
local c, s = cos(phi), sin(phi) local c, s = cos(phi), sin(phi)
self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
return self return self
end end
function vector:rotated(phi) function vector:rotated(phi)
local c, s = cos(phi), sin(phi) local c, s = cos(phi), sin(phi)
return new(c * self.x - s * self.y, s * self.x + c * self.y) return new(c * self.x - s * self.y, s * self.x + c * self.y)
end end
function vector:perpendicular() function vector:perpendicular()
return new(-self.y, self.x) return new(-self.y, self.x)
end end
function vector:projectOn(v) function vector:projectOn(v)
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v)) assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
-- (self * v) * v / v:len2() -- (self * v) * v / v:len2()
local s = (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y) local s = (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
return new(s * v.x, s * v.y) return new(s * v.x, s * v.y)
end end
function vector:mirrorOn(v) function vector:mirrorOn(v)
assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v)) assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v))
-- 2 * self:projectOn(v) - self -- 2 * self:projectOn(v) - self
local s = 2 * (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y) local s = 2 * (self.x * v.x + self.y * v.y) / (v.x * v.x + v.y * v.y)
return new(s * v.x - self.x, s * v.y - self.y) return new(s * v.x - self.x, s * v.y - self.y)
end end
function vector:cross(v) function vector:cross(v)
assert(isvector(v), "cross: wrong argument types (<vector> expected)") assert(isvector(v), "cross: wrong argument types (<vector> expected)")
return self.x * v.y - self.y * v.x return self.x * v.y - self.y * v.x
end end
-- ref.: http://blog.signalsondisplay.com/?p=336 -- ref.: http://blog.signalsondisplay.com/?p=336
function vector:trim_inplace(maxLen) function vector:trim_inplace(maxLen)
local s = maxLen * maxLen / self:len2() local s = maxLen * maxLen / self:len2()
s = (s > 1 and 1) or math.sqrt(s) s = (s > 1 and 1) or math.sqrt(s)
self.x, self.y = self.x * s, self.y * s self.x, self.y = self.x * s, self.y * s
return self return self
end end
function vector:angleTo(other) function vector:angleTo(other)
if other then if other then
return atan2(self.y, self.x) - atan2(other.y, other.x) return atan2(self.y, self.x) - atan2(other.y, other.x)
end end
return atan2(self.y, self.x) return atan2(self.y, self.x)
end end
function vector:trimmed(maxLen) function vector:trimmed(maxLen)
return self:clone():trim_inplace(maxLen) return self:clone():trim_inplace(maxLen)
end end