


function math.round(n, deci) deci = 10^(deci or 0) return math.floor(n*deci+.5)/deci end
function math.zero(value)	local newVal=0; if(value < 0) then newVal=0 else newVal=value end return newVal end
function math.angle(x1,y1, x2,y2) return math.atan2( y2-y1,x2-x1) end
function math.dist(x1,y1, x2,y2) return  ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))^0.5 end
function math.dist3(x1,y1,z1, x2,y2,z2) return ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1))^0.5 end
function math.sdist(x1,y1, x2,y2) return (x2-x1)^2+(y2-y1)^2 end 
function math.ddist(xd,yd) return (xd^2+yd^2)^0.5 end
function math.sign(n) return n>0 and 1 or n<0 and -1 or 0 end
function mf(num) return (type(num)=="number") and math.floor(num) or num end
function mu(num) return (type(num)=="number") and math.ceil(num) or num end
function mr(num) return (type(num)=="number") and math.round(num)or num  end 
function math.clamp(low, high,n) return math.min(math.max(n, low), high) end
function mc(low,n,high) return math.clamp(low, high, n) end
zero = math.zero
function capFirst(s)
	return string.upper(s:sub(1,1))..s:sub(2)
end
function angDiff (a,b)
    local diff = ( b - a + pi ) % (pi*2) - pi;
    return diff < -pi and diff + pi*2  or  diff;
end

function inspect2(t)
	
end

function returnValue(val)
  return val
end

function delayedCall(func,time)
  local x = {y=0}
  effects.tween(x,{y=1},time,"linear",1,func)
end

function waitFor(func,...)
  table.insert(waiting, func)
end

function pollWaits()
  for w,wait in lume.ripairs(waiting) do 
    if wait() then 
      table.remove(waiting,w)
    end 
  end
end

function findString(str,s)
	local t = {}                   -- table to store the indices
  local i = 0
  while true do
    i = string.find(str, s, i+1)    -- find 'next' newline
    if i == nil then break end
    table.insert(t, i)
  end
end

function toArray(t,array)
  lume.clear(array)
  for k,v in pairs(t) do
    table.insert(array,v)
  end

  return array
end

function triangleArea (a,b,c)
  local s = (a + b + c) / 2  
  return  (s*(s-a)*(s-b)*(s-c)) ^ 0.5  
end

function setTable(t,k1,v1, k2,v2, k3,v3, k4,v4, k5,v5, k6,v6, k7,v7, k8,v8, k9,v9, k10,v10, k11,v11, k12,v12, k13,v13, k14,v14, k15,v15, k16,v16) 
	if k1 then t[k1]=v1 end
	if k2 then t[k2]=v2 end
	if k3 then t[k3]=v3 end
	if k4 then t[k4]=v4 end
	if k5 then t[k5]=v5 end
	if k6 then t[k6]=v6 end
	if k7 then t[k7]=v7 end
	if k8 then t[k8]=v8 end
	if k9 then t[k9]=v9 end
	if k10 then t[k10]=v10 end
	if k11 then t[k11]=v11 end
	if k12 then t[k12]=v12 end
	if k13 then t[k13]=v13 end
	if k14 then t[k14]=v14 end
	if k15 then t[k15]=v15 end
	if k16 then t[k16]=v16 end
	return t
end


function wrap(a,n,b)
	return 	n<a and	b-(n-a)-1 or (n>b and a+(b-n)+1 or n)
end

function wrap2(a,n,b)
	return n>b and wrap2(a,n-b,b) or (n<a and wrap2(a,n+b,b) or n)
end
	
function sortBy(t,...)
	local props = {...}
	local iter = {}
	for s,set in pairs(t) do 
		table.insert(iter,set)
	end
	return lume.sort(iter, function(a,b) return sorter(a,b,props,1) end)
end


function sorter(a,b,props,i)
	if a[props[i]]==b[props[i]] and i<#props then 
		return sorter(a,b,props,i+1)
	else
		if type(a[props[i]])=="boolean" then 
			return (not a[props[i]] and b[props[i]])
		else
			return a[props[i]]<b[props[i]]
		end
	end
end

function setVec(v,vx,vy,vz)
	local x,y,z
	if vy then 
		x,y,z = vx,vy,vz
	else
		x,y,z = vx.x or vx[1],vx.y or vx[2],vx.z or vx[3]
	end
	if v.x then 
		v.x,v.y,v.z=x,y,z
	else
		v[1],v[2],v[3]=x,y,z
	end
end


function clearTable( t ,n)
	n=(n or 0)+1
	if n>64 then return end
  for k,v in pairs(t) do
    if type(v) == 'table' then
        clearTable( v ,n)
     end
     	t[k] = nil
   end
   t=nil
end

function clearTail(t,n)
  local n = n or 1
	for i=n,#t,1 do 
    t[i]=nil
	end
end

table.clear = function(t,n)
	for i=#t,n or 1,-1 do 
		t[i]=nil
	end
end

