I'd like to repeat what ivan said as it's not only not very efficient, it also wastes a lot of memory.
The problem with the code, as it is, is not primarily the closure but whenever you create a vector instance (which, at the moment, you do in almost all core operations) you create a new table and then add all the helper methods as key/value pairs to this very table. I.e. each of your vectors not only contains the x/y but also "dot", "normalized" etc. etc. as hashed keys with references to those functions.
This is very slow as it has to put those keys into the hashtable each time and of course it does cost a lot of memory. I did a quick test and it roughly requires 10x the memory for each created vector compared to a vector with only x/y.
The simplest optimization is to add all those vec:dot, vec:normalized etc. methods into the vecMt and then add the __index fallback to itself, so you can call those in the same convenient way as you can at the moment.
Like so
Code: Select all
local vector = {_VERSION = "v0.6.0", _TYPE = "module", _NAME = "vector"}
local vecMt = {
__tostring = function(self)
return self:string()
end,
__add = function(a, b)
if type(a) == "number" then return vector(a + b.x, a + b.y) end
if type(b) == "number" then return vector(a.x + b, a.y + b) end
return vector(a.x + b.x, a.y + b.y)
end,
__sub = function(a, b)
if type(a) == "number" then return vector(a - b.x, a - b.y) end
if type(b) == "number" then return vector(a.x - b, a.y - b) end
return vector(a.x - b.x, a.y - b.y)
end,
__mul = function(a, b)
if type(a) == "number" then return vector(a * b.x, a * b.y) end
if type(b) == "number" then return vector(a.x * b, a.y * b) end
return vector(a.x * b.x, a.y * b.y)
end,
__div = function(a, b)
if type(a) == "number" then return vector(a / b.x, a / b.y) end
if type(b) == "number" then return vector(a.x / b, a.y / b) end
return vector(a.x / b.x, a.y / b.y)
end,
__unm = function(t)
return vector(-t.x, -t.y)
end,
__eq = function(a, b)
return a.x == b.x and a.y == b.y
end,
__pow = function(vec, value)
return vector(vec.x ^ value, vec.y ^ value)
end,
__concat = function(a, b)
if type(a) == "string" then return a .. b:string() end
if type(b) == "string" then return a:string() .. b end
return a:string() .. b:string()
end,
string = function(self)
return "vector(" .. self.x .. ", " .. self.y ..")"
end,
angle = function(self) -- Radians
return math.atan2(self.y, self.x)
end,
normalized = function(self)
local m = (self.x^2 + self.y^2)^0.5 --magnitude
if self.x/m ~= self.x/m then self.x = 0 else self.x = self.x/m end
if self.y/m ~= self.y/m then self.y = 0 else self.y = self.y/m end
end,
distanceSquaredTo = function(self, v)
local x1, y1 = self.x, self.y
local x2, y2 = v.x, v.y
return (x2 - x1)^2 + (y2 - y1)^2
end,
distanceTo = function(self, v)
return self:distanceSquaredTo(v)^0.5
end,
distanceSquared = function(self )
return self.x^2 + self.y^2
end,
distance = function(self)
return (self:distanceSquared())^0.5
end,
dot = function(self, v)
return self.x * v.x + self.y * v.y
end,
perpDot = function(self, v)
return self.x * v.x - self.y * v.y
end,
abs = function(self)
self.x, self.y = math.abs(self.x), math.abs(self.y)
end,
round = function(self, dec)
dec = dec or 0
local mult = 10^(dec)
local nx, ny
if self.x >= 0 then nx = math.floor(self.x * mult + 0.5) / mult
else nx = math.ceil(self.x * mult - 0.5) / mult end
if self.y >= 0 then ny = math.floor(self.y * mult + 0.5) / mult
else ny = math.ceil(self.y * mult - 0.5) / mult end
self.x, self.y = nx, ny
end,
toPolar = function(self, angle, len)
len = len or 1
self.x, self.y = math.cos(angle) * len, math.sin(angle) * len
end,
rotated = function(self, phi)
self.x = math.cos(phi) * self.x - math.sin(phi) * self.y
self.y = math.sin(phi) * self.x + math.cos(phi) * self.y
end,
cross = function(self, v)
return self.x * v.y - self.y * v.x
end,
perpendicular = function(self)
local px, py = self.x, self.y
self.x = -py
self.y = px
end,
lerpTo = function(self, v, t)
local i = 1 - t
self.x, self.y = self.x * i + v.x * t, self.y * i + v.y * t
end,
unpack = function(self)
return self.x, self.y
end,
}
vecMt.__index = vecMt
local mt = { -- Metatable of vector
__call = function(_, x, y)
local vec = {x = x or 0, y = y or 0}
setmetatable(vec, vecMt)
return vec
end
}
setmetatable(vector, mt)
-- CONSTANTS
vector.DOWN = vector(0, 1)
vector.UP = vector(0, -1)
vector.LEFT = vector(-1, 0)
vector.RIGHT = vector(1, 0)
vector.ZERO = vector(0, 0)
vector.ONE = vector(1, 1)
return vector