local _, MDT = ... 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) 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) 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) 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 lower_left = final tries = tries + 1 until final == hull[1] or tries > 100 --deadlocked here otherwise?!? local hullpts = {} for _, index in ipairs(hull) do table.insert(hullpts, pts[index]) end return hullpts end local function cross(a, b) return a[1] * b[2] - a[2] * b[1] end local function area(points) 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) local rx = 0 local ry = 0 local area = area(pts) for i = 1, #pts - 1 do rx = rx + ((pts[i][1] + pts[i + 1][1]) * ((pts[i][1] * pts[i + 1][2]) - (pts[i + 1][1] * pts[i][2]))) ry = ry + ((pts[i][2] + pts[i + 1][2]) * ((pts[i][1] * pts[i + 1][2]) - (pts[i + 1][1] * pts[i][2]))) end rx = rx + ((pts[#pts][1] + pts[1][1]) * ((pts[#pts][1] * pts[1][2]) - (pts[1][1] * pts[#pts][2]))) ry = ry + ((pts[#pts][2] + pts[1][2]) * ((pts[#pts][1] * pts[1][2]) - (pts[1][1] * pts[#pts][2]))) rx = rx / (area * 6) ry = ry / (area * 6) return { rx, ry } end local function expand_polygon(poly, numCirclePoints) 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 end return res end ---TexturePool local activeTextures = {} local texturePool = {} local function getTexture() local size = tgetn(texturePool) if size == 0 then return MDT.main_frame.mapPanelFrame:CreateTexture(nil, "OVERLAY") 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 local function releaseTexture(tex) tex:Hide() tinsert(texturePool, tex) end ---ReleaseAllActiveTextures function MDT:ReleaseHullTextures() for k, tex in pairs(activeTextures) do releaseTexture(tex) end twipe(activeTextures) end function MDT:DrawHullCircle(x, y, size, color, layer, layerSublevel) 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) 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 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 end end end end end return vertices end function MDT:DrawAllHulls(pulls) MDT:ReleaseHullTextures() 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) 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 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 end if centerIndex then return centerIndex, centers[centerIndex][1], centers[centerIndex][2] end end