Math
Vector and other math-related functions for PICO-8 . Saved to, say
math.lua, and imported into a .p8 file via the #include directive,
e.g., #include math.lua .
Demos
Code
-- trig
-- in pico-8, cos() and sin() take 0..1
-- instead of 0..PI*2, and sin() is inverted.
-- Let's rewire that because I prefer to work in
-- degrees.
pi=3.14159
half_pi=1.5708
two_pi=pi*2
p8cos = cos
p8sin = sin
p8atan2 = atan2
function cos(rad) return p8cos(rad/two_pi) end
function sin(rad) return p8sin(rad/two_pi) end
function atan2(dy,dx) return p8atan2(dx,dy)*two_pi end
-- degrees to radians
function d2r(d) return d*two_pi/360 end
-- radians to degrees
function r2d(r) return 180*r/pi end
--normalize angle between 2*pi and -2*pi
function two_pi_wrap(a)
if a>2*pi then
return a-2*two_pi
elseif a<-two_pi then
return a+2*two_pi
end
return a
end
--normalize angle between pi and -pi
function pi_wrap(a)
if a>pi then
return a-two_pi
elseif a<=-pi then
return a+two_pi
end
return a
end
-- vector
function vec2(x,y)
return vec:new(x,y)
end
vec = { class="vec", x=0, y=0 }
function vec:new(x,y)
o={x=x, y=y}
setmetatable(o,self)
self.__index=self
return o
end
function vec:clone()
return vec2(self.x, self.y)
end
function vec:set(x,y)
self.x = x
self.y = y
return self
end
function vec:zero(_tolerance)
local tol = _tolerance or 0
if self:magsq() <= tol then
self.x = 0
self.y = 0
end
return self
end
function vec:is_zero(_tolerance)
local tol = _tolerance or 0
return self:magsq() <= tol
end
function vec:add(arg1, _arg2)
if type(arg1) == "number" and _arg2 then
self.x += arg1
self.y += _arg2
elseif type(arg1) == "number" and not _arg2 then
self.x += arg1
self.y += arg1
elseif type(arg1) == "table" and arg1.class == "vec" then
self.x += arg1.x
self.y += arg1.y
end
return self
end
function vec:sub(arg1, _arg2)
if type(arg1) == "number" and _arg2 then
self.x -= arg1
self.y -= _arg2
elseif type(arg1) == "number" and not _arg2 then
self.x -= arg1
self.y -= arg1
elseif type(arg1) == "table" and arg1.class == "vec" then
self.x -= arg1.x
self.y -= arg1.y
end
return self
end
function vec:mul(arg1, _arg2)
self.x *= arg1
self.y *= _arg2 or arg1
return self
end
function vec:div(arg1, _arg2)
self.x /= arg1
self.y /= _arg2 or arg1
return self
end
function vec:round()
self.x = round(self.x)
self.y = round(self.y)
return self
end
function vec:magsq()
return self.x*self.x + self.y*self.y
end
function vec:mag()
return sqrt(self:magsq())
end
function vec:dist_to(other)
return self:clone():sub(other):mag()
end
function vec:equals(other, _tolerance)
local tol = _tolerance or 0
return abs(other.x - self.x) <= tol and
abs(other.y - self.y) <= tol
end
function vec:norm()
local m = self:mag()
if m == 0 then
return vec2(0, 0)
else
return self:div(m)
end
end
function vec:heading()
return atan2(self.y ,self.x)
end
function vec:rot_by(a)
local h1 = self:heading()
local h2 = h1 + a
local mag = self:mag()
self.x = cos(h2) * mag
self.y = sin(h2) * mag
return self
end
-- rotate v1 around v2
function vec:rot_around(v2, a)
-- return vadd(vrot(vsub(v1,v2),a),v2)
return self:sub(v2):rot_by(a):add(v2)
end
function vec:flr()
self.x = flr(self.x)
self.y = flr(self.y)
return self
end
function vec:lim(l)
self.x = mid(-l, self.x, l)
self.y = mid(-l, self.y, l)
return self
end
function vec:clamp(lower, upper)
self.x = mid(lower, self.x, upper)
self.y = mid(lower, self.y, upper)
return self
end
function vec:str()
return "{x="..self.x..", y="..self.y.."}"
end
function vec:draw()
line(0,0,self.x,self.y,6)
pset(self.x,self.y,8)
end
function vec:xy()
return {self.x, self.y}
end
function vec:unpack()
return self.x, self.y
end
-- nums
function mantissa(n)
return n - flr(n)
end
function round(n)
return flr(n+0.5)
end
function pow(a,b)
local res = a
for i=1,b-1 do
res *= a
end
return res
end
function rnd2(x0, x1)
-- return random float between
-- x0 inclusive and x1 inclusive
return x0 + rnd(x1 - x0 + 1)
end
function rint(x0, x1)
-- return random int between
-- x0 inclusive and x1 inclusive
return x0 + flr(rnd(x1 - x0 + 1))
end
function rnd2(t,_reject)
local res = rnd(t)
if not _reject then
return res
else
while res == _reject do
res = rnd(t)
end
return res
end
end
function nmap(i, imin, imax, omin, omax)
-- map n, ranging from imin to imax, to between omin and omax
return omin + (omax - omin) * ((i - imin) / (imax - imin))
end
function pmap(i, ipivot, olower, oequal, ohigher)
-- return olower if i < ipivot, etc...
if i==ipivot then
return oequal
elseif i<ipivot then
return olower
elseif i>ipivot then
return ohigher
else
return nil
end
end
function sum(vals)
local res = 0
for v in all(vals) do
res += v
end
return res
end
function avg(vals)
if #vals == 0 then return 0 end
return sum(vals) / #vals
end
function num2bin(n)
local bin = ""
for i=1,16 do
bin = n %2\1 ..bin
n>>>=1
end
return "0b"..bin
end