local lib = ...
local fn = {}
fn.vsep = function(vec) -- separate a vector into a direction + magnitude
return vec:normalize(), vec:length()
end
-- minetest now only provides the version of this function that sqrts the result
-- which is pointlessly wasteful much of the time
fn.vdsq = function(a,b)
local d = vector.subtract(v1,v2)
return (d.x ^ 2) + (d.y ^ 2) + (d.z ^ 2)
end
fn.vdcomp = function(dist,v1,v2) -- compare the distance between two points
-- (cheaper than calculating distance outright)
local d if v2
then d = vector.subtract(v1,v2)
else d = v1
end
local dsq = (d.x ^ 2) + (d.y ^ 2) + (d.z ^ 2)
return dsq / (dist^2)
-- [0,1) == less then
-- 1 == equal
-- >1 == greater than
end
-- produce an SI expression for a quantity
fn.si = function(unit, val, full, uncommonScales, prec)
if val == 0 then return '0 ' .. unit end
local scales = {
{30, 'Q', 'quetta',true, 'q', 'quecto',true};
{27, 'R', 'ronna', true, 'r', 'ronto', true};
{24, 'Y', 'yotta', true, 'y', 'yocto', true};
{21, 'Z', 'zetta', true, 'z', 'zepto', true};
{18, 'E', 'exa', true, 'a', 'atto', true};
{15, 'P', 'peta', true, 'f', 'femto', true};
{12, 'T', 'tera', true, 'p', 'pico', true};
{9, 'G', 'giga', true, 'n', 'nano', true};
{6, 'M', 'mega', true, 'μ', 'micro', true};
{3, 'k', 'kilo', true, 'm', 'milli', true};
{2, 'h', 'hecto', false, 'c', 'centi', true};
{1, 'da','deca', false, 'd', 'deci', false};
}
for i, s in ipairs(scales) do
local amt, smaj, pmaj, cmaj,
smin, pmin, cmin = lib.tbl.unpack(s)
if math.abs(val) > 1 then
if uncommonScales or cmaj then
local denom = 10^amt
local vd = val/denom
if prec then vd = lib.math.trim(vd, prec) end
if math.abs(val) >= (10^(amt)) then
return string.format("%s%s%s",
vd, (full and (' ' .. pmaj) or smaj), unit)
end
end
elseif math.abs(val) < 1 then
if uncommonScales or cmin then
local denom = 10^-amt
local vd = val/denom
if prec then vd = lib.math.trim(vd, prec) end
if math.abs(val) <= (10^-(amt-1)) then
return string.format("%s%s%s",
vd, (full and (' ' .. pmin) or smin), unit)
end
end
end
end
return string.format("%s%s", val, unit)
end
function fn.lerp(t, a, b) return (1-t)*a + t*b end
function fn.trim(fl, prec)
local fac = 10^prec
return math.floor(fl * fac) / fac
end
function fn.sign(v)
if v > 0 then return 1
elseif v < 0 then return -1
else return 0 end
end
function fn.toward(from, to, mag)
local dir = fn.sign(to - from)
local step = mag * dir
if (dir == 1 and from+step < to)
or (dir == -1 and from+step > to)
then return from+step
else return to
end
end
fn.rng = lib.class {
__name = 'rng';
construct = function(seed)
return {seed = seed, rng = PcgRandom(seed)}
end;
__index = {
int = function(self,m,x)
return self.rng:next(m,x)
end;
real = function(self,m,x)
local i = self:int()
local f = (i+bit.lshift(1,31)) / bit.lshift(1, 32)
if m==nil then return f end
if x==nil then x=m m=0 end
return m + ((x-m) * f)
end;
fork = function(self)
return fn.rng(self:int())
end;
};
__add = function(self,n)
return fn.rng(self.seed + n)
end;
}
fn.seedbank = lib.class {
__name = 'seedbank';
construct = function(seed)
return {seed = seed}
end;
__index = function(self, n)
return fn.rng(PcgRandom(self.seed+n):next())
end;
__add = function(self, n)
return fn.seedbank(self.seed + n)
end;
}
-- function fn.vlerp
function fn.timespec(n)
if n == 0 then return '0s' end
if n < 0 then return '-' .. fn.timespec(n*-1) end
local sec = math.floor(n % 60)
local hr = math.floor(n / 60)
local spec = {}
if hr ~= 0 then table.insert(spec, string.format("%shr", hr)) end
if sec ~= 0 then table.insert(spec, string.format("%ss", sec)) end
return table.concat(spec, ' ')
end
return fn