From c8dc7800823e2cb16245d30ea3b6b1fd3773e516 Mon Sep 17 00:00:00 2001 From: Tercio Jose Date: Tue, 6 Aug 2024 19:26:23 -0300 Subject: [PATCH] Complete re-write for the mythic+ chart shown in the end of the run --- Libs/DF/charts.examples.lua | 49 +- Libs/DF/charts.lua | 615 ++++++++++++++++++---- Libs/DF/fw.lua | 2 +- Libs/DF/mixins.lua | 5 +- frames/window_mythicplus/window_chart.lua | 505 +++++++----------- 5 files changed, 742 insertions(+), 434 deletions(-) diff --git a/Libs/DF/charts.examples.lua b/Libs/DF/charts.examples.lua index 589dcf09..87bf1e3b 100644 --- a/Libs/DF/charts.examples.lua +++ b/Libs/DF/charts.examples.lua @@ -2,53 +2,58 @@ --documentation: see the header of the file charts.lua ---1º example: making a simple chart, just copy and paste this code into a lua file and run it +--1º example: making a simple chart with two lines, just copy and paste this code into a lua file and run it do - local ChartFrameTest = ChartFrameExample1 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample1") + local ChartFrameTest = ChartFrameExample1 or DetailsFramework:CreateGraphicMultiLineFrame(UIParent, "ChartFrameExample1") ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0) --set the position of the chart ChartFrameTest:SetSize(800, 600) --set the size of the chart DetailsFramework:ApplyStandardBackdrop(ChartFrameTest) --apply a backdrop to this example hence see the frame size - --set the data (required) + --add a line: local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24, 18, 17, 14, 15, 8, 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4, 8, 7, 4, 12, 12, 4} - local smoothnessLevel = 1 --(optional, default: 1) - ChartFrameTest:SetData(data, smoothnessLevel) + local smoothingMethod = "sma" --(optional, default: "sma") + local smoothnessLevel = 3 --(optional, default: 1) + local name = "Line 1" --(optional, default: none) + local red, green, blue, alpha = 1, 1, 1, 1 --(optional, default: 1, 1, 1, 1) + + ChartFrameTest:AddData(data, smoothingMethod, smoothnessLevel, name, red, green, blue, alpha) + + --add another line: + data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + smoothingMethod = "loess" --using a different smoothing method + smoothnessLevel = 50 + name = "Line 2" + local color = "red" --using a string with the color name + ChartFrameTest:AddData(data, smoothingMethod, smoothnessLevel, name, color) + --draw the chart ChartFrameTest:Plot() end ---2º example: setting the color, thickness and scale of the line: +--2º example: thickness and scale of the line: do - local ChartFrameTest = ChartFrameExample2 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample2") + local ChartFrameTest = ChartFrameExample2 or DetailsFramework:CreateGraphicMultiLineFrame(UIParent, "ChartFrameExample2") ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0) --set the position of the chart ChartFrameTest:SetSize(800, 600) --set the size of the chart DetailsFramework:ApplyStandardBackdrop(ChartFrameTest) --apply a backdrop to this example hence see the frame size + --add the data (required) + local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4 ,8, 7 ,4, 12, 12 , 4} + ChartFrameTest:AddData(data) + --set the line thickness (optional, default: 2) local lineThickness = 3 ChartFrameTest:SetLineThickness(lineThickness) - --set the chart color (optional, default: "white") - local lineColor = {r = 1, g = 1, b = 0} --set it to "yellow" - ChartFrameTest:SetColor(lineColor) --using {r = 1, g = 1, b = 0} - ChartFrameTest:SetColor("yellow") --using the color name - ChartFrameTest:SetColor(1, 1, 0) --passing the rgb directly - ChartFrameTest:SetColor({1, 1, 0}) --using an index table - - --set the data (required) - local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4 ,8, 7 ,4, 12, 12 , 4} - local smoothnessLevel = 1 --(optional, default: 1) - ChartFrameTest:SetData(data, smoothnessLevel) - --height modifier, if for some reason need to scale the chart height - local heightScale = 1 --(optional, default: 1) + local heightScale = 1.2 --(optional, default: 1) --draw the chart ChartFrameTest:Plot(heightScale) end --3º example: setting the axes lines and labels do - local ChartFrameTest = ChartFrameExample3 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample3") + local ChartFrameTest = ChartFrameExample3 or DetailsFramework:CreateGraphicMultiLineFrame(UIParent, "ChartFrameExample3") ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0) ChartFrameTest:SetSize(800, 600) DetailsFramework:ApplyStandardBackdrop(ChartFrameTest) @@ -76,7 +81,7 @@ do --setting the data, doesn't matter if it is set at the top or right before Plot() local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4 ,8, 7 ,4, 12, 12 , 4} - ChartFrameTest:SetData(data) --smoothnessLevel is absent here, it'll use 1 as default + ChartFrameTest:AddData(data) ChartFrameTest:Plot() end diff --git a/Libs/DF/charts.lua b/Libs/DF/charts.lua index 6d6c7101..e98d2059 100644 --- a/Libs/DF/charts.lua +++ b/Libs/DF/charts.lua @@ -33,7 +33,16 @@ local _ ---| "number" same as timer, but the number is not comverted to time ---| "value" a fixed table with values is passed by the SetXAxisData() function +---@class df_chartline : line +---@field thickness number + ---@class df_chartshared: table +---@field fillOrder table the order of the lines to be filled, this table is shared by all charts +---@field bFillChart boolean if the chart lines should be filled or not +---@field fillChartLineThickness number the thickness of the fill line +---@field bRunningInBackground boolean true if there is a proccess happening asynchronously +---@field waitForBackgroundProcessTicker timer a ticker to check if all background process finished +---@field amountOfBackgroundProcess number the amount of background processes happening ---@field yAxisLine line the vertical line which can be anchored in the left or right side of the frame, if the chart is a multi chart, this line is shared by all charts ---@field xAxisLine line the horizontal line which can be anchored in the top or bottom side of the frame, if the chart is a multi chart, this line is shared by all charts ---@field xAxisDataNumber any if the data type of the x axis is "number" or "time" @@ -49,12 +58,17 @@ local _ ---@field smoothnessLevel number default: 0, the smoothness level of the chart lines, 0 is no smoothness ---@field backdropIndicators chart_backdropindicator[] ---@field nextBackdropIndicator number tell which is the next backdrop indicator to be used +---@field SetFillChart fun(self: df_chartshared, bFill: boolean, lineThickness:number?) set if the chart lines should be filled or not +---@field GetFillState fun(self: df_chartshared) : boolean, number return if the chart lines should be filled or not +---@field ShrinkData fun(self: df_chartmulti|df_chart, data: table, skrinkBy: number, bJustDrop: boolean?) : table +---@field HasBackgroundProcess fun(self: df_chartmulti|df_chart) : boolean return true if there is a proccess happening asynchronously +---@field SetBackgroundProcessState fun(self: df_chartmulti|df_chart, bRunning: boolean) set if there is a proccess happening asynchronously ---@field CreateBackdropIndicator fun(self: df_chartmulti|df_chart, index: number) : chart_backdropindicator create a new backdrop indicator ---@field GetBackdropIndicator fun(self: df_chartmulti|df_chart) : chart_backdropindicator get a backdrop indicator by index ---@field ResetBackdropIndicators fun(self: df_chartmulti|df_chart) reset all backdrop indicators ---@field SetAxesColor fun(self: df_chartmulti, red: number|string|table|nil, green: number|nil, blue: number|nil, alpha: number|nil) : boolean set the color of both axis lines ---@field SetAxesThickness fun(self: df_chartmulti, thickness: number) : boolean set the thickness of both axis lines ----@field CreateAxesLines fun(self: df_chartmulti|df_chart, xOffset: number, yOffset: number, whichSide: "left"|"right", thickness: number, amountYLabels: number, amountXLabels: number, red: any, green: number|nil, blue: number|nil, alpha: number|nil) +---@field CreateAxesLines fun(self: df_chartmulti|df_chart, xOffset: number, yOffset: number, whichSide: "left"|"right", thickness: number, amountYLabels: number, amountXLabels: number, red: any, green: number|nil, blue: number|nil, alpha: number|nil) create the x and y axis lines with their labels, offsets are the distance from left and bottom ---@field SetXAxisDataType fun(self: df_chartmulti|df_chart, dataType: x_axisdatatype) : boolean set the data type of the x axis, if time, the x axis will be a time axis, if value, the x axis will be a value axis ---@field SetXAxisData fun(self: df_chartmulti|df_chart, data: any) set the data of the x axis, if time, the x axis will be a time axis, if value, the x axis will be a value axis ---@field SharedContrustor fun(self: df_chartmulti|df_chart) set default values for fields used on both chart types @@ -78,13 +92,15 @@ end ---@class df_chart: frame, df_data, df_value, df_chartshared ---@field _dataInfo df_data +---@field average number +---@field depth number ---@field color number[] red green blue alpha ---@field height number ---@field nextLine number ---@field minValue number ---@field maxValue number ---@field data number[] ----@field lines line[] +---@field lines df_chartline[] ---@field fixedLineWidth number ---@field chartName string ---@field dataPoint_OnEnterFunc fun(self: df_chart, onEnterFunc: function, ...) set the function to be called when the mouse hover over a data point in the chart @@ -93,8 +109,8 @@ end ---@field dataPoint_OnLeavePayload any[] set the payload to be passed to the function set by DataPoint_OnLeaveFunc ---@field GetOnEnterLeaveFunctions fun(self: df_chart) : function, any[], function, any[] return the functions and payloads set by DataPoint_OnEnterFunc and DataPoint_OnLeaveFunc ---@field ChartFrameConstructor fun(self: df_chart) set the default values for the chart frame ----@field GetLine fun(self: df_chart) : line return a line and also internally handle next line ----@field GetLines fun(self: df_chart) : line[] return a table with all lines already created +---@field GetLine fun(self: df_chart) : df_chartline return a line and also internally handle next line +---@field GetLines fun(self: df_chart) : df_chartline[] return a table with all lines already created ---@field GetLineWidth fun(self: df_chart) : number calculate the width of each drawn line ---@field SetLineWidth fun(self: df_chart, width: number) set the line width to a fixed value ---@field GetAmountLines fun(self: df_chart) : number return the amount of lines in use @@ -106,7 +122,7 @@ end ---@field SetLineThickness fun(self: df_chart, thickness: number) set the line thickness ---@field CalcYAxisPointForValue fun(self: df_chart, value: number, plotFrameHeightScaled: number) : number ---@field UpdateFrameSizeCache fun(self: df_chart) ----@field Plot fun(self: df_chart, yPointScale: number|nil, bUpdateLabels: boolean|nil) draw the graphic using lines and following the data set by SetData() or AddData() in multi chart +---@field Plot fun(self: df_chart, yPointScale: number|nil, bUpdateLabels: boolean|nil, lineId:number?) draw the graphic using lines and following the data set by SetData() or AddData() in multi chart ---@class df_chartmulti : df_chart, df_chartshared ---@field chartFrames df_chart[] @@ -117,7 +133,7 @@ end ---@field MultiChartFrameConstructor fun(self: df_chartmulti) ---@field GetCharts fun(self: df_chartmulti) : df_chart[] ---@field GetChart fun(self: df_chartmulti) : df_chart ----@field AddData fun(self: df_chartmulti, data: table, name: string, red: any, green: number|nil, blue: number|nil, alpha: number|nil) +---@field AddData fun(self: df_chartmulti, data: table, smoothingMethod: string|nil, smoothnessLevel:number, name: string, red: any, green: number|nil, blue: number|nil, alpha: number|nil) ---@field GetAmountCharts fun(self: df_chartmulti): number ---@field HideCharts fun(self: df_chartmulti) ---@field Reset fun(self: df_chartmulti) @@ -190,7 +206,6 @@ local createHorizontalAxisLabels = function(parent, amountLabels, labelsTable, r end end ----create the x and y axis lines with their labels ---@param self df_chart|df_chartmulti ---@param xOffset number ---@param yOffset number @@ -342,8 +357,84 @@ local updateLabelValues = function(self) end detailsFramework.ChartFrameSharedMixin = { - ---set the color of both axis lines + ---set if the chart lines should be filled or not, when filled, an extra line is drawn at the bottom of the chart to close the fill + ---@param self df_chartshared + ---@param bFill boolean + ---@param lineThickness number? + SetFillChart = function(self, bFill, lineThickness) + lineThickness = lineThickness or 1 + self.bFillChart = bFill + self.fillChartLineThickness = lineThickness + end, + + ---return if the chart lines should be filled or not + ---@param self df_chartshared + ---@return boolean + ---@return number + GetFillState = function(self) + return self.bFillChart, self.fillChartLineThickness + end, + + ---receives a table containing the data to be plotted in the chart, returns a new table with the data reduced by the skrinkBy value + ---if bJustDrop is true, the data will be reduced by dropping values, if false, the data will be reduced by averaging the values ---@param self df_chart|df_chartmulti + ---@param data table + ---@param skrinkBy number + ---@param bJustDrop boolean + ---@return table + ShrinkData = function(self, data, skrinkBy, bJustDrop) + local newData = {} + local dataSize = #data + + local tinsert = table.insert + + if (bJustDrop) then + if (true) then + --make a for loop to drop the values by random, for example, is shrink is 3 and index is 9, it will drop at random two values of: 9 10 or 11 + else + --it will shrink the data by dropping values each skrinkBy indexes + for i = 1, dataSize, skrinkBy do + tinsert(newData, data[i]) + end + end + else + --it will shrink the data by making an average of the values and add to newTable, shrinkBy controls how many values will be averaged + for i = 1, dataSize, skrinkBy do + local sum = 0 + for o = 0, skrinkBy - 1 do + sum = sum + (data[i + o] or 0) --attempt to perform arithmetic on field '?' (a nil value) + end + tinsert(newData, sum / skrinkBy) + end + end + + return newData + end, + + ---return if there is a proccess happening asynchronously + ---@param self df_chartmulti + ---@return boolean + HasBackgroundProcess = function(self) + return self.bRunningInBackground + end, + + ---set if there is a proccess happening asynchronously + ---@param self df_chartmulti + ---@param bRunning boolean + SetBackgroundProcessState = function(self, bRunning) + if (bRunning) then + self.amountOfBackgroundProcess = self.amountOfBackgroundProcess + 1 + self.bRunningInBackground = bRunning + else + self.amountOfBackgroundProcess = self.amountOfBackgroundProcess - 1 + if (self.amountOfBackgroundProcess == 0) then + self.bRunningInBackground = false + end + end + end, + + ---set the color of both axis lines + ---@param self df_chartmulti ---@param red any ---@param green number|nil ---@param blue number|nil @@ -372,7 +463,7 @@ detailsFramework.ChartFrameSharedMixin = { end, ---set the thickness of both axis lines - ---@param self df_chart|df_chartmulti + ---@param self df_chartmulti ---@param thickness number ---@return boolean bThicknessChanged return true if the thickness was set, false if the axis lines are not created yet SetAxesThickness = function(self, thickness) @@ -385,7 +476,7 @@ detailsFramework.ChartFrameSharedMixin = { end, ---create the x and y axis lines with their labels - ---@param self df_chart|df_chartmulti + ---@param self df_chartmulti ---@param xOffset number ---@param yOffset number ---@param whichSide "left"|"right" @@ -401,20 +492,20 @@ detailsFramework.ChartFrameSharedMixin = { return createAxesLines(self, xOffset, yOffset, whichSide, thickness, amountYLabels, amountXLabels, red, green, blue, alpha) end, - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ---@param ... any SetXAxisData = function(self, ...) setXAxisData(self, ...) end, - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ---@param dataType x_axisdatatype SetXAxisDataType = function(self, dataType) setXAxisDataType(self, dataType) end, ---create a new backdrop indicator, this is called from the function GetBackdropIndicator - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ---@return chart_backdropindicator CreateBackdropIndicator = function(self, nextIndicatorIndex) ---@type chart_backdropindicator @@ -444,7 +535,7 @@ detailsFramework.ChartFrameSharedMixin = { end, ---reset the backdrop indicators by hidding all of them - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ResetBackdropIndicators = function(self) for i = 1, #self.backdropIndicators do local thisBackdropIndicator = self.backdropIndicators[i] @@ -455,7 +546,7 @@ detailsFramework.ChartFrameSharedMixin = { end, ---get a backdrop indicator, if it doesn't exist, create a new one - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ---@return chart_backdropindicator GetBackdropIndicator = function(self) local nextIndicator = self.nextBackdropIndicator @@ -469,7 +560,7 @@ detailsFramework.ChartFrameSharedMixin = { end, ---add a backdrop indicator to the chart - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ---@param label string this is a text to be displayed on the left side of the indicator and on the top right corner of the chart panel ---@param timeStart number the start time of the indicator ---@param timeEnd number the end time of the indicator @@ -497,7 +588,7 @@ detailsFramework.ChartFrameSharedMixin = { ---when Plot() is called, this function will be called to show the backdrop indicators ---it gets the x_axisdatatype or if not existant defaults to "time", calculate the area in pixels using the plot area width and the plot area 'time' ---then set the texture color, label texts and show the small squere indicators in the top right of the plot area - ---@param self df_chartmulti|df_chart + ---@param self df_chartmulti ShowBackdropIndicators = function(self) --get the x axis data type local xDataType = self.xAxisDataType or "time" @@ -546,6 +637,117 @@ detailsFramework.ChartFrameSharedMixin = { end, } +local fillerLines_InAvailable = {} +local fillerLines_InUse = {} + +---@class df_chartlazypayload : table +---@field self df_chartmulti +---@field currentDataIndex number +---@field executionsPerFrame number +---@field dataSize number +---@field currentXPoint number +---@field currentYPoint number +---@field eachLineWidth number +---@field plotFrameHeightScaled number +---@field r number +---@field g number +---@field b number +---@field lineId number +---@field bUpdateLabels boolean +---@field bFillChart boolean +---@field fillLineThickness number + +--this is the function which is called by the schedules lazy execution system +local lazyChartUpdate = function(payload, iterationCount, maxIterations) + ---@cast payload df_chartlazypayload + + local self = payload.self + ---@cast self df_chart + + local currentDataIndex = payload.currentDataIndex + local dataSize = payload.dataSize + local currentXPoint = payload.currentXPoint + local currentYPoint = payload.currentYPoint + local eachLineWidth = payload.eachLineWidth + local plotFrameHeightScaled = payload.plotFrameHeightScaled + local r = payload.r + local g = payload.g + local b = payload.b + local lineId = payload.lineId + local bUpdateLabels = payload.bUpdateLabels + local bFillChart = payload.bFillChart + local fillLineThickness = payload.fillLineThickness + + local executionsPerFrame = payload.executionsPerFrame + currentDataIndex = currentDataIndex + executionsPerFrame + + for i = 1, payload.executionsPerFrame do + local value, dataIndex = self:GetDataNextValue() + if (not value) then + --the data stream has ended + return true + end + + local line = self:GetLine() + line:SetColorTexture(r, g, b) + + if (line.thickness ~= self.lineThickness) then + line:SetThickness(self.lineThickness) + line.thickness = self.lineThickness + end + + --get the start points + local startX = currentXPoint + local startY = currentYPoint + currentXPoint = currentXPoint + eachLineWidth + --end point + local endX = currentXPoint + currentYPoint = self:CalcYAxisPointForValue(value, plotFrameHeightScaled) + local endY = currentYPoint + + local length = detailsFramework:GetVectorLength(endX - startX, endY - startY) + --make sure the magnitude of the difference between previous point to current point is at least 1.5 + if (length < 1.5) then + local diffX = endX - startX + local diffY = endY - startY + + local diffLength = detailsFramework:GetVectorLength(diffX, diffY) + local scaleFactor = 1.5 / diffLength + + diffX = diffX * scaleFactor + diffY = diffY * scaleFactor + + endX = endX + diffX + endY = endY + diffY + end + + --the start point starts where the latest point finished + line:SetStartPoint("bottomleft", startX, startY) + line:SetEndPoint("bottomleft", endX, endY) + + if (bFillChart) then + if (lineId) then + local fillLine = table.remove(fillerLines_InAvailable) + if (not fillLine) then + fillLine = self.plotFrame:CreateLine(nil, "overlay") + fillLine:SetThickness(fillLineThickness) + fillerLines_InUse[#fillerLines_InUse+1] = fillLine + else + fillerLines_InUse[#fillerLines_InUse+1] = fillLine + end + + fillLine:SetStartPoint("bottomleft", endX, endY) + fillLine:SetEndPoint("bottomleft", endX, 0) + fillLine:SetDrawLayer("overlay", self.depth) + fillLine:SetColorTexture(r, g, b, 0.15 + (self.depth/10)) + end + end + end + + payload.currentXPoint = currentXPoint + payload.currentYPoint = currentYPoint +end + detailsFramework.ChartFrameMixin = { ---set the default values for the chart frame ---@param self df_chart @@ -557,6 +759,8 @@ detailsFramework.ChartFrameMixin = { self.data = {} self.lines = {} self.color = {1, 1, 1, 1} + self.amountOfBackgroundProcess = 0 + --OnSizeChanged self:SetScript("OnSizeChanged", self.OnSizeChanged) @@ -593,14 +797,17 @@ detailsFramework.ChartFrameMixin = { ---internally handle next line ---@param self df_chart + ---@return df_chartline GetLine = function(self) - ---@type line + ---@type df_chartline local line = self.lines[self.nextLine] if (not line) then ---@type line - line = self.plotFrame:CreateLine(nil, "overlay", nil, 5) - self.lines[self.nextLine] = line + local newLine = self.plotFrame:CreateLine(nil, "overlay", nil, 5) + ---@cast newLine df_chartline + self.lines[self.nextLine] = newLine + line = newLine end self.nextLine = self.nextLine + 1 @@ -610,7 +817,7 @@ detailsFramework.ChartFrameMixin = { ---return all lines created for this chart ---@param self df_chart - ---@return line[] + ---@return df_chartline[] GetLines = function(self) return self.lines end, @@ -717,14 +924,12 @@ detailsFramework.ChartFrameMixin = { ---@param self df_chart ---@param yPointScale number|nil ---@param bUpdateLabels boolean|nil - Plot = function(self, yPointScale, bUpdateLabels) - --debug - --self:SetData({38, 26, 12, 63, 100, 96, 42, 94, 25, 75, 61, 54, 71, 40, 34, 100, 66, 90, 39, 13, 99, 18, 72, 18, 83, 45, 56, 24, 33, 85, 95, 71, 15, 66, 19, 58, 52, 9, 83, 99, 100, 4, 3, 56, 6, 80, 94, 7, 40, 55, 98, 92, 20, 9, 35, 89, 72, 7, 13, 81, 29, 78, 55, 70, 12, 33, 39, 3, 84, 31, 10, 53, 51, 69, 66, 58, 71, 60, 31, 71, 27, 76, 21, 75, 15, 89, 2, 81, 72, 78, 74, 80, 97, 10, 59, 0, 31, 5, 1, 82, 71, 89, 78, 94, 74, 20, 65, 72, 56, 40, 92, 91, 40, 79, 4, 56, 18, 88, 88, 20, 20, 10, 47, 26, 80, 26, 75, 21, 57, 10, 67, 66, 84, 83, 14, 47, 83, 9, 7, 73, 63, 32, 64, 20, 40, 3, 46, 54, 17, 37, 82, 66, 65, 22, 12, 1, 100, 41, 1, 72, 38, 41, 71, 69, 88, 34, 10, 50, 9, 25, 19, 27, 3, 13, 40, 75, 3, 11, 93, 58, 81, 80, 93, 25, 74, 68, 91, 87, 79, 48, 66, 53, 64, 18, 51, 19, 32, 4, 21, 43}) - + Plot = function(self, yPointScale, bUpdateLabels, lineId) + lineId = lineId or 1 self:UpdateFrameSizeCache() --max amount of data is the max amount of point the chart will have - local maxLines = self:GetDataSize() + local dataSize = self:GetDataSize() --calculate where the first point height will be local firstValue = self:GetDataFirstValue() @@ -739,29 +944,29 @@ detailsFramework.ChartFrameMixin = { self:ResetDataIndex() - print(maxLines) + local r, g, b = unpack(self.color) - for i = 1, maxLines do - local line = self:GetLine() + local bFillChart, fillLineThickness = self:GetFillState() - line:SetTexture(unpack(self.color)) + local payload = { + executionsPerFrame = 50, + self = self, + currentDataIndex = 1, + dataSize = dataSize, + currentXPoint = currentXPoint, + currentYPoint = currentYPoint, + eachLineWidth = eachLineWidth, + plotFrameHeightScaled = plotFrameHeightScaled, + r = r, + g = g, + b = b, + lineId = lineId, + bUpdateLabels = bUpdateLabels, + bFillChart = bFillChart, + fillLineThickness = fillLineThickness, + } - if (line.thickness ~= self.lineThickness) then - line:SetThickness(self.lineThickness) - line.thickness = self.lineThickness - end - - --the start point starts where the latest point finished - line:SetStartPoint("bottomleft", currentXPoint, currentYPoint) - - --move x - currentXPoint = currentXPoint + eachLineWidth - - --end point - local value = self:GetDataNextValue() - currentYPoint = self:CalcYAxisPointForValue(value, plotFrameHeightScaled) - line:SetEndPoint("bottomleft", currentXPoint, currentYPoint) - end + detailsFramework.Schedules.LazyExecute(lazyChartUpdate, payload) self:ShowBackdropIndicators() @@ -771,6 +976,198 @@ detailsFramework.ChartFrameMixin = { end, } +--https://en.wikipedia.org/wiki/Local_regression +local calcLOESS = function(data, span, mainFrame, chartFrame) + local lazyLOESSUpdate = function(payload, iterationCount, maxIterations) + local data = payload.data + local span = payload.span + local lastDataIndex = payload.lastDataIndex + local result = payload.result + local halfSpan = payload.halfSpan + local sumTotal = payload.sumTotal + + local currentDataIndex = payload.currentDataIndex + payload.currentDataIndex = currentDataIndex + payload.executionsPerFrame + + local max = math.max + local min = math.min + local abs = math.abs + local tinsert = table.insert + + for i = currentDataIndex, currentDataIndex + payload.executionsPerFrame do + --define the local neighborhood + local neighborhood = {} + for o = max(1, i - halfSpan), min(lastDataIndex, i + halfSpan) do + tinsert(neighborhood, {x = o, y = data[o]}) + end + + sumTotal = sumTotal + data[i] + + --calculate weights based on distance from target point + local weights = {} + for _, point in ipairs(neighborhood) do + local distance = abs(i - point.x) + local weight = (1 - (distance / (halfSpan + 1)) ^ 3) ^ 3 + weights[point.x] = weight + end + + --fit a weighted linear regression to the neighborhood + local sum_w = 0 + local sum_wx = 0 + local sum_wy = 0 + local sum_wxx = 0 + local sum_wxy = 0 + + for _, point in ipairs(neighborhood) do + local w = weights[point.x] + sum_w = sum_w + w + sum_wx = sum_wx + w * point.x + sum_wy = sum_wy + w * point.y + sum_wxx = sum_wxx + w * point.x * point.x + sum_wxy = sum_wxy + w * point.x * point.y + end + + local denominator = sum_w * sum_wxx - sum_wx * sum_wx + local intercept = (sum_wy * sum_wxx - sum_wx * sum_wxy) / denominator + local slope = (sum_w * sum_wxy - sum_wx * sum_wy) / denominator + + --predict the smoothed value at the target point + result[i] = max(0, intercept + slope * i) + + --check if can finishe the execution + if (i == lastDataIndex) then + return true + end + end + + payload.sumTotal = sumTotal + end + + local result = {} + local dataSize = #data + local halfSpan = math.floor(span / 2) + + local payload = { + currentDataIndex = 1, + sumTotal = 0, + lastDataIndex = dataSize, + executionsPerFrame = 100, + data = data, + span = span, + result = result, + halfSpan = halfSpan, + } + + ---@type df_schedule + local schedules = detailsFramework.Schedules + + local onEndLazyExecution = function(payload) + chartFrame:SetDataRaw(payload.result) + + chartFrame.average = payload.sumTotal / dataSize + + local minValue, maxValue = chartFrame:GetDataMinMaxValues() + chartFrame:SetMinMaxValues(minValue, maxValue) + --clear the lines + chartFrame:HideLines() + mainFrame:SetBackgroundProcessState(false) + end + + mainFrame:SetBackgroundProcessState(true) + schedules.LazyExecute(lazyLOESSUpdate, payload, 999, onEndLazyExecution) +end + +--simple moving average +---@param data table +---@param averageSize number +---@param mainFrame df_chartmulti +---@param chartFrame df_chart +---@param bAddZeroPadding boolean? +local calcSMA = function(data, averageSize, mainFrame, chartFrame, bAddZeroPadding) + if (bAddZeroPadding) then + --fill the start of the data with zeros + for i = 1, averageSize - 1 do + --insert at index 1 a zero + table.insert(data, 1, 0) + end + end + + local lazySMAUpdate = function(payload, iterationCount, maxIterations) + local averageSize = payload.averageSize + local result = payload.result + local data = payload.data + local lastDataIndex = payload.lastDataIndex + local sum = payload.sum + local sumTotal = payload.sumTotal + local bAddZeroPadding = payload.bAddZeroPadding + + local currentDataIndex = payload.currentDataIndex + payload.currentDataIndex = currentDataIndex + payload.executionsPerFrame + + local tinsert = table.insert + + for i = currentDataIndex, currentDataIndex + payload.executionsPerFrame do + sum = sum + data[i] + sumTotal = sumTotal + data[i] + if (i >= averageSize) then + if (i > averageSize) then + sum = sum - data[i - averageSize] + end + tinsert(result, max(0, sum / averageSize)) + end + + --check if can finishe the execution + if (i == lastDataIndex) then + if (bAddZeroPadding) then + --remove from the data the zeros added at the start + for o = 1, averageSize - 1 do + --remove from the data the zero added at the first index + table.remove(data, 1) + end + end + return true + end + end + + payload.sumTotal = sumTotal + payload.sum = sum + end + + --return result + local result = {} + local dataSize = #data + + local payload = { + sum = 0, + sumTotal = 0, + currentDataIndex = 1, + lastDataIndex = dataSize, + executionsPerFrame = 300, + data = data, + result = result, + averageSize = averageSize, + bAddZeroPadding = bAddZeroPadding, + } + + ---@type df_schedule + local schedules = detailsFramework.Schedules + + local onEndLazyExecution = function(payload) + chartFrame:SetDataRaw(payload.result) + + chartFrame.average = payload.sumTotal / dataSize + + local minValue, maxValue = chartFrame:GetDataMinMaxValues() + chartFrame:SetMinMaxValues(minValue, maxValue) + --clear the lines + chartFrame:HideLines() + mainFrame:SetBackgroundProcessState(false) + end + + mainFrame:SetBackgroundProcessState(true) + schedules.LazyExecute(lazySMAUpdate, payload, 999, onEndLazyExecution) +end + ---create a chart frame object ---@param parent frame ---@param name string|nil @@ -788,41 +1185,31 @@ local createChartFrame = function(parent, name) chartFrame:ValueConstructor() chartFrame:ChartFrameConstructor() - --when a new data is set, update the min and max values - local onSetDataCallback = function(data, smoothnessLevel) - local newData = {} + --when a new data is set, starting an background process to smooth the data + local onSetDataCallback = function(data, payload) + local smoothnessMethod = payload.smoothnessMethod or "" + local smoothnessLevel = payload.smoothnessLevel + local mainFrame = payload.mainFrame smoothnessLevel = smoothnessLevel or 0 + smoothnessMethod = string.lower(smoothnessMethod) - if (smoothnessLevel > 0) then - smoothnessLevel = smoothnessLevel + 2 + if (smoothnessMethod == "loess") then + calcLOESS(data, smoothnessLevel, mainFrame, chartFrame) - for i = 1, #data do - local thisValue = 0 - local amountDataAdded = 0 + elseif (smoothnessMethod == "sma") then + calcSMA(data, smoothnessLevel, mainFrame, chartFrame) - --calculate the sum within the window - for o = i - math.floor(smoothnessLevel / 2), i + math.floor(smoothnessLevel / 2) do - if o >= 1 and o <= #data then - thisValue = thisValue + data[o] - amountDataAdded = amountDataAdded + 1 - end - end - - --calculate the average and store in the smoothedData value - local average = thisValue / amountDataAdded - table.insert(newData, average) - end + elseif (smoothnessMethod == "smaz") then + local bAddZeroPadding = true + calcSMA(data, smoothnessLevel, mainFrame, chartFrame, bAddZeroPadding) else - newData = data + chartFrame:SetDataRaw(data) + local minValue, maxValue = chartFrame:GetDataMinMaxValues() + chartFrame:SetMinMaxValues(minValue, maxValue) + --clear the lines + chartFrame:HideLines() end - - chartFrame:SetDataRaw(newData) - - local minValue, maxValue = chartFrame:GetDataMinMaxValues() - chartFrame:SetMinMaxValues(minValue, maxValue) - --clear the lines - chartFrame:HideLines() end chartFrame:AddDataChangeCallback(onSetDataCallback) @@ -831,13 +1218,6 @@ local createChartFrame = function(parent, name) end - -function detailsFramework:CreateGraphicLineFrame(parent, name) - ---@type df_chart - local newGraphicFrame = createChartFrame(parent, name) - return newGraphicFrame -end - detailsFramework.MultiChartFrameMixin = { MultiChartFrameConstructor = function(self) self.nextChartselframe = 1 @@ -846,6 +1226,7 @@ detailsFramework.MultiChartFrameMixin = { self.nextChartFrame = 1 self.chartFrames = {} self.lineNameIndicators = {} + self.amountOfBackgroundProcess = 0 chartFrameSharedConstructor(self) end, @@ -857,27 +1238,29 @@ detailsFramework.MultiChartFrameMixin = { ---add a new chart data and create a new chart frame if necessary to the multi chart ---@param self df_chartmulti ---@param data table + ---@param smoothingMethod string|nil ---@param smoothnessLevel number|nil ---@param name string|nil ---@param red any ---@param green number|nil ---@param blue number|nil ---@param alpha number|nil - AddData = function(self, data, smoothnessLevel, name, red, green, blue, alpha) + AddData = function(self, data, smoothingMethod, smoothnessLevel, name, red, green, blue, alpha) assert(type(data) == "table", "MultiChartFrame:AddData() usage: AddData(table)") local chartFrame = self:GetChart() red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha) chartFrame:SetColor(red, green, blue, alpha) - chartFrame:SetData(data, smoothnessLevel) - chartFrame.chartName = name or "" - self:SetMaxValueIfBigger(chartFrame:GetMaxValue()) - self:SetMinValueIfLower(chartFrame:GetMinValue()) + local payload = { + smoothnessMethod = smoothingMethod or "sma", + smoothnessLevel = smoothnessLevel or 3, + mainFrame = self, + } - local dataAmount = chartFrame:GetDataSize() - self:SetMaxDataSize(dataAmount) + --setting the data will start a background process to smooth the data + chartFrame:SetData(data, payload) end, ---internally handle next line @@ -1023,14 +1406,58 @@ detailsFramework.MultiChartFrameMixin = { end end, + ---@param self df_chartmulti + WaitForBackgroundProcess = function(self) + --start a ticker to check if the background process is done + if (not self.waitForBackgroundProcessTicker) then + self.waitForBackgroundProcessTicker = C_Timer.NewTicker(0.1, function() + if (not self:HasBackgroundProcess()) then + self.waitForBackgroundProcessTicker:Cancel() + self.waitForBackgroundProcessTicker = nil + self:Plot() + end + end) + end + end, + ---draw all the charts added to the multi chart frame ---@param multiChartFrame df_chartmulti Plot = function(multiChartFrame) + --check if there is a background process ongoing + if (multiChartFrame:HasBackgroundProcess()) then + multiChartFrame:WaitForBackgroundProcess() + return + end + + local allCharts = multiChartFrame:GetCharts() + local bFillChart, fillLineThickness = multiChartFrame:GetFillState() + ---@type table + local biggestAverage = {} + + --set the min/max values of the multi chart frame + for i = 1, multiChartFrame:GetAmountCharts() do + local chartFrame = allCharts[i] + multiChartFrame:SetMaxValueIfBigger(chartFrame:GetMaxValue()) + multiChartFrame:SetMinValueIfLower(chartFrame:GetMinValue()) + + local dataAmount = chartFrame:GetDataSize() + multiChartFrame:SetMaxDataSize(dataAmount) + + if (bFillChart) then + chartFrame:SetFillChart(true, fillLineThickness) + end + + --get the average of this chart + biggestAverage[i] = {average = chartFrame.average, chartIndex = i} + end + + --sort the averages by the biggest average placing the biggest average in the first position + table.sort(biggestAverage, function(a, b) return a.average > b.average end) + local minValue, multiChartMaxValue = multiChartFrame:GetMinMaxValues() local plotAreaWidth = multiChartFrame.plotFrame:GetWidth() --if there's no axis, the plotFrame has no width local maxDataSize = multiChartFrame:GetMaxDataSize() --it's not clearing when a new boss is selected local eachLineWidth = plotAreaWidth / maxDataSize - local allCharts = multiChartFrame:GetCharts() for i = 1, multiChartFrame:GetAmountCharts() do local chartFrame = allCharts[i] @@ -1043,11 +1470,19 @@ detailsFramework.MultiChartFrameMixin = { chartFrame:SetLineThickness(multiChartFrame.lineThickness) chartFrame:SetLineWidth(eachLineWidth) + for o = 1, #biggestAverage do + local thisAverageTable = biggestAverage[o] + if (thisAverageTable.chartIndex == i) then + chartFrame.depth = o + break + end + end + --get the percentage of how small this data is compared to the biggest data --this percentage is then used to scale down the to fit correctly the fontStrings showing the value metrics local yPointScale = chartFrame.maxValue / multiChartMaxValue local bUpdateLabels = false - chartFrame:Plot(yPointScale, bUpdateLabels) + chartFrame:Plot(yPointScale, bUpdateLabels, i) end multiChartFrame:ShowBackdropIndicators() diff --git a/Libs/DF/fw.lua b/Libs/DF/fw.lua index c7326028..04668637 100644 --- a/Libs/DF/fw.lua +++ b/Libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 556 +local dversion = 557 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) diff --git a/Libs/DF/mixins.lua b/Libs/DF/mixins.lua index 468b3ac9..0a93fc16 100644 --- a/Libs/DF/mixins.lua +++ b/Libs/DF/mixins.lua @@ -474,7 +474,7 @@ detailsFramework.SortFunctions = { ---@field GetDataMinMaxValueFromSubTable fun(self: df_data, key: string) : number, number when data uses sub tables, get the min max values from a specific index or key, if the value stored is number, return the min and max values ---@field SetData fun(self: df_data, data: table, anyValue: any) ---@field SetDataRaw fun(self: df_data, data: table) set the data without triggering callback ----@field GetDataNextValue fun(self: df_data) : any +---@field GetDataNextValue fun(self: df_data) : any, number get the next value from the data table, return the value and the index ---@field ResetDataIndex fun(self: df_data) ---mixin to use with DetailsFramework:Mixin(table, detailsFramework.DataMixin) @@ -543,11 +543,12 @@ detailsFramework.DataMixin = { ---get the next value from the data table ---@param self table ---@return any + ---@return number GetDataNextValue = function(self) local currentValue = self._dataInfo.dataCurrentIndex local value = self:GetData()[currentValue] self._dataInfo.dataCurrentIndex = self._dataInfo.dataCurrentIndex + 1 - return value + return value, currentValue end, ---reset the data index, making GetDataNextValue() return the first value again diff --git a/frames/window_mythicplus/window_chart.lua b/frames/window_mythicplus/window_chart.lua index 067304e5..26f481bb 100644 --- a/frames/window_mythicplus/window_chart.lua +++ b/frames/window_mythicplus/window_chart.lua @@ -25,17 +25,20 @@ function mythicDungeonCharts.ShowChart() mythicDungeonCharts.Frame = CreateFrame("frame", "DetailsMythicDungeonChartFrame", UIParent, "BackdropTemplate") local dungeonChartFrame = mythicDungeonCharts.Frame - dungeonChartFrame:SetSize(1200, 620) - dungeonChartFrame:SetPoint("center", UIParent, "center", 0, 0) + --get the screen width + local screenWidth = GetScreenWidth() + + dungeonChartFrame:SetSize(screenWidth - 200, 400) + dungeonChartFrame:SetPoint("center", UIParent, "center", 0, 200) dungeonChartFrame:SetFrameStrata("DIALOG") dungeonChartFrame:EnableMouse(true) dungeonChartFrame:SetMovable(true) detailsFramework:ApplyStandardBackdrop(dungeonChartFrame) + dungeonChartFrame.__background:SetAlpha(0.834) --minimized frame mythicDungeonCharts.FrameMinimized = CreateFrame("frame", "DetailsMythicDungeonChartFrameminimized", UIParent, "BackdropTemplate") local fMinimized = mythicDungeonCharts.FrameMinimized - fMinimized:SetSize(160, 24) fMinimized:SetPoint("center", UIParent, "center", 0, 0) fMinimized:SetFrameStrata("LOW") @@ -90,44 +93,17 @@ function mythicDungeonCharts.ShowChart() LibWindow.MakeDraggable(fMinimized) LibWindow.SavePosition(fMinimized) - dungeonChartFrame.ChartFrame = detailsFramework:CreateChartPanel(dungeonChartFrame, 1200, 600, "DetailsMythicDungeonChartGraphicFrame") - dungeonChartFrame.ChartFrame:SetPoint("topleft", dungeonChartFrame, "topleft", 5, -20) - - dungeonChartFrame.ChartFrame.FrameInUse = {} - dungeonChartFrame.ChartFrame.FrameFree = {} - dungeonChartFrame.ChartFrame.TextureID = 1 - - dungeonChartFrame.ChartFrame.ShowHeader = true - dungeonChartFrame.ChartFrame.HeaderOnlyIndicator = true - dungeonChartFrame.ChartFrame.HeaderShowOverlays = false - - dungeonChartFrame.ChartFrame.Graphic.DrawLine = mythicDungeonCharts.CustomDrawLine - - dungeonChartFrame.ChartFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}) - dungeonChartFrame.ChartFrame:SetBackdropColor(0, 0, 0, 0.0) - dungeonChartFrame.ChartFrame:SetBackdropBorderColor(0, 0, 0, 0) - - dungeonChartFrame.ChartFrame:EnableMouse(false) - - dungeonChartFrame.ChartFrame.CloseButton:Hide() - - dungeonChartFrame.BossWidgetsFrame = CreateFrame("frame", "$parentBossFrames", dungeonChartFrame, "BackdropTemplate") - dungeonChartFrame.BossWidgetsFrame:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+10) - dungeonChartFrame.BossWidgetsFrame.Widgets = {} - - dungeonChartFrame.BossWidgetsFrame.GraphPin = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "overlay") - dungeonChartFrame.BossWidgetsFrame.GraphPin:SetTexture([[Interface\BUTTONS\UI-RadioButton]]) - dungeonChartFrame.BossWidgetsFrame.GraphPin:SetTexCoord(17/64, 32/64, 0, 1) - dungeonChartFrame.BossWidgetsFrame.GraphPin:SetSize(16, 16) - - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "artwork") - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetTexture([[Interface\Calendar\EventNotificationGlow]]) - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetTexCoord(0, 1, 0, 1) - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetSize(14, 14) - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetBlendMode("ADD") - dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetPoint("center", dungeonChartFrame.BossWidgetsFrame.GraphPin, "center", 0, 0) - + local chartFrame = detailsFramework:CreateGraphicMultiLineFrame(dungeonChartFrame, "DetailsMythicDungeonChartGraphicFrame") + chartFrame:SetPoint("topleft", dungeonChartFrame, "topleft", 1, -20) + chartFrame:SetSize(dungeonChartFrame:GetWidth(), dungeonChartFrame:GetHeight() - 20) + chartFrame:EnableMouse(false) dungeonChartFrame:Hide() + dungeonChartFrame.ChartFrame = chartFrame + + local red, green, blue, opacity = 1, 1, 1, 1 + chartFrame:CreateAxesLines(48, 20, "left", 1, 10, 10, red, green, blue, opacity) + chartFrame:SetXAxisDataType("time") + chartFrame:SetLineThickness(2) function dungeonChartFrame.ShowChartFrame() if (dungeonChartFrame.IsMinimized) then @@ -139,150 +115,18 @@ function mythicDungeonCharts.ShowChart() end end - local closeButton = CreateFrame("button", "$parentCloseButton", dungeonChartFrame, "UIPanelCloseButton") - closeButton:GetNormalTexture():SetDesaturated(true) - closeButton:SetWidth(24) - closeButton:SetHeight(24) - closeButton:SetPoint("topright", dungeonChartFrame, "topright", 0, -1) - closeButton:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+16) + mythicDungeonCharts.CreateCloseMinimizeButtons(dungeonChartFrame) + mythicDungeonCharts.CreateBossWidgets(dungeonChartFrame) + end --finished created the chart frame - local minimizeButton = CreateFrame("button", "$parentCloseButton", dungeonChartFrame, "UIPanelCloseButton") - minimizeButton:GetNormalTexture():SetDesaturated(true) - minimizeButton:SetWidth(24) - minimizeButton:SetHeight(24) - minimizeButton:SetPoint("right", closeButton, "left", 2, 0) - minimizeButton:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+16) - minimizeButton:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) - minimizeButton:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) - minimizeButton:SetHighlightTexture([[Interface\BUTTONS\UI-Panel-MinimizeButton-Highlight]]) + local dungeonChartFrame = mythicDungeonCharts.Frame - local closeButtonWhenMinimized = CreateFrame("button", "$parentCloseButton", fMinimized, "UIPanelCloseButton") - closeButtonWhenMinimized:GetNormalTexture():SetDesaturated(true) - closeButtonWhenMinimized:SetWidth(24) - closeButtonWhenMinimized:SetHeight(24) - closeButtonWhenMinimized:SetPoint("topright", fMinimized, "topright", 0, -1) - closeButtonWhenMinimized:SetFrameLevel(fMinimized:GetFrameLevel()+16) + ---@type df_chartmulti + local chartFrame = dungeonChartFrame.ChartFrame - local minimizeButtonWhenMinimized = CreateFrame("button", "$parentCloseButton", fMinimized, "UIPanelCloseButton") - minimizeButtonWhenMinimized:GetNormalTexture():SetDesaturated(true) - minimizeButtonWhenMinimized:SetWidth(24) - minimizeButtonWhenMinimized:SetHeight(24) - minimizeButtonWhenMinimized:SetPoint("right", closeButtonWhenMinimized, "left", 2, 0) - minimizeButtonWhenMinimized:SetFrameLevel(fMinimized:GetFrameLevel()+16) - minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) - minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) - minimizeButtonWhenMinimized:SetHighlightTexture([[Interface\BUTTONS\UI-Panel-MinimizeButton-Highlight]]) - - closeButtonWhenMinimized:SetScript("OnClick", function() - dungeonChartFrame.IsMinimized = false - fMinimized:Hide() - minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) - minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) - end) - - --replace the default click function - local minimize_func = function(self) - if (dungeonChartFrame.IsMinimized) then - dungeonChartFrame.IsMinimized = false - fMinimized:Hide() - dungeonChartFrame:Show() - minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) - minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) - else - dungeonChartFrame.IsMinimized = true - dungeonChartFrame:Hide() - fMinimized:Show() - minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-CollapseButton-Up]]) - minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-CollapseButton-Up]]) - end - end - - minimizeButton:SetScript("OnClick", minimize_func) - minimizeButtonWhenMinimized:SetScript("OnClick", minimize_func) - - --enabled box - -- /run _G.DetailsMythicDungeonChartHandler.ShowChart(); DetailsMythicDungeonChartFrame.ShowChartFrame() - local on_switch_enable = function(_, _, state) - Details.mythic_plus.show_damage_graphic = state - end - local enabledSwitch, enabledLabel = detailsFramework:CreateSwitch(dungeonChartFrame, on_switch_enable, Details.mythic_plus.show_damage_graphic, _, _, _, _, _, _, _, _, _, "Enabled", detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_BRIGHT_TEMPLATE"), "GameFontHighlightLeft") - enabledSwitch:SetAsCheckBox() - enabledSwitch.tooltip = "Show this chart at the end of a mythic dungeon run.\n\nIf disabled, you can reactivate it again at the options panel > streamer settings." - - if (enabledLabel) then - enabledLabel:SetPoint("right", minimizeButton, "left", -22, 0) - enabledSwitch:SetSize(16, 16) - detailsFramework:SetFontColor(enabledLabel, "gray") - end - - enabledSwitch.checked_texture:SetVertexColor(.75, .75, .75) - - local leftDivisorLine = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "overlay") - leftDivisorLine:SetSize(2, dungeonChartFrame.ChartFrame.Graphic:GetHeight()) - leftDivisorLine:SetTexture(1, 1, 1, 1) - leftDivisorLine:SetPoint("bottomleft", dungeonChartFrame.ChartFrame.Graphic.TextFrame, "bottomleft", -2, 0) - - local bottomDivisorLine = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "overlay") - bottomDivisorLine:SetSize(dungeonChartFrame.ChartFrame.Graphic:GetWidth(), 2) - bottomDivisorLine:SetTexture(1, 1, 1, 1) - bottomDivisorLine:SetPoint("bottomleft", dungeonChartFrame.ChartFrame.Graphic.TextFrame, "bottomleft", 0, 0) - - dungeonChartFrame.ChartFrame.Graphic:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}) - dungeonChartFrame.ChartFrame.Graphic:SetBackdropColor(.5, .50, .50, 0.8) - dungeonChartFrame.ChartFrame.Graphic:SetBackdropBorderColor(0, 0, 0, 0.5) - - function dungeonChartFrame.ChartFrame.RefreshBossTimeline(self, bossTable, elapsedTime) - for i, bossTable in ipairs(mythicDungeonCharts.ChartTable.BossDefeated) do - local bossWidget = dungeonChartFrame.BossWidgetsFrame.Widgets [i] - - if (not bossWidget) then - local newBossWidget = CreateFrame("frame", "$parentBossWidget" .. i, dungeonChartFrame.BossWidgetsFrame, "BackdropTemplate") - newBossWidget:SetSize(64, 32) - newBossWidget:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}) - newBossWidget:SetBackdropColor(0, 0, 0, 0.1) - newBossWidget:SetBackdropBorderColor(0, 0, 0, 0) - - local bossAvatar = detailsFramework:CreateImage(newBossWidget, "", 64, 32, "border") - bossAvatar:SetPoint("bottomleft", newBossWidget, "bottomleft", 0, 0) - newBossWidget.AvatarTexture = bossAvatar - - local verticalLine = Details:GetFramework():CreateImage(newBossWidget, "", 1, dungeonChartFrame.ChartFrame.Graphic:GetHeight(), "overlay") - verticalLine:SetTexture(1, 1, 1, 0.3) - verticalLine:SetPoint("bottomleft", newBossWidget, "bottomright", 0, 0) - - local timeText = detailsFramework:CreateLabel(newBossWidget) - timeText:SetPoint("bottomright", newBossWidget, "bottomright", 0, 0) - newBossWidget.TimeText = timeText - - local timeBackground = Details:GetFramework():CreateImage(newBossWidget, "", 30, 12, "artwork") - timeBackground:SetTexture(0, 0, 0, 0.5) - timeBackground:SetPoint("topleft", timeText, "topleft", -2, 2) - timeBackground:SetPoint("bottomright", timeText, "bottomright", 2, 0) - - dungeonChartFrame.BossWidgetsFrame.Widgets [i] = newBossWidget - bossWidget = newBossWidget - end - - local chartLength = dungeonChartFrame.ChartFrame.Graphic:GetWidth() - local secondsPerPixel = chartLength / elapsedTime - local xPosition = bossTable[1] * secondsPerPixel - - bossWidget:SetPoint("bottomright", dungeonChartFrame.ChartFrame.Graphic, "bottomleft", xPosition, 0) - - bossWidget.TimeText:SetText(detailsFramework:IntegerToTimer(bossTable[1])) - - if (bossTable[2].bossimage) then - bossWidget.AvatarTexture:SetTexture(bossTable[2].bossimage) - else - local bossAvatar = Details:GetBossPortrait(nil, nil, bossTable[2].name, bossTable[2].ej_instance_id) - bossWidget.AvatarTexture:SetTexture(bossAvatar) - end - end - end - end - - mythicDungeonCharts.Frame.ChartFrame:Reset() + chartFrame:Reset() + --check if there is a valid chart table if (not mythicDungeonCharts.ChartTable) then if (Details222.Debug.MythicPlusChartWindowDebug) then --development @@ -295,12 +139,12 @@ function mythicDungeonCharts.ShowChart() else mythicDungeonCharts:Debug("no valid data and no saved data, canceling") - mythicDungeonCharts.Frame:Hide() + dungeonChartFrame:Hide() return end else - mythicDungeonCharts.Frame:Hide() + dungeonChartFrame:Hide() mythicDungeonCharts:Debug("no data found, canceling") if (verbosemode) then @@ -315,9 +159,9 @@ function mythicDungeonCharts.ShowChart() mythicDungeonCharts.PlayerGraphIndex = {} + --add the lines to the chart (one line per player) for playerName, playerTable in pairs(charts) do local chartData = playerTable.ChartData - local lineName = playerTable.Name classDuplicated[playerTable.Class] = (classDuplicated[playerTable.Class] or 0) + 1 @@ -334,46 +178,51 @@ function mythicDungeonCharts.ShowChart() end local combatTime = mythicDungeonCharts.ChartTable.ElapsedTime - local texture = "line" - --lowess smooth - --chartData = mythicDungeonCharts.LowessSmoothing (chartData, 75) - chartData = mythicDungeonCharts.Frame.ChartFrame:CalcLowessSmoothing(chartData, 75) + local opacity = 1 + local smoothnessLevel = 50 + local smoothnessLevel = 20 + local smoothMethod = "loess" + local smoothMethod = "sma" - local maxValue = 0 - for i = 1, #chartData do - if (chartData [i] > maxValue) then - maxValue = chartData[i] - end + local chartSize = #chartData + + local shrinkBy = 1 + if (chartSize >= 600) then + shrinkBy = math.max(2, math.floor(chartSize/400)) end - chartData.max_value = maxValue - mythicDungeonCharts.Frame.ChartFrame:AddLine(chartData, lineColor, lineName, combatTime, texture, "SMA") + local reducedData = chartFrame:ShrinkData(chartData, shrinkBy) + + chartFrame:SetFillChart(true, 5) + chartFrame:AddData(reducedData, smoothMethod, smoothnessLevel, playerName, lineColor[1], lineColor[2], lineColor[3], opacity) + chartFrame:SetXAxisData(combatTime) table.insert(mythicDungeonCharts.PlayerGraphIndex, playerName) end - mythicDungeonCharts.Frame.ChartFrame:RefreshBossTimeline(mythicDungeonCharts.ChartTable.BossDefeated, mythicDungeonCharts.ChartTable.ElapsedTime) + mythicDungeonCharts.RefreshBossTimeline(dungeonChartFrame, mythicDungeonCharts.ChartTable.ElapsedTime) --generate boss time table local bossTimeTable = {} for i, bossTable in ipairs(mythicDungeonCharts.ChartTable.BossDefeated) do local combatTime = bossTable [3] or math.random(10, 30) - table.insert(bossTimeTable, bossTable[1]) table.insert(bossTimeTable, bossTable[1] - combatTime) end - mythicDungeonCharts.Frame.ChartFrame:AddOverlay(bossTimeTable, {1, 1, 1, 0.05}, "Show Boss", "") + --chartFrame:AddOverlay(bossTimeTable, {1, 1, 1, 0.05}, "Show Boss", "") --local phrase = " Average Dps (under development)\npress Escape to hide, Details! Alpha Build." .. _detalhes.build_counter .. "." .. _detalhes.realversion local phrase = "Details!: Average Dps for " - mythicDungeonCharts.Frame.ChartFrame:SetTitle("") - detailsFramework:SetFontSize(mythicDungeonCharts.Frame.ChartFrame.chart_title, 14) + --chartFrame:SetTitle("") + --detailsFramework:SetFontSize(chartFrame.chart_title, 14) - mythicDungeonCharts.Frame.TitleText:SetText(mythicDungeonCharts.ChartTable.DungeonName and phrase .. mythicDungeonCharts.ChartTable.DungeonName or phrase) + dungeonChartFrame.TitleText:SetText(mythicDungeonCharts.ChartTable.DungeonName and phrase .. mythicDungeonCharts.ChartTable.DungeonName or phrase) - mythicDungeonCharts.Frame.ShowChartFrame() + dungeonChartFrame.ShowChartFrame() + + chartFrame:Plot() if (verbosemode) then mythicDungeonCharts:Debug("mythicDungeonCharts.ShowChart() success") @@ -421,124 +270,6 @@ local PixelFrameOnLeave = function(self) timer.ShowID = showID end -local TAXIROUTE_LINEFACTOR = 128 / 126 -- Multiplying factor for texture coordinates -local TAXIROUTE_LINEFACTOR_2 = TAXIROUTE_LINEFACTOR / 2 -- Half of that - -function mythicDungeonCharts:CustomDrawLine (C, sx, sy, ex, ey, w, color, layer, linetexture, graphIndex) - 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") - - if linetexture then --this data series texture - T:SetTexture(linetexture) - - elseif C.CustomLine then --overall chart texture - T:SetTexture(C.CustomLine) - - else --no texture assigned, use default - T:SetTexture(TextureDirectory.."line") - end - - table.insert(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() - - local playerName = mythicDungeonCharts.PlayerGraphIndex [graphIndex] - if (mythicDungeonCharts.Frame.ChartFrame.TextureID % 3 == 0 and playerName) then - - local pixelFrame = tremove(mythicDungeonCharts.Frame.ChartFrame.FrameFree) - if (not pixelFrame) then - local newFrame = CreateFrame("frame", nil, mythicDungeonCharts.Frame.ChartFrame, "BackdropTemplate") - newFrame:SetSize(1, 1) - - --newFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 2, tile = true}) - --newFrame:SetBackdropColor(0, 0, 0, 1) - newFrame:SetScript("OnEnter", PixelFrameOnEnter) - newFrame:SetScript("OnLeave", PixelFrameOnLeave) - - pixelFrame = newFrame - end - - pixelFrame:SetPoint("BOTTOMLEFT", C, relPoint, cx - Bwid, cy - Bhgt) - pixelFrame:SetPoint("TOPRIGHT", C, relPoint, cx + Bwid, cy + Bhgt) - - table.insert(mythicDungeonCharts.Frame.ChartFrame.FrameInUse, pixelFrame) - pixelFrame.PlayerName = playerName - pixelFrame.Height = ey - - end - - mythicDungeonCharts.Frame.ChartFrame.TextureID = mythicDungeonCharts.Frame.ChartFrame.TextureID + 1 - return T -end mythicDungeonCharts.ClassColors = { ["HUNTER1"] = { r = 0.67, g = 0.83, b = 0.45, colorStr = "ffabd473" }, @@ -592,4 +323,140 @@ mythicDungeonCharts.ClassColors = { if (Details222.Debug.MythicPlusChartWindowDebug) then --C_Timer.After(1, mythicDungeonCharts.ShowChart) +end + +function mythicDungeonCharts.RefreshBossTimeline(dungeonChartFrame, elapsedTime) + ---@type df_chartmulti + local chartFrame = dungeonChartFrame.ChartFrame + + for i, bossTable in ipairs(mythicDungeonCharts.ChartTable.BossDefeated) do + local bossWidget = dungeonChartFrame.BossWidgetsFrame.Widgets[i] + + if (not bossWidget) then + local newBossWidget = CreateFrame("frame", "$parentBossWidget" .. i, dungeonChartFrame.BossWidgetsFrame, "BackdropTemplate") + newBossWidget:SetSize(64, 32) + newBossWidget:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}) + newBossWidget:SetBackdropColor(0, 0, 0, 0.1) + newBossWidget:SetBackdropBorderColor(0, 0, 0, 0) + + local bossAvatar = detailsFramework:CreateImage(newBossWidget, "", 64, 32, "border") + bossAvatar:SetPoint("bottomleft", newBossWidget, "bottomleft", 0, 0) + bossAvatar:SetScale(1.0) + newBossWidget.AvatarTexture = bossAvatar + + local verticalLine = detailsFramework:CreateImage(newBossWidget, "", 1, chartFrame:GetHeight() - 25, "overlay") + verticalLine:SetColorTexture(1, 1, 1, 0.3) + verticalLine:SetPoint("bottomleft", newBossWidget, "bottomright", 0, 0) + + local timeText = detailsFramework:CreateLabel(newBossWidget) + timeText:SetPoint("bottomright", newBossWidget, "bottomright", 0, 0) + newBossWidget.TimeText = timeText + + local timeBackground = detailsFramework:CreateImage(newBossWidget, "", 30, 12, "artwork") + timeBackground:SetColorTexture(0, 0, 0, 0.8) + timeBackground:SetPoint("topleft", timeText, "topleft", -2, 2) + timeBackground:SetPoint("bottomright", timeText, "bottomright", 2, 0) + + dungeonChartFrame.BossWidgetsFrame.Widgets[i] = newBossWidget + bossWidget = newBossWidget + end + + local chartLength = chartFrame:GetWidth() + local secondsPerPixel = chartLength / elapsedTime + local xPosition = bossTable[1] * secondsPerPixel + + bossWidget:SetPoint("bottomright", chartFrame, "bottomleft", xPosition, 22) + + bossWidget.TimeText:SetText(detailsFramework:IntegerToTimer(bossTable[1])) + + if (bossTable[2].bossimage) then + bossWidget.AvatarTexture:SetTexture(bossTable[2].bossimage) + else + local bossAvatar = Details:GetBossPortrait(nil, nil, bossTable[2].name, bossTable[2].ej_instance_id) + bossWidget.AvatarTexture:SetTexture(bossAvatar) + end + end +end + +function mythicDungeonCharts.CreateCloseMinimizeButtons(dungeonChartFrame) + local fMinimized = mythicDungeonCharts.FrameMinimized + + local closeButton = CreateFrame("button", "$parentCloseButton", dungeonChartFrame, "UIPanelCloseButton") + closeButton:GetNormalTexture():SetDesaturated(true) + closeButton:SetWidth(24) + closeButton:SetHeight(24) + closeButton:SetPoint("topright", dungeonChartFrame, "topright", 0, -1) + closeButton:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+16) + + local minimizeButton = CreateFrame("button", "$parentCloseButton", dungeonChartFrame, "UIPanelCloseButton") + minimizeButton:GetNormalTexture():SetDesaturated(true) + minimizeButton:SetWidth(24) + minimizeButton:SetHeight(24) + minimizeButton:SetPoint("right", closeButton, "left", 2, 0) + minimizeButton:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+16) + minimizeButton:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) + minimizeButton:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) + minimizeButton:SetHighlightTexture([[Interface\BUTTONS\UI-Panel-MinimizeButton-Highlight]]) + + local closeButtonWhenMinimized = CreateFrame("button", "$parentCloseButton", fMinimized, "UIPanelCloseButton") + closeButtonWhenMinimized:GetNormalTexture():SetDesaturated(true) + closeButtonWhenMinimized:SetWidth(24) + closeButtonWhenMinimized:SetHeight(24) + closeButtonWhenMinimized:SetPoint("topright", fMinimized, "topright", 0, -1) + closeButtonWhenMinimized:SetFrameLevel(fMinimized:GetFrameLevel()+16) + + local minimizeButtonWhenMinimized = CreateFrame("button", "$parentCloseButton", fMinimized, "UIPanelCloseButton") + minimizeButtonWhenMinimized:GetNormalTexture():SetDesaturated(true) + minimizeButtonWhenMinimized:SetWidth(24) + minimizeButtonWhenMinimized:SetHeight(24) + minimizeButtonWhenMinimized:SetPoint("right", closeButtonWhenMinimized, "left", 2, 0) + minimizeButtonWhenMinimized:SetFrameLevel(fMinimized:GetFrameLevel()+16) + minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) + minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) + minimizeButtonWhenMinimized:SetHighlightTexture([[Interface\BUTTONS\UI-Panel-MinimizeButton-Highlight]]) + + closeButtonWhenMinimized:SetScript("OnClick", function() + dungeonChartFrame.IsMinimized = false + fMinimized:Hide() + minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) + minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) + end) + + --replace the default click function + local minimize_func = function(self) + if (dungeonChartFrame.IsMinimized) then + dungeonChartFrame.IsMinimized = false + fMinimized:Hide() + dungeonChartFrame:Show() + minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-HideButton-Up]]) + minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-HideButton-Down]]) + else + dungeonChartFrame.IsMinimized = true + dungeonChartFrame:Hide() + fMinimized:Show() + minimizeButtonWhenMinimized:SetNormalTexture([[Interface\BUTTONS\UI-Panel-CollapseButton-Up]]) + minimizeButtonWhenMinimized:SetPushedTexture([[Interface\BUTTONS\UI-Panel-CollapseButton-Up]]) + end + end + + minimizeButton:SetScript("OnClick", minimize_func) + minimizeButtonWhenMinimized:SetScript("OnClick", minimize_func) +end + +function mythicDungeonCharts.CreateBossWidgets(dungeonChartFrame) + dungeonChartFrame.BossWidgetsFrame = CreateFrame("frame", "$parentBossFrames", dungeonChartFrame, "BackdropTemplate") + dungeonChartFrame.BossWidgetsFrame:SetFrameLevel(dungeonChartFrame:GetFrameLevel()+10) + dungeonChartFrame.BossWidgetsFrame.Widgets = {} + + dungeonChartFrame.BossWidgetsFrame.GraphPin = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "overlay") + dungeonChartFrame.BossWidgetsFrame.GraphPin:SetTexture([[Interface\BUTTONS\UI-RadioButton]]) + dungeonChartFrame.BossWidgetsFrame.GraphPin:SetTexCoord(17/64, 32/64, 0, 1) + dungeonChartFrame.BossWidgetsFrame.GraphPin:SetSize(16, 16) + + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow = dungeonChartFrame.BossWidgetsFrame:CreateTexture(nil, "artwork") + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetTexture([[Interface\Calendar\EventNotificationGlow]]) + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetTexCoord(0, 1, 0, 1) + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetSize(14, 14) + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetBlendMode("ADD") + dungeonChartFrame.BossWidgetsFrame.GraphPinGlow:SetPoint("center", dungeonChartFrame.BossWidgetsFrame.GraphPin, "center", 0, 0) end \ No newline at end of file