forked from redo/BlockLua
242 lines
6.7 KiB
Lua
242 lines
6.7 KiB
Lua
-- Vector math class with operators
|
|
local vector_meta
|
|
local vector_new
|
|
local function vector_check(v, n, name, argn)
|
|
if not v.__is_vector then
|
|
error('vector ' .. name .. ': argument #' .. (argn or 1)
|
|
.. ': expected vector, got ' .. type(v), n + 1)
|
|
end
|
|
end
|
|
local function vector_checksamelen(v1, v2, name)
|
|
vector_check(v1, 3, name, 1)
|
|
vector_check(v2, 3, name, 2)
|
|
if #v1 ~= #v2 then
|
|
error('vector ' .. name .. ': vector lengths do not match (lengths are '
|
|
.. #v1 .. ' and ' .. #v2 .. ')', 3)
|
|
end
|
|
return #v1
|
|
end
|
|
local function vector_checklen(v1, v2, name, len)
|
|
vector_check(v1, 3, name, 1)
|
|
vector_check(v2, 3, name, 2)
|
|
if #v1 ~= len or #v2 ~= len then
|
|
error('vector ' .. name .. ': vector lengths are not ' .. len .. ' (lengths are '
|
|
.. #v1 .. ' and ' .. #v2 .. ')', 3)
|
|
end
|
|
end
|
|
local function vector_opnnn(name, op)
|
|
return function(v1, v2)
|
|
local len = vector_checksamelen(v1, v2, name)
|
|
local v3 = {}
|
|
for i = 1, len do
|
|
v3[i] = op(v1[i], v2[i])
|
|
end
|
|
return vector_new(v3)
|
|
end
|
|
end
|
|
local function vector_opnxn(name, op)
|
|
return function(v1, v2)
|
|
local v1v = type(v1) == 'table' and v1.__is_vector
|
|
local v2v = type(v2) == 'table' and v2.__is_vector
|
|
if v1v and v2v then
|
|
local len = vector_checksamelen(v1, v2, name)
|
|
local v3 = {}
|
|
for i = 1, len do
|
|
v3[i] = op(v1[i], v2[i])
|
|
end
|
|
return vector_new(v3)
|
|
else
|
|
if v2v then v1, v2 = v2, v1 end
|
|
local len = #v1
|
|
local v3 = {}
|
|
for i = 1, len do
|
|
v3[i] = op(v1[i], v2)
|
|
end
|
|
return vector_new(v3)
|
|
end
|
|
end
|
|
end
|
|
local function vector_opn0n(name, op)
|
|
return function(v1)
|
|
--vector_check(v1, 1, name)
|
|
local len = #v1
|
|
local v2 = {}
|
|
for i = 1, len do
|
|
v2[i] = op(v1[i])
|
|
end
|
|
return vector_new(v2)
|
|
end
|
|
end
|
|
local vector_indices = { x = 1, y = 2, z = 3, w = 4, r = 1, g = 2, b = 3, a = 4 }
|
|
local vector_meta = {
|
|
__is_vector = true,
|
|
__index = function(t, k)
|
|
if tonumber(k) then
|
|
return rawget(t, k)
|
|
elseif vector_indices[k] then
|
|
return rawget(t, vector_indices[k])
|
|
else
|
|
return getmetatable(t)[k]
|
|
end
|
|
end,
|
|
__newindex = function(t, k, v)
|
|
if tonumber(k) then
|
|
rawset(t, k, v)
|
|
elseif vector_indices[k] then
|
|
rawset(t, vector_indices[k], v)
|
|
else
|
|
return
|
|
end
|
|
end,
|
|
__add = vector_opnnn('add', function(x1, x2) return x1 + x2 end),
|
|
__sub = vector_opnnn('sub', function(x1, x2) return x1 - x2 end),
|
|
__mul = vector_opnxn('mul', function(x1, x2) return x1 * x2 end),
|
|
__div = vector_opnxn('div', function(x1, x2) return x1 / x2 end),
|
|
__pow = vector_opnxn('pow', function(x1, x2) return x1 ^ x2 end),
|
|
__unm = vector_opn0n('inv', function(x1) return -x1 end),
|
|
__concat = nil,
|
|
--__len = function(v1) return #v1 end,
|
|
__len = nil,
|
|
__eq = function(v1, v2)
|
|
local len = vector_checksamelen(v1, v2, 'equals')
|
|
for i = 1, len do
|
|
if v1[i] ~= v2[i] then return false end
|
|
end
|
|
return true
|
|
end,
|
|
__lt = nil,
|
|
__le = nil,
|
|
__call = nil,
|
|
abs = vector_opn0n('abs', math.abs),
|
|
length = function(v1)
|
|
--vector_check(v1, 2, 'length')
|
|
local len = #v1
|
|
local l = 0
|
|
for i = 1, len do
|
|
l = l + v1[i] ^ 2
|
|
end
|
|
return math.sqrt(l)
|
|
end,
|
|
normalize = function(v1)
|
|
--vector_check(v1, 2, 'normal')
|
|
local length = v1:length()
|
|
local len = #v1
|
|
local v3 = {}
|
|
for i = 1, len do
|
|
if length == 0 then
|
|
v3[i] = 0
|
|
else
|
|
v3[i] = v1[i] / length
|
|
end
|
|
end
|
|
return vector_new(v3)
|
|
end,
|
|
__tostring = function(v1)
|
|
--vector_check(v1, 2, 'tostring')
|
|
local st = {}
|
|
local len = #v1
|
|
for i = 1, len do
|
|
table.insert(st, tostring(v1[i]))
|
|
end
|
|
return 'vector{ ' .. table.concat(st, ', ') .. ' }'
|
|
end,
|
|
unpack = function(v1) return unpack(v1) end,
|
|
floor = vector_opn0n('floor', function(x1) return math.floor(x1) end),
|
|
ceil = vector_opn0n('ceil', function(x1) return math.ceil(x1) end),
|
|
round = vector_opn0n('round', function(x1) return math.floor(x1 + 0.5) end),
|
|
dot = function(v1, v2)
|
|
local len = vector_checksamelen(v1, v2, 'dot')
|
|
local x = 0
|
|
for i = 1, len do
|
|
x = x + v1[i] * v2[i]
|
|
end
|
|
return x
|
|
end,
|
|
cross = function(v1, v2)
|
|
vector_checklen(v1, v2, 'cross', 3)
|
|
return vector_new {
|
|
v1[2] * v2[3] - v1[3] * v2[2],
|
|
v1[3] * v2[1] - v1[1] * v2[3],
|
|
v1[1] * v2[2] - v1[2] * v2[1],
|
|
}
|
|
end,
|
|
rotateByAngleId = function(v1, r)
|
|
--vector_check(v1, 2, 'rotate')
|
|
if type(r) ~= 'number' or r % 1 ~= 0 then
|
|
error('vector rotateByAngleId: invalid rotation ' .. tostring(r), 2)
|
|
end
|
|
r = r % 4
|
|
local v2
|
|
if r == 0 then
|
|
v2 = vector_new { v1[1], v1[2], v1[3] }
|
|
elseif r == 1 then
|
|
v2 = vector_new { v1[2], -v1[1], v1[3] }
|
|
elseif r == 2 then
|
|
v2 = vector_new { -v1[1], -v1[2], v1[3] }
|
|
elseif r == 3 then
|
|
v2 = vector_new { -v1[2], v1[1], v1[3] }
|
|
else
|
|
error('vector rotateByAngleId: invalid rotation ' .. r, 2)
|
|
end
|
|
return v2
|
|
end,
|
|
rotateZ = function(v, r)
|
|
--vector_check(v, 2, 'rotate2d')
|
|
if type(r) ~= 'number' then
|
|
error('vector rotateZ: invalid rotation ' .. tostring(r), 2)
|
|
end
|
|
local len = math.sqrt(v[1] ^ 2 + v[2] ^ 2)
|
|
local ang = math.atan2(v[2], v[1]) + r
|
|
local v2 = vector_new { math.cos(ang) * len, math.sin(ang) * len }
|
|
return v2
|
|
end,
|
|
tsString = function(v)
|
|
--vector_check(v, 2, 'tsString')
|
|
return table.concat(v, ' ')
|
|
end,
|
|
distance = function(v1, v2)
|
|
local len = vector_checksamelen(v1, v2, 'distance')
|
|
local sum = 0
|
|
for i = 1, len do
|
|
sum = sum + (v1[i] - v2[i]) ^ 2
|
|
end
|
|
return math.sqrt(sum)
|
|
end,
|
|
copy = function(v)
|
|
--vector_check(v, 2, 'copy')
|
|
return vector_new(v)
|
|
end,
|
|
}
|
|
vector_new = function(vi)
|
|
if vi then
|
|
if type(vi) == 'string' then
|
|
local vi2 = {}
|
|
for val in vi:gmatch('[0-9%.%-e]+') do
|
|
table.insert(vi2, tonumber(val))
|
|
end
|
|
vi = vi2
|
|
elseif type(vi) ~= 'table' then
|
|
error('vector: argument #1: expected input table, got ' .. type(vi), 2)
|
|
end
|
|
local v = {}
|
|
if #vi > 0 then
|
|
for i = 1, #vi do v[i] = vi[i] end
|
|
else
|
|
for n, i in pairs(vector_indices) do v[i] = vi[n] end
|
|
if #v == 0 then
|
|
error('vector: argument #1: table contains no values', 2)
|
|
end
|
|
end
|
|
setmetatable(v, vector_meta)
|
|
return v
|
|
else
|
|
error('vector: argument #1: expected input table, got nil', 2)
|
|
end
|
|
end
|
|
|
|
vector = vector_new
|
|
|
|
print(' Executed vector.lua')
|
|
|
|
return vector_new
|