src: add the hump libraries
This commit is contained in:
parent
ce082ea8cd
commit
fa72d7c733
7 changed files with 1105 additions and 0 deletions
216
sonic-boost.love/libs/hump/camera.lua
Normal file
216
sonic-boost.love/libs/hump/camera.lua
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2010-2015 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
|
||||||
|
local cos, sin = math.cos, math.sin
|
||||||
|
|
||||||
|
local camera = {}
|
||||||
|
camera.__index = camera
|
||||||
|
|
||||||
|
-- Movement interpolators (for camera locking/windowing)
|
||||||
|
camera.smooth = {}
|
||||||
|
|
||||||
|
function camera.smooth.none()
|
||||||
|
return function(dx,dy) return dx,dy end
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera.smooth.linear(speed)
|
||||||
|
assert(type(speed) == "number", "Invalid parameter: speed = "..tostring(speed))
|
||||||
|
return function(dx,dy, s)
|
||||||
|
-- normalize direction
|
||||||
|
local d = math.sqrt(dx*dx+dy*dy)
|
||||||
|
local dts = math.min((s or speed) * love.timer.getDelta(), d) -- prevent overshooting the goal
|
||||||
|
if d > 0 then
|
||||||
|
dx,dy = dx/d, dy/d
|
||||||
|
end
|
||||||
|
|
||||||
|
return dx*dts, dy*dts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera.smooth.damped(stiffness)
|
||||||
|
assert(type(stiffness) == "number", "Invalid parameter: stiffness = "..tostring(stiffness))
|
||||||
|
return function(dx,dy, s)
|
||||||
|
local dts = love.timer.getDelta() * (s or stiffness)
|
||||||
|
return dx*dts, dy*dts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function new(x,y, zoom, rot, smoother)
|
||||||
|
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
|
||||||
|
zoom = zoom or 1
|
||||||
|
rot = rot or 0
|
||||||
|
smoother = smoother or camera.smooth.none() -- for locking, see below
|
||||||
|
return setmetatable({x = x, y = y, scale = zoom, rot = rot, smoother = smoother}, camera)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:lookAt(x,y)
|
||||||
|
self.x, self.y = x, y
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:move(dx,dy)
|
||||||
|
self.x, self.y = self.x + dx, self.y + dy
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:position()
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:rotate(phi)
|
||||||
|
self.rot = self.rot + phi
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:rotateTo(phi)
|
||||||
|
self.rot = phi
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:zoom(mul)
|
||||||
|
self.scale = self.scale * mul
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:zoomTo(zoom)
|
||||||
|
self.scale = zoom
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:attach(x,y,w,h, noclip)
|
||||||
|
x,y = x or 0, y or 0
|
||||||
|
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
|
||||||
|
self._sx,self._sy,self._sw,self._sh = love.graphics.getScissor()
|
||||||
|
if not noclip then
|
||||||
|
love.graphics.setScissor(x,y,w,h)
|
||||||
|
end
|
||||||
|
|
||||||
|
local cx,cy = x+w/2, y+h/2
|
||||||
|
love.graphics.push()
|
||||||
|
love.graphics.translate(cx, cy)
|
||||||
|
love.graphics.scale(self.scale)
|
||||||
|
love.graphics.rotate(self.rot)
|
||||||
|
love.graphics.translate(-self.x, -self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:detach()
|
||||||
|
love.graphics.pop()
|
||||||
|
love.graphics.setScissor(self._sx,self._sy,self._sw,self._sh)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:draw(...)
|
||||||
|
local x,y,w,h,noclip,func
|
||||||
|
local nargs = select("#", ...)
|
||||||
|
if nargs == 1 then
|
||||||
|
func = ...
|
||||||
|
elseif nargs == 5 then
|
||||||
|
x,y,w,h,func = ...
|
||||||
|
elseif nargs == 6 then
|
||||||
|
x,y,w,h,noclip,func = ...
|
||||||
|
else
|
||||||
|
error("Invalid arguments to camera:draw()")
|
||||||
|
end
|
||||||
|
|
||||||
|
self:attach(x,y,w,h,noclip)
|
||||||
|
func()
|
||||||
|
self:detach()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- world coordinates to camera coordinates
|
||||||
|
function camera:cameraCoords(x,y, ox,oy,w,h)
|
||||||
|
ox, oy = ox or 0, oy or 0
|
||||||
|
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
|
||||||
|
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
|
||||||
|
local c,s = cos(self.rot), sin(self.rot)
|
||||||
|
x,y = x - self.x, y - self.y
|
||||||
|
x,y = c*x - s*y, s*x + c*y
|
||||||
|
return x*self.scale + w/2 + ox, y*self.scale + h/2 + oy
|
||||||
|
end
|
||||||
|
|
||||||
|
-- camera coordinates to world coordinates
|
||||||
|
function camera:worldCoords(x,y, ox,oy,w,h)
|
||||||
|
ox, oy = ox or 0, oy or 0
|
||||||
|
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
|
||||||
|
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
|
||||||
|
local c,s = cos(-self.rot), sin(-self.rot)
|
||||||
|
x,y = (x - w/2 - ox) / self.scale, (y - h/2 - oy) / self.scale
|
||||||
|
x,y = c*x - s*y, s*x + c*y
|
||||||
|
return x+self.x, y+self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:mousePosition(ox,oy,w,h)
|
||||||
|
local mx,my = love.mouse.getPosition()
|
||||||
|
return self:worldCoords(mx,my, ox,oy,w,h)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- camera scrolling utilities
|
||||||
|
function camera:lockX(x, smoother, ...)
|
||||||
|
local dx, dy = (smoother or self.smoother)(x - self.x, self.y, ...)
|
||||||
|
self.x = self.x + dx
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:lockY(y, smoother, ...)
|
||||||
|
local dx, dy = (smoother or self.smoother)(self.x, y - self.y, ...)
|
||||||
|
self.y = self.y + dy
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:lockPosition(x,y, smoother, ...)
|
||||||
|
return self:move((smoother or self.smoother)(x - self.x, y - self.y, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
function camera:lockWindow(x, y, x_min, x_max, y_min, y_max, smoother, ...)
|
||||||
|
-- figure out displacement in camera coordinates
|
||||||
|
x,y = self:cameraCoords(x,y)
|
||||||
|
local dx, dy = 0,0
|
||||||
|
if x < x_min then
|
||||||
|
dx = x - x_min
|
||||||
|
elseif x > x_max then
|
||||||
|
dx = x - x_max
|
||||||
|
end
|
||||||
|
if y < y_min then
|
||||||
|
dy = y - y_min
|
||||||
|
elseif y > y_max then
|
||||||
|
dy = y - y_max
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transform displacement to movement in world coordinates
|
||||||
|
local c,s = cos(-self.rot), sin(-self.rot)
|
||||||
|
dx,dy = (c*dx - s*dy) / self.scale, (s*dx + c*dy) / self.scale
|
||||||
|
|
||||||
|
-- move
|
||||||
|
self:move((smoother or self.smoother)(dx,dy,...))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({new = new, smooth = camera.smooth},
|
||||||
|
{__call = function(_, ...) return new(...) end})
|
98
sonic-boost.love/libs/hump/class.lua
Normal file
98
sonic-boost.love/libs/hump/class.lua
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2010-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local function include_helper(to, from, seen)
|
||||||
|
if from == nil then
|
||||||
|
return to
|
||||||
|
elseif type(from) ~= 'table' then
|
||||||
|
return from
|
||||||
|
elseif seen[from] then
|
||||||
|
return seen[from]
|
||||||
|
end
|
||||||
|
|
||||||
|
seen[from] = to
|
||||||
|
for k,v in pairs(from) do
|
||||||
|
k = include_helper({}, k, seen) -- keys might also be tables
|
||||||
|
if to[k] == nil then
|
||||||
|
to[k] = include_helper({}, v, seen)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return to
|
||||||
|
end
|
||||||
|
|
||||||
|
-- deeply copies `other' into `class'. keys in `other' that are already
|
||||||
|
-- defined in `class' are omitted
|
||||||
|
local function include(class, other)
|
||||||
|
return include_helper(class, other, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- returns a deep copy of `other'
|
||||||
|
local function clone(other)
|
||||||
|
return setmetatable(include({}, other), getmetatable(other))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function new(class)
|
||||||
|
-- mixins
|
||||||
|
class = class or {} -- class can be nil
|
||||||
|
local inc = class.__includes or {}
|
||||||
|
if getmetatable(inc) then inc = {inc} end
|
||||||
|
|
||||||
|
for _, other in ipairs(inc) do
|
||||||
|
if type(other) == "string" then
|
||||||
|
other = _G[other]
|
||||||
|
end
|
||||||
|
include(class, other)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- class implementation
|
||||||
|
class.__index = class
|
||||||
|
class.init = class.init or class[1] or function() end
|
||||||
|
class.include = class.include or include
|
||||||
|
class.clone = class.clone or clone
|
||||||
|
|
||||||
|
-- constructor call
|
||||||
|
return setmetatable(class, {__call = function(c, ...)
|
||||||
|
local o = setmetatable({}, c)
|
||||||
|
o:init(...)
|
||||||
|
return o
|
||||||
|
end})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
|
||||||
|
if class_commons ~= false and not common then
|
||||||
|
common = {}
|
||||||
|
function common.class(name, prototype, parent)
|
||||||
|
return new{__includes = {prototype, parent}}
|
||||||
|
end
|
||||||
|
function common.instance(class, ...)
|
||||||
|
return class(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({new = new, include = include, clone = clone},
|
||||||
|
{__call = function(_,...) return new(...) end})
|
108
sonic-boost.love/libs/hump/gamestate.lua
Normal file
108
sonic-boost.love/libs/hump/gamestate.lua
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2010-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local function __NULL__() end
|
||||||
|
|
||||||
|
-- default gamestate produces error on every callback
|
||||||
|
local state_init = setmetatable({leave = __NULL__},
|
||||||
|
{__index = function() error("Gamestate not initialized. Use Gamestate.switch()") end})
|
||||||
|
local stack = {state_init}
|
||||||
|
local initialized_states = setmetatable({}, {__mode = "k"})
|
||||||
|
local state_is_dirty = true
|
||||||
|
|
||||||
|
local GS = {}
|
||||||
|
function GS.new(t) return t or {} end -- constructor - deprecated!
|
||||||
|
|
||||||
|
local function change_state(stack_offset, to, ...)
|
||||||
|
local pre = stack[#stack]
|
||||||
|
|
||||||
|
-- initialize only on first call
|
||||||
|
;(initialized_states[to] or to.init or __NULL__)(to)
|
||||||
|
initialized_states[to] = __NULL__
|
||||||
|
|
||||||
|
stack[#stack+stack_offset] = to
|
||||||
|
state_is_dirty = true
|
||||||
|
return (to.enter or __NULL__)(to, pre, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.switch(to, ...)
|
||||||
|
assert(to, "Missing argument: Gamestate to switch to")
|
||||||
|
assert(to ~= GS, "Can't call switch with colon operator")
|
||||||
|
;(stack[#stack].leave or __NULL__)(stack[#stack])
|
||||||
|
return change_state(0, to, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.push(to, ...)
|
||||||
|
assert(to, "Missing argument: Gamestate to switch to")
|
||||||
|
assert(to ~= GS, "Can't call push with colon operator")
|
||||||
|
return change_state(1, to, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.pop(...)
|
||||||
|
assert(#stack > 1, "No more states to pop!")
|
||||||
|
local pre, to = stack[#stack], stack[#stack-1]
|
||||||
|
stack[#stack] = nil
|
||||||
|
;(pre.leave or __NULL__)(pre)
|
||||||
|
state_is_dirty = true
|
||||||
|
return (to.resume or __NULL__)(to, pre, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.current()
|
||||||
|
return stack[#stack]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fetch event callbacks from love.handlers
|
||||||
|
local all_callbacks = { 'draw', 'errhand', 'update' }
|
||||||
|
for k in pairs(love.handlers) do
|
||||||
|
all_callbacks[#all_callbacks+1] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
function GS.registerEvents(callbacks)
|
||||||
|
local registry = {}
|
||||||
|
callbacks = callbacks or all_callbacks
|
||||||
|
for _, f in ipairs(callbacks) do
|
||||||
|
registry[f] = love[f] or __NULL__
|
||||||
|
love[f] = function(...)
|
||||||
|
registry[f](...)
|
||||||
|
return GS[f](...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- forward any undefined functions
|
||||||
|
setmetatable(GS, {__index = function(_, func)
|
||||||
|
-- call function only if at least one 'update' was called beforehand
|
||||||
|
-- (see issue #46)
|
||||||
|
if not state_is_dirty or func == 'update' then
|
||||||
|
state_is_dirty = false
|
||||||
|
return function(...)
|
||||||
|
return (stack[#stack][func] or __NULL__)(stack[#stack], ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return __NULL__
|
||||||
|
end})
|
||||||
|
|
||||||
|
return GS
|
102
sonic-boost.love/libs/hump/signal.lua
Normal file
102
sonic-boost.love/libs/hump/signal.lua
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2012-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local Registry = {}
|
||||||
|
Registry.__index = function(self, key)
|
||||||
|
return Registry[key] or (function()
|
||||||
|
local t = {}
|
||||||
|
rawset(self, key, t)
|
||||||
|
return t
|
||||||
|
end)()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:register(s, f)
|
||||||
|
self[s][f] = f
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:emit(s, ...)
|
||||||
|
for f in pairs(self[s]) do
|
||||||
|
f(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:remove(s, ...)
|
||||||
|
local f = {...}
|
||||||
|
for i = 1,select('#', ...) do
|
||||||
|
self[s][f[i]] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:clear(...)
|
||||||
|
local s = {...}
|
||||||
|
for i = 1,select('#', ...) do
|
||||||
|
self[s[i]] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:emitPattern(p, ...)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self:emit(s, ...) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:registerPattern(p, f)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self:register(s, f) end
|
||||||
|
end
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:removePattern(p, ...)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self:remove(s, ...) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Registry:clearPattern(p)
|
||||||
|
for s in pairs(self) do
|
||||||
|
if s:match(p) then self[s] = {} end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- instancing
|
||||||
|
function Registry.new()
|
||||||
|
return setmetatable({}, Registry)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- default instance
|
||||||
|
local default = Registry.new()
|
||||||
|
|
||||||
|
-- module forwards calls to default instance
|
||||||
|
local module = {}
|
||||||
|
for k in pairs(Registry) do
|
||||||
|
if k ~= "__index" then
|
||||||
|
module[k] = function(...) return default[k](default, ...) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(module, {__call = Registry.new})
|
210
sonic-boost.love/libs/hump/timer.lua
Normal file
210
sonic-boost.love/libs/hump/timer.lua
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2010-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local Timer = {}
|
||||||
|
Timer.__index = Timer
|
||||||
|
|
||||||
|
local function _nothing_() end
|
||||||
|
|
||||||
|
function Timer:update(dt)
|
||||||
|
local to_remove = {}
|
||||||
|
|
||||||
|
for handle in pairs(self.functions) do
|
||||||
|
-- handle: {
|
||||||
|
-- time = <number>,
|
||||||
|
-- after = <function>,
|
||||||
|
-- during = <function>,
|
||||||
|
-- limit = <number>,
|
||||||
|
-- count = <number>,
|
||||||
|
-- }
|
||||||
|
|
||||||
|
handle.time = handle.time + dt
|
||||||
|
handle.during(dt, math.max(handle.limit - handle.time, 0))
|
||||||
|
|
||||||
|
while handle.time >= handle.limit and handle.count > 0 do
|
||||||
|
if handle.after(handle.after) == false then
|
||||||
|
handle.count = 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
handle.time = handle.time - handle.limit
|
||||||
|
handle.count = handle.count - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if handle.count == 0 then
|
||||||
|
table.insert(to_remove, handle)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #to_remove do
|
||||||
|
self.functions[to_remove[i]] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:during(delay, during, after)
|
||||||
|
local handle = { time = 0, during = during, after = after or _nothing_, limit = delay, count = 1 }
|
||||||
|
self.functions[handle] = true
|
||||||
|
return handle
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:after(delay, func)
|
||||||
|
return self:during(delay, _nothing_, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:every(delay, after, count)
|
||||||
|
local count = count or math.huge -- exploit below: math.huge - 1 = math.huge
|
||||||
|
local handle = { time = 0, during = _nothing_, after = after, limit = delay, count = count }
|
||||||
|
self.functions[handle] = true
|
||||||
|
return handle
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:cancel(handle)
|
||||||
|
self.functions[handle] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:clear()
|
||||||
|
self.functions = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Timer:script(f)
|
||||||
|
local co = coroutine.wrap(f)
|
||||||
|
co(function(t)
|
||||||
|
self:after(t, co)
|
||||||
|
coroutine.yield()
|
||||||
|
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
|
||||||
|
|
||||||
|
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 error('Unknown interpolation method: ' .. key)
|
||||||
|
end})
|
||||||
|
|
||||||
|
-- Timer instancing
|
||||||
|
function Timer.new()
|
||||||
|
return setmetatable({functions = {}, tween = Timer.tween}, Timer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- default instance
|
||||||
|
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
|
||||||
|
end
|
||||||
|
module.tween = setmetatable({}, {
|
||||||
|
__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})
|
172
sonic-boost.love/libs/hump/vector-light.lua
Normal file
172
sonic-boost.love/libs/hump/vector-light.lua
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2012-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
|
||||||
|
|
||||||
|
local function str(x,y)
|
||||||
|
return "("..tonumber(x)..","..tonumber(y)..")"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mul(s, x,y)
|
||||||
|
return s*x, s*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function div(s, x,y)
|
||||||
|
return x/s, y/s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add(x1,y1, x2,y2)
|
||||||
|
return x1+x2, y1+y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sub(x1,y1, x2,y2)
|
||||||
|
return x1-x2, y1-y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function permul(x1,y1, x2,y2)
|
||||||
|
return x1*x2, y1*y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dot(x1,y1, x2,y2)
|
||||||
|
return x1*x2 + y1*y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function det(x1,y1, x2,y2)
|
||||||
|
return x1*y2 - y1*x2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function eq(x1,y1, x2,y2)
|
||||||
|
return x1 == x2 and y1 == y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lt(x1,y1, x2,y2)
|
||||||
|
return x1 < x2 or (x1 == x2 and y1 < y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function le(x1,y1, x2,y2)
|
||||||
|
return x1 <= x2 and y1 <= y2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function len2(x,y)
|
||||||
|
return x*x + y*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function len(x,y)
|
||||||
|
return sqrt(x*x + y*y)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fromPolar(angle, radius)
|
||||||
|
return cos(angle)*radius, sin(angle)*radius
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toPolar(x, y)
|
||||||
|
return atan2(y,x), len(x,y)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dist2(x1,y1, x2,y2)
|
||||||
|
return len2(x1-x2, y1-y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dist(x1,y1, x2,y2)
|
||||||
|
return len(x1-x2, y1-y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function normalize(x,y)
|
||||||
|
local l = len(x,y)
|
||||||
|
if l > 0 then
|
||||||
|
return x/l, y/l
|
||||||
|
end
|
||||||
|
return x,y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rotate(phi, x,y)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
return c*x - s*y, s*x + c*y
|
||||||
|
end
|
||||||
|
|
||||||
|
local function perpendicular(x,y)
|
||||||
|
return -y, x
|
||||||
|
end
|
||||||
|
|
||||||
|
local function project(x,y, u,v)
|
||||||
|
local s = (x*u + y*v) / (u*u + v*v)
|
||||||
|
return s*u, s*v
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mirror(x,y, u,v)
|
||||||
|
local s = 2 * (x*u + y*v) / (u*u + v*v)
|
||||||
|
return s*u - x, s*v - y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ref.: http://blog.signalsondisplay.com/?p=336
|
||||||
|
local function trim(maxLen, x, y)
|
||||||
|
local s = maxLen * maxLen / len2(x, y)
|
||||||
|
s = s > 1 and 1 or math.sqrt(s)
|
||||||
|
return x * s, y * s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function angleTo(x,y, u,v)
|
||||||
|
if u and v then
|
||||||
|
return atan2(y, x) - atan2(v, u)
|
||||||
|
end
|
||||||
|
return atan2(y, x)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return {
|
||||||
|
str = str,
|
||||||
|
|
||||||
|
fromPolar = fromPolar,
|
||||||
|
toPolar = toPolar,
|
||||||
|
|
||||||
|
-- arithmetic
|
||||||
|
mul = mul,
|
||||||
|
div = div,
|
||||||
|
add = add,
|
||||||
|
sub = sub,
|
||||||
|
permul = permul,
|
||||||
|
dot = dot,
|
||||||
|
det = det,
|
||||||
|
cross = det,
|
||||||
|
|
||||||
|
-- relation
|
||||||
|
eq = eq,
|
||||||
|
lt = lt,
|
||||||
|
le = le,
|
||||||
|
|
||||||
|
-- misc operations
|
||||||
|
len2 = len2,
|
||||||
|
len = len,
|
||||||
|
dist2 = dist2,
|
||||||
|
dist = dist,
|
||||||
|
normalize = normalize,
|
||||||
|
rotate = rotate,
|
||||||
|
perpendicular = perpendicular,
|
||||||
|
project = project,
|
||||||
|
mirror = mirror,
|
||||||
|
trim = trim,
|
||||||
|
angleTo = angleTo,
|
||||||
|
}
|
199
sonic-boost.love/libs/hump/vector.lua
Normal file
199
sonic-boost.love/libs/hump/vector.lua
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2010-2013 Matthias Richter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name(s) of the above copyright holders
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local assert = assert
|
||||||
|
local sqrt, cos, sin, atan2 = math.sqrt, math.cos, math.sin, math.atan2
|
||||||
|
|
||||||
|
local vector = {}
|
||||||
|
vector.__index = vector
|
||||||
|
|
||||||
|
local function new(x,y)
|
||||||
|
return setmetatable({x = x or 0, y = y or 0}, vector)
|
||||||
|
end
|
||||||
|
local zero = new(0,0)
|
||||||
|
|
||||||
|
local function fromPolar(angle, radius)
|
||||||
|
return new(cos(angle) * radius, sin(angle) * radius)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isvector(v)
|
||||||
|
return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number'
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:clone()
|
||||||
|
return new(self.x, self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:unpack()
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:__tostring()
|
||||||
|
return "("..tonumber(self.x)..","..tonumber(self.y)..")"
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__unm(a)
|
||||||
|
return new(-a.x, -a.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__add(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "Add: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x+b.x, a.y+b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__sub(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "Sub: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x-b.x, a.y-b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__mul(a,b)
|
||||||
|
if type(a) == "number" then
|
||||||
|
return new(a*b.x, a*b.y)
|
||||||
|
elseif type(b) == "number" then
|
||||||
|
return new(b*a.x, b*a.y)
|
||||||
|
else
|
||||||
|
assert(isvector(a) and isvector(b), "Mul: wrong argument types (<vector> or <number> expected)")
|
||||||
|
return a.x*b.x + a.y*b.y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__div(a,b)
|
||||||
|
assert(isvector(a) and type(b) == "number", "wrong argument types (expected <vector> / <number>)")
|
||||||
|
return new(a.x / b, a.y / b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__eq(a,b)
|
||||||
|
return a.x == b.x and a.y == b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__lt(a,b)
|
||||||
|
return a.x < b.x or (a.x == b.x and a.y < b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.__le(a,b)
|
||||||
|
return a.x <= b.x and a.y <= b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.permul(a,b)
|
||||||
|
assert(isvector(a) and isvector(b), "permul: wrong argument types (<vector> expected)")
|
||||||
|
return new(a.x*b.x, a.y*b.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:toPolar()
|
||||||
|
return new(atan2(self.x, self.y), self:len())
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:len2()
|
||||||
|
return self.x * self.x + self.y * self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:len()
|
||||||
|
return sqrt(self.x * self.x + self.y * self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.dist(a, b)
|
||||||
|
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
|
||||||
|
local dx = a.x - b.x
|
||||||
|
local dy = a.y - b.y
|
||||||
|
return sqrt(dx * dx + dy * dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.dist2(a, b)
|
||||||
|
assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
|
||||||
|
local dx = a.x - b.x
|
||||||
|
local dy = a.y - b.y
|
||||||
|
return (dx * dx + dy * dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:normalizeInplace()
|
||||||
|
local l = self:len()
|
||||||
|
if l > 0 then
|
||||||
|
self.x, self.y = self.x / l, self.y / l
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:normalized()
|
||||||
|
return self:clone():normalizeInplace()
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:rotateInplace(phi)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:rotated(phi)
|
||||||
|
local c, s = cos(phi), sin(phi)
|
||||||
|
return new(c * self.x - s * self.y, s * self.x + c * self.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:perpendicular()
|
||||||
|
return new(-self.y, self.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:projectOn(v)
|
||||||
|
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
|
||||||
|
-- (self * v) * v / v:len2()
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:mirrorOn(v)
|
||||||
|
assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v))
|
||||||
|
-- 2 * self:projectOn(v) - self
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:cross(v)
|
||||||
|
assert(isvector(v), "cross: wrong argument types (<vector> expected)")
|
||||||
|
return self.x * v.y - self.y * v.x
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ref.: http://blog.signalsondisplay.com/?p=336
|
||||||
|
function vector:trimInplace(maxLen)
|
||||||
|
local s = maxLen * maxLen / self:len2()
|
||||||
|
s = (s > 1 and 1) or math.sqrt(s)
|
||||||
|
self.x, self.y = self.x * s, self.y * s
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:angleTo(other)
|
||||||
|
if other then
|
||||||
|
return atan2(self.y, self.x) - atan2(other.y, other.x)
|
||||||
|
end
|
||||||
|
return atan2(self.y, self.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector:trimmed(maxLen)
|
||||||
|
return self:clone():trimInplace(maxLen)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- the module
|
||||||
|
return setmetatable({new = new, fromPolar = fromPolar, isvector = isvector, zero = zero},
|
||||||
|
{__call = function(_, ...) return new(...) end})
|
Loading…
Reference in a new issue