Skip to content

Instantly share code, notes, and snippets.

@SammyForReal
Last active July 11, 2022 20:11
Show Gist options
  • Save SammyForReal/3dfb65e46a9949ae9201343eb014c398 to your computer and use it in GitHub Desktop.
Save SammyForReal/3dfb65e46a9949ae9201343eb014c398 to your computer and use it in GitHub Desktop.
Small lua pixel render API, written for ComputerCraft.
--[[
Coater | Pixel render API | v.1.0
=================================
Available under the MIT Licence
Copyright (c) 2022 Sammy L. Koch
]]
local ccExpect = require("cc.expect")
local expect,field,range = ccExpect.expect,ccExpect.field,ccExpect.range
local tCToB = {[1]='0',[2]='1',[4]='2',[8]='3',[16]='4',[32]='5',[64]='6',[128]='7',[256]='8',[512]='9',[1024]='a',[2048]='b',[4096]='c',[8192]='d',[16384]='e',[32768]='f' }
local tBToC = {['0']=1,['1']=2,['2']=4,['3']=8,['4']=16,['5']=32,['7']=128,['8']=256,['9']=512,['a']=1024,['b']=2048,['c']=4096,['d']=8192,['e']=16384,['f']=32768 }
---Converts a given table to a blit char.
---@param str string Something like: "100110" for \153
---@return string sChar Matching counter part
---@return boolean bReversed Swaped text & background color?
local function toBlit(str)
if type(str) ~= "string" or #str < 6 then
str = "000000"
elseif str:sub(1,1) == '#' then
return str:sub(2,2), false
end
local t = {}
for i=1,6 do
t[#t+1] = tonumber(str:sub(i,i)) or 0
end
if t[6]==1 then
for i=1,5 do
t[i] = 1-t[i]
end end
local n = 128
for i=0,4 do
n = n+t[i+1]*2^i
end
return string.char(n), (t[6] == 1)
end
---Sets the text & background color or swaps them, if no colors are given.
---@param self table
---@param cBG? number Background color
---@param cFG? number Foreground color (Textcolor)
local function setColor(self, cBG,cFG)
expect(1, self, "table")
expect(2, cBG, "number", "nil")
expect(3, cFG, "number", "nil")
if not (cBG and cFG) then
cBG = term.getBackgroundColor()
self.termCBG = term.getTextColor()
self.termCFG = cBG
return cFG, cBG
else
if cBG then self.termCBG = tCToB[cBG] end
if cFG then self.termCFG = tCToB[cFG] end
end
return cBG,cFG
end
---Sets one (or multible) pixel in the buffer at a given position.
---@param self table
---@param nX number
---@param nY number
---@param pixel any The pixels. Can be a single one (number), a row (string) or a whole 'sprite' (table)
local function setPixel(self, nX,nY, pixel)
expect(0, self, "table")
expect(1, nX, "number")
expect(2, nY, "number")
expect(3, pixel,"number", "string", "table")
if type(pixel) ~= "table" then
pixel = { tostring(pixel) }
end
for i,line in pairs(pixel) do
line = tostring(line)
local nCurLine = self[nY+i-1]
if nY+i-1 > 0 and nY+i-1 < #self then
if #line >= #self[nY+i-1]-nX then
line = line:sub(0,#self[nY+i-1]-nX-2)
end
self[nY+i-1] = nCurLine:sub(1,nX-1)..line..nCurLine:sub(nX+#line)
end
end
end
---Clears the whole screen.
---@param self table
local function clear(self)
local line = ('0'):rep(#self[1])
for i=1,#self do
self[i] = line
end
end
---Writes a message at a given position.
---@param self table
---@param nX number
---@param nY number
---@param sLabel string
local function setLabel(self, nX,nY, sLabel)
expect(0, self, "table")
expect(1, nX, "number")
expect(2, nY, "number")
expect(4, sLabel,"string")
local sNew = ""
for i=1,#sLabel do
sNew = sNew..'#'..sLabel:sub(i,i)
end
self:setPixel((nX-1)*2+1,(nY-1)*3+1, { sNew })
end
---Renders a given map at a given position.
---@param self table
---@param nOffsetX? number
---@param nOffsetY? number
local function render(self, nOffsetX,nOffsetY)
expect(1, self, "table")
expect(2, nOffsetX, "number", "nil")
expect(3, nOffsetY, "number", "nil")
field(self, "cBG", "table")
field(self, "cFG", "table")
for y=1,#self,3 do
for x=1,#self[1],2 do
local bit,reverse = toBlit(
self[y]:sub(x,x+1)..
self[y+1]:sub(x,x+1)..
self[y+2]:sub(x,x+1)
)
local nX,nY = math.floor(x/2)+1,math.floor(y/3)+1
local cBG,cFG = colors.black,colors.white
if nX > 0 and nX < #self.cBG[1]
and nY > 0 and nY < #self.cBG then
cBG,cFG = tBToC[self.cBG[nY]:sub(nX,nX):lower()],tBToC[self.cFG[nY]:sub(nX,nX):lower()]
end
if reverse then cFG,cBG = cBG,cFG end
term.setBackgroundColor(cBG)
term.setTextColor(cFG)
term.setCursorPos(nX+(nOffsetX or 0), nY+(nOffsetY or 0))
term.write(bit)
end end end
---Generates an empty map with given sizes.
---@param nW? number Width
---@param nH? number Height
---@return table tNewMap
local function createMap(nW,nH)
expect(1,nW, "number")
expect(2,nH, "number")
nW = nW*2
nH = nH*3
local tNewMap = { cFG={}, cBG={} }
local sPixelRow = ('0'):rep(nW)
local sColorRow = ('f'):rep(nW/2)
for i=1,nH do
tNewMap[i] = sPixelRow
if i<nH/3 then
tNewMap.cFG[i] = sPixelRow:sub(1,nW/2)
tNewMap.cBG[i] = sColorRow
end
end
tNewMap.render = render
tNewMap.setColor = setColor
tNewMap.setPixel = setPixel
tNewMap.setLabel = setLabel
tNewMap.clear = clear
return tNewMap
end
return {
createMap = createMap
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment