You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
9.2 KiB

local _, MDT = ...
3 years ago
local twipe, tinsert, tremove, tgetn = table.wipe, table.insert, table.remove, table.getn
-- return true if a is more lower-left than b
local function is_lower_left(a, b)
3 years ago
if a[1] < b[1] then return true end
if a[1] > b[1] then return false end
if a[2] < b[2] then return true end
if a[2] > b[2] then return true end
return false
end
-- return true if c is left of line a-b
local function is_left_of(a, b, c)
3 years ago
local u1 = b[1] - a[1]
local v1 = b[2] - a[2]
local u2 = c[1] - a[1]
local v2 = c[2] - a[2]
return u1 * v2 - v1 * u2 < 0
end
local function convex_hull(pts)
3 years ago
local lower_left = 1
for i = 2, #pts do
if is_lower_left(pts[i], pts[lower_left]) then lower_left = i end
end
local hull = {}
local final = 1
local tries = 0
repeat
table.insert(hull, lower_left)
final = 1
for j = 2, #pts do
if lower_left == final or is_left_of(pts[lower_left], pts[final], pts[j]) then final = j end
end
3 years ago
lower_left = final
tries = tries + 1
until final == hull[1] or tries > 100 --deadlocked here otherwise?!?
3 years ago
local hullpts = {}
for _, index in ipairs(hull) do
table.insert(hullpts, pts[index])
end
return hullpts
end
3 years ago
local function cross(a, b)
return a[1] * b[2] - a[2] * b[1]
end
local function area(points)
3 years ago
local res = cross(points[#points], points[1])
for i = 1, #points - 1 do
res = res + cross(points[i], points[i + 1])
end
return math.abs(res) / 2
end
local function centroid(pts)
3 years ago
local rx = 0
local ry = 0
for k, v in pairs(pts) do
rx = rx + v[1]
ry = ry + v[2]
3 years ago
end
rx = rx / #pts
ry = ry / #pts
3 years ago
return { rx, ry }
end
local function expand_polygon(poly, numCirclePoints)
3 years ago
local res = {}
local resIndex = 1
for i = 1, #poly do
local x = poly[i][1]
local y = poly[i][2]
local r = poly[i][3] * 10
local adjustedNumPoints = math.max(1, math.floor(numCirclePoints * poly[i][3]))
for j = 1, adjustedNumPoints do
local cx = x + r * math.cos(2 * math.pi / adjustedNumPoints * j)
local cy = y + r * math.sin(2 * math.pi / adjustedNumPoints * j)
res[resIndex] = { cx, cy, r }
resIndex = resIndex + 1
end
3 years ago
end
return res
end
---TexturePool
local activeTextures = {}
local texturePool = {}
local function getTexture()
3 years ago
local size = tgetn(texturePool)
if size == 0 then
return MDT.main_frame.mapPanelFrame:CreateTexture(nil, "OVERLAY", nil, 0)
3 years ago
else
local tex = texturePool[size]
tremove(texturePool, size)
tex:SetRotation(0)
tex:SetTexCoord(0, 1, 0, 1)
tex:ClearAllPoints()
tex.coords = nil
tex.points = nil
return tex
end
end
3 years ago
local function releaseTexture(tex)
3 years ago
tex:Hide()
tinsert(texturePool, tex)
end
---ReleaseAllActiveTextures
function MDT:ReleaseHullTextures()
3 years ago
for k, tex in pairs(activeTextures) do
releaseTexture(tex)
end
twipe(activeTextures)
end
--make a pool for fontStrings
local activeFontStrings = {}
local fontStringPool = {}
local frameIndex = 0
local function getFontString()
local size = tgetn(fontStringPool)
if size == 0 then
local fsFrame = CreateFrame("Frame", "MDTFontStringContainerFrame" .. frameIndex, MDT.main_frame.mapPanelFrame)
frameIndex = frameIndex + 1
fsFrame:SetFrameStrata("HIGH")
fsFrame:SetFrameLevel(100)
fsFrame:SetWidth(40)
fsFrame:SetHeight(40)
local fs = fsFrame:CreateFontString(nil, "OVERLAY", nil, 0)
fs:SetPoint("CENTER", 0, 0)
fs:SetJustifyH("CENTER")
fs:SetJustifyV("MIDDLE")
fs:SetTextColor(1, 1, 1, 1)
fs:SetFontObject("GameFontNormalMed3Outline")
fsFrame.fs = fs
return fsFrame
else
local fsFrame = fontStringPool[size]
tremove(fontStringPool, size)
fsFrame.fs:SetText("")
return fsFrame
end
end
local function releaseFontString(fsFrame)
fsFrame:Hide()
tinsert(fontStringPool, fsFrame)
end
function MDT:ReleaseHullFontStrings()
for k, fsFrame in pairs(activeFontStrings) do
releaseFontString(fsFrame)
end
twipe(activeFontStrings)
end
function MDT:DrawHullFontString(hull, pullIdx)
--2. get centroid of each pull
local center
if hull and hull[#hull] then
if #hull > 2 then
center = centroid(hull)
center[1] = center[1]
center[2] = center[2]
elseif #hull == 2 then
local x1 = hull[1][1]
local y1 = hull[1][2]
local x2 = hull[2][1]
local y2 = hull[2][2]
center = { (x1 + x2) / 2, (y1 + y2) / 2 }
elseif #hull == 1 then
local x1 = hull[1][1]
local y1 = hull[1][2]
center = { x1, y1 + 15 }
end
end
if not center then return end
local fsFrame = getFontString()
fsFrame.fs:SetText(pullIdx)
fsFrame:ClearAllPoints()
fsFrame:SetSize(40, 40)
fsFrame:SetPoint("CENTER", MDT.main_frame.mapPanelTile1, "TOPLEFT", center[1], center[2])
fsFrame:Show()
tinsert(activeFontStrings, fsFrame)
end
function MDT:DrawHullCircle(x, y, size, color, layer, layerSublevel)
3 years ago
local circle = getTexture()
circle:SetDrawLayer(layer, layerSublevel)
circle:SetTexture("Interface\\AddOns\\MythicDungeonTools\\Textures\\Circle_White")
circle:SetVertexColor(color.r, color.g, color.b, color.a)
circle:SetWidth(1.1 * size)
circle:SetHeight(1.1 * size)
circle:ClearAllPoints()
circle:SetPoint("CENTER", MDT.main_frame.mapPanelTile1, "TOPLEFT", x, y)
circle:Show()
tinsert(activeTextures, circle)
end
function MDT:DrawHullLine(x, y, a, b, size, color, smooth, layer, layerSublevel, lineFactor)
3 years ago
local line = getTexture()
line:SetTexture("Interface\\AddOns\\MythicDungeonTools\\Textures\\Square_White")
line:SetVertexColor(color.r, color.g, color.b, color.a)
DrawLine(line, MDT.main_frame.mapPanelTile1, x, y, a, b, size, lineFactor and lineFactor or 1.1, "TOPLEFT")
line:SetDrawLayer(layer, layerSublevel)
line:Show()
line.coords = { x, y, a, b }
tinsert(activeTextures, line)
if smooth == true then
MDT:DrawHullCircle(x, y, size * 0.9, color, layer, layerSublevel)
end
end
function MDT:DrawHull(vertices, pullColor, pullIdx)
--if true then return end
local hull = convex_hull(vertices)
if hull then
-- expand_polygon: higher value = more points = more expensive = smoother outlines
hull = expand_polygon(hull, 30)
hull = convex_hull(hull)
for i = 1, #hull do
local a = hull[i]
local b = hull[1]
if i ~= #hull then b = hull[i + 1] end
--layerSublevel go from -8 to 7
--we rotate through the layerSublevel to avoid collisions
MDT:DrawHullLine(a[1], a[2], b[1], b[2], 3 * (MDT.scaleMultiplier[MDT:GetDB().currentDungeonIdx] or 1), pullColor,
true, "ARTWORK", pullIdx % 16 - 8, 1)
end
3 years ago
end
end
local function getPullVertices(p, blips)
local vertices = {}
for enemyIdx, clones in pairs(p) do
if tonumber(enemyIdx) then
for _, cloneIdx in pairs(clones) do
if MDT:IsCloneIncluded(enemyIdx, cloneIdx) then
for _, blip in pairs(blips) do
if (blip.enemyIdx == enemyIdx) and (blip.cloneIdx == cloneIdx) then
local endPoint, endRelativeTo, endRelativePoint, endX, endY = blip:GetPoint()
table.insert(vertices, { endX, endY, blip.normalScale })
break
end
3 years ago
end
end
3 years ago
end
end
3 years ago
end
return vertices
end
function MDT:DrawAllHulls(pulls)
3 years ago
MDT:ReleaseHullTextures()
MDT:ReleaseHullFontStrings()
3 years ago
local preset = MDT:GetCurrentPreset()
local blips = MDT:GetDungeonEnemyBlips()
local vertices
pulls = pulls or preset.value.pulls
for pullIdx, p in pairs(pulls) do
local r, g, b = MDT:DungeonEnemies_GetPullColor(pullIdx, pulls)
vertices = getPullVertices(p, blips)
MDT:DrawHull(vertices, { r = r, g = g, b = b, a = 1 }, pullIdx)
MDT:DrawHullFontString(vertices, pullIdx)
3 years ago
end
end
function MDT:FindClosestPull(x, y)
local preset = MDT:GetCurrentPreset()
local blips = MDT:GetDungeonEnemyBlips()
local vertices, hull, center
local centers = {}
--1. construct all hulls of pulls in this sublevel
for pullIdx, p in pairs(preset.value.pulls) do
vertices = getPullVertices(p, blips)
hull = convex_hull(vertices)
--2. get centroid of each pull
if hull and hull[#hull] then
if #hull > 2 then
center = centroid(hull)
centers[pullIdx] = center
elseif #hull == 2 then
local x1 = hull[1][1]
local y1 = hull[1][2]
local x2 = hull[2][1]
local y2 = hull[2][2]
centers[pullIdx] = { (x1 + x2) / 2, (y1 + y2) / 2 }
elseif #hull == 1 then
local x1 = hull[1][1]
local y1 = hull[1][2]
centers[pullIdx] = { x1, y1 }
end
end
3 years ago
end
--3. find closest centroid
local centerDist = math.huge
local centerIndex
for k, center in pairs(centers) do
local squaredDist = (x - center[1]) ^ 2 + (y - center[2]) ^ 2
if squaredDist < centerDist then
centerDist = squaredDist
centerIndex = k
end
3 years ago
end
if centerIndex then
return centerIndex, centers[centerIndex][1], centers[centerIndex][2]
end
end