diff options
author | DS <vorunbekannt75@web.de> | 2021-06-04 21:22:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-04 21:22:33 +0200 |
commit | 8f085e02a107dd8092393935bfa1bba71d2546d2 (patch) | |
tree | 977942563431784d65b5f5066d04669d522600e6 /builtin/common/vector.lua | |
parent | e15cae9fa0f99f597f349a7ba07d149cd91cc2d8 (diff) | |
download | minetest-8f085e02a107dd8092393935bfa1bba71d2546d2.tar.gz minetest-8f085e02a107dd8092393935bfa1bba71d2546d2.tar.bz2 minetest-8f085e02a107dd8092393935bfa1bba71d2546d2.zip |
Add metatables to lua vectors (#11039)
Add backwards-compatible metatable functions for vectors.
Diffstat (limited to 'builtin/common/vector.lua')
-rw-r--r-- | builtin/common/vector.lua | 212 |
1 files changed, 152 insertions, 60 deletions
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 2ef8fc617..cbaa872dc 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -1,15 +1,43 @@ +--[[ +Vector helpers +Note: The vector.*-functions must be able to accept old vectors that had no metatables +]] + +-- localize functions +local setmetatable = setmetatable vector = {} +local metatable = {} +vector.metatable = metatable + +local xyz = {"x", "y", "z"} + +-- only called when rawget(v, key) returns nil +function metatable.__index(v, key) + return rawget(v, xyz[key]) or vector[key] +end + +-- only called when rawget(v, key) returns nil +function metatable.__newindex(v, key, value) + rawset(v, xyz[key] or key, value) +end + +-- constructors + +local function fast_new(x, y, z) + return setmetatable({x = x, y = y, z = z}, metatable) +end + function vector.new(a, b, c) if type(a) == "table" then assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()") - return {x=a.x, y=a.y, z=a.z} + return fast_new(a.x, a.y, a.z) elseif a then assert(b and c, "Invalid arguments for vector.new()") - return {x=a, y=b, z=c} + return fast_new(a, b, c) end - return {x=0, y=0, z=0} + return fast_new(0, 0, 0) end function vector.from_string(s, init) @@ -27,48 +55,49 @@ end function vector.to_string(v) return string.format("(%g, %g, %g)", v.x, v.y, v.z) end +metatable.__tostring = vector.to_string function vector.equals(a, b) return a.x == b.x and a.y == b.y and a.z == b.z end +metatable.__eq = vector.equals + +-- unary operations function vector.length(v) return math.hypot(v.x, math.hypot(v.y, v.z)) end +-- Note: we can not use __len because it is already used for primitive table length function vector.normalize(v) local len = vector.length(v) if len == 0 then - return {x=0, y=0, z=0} + return fast_new(0, 0, 0) else return vector.divide(v, len) end end function vector.floor(v) - return { - x = math.floor(v.x), - y = math.floor(v.y), - z = math.floor(v.z) - } + return vector.apply(v, math.floor) end function vector.round(v) - return { - x = math.round(v.x), - y = math.round(v.y), - z = math.round(v.z) - } + return fast_new( + math.round(v.x), + math.round(v.y), + math.round(v.z) + ) end function vector.apply(v, func) - return { - x = func(v.x), - y = func(v.y), - z = func(v.z) - } + return fast_new( + func(v.x), + func(v.y), + func(v.z) + ) end function vector.distance(a, b) @@ -79,11 +108,7 @@ function vector.distance(a, b) end function vector.direction(pos1, pos2) - return vector.normalize({ - x = pos2.x - pos1.x, - y = pos2.y - pos1.y, - z = pos2.z - pos1.z - }) + return vector.subtract(pos2, pos1):normalize() end function vector.angle(a, b) @@ -98,70 +123,137 @@ function vector.dot(a, b) end function vector.cross(a, b) - return { - x = a.y * b.z - a.z * b.y, - y = a.z * b.x - a.x * b.z, - z = a.x * b.y - a.y * b.x - } + return fast_new( + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x + ) end +function metatable.__unm(v) + return fast_new(-v.x, -v.y, -v.z) +end + +-- add, sub, mul, div operations + function vector.add(a, b) if type(b) == "table" then - return {x = a.x + b.x, - y = a.y + b.y, - z = a.z + b.z} + return fast_new( + a.x + b.x, + a.y + b.y, + a.z + b.z + ) else - return {x = a.x + b, - y = a.y + b, - z = a.z + b} + return fast_new( + a.x + b, + a.y + b, + a.z + b + ) end end +function metatable.__add(a, b) + return fast_new( + a.x + b.x, + a.y + b.y, + a.z + b.z + ) +end function vector.subtract(a, b) if type(b) == "table" then - return {x = a.x - b.x, - y = a.y - b.y, - z = a.z - b.z} + return fast_new( + a.x - b.x, + a.y - b.y, + a.z - b.z + ) else - return {x = a.x - b, - y = a.y - b, - z = a.z - b} + return fast_new( + a.x - b, + a.y - b, + a.z - b + ) end end +function metatable.__sub(a, b) + return fast_new( + a.x - b.x, + a.y - b.y, + a.z - b.z + ) +end function vector.multiply(a, b) if type(b) == "table" then - return {x = a.x * b.x, - y = a.y * b.y, - z = a.z * b.z} + return fast_new( + a.x * b.x, + a.y * b.y, + a.z * b.z + ) else - return {x = a.x * b, - y = a.y * b, - z = a.z * b} + return fast_new( + a.x * b, + a.y * b, + a.z * b + ) + end +end +function metatable.__mul(a, b) + if type(a) == "table" then + return fast_new( + a.x * b, + a.y * b, + a.z * b + ) + else + return fast_new( + a * b.x, + a * b.y, + a * b.z + ) end end function vector.divide(a, b) if type(b) == "table" then - return {x = a.x / b.x, - y = a.y / b.y, - z = a.z / b.z} + return fast_new( + a.x / b.x, + a.y / b.y, + a.z / b.z + ) else - return {x = a.x / b, - y = a.y / b, - z = a.z / b} + return fast_new( + a.x / b, + a.y / b, + a.z / b + ) end end +function metatable.__div(a, b) + -- scalar/vector makes no sense + return fast_new( + a.x / b, + a.y / b, + a.z / b + ) +end + +-- misc stuff function vector.offset(v, x, y, z) - return {x = v.x + x, - y = v.y + y, - z = v.z + z} + return fast_new( + v.x + x, + v.y + y, + v.z + z + ) end function vector.sort(a, b) - return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, - {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} + return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)), + fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z)) +end + +function vector.check(v) + return getmetatable(v) == metatable end local function sin(x) @@ -229,7 +321,7 @@ end function vector.dir_to_rotation(forward, up) forward = vector.normalize(forward) - local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0} + local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0) if not up then return rot end @@ -237,7 +329,7 @@ function vector.dir_to_rotation(forward, up) "Invalid vectors passed to vector.dir_to_rotation().") up = vector.normalize(up) -- Calculate vector pointing up with roll = 0, just based on forward vector. - local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot) + local forwup = vector.rotate(vector.new(0, 1, 0), rot) -- 'forwup' and 'up' are now in a plane with 'forward' as normal. -- The angle between them is the absolute of the roll value we're looking for. rot.z = vector.angle(forwup, up) |