function getLooped(t,n)
	return t[wrap2(1,n,#t)]
end

function reverse(t)
	local rt = {}
	for i,v in lume.ripairs(t) do
		table.insert(rt,v)
	end
	return rt
end

function addrem(t,v)
		local find = lume.find(t,v)
		if find then 
			table.remove(t,find)
		else 
			table.add(t,v)
		end
	end

function toLine2D(l1)
	local l2 = {}
	for i,v in ipairs(l1) do
		table.insert(l2,v.x or v[1])
		table.insert(l2,v.y or v[2])
	end
	return l2
end

function toLine2Dz(l1)
	local l2 = {}
	for i,v in ipairs(l1) do
		table.insert(l2,v.x or v[1])
		table.insert(l2,v.z or v[2])
	end
	return l2
end

function toLine3D(l1)
	local l2 = {}
	for i,v in ipairs(l1) do
		table.insert(l2,v.x or v[1])
		table.insert(l2,v.y or v[2])
		table.insert(l2,v.z or v[3])
	end
	return l2
end

function bboxLine(line)
	local bbox = {line[1],line[2],line[3],line[4]}
	for i,b in ipairs(line) do 
		if i%2==0 then 
			bbox[2] = (b<bbox[2]) and b or bbox[2]
			bbox[4] = (b>bbox[4]) and b or bbox[4]
		else 
			bbox[1] = (b<bbox[1]) and b or bbox[1]
			bbox[3] = (b>bbox[3]) and b or bbox[3]
		end
	end
	bbox.w = bbox[3]-bbox[1]
	bbox.h = bbox[4]-bbox[2]
	return bbox
end

function bboxCollide1(box1,box2)
	local t1,l1,b1,r1 = box1[1],box1[2],box1[3],box1[4]
	local t2,l2,b2,r2 = box2[1],box2[2],box2[3],box2[4]
	return 	l1 < r2 and
		 	l2 < r1 and
		 	t1 < b2 and
		 	t2 < b1
end


function pointInBB(p,b)
	local t1,l1 	  = p.x or p[1],p.y or p[2]
	local t2,l2,b2,r2 = b.t or b[1],b.l or b[2],b.b or b[3],b.r or b[4]
	return 	l1 < r2 and
		 	l2 < l1 and
		 	t1 < b2 and
		 	t2 < t1
end

function frandom(x)
	return wrap(0,fr(x+10)-fr(x+5)*0.2,1)
end

function fr(x)
	return wrap(0,math.sin(100*x^2)*math.cos(x^4)*math.sin((8*x)^2)*math.cos(8*x^3)*3+0.5,1)
end

function _time(tag,t)
	LAST_TIME=LAST_TIME or ltimer.getTime()
	if tag then 
		local thing = t and trace or print
		thing("time:",tag,"      ",(t~=nil and t==false) and "" or 60*round(ltimer.getTime()-LAST_TIME,4))
	end
	LAST_TIME=ltimer.getTime()
end

function splice(t,first,last,t2)
	if first>#t then return end 
	local t2 = t2 or {}
	for i=first or 1,last or #t,1 do 
		table.insert(t2,t[i])
	end
	return t2
end

function math.sling(a,n,b,s)
	local diff = (n>b) and 1  or  ((n<a) and (1+math.abs(n-a))*1.5  or 1)
	return (math.abs(n-a)<0.001 or math.abs(n-b)<0.001) and n or (easeTo(n2 or n,(n<a) and a or ((n>b) and b or n),s/diff))
end
sling = hiSpec and mc or math.sling
-- sling = mc

function sling0(a,n,b,s)
	return easeTo(n,(n<0) and 0 or ((n>b) and b or n),s)
end

function concat(t1,t2)
  for i=1,#t2,1 do
    t1[#t1+1]=t2[i]
  end
end

function prepend(t1,t2)
  for i=#t2,1,-1 do
    table.insert(t1,1,t2[i])
  end
end

function classicConcat(t,t2,pre,fast)
	local pre = pre or false
	if pre then 
		for i, v in ipairs(t2) do
			table.insert(t,1,v)
		end
	else
		if fast then 
			for i=1,#t2,1 do
				t[#t+1]=t2[i]
			end
		else
			for i, v in ipairs(t2) do
				table.insert(t,v)
			end
		end
	end
	return t
end

function table.add(t,v)
  if not lume.find(t, v ) then
		table.insert(t,v)
	end
end



function ellipses(string,width,always)
	local string = string or ""
	if currentFont and pcall(currentFont.getWidth,currentFont,string) and string:len()>3 and string~="···" then 
		local limit = 2
		local w
		for i=1,string:len(),1 do
			local thing = string:sub(1,i)
			if pcall(currentFont.getWidth,currentFont,thing) and currentFont:getWidth(thing)<width  then
				limit = i
			end
		end
		return (string:sub(1, mc(1,limit,string:len())) or "")..(string:len()>limit and "…" or (always and "…" or ""))
	else
		return string or ""
	end
end


function returnColor(t)
	return t.r,t.g,t.b
end

function contrast(col,n)
	local avg = (col.r+col.b+col.g)/3
	local newCol = {
	r = col.r+(col.r-avg)*-(1-n),
	g = col.g+(col.g-avg)*-(1-n),
	b = col.b+(col.b-avg)*-(1-n)
}
	return newCol
end

function inTable(t,v)
	for i=1,#t,1 do 
		if t[i]==v then 
			return true 
		end
	end
	return false
end

function mergeIndexed(t1,t2)
	--[[local t3 = shallowcopy(t1)
	for i,v in ipairs(t2) do
		if #t1==0 or not multicheckOR(v,t1) then
			table.insert(t3,v)
		end
	end]]
	local t3 = shallowcopy(t1)
	for i,v in ipairs(t2) do
		table.add(t3,v)
	end
	return t3
end

function simpleSame(a,b)
	if lume.count(a)~=lume.count(b) then return false end
	for k,v in pairs(a) do 
		if b[k]~=v then 
			return false
		end
	end
	return true
end

function multicheckOR(a,b,v) -- Returns true if value a is in table b
	local rval  
	for _,m in pairs(b) do 
		if m == a or (v~=nil and type(m)=="table" and m[v] == a) then 
			rval=_ 
		end
	end
	return rval
end

function multicheckAND(a,b,v)  -- Returns true if all values of table b equal a
	local rval = true 
	for i=1, #b do 
		if b[i] ~= a then 
			rval=false 
		end
	end
	return rval
end


function roundToNearest( number, interval, offset )
  offset   = offset   or 0  -- default value
  interval = interval or 1  -- default value
  return math.floor( (number-offset)/interval + 0.5 ) * interval + offset
end

function mean( t , val )
	local sum = 0
	local count= 0
	for k,v in pairs(t) do

		if val~=nil and type(v[val]) == 'number' then
			sum = sum + v[val]
			count = count + 1
		elseif type(v) == 'number' then
			sum = sum + v
			count = count + 1
		end

	end
	return (sum / count)
end

function sum( t , val )
	local sum = 0

	for k,v in pairs(t) do
		if val and type(v[val]) == 'number' then
			sum = sum + v[val]
		elseif type(v) == 'number' then
			sum = sum + v
		end
	end
	return sum
end

function setColorHex(hexc,a)
	local a = a or 1 
	local col = hex2rgb(hexc)
	gfx.setColor(col.r,col.g,col.b,1)
end

function round(num, idp)
	local idp = idp or 2
	local mult = 10^(idp or 2)
	return math.floor(num * mult + 0.5) / mult
end

function tableToString(t)
	local str = ""
	for i,v in ipairs(t) do
		str=str..v
	end
	return str
end

function rmSpaces(string)
	local str = string:gsub("%s+", "")
	return str
end



function stringUUID(id)
	local id = id or simpleuuid()
	return ( (id:sub(1,1) ~= "u") and "u" or "")..string.gsub(id,"-","")
end

function numUUID(id)
	return tonumber(simpleuuid())
end

uuid = stringUUID


function lineOnTri(pt1,pt2,tri)
	local pt1 = {x=mf(pt1.x),y=mf(pt1.y)}
	local pt2 = {x=mf(pt2.x),y=mf(pt2.y)}
	return	( 	(	( pt1.x == mf( tri[1] ) and pt1.y == mf( tri[2] ) )  and  ( pt2.x == mf( tri[3] ) and pt2.y == mf( tri[4] ) )	)  
		or 		(	( pt1.x == mf( tri[3] ) and pt1.y == mf( tri[4] ) )  and  ( pt2.x == mf( tri[1] ) and pt2.y == mf( tri[2] ) )	)  
		or 		(	( pt1.x == mf( tri[5] ) and pt1.y == mf( tri[6] ) )  and  ( pt2.x == mf( tri[1] ) and pt2.y == mf( tri[2] ) )	)
		or		(	( pt1.x == mf( tri[1] ) and pt1.y == mf( tri[2] ) )  and  ( pt2.x == mf( tri[5] ) and pt2.y == mf( tri[6] ) )	)  
		or 		(	( pt1.x == mf( tri[3] ) and pt1.y == mf( tri[4] ) )  and  ( pt2.x == mf( tri[5] ) and pt2.y == mf( tri[6] ) )	)  
		or 		(	( pt1.x == mf( tri[5] ) and pt1.y == mf( tri[6] ) )  and  ( pt2.x == mf( tri[3] ) and pt2.y == mf( tri[4] ) )	) )
end

function unpackColor(c)
  if type(c)=="table" then 
    if c.r then 
      return c.r,c.g,c.b,c.a
    else
      return c[1],c[2],c[3],c[4]
    end
  else 
    return 1,1,1,1
  end
end

function setAll(x,n)
	local t = {}
	for i=1,n or 99,1 do 
		t[i]=x
	end
  return unpack(t)
end


function intersect(L1X1,L1Y1,L1X2,L1Y2, L2X1,L2Y1,L2X2,L2Y2)
  if   (L1X1 == L2X1 and L1Y1 == L2Y1 )
    or (L1X2 == L2X1 and L1Y2 == L2Y1 )
    or (L1X1 == L2X2 and L1Y1 == L2Y2 )
    or (L1X2 == L2X2 and L1Y2 == L2Y2 )
    then 
      return false
  end
  local d = (L2Y2 - L2Y1) * (L1X2 - L1X1) - (L2X2 - L2X1) * (L1Y2 - L1Y1)
  if (d == 0) then
    return false
  else
    local ua = ( (L2X2 - L2X1) * (L1Y1 - L2Y1) - (L2Y2 - L2Y1) * (L1X1 - L2X1) ) / d
    local ub = ( (L1X2 - L1X1) * (L1Y1 - L2Y1) - (L1Y2 - L1Y1) * (L1X1 - L2X1) ) / d

    if (ua >= 0 and ua <= 1 and ub >= 0 and ub <= 1) then
      return  L1X1 + (ua * (L1X2 - L1X1)), L1Y1 + (ua * (L1Y2 - L1Y1))
    end
    return false
  end
end

function intersect2(L1X1,L1Y1,L1X2,L1Y2, L2X1,L2Y1,L2X2,L2Y2)
  if   (L1X1 == L2X1 and L1Y1 == L2Y1 )
    or (L1X2 == L2X1 and L1Y2 == L2Y1 )
    or (L1X1 == L2X2 and L1Y1 == L2Y2 )
    or (L1X2 == L2X2 and L1Y2 == L2Y2 )
    then 
      return false
  end
  local d = (L2Y2 - L2Y1) * (L1X2 - L1X1) - (L2X2 - L2X1) * (L1Y2 - L1Y1)
  if (d == 0) then
    return false
  else
    local ua = ( (L2X2 - L2X1) * (L1Y1 - L2Y1) - (L2Y2 - L2Y1) * (L1X1 - L2X1) ) / d
    local ub = ( (L1X2 - L1X1) * (L1Y1 - L2Y1) - (L1Y2 - L1Y1) * (L1X1 - L2X1) ) / d

    if (ua >= 0 and ua <= 1 and ub >= 0 and ub <= 1) then
      return  true
    end
    return false
  end
end


local intersections,closest = {}
local intersected,intersection 
function math.intersectLines(l1,l2,loop,n)
	intersections = {}
	closest = {}
	local minDist,dist = 9999
	for i=1, #l1-(loop and 0 or 2), 2 do 
		for j=1, #l2-(loop and 0 or 2), 2 do 
			dist = math.dist(l1[i], l1[i+1],l2[j], l2[j+1])
			if dist<minDist then 
				minDist = dist 
				closest = {i=i,j=j}
			end
			intersected,intersection = math.intersect ( 
				l1[i], l1[i+1], l1[i>#l1-3 and 1 or i+2], l1[i>#l1-3 and 2 or i+3], 
				l2[j], l2[j+1], l2[j>#l2-3 and 1 or j+2], l2[j>#l2-3 and 2 or j+3]
			)
			if intersected then 
				intersection.i1 = {x=l1[i], 		y=l1[i+1], i=i}
				intersection.i2 = {x=l1[i+2], 	y=l1[i+3], i=i+2}
				intersection.j1 = {x=l2[j], 		y=l2[j+1], i=j}
				intersection.j2 = {x=l2[j+2], 	y=l2[j+3], i=j+2}
				intersection.n = n
				table.insert(intersections,intersection)
			else
			end
		end
	end
	return intersections,closest
end


function copyVerts(verts)
	local t = {}
	for v,vert in ipairs(verts)do
		table.insert(t,vec3(vert.x,vert.y,vert.z))
	end
	return t
end







------------------------------------------------------------------------------

-- RANDOM SHIT I DIDN'T WRITE

------------------------------------------------------------------------------

--[[
   table.binsearch( table, value [, compval [, reversed] ] )
   
   Searches the table through BinarySearch for the given value.
   If the  value is found:
      it returns a table holding all the mathing indices (e.g. { startindice,endindice } )
      endindice may be the same as startindice if only one matching indice was found
   If compval is given:
      then it must be a function that takes one value and returns a second value2,
      to be compared with the input value, e.g.:
      compvalue = function( value ) return value[1] end
   If reversed is set to true:
      then the search assumes that the table is sorted in reverse order (largest value at position 1)
      note when reversed is given compval must be given as well, it can be nil/_ in this case
   Return value:
      on success: a table holding matching indices (e.g. { startindice,endindice } )
      on failure: nil
]]--
do
   -- Avoid heap allocs for performance
   local default_fcompval = function( value ) return value end
   local fcompf = function( a,b ) return a < b end
   local fcompr = function( a,b ) return a > b end
   function table.binsearch( tbl,value,fcompval,reversed )
      -- Initialise functions
      local fcompval = fcompval or default_fcompval
      local fcomp = reversed and fcompr or fcompf
      --  Initialise numbers
      local iStart,iEnd,iMid = 1,#tbl,0
      -- Binary Search
      while iStart <= iEnd do
         -- calculate middle
         iMid = math.floor( (iStart+iEnd)/2 )
         -- get compare value
         local value2 = fcompval( tbl[iMid] )
         -- get all values that match
         if value == value2 then
            local tfound,num = { iMid,iMid },iMid - 1
            while value == fcompval( tbl[num] ) do -- ERROR: this may cause fail in fcompval if num is out of range and tbl[num] is nil
               tfound[1],num = num,num - 1
            end
            num = iMid + 1
            while value == fcompval( tbl[num] ) do -- ERROR: this may cause fail in fcompval if num is out of range and tbl[num] is nil
               tfound[2],num = num,num + 1
            end
            return tfound
         -- keep searching
         elseif fcomp( value,value2 ) then
            iEnd = iMid - 1
         else
            iStart = iMid + 1
         end
      end
   end
end





function quadBezier(t, p0, p1, p2)
  local v1 = p0 * (1 - t)^2 
  local v2 = p1 * 2 * (1 - t) * t
  local v3 = p2 * t^2
  return v1 + v2 + v3
end

--from cpml 
if not utils then 
utils=  {}
function utils.wrap(value, limit)
  if value < 0 then
    value = value + utils.round(((-value/limit)+1))*limit
  end
  return value % limit
end

function utils.map(value, min_in, max_in, min_out, max_out)
  return ((value) - (min_in)) * ((max_out) - (min_out)) / ((max_in) - (min_in)) + (min_out)
end
end


--http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#quaternions
function quatBetweenVecs(start,dest)
	start = start:normalize()
	dest = dest:normalize()

	local cosTheta = start:dot(dest)
	local rotationAxis = vec3()

	if (cosTheta<-1+0.001) then 
		rotationAxis = vec3.cross(vec3(0,0,1), start)
		if (rotationAxis:len2() < 0.01 ) then 
			rotationAxis = vec3.cross(vec3(1,0,0), start)
		end
		rotationAxis = rotationAxis:normalize()
		return quat.from_angle_axis(pi, rotationAxis)
	end

	rotationAxis = start:cross(dest)

	local s = math.sqrt( (1+cosTheta)*2 )
	local invs = 1 / s


	return quat(
		rotationAxis.x * invs,
		rotationAxis.y * invs,
		rotationAxis.z * invs,
		s * 0.5
		)
end



-- https://love2d.org/wiki/love.filesystem.remove
function recursivelyDelete( item )
  if lfs.exists( item ) then
    if lfs.isDir( item ) then
      for _, child in pairs( lfs.getDirectoryItems( item )) do
        recursivelyDelete( item .. '/' .. child )
        lfs.remove( item .. '/' .. child )
      end
    else
      lfs.remove( item )
    end
  end
end

function basename(str)
	local name = string.gsub(str, "(.*/)(.*)", "%2")
	return name
end


-- http://johnblackburne.blogspot.com/2012/05/angle-between-two-3d-vectors.html
function vec3.angle_between(vA, vB) 
    local fCrossX, fCrossY, fCrossZ, fCross, fDot
    fCrossX = vA.y * vB.z - vA.z * vB.y
    fCrossY = vA.z * vB.x - vA.x * vB.z
    fCrossZ = vA.x * vB.y - vA.y * vB.x
    fCross = math.sqrt(fCrossX * fCrossX +
        fCrossY * fCrossY + fCrossZ * fCrossZ)
    fDot = vA.x * vB.x + vA.y * vB.y + vA.z + vB.z
    return math.atan2(fCross, fDot)
  end


function simpleuuid()
		local template ='xxxxxxxxxxxxxxxx'
		return string.gsub(template, '[xy]', function (c)
				local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
				return string.format('%x', v)
		end)
end


function math.intersect2 ( a, b, c, d, e, f, g, h ,stop ,simp)
	if stop and not (a and b and c and d and e and f and g and h ) then 
		return false 
	end
	if   a == e and b == f 
		or c == e and d == f 
		or a == g and b == h 
		or c == g and d == h 
		then 
			return false
	end
	return math.intersect ( a, b, c, d, e, f, g, h , simp)
end

--https://gist.github.com/Xeoncross/9511295
function math.intersect ( a, b, c, d, e, f, g, h , simp)
  -- parameter conversion
  local L1 
	local L2 

  if e then 
    L1 = {X1=a,Y1=b,X2=c,Y2=d}
    L2 = {X1=e,Y1=f,X2=g,Y2=h}
  else 
  	L1 = {X1=a.x,Y1=a.y,X2=b.x,Y2=b.y}
    L2 = {X1=c.x,Y1=c.y,X2=d.x,Y2=d.y}
  end
  
  -- Denominator for ua and ub are the same, so store this calculation
  local d = (L2.Y2 - L2.Y1) * (L1.X2 - L1.X1) - (L2.X2 - L2.X1) * (L1.Y2 - L1.Y1)
  
  -- Make sure there is not a division by zero - this also indicates that the lines are parallel.
  -- If n_a and n_b were both equal to zero the lines would be on top of each
  -- other (coincidental).  This check is not done because it is not
  -- necessary for this implementation (the parallel check accounts for this).
  if (d == 0) then
    return false
  end
  
  -- n_a and n_b are calculated as seperate values for readability
  local n_a = (L2.X2 - L2.X1) * (L1.Y1 - L2.Y1) - (L2.Y2 - L2.Y1) * (L1.X1 - L2.X1)
  local n_b = (L1.X2 - L1.X1) * (L1.Y1 - L2.Y1) - (L1.Y2 - L1.Y1) * (L1.X1 - L2.X1)
  
  -- Calculate the intermediate fractional point that the lines potentially intersect.
  local ua = n_a / d
  local ub = n_b / d
  
  -- The fractional point will be between 0 and 1 inclusive if the lines
  -- intersect.  If the fractional calculation is larger than 1 or smaller
  -- than 0 the lines would need to be longer to intersect.
  if (ua >= 0 and ua <= 1 and ub >= 0 and ub <= 1) then
    local x = L1.X1 + (ua * (L1.X2 - L1.X1))
    local y = L1.Y1 + (ua * (L1.Y2 - L1.Y1))
    if simp then 
      return true ,x ,y
    else
      return true, {x=x, y=y}
    end
  end
  
  return false
end

function hex2rgb(hex,a)
		local a=a or 1
		local hex = hex:gsub("#","")
		local col = {r=tonumber("0x"..hex:sub(1,2)), g=tonumber("0x"..hex:sub(3,4)), b=tonumber("0x"..hex:sub(5,6)), a=a*255}
		return col
end

function simplePointInRect(point,rect)
	return (point.x>rect.x and point.x<rect.x+ (rect.w or rect.width) and point.y>rect.y and point.y<rect.y+(rect.h or rect.height) ) 
end
function pointInBBox(px,py, bbox)
  return pointInBox(px,py,bbox[1],bbox[2],bbox[3],bbox[4])
end
function pointInBox(px,py, rx1,ry1,rx2,ry2)
  return (px>rx1 and px<rx2 and py>ry1 and py<ry2) 
end


function url_decode(str)
	str = string.gsub (str, "+", " ")
	str = string.gsub (str, "%%(%x%x)",
			function(h) return string.char(tonumber(h,16)) end)
	str = string.gsub (str, "\r\n", "\n")
	return str
end

function bboxCollide(b1,b2) -- AABBCColliDDEEr
	local x1,y1,w1,h1 = b1[1],b1[2],b1[3],b1[4]
	local x2,y2,w2,h2 = b2[1],b2[2],b2[3],b2[4]
	return x1 < x2+w2 and
				 x2 < x1+w1 and
				 y1 < y2+h2 and
				 y2 < y1+h1
end

function bboxCollide1(box1,box2) -- AABBCColliDDEEr
	local l1,t1,r1,b1 = box1[1],box1[2],box1[3],box1[4]
	local l2,t2,r2,b2 = box2[1],box2[2],box2[3],box2[4]
	return 	l1 < r2 and
		 	l2 < r1 and
		 	t1 < b2 and
		 	t2 < b1
end

function bboxCollide2(b1,b2) -- AABBCColliDDEEr
	return 	 b1.x < b2.x+(b2.w or b2.width) and
			 b2.x < b1.x+(b1.w or b1.width) and
			 b1.y < b2.y+(b2.h or b2.height) and
			 b2.y < b1.y+(b1.h or b1.height)
end

function lerp(a,b,t) if not a then print_info(5) end return (1-t)*a + t*b end
function cerp(a,b,t) local f=(1-math.cos(t*math.pi))*.5 return a*(1-f)+b*f end
function lerpang( a,b,t )
	local a = a
	local b = b
	local dtheta = b-a
	if (dtheta > pi) then a = a + 2*pi
	elseif (dtheta < -pi) then a = a- 2*pi end
	return (1-t)*a + t*b 
end

local sqrt = math.sqrt
local abs  = math.abs
function distPointToLineSegment(pt,l1,l2,inf,signed)
	local dx,dy = l2[1]-l1[1],l2[2]-l1[2]
	local length = sqrt( dx*dx + dy*dy )
	dx,dy = dx/length,dy/length
	local posOnLine = dx*(pt[1]-l1[1]) + dy*(pt[2]-l1[2])
	if (not inf) and posOnLine < 0 then
  	dx,dy = pt[1]-l1[1],pt[2]-l1[2]
  	return sqrt(dx*dx+dy*dy)
	elseif (not inf) and posOnLine > length then
  	dx,dy = pt[1]-l2[1],pt[2]-l2[2]
  	return sqrt(dx*dx+dy*dy)
	else
  	if signed then
      return (dy*(pt[1]-l1[1]) - dx*(pt[2]-l1[2])) 
    else
      return abs( dy*(pt[1]-l1[1]) - dx*(pt[2]-l1[2]))
    end
	end
end
function distPointToLineSegment2(ptx, pty, l1x, l1y, l2x, l2y, inf,signed)
  local dx, dy = l2x - l1x, l2y - l1y
  local length = sqrt(dx * dx + dy * dy)
  dx, dy = dx / length, dy / length
  local posOnLine = dx * (ptx - l1x) + dy * (pty - l1y)
  if (not inf) and posOnLine < 0 then
    dx, dy = ptx - l1x, pty - l1y
    return sqrt(dx * dx + dy * dy)
  elseif (not inf) and posOnLine > length then
    dx, dy = ptx - l2x, pty - l2y
    return sqrt(dx * dx + dy * dy)
  else
    if signed then
      return (dy*(ptx-l1x) - dx*(pty-l1y)) 
    else
      return abs( dy*(ptx-l1x) - dx*(pty-l1y))
    end
  end
end
function point_in_square(tab)   -- uses sum-of-areas approach
	 local test1 = point_in_triangle(tab[1],tab[2],tab[3])
	 local test2 = point_in_triangle(tab[3],tab[4],tab[1])
	 return test1 or test2
end

function pointInTri(tri,px,py)
  return onSameSide2(px,py,tri[1],tri[2], tri[3],tri[4],tri[5],tri[6]) 
      and onSameSide2(px,py,tri[3],tri[4],tri[1],tri[2], tri[5],tri[6]) 
      and onSameSide2(px,py,tri[5],tri[6],tri[1],tri[2], tri[3],tri[4]) 
end
function onSameSide2(ax,ay,bx,by, cx,cy,dx,dy)
   local px, py = dx-cx, dy-cy
   local l = det(px,py,  ax-cx, ay-cy)
   local m = det(px,py,  bx-cx, by-cy)
   return l*m >= 0 
end


function point_in_triangle( a,b,c, p)   -- Harden Collider approach
	 return onSameSide(p,a, b,c) and onSameSide(p,b, a,c) and onSameSide(p,c, a,b)
end
function onSameSide(a,b, c,d)
   local px, py = d[1]-c[1], d[2]-c[2]
   local l = det(px,py,  a[1]-c[1], a[2]-c[2])
   local m = det(px,py,  b[1]-c[1], b[2]-c[2])
   return l*m >= 0 
end

local _tri,_point = {{},{},{}},{}
function point_in_poly(poly,point,prt)
	local success,tris
	if type(poly[1])=="table" then 
		success,tris=true,poly 
	else
		success,tris  = pcall(mth.triangulate,poly)
	end
	if not success then 
		print("Bad Polygon")
		return false
	else
		local pointIn = false
		_point[1],_point[2] = point.x or point[1] ,point.y or point[2]
		for t,tri in ipairs(tris) do 
			_tri[1][1],_tri[1][2]=tri[1],tri[2]
			_tri[2][1],_tri[2][2]=tri[3],tri[4]
			_tri[3][1],_tri[3][2]=tri[5],tri[6]
			if point_in_triangle(_tri[1],_tri[2],_tri[3],_point) then 
				pointIn = true 
			end
		end
		return pointIn
	end
end

function point_in_poly_bordered(poly,point,border)
	local inborder = false
	for l=1, #poly-2, 2 do 
		if distPointToLineSegment({point.x,point.y},{poly[l],poly[l+1]},{poly[l+2],poly[l+3]})<border then
			inborder=true
		end
	end
	return (inborder or point_in_poly(poly,point))
end


function det(x1,y1, x2,y2)   return x1*y2 - y1*x2 end

function pairsByKeys (t, f)
  local a = {}
  for n in pairs(t) do table.insert(a, n) end
  table.sort(a, f)
  local i = 0      -- iterator variable
  local iter = function ()   -- iterator function
	i = i + 1
	if a[i] == nil then return nil
	else return a[i], t[a[i]]
	end
  end
  return iter
end

function shallowcopy(orig)
	local orig_type = type(orig)
	local copy
	if orig_type == 'table' and not orig.nocopy and not mat4.is_mat4(orig) then
			copy = {}
			for orig_key, orig_value in pairs(orig) do
					copy[shallowcopy(orig_key)] = shallowcopy(orig_value)
			end
	else -- number, string, boolean, etc
			copy = orig
	end
	return copy
end

function deepcopy(orig,exclude)
	local orig_type = type(orig)
	local copy
	local depth = depth or ""

		if orig_type == 'table' and not mat4.is_mat4(orig) and (not orig.nocopy) and not(orig[1] and orig[1]=="nocopy") then
			copy = {}
			for orig_key, orig_value in next, orig, nil do
				if orig_key ~= "transform" then 
					copy[orig_key] = deepcopy(orig_value)
				end
			end
			setmetatable(copy, deepcopy(getmetatable(orig)))

		elseif orig_type~="userdata" then -- number, string, boolean, etc
			copy = orig
		end


	return copy
end

function HSV(h, s, v)
    if s <= 0 then return v,v,v end
    h, s, v = h/256*6, s/255, v/255
    local c = v*s
    local x = (1-math.abs((h%2)-1))*c
    local m,r,g,b = (v-c), 0,0,0
    if h < 1     then r,g,b = c,x,0
    elseif h < 2 then r,g,b = x,c,0
    elseif h < 3 then r,g,b = 0,c,x
    elseif h < 4 then r,g,b = 0,x,c
    elseif h < 5 then r,g,b = x,0,c
    else              r,g,b = c,0,x
    end return (r+m)*255,(g+m)*255,(b+m)*255
end

-- https://www.ilikebigbits.com/2015_03_04_plane_from_points.html
function plane_from_points(points)
    if #points < 3 then 
        return 
    end

    local sum = vec3()
    for _,p in ipairs(points) do
        sum.x=sum.x+p.x
        sum.y=sum.y+p.y
        sum.z=sum.z+p.z
    end
    local position = sum / #points

    -- Calc full 3x3 covariance matrix, excluding symmetries:
    local xx, xy, xz = 0,0,0
    local yy, yz, zz = 0,0,0

    for _,p in ipairs(points) do
        local r = p - position;
        xx = xx + r.x * r.x
        xy = xy + r.x * r.y
        xz = xz + r.x * r.z
        yy = yy + r.y * r.y
        yz = yz + r.y * r.z
        zz = zz + r.z * r.z
    end

    local det_x = yy*zz - yz*yz;
    local det_y = xx*zz - xz*xz;
    local det_z = xx*yy - xy*xy;

    local det_max = math.max(det_x, math.max(det_y, det_z))
    if det_max <= 0 then
        return -- The points don't span a plane
    end

    -- Pick path with best conditioning:
    local dir = 
    			 (det_max == det_x) and vec3(det_x,xz*yz - xy*zz,xy*yz - xz*yy)
       or ((det_max == det_y) and vec3(xz*yz - xy*zz,det_y,xy*xz - yz*xx) 
       												 or vec3(xy*yz - xz*yy,xy*xz - yz*xx,det_z))

    return position, dir:normalize()
end
 
-- https://love2d.org/wiki/Gradients
function gradientMesh( dir, ...)
    -- Check for direction
    local isHorizontal = true
    if dir == "vertical" then
        isHorizontal = false
    elseif dir ~= "horizontal" then
        error("bad argument #1 to 'gradient' (invalid value)", 2)
    end
 
    -- Check for colors
    local colorLen = select("#", ...)
    if colorLen < 2 then
        error("color list is less than two", 2)
    end


    -- Generate mesh
    local meshData = {}
    if isHorizontal then
	    for i = 1, colorLen do
        local color = select(i, ...)
        local x = (i - 1) / (colorLen - 1)

        meshData[#meshData + 1] = {x, 1, 0, 1, color[1] or 1, color[2] or 1, color[3] or 1, 1}
        meshData[#meshData + 1] = {x, 0, 0, 1, color[1] or 1, color[2] or 1, color[3] or 1, 1}
	    end
    else
      for i = 1, colorLen do
        local color = select(i, ...)
        local y = (i - 1) / (colorLen - 1)

        meshData[#meshData + 1] = {1, y, 0, 1, color[1] or 1, color[2] or 1, color[3] or 1, 1}
        meshData[#meshData + 1] = {0, y, 0, 1, color[1] or 1, color[2] or 1, color[3] or 1, 1}
      end
    end

  	return meshData
end

function PointWithinShape(shape, tx, ty)
  if #shape == 0 then 
    return false
  elseif #shape == 1 then 
    return shape[1].x == tx and shape[1].y == ty
  elseif #shape == 2 then 
    return PointWithinLine(shape, tx, ty)
  else 

    return CrossingsMultiplyTest(shape, tx, ty)
  end
end
 
function BoundingBox(box, tx, ty)
  return  (box[2].x >= tx and box[2].y >= ty)
    and (box[1].x <= tx and box[1].y <= ty)
    or  (box[1].x >= tx and box[2].y >= ty)
    and (box[2].x <= tx and box[1].y <= ty)
end
 
function colinear(line, x, y, e)
  e = e or 0.1
  m = (line[2].y - line[1].y) / (line[2].x - line[1].x)
  local function f(x) return line[1].y + m*(x - line[1].x) end
  return math.abs(y - f(x)) <= e
end
 
function PointWithinLine(line, tx, ty, e)
  e = e or 0.66
  if BoundingBox(line, tx, ty) then
    return colinear(line, tx, ty, e)
  else
    return false
  end
end

-------------------------------------------------------------------------
-- The following function is based off code from
-- [ http://erich.realtimerendering.com/ptinpoly/ ]
--
--[[
 ======= Crossings Multiply algorithm ===================================
 * This version is usually somewhat faster than the original published in
 * Graphics Gems IV; by turning the division for testing the X axis crossing
 * into a tricky multiplication test this part of the test became faster,
 * which had the additional effect of making the test for "both to left or
 * both to right" a bit slower for triangles than simply computing the
 * intersection each time.  The main increase is in triangle testing speed,
 * which was about 15% faster; all other polygon complexities were pretty much
 * the same as before.  On machines where division is very expensive (not the
 * case on the HP 9000 series on which I tested) this test should be much
 * faster overall than the old code.  Your mileage may (in fact, will) vary,
 * depending on the machine and the test data, but in general I believe this
 * code is both shorter and faster.  This test was inspired by unpublished
 * Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
 * Related work by Samosky is in:
 *
 * Samosky, Joseph, "SectionView: A system for interactively specifying and
 * visualizing sections through three-dimensional medical image data",
 * M.S. Thesis, Department of Electrical Engineering and Computer Science,
 * Massachusetts Institute of Technology, 1993.
 *
 --]]
 
--[[ Shoot a test ray along +X axis.  The strategy is to compare vertex Y values
 * to the testing point's Y and quickly discard edges which are entirely to one
 * side of the test ray.  Note that CONVEX and WINDING code can be added as
 * for the CrossingsTest() code; it is left out here for clarity.
 *
 * Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
 * _point_, returns 1 if inside, 0 if outside.
 --]]
function CrossingsMultiplyTest(pgon, tx, ty)
  local i, yflag0, yflag1, inside_flag
  local vtx0, vtx1
  local tab = type(pgon[1])=="table"
  
  if tab then 
    vtx0 = vec2(pgon[#pgon][1],pgon[#pgon][2])
    vtx1 = vec2(pgon[1][1],pgon[1][2])
  else
    vtx0 = vec2(pgon[#pgon-1],pgon[#pgon])
    vtx1 = vec2(pgon[1],pgon[2])
  end
 
  -- get test bit for above/below X axis
  yflag0 = ( vtx0.y >= ty )
  inside_flag = false
 
  local first = tab and 2 or 3
  local last = tab and #pgon+1 or #pgon+2
  local inc = tab and 1 or 2
  for i=first,last,inc do
    yflag1 = ( vtx1.y >= ty )
 
    if ( yflag0 ~= yflag1 ) then
      if ( ((vtx1.y - ty) * (vtx0.x - vtx1.x) >= (vtx1.x - tx) * (vtx0.y - vtx1.y)) == yflag1 ) then
        inside_flag =  not inside_flag
      end
    end
 
    -- Move to the next pair of vertices, retaining info as possible.
    yflag0  = yflag1
      vtx0:set(vtx1) 
    if tab then 
      if i<#pgon then 
        vtx1:set(pgon[i][1],pgon[i][2])
      end
    else
      if i<#pgon then 
        vtx1:set(pgon[i],pgon[i+1])
      end
    end
  end
 
  return  inside_flag
end
