Files
Basalt2/src/elements/Graph.lua
Robert Jelic d36c7e7867 - Added Timer
- Fixed BaseElement Visual request issues
- Added number animation
- Added entries animation
2025-04-20 10:27:48 +02:00

224 lines
6.8 KiB
Lua

local elementManager = require("elementManager")
local VisualElement = elementManager.getElement("VisualElement")
local tHex = require("libraries/colorHex")
---@configDescription A point based graph element
---@configDefault false
--- This is the base class for all graph elements. It is a point based graph.
--- @usage local graph = main:addGraph()
--- @usage :addSeries("input", " ", colors.green, colors.green, 10)
--- @usage :addSeries("output", " ", colors.red, colors.red, 10)
--- @usage
--- @usage basalt.schedule(function()
--- @usage while true do
--- @usage graph:addPoint("input", math.random(1,100))
--- @usage graph:addPoint("output", math.random(1,100))
--- @usage sleep(2)
--- @usage end
--- @usage end)
--- @class Graph : VisualElement
local Graph = setmetatable({}, VisualElement)
Graph.__index = Graph
---@property minValue number 0 The minimum value of the graph
Graph.defineProperty(Graph, "minValue", {default = 0, type = "number", canTriggerRender = true})
---@property maxValue number 100 The maximum value of the graph
Graph.defineProperty(Graph, "maxValue", {default = 100, type = "number", canTriggerRender = true})
---@property series table {} The series of the graph
Graph.defineProperty(Graph, "series", {default = {}, type = "table", canTriggerRender = true})
--- Creates a new Graph instance
--- @shortDescription Creates a new Graph instance
--- @return Graph self The newly created Graph instance
--- @private
function Graph.new()
local self = setmetatable({}, Graph):__init()
self.class = Graph
return self
end
--- @shortDescription Initializes the Graph instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Graph self The initialized instance
--- @protected
function Graph:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Graph")
self.set("width", 20)
self.set("height", 10)
return self
end
--- @shortDescription Adds a series to the graph
--- @param name string The name of the series
--- @param symbol string The symbol of the series
--- @param bgCol number The background color of the series
--- @param fgCol number The foreground color of the series
--- @param pointCount number The number of points in the series
--- @return Graph self The graph instance
function Graph:addSeries(name, symbol, bgCol, fgCol, pointCount)
local series = self.get("series")
table.insert(series, {
name = name,
symbol = symbol or " ",
bgColor = bgCol or colors.white,
fgColor = fgCol or colors.black,
pointCount = pointCount or self.get("width"),
data = {},
visible = true
})
self:updateRender()
return self
end
--- @shortDescription Removes a series from the graph
--- @param name string The name of the series
--- @return Graph self The graph instance
function Graph:removeSeries(name)
local series = self.get("series")
for i, s in ipairs(series) do
if s.name == name then
table.remove(series, i)
break
end
end
self:updateRender()
return self
end
--- @shortDescription Gets a series from the graph
--- @param name string The name of the series
--- @return table? series The series
function Graph:getSeries(name)
local series = self.get("series")
for _, s in ipairs(series) do
if s.name == name then
return s
end
end
return nil
end
--- @shortDescription Changes the visibility of a series
--- @param name string The name of the series
--- @param visible boolean Whether the series should be visible
--- @return Graph self The graph instance
function Graph:changeSeriesVisibility(name, visible)
local series = self.get("series")
for _, s in ipairs(series) do
if s.name == name then
s.visible = visible
break
end
end
self:updateRender()
return self
end
--- @shortDescription Adds a point to a series
--- @param name string The name of the series
--- @param value number The value of the point
--- @return Graph self The graph instance
function Graph:addPoint(name, value)
local series = self.get("series")
for _, s in ipairs(series) do
if s.name == name then
table.insert(s.data, value)
while #s.data > s.pointCount do
table.remove(s.data, 1)
end
break
end
end
self:updateRender()
return self
end
--- @shortDescription Focuses a series
--- @param name string The name of the series
--- @return Graph self The graph instance
function Graph:focusSeries(name)
local series = self.get("series")
for index, s in ipairs(series) do
if s.name == name then
table.remove(series, index)
table.insert(series, s)
break
end
end
self:updateRender()
return self
end
--- @shortDescription Sets the point count of a series
--- @param name string The name of the series
--- @param count number The number of points in the series
--- @return Graph self The graph instance
function Graph:setSeriesPointCount(name, count)
local series = self.get("series")
for _, s in ipairs(series) do
if s.name == name then
s.pointCount = count
while #s.data > count do
table.remove(s.data, 1)
end
break
end
end
self:updateRender()
return self
end
--- Clears all points from a series
--- @shortDescription Clears all points from a series
--- @param name? string The name of the series
--- @return Graph self The graph instance
function Graph:clear(seriesName)
local series = self.get("series")
if seriesName then
for _, s in ipairs(series) do
if s.name == seriesName then
s.data = {}
break
end
end
else
for _, s in ipairs(series) do
s.data = {}
end
end
return self
end
--- @shortDescription Renders the graph
--- @protected
function Graph:render()
VisualElement.render(self)
local width = self.get("width")
local height = self.get("height")
local minVal = self.get("minValue")
local maxVal = self.get("maxValue")
local series = self.get("series")
for _, s in pairs(series) do
if(s.visible)then
local dataCount = #s.data
local spacing = (width - 1) / math.max((dataCount - 1), 1)
for i, value in ipairs(s.data) do
local x = math.floor(((i-1) * spacing) + 1)
local normalizedValue = (value - minVal) / (maxVal - minVal)
local y = math.floor(height - (normalizedValue * (height-1)))
y = math.max(1, math.min(y, height))
self:blit(x, y, s.symbol, tHex[s.bgColor], tHex[s.fgColor])
end
end
end
end
return Graph