-remade animation -added xml support -finished dynamic values -added new object: graphic -added themes for frames -textfield got some basic improvements to create coding editors
471 lines
18 KiB
Lua
471 lines
18 KiB
Lua
local Object = require("Object")
|
|
local geometric = require("geometricPoints")
|
|
local tHex = require("tHex")
|
|
local xmlValue = require("utils").getValueFromXML
|
|
|
|
local sub,len,max,min = string.sub,string.len,math.max,math.min
|
|
|
|
return function(name)
|
|
-- Graphic
|
|
local base = Object(name)
|
|
local objectType = "Graphic"
|
|
base:setZIndex(2)
|
|
|
|
local graphicObjects = {}
|
|
local graphic = {}
|
|
local shrinkedGraphic = {}
|
|
local isGraphicShrinked = false
|
|
local xOffset, yOffset = 0, 0
|
|
local dragable = false
|
|
local xMouse,yMouse
|
|
local w, h = 40, 15
|
|
local canvasSizeChanged = false
|
|
|
|
local tColourLookup = {}
|
|
for n=1,16 do
|
|
tColourLookup[ string.byte( "0123456789abcdef",n,n ) ] = 2^(n-1)
|
|
end
|
|
|
|
local function stringToTable(str)
|
|
local t = {}
|
|
for i = 1, #str do
|
|
t[i] = str:sub(i, i)
|
|
end
|
|
return t
|
|
end
|
|
|
|
local function setBG(x, y, width, height, colorStr)
|
|
if (y >= 1) and (y <= height) then
|
|
if (x + len(colorStr) > 0) and (x <= width) then
|
|
local oldCache = graphic[y]
|
|
local newCache
|
|
local nEnd = x + #colorStr - 1
|
|
|
|
if (x < 1) then
|
|
colorStr = sub(colorStr, 1 - x + 1, width - x + 1)
|
|
elseif (nEnd > width) then
|
|
colorStr = sub(colorStr, 1, width - x + 1)
|
|
end
|
|
|
|
if (x > 1) then
|
|
newCache = sub(oldCache, 1, x - 1) .. colorStr
|
|
else
|
|
newCache = colorStr
|
|
end
|
|
if nEnd < width then
|
|
newCache = newCache .. sub(oldCache, nEnd + 1, width)
|
|
end
|
|
graphic[y] = newCache
|
|
end
|
|
end
|
|
end
|
|
|
|
local function redrawCanvasSize()
|
|
local w,h = w,h
|
|
if(isGraphicShrinked)then w = w*2 h = h*3 end
|
|
for y=1,h do
|
|
if(graphic[y]~=nil)then
|
|
if(w>graphic[y]:len())then
|
|
graphic[y] = graphic[y]..(tHex[base.bgColor]):rep(w-graphic[y]:len())
|
|
else
|
|
graphic[y] = graphic[y]:sub(1,w)
|
|
end
|
|
else
|
|
graphic[y] = (tHex[base.bgColor]):rep(w)
|
|
end
|
|
end
|
|
end
|
|
redrawCanvasSize()
|
|
|
|
|
|
local function shrink()
|
|
local function parseLine( tImageArg, sLine )
|
|
local tLine = {}
|
|
for x=1,sLine:len() do
|
|
tLine[x] = tColourLookup[ string.byte(sLine,x,x) ] or 0
|
|
end
|
|
table.insert( tImageArg, tLine )
|
|
end
|
|
function parseImage( sRawData )
|
|
if type( sRawData ) ~= "string" then
|
|
error( "bad argument #1 (expected string, got " .. type( sRawData ) .. ")" )
|
|
end
|
|
local tImage = {}
|
|
for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do
|
|
parseLine( tImage, sLine )
|
|
end
|
|
return tImage
|
|
end
|
|
|
|
local rawImg = ""
|
|
for y=1,#graphic do
|
|
if(y==#graphic)then
|
|
rawImg = rawImg..graphic[y]
|
|
else
|
|
rawImg = rawImg..graphic[y].."\n"
|
|
end
|
|
end
|
|
local img = parseImage(rawImg)
|
|
-- shrinkSystem is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/
|
|
local relations = { [0] = { 8, 4, 3, 6, 5 }, { 4, 14, 8, 7 }, { 6, 10, 8, 7 }, { 9, 11, 8, 0 }, { 1, 14, 8, 0 }, { 13, 12, 8, 0 }, { 2, 10, 8, 0 }, { 15, 8, 10, 11, 12, 14 },
|
|
{ 0, 7, 1, 9, 2, 13 }, { 3, 11, 8, 7 }, { 2, 6, 7, 15 }, { 9, 3, 7, 15 }, { 13, 5, 7, 15 }, { 5, 12, 8, 7 }, { 1, 4, 7, 15 }, { 7, 10, 11, 12, 14 } }
|
|
|
|
local colourNum, exponents, colourChar = {}, {}, {}
|
|
for i = 0, 15 do
|
|
exponents[2 ^ i] = i
|
|
end
|
|
do
|
|
local hex = "0123456789abcdef"
|
|
for i = 1, 16 do
|
|
colourNum[hex:sub(i, i)] = i - 1
|
|
colourNum[i - 1] = hex:sub(i, i)
|
|
colourChar[hex:sub(i, i)] = 2 ^ (i - 1)
|
|
colourChar[2 ^ (i - 1)] = hex:sub(i, i)
|
|
|
|
local thisRel = relations[i - 1]
|
|
for i = 1, #thisRel do
|
|
thisRel[i] = 2 ^ thisRel[i]
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getBestColourMatch(usage)
|
|
local lastCol = relations[exponents[usage[#usage][1]]]
|
|
|
|
for j = 1, #lastCol do
|
|
local thisRelation = lastCol[j]
|
|
for i = 1, #usage - 1 do
|
|
if usage[i][1] == thisRelation then
|
|
return i
|
|
end
|
|
end
|
|
end
|
|
|
|
return 1
|
|
end
|
|
|
|
local function colsToChar(pattern, totals)
|
|
if not totals then
|
|
local newPattern = {}
|
|
totals = {}
|
|
for i = 1, 6 do
|
|
local thisVal = pattern[i]
|
|
local thisTot = totals[thisVal]
|
|
totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal
|
|
end
|
|
pattern = newPattern
|
|
end
|
|
|
|
local usage = {}
|
|
for key, value in pairs(totals) do
|
|
usage[#usage + 1] = { key, value }
|
|
end
|
|
|
|
if #usage > 1 then
|
|
-- Reduce the chunk to two colours:
|
|
while #usage > 2 do
|
|
table.sort(usage, function(a, b)
|
|
return a[2] > b[2]
|
|
end)
|
|
local matchToInd, usageLen = getBestColourMatch(usage), #usage
|
|
local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1]
|
|
for i = 1, 6 do
|
|
if pattern[i] == matchFrom then
|
|
pattern[i] = matchTo
|
|
usage[matchToInd][2] = usage[matchToInd][2] + 1
|
|
end
|
|
end
|
|
usage[usageLen] = nil
|
|
end
|
|
|
|
-- Convert to character. Adapted from oli414's function:
|
|
-- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/
|
|
local data = 128
|
|
for i = 1, #pattern - 1 do
|
|
if pattern[i] ~= pattern[6] then
|
|
data = data + 2 ^ (i - 1)
|
|
end
|
|
end
|
|
return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]]
|
|
else
|
|
-- Solid colour character:
|
|
return "\128", colourChar[pattern[1]], colourChar[pattern[1]]
|
|
end
|
|
end
|
|
|
|
local results, width, height, bgCol = { {}, {}, {} }, 0, #img + #img % 3, base.bgColor or colors.black
|
|
for i = 1, #img do
|
|
if #img[i] > width then
|
|
width = #img[i]
|
|
end
|
|
end
|
|
|
|
for y = 0, height - 1, 3 do
|
|
local cRow, tRow, bRow, counter = {}, {}, {}, 1
|
|
|
|
for x = 0, width - 1, 2 do
|
|
-- Grab a 2x3 chunk:
|
|
local pattern, totals = {}, {}
|
|
|
|
for yy = 1, 3 do
|
|
for xx = 1, 2 do
|
|
pattern[#pattern + 1] = (img[y + yy] and img[y + yy][x + xx]) and (img[y + yy][x + xx] == 0 and bgCol or img[y + yy][x + xx]) or bgCol
|
|
totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1
|
|
end
|
|
end
|
|
|
|
cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals)
|
|
counter = counter + 1
|
|
end
|
|
|
|
results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow)
|
|
end
|
|
|
|
results.width, results.height = #results[1][1], #results[1]
|
|
|
|
shrinkedGraphic = results
|
|
end
|
|
|
|
local function redraw()
|
|
local w,h = w,h
|
|
if(isGraphicShrinked)then w = w*2 h = h*3 end
|
|
for k,v in pairs(graphicObjects)do
|
|
for a,b in pairs(v[1])do
|
|
setBG(b.x, b.y, w, h, v[2])
|
|
end
|
|
end
|
|
if(isGraphicShrinked)then
|
|
shrink()
|
|
end
|
|
end
|
|
|
|
local object = {
|
|
init = function(self)
|
|
self.bgColor = self.parent:getTheme("GraphicBG")
|
|
end,
|
|
|
|
getType = function(self)
|
|
return objectType
|
|
end;
|
|
|
|
setSize = function(self, width, height, rel)
|
|
base.setSize(self, width, height, rel)
|
|
if not(canvasSizeChanged)then
|
|
w = width
|
|
h = height
|
|
redrawCanvasSize()
|
|
end
|
|
redraw()
|
|
return self
|
|
end,
|
|
|
|
setOffset = function(self, x, y)
|
|
xOffset = x or xOffset
|
|
yOffset = y or yOffset
|
|
return self
|
|
end,
|
|
|
|
setCanvasSize = function(self, width, height)
|
|
w,h = width,height
|
|
canvasSizeChanged = true
|
|
redrawCanvasSize()
|
|
return self
|
|
end,
|
|
|
|
clearCanvas = function(self)
|
|
graphicObjects = {}
|
|
graphic = {}
|
|
redrawCanvasSize()
|
|
end,
|
|
|
|
getOffset = function(self)
|
|
return xOffset,yOffset
|
|
end,
|
|
|
|
setValuesByXMLData = function(self, data)
|
|
base.setValuesByXMLData(self, data)
|
|
if(xmlValue("text", data)~=nil)then self:setText(xmlValue("text", data)) end
|
|
if(xmlValue("xOffset", data)~=nil)then self:setOffset(xmlValue("xOffset", data), yOffset) end
|
|
if(xmlValue("yOffset", data)~=nil)then self:setOffset(xOffset, xmlValue("yOffset", data)) end
|
|
if(xmlValue("wCanvas", data)~=nil)then w = xmlValue("wCanvas", data) end
|
|
if(xmlValue("hCanvas", data)~=nil)then h = xmlValue("hCanvas", data) end
|
|
if(xmlValue("shrink", data)~=nil)then if(xmlValue("shrink", data))then self:shrink() end end
|
|
if(xmlValue("dragable", data)~=nil)then if(xmlValue("dragable", data))then dragable = true end end
|
|
if(data["ellipse"]~=nil)then
|
|
local tab = data["ellipse"]
|
|
if(tab.properties~=nil)then tab = {tab} end
|
|
for k,v in pairs(tab)do
|
|
local col = colors[xmlValue("color", v)]
|
|
local rad1 = xmlValue("radius", v)
|
|
local rad2 = xmlValue("radius2", v)
|
|
local x = xmlValue("x", v)
|
|
local y = xmlValue("y", v)
|
|
local filled = xmlValue("filled", v)
|
|
self:addEllipse(col, rad1, rad2, x, y, filled)
|
|
end
|
|
end
|
|
if(data["circle"]~=nil)then
|
|
local tab = data["circle"]
|
|
if(tab.properties~=nil)then tab = {tab} end
|
|
for k,v in pairs(tab)do
|
|
local col = colors[xmlValue("color", v)]
|
|
local rad = tonumber(xmlValue("radius", v))
|
|
local x = tonumber(xmlValue("x", v))
|
|
local y = tonumber(xmlValue("y", v))
|
|
local filled = xmlValue("filled", v)
|
|
self:addCircle(col, rad, x, y, filled)
|
|
end
|
|
end
|
|
if(data["line"]~=nil)then
|
|
local tab = data["line"]
|
|
if(tab.properties~=nil)then tab = {tab} end
|
|
for k,v in pairs(tab)do
|
|
local col = colors[xmlValue("color", v)]
|
|
local x = tonumber(xmlValue("x", v))
|
|
local x2 = tonumber(xmlValue("x2", v))
|
|
local y = tonumber(xmlValue("y", v))
|
|
local y2 = tonumber(xmlValue("y2", v))
|
|
self:addLine(col, x, y, x2, y2)
|
|
end
|
|
end
|
|
|
|
if(data["rectangle"]~=nil)then
|
|
local tab = data["rectangle"]
|
|
if(tab.properties~=nil)then tab = {tab} end
|
|
for k,v in pairs(tab)do
|
|
local col = colors[xmlValue("color", v)]
|
|
local x = tonumber(xmlValue("x", v))
|
|
local x2 = tonumber(xmlValue("x2", v))
|
|
local y = tonumber(xmlValue("y", v))
|
|
local y2 = tonumber(xmlValue("y2", v))
|
|
local filled = xmlValue("filled", v)=="true" and true or false
|
|
self:addRectangle(col, x, y, x2, y2, filled)
|
|
end
|
|
end
|
|
if(data["triangle"]~=nil)then
|
|
local tab = data["triangle"]
|
|
if(tab.properties~=nil)then tab = {tab} end
|
|
for k,v in pairs(tab)do
|
|
local col = colors[xmlValue("color", v)]
|
|
local x = tonumber(xmlValue("x", v))
|
|
local x2 = tonumber(xmlValue("x2", v))
|
|
local x3 = tonumber(xmlValue("x2", v))
|
|
local y = tonumber(xmlValue("y", v))
|
|
local y2 = tonumber(xmlValue("y2", v))
|
|
local y3 = tonumber(xmlValue("y3", v))
|
|
local filled = xmlValue("filled", v)
|
|
self:addTriangle(col, x, y, x2, y2, x3, y3, filled)
|
|
end
|
|
end
|
|
|
|
return self
|
|
end,
|
|
|
|
addCircle = function(self, color, rad, x, y, filled)
|
|
local col = tHex[color]
|
|
table.insert(graphicObjects, {geometric.circle(x or 1, y or 1, rad, filled), tHex[color]})
|
|
redraw()
|
|
return self
|
|
end;
|
|
|
|
addEllipse = function(self, color, rad, rad2, x, y, filled)
|
|
table.insert(graphicObjects, {geometric.ellipse(x or 1, y or 1, rad, rad2, filled), tHex[color]})
|
|
redraw()
|
|
return self
|
|
end;
|
|
|
|
addLine = function(self, color, x1, y1, x2, y2)
|
|
table.insert(graphicObjects, {geometric.line(x1 or 1, y1 or 1, x2 or 1, y2 or 1), tHex[color]})
|
|
redraw()
|
|
return self
|
|
end;
|
|
|
|
addTriangle = function(self, color, x1, y1, x2, y2, x3, y3, filled)
|
|
table.insert(graphicObjects, {geometric.triangle(x1 or 1, y1 or 1, x2 or 1, y2 or 1, x3 or 1, y3 or 1, filled), tHex[color]})
|
|
redraw()
|
|
return self
|
|
end;
|
|
|
|
addRectangle = function(self, color, x1, y1, x2, y2, filled)
|
|
table.insert(graphicObjects, {geometric.rectangle(x1 or 1, y1 or 1, x2 or 1, y2 or 1, filled), tHex[color]})
|
|
redraw()
|
|
return self
|
|
end;
|
|
|
|
shrink = function(self)
|
|
isGraphicShrinked = true
|
|
redrawCanvasSize()
|
|
shrink()
|
|
return self
|
|
end,
|
|
|
|
setDragable = function(self, drag)
|
|
dragable = drag == true and true or false
|
|
return self
|
|
end,
|
|
|
|
mouseHandler = function(self, event, button, x, y)
|
|
if(base.mouseHandler(self, event, button, x, y))then
|
|
if(dragable)then
|
|
if(event=="mouse_click")then
|
|
xMouse,yMouse = x,y
|
|
end
|
|
|
|
if(event=="mouse_drag")then
|
|
if(xMouse~=nil)and(yMouse~=nil)then
|
|
xOffset = max(min(xOffset+xMouse-x, w-self:getWidth()),0)
|
|
xMouse = x
|
|
yOffset = max(min(yOffset+yMouse-y, h-self:getHeight()),0)
|
|
yMouse = y
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
return false
|
|
end,
|
|
|
|
draw = function(self)
|
|
if (base.draw(self)) then
|
|
if (self.parent ~= nil) then
|
|
local obx, oby = self:getAnchorPosition()
|
|
local w,h = self:getSize()
|
|
if(self.bgColor~=false)then
|
|
self.parent:drawBackgroundBox(obx, oby, w, h, self.bgColor)
|
|
end
|
|
if (isGraphicShrinked) then
|
|
-- this is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/
|
|
local t, tC, bC = shrinkedGraphic[1], shrinkedGraphic[2], shrinkedGraphic[3]
|
|
for i = 1, shrinkedGraphic.height do
|
|
local x, y = obx+xOffset, oby + i - 1 + yOffset
|
|
if(y>oby-1)and(y<=oby+h-1)and(x<=w+obx)then
|
|
local tI = t[i]
|
|
local xpos,substart,subend = max(x, obx), max(1 - x + 1, 1), min(w - (x-obx), w)
|
|
if type(tI) == "string" then
|
|
self.parent:setText(xpos, y, sub(tI, substart, subend))
|
|
self.parent:setFG(xpos, y, sub(tC[i], substart, subend))
|
|
self.parent:setBG(xpos, y, sub(bC[i], substart, subend))
|
|
elseif type(tI) == "table" then
|
|
self.parent:setText(xpos, y, sub(tI[2], substart, subend))
|
|
self.parent:setFG(xpos, y, sub(tC[i], substart, subend))
|
|
self.parent:setBG(xpos, y, sub(bC[i], substart, subend))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for i = 1, #graphic do
|
|
local x, y = obx+xOffset, oby + i - 1 + yOffset
|
|
if(y>oby-1)and(y<=oby+h-1)and(x<=w+obx)then
|
|
local xpos,substart,subend = max(x, obx), max(1 - x + 1, 1), min(w - (x-obx), w)
|
|
self.parent:setBG(xpos, y, sub(graphic[i],substart,subend))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self:setVisualChanged(false)
|
|
end
|
|
end;
|
|
}
|
|
|
|
return setmetatable(object, base)
|
|
end |