forked from redo/BlockLua
initial
This commit is contained in:
219
src/util/vector.lua
Normal file
219
src/util/vector.lua
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
-- 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 '{ '..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,
|
||||
rotate2d = function(v, r)
|
||||
--vector_check(v, 2, 'rotate2d')
|
||||
if type(r)~='number' then
|
||||
error('vector rotate2d: 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
|
||||
return vector_new
|
||||
Reference in New Issue
Block a user