Math
Vector and other, math-related functions for PICO-8 .
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