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

Date: 2024-04-08 Mon 07:49

Author: Alexandre Rousseau

Created: 2024-05-27 Mon 22:28

Validate