start of vec2 friendly api - will settle on something here before touching vec3

This commit is contained in:
Max Cahill 2021-07-23 10:58:21 +10:00
parent 1a8769fc22
commit cb35811b8c

324
vec2.lua
View File

@ -16,17 +16,19 @@ function vec2:__tostring()
return ("(%.2f, %.2f)"):format(self.x, self.y) return ("(%.2f, %.2f)"):format(self.x, self.y)
end end
--probably-too-flexible ctor --ctor
function vec2:new(x, y) function vec2:new(x, y)
if type(x) == "number" or type(x) == "nil" then --0 init by default
self:sset(x or 0, y) self:scalar_set(0)
if type(x) == "number" then
self:scalar_set(x, y)
elseif type(x) == "table" then elseif type(x) == "table" then
if type(x.type) == "function" and x:type() == "vec2" then if type(x.type) == "function" and x:type() == "vec2" then
self:vset(x) self:vector_set(x)
elseif x[1] then elseif x[1] then
self:sset(x[1], x[2]) self:scalar_set(x[1], x[2])
else else
self:sset(x.x, x.y) self:scalar_set(x.x, x.y)
end end
end end
end end
@ -40,8 +42,12 @@ function vec2:xy(x, y)
return vec2(x, y) return vec2(x, y)
end end
function vec2:polar(length, angle)
return vec2(length, 0):rotatei(angle)
end
function vec2:filled(v) function vec2:filled(v)
return vec2(v) return vec2(v, v)
end end
function vec2:zero() function vec2:zero()
@ -63,19 +69,19 @@ make_pooled(vec2, 128)
--get a pooled copy of an existing vector --get a pooled copy of an existing vector
function vec2:pooled_copy() function vec2:pooled_copy()
return vec2:pooled():vset(self) return vec2:pooled():vector_set(self)
end end
--modify --modify
function vec2:sset(x, y) function vec2:scalar_set(x, y)
if not y then y = x end if not y then y = x end
self.x = x self.x = x
self.y = y self.y = y
return self return self
end end
function vec2:vset(v) function vec2:vector_set(v)
self.x = v.x self.x = v.x
self.y = v.y self.y = v.y
return self return self
@ -83,8 +89,8 @@ end
function vec2:swap(v) function vec2:swap(v)
local sx, sy = self.x, self.y local sx, sy = self.x, self.y
self:vset(v) self:vector_set(v)
v:sset(sx, sy) v:scalar_set(sx, sy)
return self return self
end end
@ -116,108 +122,74 @@ end
--arithmetic --arithmetic
----------------------------------------------------------- -----------------------------------------------------------
--immediate mode
--vector --vector
function vec2:vaddi(v) function vec2:vector_add(v)
self.x = self.x + v.x self.x = self.x + v.x
self.y = self.y + v.y self.y = self.y + v.y
return self return self
end end
function vec2:vsubi(v) function vec2:vector_sub(v)
self.x = self.x - v.x self.x = self.x - v.x
self.y = self.y - v.y self.y = self.y - v.y
return self return self
end end
function vec2:vmuli(v) function vec2:vector_mul(v)
self.x = self.x * v.x self.x = self.x * v.x
self.y = self.y * v.y self.y = self.y * v.y
return self return self
end end
function vec2:vdivi(v) function vec2:vector_div(v)
self.x = self.x / v.x self.x = self.x / v.x
self.y = self.y / v.y self.y = self.y / v.y
return self return self
end end
--alias; we're a vector library so arithmetic defaults to vector
vec2.add = vec2.vector_add
vec2.sub = vec2.vector_sub
vec2.mul = vec2.vector_mul
vec2.div = vec2.vector_div
--scalar --scalar
function vec2:saddi(x, y) function vec2:scalar_add(x, y)
if not y then y = x end if not y then y = x end
self.x = self.x + x self.x = self.x + x
self.y = self.y + y self.y = self.y + y
return self return self
end end
function vec2:ssubi(x, y) function vec2:scalar_sub(x, y)
if not y then y = x end if not y then y = x end
self.x = self.x - x self.x = self.x - x
self.y = self.y - y self.y = self.y - y
return self return self
end end
function vec2:smuli(x, y) function vec2:scalar_mul(x, y)
if not y then y = x end if not y then y = x end
self.x = self.x * x self.x = self.x * x
self.y = self.y * y self.y = self.y * y
return self return self
end end
function vec2:sdivi(x, y) function vec2:scalar_div(x, y)
if not y then y = x end if not y then y = x end
self.x = self.x / x self.x = self.x / x
self.y = self.y / y self.y = self.y / y
return self return self
end end
--garbage mode --(a + (b * t))
--useful for integrating physics and adding directional offsets
function vec2:vadd(v) function vec2:fused_multiply_add(v, t)
return self:copy():vaddi(v)
end
function vec2:vsub(v)
return self:copy():vsubi(v)
end
function vec2:vmul(v)
return self:copy():vmuli(v)
end
function vec2:vdiv(v)
return self:copy():vdivi(v)
end
function vec2:sadd(x, y)
return self:copy():saddi(x, y)
end
function vec2:ssub(x, y)
return self:copy():ssubi(x, y)
end
function vec2:smul(x, y)
return self:copy():smuli(x, y)
end
function vec2:sdiv(x, y)
return self:copy():sdivi(x, y)
end
--fused multiply-add (a + (b * t))
function vec2:fmai(v, t)
self.x = self.x + (v.x * t) self.x = self.x + (v.x * t)
self.y = self.y + (v.y * t) self.y = self.y + (v.y * t)
return self return self
end end
function vec2:fma(v, t)
return self:copy():fmai(v, t)
end
----------------------------------------------------------- -----------------------------------------------------------
-- geometric methods -- geometric methods
----------------------------------------------------------- -----------------------------------------------------------
@ -240,31 +212,31 @@ function vec2:distance(other)
return math.sqrt(self:distance_squared(other)) return math.sqrt(self:distance_squared(other))
end end
--immediate mode function vec2:normalise_both()
function vec2:normalisei_both()
local len = self:length() local len = self:length()
if len == 0 then if len == 0 then
return self, 0 return self, 0
end end
return self:sdivi(len), len return self:scalar_div(len), len
end end
function vec2:normalisei() function vec2:normalise()
local v, len = self:normalisei_both() local v, len = self:normalise_both()
return v return v
end end
function vec2:normalisei_len() function vec2:normalise_len()
local v, len = self:normalisei_both() local v, len = self:normalise_both()
return len return len
end end
function vec2:inversei() function vec2:inverse()
return self:smuli(-1) return self:scalar_mul(-1)
end end
function vec2:rotatei(angle) -- angle/direction specific
function vec2:rotate(angle)
local s = math.sin(angle) local s = math.sin(angle)
local c = math.cos(angle) local c = math.cos(angle)
local ox = self.x local ox = self.x
@ -274,7 +246,15 @@ function vec2:rotatei(angle)
return self return self
end end
function vec2:rot90ri() function vec2:rotate_around(angle, pivot)
self:vector_sub(pivot)
self:rotate(angle)
self:vector_add(pivot)
return self
end
--fast quarter/half rotations
function vec2:rot90r()
local ox = self.x local ox = self.x
local oy = self.y local oy = self.y
self.x = -oy self.x = -oy
@ -282,7 +262,7 @@ function vec2:rot90ri()
return self return self
end end
function vec2:rot90li() function vec2:rot90l()
local ox = self.x local ox = self.x
local oy = self.y local oy = self.y
self.x = oy self.x = oy
@ -290,51 +270,8 @@ function vec2:rot90li()
return self return self
end end
vec2.rot180i = vec2.inversei --alias
function vec2:rotate_aroundi(angle, pivot)
self:vsubi(pivot)
self:rotatei(angle)
self:vaddi(pivot)
return self
end
--garbage mode
function vec2:normalised()
return self:copy():normalisei()
end
function vec2:normalised_len()
local v = self:copy()
local len = v:normalisei_len()
return v, len
end
function vec2:inverse()
return self:copy():inversei()
end
function vec2:rotate(angle)
return self:copy():rotatei(angle)
end
function vec2:rot90r()
return self:copy():rot90ri()
end
function vec2:rot90l()
return self:copy():rot90li()
end
vec2.rot180 = vec2.inverse --alias vec2.rot180 = vec2.inverse --alias
function vec2:rotate_around(angle, pivot)
return self:copy():rotate_aroundi(angle, pivot)
end
-- angle/direction specific
--get the angle of this vector relative to (1, 0) --get the angle of this vector relative to (1, 0)
function vec2:angle() function vec2:angle()
return math.atan2(self.y, self.x) return math.atan2(self.y, self.x)
@ -347,180 +284,125 @@ end
--lerp towards the direction of a provided vector --lerp towards the direction of a provided vector
--(length unchanged) --(length unchanged)
function vec2:lerp_directioni(v, t)
return self:rotatei(self:angle_difference(v) * t)
end
function vec2:lerp_direction(v, t) function vec2:lerp_direction(v, t)
return self:copy():lerp_directioni(v, t) return self:rotate(self:angle_difference(v) * t)
end end
----------------------------------------------------------- -----------------------------------------------------------
-- per-component clamping ops -- per-component clamping ops
----------------------------------------------------------- -----------------------------------------------------------
function vec2:mini(v) function vec2:min(v)
self.x = math.min(self.x, v.x) self.x = math.min(self.x, v.x)
self.y = math.min(self.y, v.y) self.y = math.min(self.y, v.y)
return self return self
end end
function vec2:maxi(v) function vec2:max(v)
self.x = math.max(self.x, v.x) self.x = math.max(self.x, v.x)
self.y = math.max(self.y, v.y) self.y = math.max(self.y, v.y)
return self return self
end end
function vec2:clampi(min, max) function vec2:clamp(min, max)
self.x = math.clamp(self.x, min.x, max.x) self.x = math.clamp(self.x, min.x, max.x)
self.y = math.clamp(self.y, min.y, max.y) self.y = math.clamp(self.y, min.y, max.y)
return self return self
end end
function vec2:min(v)
return self:copy():mini(v)
end
function vec2:max(v)
return self:copy():maxi(v)
end
function vec2:clamp(min, max)
return self:copy():clampi(min, max)
end
----------------------------------------------------------- -----------------------------------------------------------
-- absolute value -- absolute value
----------------------------------------------------------- -----------------------------------------------------------
function vec2:absi() function vec2:abs()
self.x = math.abs(self.x) self.x = math.abs(self.x)
self.y = math.abs(self.y) self.y = math.abs(self.y)
return self return self
end end
function vec2:abs()
return self:copy():absi()
end
----------------------------------------------------------- -----------------------------------------------------------
-- sign -- sign
----------------------------------------------------------- -----------------------------------------------------------
function vec2:signi() function vec2:sign()
self.x = math.sign(self.x) self.x = math.sign(self.x)
self.y = math.sign(self.y) self.y = math.sign(self.y)
return self return self
end end
function vec2:sign()
return self:copy():signi()
end
----------------------------------------------------------- -----------------------------------------------------------
-- truncation/rounding -- truncation/rounding
----------------------------------------------------------- -----------------------------------------------------------
function vec2:floori() function vec2:floor()
self.x = math.floor(self.x) self.x = math.floor(self.x)
self.y = math.floor(self.y) self.y = math.floor(self.y)
return self return self
end end
function vec2:ceili() function vec2:ceil()
self.x = math.ceil(self.x) self.x = math.ceil(self.x)
self.y = math.ceil(self.y) self.y = math.ceil(self.y)
return self return self
end end
function vec2:roundi() function vec2:round()
self.x = math.round(self.x) self.x = math.round(self.x)
self.y = math.round(self.y) self.y = math.round(self.y)
return self return self
end end
function vec2:floor()
return self:copy():floori()
end
function vec2:ceil()
return self:copy():ceili()
end
function vec2:round()
return self:copy():roundi()
end
----------------------------------------------------------- -----------------------------------------------------------
-- interpolation -- interpolation
----------------------------------------------------------- -----------------------------------------------------------
function vec2:lerpi(other, amount) function vec2:lerp(other, amount)
self.x = math.lerp(self.x, other.x, amount) self.x = math.lerp(self.x, other.x, amount)
self.y = math.lerp(self.y, other.y, amount) self.y = math.lerp(self.y, other.y, amount)
return self return self
end end
function vec2:lerp(other, amount) function vec2:lerp_eps(other, amount, eps)
return self:copy():lerpi(other, amount)
end
function vec2:lerp_epsi(other, amount, eps)
self.x = math.lerp_eps(self.x, other.x, amount, eps) self.x = math.lerp_eps(self.x, other.x, amount, eps)
self.y = math.lerp_eps(self.y, other.y, amount, eps) self.y = math.lerp_eps(self.y, other.y, amount, eps)
return self return self
end end
function vec2:lerp_eps(other, amount, eps)
return self:copy():lerp_epsi(other, amount, eps)
end
----------------------------------------------------------- -----------------------------------------------------------
-- vector products and projections -- vector products and projections
----------------------------------------------------------- -----------------------------------------------------------
function vec2.dot(a, b) function vec2:dot(other)
return a.x * b.x + a.y * b.y return self.x * other.x + self.y * other.y
end end
--"fake", but useful - also called the wedge product apparently --"fake", but useful - also called the wedge product apparently
function vec2.cross(a, b) function vec2:cross(other)
return a.x * b.y - a.y * b.x return self.x * other.y - self.y * other.x
end end
--scalar projection a onto b function vec2:scalar_projection(other)
function vec2.sproj(a, b) local len = other:length()
local len = b:length()
if len == 0 then if len == 0 then
return 0 return 0
end end
return a:dot(b) / len return self:dot(other) / len
end end
--vector projection a onto b (writes into a) function vec2:vector_projection(other)
function vec2.vproji(a, b) local div = other:dot(other)
local div = b:dot(b)
if div == 0 then if div == 0 then
return a:sset(0,0) return self:scalar_set(0)
end end
local fac = a:dot(b) / div local fac = self:dot(other) / div
return a:vset(b):smuli(fac) return self:vector_set(other):scalar_muli(fac)
end end
function vec2.vproj(a, b) function vec2:vector_rejection(o)
return a:copy():vproji(b) local tx, ty = self.x, self.y
end self:vector_proji(other)
self:scalar_set(tx - self.x, ty - self.y)
--vector rejection a onto b (writes into a) return self
function vec2.vreji(a, b)
local tx, ty = a.x, a.y
a:vproji(b)
a:sset(tx - a.x, ty - a.y)
return a
end
function vec2.vrej(a, b)
return a:copy():vreji(b)
end end
--get the winding side of p, relative to the line a-b --get the winding side of p, relative to the line a-b
@ -540,19 +422,19 @@ end
----------------------------------------------------------- -----------------------------------------------------------
--"physical" friction --"physical" friction
local _v_friction = vec2:zero() --avoid alloc local _v_friction = vec2() --avoid alloc
function vec2:apply_friction(mu, dt) function vec2:apply_friction(mu, dt)
_v_friction:vset(self):smuli(mu * dt) _v_friction:vector_set(self):scalar_muli(mu * dt)
if _v_friction:length_squared() > self:length_squared() then if _v_friction:length_squared() > self:length_squared() then
self:sset(0, 0) self:scalar_set(0, 0)
else else
self:vsubi(_v_friction) self:vector_subi(_v_friction)
end end
return self return self
end end
--"gamey" friction in one dimension --"gamey" friction in one dimension
local function apply_friction_1d(v, mu, dt) local function _friction_1d(v, mu, dt)
local friction = mu * v * dt local friction = mu * v * dt
if math.abs(friction) > math.abs(v) then if math.abs(friction) > math.abs(v) then
return 0 return 0
@ -563,8 +445,8 @@ end
--"gamey" friction in both dimensions --"gamey" friction in both dimensions
function vec2:apply_friction_xy(mu_x, mu_y, dt) function vec2:apply_friction_xy(mu_x, mu_y, dt)
self.x = apply_friction_1d(self.x, mu_x, dt) self.x = _friction_1d(self.x, mu_x, dt)
self.y = apply_friction_1d(self.y, mu_y, dt) self.y = _friction_1d(self.y, mu_y, dt)
return self return self
end end
@ -578,7 +460,7 @@ function vec2:maxcomp()
end end
-- mask out min component, with preference to keep x -- mask out min component, with preference to keep x
function vec2:majori() function vec2:major()
if self.x > self.y then if self.x > self.y then
self.y = 0 self.y = 0
else else
@ -587,7 +469,7 @@ function vec2:majori()
return self return self
end end
-- mask out max component, with preference to keep x -- mask out max component, with preference to keep x
function vec2:minori() function vec2:minor()
if self.x < self.y then if self.x < self.y then
self.y = 0 self.y = 0
else else
@ -596,14 +478,24 @@ function vec2:minori()
return self return self
end end
--garbage generating functions that return a new vector rather than modifying self
for _, v in ipairs({
--garbage generating versions }) do
function vec2:major(axis) vec2[name] = function(self, ...)
return self:copy():majori(axis) self = self:copy()
self[v](self, ...)
end
end end
function vec2:minor(axis) --"hungarian" shorthand aliases
return self:copy():minori(axis) for _, v in ipairs({
{"saddi", "scalar_add"},
{"sadd", "scalar_add_copy"},
}) do
local shorthand, original = v[1], v[2]
vec2[shorthand] = vec2[original]
end end
return vec2 return vec2