alpaca
King of Ungulates
- Joined
- Aug 3, 2006
- Messages
- 2,322
I needed a more powerful and general vector implementation that supports adding and such, so yeah, not much to say, really. Create a new vector with Vector.new({1,1,0}) or Vector.new(3), where the latter creates a 0 vector of length 3, or by calling Vector({1,1,0}).
Supports adding, subtracting, scalar (elementwise) multiplication and division, dot product using the concatenation .. operator, cross-product for 3d vectors using the ^ operator. Also supports equality checks and a less than v1 < v2 implementation that checks if the absolute value of v1 is smaller than that of v2 (so v1 is in a ball defined by abs(v2)).
Most functions work with scalars if you would expect them to, so *, /, +, -, <, == all support scalars in some way.
Just add the code to a lua file and into the VFS, then include it in your scripts when you need it.
Supports adding, subtracting, scalar (elementwise) multiplication and division, dot product using the concatenation .. operator, cross-product for 3d vectors using the ^ operator. Also supports equality checks and a less than v1 < v2 implementation that checks if the absolute value of v1 is smaller than that of v2 (so v1 is in a ball defined by abs(v2)).
Most functions work with scalars if you would expect them to, so *, /, +, -, <, == all support scalars in some way.
Just add the code to a lua file and into the VFS, then include it in your scripts when you need it.
Code:
--[[
vector.lua
Creator: alpaca
Last Change: 04.02.2011
Description: Defines a vector type with the following operators:
+ adds two vectors or adds a scalar onto each element of the vector
- the same but subtracts rhs from lhs; can be used unary in which case -v is returned
* scalar multiplication for vector*scalar or element-wise product for vector*vector
/ scalar division; element-wise for vectors
.. dot (scalar) product
^ outer product (c = a^b means c[i] = a[i+1]*b[i+2] - a[i+2]*b[i+1] where i is taken modulo #n, so a[4] == a[1]); only defined for vectors of dimension 3
Supports equality and an implementation of v1 < v2 that means abs(v1) < abs(v2)
Note: Civ5 lua doesn't support rawget :/
{Supports slicing in the form ["a:b"] where a is the first included index and b the last included one (so v["1:#v"] = v, v["1:1"] = v[1]); a or b can be nil in which case 1 or #v respectively will be assumed}
]]--
Vector = {}
local meta = {}
local mt = {}
setmetatable(Vector, mt)
--[[
Constructor. Shallow-copies the array part of a table for initialisation
Arguments:
t: table or num. The array part of t will be used to initialise the vector, so new({1,0,0}) will create a 3d-unit vector. If t is a number, a zero-vector of length t will be created
]]--
function Vector.new(t)
local vector = {}
if t then
if type(t) == "table" then
for k, v in ipairs(t) do
vector[k] = v
end
elseif type(t) == "number" then
for k = 1, t do
vector[k] = 0
end
else
error("Vector constructor called with illegal value: "..tostring(t))
end
end
vector.type = "vector"
vector.abs = Vector.abs
setmetatable(vector, meta)
return vector
end
mt.__call = function(tbl, ...) return Vector.new(...) end
function Vector.add(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" then
if type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Can't add, vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v + rhs[k]
end
return result
elseif type(rhs) == "number" then
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v + rhs
end
return result
end
elseif type(rhs) == "table" and rhs.type == "vector" then
local result = Vector.new(lhs)
for k, v in ipairs(rhs) do
result[k] = v + lhs
end
return result
end
error("Can't add. Either argument is neither a vector nor a number: "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.subtract(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" then
if type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Can't subtract, vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v - rhs[k]
end
return result
elseif type(rhs) == "number" then
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v - rhs
end
return result
end
elseif type(rhs) == "table" and rhs.type == "vector" then
local result = Vector.new(lhs)
for k, v in ipairs(rhs) do
result[k] = lhs - v
end
return result
end
error("Can't subtract. Either argument is neither a vector nor a number: "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.negate(v)
return Vector.subtract(0, v)
end
function Vector.scalarMult(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" then
if type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Scalar multiplication invalid: vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v * rhs[k]
end
return result
elseif type(rhs) == "number" then
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v * rhs
end
return result
else
error("Scalar multiplication invalid: lhs is a vector but rhs is neither vector nor scalar: "..tostring(lhs)..", "..tostring(rhs))
end
elseif type(rhs) == "table" and rhs.type == "vector" then
if type(lhs) == "number" then
local result = Vector.new(rhs)
for k, v in ipairs(rhs) do
result[k] = v * lhs
end
return result
else
error("Scalar multiplication invalid: rhs is a vector but lhs is no scalar"..tostring(lhs)..", "..tostring(rhs))
end
end
error("Scalar multiplication invalid: neither side is a vector"..tostring(lhs)..", "..tostring(rhs))
end
function Vector.divide(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" then
if type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Scalar division invalid: vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v / rhs[k]
end
return result
elseif type(rhs) == "number" then
local result = Vector.new(lhs)
for k, v in ipairs(lhs) do
result[k] = v / rhs
end
return result
else
error("Scalar division invalid: lhs is a vector but rhs is neither vector nor scalar: "..tostring(lhs)..", "..tostring(rhs))
end
elseif type(rhs) == "table" and rhs.type == "vector" then
if type(lhs) == "number" then
local result = Vector.new(rhs)
for k, v in ipairs(rhs) do
result[k] = lhs / v
end
return result
else
error("Scalar division invalid: rhs is a vector but lhs is no scalar"..tostring(lhs)..", "..tostring(rhs))
end
end
error("Scalar division invalid: neither side is a vector"..tostring(lhs)..", "..tostring(rhs))
end
function Vector.dotProduct(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Dot product invalid: vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
local result = 0
for k, v in ipairs(lhs) do
result = result + v * rhs[k]
end
return result
end
error("Dot product invalid: either lhs or rhs is not a vector: "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.crossProduct(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= 3 or #rhs ~= 3 then
error("Cross product invalid: both vectors have to be of length 3: "..tostring(lhs)..", "..tostring(rhs))
end
local result = Vector.new(3)
result[1] = lhs[2]*rhs[3] - lhs[3]*rhs[2]
result[2] = lhs[3]*rhs[1] - lhs[1]*rhs[3]
result[3] = lhs[1]*rhs[2] - lhs[2]*rhs[1]
return result
end
error("Cross product invalid: either lhs or rhs is not a vector: "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.eq(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "table" and rhs.type == "vector" then
if #lhs ~= #rhs then
error("Equality check invalid: vectors don't have the same length: "..tostring(lhs)..", "..tostring(rhs))
end
for k, v in ipairs(lhs) do
if v ~= rhs[k] then
return false
end
end
return true
elseif type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "number" then
return Vector.abs(lhs) == rhs
elseif type(rhs) == "table" and rhs.type == "vector" and type(lhs) == "number" then
return lhs == Vector.abs(rhs)
end
error("Equality check invalid: not both sides are vectors "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.lt(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "table" and rhs.type == "vector" then
return Vector.abs(lhs) < Vector.abs(rhs)
elseif type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "number" then
return Vector.abs(lhs) < rhs
elseif type(rhs) == "table" and rhs.type == "vector" and type(lhs) == "number" then
return lhs < Vector.abs(rhs)
end
error("Less than check invalid: not both sides are vectors "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.le(lhs, rhs)
if type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "table" and rhs.type == "vector" then
return Vector.abs(lhs) <= Vector.abs(rhs)
elseif type(lhs) == "table" and lhs.type == "vector" and type(rhs) == "number" then
return Vector.abs(lhs) <= rhs
elseif type(rhs) == "table" and rhs.type == "vector" and type(lhs) == "number" then
return lhs <= Vector.abs(rhs)
end
error("Leq check invalid: not both sides are vectors "..tostring(lhs)..", "..tostring(rhs))
end
function Vector.tostring(v)
local rs = "("
for k, n in ipairs(v) do
rs = rs .. tostring(n) .. (k < #v and ", " or "")
end
return rs .. ")"
end
meta.__add = Vector.add
meta.__sub = Vector.subtract
meta.__unm = Vector.negate
meta.__mul = Vector.scalarMult
meta.__div = Vector.divide
meta.__concat = Vector.dotProduct
meta.__pow = Vector.crossProduct
meta.__eq = Vector.eq
meta.__lt = Vector.lt
meta.__le = Vector.le
meta.__tostring = Vector.tostring
function Vector.abs(v)
local result = 0
for _, v in ipairs(v) do
result = result + v^2
end
return math.sqrt(result)
end