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.

252 lines
7.9 KiB

local _, MDT = ...
local L = MDT.L
local db
local twipe,tinsert,tremove,tgetn,CreateFrame = table.wipe,table.insert,table.remove,table.getn,CreateFrame
-- 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
hull = expand_polygon(hull,13)
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