Files
ElvUIModernized/ElvUI/Core/Math.lua
T
2018-11-01 12:12:17 -05:00

452 lines
13 KiB
Lua

local E, L, V, P, G = unpack(ElvUI); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
--Cache global variables
--Lua functions
local unpack, assert, tonumber, type, pairs = unpack, assert, tonumber, type, pairs
local getn, tinsert, tremove = table.getn, tinsert, tremove
local abs, ceil, floor, modf, mod = math.abs, math.ceil, math.floor, math.modf, math.mod
local find, format, byte, len, sub, gsub, upper, split, utf8sub = string.find, string.format, string.byte, string.len, string.sub, string.gsub, string.upper, string.split, string.utf8sub
--WoW API / Variables
local GetScreenWidth, GetScreenHeight = GetScreenWidth, GetScreenHeight
local CreateFrame = CreateFrame
--Return short value of a number
local shortValueDec, value
function E:ShortValue(v)
shortValueDec = format("%%.%df", E.db.general.decimalLength or 1)
value = abs(v)
if E.db.general.numberPrefixStyle == "METRIC" then
if value >= 1e12 then
return format(shortValueDec.."T", v / 1e12)
elseif value >= 1e9 then
return format(shortValueDec.."G", v / 1e9)
elseif value >= 1e6 then
return format(shortValueDec.."M", v / 1e6)
elseif value >= 1e3 then
return format(shortValueDec.."k", v / 1e3)
else
return format("%.0f", v)
end
elseif E.db.general.numberPrefixStyle == "CHINESE" then
if value >= 1e8 then
return format(shortValueDec.."Y", v / 1e8)
elseif value >= 1e4 then
return format(shortValueDec.."W", v / 1e4)
else
return format("%.0f", v)
end
elseif E.db.general.numberPrefixStyle == "KOREAN" then
if value >= 1e8 then
return format(shortValueDec.."억", v / 1e8)
elseif value >= 1e4 then
return format(shortValueDec.."만", v / 1e4)
elseif value >= 1e3 then
return format(shortValueDec.."천", v / 1e3)
else
return format("%.0f", v)
end
elseif E.db.general.numberPrefixStyle == "GERMAN" then
if value >= 1e12 then
return format(shortValueDec.."Bio", v / 1e12)
elseif value >= 1e9 then
return format(shortValueDec.."Mrd", v / 1e9)
elseif value >= 1e6 then
return format(shortValueDec.."Mio", v / 1e6)
elseif value >= 1e3 then
return format(shortValueDec.."Tsd", v / 1e3)
else
return format("%.0f", v)
end
else
if value >= 1e12 then
return format(shortValueDec.."T", v / 1e12)
elseif value >= 1e9 then
return format(shortValueDec.."B", v / 1e9)
elseif value >= 1e6 then
return format(shortValueDec.."M", v / 1e6)
elseif value >= 1e3 then
return format(shortValueDec.."K", v / 1e3)
else
return format("%.0f", v)
end
end
end
function E:IsEvenNumber(num)
return mod(num, 2) == 0
end
function E:ColorGradient(perc, r1, g1, b1, r2, g2, b2, r3, g3, b3)
if perc >= 1 then
return r3, g3, b3
elseif perc <= 0 then
return r1, g1, b1
end
local segment, relperc = modf(perc)
if segment > 0 then
r1, g1, b1, r2, g2, b2 = r2, g2, b2, r3, g3, b3
end
return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
end
function E:Round(num, idp)
if idp and idp > 0 then
local mult = 10 ^ idp
return floor(num * mult + 0.5) / mult
end
return floor(num + 0.5)
end
function E:Truncate(v, decimals)
return v - (mod(v, 0.1 ^ (decimals or 0)))
end
function E:RGBToHex(r, g, b)
r = r <= 1 and r >= 0 and r or 1
g = g <= 1 and g >= 0 and g or 1
b = b <= 1 and b >= 0 and b or 1
return format("|cff%02x%02x%02x", r*255, g*255, b*255)
end
function E:HexToRGB(hex)
local rhex, ghex, bhex = sub(hex, 1, 2), sub(hex, 3, 4), sub(hex, 5, 6)
return tonumber(rhex, 16), tonumber(ghex, 16), tonumber(bhex, 16)
end
function E:FramesOverlap(frameA, frameB)
if not frameA or not frameB then return end
local sA, sB = frameA:GetEffectiveScale(), frameB:GetEffectiveScale()
if not sA or not sB then return end
local frameALeft = frameA:GetLeft()
local frameARight = frameA:GetRight()
local frameABottom = frameA:GetBottom()
local frameATop = frameA:GetTop()
local frameBLeft = frameB:GetLeft()
local frameBRight = frameB:GetRight()
local frameBBottom = frameB:GetBottom()
local frameBTop = frameB:GetTop()
if not frameALeft or not frameARight or not frameABottom or not frameATop then return end
if not frameBLeft or not frameBRight or not frameBBottom or not frameBTop then return end
return ((frameALeft*sA) < (frameBRight*sB))
and ((frameBLeft*sB) < (frameARight*sA))
and ((frameABottom*sA) < (frameBTop*sB))
and ((frameBBottom*sB) < (frameATop*sA))
end
function E:GetScreenQuadrant(frame)
local x, y = frame:GetCenter()
local screenWidth = GetScreenWidth()
local screenHeight = GetScreenHeight()
local point
if not frame:GetCenter() then
return "UNKNOWN", frame:GetName()
end
if (x > (screenWidth / 3) and x < (screenWidth / 3)*2) and y > (screenHeight / 3)*2 then
point = "TOP"
elseif x < (screenWidth / 3) and y > (screenHeight / 3)*2 then
point = "TOPLEFT"
elseif x > (screenWidth / 3)*2 and y > (screenHeight / 3)*2 then
point = "TOPRIGHT"
elseif (x > (screenWidth / 3) and x < (screenWidth / 3)*2) and y < (screenHeight / 3) then
point = "BOTTOM"
elseif x < (screenWidth / 3) and y < (screenHeight / 3) then
point = "BOTTOMLEFT"
elseif x > (screenWidth / 3)*2 and y < (screenHeight / 3) then
point = "BOTTOMRIGHT"
elseif x < (screenWidth / 3) and (y > (screenHeight / 3) and y < (screenHeight / 3)*2) then
point = "LEFT"
elseif x > (screenWidth / 3)*2 and y < (screenHeight / 3)*2 and y > (screenHeight / 3) then
point = "RIGHT"
else
point = "CENTER"
end
return point
end
function E:GetXYOffset(position, override)
local default = E.Spacing
local x, y = override or default, override or default
if position == "TOP" then
return 0, y
elseif position == "TOPLEFT" then
return x, y
elseif position == "TOPRIGHT" then
return -x, y
elseif position == "BOTTOM" then
return 0, -y
elseif position == "BOTTOMLEFT" then
return x, -y
elseif position == "BOTTOMRIGHT" then
return -x, -y
elseif position == "LEFT" then
return -x, 0
elseif position == "RIGHT" then
return x, 0
elseif position == "CENTER" then
return 0, 0
end
end
local styles = {
-- keep percents in this table with `PERCENT` in the key, and `%.1f%%` in the value somewhere.
-- we use these two things to follow our setting for decimal length. they need to be EXACT.
["CURRENT"] = "%s",
["CURRENT_MAX"] = "%s - %s",
["CURRENT_PERCENT"] = "%s - %.1f%%",
["CURRENT_MAX_PERCENT"] = "%s - %s | %.1f%%",
["PERCENT"] = "%.1f%%",
["DEFICIT"] = "-%s"
}
local gftDec, gftUseStyle, gftDeficit
function E:GetFormattedText(style, min, max)
assert(styles[style], "Invalid format style: "..style)
assert(min, "You need to provide a current value. Usage: E:GetFormattedText(style, min, max)")
assert(max, "You need to provide a maximum value. Usage: E:GetFormattedText(style, min, max)")
if max == 0 then max = 1 end
gftDec = (E.db.general.decimalLength or 1)
if (gftDec ~= 1) and find(style, "PERCENT") then
gftUseStyle = gsub(styles[style], "%%%.1f%%%%", "%%."..gftDec.."f%%%%")
else
gftUseStyle = styles[style]
end
if style == "DEFICIT" then
gftDeficit = max - min
return ((gftDeficit > 0) and format(gftUseStyle, E:ShortValue(gftDeficit))) or ""
elseif style == "PERCENT" then
return format(gftUseStyle, min / max * 100)
elseif style == "CURRENT" or ((style == "CURRENT_MAX" or style == "CURRENT_MAX_PERCENT" or style == "CURRENT_PERCENT") and min == max) then
return format(styles["CURRENT"], E:ShortValue(min))
elseif style == "CURRENT_MAX" then
return format(gftUseStyle, E:ShortValue(min), E:ShortValue(max))
elseif style == "CURRENT_PERCENT" then
return format(gftUseStyle, E:ShortValue(min), min / max * 100)
elseif style == "CURRENT_MAX_PERCENT" then
return format(gftUseStyle, E:ShortValue(min), E:ShortValue(max), min / max * 100)
end
end
function E:AbbreviateString(string, allUpper)
local newString = ""
local words = {split(" ", string)}
for _, word in pairs(words) do
word = utf8sub(word, 1, 1)
if allUpper then
word = upper(word)
end
newString = newString..word
end
return newString
end
function E:ShortenString(string, numChars, dots)
local bytes = len(string)
if bytes <= numChars then
return string
else
local len, pos = 0, 1
while pos <= bytes do
len = len + 1
local c = byte(string, pos)
if c > 0 and c <= 127 then
pos = pos + 1
elseif c >= 192 and c <= 223 then
pos = pos + 2
elseif c >= 224 and c <= 239 then
pos = pos + 3
elseif c >= 240 and c <= 247 then
pos = pos + 4
end
if len == numChars then break end
end
if len == numChars and pos <= bytes then
return sub(string, 1, pos - 1)..(dots and "..." or "")
else
return string
end
end
end
local waitTable = {}
local waitFrame
function E:Delay(delay, func, ...)
if (type(delay) ~= "number") or (type(func) ~= "function") then
return false
end
if waitFrame == nil then
waitFrame = CreateFrame("Frame", "WaitFrame", E.UIParent)
waitFrame:SetScript("OnUpdate", function()
local waitRecord, waitDelay, waitFunc, waitParams
local i, count = 1, getn(waitTable)
while i <= count do
waitRecord = tremove(waitTable, i)
waitDelay = tremove(waitRecord, 1)
waitFunc = tremove(waitRecord, 1)
waitParams = tremove(waitRecord, 1)
if waitDelay > arg1 then
tinsert(waitTable, i, {waitDelay - arg1, waitFunc, waitParams})
i = i + 1
else
count = count - 1
waitFunc(unpack(waitParams))
end
end
end)
end
tinsert(waitTable, {delay, func, arg})
return true
end
function E:StringTitle(str)
return gsub(str, "(.)", upper, 1)
end
E.TimeThreshold = 3
E.TimeColors = {
[0] = "|cffeeeeee",
[1] = "|cffeeeeee",
[2] = "|cffeeeeee",
[3] = "|cffeeeeee",
[4] = "|cfffe0000",
[5] = "|cff909090", --mmss
[6] = "|cff707070", --hhmm
}
E.TimeFormats = {
[0] = {"%dd", "%dd"},
[1] = {"%dh", "%dh"},
[2] = {"%dm", "%dm"},
[3] = {"%ds", "%d"},
[4] = {"%.1fs", "%.1f"},
[5] = {"%d:%02d", "%d:%02d"}, --mmss
[6] = {"%d:%02d", "%d:%02d"}, --hhmm
}
local DAY, HOUR, MINUTE = 86400, 3600, 60
local DAYISH, HOURISH, MINUTEISH = HOUR * 23.5, MINUTE * 59.5, 59.5
local HALFDAYISH, HALFHOURISH, HALFMINUTEISH = DAY/2 + 0.5, HOUR/2 + 0.5, MINUTE/2 + 0.5
function E:GetTimeInfo(s, threshhold, hhmm, mmss)
if s < MINUTE then
if s >= threshhold then
return floor(s), 3, 0.51
else
return s, 4, 0.051
end
elseif s < HOUR then
if mmss and s < mmss then
return s/MINUTE, 5, 0.51, mod(s, MINUTE)
else
local minutes = floor((s/MINUTE)+.5)
if hhmm and s < (hhmm * MINUTE) then
return s/HOUR, 6, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH), mod(minutes, MINUTE)
else
return ceil(s / MINUTE), 2, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH)
end
end
elseif s < DAY then
if mmss and s < mmss then
return s/MINUTE, 5, 0.51, mod(s, MINUTE)
elseif hhmm and s < (hhmm * MINUTE) then
local minutes = floor((s/MINUTE)+.5)
return s/HOUR, 6, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH), mod(minutes, MINUTE)
else
local hours = floor((s/HOUR)+.5)
return ceil(s / HOUR), 1, hours > 1 and (s - (hours*HOUR - HALFHOURISH)) or (s - HOURISH)
end
else
local days = floor((s/DAY)+.5)
return ceil(s / DAY), 0, days > 1 and (s - (days*DAY - HALFDAYISH)) or (s - DAYISH)
end
end
local COLOR_COPPER = "|cffeda55f"
local COLOR_SILVER = "|cffc7c7cf"
local COLOR_GOLD = "|cffffd700"
function E:FormatMoney(amount, style)
local coppername = L.copperabbrev
local silvername = L.silverabbrev
local goldname = L.goldabbrev
local value = abs(amount)
local gold = floor(value / 10000)
local silver = floor(mod(value / 100, 100))
local copper = floor(mod(value, 100))
if not style or style == "SMART" then
local str = ""
if gold > 0 then
str = format("%d%s%s", gold, goldname, (silver > 0 or copper > 0) and " " or "")
end
if silver > 0 then
str = format("%s%d%s%s", str, silver, silvername, copper > 0 and " " or "")
end
if copper > 0 or value == 0 then
str = format("%s%d%s", str, copper, coppername)
end
return str
end
if style == "FULL" then
if gold > 0 then
return format("%d%s %d%s %d%s", gold, goldname, silver, silvername, copper, coppername)
elseif silver > 0 then
return format("%d%s %d%s", silver, silvername, copper, coppername)
else
return format("%d%s", copper, coppername)
end
elseif style == "SHORT" then
if gold > 0 then
return format("%.1f%s", amount / 10000, goldname)
elseif silver > 0 then
return format("%.1f%s", amount / 100, silvername)
else
return format("%d%s", amount, coppername)
end
elseif style == "SHORTINT" then
if gold > 0 then
return format("%d%s", gold, goldname)
elseif silver > 0 then
return format("%d%s", silver, silvername)
else
return format("%d%s", copper, coppername)
end
elseif style == "CONDENSED" then
if gold > 0 then
return format("%s%d|r.%s%02d|r.%s%02d|r", COLOR_GOLD, gold, COLOR_SILVER, silver, COLOR_COPPER, copper)
elseif silver > 0 then
return format("%s%d|r.%s%02d|r", COLOR_SILVER, silver, COLOR_COPPER, copper)
else
return format("%s%d|r", COLOR_COPPER, copper)
end
elseif style == "BLIZZARD" then
if gold > 0 then
return format("%s%s %d%s %d%s", gold, goldname, silver, silvername, copper, coppername)
elseif silver > 0 then
return format("%d%s %d%s", silver, silvername, copper, coppername)
else
return format("%d%s", copper, coppername)
end
end
return self:FormatMoney(amount, "SMART")
end