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.

2275 lines
63 KiB

4 years ago
--[[
Name: LibGraph-2.0
Revision: $Rev: 56 $
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"
3 years ago
local minor = 90000 + tonumber(("$Revision: 58 $"):match("(%d+)"))
4 years ago
--Search for just Addon\\ at the front since the interface part often gets trimmed
--Do this before anything else, so if it errors, any existing loaded copy of LibGraph-2.0
--doesn't get modified with a newer revision (this one)
local TextureDirectory
do
local path = string.match(debugstack(1, 1, 0), "AddOns\\(.+)LibGraph%-2%.0%.lua")
if path then
TextureDirectory = "Interface\\AddOns\\"..path
else
3 years ago
--error(major.." cannot determine the folder it is located in because the path is too long and got truncated in the debugstack(1, 1, 0) function call")
--beta doing some errors here
if (Details) then
TextureDirectory = [[Interface\AddOns\Details\Libs\LibGraph-2.0]]
end
4 years ago
end
end
3 years ago
TextureDirectory = "Interface\\Addons\\Details\\Libs\\LibGraph-2.0"
4 years ago
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 gsub = gsub
local ipairs = ipairs
local pairs = pairs
local sqrt = sqrt
local table = table
local tinsert = tinsert
local tremove = tremove
local type = type
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 CreateFrame = CreateFrame
local GetCursorPosition = GetCursorPosition
local GetTime = GetTime
local MouseIsOver = MouseIsOver
local UnitHealth = UnitHealth
local UIParent = UIParent
local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME
-- 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, BackdropTemplateMixin and "BackdropTemplate")
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, BackdropTemplateMixin and "BackdropTemplate")--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")
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.SetLineTexture = GraphFunctions.SetLineTexture
graph.SetBorderSize = GraphFunctions.SetBorderSize
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, BackdropTemplateMixin and "BackdropTemplate")
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, BackdropTemplateMixin and "BackdropTemplate")
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, BackdropTemplateMixin and "BackdropTemplate")
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")
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, linetexture)
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
if linetexture then
3 years ago
if not linetexture:find("\\") and not linetexture:find("//") then
4 years ago
linetexture = TextureDirectory..linetexture
end
end
tinsert(self.Data,{Points = data; Color = color; LineTexture=linetexture})
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 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 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
--Are we on the Pie Chart?
if Dist < self.Radius then
--What section are we on?
for k = 1, self.TotalSections do
local 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:SetLineTexture(texture)
3 years ago
if (type(texture) ~= "string") then
return assert(false, "Parameter 1 for SetLineTexture must be a string")
4 years ago
end
3 years ago
--full path
if (texture:find("\\") or texture:find("//")) then
4 years ago
self.CustomLine = texture
3 years ago
--using an image inside lib-graph folder
4 years ago
else
self.CustomLine = TextureDirectory..texture
end
end
function GraphFunctions:SetBorderSize(border, size)
3 years ago
border = string.lower(border)
4 years ago
3 years ago
if (type(size) ~= "number") then
return assert(false, "Parameter 2 for SetBorderSize must be a number")
4 years ago
end
if (border == "left") then
self.CustomLeftBorder = size
return true
elseif (border == "right") then
self.CustomRightBorder = size
return true
elseif (border == "top") then
self.CustomTopBorder = size
return true
elseif (border == "bottom") then
self.CustomBottomBorder = size
return true
end
3 years ago
return assert(false, "Usage: GraphObject:SetBorderSize (LEFT RIGHT TOP BOTTOM, SIZE)")
4 years ago
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")
local F
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))
--UpperYGridLine = math_min(UpperYGridLine, self.YGridMax or 16)
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
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))
--UpperXGridLine = math_min(UpperXGridLine, self.XGridMax or 16)
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 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 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
local Spacing
if self.YMax < 25 then
Spacing = -1
else
Spacing = math.log(self.YMax / 100) / math.log(2)
end
self.YGridInterval = 25 * math.pow(2, math.floor(Spacing))
end
end
self:SetBars()
end
if self.NeedsUpdate then
self.NeedsUpdate = false
self:RefreshGraph()
end
end
--Line Graph
function GraphFunctions:RefreshLineGraph()
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
if (self.CustomLeftBorder) then
3 years ago
self.XMin = MinX + self.CustomLeftBorder --custom size of left border
4 years ago
else
self.XMin = MinX - XBorder
end
end
if not self.LockOnXMax then
if (self.CustomRightBorder) then
3 years ago
self.XMax = MaxX + self.CustomRightBorder --custom size of right border
4 years ago
else
self.XMax = MaxX + XBorder
end
end
if not self.LockOnYMin then
if (self.CustomBottomBorder) then
3 years ago
self.YMin = MinY + self.CustomBottomBorder --custom size of bottom border
4 years ago
else
self.YMin = MinY - YBorder
end
end
if not self.LockOnYMax then
if (self.CustomTopBorder) then
3 years ago
self.YMax = MaxY + self.CustomTopBorder --custom size of top border
4 years ago
else
self.YMax = MaxY + YBorder
end
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)
--tercioo: send the data index to DrawLine so custom draw functions know what they are drawing
self:DrawLine(self, LastPoint.x, LastPoint.y, TPoint.x, TPoint.y, 32, series.Color, nil, series.LineTexture, 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
--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()
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")
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, linetexture)
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")
3 years ago
if linetexture then --this data series texture
4 years ago
T:SetTexture(linetexture)
3 years ago
elseif C.CustomLine then --overall chart texture
4 years ago
T:SetTexture(C.CustomLine)
3 years ago
else --no texture assigned, use default
4 years ago
T:SetTexture(TextureDirectory.."line")
end
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:SetColorTexture(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 Realtime 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