local Graph = LibStub:GetLibrary("LibGraph-2.0") local AceLocale = LibStub("AceLocale-3.0") local L = AceLocale:GetLocale( "Recount" ) local me={} local _ local revision = tonumber(string.sub("$Revision: 1123 $", 12, -3)) local Recount = _G.Recount if Recount.Version < revision then Recount.Version = revision end local GraphColors={ Damage={1.0,0.0,0.0,1.0}, DamageTaken={1.0,1.0,0.0,1.0}, Healing={0.0,1.0,0.0,1.0}, HealingTaken={0.0,1.0,1.0,1.0}, Overhealing={0.5,0.0,1.0,1.0}, Threat={1.0,0.5,0,1.0}, TPS={1.0,0.6,0.8,1.0}, } me.GraphColors={} local ClassCount={} local GraphName={ Damage="Damage", DamageTaken="Damage Taken", Healing="Healing", HealingTaken="Healing Taken", Overhealing="Overhealing", } local ShadeVariant = { 1.0, --1 0.6, --2 0.8, --3 0.4, --4 0.7, --5 0.5, --6 0.3, --7 0.9, --8 } function me:DataCopy(data) if not (data and data[1]) then return end local Copy=Recount:GetTable() Copy[1]=Recount:GetTable() Copy[2]=Recount:GetTable() for i=1,#data[1] do Copy[1][i]=data[1][i] Copy[2][i]=data[2][i] end return Copy end function me:DataSparsen(data,amount) local Keep=Recount:GetTable() if #data[1]==0 then return end local Min,Max,MinV,MaxV,CurVal local Time=data[1][1]+0.5 Keep[1]=true Min=0 for k,v in ipairs(data[1]) do CurVal=data[2][k] if v>Time and v<(Time+amount) then if Min==0 then Min=k Max=k MinV=CurVal MaxV=CurVal elseif CurVal=MaxV then Max=k MaxV=CurVal end elseif v>=(Time+amount) then if Min~=0 then Keep[Min]=true Keep[Max]=true end Time=v Min=k Max=k MinV=CurVal MaxV=CurVal end end if Min~=0 then Keep[Min]=true Keep[Max]=true Keep[#data[1]]=true end local i=#data[1] while i>0 do if not Keep[i] then table.remove(data[1],i) table.remove(data[2],i) end i=i-1 end Recount:FreeTable(Keep) end function me:FilterDataByTime(data) local filtered=Recount:GetTable() filtered[1]=Recount:GetTable() filtered[2]=Recount:GetTable() local First=true local Last=0 for k,v in ipairs(data[1]) do if v>=Recount.TimeRangeLower and v<=Recount.TimeRangeUpper then --Need to add value right at the front edge if First then if k~=1 then --If not first need to LERP to find the edge value local Weight=(Recount.TimeRangeLower-data[1][k-1])/(data[1][k]-data[1][k-1]) filtered[2][#filtered[2]+1]=Weight*data[2][k]+(1-Weight)*data[2][k-1] else --If this is first then insert 0 at front filtered[2][#filtered[2]+1]=0 end filtered[1][#filtered[1]+1]=Recount.TimeRangeLower First=false end filtered[1][#filtered[1]+1]=v filtered[2][#filtered[2]+1]=data[2][k] Last=k end end --Only add a trailer if we have data otherwise don't do anything if Last~=0 then --Do we have something we can LERP with? if data[1][Last+1] then --Yes, so calculate a weight and then LERP local Weight=(Recount.TimeRangeUpper-data[1][Last])/(data[1][Last+1]-data[1][Last]) filtered[2][#filtered[2]+1]=Weight*data[2][Last+1]+(1-Weight)*data[2][Last] else --No so lets insert 0 filtered[2][#filtered[2]+1]=0 end filtered[1][#filtered[1]+1]=Recount.TimeRangeUpper end return filtered end function me:FindMax(data) local Min=data[2][1] local Max=data[2][1] for _,v in ipairs(data[2]) do if v>Max then Max=v end if vb[1] then return true elseif a[1]==b[1] and a[4]>b[4] then return true end return false end local function SortForDisplay(a,b) if a[1]TimeRangeUpper then TimeRangeUpper=v[1][table.maxn(v[1])] end end end end --Need to pretend like a Time Range is set then Recount.TimeRangeLower=TimeRangeLower Recount.TimeRangeUpper=TimeRangeUpper Graph:SetXAxis(TimeRangeLower,TimeRangeUpper) Graph:LockXMin(true) Graph:LockXMax(true) end if Recount.GraphWindow.Data then for k,v in pairs(Recount.GraphWindow.Data) do if type(v)=="table" and table.maxn(v[1])>0 and (v[1][1]~=nil) and me.Enabled[k] then --Figure out the color used local color=me.GraphColors[k] --Start procecssing this data Filtered=me:FilterDataByTime(v) --Need to ensure its actually a copy if not then copy it if Filtered==v then Filtered=me:DataCopy(v) end if table.maxn(Filtered[1])>0 then if Recount.GraphWindow.IntegrateOn then me:IntegrateData(Filtered) end if not Recount.GraphWindow.StackedOn then Length=Filtered[1][table.maxn(Filtered[1])]-Filtered[1][1] if Length>300 then me:DataSparsen(Filtered,Length/100) end if Recount.GraphWindow.NormalizeOn then me:NormalizeData(Filtered) end _, DataMax=me:FindMax(Filtered) me:SetMin(Filtered,0) if MaxAmount200 then me:DataSparsen(v[2],Length/70) end Current=me:AddDataSeries(v[2],Current) if Current~=v[2] then Recount:FreeTableRecurse(v[2]) v[2]=Current end end if Recount.GraphWindow.NormalizeOn then --Divide now by the total local Total=me:DataCopy(Current) for k,v in pairs(Stacked) do me:DivideDataSeries(v[2],Total) end end table.sort(Stacked,SortForDisplay) if Stacked[1] and Stacked[1][2] then _, MaxAmount=me:FindMax(Stacked[1][2]) for k,v in pairs(Stacked) do Length=v[2][1][table.maxn(v[2][1])]-v[2][1][1] if Length>200 then me:DataSparsen(v[2],Length/70) end if MaxLengthXMax then return end T=Background:FindTexture() T:Show() if Recount.GraphWindow.LastTimeOver~=k then T:SetTexture(1,0,0,0.1) else T:SetTexture(1,1,0,0.1) end T:SetPoint("TOPLEFT",Background,"TOPLEFT",(v[1]-XMin)*Width,0) if v[2]>XMax then T:SetPoint("BOTTOMRIGHT",Background,"BOTTOMRIGHT",0,0) return end T:SetPoint("BOTTOMRIGHT",Background,"BOTTOMLEFT",(v[2]-XMin)*Width,0) T.id=k CurPos=v[2] elseif CurPosXMax then T:SetPoint("BOTTOMRIGHT",Background,"BOTTOMRIGHT",0,0) return end T:SetPoint("BOTTOMRIGHT",Background,"BOTTOMLEFT",(v[2]-XMin)*Width,0) T.id=k CurPos=v[2] end end end function me:SelectCombatTimes(id) local Graph=Recount.GraphWindow.LineGraph local Rows=Recount.GraphWindow.TimeRows local offset = FauxScrollFrame_GetOffset(Recount.GraphWindow.ScrollBar2) if Recount.TimeRangeSet==(id+offset) then Recount.TimeRangeSet=false Rows[id].Background:Hide() Graph:LockXMin(false) Graph:LockXMax(false) me:RefreshGraph() return end Recount.TimeRangeSet=id+offset local Times=Recount.db2.CombatTimes[id+offset] Recount.TimeRangeLower=Times[1]-30 Recount.TimeRangeUpper=Times[2]+30 for i=1,10 do Rows[i].Background:Hide() end Rows[id].Background:Show() Graph:LockXMin(true) Graph:LockXMax(true) Graph:SetXAxis(Recount.TimeRangeLower,Recount.TimeRangeUpper) me:RefreshGraph() end function me:SelectCombatTime(time) for k,v in pairs(Recount.db2.CombatTimes) do if v[1]<=time and time<=v[2] then local Graph=Recount.GraphWindow.LineGraph Recount.TimeRangeSet=k Recount.TimeRangeLower=v[1]-30 Recount.TimeRangeUpper=v[2]+30 Graph:LockXMin(true) Graph:LockXMax(true) Graph:SetXAxis(Recount.TimeRangeLower,Recount.TimeRangeUpper) me:RefreshGraph() Recount:GraphRefreshCombat() return end end end function me:HighlightCombatTime(time) local TimeOver=nil if time then for k,v in pairs(Recount.db2.CombatTimes) do if v[1]<=time and time<=v[2] then TimeOver=k break end end end if TimeOver~=Recount.GraphWindow.LastTimeOver then for _,v in pairs(Recount.GraphWindow.GraphBackground.Textures) do if v.id==TimeOver then v:SetTexture(1,1,0,0.1) elseif v.id==Recount.GraphWindow.LastTimeOver then v:SetTexture(1,0,0,0.1) end end Recount.GraphWindow.LastTimeOver=TimeOver end end function Recount:CheckFontStringLength(fontstring,maxwidth) local Text=fontstring:GetText() while fontstring:GetStringWidth()>maxwidth do Text=string.sub(Text, 1, string.len(Text)-1) fontstring:SetText(Text.."...") end end function Recount:SetGraphData(name,data,combat) Recount.GraphWindow.Title:SetText(L["Graph Window"].." - "..name) Recount.GraphWindow.Data=data Recount.GraphWindow.CombatTime=combat Recount.GraphWindow:Show() Recount:SetWindowTop(Recount.GraphWindow) --Need to reset class counts if in compare mode if Recount.GraphCompare then for k,_ in pairs(ClassCount) do ClassCount[k]=nil end end local Class, Shade if me.Enabled==nil then me.Enabled={} end local i=1 if Recount.GraphWindow.Data then for k, _ in pairs(Recount.GraphWindow.Data) do local color=GraphColors[k] if type(color)~="table" then if Recount.GraphCompare and Recount.GraphClass[k] then Class=Recount.GraphClass[k] ClassCount[Class]=(ClassCount[Class] or 0) + 1 if ClassCount[Class]<=8 then Shade=ShadeVariant[ClassCount[Class]] color={Shade*Recount.db.profile.Colors.Class[Class].r,Shade*Recount.db.profile.Colors.Class[Class].g,Shade*Recount.db.profile.Colors.Class[Class].b,1} else color={math.random(),math.random(),math.random(),1} end else color={math.random(),math.random(),math.random(),1} end end me.GraphColors[k]=color me.Enabled[k]=true if i<=10 then Recount.GraphWindow.Rows[i]:Show() Recount.GraphWindow.Rows[i].Key:SetVertexColor(color[1],color[2],color[3],1) Recount.GraphWindow.Rows[i].Name:SetText(GraphName[k] or k) end i=i+1 end end Recount.GraphWindow.Entries=i-1 for j=i,10 do Recount.GraphWindow.Rows[j]:Hide() end Recount:GraphRefreshData() Recount:GraphRefreshCombat() me:RefreshGraph() end function Recount:GraphRefreshData() local data=Recount.GraphWindow.Data local size=Recount.GraphWindow.Entries FauxScrollFrame_Update(Recount.GraphWindow.ScrollBar1, size, 10, 20) local offset = FauxScrollFrame_GetOffset(Recount.GraphWindow.ScrollBar1) local Rows=Recount.GraphWindow.Rows local i=1-offset if Recount.GraphWindow.Data then for k, v in pairs(Recount.GraphWindow.Data) do if i>=1 and i<=10 then Rows[i]:Show() Rows[i].Key:SetVertexColor(me.GraphColors[k][1],me.GraphColors[k][2],me.GraphColors[k][3],1) Rows[i].Name:SetText(GraphName[k] or k) Recount:CheckFontStringLength(Rows[i].Name,180) Rows[i].Enabled:SetChecked(me.Enabled[k]) Rows[i].Enabled.Key=k end i=i+1 end end while i<=10 do Rows[i]:Hide() i=i+1 end end function Recount:GraphRefreshCombat() local combat=Recount.db2.CombatTimes local size=table.getn(combat) FauxScrollFrame_Update(Recount.GraphWindow.ScrollBar2, size, 10, 20) local offset = FauxScrollFrame_GetOffset(Recount.GraphWindow.ScrollBar2) local Rows=Recount.GraphWindow.TimeRows for i=1,10 do local c=combat[i+offset] if c then Rows[i]:Show() Rows[i].Who:SetText(c[5]) Recount:CheckFontStringLength(Rows[i].Who,115) Rows[i].Start:SetText(c[3]) Rows[i].End:SetText(c[4]) if i+offset~=Recount.TimeRangeSet then Rows[i].Background:Hide() else Rows[i].Background:Show() end else Rows[i]:Hide() end end end function Recount:CreateGraphWindow() Recount.GraphWindow=CreateFrame("Frame","Recount_GraphWindow",UIParent) local theFrame=Recount.GraphWindow theFrame.Me=me theFrame:ClearAllPoints() theFrame:SetPoint("CENTER",UIParent) theFrame:SetHeight(432+20) theFrame:SetWidth(625+24) theFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = "Interface\\AddOns\\Recount\\textures\\otravi-semi-full-border", edgeSize = 32, insets = {left = 1, right = 1, top = 20, bottom = 1}, }) theFrame:SetBackdropBorderColor(1.0,0.0,0.0) theFrame:SetBackdropColor(24/255, 24/255, 24/255) Recount.Colors:RegisterBorder("Other Windows","Title",theFrame) Recount.Colors:RegisterBackground("Other Windows","Background",theFrame) theFrame:EnableMouse(true) theFrame:SetMovable(true) theFrame:SetFrameLevel(Recount.MainWindow:GetFrameLevel()+20) theFrame:SetScript("OnMouseDown", function(this) if ( ( ( not this.isLocked ) or ( this.isLocked == 0 ) ) and ( arg1 == "LeftButton" ) ) then Recount:SetWindowTop(this) this:StartMoving(); this.isMoving = true; end end) theFrame:SetScript("OnMouseUp", function(this) if ( this.isMoving ) then local point,relativeTo,relativePoint,xOfs,yOfs = this:GetPoint(1) Recount.db.profile.GraphWindowX=xOfs Recount.db.profile.GraphWindowY=yOfs this:StopMovingOrSizing(); this.isMoving = false; end end) theFrame:SetScript("OnShow", function(this) Recount:SetWindowTop(this) end) theFrame:SetScript("OnHide", function(this) if ( this.isMoving ) then local point,relativeTo,relativePoint,xOfs,yOfs = this:GetPoint(1) Recount.db.profile.GraphWindowX=xOfs Recount.db.profile.GraphWindowY=yOfs this:StopMovingOrSizing(); this.isMoving = false; end end) theFrame.Title=theFrame:CreateFontString(nil,"OVERLAY","GameFontNormal") theFrame.Title:SetPoint("TOPLEFT",theFrame,"TOPLEFT",6,-15) theFrame.Title:SetTextColor(1.0,1.0,1.0,1.0) theFrame.Title:SetText(L["Graph Window"]) -- Recount.Colors:UnregisterItem(Recount.GraphWindow.Title) Recount.Colors:RegisterFont("Other Windows","Title Text",Recount.GraphWindow.Title) theFrame.LineGraph=Graph:CreateGraphLine("Recount_GraphWindow_LineGraph",theFrame,"TOPLEFT","TOPLEFT",1,-30,400,420) theFrame.LineGraph:SetAutoScale(true) theFrame.LineGraph:SetYAxis(0,100) theFrame.LineGraph:SetGridSpacing(15,25) theFrame.LineGraph:LockYMin(true) theFrame.LineGraph:SetYLabels(true,true) theFrame.LineGraph:SetFrameLevel(theFrame:GetFrameLevel()+2) theFrame.LineGraph.TimeRef=theFrame.LineGraph:CreateFontString(nil,"OVERLAY","GameFontNormal") theFrame.LineGraph.TimeRef:SetPoint("TOP",theFrame.LineGraph,"TOP",0,-2) theFrame.LineGraph.TimeRef:SetTextColor(1.0,1.0,1.0,1.0) theFrame.GraphBackground=CreateFrame("Frame",nil,theFrame) theFrame.GraphBackground:SetAllPoints(theFrame.LineGraph) theFrame.GraphBackground.HideTextures=me.HideTextures theFrame.GraphBackground.FindTexture=me.FindTexture theFrame.GraphBackground:EnableMouse(true) theFrame.GraphBackground.TimeSelect=theFrame.GraphBackground:CreateTexture(nil,"BACKGROUND") theFrame.GraphBackground.TimeSelect:SetTexture(0,1,0,0.1) theFrame.GraphBackground.TimeSelect:Hide() theFrame.GraphBackground:SetScript("OnUpdate", function(this) local sX=this:GetCenter() local Scale=this:GetEffectiveScale() local mX=GetCursorPosition() local TimePos local GraphWindow=Recount.GraphWindow if Recount.TimeRangeUpper and (MouseIsOver(GraphWindow.GraphBackground) or Recount.GraphWindow.Dragging) then TimePos=(mX/Scale-sX+this:GetWidth()/2)/this:GetWidth() TimePos=TimePos*(Recount.TimeRangeUpper-Recount.TimeRangeLower)+Recount.TimeRangeLower if MouseIsOver(GraphWindow.GraphBackground) then me:HighlightCombatTime(TimePos) end if GraphWindow.Dragging then local Graph=GraphWindow.LineGraph local Background=GraphWindow.GraphBackground local XMin=Graph.XMin local XMax=Graph.XMax --Hack fix for no data if XMin==nil or XMax==nil then Graph:SetXAxis(5,30) XMin=Graph.XMin XMax=Graph.XMax end local Width=Background:GetWidth()/(XMax-XMin) local Left,Right Left=math.max(math.min(GraphWindow.DragTimeStart,XMax),XMin) Right=math.max(math.min(TimePos,XMax),XMin) if Right