SNKRX/engine/math/vector.lua

254 lines
4.0 KiB
Lua

-- The base Vector class.
local EPSILON = 0.0001
local EPSILON_SQUARED = EPSILON*EPSILON
Vector = Object:extend()
function Vector:init(x, y)
self.x = x or 0
self.y = y or x or 0
end
function Vector:clone()
return Vector(self.x, self.y)
end
function Vector:unpack()
return self.x, self.y
end
function Vector.__tostring(self)
return "(" .. tonumber(self.x) .. ", " .. tonumber(self.y) .. ")"
end
function Vector:set(x, y)
if not y then
self.x = x.x
self.y = x.y
else
self.x = x
self.y = y
end
return self
end
function Vector:add(x, y)
if not y then
self.x = self.x + x.x
self.y = self.y + x.y
else
self.x = self.x + x
self.y = self.y + y
end
return self
end
function Vector:sub(x, y)
if not y then
self.x = self.x - x.x
self.y = self.y - x.y
else
self.x = self.x - x
self.y = self.y - y
end
return self
end
function Vector:mul(s)
if type(s) == "table" then
self.x = self.x*s.x
self.y = self.y*s.y
else
self.x = self.x*s
self.y = self.y*s
end
return self
end
function Vector:div(s)
if type(s) == "table" then
self.x = self.x*s.x
self.y = self.y*s.y
else
self.x = self.x/s
self.y = self.y/s
end
return self
end
function Vector:scale(k)
self.x = self.x*k
self.y = self.y*k
return self
end
function Vector:rotate(p, r)
local cos = math.cos(r)
local sin = math.sin(r)
local dx = self.x - p.x
local dy = self.y - p.y
self.x = dx*cos - sin*dy + p.x
self.y = sin*dx + cos*dy + p.y
return self
end
function Vector:floor()
self.x = math.floor(self.x)
self.y = math.floor(self.y)
return self
end
function Vector:ceil()
self.x = math.ceil(self.x)
self.y = math.ceil(self.y)
return self
end
function Vector:round(p)
self.x = math.round(self.x, p)
self.y = math.round(self.y, p)
return self
end
function Vector:dot(v)
return self.x*v.x + self.y*v.y
end
function Vector:is_perpendicular(v)
return math.abs(self:dot(v)) < EPSILON_SQUARED
end
function Vector:cross(v)
return self.x*v.y - self.y*v.x
end
function Vector:is_parallel(v)
return math.abs(self:cross(v)) < EPSILON_SQUARED
end
function Vector:is_set()
return self.x or self.y
end
function Vector:is_zero()
return math.abs(self.x) < EPSILON and math.abs(self.y) < EPSILON
end
function Vector:zero()
self.x = 0
self.y = 0
return self
end
function Vector:length()
return math.sqrt(self.x*self.x + self.y*self.y)
end
function Vector:length_squared()
return self.x*self.x + self.y*self.y
end
function Vector:normalize()
if self:is_zero() then return self end
return self:scale(1/self:length())
end
function Vector:perpendicular()
return Vector(-self.y, self.x)
end
function Vector:left_normal()
return Vector(self.y, -self.x)
end
function Vector:invert()
self.x = self.x*-1
self.y = self.y*-1
return self
end
function Vector:project_to(v)
local lsq = v:length_squared()
local dp = self:dot(v)
return Vector(dp*v.x/lsq, dp*v.y/lsq)
end
function Vector:truncate(max)
local s = max*max/self:length_squared()
s = (s > 1 and 1) or math.sqrt(s)
self.x = self.x*s
self.y = self.y*s
return self
end
function Vector:angle_to(v)
return math.atan2(v.y - self.y, v.x - self.x)
end
function Vector:angle()
return math.atan2(self.y, self.x)
end
function Vector:distance_squared(v)
local dx = v.x - self.x
local dy = v.y - self.y
return dx*dx + dy*dy
end
function Vector:distance(v)
return math.sqrt(self:distance_squared(v))
end
function Vector:bounce(normal, bounce_coefficient)
local d = (1 + (bounce_coefficient or 1))*self:dot(normal)
self.x = self.x - d*normal.x
self.y = self.y - d*normal.y
return self
end
function Vector:overlaps_with_circle(cx, cy, r)
return mlib.circle.checkPoint(self.x, self.y, cx, cy, r)
end
function Vector:overlaps_with_polygon(vs)
return mlib.polygon.checkPoint(self.x, self.y, vs)
end
function Vector:overlaps_with_rectangle(x, y, w, h)
return mlib.polygon.checkPoint(self.x, self.y, x - w/2, y - h/2, x + w/2, y - h/2, x + w/2, y + h/2, x - w/2, y + h/2)
end