2203 lines
54 KiB
Lua
2203 lines
54 KiB
Lua
--[[
|
|
Name: LibGraph-2.0
|
|
Revision: $Rev: 49 $
|
|
Author(s): Cryect (cryect@gmail.com), Xinhuan
|
|
Website: http://www.wowace.com/
|
|
Documentation: http://www.wowace.com/wiki/GraphLib
|
|
SVN: http://svn.wowace.com/root/trunk/GraphLib/
|
|
Description: Allows for easy creation of graphs
|
|
]]
|
|
|
|
--Thanks to Nelson Minar for catching several errors where width was being used instead of height (damn copy and paste >_>)
|
|
|
|
local major = "LibGraph-2.0"
|
|
local minor = 90000 + tonumber(("$Revision: 49 $"):match("(%d+)"))
|
|
|
|
|
|
-- hard coded path for TSM
|
|
local TextureDirectory = "Interface\\AddOns\\TradeSkillMaster\\Libs\\LibGraph-2.0\\"
|
|
|
|
|
|
if not LibStub then error(major .. " requires LibStub") end
|
|
|
|
local lib, oldLibMinor = LibStub:NewLibrary(major, minor)
|
|
if not lib then return end
|
|
|
|
local GraphFunctions={}
|
|
|
|
local tinsert, tremove = tinsert, tremove
|
|
local pairs, ipairs = pairs, ipairs
|
|
local math_max = math.max
|
|
local math_min = math.min
|
|
local math_ceil = math.ceil
|
|
local math_pi = math.pi
|
|
local math_floor = math.floor
|
|
local math_pow = math.pow
|
|
local math_random = math.random
|
|
local math_cos = math.cos
|
|
local math_sin = math.sin
|
|
local math_deg = math.deg
|
|
local math_atan = math.atan
|
|
local math_abs = math.abs
|
|
local math_fmod = math.fmod
|
|
local math_huge = math.huge
|
|
local GetTime = GetTime
|
|
|
|
-- lib upgrade stuff
|
|
lib.RegisteredGraphRealtime = lib.RegisteredGraphRealtime or {}
|
|
lib.RegisteredGraphLine = lib.RegisteredGraphLine or {}
|
|
lib.RegisteredGraphScatterPlot = lib.RegisteredGraphScatterPlot or {}
|
|
lib.RegisteredGraphPieChart = lib.RegisteredGraphPieChart or {}
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
--Graph Creation Functions
|
|
--------------------------------------------------------------------------------
|
|
|
|
--Realtime Graph
|
|
local function SetupGraphRealtimeFunctions(graph, upgrade)
|
|
local self = lib
|
|
|
|
--Set the various functions
|
|
graph.SetXAxis=GraphFunctions.SetXAxis
|
|
graph.SetYMax=GraphFunctions.SetYMax
|
|
graph.AddTimeData=GraphFunctions.AddTimeData
|
|
graph.OnUpdate=GraphFunctions.OnUpdateGraphRealtime
|
|
graph.CreateGridlines=GraphFunctions.CreateGridlines
|
|
graph.RefreshGraph=GraphFunctions.RefreshRealtimeGraph
|
|
graph.SetAxisDrawing=GraphFunctions.SetAxisDrawing
|
|
graph.SetGridSpacing=GraphFunctions.SetGridSpacing
|
|
graph.SetAxisColor=GraphFunctions.SetAxisColor
|
|
graph.SetGridColor=GraphFunctions.SetGridColor
|
|
graph.SetGridColorSecondary=GraphFunctions.SetGridColorSecondary
|
|
graph.SetGridSecondaryMultiple=GraphFunctions.SetGridSecondaryMultiple
|
|
graph.SetFilterRadius=GraphFunctions.SetFilterRadius
|
|
--graph.SetAutoscaleYAxis=GraphFunctions.SetAutoscaleYAxis
|
|
graph.SetBarColors=GraphFunctions.SetBarColors
|
|
graph.SetMode=GraphFunctions.SetMode
|
|
graph.SetAutoScale=GraphFunctions.SetAutoScale
|
|
if not upgrade then
|
|
-- This is the original frame:SetWidth() and frame:SetHeight()
|
|
-- standard frame functions
|
|
graph.OldSetWidth=graph.SetWidth
|
|
graph.OldSetHeight=graph.SetHeight
|
|
end
|
|
graph.SetWidth=GraphFunctions.RealtimeSetWidth
|
|
graph.SetHeight=GraphFunctions.RealtimeSetHeight
|
|
graph.SetBarColors=GraphFunctions.RealtimeSetColors
|
|
graph.GetMaxValue=GraphFunctions.GetMaxValue
|
|
graph.GetValue=GraphFunctions.RealtimeGetValue
|
|
graph.SetUpdateLimit=GraphFunctions.SetUpdateLimit
|
|
graph.SetDecay=GraphFunctions.SetDecay
|
|
graph.SetMinMaxY=GraphFunctions.SetMinMaxY
|
|
graph.AddBar=GraphFunctions.AddBar
|
|
graph.SetYLabels=GraphFunctions.SetYLabels
|
|
|
|
|
|
|
|
graph.DrawLine=self.DrawLine
|
|
graph.DrawHLine=self.DrawHLine
|
|
graph.DrawVLine=self.DrawVLine
|
|
graph.HideLines=self.HideLines
|
|
graph.HideFontStrings=GraphFunctions.HideFontStrings
|
|
graph.FindFontString=GraphFunctions.FindFontString
|
|
graph.SetBars=GraphFunctions.SetBars
|
|
|
|
|
|
--Set the update function
|
|
graph:SetScript("OnUpdate", graph.OnUpdate)
|
|
end
|
|
|
|
function lib:CreateGraphRealtime(name,parent,relative,relativeTo,offsetX,offsetY,Width,Height)
|
|
local graph
|
|
local i
|
|
graph = CreateFrame("Frame",name,parent)
|
|
|
|
Width=math_floor(Width)
|
|
|
|
|
|
graph:SetPoint(relative,parent,relativeTo,offsetX,offsetY)
|
|
graph:SetWidth(Width)
|
|
graph:SetHeight(Height)
|
|
graph:Show()
|
|
|
|
--Create the bars
|
|
graph.Bars={}
|
|
graph.BarsUsing={}
|
|
graph.BarNum=Width
|
|
graph.Height=Height
|
|
for i=1,Width do
|
|
local bar
|
|
bar = CreateFrame("StatusBar",name.."Bar"..i,graph)--graph:CreateTexture(nil,"ARTWORK")
|
|
bar:SetPoint("BOTTOMLEFT",graph,"BOTTOMLEFT",i-1,0)
|
|
bar:SetHeight(Height)
|
|
bar:SetWidth(1)
|
|
bar:SetOrientation("VERTICAL")
|
|
bar:SetMinMaxValues(0,1)
|
|
bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8.blp")
|
|
bar:GetStatusBarTexture():SetHorizTile(false)
|
|
bar:GetStatusBarTexture():SetVertTile(false)
|
|
|
|
local t=bar:GetStatusBarTexture()
|
|
t:SetGradientAlpha("VERTICAL",0.2,0.0,0.0,0.5,1.0,0.0,0.0,1.0)
|
|
|
|
bar:Show()
|
|
tinsert(graph.Bars,bar)
|
|
tinsert(graph.BarsUsing,bar)
|
|
end
|
|
|
|
|
|
SetupGraphRealtimeFunctions(graph)
|
|
|
|
|
|
--Initialize Data
|
|
graph.GraphType="REALTIME"
|
|
graph.YMax=60
|
|
graph.YMin=0
|
|
graph.XMax=-0.75
|
|
graph.XMin=-10
|
|
graph.TimeRadius=0.5
|
|
graph.Mode="FAST"
|
|
graph.Filter="RECT"
|
|
graph.AxisColor={1.0,1.0,1.0,1.0}
|
|
graph.GridColor={0.5,0.5,0.5,0.5}
|
|
graph.BarColorTop={1.0,0.0,0.0,1.0}
|
|
graph.BarColorBot={0.2,0.0,0.0,0.5}
|
|
graph.AutoScale=false
|
|
graph.Data={}
|
|
graph.MinMaxY=0
|
|
graph.CurVal=0
|
|
graph.LastDataTime=GetTime()
|
|
|
|
graph.Textures={}
|
|
graph.TexturesUsed={}
|
|
|
|
graph.LimitUpdates=0
|
|
graph.NextUpdate=0
|
|
|
|
graph.BarHeight={}
|
|
graph.LastShift=GetTime()
|
|
graph.BarWidth=(graph.XMax-graph.XMin)/graph.BarNum
|
|
graph.DecaySet=0.8
|
|
graph.Decay=math_pow(graph.DecaySet,graph.BarWidth)
|
|
graph.ExpNorm=1/(1-graph.Decay)
|
|
|
|
graph.FilterOverlap=math_max(math_ceil((graph.TimeRadius+graph.XMax)/graph.BarWidth),0)
|
|
for i=1,graph.BarNum do
|
|
graph.BarHeight[i]=0
|
|
end
|
|
|
|
graph.TextFrame = CreateFrame("Frame",nil,graph)
|
|
graph.TextFrame:SetAllPoints(graph)
|
|
graph.TextFrame:SetFrameLevel(graph:GetFrameLevel()+2)
|
|
|
|
tinsert(self.RegisteredGraphRealtime, graph)
|
|
return graph
|
|
end
|
|
|
|
--Line Graph
|
|
local function SetupGraphLineFunctions(graph)
|
|
local self = lib
|
|
|
|
--Set the various functions
|
|
graph.SetXAxis=GraphFunctions.SetXAxis
|
|
graph.SetYAxis=GraphFunctions.SetYAxis
|
|
graph.AddDataSeries=GraphFunctions.AddDataSeries
|
|
graph.AddFilledDataSeries=GraphFunctions.AddFilledDataSeries
|
|
graph.ResetData=GraphFunctions.ResetData
|
|
graph.RefreshGraph=GraphFunctions.RefreshLineGraph
|
|
graph.CreateGridlines=GraphFunctions.CreateGridlines
|
|
graph.SetAxisDrawing=GraphFunctions.SetAxisDrawing
|
|
graph.SetGridSpacing=GraphFunctions.SetGridSpacing
|
|
graph.SetAxisColor=GraphFunctions.SetAxisColor
|
|
graph.SetGridColor=GraphFunctions.SetGridColor
|
|
graph.SetGridColorSecondary=GraphFunctions.SetGridColorSecondary
|
|
graph.SetGridSecondaryMultiple=GraphFunctions.SetGridSecondaryMultiple
|
|
graph.SetAutoScale=GraphFunctions.SetAutoScale
|
|
graph.SetYLabels=GraphFunctions.SetYLabels
|
|
graph.OnUpdate=GraphFunctions.OnUpdateGraph
|
|
|
|
graph.LockXMin=GraphFunctions.LockXMin
|
|
graph.LockXMax=GraphFunctions.LockXMax
|
|
graph.LockYMin=GraphFunctions.LockYMin
|
|
graph.LockYMax=GraphFunctions.LockYMax
|
|
|
|
|
|
|
|
graph.DrawLine=self.DrawLine
|
|
graph.DrawHLine=self.DrawHLine
|
|
graph.DrawVLine=self.DrawVLine
|
|
graph.HideLines=self.HideLines
|
|
graph.DrawBar=self.DrawBar
|
|
graph.HideBars=self.HideBars
|
|
graph.HideFontStrings=GraphFunctions.HideFontStrings
|
|
graph.FindFontString=GraphFunctions.FindFontString
|
|
|
|
--Set the update function
|
|
graph:SetScript("OnUpdate", graph.OnUpdate)
|
|
end
|
|
|
|
--TODO: Clip lines with the bounds
|
|
function lib:CreateGraphLine(name,parent,relative,relativeTo,offsetX,offsetY,Width,Height)
|
|
local graph
|
|
local i
|
|
graph = CreateFrame("Frame",name,parent)
|
|
|
|
|
|
graph:SetPoint(relative,parent,relativeTo,offsetX,offsetY)
|
|
graph:SetWidth(Width)
|
|
graph:SetHeight(Height)
|
|
graph:Show()
|
|
|
|
|
|
SetupGraphLineFunctions(graph)
|
|
|
|
|
|
graph.NeedsUpdate=false
|
|
|
|
|
|
--Initialize Data
|
|
graph.GraphType="LINE"
|
|
graph.YMax=1
|
|
graph.YMin=-1
|
|
graph.XMax=1
|
|
graph.XMin=-1
|
|
graph.AxisColor={1.0,1.0,1.0,1.0}
|
|
graph.GridColor={0.5,0.5,0.5,0.5}
|
|
graph.XGridInterval=0.25
|
|
graph.YGridInterval=0.25
|
|
graph.XAxisDrawn=true
|
|
graph.YAxisDrawn=true
|
|
|
|
graph.LockOnXMin=false
|
|
graph.LockOnXMax=false
|
|
graph.LockOnYMin=false
|
|
graph.LockOnYMax=false
|
|
graph.Data={}
|
|
graph.FilledData={}
|
|
graph.Textures={}
|
|
graph.TexturesUsed={}
|
|
graph.TextFrame = CreateFrame("Frame",nil,graph)
|
|
graph.TextFrame:SetAllPoints(graph)
|
|
|
|
|
|
tinsert(self.RegisteredGraphLine, graph)
|
|
return graph
|
|
end
|
|
|
|
|
|
--Scatter Plot
|
|
local function SetupGraphScatterPlotFunctions(graph)
|
|
local self = lib
|
|
|
|
--Set the various functions
|
|
graph.SetXAxis=GraphFunctions.SetXAxis
|
|
graph.SetYAxis=GraphFunctions.SetYAxis
|
|
graph.AddDataSeries=GraphFunctions.AddDataSeries
|
|
graph.ResetData=GraphFunctions.ResetData
|
|
graph.RefreshGraph=GraphFunctions.RefreshScatterPlot
|
|
graph.CreateGridlines=GraphFunctions.CreateGridlines
|
|
graph.OnUpdate=GraphFunctions.OnUpdateGraph
|
|
|
|
graph.LinearRegression=GraphFunctions.LinearRegression
|
|
graph.SetAxisDrawing=GraphFunctions.SetAxisDrawing
|
|
graph.SetGridSpacing=GraphFunctions.SetGridSpacing
|
|
graph.SetAxisColor=GraphFunctions.SetAxisColor
|
|
graph.SetGridColor=GraphFunctions.SetGridColor
|
|
graph.SetGridColorSecondary=GraphFunctions.SetGridColorSecondary
|
|
graph.SetGridSecondaryMultiple=GraphFunctions.SetGridSecondaryMultiple
|
|
graph.SetLinearFit=GraphFunctions.SetLinearFit
|
|
graph.SetAutoScale=GraphFunctions.SetAutoScale
|
|
graph.SetYLabels=GraphFunctions.SetYLabels
|
|
|
|
graph.LockXMin=GraphFunctions.LockXMin
|
|
graph.LockXMax=GraphFunctions.LockXMax
|
|
graph.LockYMin=GraphFunctions.LockYMin
|
|
graph.LockYMax=GraphFunctions.LockYMax
|
|
|
|
graph.DrawLine=self.DrawLine
|
|
graph.DrawHLine=self.DrawHLine
|
|
graph.DrawVLine=self.DrawVLine
|
|
graph.HideLines=self.HideLines
|
|
graph.HideTextures=GraphFunctions.HideTextures
|
|
graph.FindTexture=GraphFunctions.FindTexture
|
|
graph.HideFontStrings=GraphFunctions.HideFontStrings
|
|
graph.FindFontString=GraphFunctions.FindFontString
|
|
|
|
--Set the update function
|
|
graph:SetScript("OnUpdate", graph.OnUpdate)
|
|
end
|
|
|
|
function lib:CreateGraphScatterPlot(name,parent,relative,relativeTo,offsetX,offsetY,Width,Height)
|
|
local graph
|
|
local i
|
|
graph = CreateFrame("Frame",name,parent)
|
|
|
|
|
|
graph:SetPoint(relative,parent,relativeTo,offsetX,offsetY)
|
|
graph:SetWidth(Width)
|
|
graph:SetHeight(Height)
|
|
graph:Show()
|
|
|
|
|
|
SetupGraphScatterPlotFunctions(graph)
|
|
|
|
|
|
graph.NeedsUpdate=false
|
|
|
|
--Initialize Data
|
|
graph.GraphType="SCATTER"
|
|
graph.YMax=1
|
|
graph.YMin=-1
|
|
graph.XMax=1
|
|
graph.XMin=-1
|
|
graph.AxisColor={1.0,1.0,1.0,1.0}
|
|
graph.GridColor={0.5,0.5,0.5,0.5}
|
|
graph.XGridInterval=0.25
|
|
graph.YGridInterval=0.25
|
|
graph.XAxisDrawn=true
|
|
graph.YAxisDrawn=true
|
|
graph.AutoScale=false
|
|
graph.LinearFit=false
|
|
graph.LockOnXMin=false
|
|
graph.LockOnXMax=false
|
|
graph.LockOnYMin=false
|
|
graph.LockOnYMax=false
|
|
graph.Data={}
|
|
graph.Textures={}
|
|
graph.TexturesUsed={}
|
|
|
|
graph.TextFrame = CreateFrame("Frame",nil,graph)
|
|
graph.TextFrame:SetAllPoints(graph)
|
|
|
|
tinsert(self.RegisteredGraphScatterPlot, graph)
|
|
return graph
|
|
end
|
|
|
|
--Pie Chart
|
|
local function SetupGraphPieChartFunctions(graph)
|
|
local self = lib
|
|
|
|
--Set the various functions
|
|
graph.AddPie=GraphFunctions.AddPie
|
|
graph.CompletePie=GraphFunctions.CompletePie
|
|
graph.ResetPie=GraphFunctions.ResetPie
|
|
|
|
graph.DrawLine=self.DrawLine
|
|
graph.DrawHLine=self.DrawHLine
|
|
graph.DrawVLine=self.DrawVLine
|
|
graph.DrawLinePie=GraphFunctions.DrawLinePie
|
|
graph.HideLines=self.HideLines
|
|
graph.HideTextures=GraphFunctions.HideTextures
|
|
graph.FindTexture=GraphFunctions.FindTexture
|
|
graph.OnUpdate=GraphFunctions.PieChart_OnUpdate
|
|
graph.SetSelectionFunc=GraphFunctions.SetSelectionFunc
|
|
|
|
graph:SetScript("OnUpdate", graph.OnUpdate)
|
|
end
|
|
|
|
function lib:CreateGraphPieChart(name,parent,relative,relativeTo,offsetX,offsetY,Width,Height)
|
|
local graph
|
|
local i
|
|
graph = CreateFrame("Frame",name,parent)
|
|
|
|
|
|
graph:SetPoint(relative,parent,relativeTo,offsetX,offsetY)
|
|
graph:SetWidth(Width)
|
|
graph:SetHeight(Height)
|
|
graph:Show()
|
|
|
|
|
|
SetupGraphPieChartFunctions(graph)
|
|
|
|
|
|
|
|
--Initialize Data
|
|
graph.GraphType="PIE"
|
|
graph.PieUsed=0
|
|
graph.PercentOn=0
|
|
graph.Remaining=0
|
|
graph.Textures={}
|
|
graph.Ratio=Width/Height
|
|
graph.Radius=0.88*(Width/2)
|
|
graph.Radius=graph.Radius*graph.Radius
|
|
graph.Sections={}
|
|
graph.Textures={}
|
|
graph.TexturesUsed={}
|
|
graph.LastSection=nil
|
|
graph.onColor=1
|
|
graph.TotalSections=0
|
|
|
|
tinsert(self.RegisteredGraphPieChart, graph)
|
|
return graph
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
--Functions for Realtime Graphs
|
|
-------------------------------------------------------------------------------
|
|
|
|
--AddTimeData - Adds a data value to the realtime graph at this moment in time
|
|
function GraphFunctions:AddTimeData(value)
|
|
if type(value)~="number" then
|
|
return
|
|
end
|
|
|
|
local t={}
|
|
t.Time=GetTime()
|
|
self.LastDataTime=t.Time
|
|
t.Value=value
|
|
tinsert(self.Data,t)
|
|
end
|
|
|
|
--RefreshRealtimeGraph - Refreshes the gridlines for the realtime graph
|
|
function GraphFunctions:RefreshRealtimeGraph()
|
|
self:HideLines(self)
|
|
self:CreateGridlines()
|
|
end
|
|
|
|
--SetFilterRadius - controls the radius of the filter
|
|
function GraphFunctions:SetFilterRadius(radius)
|
|
self.TimeRadius=radius
|
|
end
|
|
|
|
--SetAutoscaleYAxis - If enabled the maximum y axis is adjusted to be 25% more than the max value
|
|
function GraphFunctions:SetAutoscaleYAxis(scale)
|
|
self.AutoScale=scale
|
|
end
|
|
|
|
--SetBarColors -
|
|
function GraphFunctions:SetBarColors(BotColor,TopColor)
|
|
local Temp
|
|
if BotColor.r then
|
|
Temp=BotColor
|
|
BotColor={Temp.r,Temp.g,Temp.b,Temp.a}
|
|
end
|
|
if TopColor.r then
|
|
Temp=TopColor
|
|
TopColor={Temp.r,Temp.g,Temp.b,Temp.a}
|
|
end
|
|
for i=1,self.BarNum do
|
|
local t=self.Bars[i]:GetStatusBarTexture()
|
|
t:SetGradientAlpha("VERTICAL",BotColor[1],BotColor[2],BotColor[3],BotColor[4],TopColor[1],TopColor[2],TopColor[3],TopColor[4])
|
|
end
|
|
end
|
|
|
|
function GraphFunctions:SetMode(mode)
|
|
self.Mode=mode
|
|
|
|
if mode~="SLOW" then
|
|
self.LastShift=GetTime()+self.XMin
|
|
end
|
|
end
|
|
|
|
function GraphFunctions:RealtimeSetColors(BotColor,TopColor)
|
|
local Temp
|
|
if BotColor.r then
|
|
Temp=BotColor
|
|
BotColor={Temp.r,Temp.g,Temp.b,Temp.a}
|
|
end
|
|
if TopColor.r then
|
|
Temp=TopColor
|
|
TopColor={Temp.r,Temp.g,Temp.b,Temp.a}
|
|
end
|
|
self.BarColorBot=BotColor
|
|
self.BarColorTop=TopColor
|
|
for _,v in pairs(self.Bars) do
|
|
v:GetStatusBarTexture():SetGradientAlpha("VERTICAL",self.BarColorBot[1],self.BarColorBot[2],self.BarColorBot[3],self.BarColorBot[4],self.BarColorTop[1],self.BarColorTop[2],self.BarColorTop[3],self.BarColorTop[4])
|
|
end
|
|
end
|
|
|
|
function GraphFunctions:RealtimeSetWidth(Width)
|
|
Width=math_floor(Width)
|
|
|
|
if Width==self.BarNum then
|
|
return
|
|
end
|
|
|
|
self.BarNum=Width
|
|
for i=1,Width do
|
|
if type(self.Bars[i])=="nil" then
|
|
local bar
|
|
bar = CreateFrame("StatusBar",self:GetName().."Bar"..i,self)
|
|
bar:SetPoint("BOTTOMLEFT",self,"BOTTOMLEFT",i-1,0)
|
|
bar:SetHeight(self.Height)
|
|
bar:SetWidth(1)
|
|
bar:SetOrientation("VERTICAL")
|
|
bar:SetMinMaxValues(0,1)
|
|
bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8.blp")
|
|
bar:GetStatusBarTexture():SetHorizTile(false)
|
|
bar:GetStatusBarTexture():SetVertTile(false)
|
|
|
|
local t=bar:GetStatusBarTexture()
|
|
t:SetGradientAlpha("VERTICAL",self.BarColorBot[1],self.BarColorBot[2],self.BarColorBot[3],self.BarColorBot[4],self.BarColorTop[1],self.BarColorTop[2],self.BarColorTop[3],self.BarColorTop[4])
|
|
|
|
tinsert(self.Bars,bar)
|
|
else
|
|
self.Bars[i]:SetPoint("BOTTOMLEFT",self,"BOTTOMLEFT",i-1,0)
|
|
end
|
|
self.BarHeight[i]=0
|
|
end
|
|
|
|
local SizeOfBarsUsed=table.maxn(self.BarsUsing)
|
|
|
|
if Width>SizeOfBarsUsed then
|
|
for i=SizeOfBarsUsed+1,Width do
|
|
tinsert(self.BarsUsing,self.Bars[i])
|
|
self.Bars[i]:Show()
|
|
end
|
|
elseif Width<SizeOfBarsUsed then
|
|
for i=Width+1,SizeOfBarsUsed do
|
|
tremove(self.BarsUsing,Width+1)
|
|
self.Bars[i]:Hide()
|
|
end
|
|
end
|
|
|
|
self.BarWidth=(self.XMax-self.XMin)/self.BarNum
|
|
self.Decay=math_pow(self.DecaySet,self.BarWidth)
|
|
self.ExpNorm=1/(1-self.Decay)/0.95 --Actually a finite geometric series
|
|
|
|
|
|
|
|
self:OldSetWidth(Width)
|
|
self:RefreshGraph()
|
|
end
|
|
|
|
function GraphFunctions:RealtimeSetHeight(Height)
|
|
self.Height=Height
|
|
|
|
for i=1,self.BarNum do
|
|
--self.Bars[i]:Hide()
|
|
self.Bars[i]:SetValue(0)
|
|
self.Bars[i]:SetHeight(self.Height)
|
|
end
|
|
|
|
self:OldSetHeight(Height)
|
|
self:RefreshGraph()
|
|
end
|
|
|
|
function GraphFunctions:GetMaxValue()
|
|
--Is there any data that could possibly be not zero?
|
|
if self.LastDataTime<(self.LastShift+self.XMin-self.TimeRadius) then
|
|
return 0
|
|
end
|
|
|
|
local MaxY=0
|
|
|
|
for i=1,self.BarNum do
|
|
MaxY=math_max(MaxY,self.BarHeight[i])
|
|
end
|
|
|
|
return MaxY
|
|
end
|
|
|
|
|
|
|
|
function GraphFunctions:RealtimeGetValue(Time)
|
|
local Bar
|
|
if Time<self.XMin or Time>self.XMax then
|
|
return 0
|
|
end
|
|
|
|
Bar=math_min(math_max(math_floor(self.BarNum*(Time-self.XMin)/(self.XMax-self.XMin)+0.5),1),self.BarNum)
|
|
|
|
return self.BarHeight[Bar]
|
|
end
|
|
|
|
function GraphFunctions:SetUpdateLimit(Time)
|
|
self.LimitUpdates=Time
|
|
end
|
|
|
|
function GraphFunctions:SetDecay(decay)
|
|
self.DecaySet=decay
|
|
self.Decay=math_pow(self.DecaySet,self.BarWidth)
|
|
self.ExpNorm=1/(1-self.Decay)/0.95 --Actually a finite geometric series (divide 0.96 instead of 1 since seems doesn't quite work right)
|
|
end
|
|
|
|
function GraphFunctions:AddBar(value)
|
|
for i=1,self.BarNum-1 do
|
|
self.BarHeight[i]=self.BarHeight[i+1]
|
|
end
|
|
self.BarHeight[self.BarNum]=value
|
|
self.AddedBar=true
|
|
end
|
|
|
|
function GraphFunctions:SetBars()
|
|
local YHeight=self.YMax-self.YMin
|
|
|
|
for i,bar in pairs(self.BarsUsing) do
|
|
local h
|
|
h=(self.BarHeight[i]-self.YMin)/YHeight
|
|
|
|
bar:SetValue(h)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
--Functions for Line Graph Data
|
|
-------------------------------------------------------------------------------
|
|
|
|
function GraphFunctions:AddDataSeries(points,color,n2)
|
|
local data
|
|
--Make sure there is data points
|
|
if not points then
|
|
return
|
|
end
|
|
|
|
data=points
|
|
if n2==nil then
|
|
n2=false
|
|
end
|
|
if n2 or (table.getn(points)==2 and table.getn(points[1])~=2) then
|
|
data={}
|
|
for k,v in ipairs(points[1]) do
|
|
tinsert(data,{v,points[2][k]})
|
|
end
|
|
end
|
|
|
|
tinsert(self.Data,{Points=data;Color=color})
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:AddFilledDataSeries(points,color,n2)
|
|
local data
|
|
--Make sure there is data points
|
|
if not points or #points==0 then
|
|
return
|
|
end
|
|
|
|
data=points
|
|
if n2==nil then
|
|
n2=false
|
|
end
|
|
|
|
if n2 or (table.getn(points)==2 and table.getn(points[1])~=2) then
|
|
data={}
|
|
for k,v in ipairs(points[1]) do
|
|
tinsert(data,{v,points[2][k]})
|
|
end
|
|
end
|
|
|
|
tinsert(self.FilledData,{Points=data;Color=color})
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
|
|
function GraphFunctions:ResetData()
|
|
self.Data={}
|
|
|
|
if self.FilledData then
|
|
self.FilledData={}
|
|
end
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetLinearFit(fit)
|
|
self.LinearFit=fit
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
|
|
|
|
function GraphFunctions:HideTextures()
|
|
local k=#self.TexturesUsed
|
|
while k>0 do
|
|
self.Textures[#self.Textures+1]=self.TexturesUsed[k]
|
|
self.TexturesUsed[k]:Hide()
|
|
self.TexturesUsed[k]=nil
|
|
k=k-1
|
|
end
|
|
end
|
|
|
|
--Make sure to show a texture after you grab it or its free for anyone else to grab
|
|
function GraphFunctions:FindTexture()
|
|
local t
|
|
if #self.Textures>0 then
|
|
t=self.Textures[#self.Textures]
|
|
self.TexturesUsed[#self.TexturesUsed+1]=t
|
|
self.Textures[#self.Textures]=nil
|
|
return t
|
|
end
|
|
local g=self:CreateTexture(nil,"BACKGROUND")
|
|
self.TexturesUsed[#self.TexturesUsed+1]=g
|
|
return g
|
|
end
|
|
|
|
function GraphFunctions:HideFontStrings()
|
|
if not self.FontStrings then
|
|
self.FontStrings={}
|
|
end
|
|
for k, t in pairs(self.FontStrings) do
|
|
t:Hide()
|
|
end
|
|
end
|
|
|
|
--Make sure to show a fontstring after you grab it or its free for anyone else to grab
|
|
function GraphFunctions:FindFontString()
|
|
for k, t in pairs(self.FontStrings) do
|
|
if not t:IsShown() then
|
|
return t
|
|
end
|
|
end
|
|
local g
|
|
|
|
if self.TextFrame then
|
|
g=self.TextFrame:CreateFontString(nil,"OVERLAY")
|
|
else
|
|
g=self:CreateFontString(nil,"OVERLAY")
|
|
end
|
|
tinsert(self.FontStrings,g)
|
|
return g
|
|
end
|
|
|
|
--Linear Regression via Least Squares
|
|
function GraphFunctions:LinearRegression(data)
|
|
local alpha, beta
|
|
local n, SX,SY,SXX, SXY = 0,0,0,0,0
|
|
|
|
for k,v in pairs(data) do
|
|
n=n+1
|
|
|
|
SX=SX+v[1]
|
|
SXX=SXX+v[1]*v[1]
|
|
SY=SY+v[2]
|
|
SXY=SXY+v[1]*v[2]
|
|
end
|
|
|
|
beta=(n*SXY-SX*SY)/(n*SXX-SX*SX)
|
|
alpha=(SY-beta*SX)/n
|
|
|
|
return alpha, beta
|
|
end
|
|
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
--Functions for Pie Chart
|
|
-------------------------------------------------------------------------------
|
|
local PiePieces={"1-2",
|
|
"1-4",
|
|
"1-8",
|
|
"1-16",
|
|
"1-32",
|
|
"1-64",
|
|
"1-128"}
|
|
|
|
--26 Colors
|
|
local ColorTable={
|
|
{0.9,0.1,0.1},
|
|
{0.1,0.9,0.1},
|
|
{0.1,0.1,0.9},
|
|
{0.9,0.9,0.1},
|
|
{0.9,0.1,0.9},
|
|
{0.1,0.9,0.9},
|
|
{0.9,0.9,0.9},
|
|
{0.5,0.1,0.1},
|
|
{0.1,0.5,0.1},
|
|
{0.1,0.1,0.5},
|
|
{0.5,0.5,0.1},
|
|
{0.5,0.1,0.5},
|
|
{0.1,0.5,0.5},
|
|
{0.5,0.5,0.5},
|
|
{0.75,0.15,0.15},
|
|
{0.15,0.75,0.15},
|
|
{0.15,0.15,0.75},
|
|
{0.75,0.75,0.15},
|
|
{0.75,0.15,0.75},
|
|
{0.15,0.75,0.75},
|
|
{0.9,0.5,0.1},
|
|
{0.1,0.5,0.9},
|
|
{0.9,0.1,0.5},
|
|
{0.5,0.9,0.1},
|
|
{0.5,0.1,0.9},
|
|
{0.1,0.9,0.5},
|
|
}
|
|
function GraphFunctions:AddPie(Percent, Color)
|
|
local k,v
|
|
local PiePercent=self.PercentOn
|
|
|
|
local CurPiece=50
|
|
local Angle=180
|
|
local CurAngle=PiePercent*360/100
|
|
|
|
self.TotalSections=self.TotalSections+1
|
|
if type(self.Sections[self.TotalSections])~="table" then
|
|
self.Sections[self.TotalSections]={}
|
|
end
|
|
|
|
local Section=self.Sections[self.TotalSections]
|
|
Section.Textures={}
|
|
|
|
if type(Color)~="table" then
|
|
if self.onColor<=table.maxn(ColorTable) then
|
|
Color=ColorTable[self.onColor]
|
|
else
|
|
Color={math_random(),math_random(),math_random()}
|
|
end
|
|
self.onColor=self.onColor+1
|
|
end
|
|
|
|
if PiePercent==0 then
|
|
self:DrawLinePie(0)
|
|
end
|
|
|
|
Percent=Percent+self.Remaining
|
|
local LastPiece=0
|
|
for k,v in pairs(PiePieces) do
|
|
if (Percent+0.1)>CurPiece then
|
|
local t=self:FindTexture()
|
|
t:SetTexture(TextureDirectory..v)
|
|
t:ClearAllPoints()
|
|
t:SetPoint("CENTER",self,"CENTER",0,0)
|
|
t:SetHeight(self:GetHeight())
|
|
t:SetWidth(self:GetWidth())
|
|
GraphFunctions:RotateTexture(t,CurAngle)
|
|
t:Show()
|
|
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
Percent=Percent-CurPiece
|
|
PiePercent=PiePercent+CurPiece
|
|
CurAngle=CurAngle+Angle
|
|
|
|
tinsert(Section.Textures,t)
|
|
|
|
if k == 7 then
|
|
LastPiece=0.09
|
|
end
|
|
end
|
|
CurPiece=CurPiece/2
|
|
Angle=Angle/2
|
|
end
|
|
|
|
--Finish adding section data
|
|
Section.Color=Color
|
|
Section.Angle=CurAngle
|
|
|
|
self:DrawLinePie((PiePercent+LastPiece)*360/100)
|
|
self.PercentOn=PiePercent
|
|
self.Remaining=Percent
|
|
|
|
return Color
|
|
end
|
|
|
|
function GraphFunctions:CompletePie(Color)
|
|
local Percent=100-self.PercentOn
|
|
local k,v
|
|
local PiePercent=self.PercentOn
|
|
|
|
local CurPiece=50
|
|
local Angle=180
|
|
local CurAngle=PiePercent*360/100
|
|
|
|
self.TotalSections=self.TotalSections+1
|
|
if not self.Sections[self.TotalSections] then
|
|
self.Sections[self.TotalSections]={}
|
|
end
|
|
|
|
local Section=self.Sections[self.TotalSections]
|
|
Section.Textures={}
|
|
|
|
if type(Color)~="table" then
|
|
if self.onColor<=table.maxn(ColorTable) then
|
|
Color=ColorTable[self.onColor]
|
|
else
|
|
Color={math_random(),math_random(),math_random()}
|
|
end
|
|
self.onColor=self.onColor+1
|
|
end
|
|
|
|
|
|
|
|
Percent=Percent+self.Remaining
|
|
if PiePercent~=0 then
|
|
for k,v in pairs(PiePieces) do
|
|
if (Percent+0.1)>CurPiece then
|
|
local t=self:FindTexture()
|
|
t:SetTexture(TextureDirectory..v)
|
|
t:ClearAllPoints()
|
|
t:SetPoint("CENTER",self,"CENTER",0,0)
|
|
t:SetHeight(self:GetHeight())
|
|
t:SetWidth(self:GetWidth())
|
|
GraphFunctions:RotateTexture(t,CurAngle)
|
|
t:Show()
|
|
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
Percent=Percent-CurPiece
|
|
PiePercent=PiePercent+CurPiece
|
|
CurAngle=CurAngle+Angle
|
|
|
|
tinsert(Section.Textures,t)
|
|
end
|
|
CurPiece=CurPiece/2
|
|
Angle=Angle/2
|
|
end
|
|
else--Special case if its by itself
|
|
local t=self:FindTexture()
|
|
t:SetTexture(TextureDirectory.."1-1")
|
|
t:ClearAllPoints()
|
|
t:SetPoint("CENTER",self,"CENTER",0,0)
|
|
t:SetHeight(self:GetHeight())
|
|
t:SetWidth(self:GetWidth())
|
|
GraphFunctions:RotateTexture(t,CurAngle)
|
|
t:Show()
|
|
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
tinsert(Section.Textures,t)
|
|
end
|
|
|
|
--Finish adding section data
|
|
Section.Color=Color
|
|
Section.Angle=360
|
|
|
|
self.PercentOn=PiePercent
|
|
self.Remaining=Percent
|
|
|
|
return Color
|
|
end
|
|
|
|
function GraphFunctions:ResetPie()
|
|
self:HideTextures()
|
|
self:HideLines(self)
|
|
|
|
self.PieUsed=0
|
|
self.PercentOn=0
|
|
self.Remaining=0
|
|
self.onColor=1
|
|
self.LastSection=nil
|
|
self.TotalSections=0
|
|
--self.Sections={}
|
|
end
|
|
|
|
function GraphFunctions:DrawLinePie(angle)
|
|
local sx,sy,ex,ey
|
|
local Radian=math_pi*(90-angle)/180
|
|
local w,h
|
|
w=self:GetWidth()/2
|
|
h=self:GetHeight()/2
|
|
|
|
|
|
sx=w
|
|
sy=h
|
|
|
|
ex=sx+0.88*w*math_cos(Radian)
|
|
ey=sx+0.88*h*math_sin(Radian)
|
|
|
|
self:DrawLine(self,sx,sy,ex,ey,34,{0.0,0.0,0.0,1.0},"OVERLAY")
|
|
end
|
|
|
|
--Used to rotate the pie slices
|
|
function GraphFunctions:RotateTexture(texture,angle)
|
|
local Radian=math_pi*(45-angle)/180
|
|
local Radian2=math_pi*(45+90-angle)/180
|
|
local Radius=0.70710678118654752440084436210485
|
|
|
|
local tx,ty,tx2,ty2
|
|
tx=Radius*math_cos(Radian)
|
|
ty=Radius*math_sin(Radian)
|
|
tx2=-ty
|
|
ty2=tx
|
|
|
|
texture:SetTexCoord(0.5-tx,0.5-ty,0.5+tx2,0.5+ty2,0.5-tx2,0.5-ty2,0.5+tx,0.5+ty)
|
|
end
|
|
|
|
function GraphFunctions:SetSelectionFunc(f)
|
|
self.SelectionFunc=f
|
|
end
|
|
|
|
--TODO: Pie chart pieces need to be clickable
|
|
function GraphFunctions:PieChart_OnUpdate()
|
|
if (MouseIsOver(self)) then
|
|
local sX,sY=self:GetCenter()
|
|
local Scale=self:GetEffectiveScale()
|
|
local mX,mY=GetCursorPosition()
|
|
local dX,dY
|
|
|
|
dX=mX/Scale-sX
|
|
dY=mY/Scale-sY
|
|
|
|
local Angle=90-math_deg(math_atan(dY/dX))
|
|
dY=dY*self.Ratio
|
|
local Dist=dX*dX+dY*dY
|
|
|
|
if dX<0 then
|
|
Angle=Angle+180
|
|
end
|
|
|
|
local k,v
|
|
--Are we on the Pie Chart?
|
|
if Dist<self.Radius then
|
|
--What section are we on?
|
|
for k=1,self.TotalSections do
|
|
v=self.Sections[k]
|
|
if Angle<v.Angle then
|
|
local Color
|
|
if k~=self.LastSection then
|
|
if self.LastSection then
|
|
local Section=self.Sections[self.LastSection]
|
|
for _, t in pairs(Section.Textures) do
|
|
Color=Section.Color
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
end
|
|
end
|
|
|
|
if self.SelectionFunc then
|
|
self:SelectionFunc(k)
|
|
end
|
|
end
|
|
|
|
local ColorAdd=0.15*math_abs(math_fmod(GetTime(),3)-1.5)-0.1125
|
|
|
|
Color={}
|
|
Color[1]=v.Color[1]+ColorAdd
|
|
Color[2]=v.Color[2]+ColorAdd
|
|
Color[3]=v.Color[3]+ColorAdd
|
|
|
|
for _, t in pairs(v.Textures) do
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
end
|
|
|
|
self.LastSection = k
|
|
|
|
return
|
|
end
|
|
end
|
|
elseif self.LastSection then
|
|
local Section=self.Sections[self.LastSection]
|
|
for _, t in pairs(Section.Textures) do
|
|
local Color=Section.Color
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
end
|
|
self.LastSection=nil
|
|
if self.SelectionFunc then
|
|
self:SelectionFunc(nil)
|
|
end
|
|
end
|
|
else
|
|
if self.LastSection then
|
|
local Section=self.Sections[self.LastSection]
|
|
for _, t in pairs(Section.Textures) do
|
|
local Color=Section.Color
|
|
t:SetVertexColor(Color[1],Color[2],Color[3],1.0)
|
|
end
|
|
self.LastSection=nil
|
|
if self.SelectionFunc then
|
|
self:SelectionFunc(nil)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
--Axis Setting Functions
|
|
-------------------------------------------------------------------------------
|
|
function GraphFunctions:SetYMax(ymax)
|
|
if ymax==self.YMax then
|
|
return
|
|
end
|
|
|
|
self.YMax=ymax
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetYAxis(ymin,ymax)
|
|
if self.YMin==ymin and self.YMax==ymax then
|
|
return
|
|
end
|
|
|
|
self.YMin=ymin
|
|
self.YMax=ymax
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetMinMaxY(val)
|
|
if self.MinMaxY==val then
|
|
return
|
|
end
|
|
|
|
self.MinMaxY=val
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetXAxis(xmin,xmax)
|
|
if self.XMin==xmin and self.XMax==xmax then
|
|
return
|
|
end
|
|
|
|
self.XMin=xmin
|
|
self.XMax=xmax
|
|
|
|
self.NeedsUpdate=true
|
|
|
|
if self.GraphType=="REALTIME" then
|
|
self.BarWidth=(xmax-xmin)/self.BarNum
|
|
self.Decay=math_pow(self.DecaySet,self.BarWidth)
|
|
self.FilterOverlap=math_max(math_ceil((self.TimeRadius+xmax)/self.BarWidth),0)
|
|
self.LastShift=GetTime()+xmin
|
|
end
|
|
end
|
|
|
|
function GraphFunctions:SetAutoScale(auto)
|
|
self.AutoScale=auto
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
--The various Lock Functions let you use Autoscale but holds the locked points in place
|
|
function GraphFunctions:LockXMin(state)
|
|
if state==nil then
|
|
self.LockOnXMin = not self.LockOnXMin
|
|
return
|
|
end
|
|
self.LockOnXMin = state
|
|
end
|
|
|
|
function GraphFunctions:LockXMax(state)
|
|
if state==nil then
|
|
self.LockOnXMax = not self.LockOnXMax
|
|
return
|
|
end
|
|
self.LockOnXMax = state
|
|
end
|
|
|
|
function GraphFunctions:LockYMin(state)
|
|
if state==nil then
|
|
self.LockOnYMin = not self.LockOnYMin
|
|
return
|
|
end
|
|
self.LockOnYMin = state
|
|
end
|
|
|
|
function GraphFunctions:LockYMax(state)
|
|
if state==nil then
|
|
self.LockOnYMax = not self.LockOnYMax
|
|
return
|
|
end
|
|
self.LockOnYMax = state
|
|
end
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
--Grid & Axis Drawing Functions
|
|
-------------------------------------------------------------------------------
|
|
function GraphFunctions:SetAxisDrawing(xaxis, yaxis)
|
|
if xaxis==self.XAxisDrawn and self.YAxisDrawn==yaxis then
|
|
return
|
|
end
|
|
|
|
self.XAxisDrawn=xaxis
|
|
self.YAxisDrawn=yaxis
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetGridSpacing(xspacing,yspacing)
|
|
if xspacing==self.XGridInterval and self.YGridInterval==yspacing then
|
|
return
|
|
end
|
|
|
|
self.XGridInterval=xspacing
|
|
self.YGridInterval=yspacing
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetAxisColor(color)
|
|
if self.AxisColor[1]==color[1] and self.AxisColor[2]==color[2] and self.AxisColor[3]==color[3] and self.AxisColor[4]==color[4] then
|
|
return
|
|
end
|
|
|
|
self.AxisColor=color
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetGridColor(color)
|
|
if self.GridColor[1]==color[1] and self.GridColor[2]==color[2] and self.GridColor[3]==color[3] and self.GridColor[4]==color[4] then
|
|
return
|
|
end
|
|
|
|
self.GridColor=color
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetGridColorSecondary(color)
|
|
if self.GridColorSecondary~=nil and self.GridColorSecondary[1]==color[1] and self.GridColorSecondary[2]==color[2] and self.GridColorSecondary[3]==color[3] and self.GridColorSecondary[4]==color[4] then
|
|
return
|
|
end
|
|
|
|
self.GridColorSecondary=color
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetGridSecondaryMultiple(XAxis,YAxis)
|
|
if type(XAxis)~="number" then
|
|
XAxis=1
|
|
end
|
|
if type(YAxis)~="number" then
|
|
YAxis=1
|
|
end
|
|
self.GridSecondaryX=XAxis
|
|
self.GridSecondaryY=YAxis
|
|
|
|
self.NeedsUpdate=true
|
|
end
|
|
|
|
function GraphFunctions:SetYLabels(Left,Right)
|
|
self.YLabelsLeft=Left
|
|
self.YLabelsRight=Right
|
|
end
|
|
|
|
|
|
function GraphFunctions:CreateGridlines()
|
|
local Width=self:GetWidth()
|
|
local Height=self:GetHeight()
|
|
local NoSecondary=(self.GridSecondaryY==nil) or (self.GridSecondaryX==nil) or (type(self.GridColorSecondary)~="table")
|
|
self:HideLines(self)
|
|
self:HideFontStrings()
|
|
|
|
if self.YGridInterval then
|
|
local LowerYGridLine,UpperYGridLine,TopSpace
|
|
LowerYGridLine=self.YMin/self.YGridInterval
|
|
LowerYGridLine=math_max(math_floor(LowerYGridLine),math_ceil(LowerYGridLine))
|
|
UpperYGridLine=self.YMax/self.YGridInterval
|
|
UpperYGridLine=math_min(math_floor(UpperYGridLine),math_ceil(UpperYGridLine))
|
|
TopSpace=Height*(1-(UpperYGridLine*self.YGridInterval-self.YMin)/(self.YMax-self.YMin))
|
|
for i=LowerYGridLine,UpperYGridLine do
|
|
if i~=0 or not self.YAxisDrawn then
|
|
local YPos,T,F
|
|
YPos=Height*(i*self.YGridInterval-self.YMin)/(self.YMax-self.YMin)
|
|
if NoSecondary or math_fmod(i,self.GridSecondaryY)==0 then
|
|
T=self:DrawLine(self,0,YPos,Width,YPos,24,self.GridColor,"BACKGROUND")
|
|
else
|
|
T=self:DrawLine(self,0,YPos,Width,YPos,24,self.GridColorSecondary,"BACKGROUND")
|
|
end
|
|
|
|
if ((i~=UpperYGridLine) or (TopSpace>12)) and (NoSecondary or math_fmod(i,self.GridSecondaryY)==0) then
|
|
if self.YLabelsLeft then
|
|
F=self:FindFontString()
|
|
F:SetFontObject("GameFontHighlightSmall")
|
|
F:SetTextColor(1,1,1)
|
|
F:ClearAllPoints()
|
|
F:SetPoint("BOTTOMLEFT",T,"LEFT",2,2)
|
|
F:SetText(i*self.YGridInterval)
|
|
F:Show()
|
|
end
|
|
|
|
if self.YLabelsRight then
|
|
F=self:FindFontString()
|
|
F:SetFontObject("GameFontHighlightSmall")
|
|
F:SetTextColor(1,1,1)
|
|
F:ClearAllPoints()
|
|
F:SetPoint("BOTTOMRIGHT",T,"RIGHT",-2,2)
|
|
F:SetText(i*self.YGridInterval)
|
|
F:Show()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if self.XGridInterval then
|
|
local LowerXGridLine,UpperXGridLine
|
|
LowerXGridLine=self.XMin/self.XGridInterval
|
|
LowerXGridLine=math_max(math_floor(LowerXGridLine),math_ceil(LowerXGridLine))
|
|
UpperXGridLine=self.XMax/self.XGridInterval
|
|
UpperXGridLine=math_min(math_floor(UpperXGridLine),math_ceil(UpperXGridLine))
|
|
|
|
for i=LowerXGridLine,UpperXGridLine do
|
|
if i~=0 or not self.XAxisDrawn then
|
|
local XPos
|
|
XPos=Width*(i*self.XGridInterval-self.XMin)/(self.XMax-self.XMin)
|
|
if NoSecondary or math_fmod(i,self.GridSecondaryX)==0 then
|
|
self:DrawLine(self,XPos,0,XPos,Height,24,self.GridColor,"BACKGROUND")
|
|
else
|
|
self:DrawLine(self,XPos,0,XPos,Height,24,self.GridColorSecondary,"BACKGROUND")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if self.YAxisDrawn and self.YMax>=0 and self.YMin<=0 then
|
|
local YPos,T
|
|
|
|
YPos=Height*(-self.YMin)/(self.YMax-self.YMin)
|
|
T=self:DrawLine(self,0,YPos,Width,YPos,24,self.AxisColor,"BACKGROUND")
|
|
|
|
if self.YLabelsLeft then
|
|
F=self:FindFontString()
|
|
F:SetFontObject("GameFontHighlightSmall")
|
|
F:SetTextColor(1,1,1)
|
|
F:ClearAllPoints()
|
|
F:SetPoint("BOTTOMLEFT",T,"LEFT",2,2)
|
|
F:SetText(0)
|
|
F:Show()
|
|
end
|
|
if self.YLabelsRight then
|
|
F=self:FindFontString()
|
|
F:SetFontObject("GameFontHighlightSmall")
|
|
F:SetTextColor(1,1,1)
|
|
F:ClearAllPoints()
|
|
F:SetPoint("BOTTOMRIGHT",T,"RIGHT",-2,2)
|
|
F:SetText(0)
|
|
F:Show()
|
|
end
|
|
end
|
|
|
|
if self.XAxisDrawn and self.XMax>=0 and self.XMin<=0 then
|
|
local XPos;
|
|
|
|
XPos=Width*(-self.XMin)/(self.XMax-self.XMin)
|
|
self:DrawLine(self,XPos,0,XPos,Height,24,self.AxisColor,"BACKGROUND")
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
--Refresh functions
|
|
--------------------------------------------------------------------------------
|
|
|
|
function GraphFunctions:OnUpdateGraph()
|
|
if self.NeedsUpdate and self.RefreshGraph then
|
|
self:RefreshGraph()
|
|
self.NeedsUpdate=false
|
|
end
|
|
end
|
|
|
|
--Performs a convolution in realtime allowing to graph Framerate, DPS, or any other data you want graphed in realtime
|
|
function GraphFunctions:OnUpdateGraphRealtime()
|
|
local i,j
|
|
local CurTime=GetTime()
|
|
local BarsChanged
|
|
|
|
if self.NextUpdate>CurTime or (self.Mode=="RAW" and not (self.NeedsUpdate or self.AddedBar)) then
|
|
return
|
|
end
|
|
|
|
self.NextUpdate=CurTime+self.LimitUpdates
|
|
|
|
--Slow Mode performs an entire convolution every frame
|
|
if self.Mode=="SLOW" then
|
|
--Initialize Bar Data
|
|
self.BarHeight={}
|
|
for i=1,self.BarNum do
|
|
self.BarHeight[i]=0
|
|
end
|
|
local k,v
|
|
local BarTimeRadius=(self.XMax-self.XMin)/self.BarNum
|
|
local DataValue=1/(2*self.TimeRadius)
|
|
|
|
if self.Filter=="RECT" then
|
|
--Take the convolution of the dataset on to the bars wtih a rectangular filter
|
|
local DataValue=1/(2*self.TimeRadius)
|
|
for k,v in pairs(self.Data) do
|
|
if v.Time<(CurTime+self.XMin-self.TimeRadius) then
|
|
tremove(self.Data,k)
|
|
else
|
|
local DataTime=v.Time-CurTime
|
|
local LowestBar=math_max(math_floor((DataTime-self.XMin-self.TimeRadius)/BarTimeRadius),1)
|
|
local HighestBar=math_min(math_ceil((DataTime-self.XMin+self.TimeRadius)/BarTimeRadius),self.BarNum)
|
|
for i=LowestBar,HighestBar do
|
|
self.BarHeight[i]=self.BarHeight[i]+v.Value*DataValue
|
|
end
|
|
end
|
|
end
|
|
elseif self.Filter=="TRI" then
|
|
--Needs optimization badly
|
|
--Take the convolution of the dataset on to the bars wtih a triangular filter
|
|
local DataValue=1/(self.TimeRadius)
|
|
for k,v in pairs(self.Data) do
|
|
local Temp
|
|
if v.Time<(CurTime+self.XMin-self.TimeRadius) then
|
|
tremove(self.Data,k)
|
|
else
|
|
local DataTime=v.Time-CurTime
|
|
local LowestBar=math_max(math_floor((DataTime-self.XMin-self.TimeRadius)/BarTimeRadius),1)
|
|
local HighestBar=math_min(math_ceil((DataTime-self.XMin+self.TimeRadius)/BarTimeRadius),self.BarNum)
|
|
|
|
|
|
for i=LowestBar,HighestBar do
|
|
self.BarHeight[i]=self.BarHeight[i]+v.Value*DataValue*math_abs(BarTimeRadius*i+self.XMin-DataTime)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
BarsChanged=true
|
|
elseif self.Mode=="FAST" then
|
|
local ShiftBars=math_floor((CurTime-self.LastShift)/self.BarWidth)
|
|
|
|
if ShiftBars>0 and not (self.LastDataTime<(self.LastShift+self.XMin-self.TimeRadius*2)) then
|
|
local RecalcBars=self.BarNum-(ShiftBars+self.FilterOverlap)+1
|
|
|
|
for i=1,self.BarNum do
|
|
if i<RecalcBars then
|
|
self.BarHeight[i]=self.BarHeight[i+ShiftBars]
|
|
else
|
|
self.BarHeight[i]=0
|
|
end
|
|
end
|
|
|
|
local k,v
|
|
local BarTimeRadius=(self.XMax-self.XMin)/self.BarNum
|
|
local DataValue=1/(2*self.TimeRadius)
|
|
local TimeDiff=CurTime-self.LastShift
|
|
|
|
CurTime=self.LastShift+ShiftBars*self.BarWidth
|
|
self.LastShift=CurTime
|
|
|
|
if self.Filter=="RECT" then
|
|
--Take the convolution of the dataset on to the bars wtih a rectangular filter
|
|
local DataValue=1/(2*self.TimeRadius)
|
|
for k,v in pairs(self.Data) do
|
|
if v.Time<(CurTime+self.XMax-self.TimeRadius-TimeDiff) then
|
|
tremove(self.Data,k)
|
|
else
|
|
local DataTime=v.Time-CurTime
|
|
local LowestBar=math_max(math_max(math_floor((DataTime-self.XMin-self.TimeRadius)/BarTimeRadius),RecalcBars),1)
|
|
local HighestBar=math_min(math_ceil((DataTime-self.XMin+self.TimeRadius)/BarTimeRadius),self.BarNum)
|
|
if LowestBar<=HighestBar then
|
|
for i=LowestBar,HighestBar do
|
|
self.BarHeight[i]=self.BarHeight[i]+v.Value*DataValue
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
BarsChanged=true
|
|
else
|
|
CurTime=self.LastShift+ShiftBars*self.BarWidth
|
|
self.LastShift=CurTime
|
|
end
|
|
elseif self.Mode=="EXP" then
|
|
local ShiftBars=math_floor((CurTime-self.LastShift)/self.BarWidth)
|
|
|
|
if ShiftBars>0 then
|
|
local RecalcBars=self.BarNum-ShiftBars+1
|
|
|
|
for i=1,self.BarNum do
|
|
if i<RecalcBars then
|
|
self.BarHeight[i]=self.BarHeight[i+ShiftBars]
|
|
end
|
|
end
|
|
|
|
--Now to calculate the new bars
|
|
local Total
|
|
local Weight=1/self.TimeRadius/self.ExpNorm
|
|
|
|
for i=RecalcBars,self.BarNum do
|
|
Total=0
|
|
|
|
--Implement an EXPFAST which does this only once instead of for each bar
|
|
for k,v in pairs(self.Data) do
|
|
Total=Total+v.Value*Weight
|
|
if v.Time<(self.LastShift-self.TimeRadius) then
|
|
tremove(self.Data,k)
|
|
end
|
|
end
|
|
|
|
self.CurVal=self.Decay*self.CurVal+Total
|
|
|
|
self.BarHeight[i]=self.CurVal
|
|
|
|
self.LastShift=self.LastShift+self.BarWidth
|
|
end
|
|
|
|
if self.CurVal<0.1 then
|
|
self.CurVal=0
|
|
end
|
|
BarsChanged=true
|
|
else
|
|
self.LastShift=self.LastShift+self.BarWidth*ShiftBars
|
|
end
|
|
elseif self.Mode=="EXPFAST" then
|
|
local ShiftBars=math_floor((CurTime-self.LastShift)/self.BarWidth)
|
|
local RecalcBars=self.BarNum-ShiftBars+1
|
|
|
|
if ShiftBars>0 and not (self.LastDataTime<(self.LastShift+self.XMin-self.TimeRadius)) then
|
|
for i=1,self.BarNum do
|
|
if i<RecalcBars then
|
|
self.BarHeight[i]=self.BarHeight[i+ShiftBars]
|
|
end
|
|
end
|
|
|
|
--Now to calculate the new bars
|
|
local Total
|
|
local Weight=1/self.TimeRadius/self.ExpNorm
|
|
|
|
Total=0
|
|
|
|
--Implement an EXPFAST which does this only once instead of for each bar
|
|
for k,v in pairs(self.Data) do
|
|
Total=Total+v.Value*Weight
|
|
if v.Time<(self.LastShift-self.TimeRadius) then
|
|
tremove(self.Data,k)
|
|
end
|
|
end
|
|
|
|
--self.LastShift=self.LastShift+self.BarWidth*ShiftBars
|
|
|
|
if self.CurVal~=0 or Total~=0 then
|
|
for i=RecalcBars,self.BarNum do
|
|
self.CurVal=self.Decay*self.CurVal+Total
|
|
self.BarHeight[i]=self.CurVal
|
|
end
|
|
self.LastDataTime=self.LastShift+self.BarWidth*ShiftBars
|
|
else
|
|
for i=RecalcBars,self.BarNum do
|
|
self.BarHeight[i]=0
|
|
end
|
|
end
|
|
|
|
if self.CurVal<0.1 then
|
|
self.CurVal=0
|
|
end
|
|
BarsChanged=true
|
|
end
|
|
self.LastShift=self.LastShift+self.BarWidth*ShiftBars
|
|
elseif self.Mode=="RAW" then
|
|
--Do nothing really
|
|
--Using .AddedBar so we cut down on updating the grid
|
|
self.AddedBar=false
|
|
BarsChanged=true
|
|
end
|
|
|
|
|
|
|
|
if BarsChanged then
|
|
if self.AutoScale then
|
|
local MaxY=0
|
|
|
|
for i=1,self.BarNum do
|
|
MaxY=math_max(MaxY,self.BarHeight[i])
|
|
end
|
|
MaxY=1.25*MaxY
|
|
|
|
MaxY=math_max(MaxY,self.MinMaxY)
|
|
|
|
if MaxY~=0 and math_abs(self.YMax-MaxY)>0.01 then
|
|
self.YMax=MaxY
|
|
self.NeedsUpdate=true
|
|
end
|
|
end
|
|
self:SetBars()
|
|
end
|
|
|
|
if self.NeedsUpdate then
|
|
self.NeedsUpdate=false
|
|
self:RefreshGraph()
|
|
end
|
|
end
|
|
|
|
--Line Graph
|
|
function GraphFunctions:RefreshLineGraph()
|
|
local k1, k2, series
|
|
self:HideLines(self)
|
|
self:HideBars(self)
|
|
|
|
if self.AutoScale and self.Data then
|
|
local MinX, MaxX, MinY, MaxY = math_huge, -math_huge, math_huge, -math_huge
|
|
--Go through line data first
|
|
for k1, series in pairs(self.Data) do
|
|
for k2, point in pairs(series.Points) do
|
|
MinX=math_min(point[1],MinX)
|
|
MaxX=math_max(point[1],MaxX)
|
|
MinY=math_min(point[2],MinY)
|
|
MaxY=math_max(point[2],MaxY)
|
|
end
|
|
end
|
|
|
|
--Now through the Filled Lines
|
|
for k1, series in pairs(self.FilledData) do
|
|
for k2, point in pairs(series.Points) do
|
|
MinX=math_min(point[1],MinX)
|
|
MaxX=math_max(point[1],MaxX)
|
|
MinY=math_min(point[2],MinY)
|
|
MaxY=math_max(point[2],MaxY)
|
|
end
|
|
end
|
|
|
|
local XBorder, YBorder
|
|
|
|
XBorder=0.1*(MaxX-MinX)
|
|
YBorder=0.1*(MaxY-MinY)
|
|
|
|
if not self.LockOnXMin then
|
|
self.XMin=MinX-XBorder
|
|
end
|
|
if not self.LockOnXMax then
|
|
self.XMax=MaxX+XBorder
|
|
end
|
|
if not self.LockOnYMin then
|
|
self.YMin=MinY-YBorder
|
|
end
|
|
if not self.LockOnYMax then
|
|
self.YMax=MaxY+YBorder
|
|
end
|
|
end
|
|
|
|
self:CreateGridlines()
|
|
|
|
local Width=self:GetWidth()
|
|
local Height=self:GetHeight()
|
|
|
|
for k1, series in pairs(self.Data) do
|
|
local LastPoint
|
|
LastPoint=nil
|
|
|
|
for k2, point in pairs(series.Points) do
|
|
if LastPoint then
|
|
local TPoint={x=point[1];y=point[2]}
|
|
|
|
TPoint.x=Width*(TPoint.x-self.XMin)/(self.XMax-self.XMin)
|
|
TPoint.y=Height*(TPoint.y-self.YMin)/(self.YMax-self.YMin)
|
|
|
|
self:DrawLine(self,LastPoint.x,LastPoint.y,TPoint.x,TPoint.y,32,series.Color)
|
|
|
|
LastPoint=TPoint
|
|
else
|
|
LastPoint={x=point[1];y=point[2]}
|
|
LastPoint.x=Width*(LastPoint.x-self.XMin)/(self.XMax-self.XMin)
|
|
LastPoint.y=Height*(LastPoint.y-self.YMin)/(self.YMax-self.YMin)
|
|
end
|
|
end
|
|
end
|
|
|
|
--Filled Line Graphs
|
|
for k1, series in pairs(self.FilledData) do
|
|
local LastPoint
|
|
LastPoint=nil
|
|
|
|
for k2, point in pairs(series.Points) do
|
|
if LastPoint then
|
|
local TPoint={x=point[1];y=point[2]}
|
|
|
|
TPoint.x=Width*(TPoint.x-self.XMin)/(self.XMax-self.XMin)
|
|
TPoint.y=Height*(TPoint.y-self.YMin)/(self.YMax-self.YMin)
|
|
|
|
self:DrawBar(self,LastPoint.x,LastPoint.y,TPoint.x,TPoint.y,series.Color,k1)
|
|
|
|
LastPoint=TPoint
|
|
else
|
|
LastPoint={x=point[1];y=point[2]}
|
|
LastPoint.x=Width*(LastPoint.x-self.XMin)/(self.XMax-self.XMin)
|
|
LastPoint.y=Height*(LastPoint.y-self.YMin)/(self.YMax-self.YMin)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--Scatter Plot Refresh
|
|
function GraphFunctions:RefreshScatterPlot()
|
|
local k1, k2, series, point
|
|
self:HideLines(self)
|
|
|
|
if self.AutoScale and self.Data then
|
|
local MinX, MaxX, MinY, MaxY = math_huge, -math_huge, math_huge, -math_huge
|
|
for k1, series in pairs(self.Data) do
|
|
for k2, point in pairs(series.Points) do
|
|
MinX=math_min(point[1],MinX)
|
|
MaxX=math_max(point[1],MaxX)
|
|
MinY=math_min(point[2],MinY)
|
|
MaxY=math_max(point[2],MaxY)
|
|
end
|
|
end
|
|
|
|
local XBorder, YBorder
|
|
|
|
XBorder=0.1*(MaxX-MinX)
|
|
YBorder=0.1*(MaxY-MinY)
|
|
|
|
if not self.LockOnXMin then
|
|
self.XMin=MinX-XBorder
|
|
end
|
|
if not self.LockOnXMax then
|
|
self.XMax=MaxX+XBorder
|
|
end
|
|
if not self.LockOnYMin then
|
|
self.YMin=MinY-YBorder
|
|
end
|
|
if not self.LockOnYMax then
|
|
self.YMax=MaxY+YBorder
|
|
end
|
|
end
|
|
|
|
self:CreateGridlines()
|
|
|
|
local Width=self:GetWidth()
|
|
local Height=self:GetHeight()
|
|
|
|
self:HideTextures()
|
|
for k1, series in pairs(self.Data) do
|
|
local MinX,MaxX = self.XMax, self.XMin
|
|
for k2, point in pairs(series.Points) do
|
|
local x,y
|
|
MinX=math_min(point[1],MinX)
|
|
MaxX=math_max(point[1],MaxX)
|
|
x=Width*(point[1]-self.XMin)/(self.XMax-self.XMin)
|
|
y=Height*(point[2]-self.YMin)/(self.YMax-self.YMin)
|
|
|
|
local g=self:FindTexture()
|
|
g:SetTexture("Spells\\GENERICGLOW2_64.blp")
|
|
g:SetWidth(6)
|
|
g:SetHeight(6)
|
|
g:ClearAllPoints()
|
|
g:SetPoint("CENTER",self,"BOTTOMLEFT",x,y)
|
|
g:SetVertexColor(series.Color[1],series.Color[2],series.Color[3],series.Color[4]);
|
|
g:Show()
|
|
end
|
|
|
|
if self.LinearFit then
|
|
local alpha, beta = self:LinearRegression(series.Points)
|
|
local sx,sy,ex,ey
|
|
|
|
sx=MinX
|
|
sy=beta*sx+alpha
|
|
ex=MaxX
|
|
ey=beta*ex+alpha
|
|
|
|
sx=Width*(sx-self.XMin)/(self.XMax-self.XMin)
|
|
sy=Height*(sy-self.YMin)/(self.YMax-self.YMin)
|
|
ex=Width*(ex-self.XMin)/(self.XMax-self.XMin)
|
|
ey=Height*(ey-self.YMin)/(self.YMax-self.YMin)
|
|
|
|
self:DrawLine(self,sx,sy,ex,ey,32,series.Color)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--Copied from Blizzard's TaxiFrame code and modifed for IMBA then remodified for GraphLib
|
|
|
|
-- The following function is used with permission from Daniel Stephens <iriel@vigilance-committee.org>
|
|
local TAXIROUTE_LINEFACTOR = 128/126; -- Multiplying factor for texture coordinates
|
|
local TAXIROUTE_LINEFACTOR_2 = TAXIROUTE_LINEFACTOR / 2; -- Half of that
|
|
|
|
-- T - Texture
|
|
-- C - Canvas Frame (for anchoring)
|
|
-- sx,sy - Coordinate of start of line
|
|
-- ex,ey - Coordinate of end of line
|
|
-- w - Width of line
|
|
-- relPoint - Relative point on canvas to interpret coords (Default BOTTOMLEFT)
|
|
function lib:DrawLine(C, sx, sy, ex, ey, w, color, layer)
|
|
local relPoint = "BOTTOMLEFT"
|
|
|
|
if sx==ex then
|
|
if sy==ey then
|
|
return
|
|
else
|
|
return self:DrawVLine(C,sx,sy,ey,w, color, layer)
|
|
end
|
|
elseif sy==ey then
|
|
return self:DrawHLine(C,sx,ex,sy,w, color, layer)
|
|
end
|
|
|
|
if not C.GraphLib_Lines then
|
|
C.GraphLib_Lines={}
|
|
C.GraphLib_Lines_Used={}
|
|
end
|
|
|
|
local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
|
|
T:SetTexture(TextureDirectory.."line")
|
|
tinsert(C.GraphLib_Lines_Used,T)
|
|
|
|
T:SetDrawLayer(layer or "ARTWORK")
|
|
|
|
T:SetVertexColor(color[1],color[2],color[3],color[4]);
|
|
-- Determine dimensions and center point of line
|
|
local dx,dy = ex - sx, ey - sy;
|
|
local cx,cy = (sx + ex) / 2, (sy + ey) / 2;
|
|
|
|
-- Normalize direction if necessary
|
|
if (dx < 0) then
|
|
dx,dy = -dx,-dy;
|
|
end
|
|
|
|
-- Calculate actual length of line
|
|
local l = sqrt((dx * dx) + (dy * dy));
|
|
|
|
-- Sin and Cosine of rotation, and combination (for later)
|
|
local s,c = -dy / l, dx / l;
|
|
local sc = s * c;
|
|
|
|
-- Calculate bounding box size and texture coordinates
|
|
local Bwid, Bhgt, BLx, BLy, TLx, TLy, TRx, TRy, BRx, BRy;
|
|
if (dy >= 0) then
|
|
Bwid = ((l * c) - (w * s)) * TAXIROUTE_LINEFACTOR_2;
|
|
Bhgt = ((w * c) - (l * s)) * TAXIROUTE_LINEFACTOR_2;
|
|
BLx, BLy, BRy = (w / l) * sc, s * s, (l / w) * sc;
|
|
BRx, TLx, TLy, TRx = 1 - BLy, BLy, 1 - BRy, 1 - BLx;
|
|
TRy = BRx;
|
|
else
|
|
Bwid = ((l * c) + (w * s)) * TAXIROUTE_LINEFACTOR_2;
|
|
Bhgt = ((w * c) + (l * s)) * TAXIROUTE_LINEFACTOR_2;
|
|
BLx, BLy, BRx = s * s, -(l / w) * sc, 1 + (w / l) * sc;
|
|
BRy, TLx, TLy, TRy = BLx, 1 - BRx, 1 - BLx, 1 - BLy;
|
|
TRx = TLy;
|
|
end
|
|
|
|
-- Thanks Blizzard for adding (-)10000 as a hard-cap and throwing errors!
|
|
-- The cap was added in 3.1.0 and I think it was upped in 3.1.1
|
|
-- (way less chance to get the error)
|
|
if TLx > 10000 then TLx = 10000 elseif TLx < -10000 then TLx = -10000 end
|
|
if TLy > 10000 then TLy = 10000 elseif TLy < -10000 then TLy = -10000 end
|
|
if BLx > 10000 then BLx = 10000 elseif BLx < -10000 then BLx = -10000 end
|
|
if BLy > 10000 then BLy = 10000 elseif BLy < -10000 then BLy = -10000 end
|
|
if TRx > 10000 then TRx = 10000 elseif TRx < -10000 then TRx = -10000 end
|
|
if TRy > 10000 then TRy = 10000 elseif TRy < -10000 then TRy = -10000 end
|
|
if BRx > 10000 then BRx = 10000 elseif BRx < -10000 then BRx = -10000 end
|
|
if BRy > 10000 then BRy = 10000 elseif BRy < -10000 then BRy = -10000 end
|
|
|
|
-- Set texture coordinates and anchors
|
|
T:ClearAllPoints();
|
|
T:SetTexCoord(TLx, TLy, BLx, BLy, TRx, TRy, BRx, BRy);
|
|
T:SetPoint("BOTTOMLEFT", C, relPoint, cx - Bwid, cy - Bhgt);
|
|
T:SetPoint("TOPRIGHT", C, relPoint, cx + Bwid, cy + Bhgt);
|
|
T:Show()
|
|
return T
|
|
end
|
|
|
|
--Thanks to Celandro
|
|
function lib:DrawVLine(C, x, sy, ey, w, color, layer)
|
|
local relPoint = "BOTTOMLEFT"
|
|
|
|
if not C.GraphLib_Lines then
|
|
C.GraphLib_Lines={}
|
|
C.GraphLib_Lines_Used={}
|
|
end
|
|
|
|
local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
|
|
T:SetTexture(TextureDirectory.."sline");
|
|
tinsert(C.GraphLib_Lines_Used,T);
|
|
|
|
T:SetDrawLayer(layer or "ARTWORK")
|
|
|
|
T:SetVertexColor(color[1],color[2],color[3],color[4]);
|
|
|
|
if sy>ey then
|
|
sy, ey = ey, sy
|
|
end
|
|
|
|
-- Set texture coordinates and anchors
|
|
T:ClearAllPoints();
|
|
T:SetTexCoord(1, 0, 0, 0, 1, 1, 0, 1);
|
|
T:SetPoint("BOTTOMLEFT", C, relPoint, x-w/2, sy);
|
|
T:SetPoint("TOPRIGHT", C, relPoint, x+w/2, ey);
|
|
T:Show()
|
|
return T
|
|
end
|
|
|
|
function lib:DrawHLine(C, sx, ex, y, w, color, layer)
|
|
local relPoint = "BOTTOMLEFT"
|
|
|
|
if not C.GraphLib_Lines then
|
|
C.GraphLib_Lines={}
|
|
C.GraphLib_Lines_Used={}
|
|
end
|
|
|
|
local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
|
|
T:SetTexture(TextureDirectory.."sline");
|
|
tinsert(C.GraphLib_Lines_Used,T);
|
|
|
|
T:SetDrawLayer(layer or "ARTWORK")
|
|
|
|
T:SetVertexColor(color[1],color[2],color[3],color[4]);
|
|
|
|
if sx>ex then
|
|
sx, ex = ex, sx
|
|
end
|
|
|
|
-- Set texture coordinates and anchors
|
|
T:ClearAllPoints();
|
|
T:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1);
|
|
T:SetPoint("BOTTOMLEFT", C, relPoint, sx, y-w/2);
|
|
T:SetPoint("TOPRIGHT", C, relPoint, ex, y+w/2);
|
|
T:Show()
|
|
return T
|
|
end
|
|
|
|
function lib:HideLines(C)
|
|
if C.GraphLib_Lines then
|
|
for i = #C.GraphLib_Lines_Used, 1, -1 do
|
|
C.GraphLib_Lines_Used[i]:Hide()
|
|
tinsert(C.GraphLib_Lines,tremove(C.GraphLib_Lines_Used))
|
|
end
|
|
end
|
|
end
|
|
|
|
--Two parts to each bar
|
|
function lib:DrawBar(C, sx, sy, ex, ey, color, level)
|
|
local Bar, Tri, barNum, MinY, MaxY
|
|
|
|
--Want sx<=ex if not then flip them
|
|
if sx>ex then
|
|
sx, ex = ex, sx
|
|
sy, ey = ey, sy
|
|
end
|
|
|
|
if not C.GraphLib_Bars then
|
|
C.GraphLib_Bars={}
|
|
C.GraphLib_Tris={}
|
|
C.GraphLib_Bars_Used={}
|
|
C.GraphLib_Tris_Used={}
|
|
C.GraphLib_Frames={}
|
|
end
|
|
|
|
if (#C.GraphLib_Bars)>0 then
|
|
Bar=C.GraphLib_Bars[#C.GraphLib_Bars]
|
|
tremove(C.GraphLib_Bars,#C.GraphLib_Bars)
|
|
Bar:Show()
|
|
|
|
Tri=C.GraphLib_Tris[#C.GraphLib_Tris]
|
|
tremove(C.GraphLib_Tris,#C.GraphLib_Tris)
|
|
Tri:Show()
|
|
end
|
|
|
|
if not Bar then
|
|
Bar=C:CreateTexture(nil, "ARTWORK")
|
|
Bar:SetTexture(1,1,1,1)
|
|
|
|
Tri=C:CreateTexture(nil, "ARTWORK")
|
|
Tri:SetTexture(TextureDirectory.."triangle")
|
|
end
|
|
|
|
tinsert(C.GraphLib_Bars_Used,Bar)
|
|
tinsert(C.GraphLib_Tris_Used,Tri)
|
|
|
|
if level then
|
|
if type(C.GraphLib_Frames[level])=="nil" then
|
|
local newLevel=C:GetFrameLevel()+level
|
|
C.GraphLib_Frames[level]=CreateFrame("Frame",nil,C)
|
|
C.GraphLib_Frames[level]:SetFrameLevel(newLevel)
|
|
C.GraphLib_Frames[level]:SetAllPoints(C)
|
|
|
|
if C.TextFrame and C.TextFrame:GetFrameLevel()<=newLevel then
|
|
C.TextFrame:SetFrameLevel(newLevel+1)
|
|
self.NeedsUpdate=true
|
|
end
|
|
end
|
|
|
|
Bar:SetParent(C.GraphLib_Frames[level])
|
|
Tri:SetParent(C.GraphLib_Frames[level])
|
|
end
|
|
|
|
Bar:SetVertexColor(color[1],color[2],color[3],color[4])
|
|
Tri:SetVertexColor(color[1],color[2],color[3],color[4])
|
|
|
|
|
|
|
|
if sy<ey then
|
|
Tri:SetTexCoord(0,0,0,1,1,0,1,1)
|
|
MinY=sy
|
|
MaxY=ey
|
|
else
|
|
Tri:SetTexCoord(1,0,1,1,0,0,0,1)
|
|
MinY=ey
|
|
MaxY=sy
|
|
end
|
|
|
|
--Has to be at least 1 wide
|
|
if MinY<=1 then
|
|
MinY=1
|
|
end
|
|
|
|
|
|
|
|
Bar:ClearAllPoints()
|
|
Bar:SetPoint("BOTTOMLEFT", C, "BOTTOMLEFT", sx, 0);
|
|
|
|
local Width=ex-sx
|
|
if Width<1 then
|
|
Width=1
|
|
end
|
|
Bar:SetWidth(Width)
|
|
Bar:SetHeight(MinY)
|
|
|
|
|
|
if (MaxY-MinY)>=1 then
|
|
Tri:ClearAllPoints()
|
|
Tri:SetPoint("BOTTOMLEFT", C, "BOTTOMLEFT", sx, MinY)
|
|
Tri:SetWidth(Width)
|
|
Tri:SetHeight(MaxY-MinY)
|
|
else
|
|
Tri:Hide()
|
|
end
|
|
end
|
|
|
|
|
|
function lib:HideBars(C)
|
|
if not C.GraphLib_Bars then
|
|
return
|
|
end
|
|
|
|
while (#C.GraphLib_Bars_Used)>0 do
|
|
C.GraphLib_Bars[#C.GraphLib_Bars+1]=C.GraphLib_Bars_Used[#C.GraphLib_Bars_Used]
|
|
C.GraphLib_Bars[#C.GraphLib_Bars]:Hide()
|
|
C.GraphLib_Bars_Used[#C.GraphLib_Bars_Used]=nil
|
|
|
|
C.GraphLib_Tris[#C.GraphLib_Tris+1]=C.GraphLib_Tris_Used[#C.GraphLib_Tris_Used]
|
|
C.GraphLib_Tris[#C.GraphLib_Tris]:Hide()
|
|
C.GraphLib_Tris_Used[#C.GraphLib_Tris_Used]=nil
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- lib upgrade stuff, overwrite the old function references in
|
|
-- existing graphs with the ones in this newer library
|
|
for _, graph in ipairs(lib.RegisteredGraphRealtime) do
|
|
SetupGraphRealtimeFunctions(graph, true)
|
|
end
|
|
for _, graph in ipairs(lib.RegisteredGraphLine) do
|
|
SetupGraphLineFunctions(graph)
|
|
end
|
|
for _, graph in ipairs(lib.RegisteredGraphScatterPlot) do
|
|
SetupGraphScatterPlotFunctions(graph)
|
|
end
|
|
for _, graph in ipairs(lib.RegisteredGraphPieChart) do
|
|
SetupGraphPieChartFunctions(graph)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---------------------------------------------------
|
|
--Test Functions, for reference for addon authors to test, use and copy
|
|
--To test the library do /script LibStub("LibGraph-2.0"):TestGraph2Lib()
|
|
local function TestRealtimeGraph()
|
|
local Graph=LibStub(major)
|
|
local g=Graph:CreateGraphRealtime("TestRealtimeGraph",UIParent,"CENTER","CENTER",-90,90,150,150)
|
|
g:SetAutoScale(true)
|
|
g:SetGridSpacing(1.0,10.0)
|
|
g:SetYMax(120)
|
|
g:SetXAxis(-11,-1)
|
|
g:SetFilterRadius(1)
|
|
g:SetBarColors({0.2,0.0,0.0,0.4},{1.0,0.0,0.0,1.0})
|
|
|
|
local f = CreateFrame("Frame")
|
|
f:SetScript("OnUpdate",function() g:AddTimeData(1) end)
|
|
f:Show()
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing Reattime Graph")
|
|
end
|
|
|
|
local function TestRealtimeGraphRaw()
|
|
local Graph=LibStub(major)
|
|
local g=Graph:CreateGraphRealtime("TestRealtimeGraph",UIParent,"TOP","TOP",0,0,150,150)
|
|
g:SetAutoScale(true)
|
|
g:SetGridSpacing(1.0,10.0)
|
|
g:SetYMax(120)
|
|
g:SetXAxis(-10,0)
|
|
g:SetMode("RAW")
|
|
g:SetBarColors({0.2,0.0,0.0,0.4},{1.0,0.0,0.0,1.0})
|
|
|
|
local f = CreateFrame("Frame")
|
|
f.frames=0
|
|
f.NextUpdate=GetTime()
|
|
f:SetScript("OnUpdate",function()
|
|
if f.NextUpdate>GetTime() then
|
|
return
|
|
end
|
|
|
|
g:AddBar(UnitHealth("player"))
|
|
f.NextUpdate=f.NextUpdate+g.BarWidth
|
|
end)
|
|
f:Show()
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing 0")
|
|
end
|
|
|
|
|
|
|
|
local function TestLineGraph()
|
|
local Graph=LibStub(major)
|
|
local g=Graph:CreateGraphLine("TestLineGraph",UIParent,"CENTER","CENTER",90,90,150,150)
|
|
g:SetXAxis(-1,1)
|
|
g:SetYAxis(-1,1)
|
|
g:SetGridSpacing(0.25,0.25)
|
|
g:SetGridColor({0.5,0.5,0.5,0.5})
|
|
g:SetAxisDrawing(true,true)
|
|
g:SetAxisColor({1.0,1.0,1.0,1.0})
|
|
g:SetAutoScale(true)
|
|
|
|
local Data1={{0.05,0.05},{0.2,0.3},{0.4,0.2},{0.9,0.6}}
|
|
local Data2={{0.05,0.8},{0.3,0.1},{0.5,0.4},{0.95,0.05}}
|
|
|
|
g:AddDataSeries(Data1,{1.0,0.0,0.0,0.8})
|
|
g:AddDataSeries(Data2,{0.0,1.0,0.0,0.8})
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing Line Graph")
|
|
end
|
|
|
|
local function TestScatterPlot()
|
|
local Graph=LibStub(major)
|
|
local g=Graph:CreateGraphScatterPlot("TestScatterPlot",UIParent,"CENTER","CENTER",90,-90,150,150)
|
|
g:SetXAxis(-1,1)
|
|
g:SetYAxis(-1,1)
|
|
g:SetGridSpacing(0.25,0.25)
|
|
g:SetGridColor({0.5,0.5,0.5,0.5})
|
|
g:SetAxisDrawing(true,true)
|
|
g:SetAxisColor({1.0,1.0,1.0,1.0})
|
|
g:SetLinearFit(true)
|
|
g:SetAutoScale(true)
|
|
|
|
local Data1={{0.05,0.05},{0.2,0.3},{0.4,0.2},{0.9,0.6}}
|
|
local Data2={{0.05,0.8},{0.3,0.1},{0.5,0.4},{0.95,0.05}}
|
|
|
|
g:AddDataSeries(Data1,{1.0,0.0,0.0,0.8})
|
|
g:AddDataSeries(Data2,{0.0,1.0,0.0,0.8})
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing Scatter Plot")
|
|
end
|
|
|
|
local function TestPieChart()
|
|
local Graph=LibStub(major)
|
|
local g=Graph:CreateGraphPieChart("TestPieChart",UIParent,"CENTER","CENTER",-90,-90,150,150)
|
|
g:AddPie(35,{1.0,0.0,0.0})
|
|
g:AddPie(21,{0.0,1.0,0.0})
|
|
g:AddPie(10,{1.0,1.0,1.0})
|
|
g:CompletePie({0.2,0.2,1.0})
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing Pie Chart")
|
|
end
|
|
|
|
function lib:TestGraph2Lib()
|
|
DEFAULT_CHAT_FRAME:AddMessage("Testing "..major..", "..gsub(minor, "%$", ""))
|
|
TestRealtimeGraph()
|
|
TestLineGraph()
|
|
TestScatterPlot()
|
|
TestPieChart()
|
|
end
|