🌟 | 現在、 鉄壁ヘッドショットには対応済みです。 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:Utility/Scale」の版間の差分
ナビゲーションに移動
検索に移動
(LogScaleの追加) |
(ticks算出のロジックを再改善) |
||
(同じ利用者による、間の2版が非表示) | |||
14行目: | 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 | ||
149行目: | 161行目: | ||
local ticks = {} | local ticks = {} | ||
if step > 0 then | if step > 0 then | ||
local r0 = math.floor(start / step) | local r0 = math.floor(start / step + 0.5) | ||
local r1 = math. | 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 | r0 = r0 + 1 | ||
end | end | ||
if | if r1s > stop or math.isSame(r1s, stop) then | ||
r1 = r1 - 1 | r1 = r1 - 1 | ||
end | end | ||
164行目: | 178行目: | ||
step = -step | step = -step | ||
local r0 = math. | local r0 = math.floor(start * step + 0.5) | ||
local r1 = math.floor(stop * step) | 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 | r0 = r0 + 1 | ||
end | end | ||
if | if r1s > stop or math.isSame(r1s, stop) then | ||
r1 = r1 - 1 | r1 = r1 - 1 | ||
end | end | ||
198行目: | 214行目: | ||
-- [Constructor] | -- [Constructor] | ||
function __Scale__Continuous.__new(domain, transform, untransform) | function __Scale__Continuous.__new(typename, domain, transform, untransform) | ||
local obj = { | local obj = { | ||
typename = | typename = typename, | ||
domain = domain, | domain = domain, | ||
range = { 0, 100 }, | range = { 0, 100 }, | ||
320行目: | 336行目: | ||
-- [Constructor] | -- [Constructor] | ||
function __Scale__Linear.new() | function __Scale__Linear.new() | ||
local obj = __Scale__Continuous.__new(unit, 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 | end | ||
456行目: | 472行目: | ||
-- [Constructor] | -- [Constructor] | ||
function __Scale__Log.new() | function __Scale__Log.new() | ||
local obj = __Scale__Continuous.__new(logUnit, math.log, math.exp) | local obj = __Scale__Continuous.__new(__Scale__LogType, logUnit, math.log, math.exp) | ||
obj.__base___rescale = obj._rescale | obj.__base___rescale = obj._rescale | ||
obj.base = 10 | obj.base = 10 |
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