| 🌟 | 現在、 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:Utility/Scale」の版間の差分
ナビゲーションに移動
検索に移動
(ページの作成:「local libraryUtil = require('libraryUtil') local table = require('Module:TableExtensions') function math.isNaN(v) --return tostring(v) == '-nan' return v ~= v en…」) |
(ticks算出のロジックを再改善) |
||
| (同じ利用者による、間の6版が非表示) | |||
| 2行目: | 2行目: | ||
local table = require('Module:TableExtensions') | local table = require('Module:TableExtensions') | ||
local ninf = -math.huge | |||
local pinf = math.huge | |||
function math.isInf(v) | |||
return v == pinf or v == ninf | |||
end | |||
function math.isNaN(v) | function math.isNaN(v) | ||
--return tostring(v) == '-nan' | --return tostring(v) == '-nan' | ||
| 9行目: | 14行目: | ||
function math.log10(x) | function math.log10(x) | ||
return math.log(x) * log10e | return math.log(x) * log10e | ||
end | |||
local eps = 2.2204460492503131e-16 | |||
function math.isSame(a, b) | |||
local diff = math.abs(a - b) | |||
local aa = math.abs(a) | |||
local ab = math.abs(b) | |||
if aa > ab then | |||
return diff <= eps * ab | |||
else | |||
return diff <= eps * aa | |||
end | |||
end | end | ||
| 124行目: | 141行目: | ||
return -(10 ^ -power) / nice | return -(10 ^ -power) / nice | ||
end | end | ||
end | |||
-- function: ticks | |||
local function ticks(start, stop, count) | |||
if start == stop and count > 0 then | |||
return { start } | |||
end | |||
local reverse = start > stop | |||
if reverse then | |||
start, stop = stop, start | |||
end | |||
local step = tickIncrement(start, stop, count) | |||
if step == 0 or math.isInf(step) then | |||
return {} | |||
end | |||
local ticks = {} | |||
if step > 0 then | |||
local r0 = math.floor(start / step + 0.5) | |||
local r1 = math.floor(stop / step + 0.5) | |||
local r0s = r0 * step | |||
local r1s = r1 * step | |||
if r0s < start or math.isSame(r0s, start) then | |||
r0 = r0 + 1 | |||
end | |||
if r1s > stop or math.isSame(r1s, stop) then | |||
r1 = r1 - 1 | |||
end | |||
for i = 0, r1 - r0 do | |||
ticks[i + 1] = (r0 + i) * step | |||
end | |||
else | |||
step = -step | |||
local r0 = math.floor(start * step + 0.5) | |||
local r1 = math.floor(stop * step + 0.5) | |||
local r0s = r0 / step | |||
local r1s = r1 / step | |||
if r0s < start or math.isSame(r0s, start) then | |||
r0 = r0 + 1 | |||
end | |||
if r1s > stop or math.isSame(r1s, stop) then | |||
r1 = r1 - 1 | |||
end | |||
for i = 0, r1 - r0 do | |||
ticks[i + 1] = (r0 + i) / step | |||
end | |||
end | |||
if reverse then | |||
table.reverse(ticks) | |||
end | |||
return ticks | |||
end | end | ||
| 140行目: | 214行目: | ||
-- [Constructor] | -- [Constructor] | ||
function __Scale__Continuous.__new(transform, untransform) | function __Scale__Continuous.__new(typename, domain, transform, untransform) | ||
local obj = { | local obj = { | ||
typename = | typename = typename, | ||
domain = | domain = domain, | ||
range = { 0, 100 }, | range = { 0, 100 }, | ||
_clamp = identity, | _clamp = identity, | ||
| 163行目: | 237行目: | ||
end | end | ||
self.domain = table. | self.domain = table.mapValues(newDomain, asNumber) | ||
self:_rescale() | self:_rescale() | ||
return self | return self | ||
| 176行目: | 250行目: | ||
self.range = table.map(newRange, asNumber) | self.range = table.map(newRange, asNumber) | ||
self:_rescale() | |||
return self | |||
end | |||
-- [Property] Set clamp | |||
function __Scale__Continuous:setClamp(newClamp) | |||
libraryUtil.checkType('setClamp', 1, newClamp, 'boolean') | |||
self._clamp = newClamp or identity | |||
self:_rescale() | self:_rescale() | ||
return self | return self | ||
| 184行目: | 267行目: | ||
local n = math.min(#self.domain, #self.range) | local n = math.min(#self.domain, #self.range) | ||
if self._clamp ~= identity then | if self._clamp ~= identity then | ||
self._clamp = clamper(self.domain[ | self._clamp = clamper(self.domain[1], self.domain[n]) | ||
end | end | ||
if n > 2 then | if n > 2 then | ||
| 204行目: | 287行目: | ||
if not self._output then | if not self._output then | ||
self._output = self._piecewise( | self._output = self._piecewise( | ||
table. | table.mapValues(self.domain, self._transform), | ||
self.range, | self.range, | ||
self._interpolate) | self._interpolate) | ||
| 219行目: | 302行目: | ||
self._input = self._piecewise( | self._input = self._piecewise( | ||
self.range, | self.range, | ||
table. | table.mapValues(self.domain, self._transform), | ||
interpolateValue) | interpolateValue) | ||
end | end | ||
return self._clamp(self._untransform(self._input(y))) | return self._clamp(self._untransform(self._input(y))) | ||
end | |||
-- [Function] Nice | |||
function __Scale__Continuous:_nice(floor, ceil) | |||
local i0 = 1 | |||
local i1 = #self.domain | |||
local start = self.domain[i0] | |||
local stop = self.domain[i1] | |||
if start > stop then | |||
start, stop = stop, start | |||
i0, i1 = i1, i0 | |||
end | |||
self.domain[i0] = floor(start) | |||
self.domain[i1] = ceil(stop) | |||
self:_rescale() | |||
return self | |||
end | end | ||
| 236行目: | 336行目: | ||
-- [Constructor] | -- [Constructor] | ||
function __Scale__Linear.new() | function __Scale__Linear.new() | ||
local obj = __Scale__Continuous.__new(identity, identity) | local obj = __Scale__Continuous.__new(__Scale__LinearType, unit, identity, identity) | ||
return setmetatable(obj, { __index = __Scale__Linear }) | return setmetatable(obj, { __index = __Scale__Linear }) | ||
end | |||
-- [Function] Get ticks | |||
function __Scale__Linear:ticks(count) | |||
libraryUtil.checkType('nice', 1, count, 'number', true) | |||
count = count or 10 | |||
return ticks(self.domain[1], self.domain[#self.domain], count) | |||
end | end | ||
| 261行目: | 369行目: | ||
self.domain[i0] = start | self.domain[i0] = start | ||
self.domain[i1] = stop | self.domain[i1] = stop | ||
self:_rescale() | |||
break | break | ||
elseif step > 0 then | elseif step > 0 then | ||
| 280行目: | 389行目: | ||
-- [Export] | -- [Export] | ||
scale.LinearScale = __Scale__Linear | scale.LinearScale = __Scale__Linear | ||
-- ========================== | |||
-- type: LogScale: Continuous | |||
-- ========================== | |||
local logUnit = { 1, 10 } | |||
local e = 2.7182818284590452 | |||
-- function: logn | |||
local function logn(x) | |||
return -math.log(-x) | |||
end | |||
-- function: expn | |||
local function expn(x) | |||
return -math.exp(-x) | |||
end | |||
-- function: fnlogp | |||
local function fnlogp(base) | |||
if base == e then | |||
return math.log | |||
else | |||
local y = 1 / math.log(base) | |||
return function(x) | |||
return math.log(x) * y | |||
end | |||
end | |||
end | |||
-- function: fnpowp | |||
local function fnpowp(base) | |||
if base == e then | |||
return math.exp | |||
else | |||
return function(x) | |||
return math.pow(base, x) | |||
end | |||
end | |||
end | |||
-- function: fnlogn | |||
local function fnlogn(base) | |||
if base == e then | |||
return function(x) | |||
return -math.log(-x) | |||
end | |||
else | |||
local y = 1 / math.log(base) | |||
return function(x) | |||
return -math.log(-x) * y | |||
end | |||
end | |||
end | |||
-- function: fnpown | |||
local function fnpown(base) | |||
if base == e then | |||
return function(x) | |||
return -math.exp(-x) | |||
end | |||
else | |||
return function(x) | |||
return -math.pow(base, -x) | |||
end | |||
end | |||
end | |||
-- function: reflect | |||
local function reflect(fn) | |||
return function(x) | |||
return -fn(-x) | |||
end | |||
end | |||
-- Define object | |||
local __Scale__LogType = 'log' | |||
local __Scale__Log = { | |||
__typename = __Scale__LogType, | |||
} | |||
setmetatable(__Scale__Log, { __index = __Scale__Continuous }) | |||
-- [Constructor] | |||
function __Scale__Log.new() | |||
local obj = __Scale__Continuous.__new(__Scale__LogType, logUnit, math.log, math.exp) | |||
obj.__base___rescale = obj._rescale | |||
obj.base = 10 | |||
obj._log = fnlogp(10) | |||
obj._pow = fnpowp(10) | |||
return setmetatable(obj, { __index = __Scale__Log }) | |||
end | |||
-- [Function] Get ticks | |||
function __Scale__Log:ticks(count) | |||
libraryUtil.checkType('nice', 1, count, 'number', true) | |||
count = count or 10 | |||
local startLinear = self.domain[1] | |||
local stopLinear = self.domain[#self.domain] | |||
if startLinear == stopLinear and count > 0 then | |||
return { startLinear } | |||
end | |||
local reverse = startLinear > stopLinear | |||
if reverse then | |||
startLinear, stopLinear = stopLinear, startLinear | |||
end | |||
local start = self._log(startLinear) | |||
local stop = self._log(stopLinear) | |||
local diff = stop - start | |||
local z | |||
if (not (self.base % 1)) and diff < count then | |||
start = math.floor(start) | |||
stop = math.ceil(stop) | |||
z = {} | |||
if startLinear > 0 then | |||
for i = start, stop do | |||
local p = self._pow(i) | |||
for k = 1, base - 1 do | |||
local tick = p * k | |||
if tick >= startLinear then | |||
if tick > stopLinear then | |||
break | |||
end | |||
table.insert(z, tick) | |||
end | |||
end | |||
end | |||
else | |||
for i = start, stop do | |||
local p = self._pow(i) | |||
for k = base - 1, 1, -1 do | |||
local tick = p * k | |||
if tick >= startLinear then | |||
if tick > stopLinear then | |||
break | |||
end | |||
table.insert(z, tick) | |||
end | |||
end | |||
end | |||
end | |||
if 2 * #z < count then | |||
z = ticks(startLinear, stopLinear, count) | |||
end | |||
else | |||
z = table.mapValues(ticks(start, stop, math.min(diff, count)), self._pow) | |||
end | |||
if reverse then | |||
table.reverse(z) | |||
end | |||
return z | |||
end | |||
-- [Property] Set base | |||
function __Scale__Log:setBase(newBase) | |||
libraryUtil.checkType('setBase', 1, newBase, 'number') | |||
if self.base ~= newBase then | |||
self.base = newBase | |||
self:_rescale() | |||
end | |||
return self | |||
end | |||
-- [Function] Rescale | |||
function __Scale__Log:_rescale() | |||
if self.domain[1] < 0 then | |||
self._log = fnlogn(self.base) | |||
self._pow = fnpown(self.base) | |||
self._transform = logn | |||
self._untransform = expn | |||
else | |||
self._log = fnlogp(self.base) | |||
self._pow = fnpowp(self.base) | |||
self._transform = math.log | |||
self._untransform = math.exp | |||
end | |||
self:__base___rescale() | |||
end | |||
-- [Function] Nice | |||
function __Scale__Log:nice() | |||
local floor = function(x) | |||
return self._pow(math.floor(self._log(x))) | |||
end | |||
local ceil = function(x) | |||
return self._pow(math.ceil(self._log(x))) | |||
end | |||
return self:_nice(floor, ceil) | |||
end | |||
-- [Export] | |||
scale.LogScale = __Scale__Log | |||
return scale | return scale | ||
2021年9月2日 (木) 12:33時点における最新版
このモジュールについての説明文ページを モジュール:Utility/Scale/doc に作成できます
local libraryUtil = require('libraryUtil')
local table = require('Module:TableExtensions')
local ninf = -math.huge
local pinf = math.huge
function math.isInf(v)
return v == pinf or v == ninf
end
function math.isNaN(v)
--return tostring(v) == '-nan'
return v ~= v
end
local log10e = 1 / math.log(10)
function math.log10(x)
return math.log(x) * log10e
end
local eps = 2.2204460492503131e-16
function math.isSame(a, b)
local diff = math.abs(a - b)
local aa = math.abs(a)
local ab = math.abs(b)
if aa > ab then
return diff <= eps * ab
else
return diff <= eps * aa
end
end
-- ============
-- Define utils
-- ============
local unit = { 0, 1 }
local function identity(num)
return num
end
-- function: asNumber - as number if possible
local function asNumber(value)
local typename = type(value)
if typename == 'number' then
return value
elseif typename == 'string' then
return tonumber(value)
else
error('The "value" type must be "number" or "string".')
end
end
-- function: constant
local function constant(num)
return function()
return num
end
end
-- function: normalize
local function normalize(a, b)
b = b - a
if math.isNaN(b) then
return constant(b)
elseif b == 0 then
return constant(0.5)
else
return function(x)
return (x - a) / b
end
end
end
-- function: interpolateValue
local function interpolateValue(a, b)
return function(t)
return a + (b - a) * t
end
end
-- function: clamper - generate clamp function
local function clamper(a, b)
if a > b then
a, b = b, a
end
return function(x)
return math.max(a, math.min(b, x))
end
end
-- function: bimap
local function bimap(domain, range, interpolate)
local d1 = domain[1]
local d2 = domain[2]
local r1 = range[1]
local r2 = range[2]
if d2 < d1 then
d1 = normalize(d2, d1)
r1 = interpolate(r2, r1)
else
d1 = normalize(d1, d2)
r1 = interpolate(r1, r2)
end
return function(x)
return r1(d1(x))
end
end
-- function: tickIncrement
local e10 = math.sqrt(50)
local e5 = math.sqrt(10)
local e2h = math.sqrt(5)
local e2 = math.sqrt(2)
local function tickIncrement(start, stop, count)
local step = (stop - start) / math.max(0, count)
local power = math.floor(math.log10(step))
local error = step / (10 ^ power)
local nice
if power >= 0 then
if error >= e10 then
nice = 10
elseif error >= e5 then
nice = 5
elseif error >= e2h then
nice = 2.5
elseif error >= e2 then
nice = 2
else
nice = 1
end
return nice * (10 ^ power)
else
if error >= e10 then
nice = 10
elseif error >= e5 then
nice = 5
elseif error >= e2h then
nice = 2.5
elseif error >= e2 then
nice = 2
else
nice = 1
end
return -(10 ^ -power) / nice
end
end
-- function: ticks
local function ticks(start, stop, count)
if start == stop and count > 0 then
return { start }
end
local reverse = start > stop
if reverse then
start, stop = stop, start
end
local step = tickIncrement(start, stop, count)
if step == 0 or math.isInf(step) then
return {}
end
local ticks = {}
if step > 0 then
local r0 = math.floor(start / step + 0.5)
local r1 = math.floor(stop / step + 0.5)
local r0s = r0 * step
local r1s = r1 * step
if r0s < start or math.isSame(r0s, start) then
r0 = r0 + 1
end
if r1s > stop or math.isSame(r1s, stop) then
r1 = r1 - 1
end
for i = 0, r1 - r0 do
ticks[i + 1] = (r0 + i) * step
end
else
step = -step
local r0 = math.floor(start * step + 0.5)
local r1 = math.floor(stop * step + 0.5)
local r0s = r0 / step
local r1s = r1 / step
if r0s < start or math.isSame(r0s, start) then
r0 = r0 + 1
end
if r1s > stop or math.isSame(r1s, stop) then
r1 = r1 - 1
end
for i = 0, r1 - r0 do
ticks[i + 1] = (r0 + i) / step
end
end
if reverse then
table.reverse(ticks)
end
return ticks
end
-- ================
-- namespace: scale
-- ================
local scale = {}
-- ================
-- type: Continuous
-- ================
local __Scale__ContinuousType = 'continuous'
local __Scale__Continuous = {
__typename = __Scale__ContinuousType,
}
-- [Constructor]
function __Scale__Continuous.__new(typename, domain, transform, untransform)
local obj = {
typename = typename,
domain = domain,
range = { 0, 100 },
_clamp = identity,
_piecewise = bimap,
_interpolate = interpolateValue,
_transform = transform,
_untransform = untransform,
_input = nil,
_output = nil,
}
return setmetatable(obj, { __index = __Scale__Continuous })
end
-- [Property] Set domain
function __Scale__Continuous:setDomain(newDomain)
libraryUtil.checkType('setDomain', 1, newDomain, 'table')
if #newDomain < 2 then
error('The "domain" table size must be at least 2.')
end
self.domain = table.mapValues(newDomain, asNumber)
self:_rescale()
return self
end
-- [Property] Set range
function __Scale__Continuous:setRange(newRange)
libraryUtil.checkType('setRange', 1, newRange, 'table')
if #newRange < 2 then
error('The "range" table size must be at least 2.')
end
self.range = table.map(newRange, asNumber)
self:_rescale()
return self
end
-- [Property] Set clamp
function __Scale__Continuous:setClamp(newClamp)
libraryUtil.checkType('setClamp', 1, newClamp, 'boolean')
self._clamp = newClamp or identity
self:_rescale()
return self
end
-- [Function] Rescale
function __Scale__Continuous:_rescale()
local n = math.min(#self.domain, #self.range)
if self._clamp ~= identity then
self._clamp = clamper(self.domain[1], self.domain[n])
end
if n > 2 then
error('The multiple values of the domain aren\'t currently supported.')
else
self._piecewise = bimap
end
self._input = nil
self._output = nil
end
-- [Function] Scale
function __Scale__Continuous:scale(x)
libraryUtil.checkType('scale', 1, x, 'number')
if math.isNaN(x) then
return nil
else
if not self._output then
self._output = self._piecewise(
table.mapValues(self.domain, self._transform),
self.range,
self._interpolate)
end
return self._output(self._transform(self._clamp(x)))
end
end
-- [Function] Invert
function __Scale__Continuous:invert(y)
libraryUtil.checkType('invert', 1, y, 'number')
if not self._input then
self._input = self._piecewise(
self.range,
table.mapValues(self.domain, self._transform),
interpolateValue)
end
return self._clamp(self._untransform(self._input(y)))
end
-- [Function] Nice
function __Scale__Continuous:_nice(floor, ceil)
local i0 = 1
local i1 = #self.domain
local start = self.domain[i0]
local stop = self.domain[i1]
if start > stop then
start, stop = stop, start
i0, i1 = i1, i0
end
self.domain[i0] = floor(start)
self.domain[i1] = ceil(stop)
self:_rescale()
return self
end
-- =============================
-- type: LinearScale: Continuous
-- =============================
local __Scale__LinearType = 'linear'
local __Scale__Linear = {
__typename = __Scale__LinearType,
}
setmetatable(__Scale__Linear, { __index = __Scale__Continuous })
-- [Constructor]
function __Scale__Linear.new()
local obj = __Scale__Continuous.__new(__Scale__LinearType, unit, identity, identity)
return setmetatable(obj, { __index = __Scale__Linear })
end
-- [Function] Get ticks
function __Scale__Linear:ticks(count)
libraryUtil.checkType('nice', 1, count, 'number', true)
count = count or 10
return ticks(self.domain[1], self.domain[#self.domain], count)
end
-- [Function] Nice
function __Scale__Linear:nice(count)
libraryUtil.checkType('nice', 1, count, 'number', true)
count = count or 10
local i0 = 1
local i1 = #self.domain
local start = self.domain[i0]
local stop = self.domain[i1]
if start > stop then
start, stop = stop, start
i0, i1 = i1, i0
end
local prestep, step
local maxIter = 5
while maxIter > 0 do
step = tickIncrement(start, stop, count)
if step == prestep then
self.domain[i0] = start
self.domain[i1] = stop
self:_rescale()
break
elseif step > 0 then
start = math.floor(start / step) * step
stop = math.ceil (stop / step) * step
elseif step < 0 then
start = math.ceil (start * step) / step
stop = math.floor(stop * step) / step
else
break
end
prestep = step
maxIter = maxIter - 1
end
return self
end
-- [Export]
scale.LinearScale = __Scale__Linear
-- ==========================
-- type: LogScale: Continuous
-- ==========================
local logUnit = { 1, 10 }
local e = 2.7182818284590452
-- function: logn
local function logn(x)
return -math.log(-x)
end
-- function: expn
local function expn(x)
return -math.exp(-x)
end
-- function: fnlogp
local function fnlogp(base)
if base == e then
return math.log
else
local y = 1 / math.log(base)
return function(x)
return math.log(x) * y
end
end
end
-- function: fnpowp
local function fnpowp(base)
if base == e then
return math.exp
else
return function(x)
return math.pow(base, x)
end
end
end
-- function: fnlogn
local function fnlogn(base)
if base == e then
return function(x)
return -math.log(-x)
end
else
local y = 1 / math.log(base)
return function(x)
return -math.log(-x) * y
end
end
end
-- function: fnpown
local function fnpown(base)
if base == e then
return function(x)
return -math.exp(-x)
end
else
return function(x)
return -math.pow(base, -x)
end
end
end
-- function: reflect
local function reflect(fn)
return function(x)
return -fn(-x)
end
end
-- Define object
local __Scale__LogType = 'log'
local __Scale__Log = {
__typename = __Scale__LogType,
}
setmetatable(__Scale__Log, { __index = __Scale__Continuous })
-- [Constructor]
function __Scale__Log.new()
local obj = __Scale__Continuous.__new(__Scale__LogType, logUnit, math.log, math.exp)
obj.__base___rescale = obj._rescale
obj.base = 10
obj._log = fnlogp(10)
obj._pow = fnpowp(10)
return setmetatable(obj, { __index = __Scale__Log })
end
-- [Function] Get ticks
function __Scale__Log:ticks(count)
libraryUtil.checkType('nice', 1, count, 'number', true)
count = count or 10
local startLinear = self.domain[1]
local stopLinear = self.domain[#self.domain]
if startLinear == stopLinear and count > 0 then
return { startLinear }
end
local reverse = startLinear > stopLinear
if reverse then
startLinear, stopLinear = stopLinear, startLinear
end
local start = self._log(startLinear)
local stop = self._log(stopLinear)
local diff = stop - start
local z
if (not (self.base % 1)) and diff < count then
start = math.floor(start)
stop = math.ceil(stop)
z = {}
if startLinear > 0 then
for i = start, stop do
local p = self._pow(i)
for k = 1, base - 1 do
local tick = p * k
if tick >= startLinear then
if tick > stopLinear then
break
end
table.insert(z, tick)
end
end
end
else
for i = start, stop do
local p = self._pow(i)
for k = base - 1, 1, -1 do
local tick = p * k
if tick >= startLinear then
if tick > stopLinear then
break
end
table.insert(z, tick)
end
end
end
end
if 2 * #z < count then
z = ticks(startLinear, stopLinear, count)
end
else
z = table.mapValues(ticks(start, stop, math.min(diff, count)), self._pow)
end
if reverse then
table.reverse(z)
end
return z
end
-- [Property] Set base
function __Scale__Log:setBase(newBase)
libraryUtil.checkType('setBase', 1, newBase, 'number')
if self.base ~= newBase then
self.base = newBase
self:_rescale()
end
return self
end
-- [Function] Rescale
function __Scale__Log:_rescale()
if self.domain[1] < 0 then
self._log = fnlogn(self.base)
self._pow = fnpown(self.base)
self._transform = logn
self._untransform = expn
else
self._log = fnlogp(self.base)
self._pow = fnpowp(self.base)
self._transform = math.log
self._untransform = math.exp
end
self:__base___rescale()
end
-- [Function] Nice
function __Scale__Log:nice()
local floor = function(x)
return self._pow(math.floor(self._log(x)))
end
local ceil = function(x)
return self._pow(math.ceil(self._log(x)))
end
return self:_nice(floor, ceil)
end
-- [Export]
scale.LogScale = __Scale__Log
return scale