dobrograd-13-06-2022/garrysmod/addons/util-dlib/lua/dlib/modules/luavector.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

911 lines
17 KiB
Lua

-- Copyright (C) 2018-2020 DBotThePony
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.
local meta = FindMetaTable('LVector') or {}
debug.getregistry().LVector = meta
local table = table
local rawget = rawget
local rawset = rawset
local error = error
local string = string
local Vector = Vector
local type = luatype
local setmetatable = setmetatable
local Angle = Angle
local Color = Color
local Lerp = Lerp
meta.MetaName = 'LVector'
--[[
@doc
@fname LVector:__index
@args any key
@returns
any: associated value or function or nil
]]
function meta:__index(key)
if key == 1 then
return self.x
elseif key == 2 then
return self.y
elseif key == 3 then
return self.z
end
local func = meta[key]
if func then
return func
end
return rawget(self, key)
end
--[[
@doc
@fname LVector:__newindex
@args any key, any value
@internal
]]
function meta:__newindex(key, value)
if key == 1 then
key = 'x'
elseif key == 2 then
key = 'y'
elseif key == 3 then
key = 'z'
end
rawset(self, key, value)
end
--[[
@doc
@fname LVector
@args any x = 0, number y = 0, number z = 0
@desc
Creates a new Lua based Vector object
this Vector object **can not** be passed to C defined methods!
but can be used in Lua based methods however (which either don't call C methods
or know that they can get `LVector` instead of `Vector`)
If `x` provided is a `LVector` or `Vector`, it will be copied.
Main advantage over C based `Vector` of this that this class is JIT friendly,
and if used wisely can noticeably increase performance of hot loops
This custom class implements the most frequent used methods of vectors
@enddesc
@returns
LVector: newly created object
]]
local function LVector(x, y, z)
if type(x) == 'Vector' or luatype(x) == 'LVector' then
y = x.y
z = x.z
x = x.x
end
if type(x) ~= 'nil' and type(x) ~= 'number' then
error('Invalid X variable. typeof ' .. type(x))
end
if type(y) ~= 'nil' and type(y) ~= 'number' then
error('Invalid Y variable. typeof ' .. type(y))
end
if type(z) ~= 'nil' and type(z) ~= 'number' then
error('Invalid Z variable. typeof ' .. type(z))
end
local object = {
x = x or 0,
y = y or 0,
z = z or 0
}
return setmetatable(object, meta)
end
_G.LVector = LVector
_G.LuaVector = LVector
--[[
@doc
@fname LVector:ToNative
@returns
Vector
]]
function meta:ToNative()
return Vector(self.x, self.y, self.z)
end
--[[
@doc
@fname LVector:ToLua
@returns
LVector: copy
]]
function meta:ToLua()
return LVector(self)
end
--[[
@doc
@fname LVector:Copy
@returns
LVector: copy
]]
function meta:Copy()
return LVector(self)
end
--[[
@doc
@fname LVector:ToVector
@returns
LVector: copy
]]
function meta:ToVector()
return LVector(self)
end
--[[
@doc
@fname LVector:__tostring
@returns
string
]]
function meta:__tostring()
return string.format('LuaVector [%.6f %.6f %.6f]', self.x, self.y, self.z)
end
--[[
@doc
@fname LVector:__call
@returns
LVector: copy
]]
function meta:__call()
return LVector(self)
end
-- Vector maths
local math = math
--[[
@doc
@fname LVector:Length
@returns
number
]]
function meta:Length()
return math.sqrt(self.x:pow(2) + self.y:pow(2) + self.z:pow(2))
end
--[[
@doc
@fname LVector:LengthSqr
@returns
number
]]
function meta:LengthSqr()
return self.x:pow(2) + self.y:pow(2) + self.z:pow(2)
end
--[[
@doc
@fname LVector:Length2D
@returns
number
]]
function meta:Length2D()
return math.sqrt(self.x:pow(2) + self.y:pow(2))
end
--[[
@doc
@fname LVector:Length2DSqr
@returns
number
]]
function meta:Length2DSqr()
return self.x:pow(2) + self.y:pow(2)
end
function meta:Normalize()
local len = self:Length()
self.x, self.y, self.z = self.x / len, self.y / len, self.z / len
return self
end
--[[
@doc
@fname LVector:GerNormalized
@returns
LVector: normalized copy of vector
]]
function meta:GetNormalized()
local len = self:Length()
return LVector(self.x / len, self.y / len, self.z / len)
end
--[[
@doc
@fname LVector:IsNormalized
@returns
boolean
]]
function meta:IsNormalized()
return self.x <= 1 and self.y <= 1 and self.z <= 1 and self.x >= -1 and self.y >= -1 and self.z >= -1
end
local UP = LVector(0, 0, 1)
local FORWARD = LVector(1, 0, 0)
local LEFT = LVector(0, 1, 0)
--[[
@doc
@fname LVector:Dot
@args LVector another
@desc
See !g:Vector:Dot
@enddesc
@returns
number
]]
function meta:Dot(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:Dot(' .. type(another) .. ') - invalid call')
end
local scalar =
self.x * another.x +
self.y * another.y +
self.z * another.z
local len = self:Length() * another:Length()
if len == 0 then return 0 end
return scalar / len
end
--[[
@doc
@fname LVector:IsZero
@returns
boolean
]]
function meta:IsZero()
return self.x == 0 and self.y == 0 and self.z == 0
end
--[[
@doc
@fname LVector:Lerp
@args number lerpValue, LVector lerpTo
@returns
LVector: copy
]]
function meta:Lerp(lerpValue, lerpTo)
if type(lerpTo) ~= 'Vector' and type(lerpTo) ~= 'LVector' then
error('LVector:Lerp(' .. type(lerpValue) .. ', ' .. type(lerpTo) .. ') - invalid call')
end
local x = Lerp(lerpValue, self.x, lerpTo.x)
local y = Lerp(lerpValue, self.y, lerpTo.x)
local z = Lerp(lerpValue, self.z, lerpTo.z)
return LVector(x, y, z)
end
meta.CosinusBetween = meta.Dot
meta.DegreeBetween = meta.Dot
-- Various vanilla things
--[[
@doc
@fname LVector:Add
@args LVector another
@desc
Adds another vector
This method **modifies** original LVector
@enddesc
@returns
LVector: self
]]
function meta:Add(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:Add(' .. type(another) .. ') - invalid call')
end
self.x = self.x + another.x
self.y = self.y + another.y
self.z = self.z + another.z
return self
end
--[[
@doc
@fname LVector:Sub
@args LVector another
@desc
Substrates another vector
This method **modifies** original LVector
@enddesc
@returns
LVector: self
]]
function meta:Sub(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:Sub(' .. type(another) .. ') - invalid call')
end
self.x = self.x - another.x
self.y = self.y - another.y
self.z = self.z - another.z
return self
end
--[[
@doc
@fname LVector:Set
@args LVector another
@desc
This method **modifies** original LVector
This method sets `x`, `y`, and `z` to their corresponding values from `another`
@enddesc
@returns
LVector: self
]]
function meta:Set(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:Set(' .. type(another) .. ') - invalid call')
end
self.x = another.x
self.y = another.y
self.z = another.z
return self
end
--[[
@doc
@fname LVector:Zero
@args LVector another
@desc
This method **modifies** original LVector
sets `x`, `y` and `z` to 0
@enddesc
@returns
LVector: self
]]
function meta:Zero()
self.x = 0
self.y = 0
self.z = 0
return self
end
--[[
@doc
@fname LVector:WithinAABox
@args LVector mins, LVector maxs
@returns
boolean
]]
function meta:WithinAABox(mins, maxs)
if type(mins) ~= 'Vector' and type(mins) ~= 'LVector' then
error('LVector:WithinAABox(' .. type(mins) .. ', ' .. type(maxs) .. ') - invalid call')
end
if type(maxs) ~= 'Vector' and type(maxs) ~= 'LVector' then
error('LVector:WithinAABox(' .. type(mins) .. ', ' .. type(maxs) .. ') - invalid call')
end
return self.x >= mins.x
and self.y >= mins.y
and self.z >= mins.z
and self.x <= maxs.x
and self.y <= maxs.y
and self.z <= maxs.z
end
--[[
@doc
@fname LVector:Distance
@args LVector another
@returns
number: length in hammer units
]]
function meta:Distance(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:Distance(' .. type(another) .. ') - invalid call')
end
return math.sqrt((self.x - another.x):pow(2) + (self.y - another.y):pow(2) + (self.z - another.z):pow(2))
end
--[[
@doc
@fname LVector:DistToSqr
@args LVector another
@returns
number: length in hammer units without square rooting
]]
function meta:DistToSqr(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:DistToSqr(' .. type(another) .. ') - invalid call')
end
return (self.x - another.x):pow(2) + (self.y - another.y):pow(2) + (self.z - another.z):pow(2)
end
--[[
@doc
@fname LVector:Mul
@args LVector another
@desc
Multiplies this vector by another
This method **modifies** original LVector
@enddesc
@returns
LVector: self
]]
function meta:Mul(number)
if type(number) ~= 'number' then
error('LVector:Mul(' .. type(number) .. ') - invalid call')
end
self.x = self.x * number
self.y = self.y * number
self.z = self.z * number
return self
end
--[[
@doc
@fname LVector:Div
@args LVector another
@desc
Divides this vector by another
This method **modifies** original LVector
@enddesc
@returns
LVector: self
]]
function meta:Div(number)
if type(number) ~= 'number' then
error('LVector:Div(' .. type(number) .. ') - invalid call')
end
self.x = self.x / number
self.y = self.y / number
self.z = self.z / number
return self
end
--[[
@doc
@fname LVector:ToColor
@returns
Color
]]
function meta:ToColor()
return Color(self.x / 255, self.y / 255, self.z / 255)
end
--[[
@doc
@fname LVector:Angle
@returns
Angle
]]
function meta:Angle()
local normal = self:GetNormalized()
local x, y = normal.x, normal.y
local add = 45
if x > 0 and y < 0 then
add = -45
end
local dotPitch = normal:Dot(UP)
local dotYaw = LVector(x, y):Dot(LVector(1, 1)):acos():deg() + add
if x > 0 and y < 0 then
dotYaw = -dotYaw
end
return Angle(-dotPitch:asin():deg(), dotYaw, 0)
end
-- Extended
--[[
@doc
@fname LVector:Invert
@args LVector another
@desc
Inverts sign of coordinates
This method **modifies** original LVector
@enddesc
@returns
LVector: self
]]
function meta:Invert()
return LVector(-self.x, -self.y, -self.z)
end
--[[
@doc
@fname LVector:IsValid
@returns
boolean: whenever vector contains valid coordinates
]]
function meta:IsValid()
return
self.x == self.x
and self.y == self.y
and self.z == self.z
and self.x ~= self.x.huge
and self.y ~= self.y.huge
and self.z ~= self.z.huge
and self.x ~= -self.x.huge
and self.y ~= -self.y.huge
and self.z ~= -self.z.huge
end
-- Lua metamethods
--[[
@doc
@fname LVector:__unm
@desc
-var1
returns inverted vector
@enddesc
@returns
LVector: copy
]]
function meta:__unm()
return LVector(self):Invert()
end
--[[
@doc
@fname LVector:__eq
@args LVector another
@desc
var1 == var2
@enddesc
@returns
LVector: copy
]]
function meta:__eq(another)
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__eq(' .. type(another) .. ') - invalid call')
end
return self.x == another.x and self.y == another.y and self.z == another.z
end
--[[
@doc
@fname LVector:__add
@args LVector another
@desc
var1 + var2
@enddesc
@returns
LVector: copy
]]
function meta:__add(another)
--[[if type(self) == 'number' then
return LVector(another.x + self, another.y + self, another.z + self)
end
if type(another) == 'number' then
return LVector(another + self.x, another + self.y, another + self.z)
end]]
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__add(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__add(' .. type(self) .. ') - invalid call')
end
return LVector(another.x + self.x, another.y + self.y, another.z + self.z)
end
--[[
@doc
@fname LVector:__sub
@args LVector another
@desc
var1 - var2
@enddesc
@returns
LVector: copy
]]
function meta:__sub(another)
--[[if type(self) == 'number' then
return LVector(another.x - self, another.y - self, another.z - self)
end
if type(another) == 'number' then
return LVector(self.x - another, self.y - another, self.z - another)
end]]
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__sub(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__sub(' .. type(self) .. ') - invalid call')
end
return LVector(self.x - another.x, self.y - another.x, self.z - another.x)
end
--[[
@doc
@fname LVector:__mul
@args LVector another
@desc
var1 * var2
@enddesc
@returns
LVector: copy
]]
function meta:__mul(another)
if type(self) == 'number' then
return LVector(another.x * self, another.y * self, another.z * self)
end
if type(another) == 'number' then
return LVector(self.x * another, self.y * another, self.z * another)
end
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__mul(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__mul(' .. type(self) .. ') - invalid call')
end
return LVector(self.x * another.x, self.y * another.x, self.z * another.x)
end
--[[
@doc
@fname LVector:__div
@args LVector another
@desc
var1 / var2
@enddesc
@returns
LVector: copy
]]
function meta:__div(another)
if type(self) == 'number' then
return LVector(another.x / self, another.y / self, another.z / self)
end
if type(another) == 'number' then
return LVector(self.x / another, self.y / another, self.z / another)
end
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__div(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__div(' .. type(self) .. ') - invalid call')
end
return LVector(self.x / another.x, self.y / another.x, self.z / another.x)
end
--[[
@doc
@fname LVector:__mod
@args any another
@desc
var1 % var2
this function accept `Vector`, `LVector` and `number` arguments
@enddesc
@returns
LVector: copy
]]
function meta:__mod(another)
if type(self) == 'number' then
return LVector(another.x % self, another.y % self, another.z % self)
end
if type(another) == 'number' then
return LVector(self.x % another, self.y % another, self.z % another)
end
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__mod(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__mod(' .. type(self) .. ') - invalid call')
end
return LVector(self.x % another.x, self.y % another.x, self.z % another.x)
end
--[[
@doc
@fname LVector:__lt
@args any another
@desc
var1 < var2
this function accept `Vector`, `LVector` and `number` arguments
@enddesc
@returns
boolean
]]
function meta:__lt(another)
if type(self) == 'number' then
return another:Length() < self
end
if type(another) == 'number' then
return self:Length() < another
end
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__lt(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__lt(' .. type(self) .. ') - invalid call')
end
return self:Length() < another:Length()
end
--[[
@doc
@fname LVector:__le
@args any another
@desc
var1 <= var2
this function accept `Vector`, `LVector` and `number` arguments
@enddesc
@returns
boolean
]]
function meta:__le(another)
if type(self) == 'number' then
return another:Length() <= self
end
if type(another) == 'number' then
return self:Length() <= another
end
if type(another) ~= 'Vector' and type(another) ~= 'LVector' then
error('LVector:__le(' .. type(another) .. ') - invalid call')
end
if type(self) ~= 'Vector' and type(self) ~= 'LVector' then
error('LVector:__le(' .. type(self) .. ') - invalid call')
end
return self:Length() <= another:Length()
end
--[[
@doc
@fname LVector:__concat
@args any another
@desc
var1 .. var2
@enddesc
@returns
string
]]
function meta:__concat(another)
if type(self) == 'LVector' then
return string.format('Vector [%.2f %.2f %.2f]', self.x, self.y, self.z) .. another
else
return self .. string.format('Vector [%.2f %.2f %.2f]', another.x, another.y, another.z)
end
end