Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Reference:
- vec = require'ffivec'
- a = vec(3, 4)
- b = vec(-30, -40)
- c = vec(3, -2)
- -- Call. It faster then defineing new vectors
- a(b) --> a = {-30, -40}, a get new value, is not link to b
- a(3, 4) --> a = {3, 4}, set it back
- -- Arithmetic:
- d = a+b --> d = {-27, -36}
- d = b-a --> d = {-33, -44}
- d = a*b --> d = {-90, -160}
- d = -b*0.2 --> d = {6, 8}
- d = a/b --> d = {0.1, 0.1}
- d = a/2 --> d = {1.5, 2}
- d = a^c --> d = {27, 0.0625}
- d = a^3 --> d = {27, 64}
- -- Equality
- a == 5 --> true, because a:len() = 5
- a == d --> false
- b > a --> true
- b <= 50 --> true, because #b = 50
- -- Len
- e = #a --> e = 5
- e = a:len() --> e = 5
- e = a:len2()--> e = 25
- -- Tostring, concat
- str = tostring(a) --> str = '[3, 4]'
- print('sum of vectors: '..(a+b)) --> sum of vectors: [33, 44]
- -- Util methods:
- print(a:unpack()) --> 3 4
- print(a:join(b, 10, 20, c, 30, 40))
- --> return touple of all arguments (a.x, a.y, b.x, b.y, 10, 20, c.x, c.y, 30, 40)
- vec.isVec(b) --> return true
- d = a:clone()--> d = {3, 4}, work like call
- --Methods:
- -- suffix 'ed' in two-way methods means function return new vector, not modding original
- --a:area() - S of a-rectangle (a.x * a.y)
- --a:floor([n]) - flooring, n - digits after the decimal point, 0 as default
- --a:ceil([n]) - ceiling, n - digits after the decimal point, 0 as default
- --a:permul(b) - like a*b, but only for vectors, little bit faster
- --a:dist(b) - return distance between a and b
- --a:dist2(b) - return distance^2 between a and b
- --a:dot(b) - dot product (a.x * b.x + a.y * b.y)
- --a:det(b) - det product (a.x * b.x - a.y * b.y)
- --a:cross(b) - cross product (a.x * b.y - a.y * b.x)
- --a:trim(len) - vector trimming, a become new value
- --a:trimmed(len) - vector trimming, return new vector, a not modded
- --a:angle([b|n,m]) - return vector angle (no args) or angle between two vectors (arg can be vec or two num)
- --a:setAngle(n) - set vector by angle n
- --a:rotate(n) - rotate the vector by the angle n
- --a:rotated(n) - return a-clone, rotated by the angle n
- --a:normalize() - normalize vector
- --a:normalized() - return normalized a-clone
- --a:perpendicular([b|n,m]) - set a perpendicular to b
- --a:perpendiculared([b|n,m]) - return b-perpendiculared a-clone
- --a:project([b|n,m]) - set a to projection a to b
- --a:projected([b|n,m]) - return vector-projection a to b
- --a:mirror([b|n,m]) - mirror a relatively b
- --a:mirrored([b|n,m]) - return vector-mirror of a relatively b
- ]]
- local lvl = 2
- local ffi = require("ffi")
- ffi.cdef 'typedef struct { double x, y; } vec2;'
- local point, vector, mt = {}, {}, {}
- local error = error
- local pi, tau, sqrt, cos, sin, acos, atan2 = math.pi, math.pi*2, math.sqrt, math.cos, math.sin, math.acos, math.atan2
- local floor, ceil, min, max = math.floor, math.ceil, math.min, math.max
- local function isVec(v) return type(v) == 'cdata' and v.x and v.y end
- local function isNum(v) return type(v) == 'number' end
- local oldtype = type
- local type = function(v)
- return isVec(v) and 'vector'
- or oldtype(v)
- end
- local function switchVec(a, b)
- if isVec(a) then
- return a, b
- else
- return b, a
- end
- end
- local function anorm(a)
- local v = ((a % tau + tau) % tau)
- return v < pi and v or v - tau
- end
- local function set(self, a, b)
- self.x, self.y = a, b
- return self
- end
- setmetatable(vector, {
- __call = function(self, a, b)
- if not isNum(a) or not isNum(b) then error('Vector: use vectors with two numbers, you know? I got '..type(a)..' and '..type(b)..', you excite me') end
- local vec = self.cdata(a, b)
- return vec
- end})
- local mt = {}
- function mt.__call(self, a, b)
- return isVec(a) and set(self, a.x, a.y)
- or isNum(a) and isNum(b) and set(self, a, b)
- or isNum(a) and self:normalize(a)
- or error('Vector: vector or two scalars expected, got: '..type(a)..' and '..type(b), lvl)
- end
- function mt.__add(a, b)
- a, b = switchVec(a, b)
- return isVec(b) and vector(a.x + b.x, a.y + b.y)
- or error('Vector: two vectors expected, got addition of the vector with a scalar', lvl)
- end
- function mt.__sub(a,b)
- return isVec(a) and isVec(b) and vector(a.x-b.x, a.y-b.y)
- or error('Vector: two vectors or [vector/scalar] expected, got: '..type(a)..' and '..type(b), lvl)
- end
- function mt.__mul(a, b)
- a, b = switchVec(a, b)
- return isVec(b) and vector(a.x * b.x, a.y * b.y)
- or isNum(b) and vector(a.x * b, a.y * b)
- or error('Vector: two vectors or vector and scalar expected, got vector and '..type(b), lvl)
- end
- function mt.__div(a, b)
- return isVec(b) and vector(a.x/b.x, a.y/b.y)
- or isNum(b) and vector(a.x/b, a.y/b)
- or error('Vector: two vectors or [vector/scalar] expected, got: '..type(a)..' and '..type(b), lvl)
- end
- function mt.__pow(a, b)
- return isVec(b) and vector(a.x^b.x, a.y^b.y)
- or isNum(b) and vector(a.x^b, a.y^b)
- or error('Vector: two vectors or [vector/scalar] expected, got: '..type(a)..' and '..type(b), lvl)
- end
- function mt.__eq(a,b)
- a, b = switchVec(a, b)
- return isVec(b) and a.x == b.x and a.y == b.y
- or isNum(b) and #a == b
- end
- function mt.__lt(a,b)
- if isVec(a) and isVec(b) then
- return a.x < b.x
- or (a.x == b.x and a.y < b.y)
- end
- return isNum(a) and a < #b
- or isNum(b) and #a < b
- end
- function mt.__le(a,b)
- if isVec(a) and isVec(b) then
- return a.x <= b.x and a.y <= b.y
- end
- return isNum(a) and a <= #b
- or isNum(b) and #a <= b
- end
- function mt.__unm(a)
- return vector(-a.x, -a.y)
- end
- function mt.__len(a)
- return (a.x*a.x + a.y*a.y)^0.5
- end
- function mt.__tostring(a)
- return '['..a.x..', '..a.y..']'
- end
- function mt.__tonumber(a)
- return #a
- end
- function mt.__concat(a, b)
- return tostring(a)..tostring(b)
- end
- mt.__index = vector
- vector.cdata = ffi.metatype("vec2", mt)
- function vector:unpack(v)
- if v then
- return self.x*v, self.y*v
- else
- return self.x, self.y
- end
- end
- function vector:clone()
- return vector(self.x, self.y)
- end
- function vector:area()
- return self.x*self.x + self.y*self.y
- end
- function vector:floor(v)
- return isNum(v) and vector(floor(self.x*10*v)/(18*v), floor(self.y*10*v)/(18*v))
- or vector(floor(self.x), floor(self.y))
- end
- function vector:ceil(v)
- return isNum(v) and vector(ceil(self.x*10*v)/(18*v), ceil(self.y*10*v)/(18*v))
- or vector(ceil(self.x), ceil(self.y))
- end
- function vector:permul(a, b)
- if isVec(a) then
- return vector(self.x*a.x, self.y*a.y)
- elseif isNum(a) and isNum(b) then
- return vector(self.x*a, self.y*b)
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:permul: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:dot(a, b)
- if isVec(a) then
- return self.x * a.x + self.y * a.y
- elseif isNum(a) and isNum(b) then
- return self.x * a + self.y * b
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:dot: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:det(a, b)
- if isVec(a) then
- return self.x * a.x - self.y * a.y
- elseif isNum(a) and isNum(b) then
- return self.x * a - self.y * b
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:det: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:cross(a, b)
- if isVec(a) then
- return self.x * a.y - self.y * a.x
- elseif isNum(a) and isNum(b) then
- return self.x * b - self.y * a
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:cross: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:trim(l)
- if not isNum(l) then error('Vector:trim: number expected, got: '..type(l), lvl) end
- local s = l * l / self:len2()
- s = (s > 1 and 1) or math.sqrt(s)
- self.x, self.y = self.x * s, self.y * s
- return self
- end
- function vector:trimmed(l)
- if not isNum(l) then error('Vector:trim: number expected, got: '..type(l), lvl) end
- return self:clone():trim(l)
- end
- function vector:isVec(v)
- return v and isVec(v) or isVec(self)
- end
- function vector:angle(a, b)
- if isVec(a) then
- return atan2(self.y, self.x) - atan2(a.y, a.x)
- elseif isNum(a) and isNum(b) then
- return atan2(self.y, self.x) - atan2(b, a)
- elseif isNum(a) then
- return atan2(self.y, self.x) - a
- end
- return atan2(self.y, self.x)
- end
- function vector:len2()
- return self.x * self.x + self.y * self.y
- end
- function vector:len()
- return self:len2()^0.5
- end
- function vector:dist(a, b)
- if isVec(a) then
- local dx = self.x - a.x
- local dy = self.y - a.y
- return sqrt(dx * dx + dy * dy)
- elseif isNum(a) and isNum(b) then
- local dx = self.x - a
- local dy = self.y - b
- return sqrt(dx * dx + dy * dy)
- else
- local str = type(a)
- str = b and str..', '..type(b) or str
- error('Vector:dist: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:dist2(a, b)
- if isVec(a) then
- local dx = self.x - a.x
- local dy = self.y - a.y
- return dx * dx + dy * dy
- elseif isNum(a) and isNum(b) then
- local dx = self.x - a
- local dy = self.y - b
- return dx * dx + dy * dy
- else
- local str = type(a)
- str = b and str..', '..type(b) or str
- error('Vector:dist: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:normalize(v)
- local d = isVec(v) and #v or isNum(v) and v or 1
- local l = #self
- if l > 0 then
- self.x, self.y = self.x / l * d, self.y / l * d
- end
- return self
- end
- function vector:normalized(v)
- return self:clone():normalize(v)
- end
- function vector:setAngle(phi)
- -- phi = (phi % 2*pi)-pi
- local a = #self
- self.x, self.y = cos(phi) * a, sin(phi) * a
- return self
- end
- function vector:rotate(phi)
- local c, s = cos(phi), sin(phi)
- self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y
- return self
- end
- function vector:rotated(phi)
- return self:clone():rotate(phi)
- end
- function vector:perpendicular(a, b)
- if isVec(a) then
- local d = #self/#a
- self.x, self.y = a.y*d, -a.x*d
- return self
- elseif isNum(a) and isNum(b) then
- local d = #self/vector.len(a, b)
- self.x, self.y = b*d, -a*d
- return self
- elseif not a and not b then
- self.x, self.y = self.y, -self.x
- return self
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:perpendicular: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:perpendiculared(a, b)
- if isVec(a) then
- local d = #self/#a
- return vector(a.y*d, -a.x*d)
- elseif isNum(a) and isNum(b) then
- local d = #self/vector.len(a, b)
- return vector(b*d, -a*d)
- elseif not a and not b then
- return vector(self.y, -self.x)
- elseif type(a) == 'boolean' then
- return vector(-self.y, self.x)
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:perpendiculared: vector or two numbers or boolean expected, got: '..str, lvl)
- end
- end
- function vector:project(a, b)
- if isVec(a) then
- local s = (self.x * a.x + self.y * a.y) / (a.x * a.x + a.y * a.y)
- self.x, self.y = s * a.x, s * a.y
- return self
- elseif isNum(a) and isNum(b) then
- local s = (self.x * a + self.y * b) / (a * a + b * b)
- self.x, self.y = s * a, s * b
- return self
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:project: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:projected(a, b)
- if isVec(a) then
- local s = (self.x * a.x + self.y * a.y) / (a.x * a.x + a.y * a.y)
- return vector(s * a.x, s * a.y)
- elseif isNum(a) and isNum(b) then
- local s = (self.x * a + self.y * b) / (a * a + b * b)
- return vector(s * a, s * b)
- else
- local str = type(a)
- str = b and str..', '..type(b) or str
- error('Vector:projected: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:mirror(a, b)
- if isVec(a) then
- local s = 2 * (self.x * a.x + self.y * a.y) / (a.x * a.x + a.y * a.y)
- self.x = s * a.x - self.x
- self.y = s * a.y - self.y
- return self
- elseif isNum(a) and isNum(b) then
- local s = 2 * (self.x * a + self.y * b) / (a * a + b * b)
- self.x = s * a - self.x
- self.y = s * b - self.y
- return self
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:mirror: vector or two numbers expected, got: '..str, lvl)
- end
- end
- function vector:mirrored(a, b)
- if isVec(a) then
- local s = 2 * (self.x * a.x + self.y * a.y) / (a.x * a.x + a.y * a.y)
- return vector(s * a.x - self.x, s * a.y - self.y)
- elseif isNum(a) and isNum(b) then
- local s = 2 * (self.x * a + self.y * b) / (a * a + b * b)
- return vector(s * a - self.x, s * b - self.y)
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:mirrored: vector or two numbers expected, got: '..str, lvl)
- end
- end
- local function lerp(a, b, t) return a + (b - a) * t end
- function vector:lerp(a, b, c)
- if isVec(a) and isNum(b) then
- return self + (a - self) * b
- elseif isNum(a) and isNum(b) and isNum(c) then
- return self + (vector(a, b) - self) * c
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- if c then str = str..', '..type(c) end
- error('Vector:lerp: vector and number-dt or two numbers and dt expected, got: '..str, lvl)
- end
- end
- local function vmin(a, b) return a < b and b or a end
- local function vmax(a, b) return a > b and b or a end
- function vector:clamp(a, b)
- if isVec(a) and isVec(b) then
- return vector(vmin(vmax(self, a), b))
- else
- local str = type(a)
- if b then str = str..', '..type(b) end
- error('Vector:clamp: two vectors expected, got: '..str, lvl)
- end
- end
- local function lerp(a, b, t) return a + (b - a) * t end
- function mod(v)
- return v < 0 and -v or v
- end
- function vector:alerp(a, b, dt)
- local ca, ta = self:angle(), a:angle()
- local theta = ta - ca
- local ca = theta > pi and ca + pi*2 or theta < -pi and ca - pi*2 or ca
- return self:clone():setAngle(ca + (ta - ca) * b)
- end
- local abs = math.abs
- function vector:magnitude()
- return vector(abs(self.x), abs(self.y))
- end
- function vector:direction(other)
- return self:dot(other)/(#self * #other)
- end
- local insert = table.insert
- function vector.join(...)
- local t = {}
- for i, v in ipairs({...}) do
- if isVec(v) then
- insert(t, v.x)
- insert(t, v.y)
- else
- insert(t, v)
- end
- end
- return unpack(t)
- end
- return vector
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement