mirin. module. mirin. module

This commit is contained in:
Jill 2022-09-23 23:02:17 +03:00
parent 4f473a744a
commit 3d55d3daeb
6 changed files with 2345 additions and 1 deletions

View File

@ -19,7 +19,7 @@
-- https://github.com/XeroOl/notitg-mirin/blob/0fbff2ee93d905feeb58c4aac4fe7f5f9ebc9647/template/std.lua#L17
oat.package = {
-- uranium template loader path
path = 'src/?.lua;src/?/init.lua;template/?.lua',
path = 'src/?.lua;src/?/init.lua;template/?.lua;template/?/init.lua',
preload = {},
loaded = {},
loaders = {

339
stdlib/mirin/ease.lua Normal file
View File

@ -0,0 +1,339 @@
---@diagnostic disable: lowercase-global
-- Convenience shortcuts
local sqrt = math.sqrt
local sin = math.sin
local asin = math.asin
local cos = math.cos
local pow = math.pow
local exp = math.exp
local pi = math.pi
local abs = math.abs
xero()
-- ===================================================================== --
-- Utility functions
--- Flip any easing function, making it go from 1 to 0
-- Example use:
-- ```lua
-- ease {0, 20, flip(outQuad), 50, 'modname'}
-- ```
flip = setmetatable({}, {
__call = function(self, fn)
self[fn] = self[fn] or function(x) return 1 - fn(x) end
return self[fn]
end
})
-- Mix two easing functions together into a new ease
-- the new ease starts by acting like the first argument, and then ends like the second argument
-- Example: ease {0, 20, blendease(inQuad, outQuad), 100, 'modname'}
blendease = setmetatable({}, {
__index = function(self, key)
self[key] = {}
return self[key]
end,
__call = function(self, fn1, fn2)
if not self[fn1][fn2] then
local transient1 = fn1(1) <= 0.5
local transient2 = fn2(1) <= 0.5
if transient1 and not transient2 then
error('blendease: the first argument is a transient ease, but the second argument doesn\'t match')
end
if transient2 and not transient1 then
error('blendease: the second argument is a transient ease, but the first argument doesn\'t match')
end
self[fn1][fn2] = function(x)
local mixFactor = 3*x^2-2*x^3
return (1 - mixFactor) * fn1(x) + mixFactor * fn2(x)
end
end
return self[fn1][fn2]
end
})
local function param1cache(self, param1)
self.cache[param1] = self.cache[param1] or function(x)
return self.fn(x, param1)
end
return self.cache[param1]
end
local param1mt = {
__call = function(self, x, param1)
return self.fn(x, param1 or self.dp1)
end,
__index = {
param = param1cache,
params = param1cache,
}
}
-- Declare an easing function taking one custom parameter
function with1param(fn, defaultparam1)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
cache = {},
}, param1mt)
end
local function param2cache(self, param1, param2)
self.cache[param1] = self.cache[param1] or {}
self.cache[param1][param2] = self.cache[param1][param2] or function(x)
return self.fn(x, param1, param2)
end
return self.cache[param1][param2]
end
local param2mt = {
__call = function(self, x, param1, param2)
return self.fn(x, param1 or self.dp1, param2 or self.dp2)
end,
__index = {
param=param2cache,
params=param2cache,
}
}
-- Declare an easing function taking two custom parameters
function with2params(fn, defaultparam1, defaultparam2)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
dp2 = defaultparam2,
cache = {},
}, param2mt)
end
-- ===================================================================== --
-- Easing functions
function bounce(t) return 4 * t * (1 - t) end
function tri(t) return 1 - abs(2 * t - 1) end
function bell(t) return inOutQuint(tri(t)) end
function pop(t) return 3.5 * (1 - t) * (1 - t) * sqrt(t) end
function tap(t) return 3.5 * t * t * sqrt(1 - t) end
function pulse(t) return t < .5 and tap(t * 2) or -pop(t * 2 - 1) end
function spike(t) return exp(-10 * abs(2 * t - 1)) end
function inverse(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end
local function popElasticInternal(t, damp, count)
return (1000 ^ -(t ^ damp) - 0.001) * sin(count * pi * t)
end
local function tapElasticInternal(t, damp, count)
return (1000 ^ -((1 - t) ^ damp) - 0.001) * sin(count * pi * (1 - t))
end
local function pulseElasticInternal(t, damp, count)
if t < .5 then
return tapElasticInternal(t * 2, damp, count)
else
return -popElasticInternal(t * 2 - 1, damp, count)
end
end
popElastic = with2params(popElasticInternal, 1.4, 6)
tapElastic = with2params(tapElasticInternal, 1.4, 6)
pulseElastic = with2params(pulseElasticInternal, 1.4, 6)
impulse = with1param(function(t, damp)
t = t ^ damp
return t * (1000 ^ -t - 0.001) * 18.6
end, 0.9)
function instant() return 1 end
function linear(t) return t end
function inQuad(t) return t * t end
function outQuad(t) return -t * (t - 2) end
function inOutQuad(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 2
else
return 1 - 0.5 * (2 - t) ^ 2
end
end
function outInQuad(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 2
else
return 0.5 + 0.5 * (t - 1) ^ 2
end
end
function inCubic(t) return t * t * t end
function outCubic(t) return 1 - (1 - t) ^ 3 end
function inOutCubic(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 3
else
return 1 - 0.5 * (2 - t) ^ 3
end
end
function outInCubic(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 3
else
return 0.5 + 0.5 * (t - 1) ^ 3
end
end
function inQuart(t) return t * t * t * t end
function outQuart(t) return 1 - (1 - t) ^ 4 end
function inOutQuart(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 4
else
return 1 - 0.5 * (2 - t) ^ 4
end
end
function outInQuart(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 4
else
return 0.5 + 0.5 * (t - 1) ^ 4
end
end
function inQuint(t) return t ^ 5 end
function outQuint(t) return 1 - (1 - t) ^ 5 end
function inOutQuint(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 5
else
return 1 - 0.5 * (2 - t) ^ 5
end
end
function outInQuint(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 5
else
return 0.5 + 0.5 * (t - 1) ^ 5
end
end
function inExpo(t) return 1000 ^ (t - 1) - 0.001 end
function outExpo(t) return 1.001 - 1000 ^ -t end
function inOutExpo(t)
t = t * 2
if t < 1 then
return 0.5 * 1000 ^ (t - 1) - 0.0005
else
return 1.0005 - 0.5 * 1000 ^ (1 - t)
end
end
function outInExpo(t)
if t < 0.5 then
return outExpo(t * 2) * 0.5
else
return inExpo(t * 2 - 1) * 0.5 + 0.5
end
end
function inCirc(t) return 1 - sqrt(1 - t * t) end
function outCirc(t) return sqrt(-t * t + 2 * t) end
function inOutCirc(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * sqrt(1 - t * t)
else
t = t - 2
return 0.5 + 0.5 * sqrt(1 - t * t)
end
end
function outInCirc(t)
if t < 0.5 then
return outCirc(t * 2) * 0.5
else
return inCirc(t * 2 - 1) * 0.5 + 0.5
end
end
function outBounce(t)
if t < 1 / 2.75 then
return 7.5625 * t * t
elseif t < 2 / 2.75 then
t = t - 1.5 / 2.75
return 7.5625 * t * t + 0.75
elseif t < 2.5 / 2.75 then
t = t - 2.25 / 2.75
return 7.5625 * t * t + 0.9375
else
t = t - 2.625 / 2.75
return 7.5625 * t * t + 0.984375
end
end
function inBounce(t) return 1 - outBounce(1 - t) end
function inOutBounce(t)
if t < 0.5 then
return inBounce(t * 2) * 0.5
else
return outBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function outInBounce(t)
if t < 0.5 then
return outBounce(t * 2) * 0.5
else
return inBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function inSine(x) return 1 - cos(x * (pi * 0.5)) end
function outSine(x) return sin(x * (pi * 0.5)) end
function inOutSine(x)
return 0.5 - 0.5 * cos(x * pi)
end
function outInSine(t)
if t < 0.5 then
return outSine(t * 2) * 0.5
else
return inSine(t * 2 - 1) * 0.5 + 0.5
end
end
function outElasticInternal(t, a, p)
return a * pow(2, -10 * t) * sin((t - p / (2 * pi) * asin(1/a)) * 2 * pi / p) + 1
end
local function inElasticInternal(t, a, p)
return 1 - outElasticInternal(1 - t, a, p)
end
function inOutElasticInternal(t, a, p)
return t < 0.5
and 0.5 * inElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * outElasticInternal(t * 2 - 1, a, p)
end
function outInElasticInternal(t, a, p)
return t < 0.5
and 0.5 * outElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * inElasticInternal(t * 2 - 1, a, p)
end
inElastic = with2params(inElasticInternal, 1, 0.3)
outElastic = with2params(outElasticInternal, 1, 0.3)
inOutElastic = with2params(inOutElasticInternal, 1, 0.3)
outInElastic = with2params(outInElasticInternal, 1, 0.3)
function inBackInternal(t, a) return t * t * (a * t + t - a) end
function outBackInternal(t, a) t = t - 1 return t * t * ((a + 1) * t + a) + 1 end
function inOutBackInternal(t, a)
return t < 0.5
and 0.5 * inBackInternal(t * 2, a)
or 0.5 + 0.5 * outBackInternal(t * 2 - 1, a)
end
function outInBackInternal(t, a)
return t < 0.5
and 0.5 * outBackInternal(t * 2, a)
or 0.5 + 0.5 * inBackInternal(t * 2 - 1, a)
end
inBack = with1param(inBackInternal, 1.70158)
outBack = with1param(outBackInternal, 1.70158)
inOutBack = with1param(inOutBackInternal, 1.70158)
outInBack = with1param(outInBackInternal, 1.70158)

19
stdlib/mirin/init.lua Normal file
View File

@ -0,0 +1,19 @@
xero = oat
xero.foreground = oat._main
xero.MIRIN_VERSION = 'URANIUM-5.0.1'
-- Load all of the core .lua files
-- The order DOES matter here:
-- std.lua needs to be loaded first
-- template.lua needs to be last
require('stdlib.mirin.std')
require('stdlib.mirin.sort')
require('stdlib.mirin.ease')
require('stdlib.mirin.template')
local xeroActorsAF = Quad()
function uranium.init()
xero.init_command(xeroActorsAF)
end

152
stdlib/mirin/sort.lua Normal file
View File

@ -0,0 +1,152 @@
--[[
this is based on code from Dirk Laurie and Steve Fisher,
used under license as follows:
Copyright © 2013 Dirk Laurie and Steve Fisher.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the 'Software'),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
(modifications by Max Cahill 2018, 2020)
(modifications by XeroOl 2021)
Found at: https://github.com/1bardesign/batteries/blob/master/sort.lua
]]
-- `sort` object, container of the following methods
local sort = {}
-- Tunable threshold, deciding between the insertion sort and merge sort
sort.max_chunk_size = 32
-- ===================================================================== --
-- Internal implementations
-- Insertion sort on a section of an array
function sort._insertion_sort_impl(array, first, last, less)
for i = first + 1, last do
local k = first
local v = array[i]
for j = i, first + 1, -1 do
if less(v, array[j - 1]) then
array[j] = array[j - 1]
else
k = j
break
end
end
array[k] = v
end
end
-- Merge sort on two sorted portions of an array
function sort._merge(array, workspace, low, middle, high, less)
local i, j, k
i = 1
-- copy first half of array to auxiliary array
for j = low, middle do
workspace[i] = array[j]
i = i + 1
end
-- sieve through
i = 1
j = middle + 1
k = low
while true do
if (k >= j) or (j > high) then
break
end
if less(array[j], workspace[i]) then
array[k] = array[j]
j = j + 1
else
array[k] = workspace[i]
i = i + 1
end
k = k + 1
end
-- copy back any remaining elements of first half
for k = k, j - 1 do
array[k] = workspace[i]
i = i + 1
end
end
-- Recursive merge sort implementation
function sort._merge_sort_impl(array, workspace, low, high, less)
if high - low <= sort.max_chunk_size then
sort._insertion_sort_impl(array, low, high, less)
else
local middle = math.floor((low + high) / 2)
sort._merge_sort_impl(array, workspace, low, middle, less)
sort._merge_sort_impl(array, workspace, middle + 1, high, less)
sort._merge(array, workspace, low, middle, high, less)
end
end
-- Default comparison function: sort from smallest to biggest
local function default_less(a, b)
return a < b
end
-- Setup a sorting algorithm, check the validity of the comparator,
-- and determine if a case is trivial (no sorting needed)
function sort._sort_setup(array, less)
less = less or default_less
local n = #array
--trivial cases; empty or 1 element
local trivial = (n <= 1)
if not trivial then
--check less
if less(array[1], array[1]) then
error('invalid order function for sorting; less(v, v) should not be true for any v.')
end
end
--setup complete
return trivial, n, less
end
-- Public method: merge sort on an array. If the array length is
-- less than `max_chunk_size`, an insertion sort will be done instead.
function sort.stable_sort(array, less)
--setup
local trivial, n, less = sort._sort_setup(array, less)
if not trivial then
--temp storage; allocate ahead of time
local workspace = {}
local middle = math.ceil(n / 2)
workspace[middle] = array[1]
--dive in
sort._merge_sort_impl( array, workspace, 1, n, less )
end
return array
end
-- Public method (currently not exposed): insertion sort
function sort.insertion_sort(array, less)
--setup
local trivial, n, less = sort._sort_setup(array, less)
if not trivial then
sort._insertion_sort_impl(array, 1, n, less)
end
return array
end
-- Exports
xero.unstable_sort = table.sort
xero.stable_sort = sort.stable_sort

249
stdlib/mirin/std.lua Normal file
View File

@ -0,0 +1,249 @@
-- Environments
--[[
-- Create the xero environment, in which everything is
setmetatable(xero, {
-- if something isn't found in the xero table, fall back to a _G lookup
__index = _G,
-- Calling xero() sets the environment of the current function
-- Calling xero(func) returns `func` with the xero environment applied
__call = function(self, f)
setfenv(f or 2, self)
return f
end
})
-- make require work
xero.package = {
-- mirin template loader path
path = 'lua/?.lua;lua/?/init.lua;plugins/?.lua;plugins/?/init.lua',
preload = {},
loaded = {},
loaders = {
function(modname)
local preload = xero.package.preload[modname]
return preload or 'no field xero.package.preload[\''..modname..'\']'
end,
function(modname)
local errors = {}
-- get the filename
local filename = string.gsub(modname, '%.', '/')
for path in (string.gfind or string.gmatch)(xero.package.path, '[^;]+') do
-- get the file path
local filepath = xero.dir .. string.gsub(path, '%?', filename)
-- check if file exists
if not GAMESTATE:GetFileStructure(filepath) then
table.insert(errors, 'no file \''..filepath..'\'')
else
local loader, err = loadfile(filepath)
-- check if file loads properly
if err then
error(err, 3)
elseif loader then
return xero(loader)
end
end
end
return table.concat(errors, '\n')
end,
},
}
function xero.require(modname)
local loaded = xero.package.loaded
if not loaded[modname] then
local errors = {'module \''..modname..'\' not found:'}
local chunk
for _, loader in ipairs(xero.package.loaders) do
local result = loader(modname)
if type(result) == 'string' then
table.insert(errors, result)
elseif type(result) == 'function' then
chunk = result
break
end
end
if not chunk then
error(table.concat(errors, '\n'), 2)
end
loaded[modname] = chunk()
if loaded[modname] == nil then
loaded[modname] = true
end
end
return loaded[modname]
end
-- Apply the environment. :)
xero()
--- Create the strict environment, forbidding creating any variable
--- This environment is not related to the xero table, and won't fetch
--- values from it, unless explicitely prefixed with `xero.`
xero.strict = setmetatable({}, {
-- Allow access to _G elements, containing all game methods
__index = _G,
-- Prevent creating any variable
__newindex = function(s, t)
error(t)
end
})
]]
-- ===================================================================== --
-- Utility functions
--- Returns a shallow copy of the table `src`
function copy(src)
local dest = {}
for k, v in pairs(src) do
dest[k] = v
end
return dest
end
-- Clear a table's contents, leaving it empty.
-- Useful for resetting a table containing metatables.
function clear(t)
for k, v in pairs(t) do
t[k] = nil
end
return t
end
-- Clear a table's contents, when the table only contains 'logical' indexes
-- (as in: contiguous numerical indexes from 1 to #table)
function iclear(t)
for i = 1, #t do
table.remove(t)
end
return t
end
-- Move global functions to the xero table, allowing for slightly faster
-- performance due to not having to go back and forth between xero and _G.
--[[
xero.xero = _G.xero
xero.type = _G.type
xero.print = _G.print
xero.pairs = _G.pairs
xero.ipairs = _G.ipairs
xero.unpack = _G.unpack
xero.tonumber = _G.tonumber
xero.tostring = _G.tostring
xero.math = copy(_G.math)
xero.table = copy(_G.table)
xero.string = copy(_G.string)
]]
-- ===================================================================== --
-- Data structure for all the `func` declarations.
-- This custom data structure smartly handles func priorities, so the order
-- they're declared in mods.xml is respected no matter what.
-- This data structure is generic enough to be used for any context, but
-- that is not the case for now.
local methods = {}
function methods:add(obj)
local stage = self.stage
self.n = self.n + 1
stage.n = stage.n + 1
stage[stage.n] = obj
end
function methods:remove()
local swap = self.swap
swap[swap.n] = nil
swap.n = swap.n - 1
self.n = self.n - 1
end
function methods:next()
if self.n == 0 then return end
local swap = self.swap
local stage = self.stage
local list = self.list
if swap.n == 0 then
stable_sort(stage, self.reverse_comparator)
end
if stage.n == 0 then
if list.n == 0 then
while swap.n ~= 0 do
list.n = list.n + 1
list[list.n] = swap[swap.n]
swap[swap.n] = nil
swap.n = swap.n - 1
end
else
swap.n = swap.n + 1
swap[swap.n] = list[list.n]
list[list.n] = nil
list.n = list.n - 1
end
else
if list.n == 0 then
swap.n = swap.n + 1
swap[swap.n] = stage[stage.n]
stage[stage.n] = nil
stage.n = stage.n - 1
else
if self.comparator(list[list.n], stage[stage.n]) then
swap.n = swap.n + 1
swap[swap.n] = list[list.n]
list[list.n] = nil
list.n = list.n - 1
else
swap.n = swap.n + 1
swap[swap.n] = stage[stage.n]
stage[stage.n] = nil
stage.n = stage.n - 1
end
end
end
return swap[swap.n]
end
local mt = {__index = methods}
function perframe_data_structure(comparator)
return setmetatable({
comparator = comparator,
reverse_comparator = function(a, b) return comparator(b, a) end,
stage = {n = 0},
list = {n = 0},
swap = {n = 0},
n = 0,
}, mt)
end
-- the behavior of a stringbuilder
local stringbuilder_mt = {
__index = {
-- :build() method converts a stringbuilder into a string, with optional delimiter
build = table.concat,
-- :clear() method empties the stringbuilder
clear = iclear,
},
-- calling a stringbuilder appends to it
__call = function(self, a)
table.insert(self, tostring(a))
return self
end,
-- stringbuilder can convert to a string
__tostring = table.concat,
}
-- stringbuilder constructor
function stringbuilder()
return setmetatable({}, stringbuilder_mt)
end

1585
stdlib/mirin/template.lua Normal file

File diff suppressed because it is too large Load Diff