Canvas test
This commit is contained in:
@@ -109,6 +109,27 @@ function BaseFrame:textBg(x, y, text, bg)
|
||||
self._render:textBg(x, y, text, bg)
|
||||
end
|
||||
|
||||
--- @shortDescription Renders a text with a background color to the render Object
|
||||
--- @param x number The x position to render to
|
||||
--- @param y number The y position to render to
|
||||
--- @param text string The text to render
|
||||
--- @param bg colors The background color
|
||||
--- @protected
|
||||
function BaseFrame:drawText(x, y, text)
|
||||
if x < 1 then text = string.sub(text, 1 - x); x = 1 end
|
||||
self._render:text(x, y, text)
|
||||
end
|
||||
|
||||
function BaseFrame:drawFg(x, y, fg)
|
||||
if x < 1 then fg = string.sub(fg, 1 - x); x = 1 end
|
||||
self._render:fg(x, y, fg)
|
||||
end
|
||||
|
||||
function BaseFrame:drawBg(x, y, bg)
|
||||
if x < 1 then bg = string.sub(bg, 1 - x); x = 1 end
|
||||
self._render:bg(x, y, bg)
|
||||
end
|
||||
|
||||
--- @shortDescription Renders a text with a foreground and background color to the render Object
|
||||
--- @param x number The x position to render to
|
||||
--- @param y number The y position to render to
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
local elementManager = require("elementManager")
|
||||
local errorManager = require("errorManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local expect = require("libraries/expect")
|
||||
local split = require("libraries/utils").split
|
||||
@@ -576,6 +577,46 @@ function Container:textBg(x, y, text, bg)
|
||||
return self
|
||||
end
|
||||
|
||||
function Container:drawText(x, y, text)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
local textStart = x < 1 and (2 - x) or 1
|
||||
local textLen = math.min(#text - textStart + 1, w - math.max(1, x) + 1)
|
||||
|
||||
if textLen <= 0 then return self end
|
||||
|
||||
VisualElement.drawText(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1))
|
||||
return self
|
||||
end
|
||||
|
||||
function Container:drawFg(x, y, fg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
local textStart = x < 1 and (2 - x) or 1
|
||||
local textLen = math.min(#fg - textStart + 1, w - math.max(1, x) + 1)
|
||||
if textLen <= 0 then return self end
|
||||
|
||||
VisualElement.drawFg(self, math.max(1, x), math.max(1, y), fg:sub(textStart, textStart + textLen - 1))
|
||||
return self
|
||||
end
|
||||
|
||||
function Container:drawBg(x, y, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
local textStart = x < 1 and (2 - x) or 1
|
||||
local textLen = math.min(#bg - textStart + 1, w - math.max(1, x) + 1)
|
||||
if textLen <= 0 then return self end
|
||||
|
||||
VisualElement.drawBg(self, math.max(1, x), math.max(1, y), bg:sub(textStart, textStart + textLen - 1))
|
||||
return self
|
||||
end
|
||||
|
||||
--- @shortDescription Draws a line of text and fg and bg as colors
|
||||
--- @param x number The x position to draw the text
|
||||
--- @param y number The y position to draw the text
|
||||
@@ -618,13 +659,15 @@ function Container:render()
|
||||
end
|
||||
for _, child in ipairs(self.get("visibleChildren")) do
|
||||
if child == self then
|
||||
self.basalt.LOGGER.error("CIRCULAR REFERENCE DETECTED!")
|
||||
errorManager.error("CIRCULAR REFERENCE DETECTED!")
|
||||
return
|
||||
end
|
||||
child:render()
|
||||
child:postRender()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @private
|
||||
function Container:destroy()
|
||||
for _, child in ipairs(self._values.children) do
|
||||
|
||||
@@ -168,6 +168,27 @@ function VisualElement:textBg(x, y, text, bg)
|
||||
self.parent:textBg(x, y, text, bg)
|
||||
end
|
||||
|
||||
function VisualElement:drawText(x, y, text)
|
||||
local xElement, yElement = self:calculatePosition()
|
||||
x = x + xElement - 1
|
||||
y = y + yElement - 1
|
||||
self.parent:drawText(x, y, text)
|
||||
end
|
||||
|
||||
function VisualElement:drawFg(x, y, fg)
|
||||
local xElement, yElement = self:calculatePosition()
|
||||
x = x + xElement - 1
|
||||
y = y + yElement - 1
|
||||
self.parent:drawFg(x, y, fg)
|
||||
end
|
||||
|
||||
function VisualElement:drawBg(x, y, bg)
|
||||
local xElement, yElement = self:calculatePosition()
|
||||
x = x + xElement - 1
|
||||
y = y + yElement - 1
|
||||
self.parent:drawBg(x, y, bg)
|
||||
end
|
||||
|
||||
--- @shortDescription Draws text with both colors
|
||||
--- @param x number The x position to draw
|
||||
--- @param y number The y position to draw
|
||||
@@ -440,4 +461,9 @@ function VisualElement:render()
|
||||
self:multiBlit(1, 1, width, height, " ", tHex[self.get("foreground")], tHex[self.get("background")])
|
||||
end
|
||||
|
||||
--- @shortDescription Post-rendering function for the element
|
||||
--- @protected
|
||||
function VisualElement:postRender()
|
||||
end
|
||||
|
||||
return VisualElement
|
||||
@@ -8,7 +8,9 @@ local main = format:gsub("path", basaltPath)
|
||||
package.path = main.."rom/?"
|
||||
|
||||
local function errorHandler(err)
|
||||
package.path = main.."rom/?"
|
||||
local errorManager = require("errorManager")
|
||||
package.path = defaultPath
|
||||
errorManager.header = "Basalt Loading Error"
|
||||
errorManager.error(err)
|
||||
end
|
||||
|
||||
@@ -261,6 +261,7 @@ end
|
||||
local function renderFrames()
|
||||
for _, frame in pairs(activeFrames)do
|
||||
frame:render()
|
||||
frame:postRender()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
282
src/plugins/canvas.lua
Normal file
282
src/plugins/canvas.lua
Normal file
@@ -0,0 +1,282 @@
|
||||
local tHex = require("libraries/colorHex")
|
||||
local errorManager = require("errorManager")
|
||||
local Canvas = {}
|
||||
Canvas.__index = Canvas
|
||||
|
||||
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 = {}
|
||||
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)
|
||||
if index > 0 and index <= #self.commands then
|
||||
self.commands[index] = drawFn
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Canvas:removeCommand(index)
|
||||
self.commands[index] = nil
|
||||
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] or _fg
|
||||
local __bg = type(_bg) == "number" and tHex[_bg] 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)
|
||||
|
||||
if _bg and _fg and _char then
|
||||
render:multiBlit(_x, _y, _width, _height, textLine, fgLine, bgLine)
|
||||
return
|
||||
end
|
||||
|
||||
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 ipairs(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 ipairs(canvas.commands.post) do
|
||||
cmd(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
VisualElement = VisualElement,
|
||||
API = Canvas
|
||||
}
|
||||
@@ -142,6 +142,48 @@ function Render:textBg(x, y, text, bg)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Renders the text to the screen
|
||||
--- @param x number The x position to blit to
|
||||
--- @param y number The y position to blit to
|
||||
--- @param text string The text to blit
|
||||
--- @return Render
|
||||
function Render:text(x, y, text)
|
||||
if y < 1 or y > self.height then return self end
|
||||
|
||||
self.buffer.text[y] = sub(self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text), 1, self.width)
|
||||
self:addDirtyRect(x, y, #text, 1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Blits a foreground color to the screen
|
||||
--- @param x number The x position
|
||||
--- @param y number The y position
|
||||
--- @param fg string The foreground color to blit
|
||||
--- @return Render
|
||||
function Render:fg(x, y, fg)
|
||||
if y < 1 or y > self.height then return self end
|
||||
|
||||
self.buffer.fg[y] = sub(self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg), 1, self.width)
|
||||
self:addDirtyRect(x, y, #fg, 1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Blits a background color to the screen
|
||||
--- @param x number The x position
|
||||
--- @param y number The y position
|
||||
--- @param bg string The background color to blit
|
||||
--- @return Render
|
||||
function Render:bg(x, y, bg)
|
||||
if y < 1 or y > self.height then return self end
|
||||
|
||||
self.buffer.bg[y] = sub(self.buffer.bg[y]:sub(1,x-1) .. bg .. self.buffer.bg[y]:sub(x+#bg), 1, self.width)
|
||||
self:addDirtyRect(x, y, #bg, 1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Blits text to the screen
|
||||
--- @param x number The x position to blit to
|
||||
--- @param y number The y position to blit to
|
||||
|
||||
Reference in New Issue
Block a user