277 lines
7.7 KiB
Lua
277 lines
7.7 KiB
Lua
local tHex = require("libraries/colorHex")
|
|
local errorManager = require("errorManager")
|
|
local Canvas = {}
|
|
Canvas.__index = Canvas
|
|
---@configDefault false
|
|
|
|
local sub, rep = string.sub, string.rep
|
|
|
|
function Canvas.new(element)
|
|
local self = setmetatable({}, Canvas)
|
|
self.commands = {pre={},post={}}
|
|
self.type = "pre"
|
|
self.element = element
|
|
return self
|
|
end
|
|
|
|
function Canvas:clear()
|
|
self.commands = {pre={},post={}}
|
|
return self
|
|
end
|
|
|
|
function Canvas:getValue(v)
|
|
if type(v) == "function" then
|
|
return v(self.element)
|
|
end
|
|
return v
|
|
end
|
|
|
|
function Canvas:setType(type)
|
|
if type == "pre" or type == "post" then
|
|
self.type = type
|
|
else
|
|
errorManager.error("Invalid type. Use 'pre' or 'post'.")
|
|
end
|
|
return self
|
|
end
|
|
|
|
function Canvas:addCommand(drawFn)
|
|
local index = #self.commands[self.type] + 1
|
|
self.commands[self.type][index] = drawFn
|
|
return index
|
|
end
|
|
|
|
function Canvas:setCommand(index, drawFn)
|
|
self.commands[index] = drawFn
|
|
return self
|
|
end
|
|
|
|
function Canvas:removeCommand(index)
|
|
--self.commands[self.type][index] = nil
|
|
table.remove(self.commands[self.type], index)
|
|
return self
|
|
end
|
|
|
|
function Canvas:text(x, y, text, fg, bg)
|
|
return self:addCommand(function(render)
|
|
local _x, _y = self:getValue(x), self:getValue(y)
|
|
local _text = self:getValue(text)
|
|
local _fg = self:getValue(fg)
|
|
local _bg = self:getValue(bg)
|
|
local __fg = type(_fg) == "number" and tHex[_fg]:rep(#text) or _fg
|
|
local __bg = type(_bg) == "number" and tHex[_bg]:rep(#text) or _bg
|
|
render:drawText(_x, _y, _text)
|
|
if __fg then render:drawFg(_x, _y, __fg) end
|
|
if __bg then render:drawBg(_x, _y, __bg) end
|
|
end)
|
|
end
|
|
|
|
function Canvas:bg(x, y, bg)
|
|
return self:addCommand(function(render)
|
|
render:drawBg(x, y, bg)
|
|
end)
|
|
end
|
|
|
|
function Canvas:fg(x, y, fg)
|
|
return self:addCommand(function(render)
|
|
render:drawFg(x, y, fg)
|
|
end)
|
|
end
|
|
|
|
function Canvas:rect(x, y, width, height, char, fg, bg)
|
|
return self:addCommand(function(render)
|
|
local _x, _y = self:getValue(x), self:getValue(y)
|
|
local _width, _height = self:getValue(width), self:getValue(height)
|
|
local _char = self:getValue(char)
|
|
local _fg = self:getValue(fg)
|
|
local _bg = self:getValue(bg)
|
|
|
|
if(type(_fg) == "number") then _fg = tHex[_fg] end
|
|
if(type(_bg) == "number") then _bg = tHex[_bg] end
|
|
|
|
local bgLine = _bg and sub(_bg:rep(_width), 1, _width)
|
|
local fgLine = _fg and sub(_fg:rep(_width), 1, _width)
|
|
local textLine = _char and sub(_char:rep(_width), 1, _width)
|
|
|
|
for i = 0, _height - 1 do
|
|
if _bg then render:drawBg(_x, _y + i, bgLine) end
|
|
if _fg then render:drawFg(_x, _y + i, fgLine) end
|
|
if _char then render:drawText(_x, _y + i, textLine) end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Canvas:line(x1, y1, x2, y2, char, fg, bg)
|
|
local function linePoints(x1, y1, x2, y2)
|
|
local points = {}
|
|
local count = 0
|
|
|
|
local dx = math.abs(x2 - x1)
|
|
local dy = math.abs(y2 - y1)
|
|
local sx = (x1 < x2) and 1 or -1
|
|
local sy = (y1 < y2) and 1 or -1
|
|
local err = dx - dy
|
|
|
|
while true do
|
|
count = count + 1
|
|
points[count] = {x = x1, y = y1}
|
|
|
|
if (x1 == x2) and (y1 == y2) then break end
|
|
|
|
local err2 = err * 2
|
|
if err2 > -dy then
|
|
err = err - dy
|
|
x1 = x1 + sx
|
|
end
|
|
if err2 < dx then
|
|
err = err + dx
|
|
y1 = y1 + sy
|
|
end
|
|
end
|
|
|
|
return points
|
|
end
|
|
local needsRecreate = false
|
|
local points
|
|
if type(x1) == "function" or type(y1) == "function" or type(x2) == "function" or type(y2) == "function" then
|
|
needsRecreate = true
|
|
else
|
|
points = linePoints(self:getValue(x1), self:getValue(y1), self:getValue(x2), self:getValue(y2))
|
|
end
|
|
|
|
return self:addCommand(function(render)
|
|
if needsRecreate then
|
|
points = linePoints(self:getValue(x1), self:getValue(y1), self:getValue(x2), self:getValue(y2))
|
|
end
|
|
local _char = self:getValue(char)
|
|
local _fg = self:getValue(fg)
|
|
local _bg = self:getValue(bg)
|
|
local __fg = type(_fg) == "number" and tHex[_fg] or _fg
|
|
local __bg = type(_bg) == "number" and tHex[_bg] or _bg
|
|
|
|
for _, point in ipairs(points) do
|
|
local x = math.floor(point.x)
|
|
local y = math.floor(point.y)
|
|
|
|
if _char then render:drawText(x, y, _char) end
|
|
if __fg then render:drawFg(x, y, __fg) end
|
|
if __bg then render:drawBg(x, y, __bg) end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Canvas:ellipse(centerX, centerY, radiusX, radiusY, char, fg, bg)
|
|
local function ellipsePoints(x, y, radiusX, radiusY)
|
|
local points = {}
|
|
local count = 0
|
|
|
|
local a2 = radiusX * radiusX
|
|
local b2 = radiusY * radiusY
|
|
|
|
local px = 0
|
|
local py = radiusY
|
|
|
|
local p = b2 - a2 * radiusY + 0.25 * a2
|
|
local px2 = 0
|
|
local py2 = 2 * a2 * py
|
|
|
|
local function addPoint(px, py)
|
|
count = count + 1
|
|
points[count] = {x = x + px, y = y + py}
|
|
count = count + 1
|
|
points[count] = {x = x - px, y = y + py}
|
|
count = count + 1
|
|
points[count] = {x = x + px, y = y - py}
|
|
count = count + 1
|
|
points[count] = {x = x - px, y = y - py}
|
|
end
|
|
|
|
addPoint(px, py)
|
|
|
|
while px2 < py2 do
|
|
px = px + 1
|
|
px2 = px2 + 2 * b2
|
|
if p < 0 then
|
|
p = p + b2 + px2
|
|
else
|
|
py = py - 1
|
|
py2 = py2 - 2 * a2
|
|
p = p + b2 + px2 - py2
|
|
end
|
|
addPoint(px, py)
|
|
end
|
|
|
|
p = b2 * (px + 0.5) * (px + 0.5) + a2 * (py - 1) * (py - 1) - a2 * b2
|
|
|
|
while py > 0 do
|
|
py = py - 1
|
|
py2 = py2 - 2 * a2
|
|
if p > 0 then
|
|
p = p + a2 - py2
|
|
else
|
|
px = px + 1
|
|
px2 = px2 + 2 * b2
|
|
p = p + a2 - py2 + px2
|
|
end
|
|
addPoint(px, py)
|
|
end
|
|
|
|
return points
|
|
end
|
|
|
|
local points = ellipsePoints(centerX, centerY, radiusX, radiusY)
|
|
return self:addCommand(function(render)
|
|
local _char = self:getValue(char)
|
|
local _fg = self:getValue(fg)
|
|
local _bg = self:getValue(bg)
|
|
local __fg = type(_fg) == "number" and tHex[_fg] or _fg
|
|
local __bg = type(_bg) == "number" and tHex[_bg] or _bg
|
|
|
|
for y, line in pairs(points) do
|
|
local x = math.floor(line.x)
|
|
local y = math.floor(line.y)
|
|
|
|
if _char then render:drawText(x, y, _char) end
|
|
if __fg then render:drawFg(x, y, __fg) end
|
|
if __bg then render:drawBg(x, y, __bg) end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local VisualElement = {hooks={}}
|
|
|
|
function VisualElement.setup(element)
|
|
element.defineProperty(element, "canvas", {
|
|
default = nil,
|
|
type = "table",
|
|
getter = function(self)
|
|
if not self._values.canvas then
|
|
self._values.canvas = Canvas.new(self)
|
|
end
|
|
return self._values.canvas
|
|
end
|
|
})
|
|
end
|
|
|
|
function VisualElement.hooks.render(self)
|
|
local canvas = self.get("canvas")
|
|
if canvas and #canvas.commands.pre > 0 then
|
|
for _, cmd in pairs(canvas.commands.pre) do
|
|
cmd(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
function VisualElement.hooks.postRender(self)
|
|
local canvas = self.get("canvas")
|
|
if canvas and #canvas.commands.post > 0 then
|
|
for _, cmd in pairs(canvas.commands.post) do
|
|
cmd(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
return {
|
|
VisualElement = VisualElement,
|
|
API = Canvas
|
|
} |