-- display API
-- made by akaJag
-- work in progress
function round(num)
if type(num) ~= "number" then return num end
if num%1<.5 then num=math.floor(num) end
if num%1>=.5 then num=math.ceil(num) end
return num
end
function setupdaterate(class,s)
class.updaterate = s
end
function getupdaterate(class)
return class.updaterate
end
function removepixles(class)
local w,h = class.obj.getSize()
-- check row
local width = next(class.pixles)
while width do
if width > w or width < 0 then
-- remove row
local tmpw = width
width = next(class.pixles,width)
class.pixles[tmpw] = nil
else
-- check column
local height = next(class.pixles[width])
while height do
if height > h or height < 0 then
-- remove column
local tmph = height
height = next(class.pixles[width],height)
class.pixles[width][tmph] = nil
else
-- next iteration
height = next(class.pixles[width],height)
end
end
-- next iteration
width = next(class.pixles,width)
end
end
end
function fixpixles(class)
local w,h = class.obj.getSize()
for width = 1,w do
if class.pixles[width] == nil then class.pixles[width] = {} end
for height = 1,h do
if class.pixles[width][height] == nil then
class.pixles[width][height] = {sym=" ",bg=colors.black,fg=colors.white}
end
end
end
end
-- returns:
-- (number) update time
function update(class)
local startupdate = os.clock()
class:removepixles()
class:fixpixles()
local endupdate = os.clock()
return endupdate - startupdate
end
function draw(class)
local startdraw = os.clock()
for w in pairs(class.pixles) do
for h,pixle in pairs(class.pixles[w]) do
class.obj.setCursorPos(w,h)
class.obj.setBackgroundColor(pixle.bg or colors.black)
class.obj.setTextColor(pixle.fg or colors.white)
class.obj.write(pixle.sym or " ")
end
end
local enddraw = os.clock()
return enddraw - startdraw
end
-- can be used in a parallel loop
-- dont forget to do prepareloop() before running
-- stop it with stoploop()
function loop(class)
if not running then return end
local s = class:update()
s = s + class:draw()
sleep(updaterate - s)
end
function prepareloop(class)
class.looprunning = true
class.looptime = os.clock()
end
-- returns the time the loop has been on
function stoploop()
class.looprunning = false
return os.clock - class.looptime
end
function setcolor(class,fg,bg)
class.fgcolor = fg or class.fgcolor
class.bgcolor = bg or class.bgcolor
end
function getcolor(class)
return class.fgcolor,class.bgcolor
end
function setfgcolor(class,col)
class.fgcolor = col
end
function getfgcolor(class)
return class.fgcolor
end
function setbgcolor(class,col)
class.bgcolor = col
end
function getbgcolor(class)
return class.bgcolor
end
-- stroke is only used with "line" draw mode
function setstroke(class,var)
class.stroke=var
end
function getstroke(class)
return class.stroke
end
-- clears the entire screen
-- bg = only change background, ignore text
function clear(class,bg)
for w in pairs(class.pixles) do
for h,pixle in pairs(class.pixles[w]) do
class.pixles[w][h].bg = class.bgcolor
if bg ~= true then
class.pixles[w][h].sym = " "
end
end
end
end
-- trans = transparant option
function write(class,str,x,y,trans)
x=round(x) y=round(y)
for count = 1,#str do
if class.pixles[x+count-1] then
if class.pixles[x+count-1][y] then
class.pixles[x+count-1][y].sym = str:sub(count,count)
class.pixles[x+count-1][y].fg = class.fgcolor
if trans ~= true then
class.pixles[x+count-1][y].bg = class.bgcolor
end
end
end
end
end
function line(class,x1,y1,x2,y2,bg)
-- Bresenham's algorithm
local dx = x2 - x1
if dx < 0 then dx = -dx end
local dy = y2 - y1
if dy < 0 then dy = -dy end
local sx,sy
if x1 < x2 then sx = 1
else sx = -1 end
if y1 < y2 then sy = 1
else sy = -1 end
local err = dx - dy
repeat
local x,y = round(x1),round(y1)
if class.pixles[x] then
if class.pixles[x][y] then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
if x1 == x2 and y1 == y2 then break end
local e2 = 2*err
if e2 > -dy then
err = err - dy
x1 = x1 + sx
end
if e2 < dx then
err = err + dx
y1 = y1 + sy
end
until false
end
-- supported modes: fill, line
-- bg = only set background color
function rect(class,mode,x1,y1,x2,y2,bg)
x1=round(x1) y1=round(y1) x2=round(x2) y2=round(y2)
if mode == "fill" then
for x = x1,x2 do
for y = y1,y2 do
if class.pixles[x] ~= nil then
if class.pixles[x][y] ~= nil then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
end
end
elseif mode == "line" then
-- top line
for x = x1,x2 do
for y = y1,y1+class.stroke-1 do
if class.pixles[x] ~= nil then
if class.pixles[x][y] ~= nil then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
end
end
-- bottom line
for x = x1,x2 do
for y = y2-class.stroke+1,y2 do
if class.pixles[x] ~= nil then
if class.pixles[x][y] ~= nil then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
end
end
-- right line
for x = x2-class.stroke+1,x2 do
for y = y1,y2 do
if class.pixles[x] ~= nil then
if class.pixles [x][y] ~= nil then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
end
end
-- left line
for x = x1,x1+class.stroke-1 do
for y = y1,y2 do
if class.pixles[x] ~= nil then
if class.pixles [x][y] ~= nil then
class.pixles[x][y].bg = class.bgcolor
if bg ~= true then
class.pixles[x][y].sym = " "
end
end
end
end
end
end
end
-- supported modes: fill, line
-- bg = only set background color
function circle(class,mode,x,y,r,bg)
x=round(x) y=round(y)
for w in pairs(class.pixles) do
for h in pairs(class.pixles[w]) do
-- pythagrias sats
-- a^2 + b^2 = c^2
local dis = math.sqrt((w-x)^2 + (h-y)^2)
if dis <= r then
if mode == "fill" or
(mode == "line" and dis >= r-class.stroke) then
if class.pixles[w] ~= nil then
if class.pixles[w][h] ~= nil then
class.pixles[w][h].bg = class.bgcolor
if bg ~= true then
class.pixles[w][h].sym = " "
end
end
end
end
end
end
end
end
local function strtotbl(str)
local tbl = {}
for uchar in str:gmatch("([%z\1-\127\194-\244][\128-\191]*)") do
table.insert(tbl,uchar)
end
return tbl
end
local function tbltostr(tbl)
local str = ""
for _,value in pairs(tbl) do
str=str..tostring(value)
end
end
-- x,y = top left corner of text field
-- w = width of text field
-- l = max lenght of text
-- c = make the text look like a certain character
-- trans = transparent
-- callback = function, gets events as parameters, if returns true then exit
function read(class,x,y,w,l,initstr,c,trans,callback)
x=round(x) y=round(y) w=round(w) l=round(l)
if type(l)~="number" then l=-1 end
if type(initstr)~="string" then initstr="" end
local running = true
local text = initstr or ""
local padding = #text-w
local cursor = #text
callback = callback or function(ev,p1,p2,p3,p4,p5)
if ev == "key" and p1 == keys.enter then
return true
end
end
while running do
-- fix cursor and padding
if cursor > #text then cursor = #text end
if cursor < 0 then cursor = 0 end
if padding > #text-1 then padding = #text-1 end
if padding < 0 then padding = 0 end
-- drawing
if trans ~= true then
class:rect("fill",x,y,x+w,y,false)
end
local tbl = strtotbl(text)
if type(c) == "string" then
local str = c:rep(math.floor(#tbl/#c)) .. c:sub(1,#tbl%#c)
class:write(str:sub(padding+1,padding+w+1),x,y,true)
else
local count = 0
for index = padding+1,padding+w+1 do
if text[index] then
class:write(text[index],x+count,y,true)
end
count = count + 1
end
end
class:update()
class:draw()
if cursor-padding <= w and cursor-padding >= 0 then
class.obj.setCursorPos(x+cursor-padding,y)
class.obj.setTextColor(class:getfgcolor())
class.obj.setCursorBlink(true)
end
local ev,p1,p2,p3,p4,p5 = os.pullEvent()
class.obj.setCursorBlink(false)
if callback(ev,p1,p2,p3,p4,p5) == true then
return text,ev,p1,p2,p3,p4,p5
end
if ev == "key" then
if p1 == keys.left then
-- move left
cursor = cursor - 1
if cursor<padding then padding = padding - 1 end
elseif p1 == keys.right then
-- move right
cursor = cursor + 1
if cursor-padding > w then padding = padding + 1 end
elseif p1 == keys.backspace then
-- remove a character
local num = cursor-1
if num < 0 then num = 0 end
text = text:sub(1,num) .. text:sub(cursor+1)
cursor = cursor - 1
if cursor<padding then padding = padding - 1 end
elseif p1 == keys.delete then
-- remove a character
text = text:sub(1,cursor) .. text:sub(cursor+2)
elseif p1 == keys.home then
cursor = 0
padding = 0
elseif p1 == keys["end"] then
cursor = text:len()
padding = text:len()-w
end
elseif ev == "char" then
if #text < l or l <= 0 then -- check length limit
-- add a character
text = text:sub(1,cursor) .. p1 .. text:sub(cursor+1)
cursor = cursor + 1
if cursor-padding > w then padding = padding + 1 end
end
elseif (ev == "mouse_click" and class.type == "term")
or (ev == "monitor_touch" and class.type == "monitor" and p1 == class.path) then
-- move cursor
-- x=p2, y=p3
if p2<x then p2=x end
if p2>x+w then p2=x+w end
cursor = p2-x+padding
elseif ev == "mouse_scroll" then
-- scrolling left/right
padding = padding + p1
end
end
end
function load(objpath)
local startload = os.clock()
tobj = {}
-- object variables
objpath = objpath or "N/A"
if peripheral.isPresent(objpath)
and peripheral.getType(objpath) == "monitor" then
tobj.obj = peripheral.wrap(objpath or "N/A")
tobj.type = "monitor"
tobj.path = objpath
tobj.eventname = "monitor_touch"
else
tobj.type = "term"
tobj.obj = term
tobj.path = "N/A"
tobj.eventname = "mouse_click"
end
-- variables
tobj.pixles = {}
tobj.updaterate = 0.2
tobj.looprunning = false
tobj.looptime = 0
tobj.stroke = 1
tobj.bgcolor = colors.black
tobj.fgcolor = colors.white
-- misc functions
tobj.setupdaterate = setupdaterate
tobj.getupdaterate = getupdaterate
tobj.removepixles = removepixles
tobj.fixpixles = fixpixles
tobj.update = update
tobj.draw = draw
tobj.loop = loop
tobj.prepareloop = prepareloop
tobj.stoploop = stoploop
-- color functions
tobj.setcolor = setcolor
tobj.setfgcolor = setfgcolor
tobj.setbgcolor = setbgcolor
tobj.getcolor = getcolor
tobj.getfgcolor = getfgcolor
tobj.getbgcolor = getbgcolor
tobj.setstroke = setstroke
tobj.getstroke = getstroke
-- british
tobj.setcolour = setcolor
tobj.setfgcolour = setfgcolor
tobj.setbgcolour = setbgcolor
tobj.getcolour = getcolor
tobj.getfgcolour = getfgcolor
tobj.getbgcolour = getbgcolor
-- drawing functions
tobj.clear = clear
tobj.write = write
tobj.line = line
tobj.rect = rect
tobj.circle = circle
tobj.read = read
tobj:removepixles()
tobj:fixpixles()
local endload = os.clock()
return tobj,endload-startload
end
Comments