Advertisement
toastonrye

CanvasTerm.lua

Feb 12th, 2023
787
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.08 KB | None | 0 0
  1. --------------------------------------------------------------------------------------------------------
  2. -- Wojbies API 4.3 - CanvasTerm - Api for drawing window-like term object on Plethora overlay glasses --
  3. --------------------------------------------------------------------------------------------------------
  4. --   Copyright (c) 2015-2021 Wojbie ([email protected])
  5. --   Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
  6. --   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  7. --   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  8. --   3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  9. --   4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  10. --   5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
  11. --   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY.
  12.  
  13. --### Basic utility functions
  14. local function copyTable(...)
  15.     local tArgs = {...}
  16.     local copy = {}
  17.     for _, piece in pairs(tArgs) do
  18.         if piece and type(piece) == "table" then
  19.             for key, val in pairs(piece) do
  20.                 if type(val) == "table" then copy[key] = copyTable( copy[key] or {}, val)
  21.                 else copy[key] = val end
  22.             end
  23.         end
  24.     end
  25.     return copy
  26. end
  27.  
  28. --### Initializing
  29. local c = shell and {} or (_ENV or getfenv())
  30. c.versionName = "CanvasTerm By Wojbie"
  31. c.versionNum = 4.338 --2020-01-15
  32.  
  33. local expect = require "cc.expect".expect
  34.  
  35. --### Internal palette related tables.
  36. local tDefaultPallette = {[ colors.white ] = 0xF0F0F0, [ colors.orange ] = 0xF2B233, [ colors.magenta ] = 0xE57FD8, [ colors.lightBlue ] = 0x99B2F2, [ colors.yellow ] = 0xDEDE6C, [ colors.lime ] = 0x7FCC19, [ colors.pink ] = 0xF2B2CC, [ colors.gray ] = 0x4C4C4C, [ colors.lightGray ] = 0x999999, [ colors.cyan ] = 0x4C99B2, [ colors.purple ] = 0xB266E5, [ colors.blue ] = 0x3366CC, [ colors.brown ] = 0x7F664C, [ colors.green ] = 0x57A64E, [ colors.red ] = 0xCC4C4C, [ colors.black ] = 0x111111}
  37.  
  38. local tRGBA = {[ "0" ] = 0xF0F0F0FF, [ "1" ] = 0xF2B233FF, [ "2" ] = 0xE57FD8FF, [ "3" ] = 0x99B2F2FF, [ "4" ] = 0xDEDE6CFF, [ "5" ] = 0x7FCC19FF, [ "6" ] = 0xF2B2CCFF, [ "7" ] = 0x4C4C4CFF, [ "8" ] = 0x999999FF, [ "9" ] = 0x4C99B2FF, [ "a" ] = 0xB266E5FF, [ "b" ] = 0x3366CCFF, [ "c" ] = 0x7F664CFF, [ "d" ] = 0x57A64EFF, [ "e" ] = 0xCC4C4CFF, [ "f" ] = 0x111111FF}
  39.  
  40. local tHex = {[ colors.white ] = "0", [ colors.orange ] = "1", [ colors.magenta ] = "2", [ colors.lightBlue ] = "3", [ colors.yellow ] = "4", [ colors.lime ] = "5", [ colors.pink ] = "6", [ colors.gray ] = "7", [ colors.lightGray ] = "8", [ colors.cyan ] = "9", [ colors.purple ] = "a", [ colors.blue ] = "b", [ colors.brown ] = "c", [ colors.green ] = "d", [ colors.red ] = "e", [ colors.black ] = "f"}
  41.  
  42. c.dummyTerm = function(nWidth, nHeight, bColor)
  43.  
  44.     local isColor = bColor and true or false
  45.     local tDefaultPallette = copyTable(tDefaultPallette)
  46.  
  47.     local dummy = {}
  48.     dummy.getPaletteColour = function( c ) return colors.unpackRGB(tDefaultPallette[c])  end
  49.     dummy.isColor = function() return isColor end
  50.  
  51.     local buffer = window.create( dummy, 1, 1, nWidth, nHeight, false )
  52.     local bufferResize = buffer.reposition
  53.  
  54.     buffer.setVisible = nil
  55.     buffer.redraw = nil
  56.     buffer.restoreCursor = nil
  57.     buffer.getPosition = nil
  58.     buffer.reposition = nil
  59.     buffer.resize = function(nWidth, nHeight) return bufferResize(1, 1, nWidth, nHeight) end
  60.  
  61.     return buffer
  62. end local dummyTerm = c.dummyTerm
  63.  
  64. local textOffsetX = { --How much it goes to right on each symbol
  65.     ["!"] = 2, ['"'] = 1, ["'"] = 2, ['('] = 1, [')'] = 1, ['*'] = 1, [","] = 2, ["."] = 2, [":"] = 2, [";"] = 2, ["I"] = 1, ["["] = 1, ["]"] = 1, ["`"] = 2, ["f"] = 1, ["i"] = 2, ["k"] = 1, ["l"] = 2, ["t"] = 1, ["{"] = 1, ["|"] = 2, ["}"] = 1,
  66.     ["\161"] = 2, ["\162"] = 1, ["\164"] = 1, ["\165"] = 1, ["\166"] = 3, ["\167"] = 1, ["\168"] = 1, ["\169"] = 1, ["\176"] = -1, ["\181"] = 1, ["\182"] = 1, ["\184"] = 2, ["\195"] = 1, ["\204"] = 1, ["\205"] = 1, ["\206"] = 1, ["\207"] = 1, ["\208"] = 1, ["\210"] = 1, ["\215"] = 1, ["\217"] = 1, ["\219"] = 1, ["\221"] = 1, ["\222"] = 1, ["\236"] = 1, ["\237"] = 2, ["\239"] = 1, ["\240"] = 1, ["\253"] = 1, ["\254"] = 1,
  67. }
  68. for i = 1, 8 do textOffsetX[string.char(i)] = -1 end
  69. for i = 11, 12 do textOffsetX[string.char(i)] = -1 end
  70. for i = 14, 31 do textOffsetX[string.char(i)] = -1 end
  71. for i = 127, 159 do textOffsetX[string.char(i)] = -1 end
  72. textOffsetX["\173"] = -1
  73.  
  74. --### Main function
  75. c.create = function( parent, nX, nY, nWidth, nHeight, nScale, bVisible, bFrameCursor, bDisableAutoRedraw )
  76.  
  77.     expect(1, parent, "table")
  78.     if not parent.addGroup then
  79.         error( "Invalid parent object, please provide 2d canvas. try canvas() or canvas3d().create().addFrame()", 2 )
  80.     end
  81.     expect(2, nX, "number")
  82.     expect(3, nY, "number")
  83.     expect(4, nWidth, "number")
  84.     expect(5, nHeight, "number")
  85.     expect(6, nScale, "number", "nil")
  86.     expect(7, bVisible, "boolean", "nil")
  87.     expect(8, bFrameCursor, "boolean", "nil")
  88.     expect(9, bDisableAutoRedraw, "boolean", "nil")
  89.  
  90.     bVisible = bVisible ~= false
  91.     nScale = nScale or 1
  92.  
  93.     local group = parent.addGroup({nX, nY})
  94.     local buffer = dummyTerm(nWidth, nHeight, true)
  95.     local bufferResize = buffer.resize
  96.     buffer.resize = nil
  97.     local tRGBA = copyTable(tRGBA)
  98.     local tHex = copyTable(tHex)
  99.     local textOffsetX = copyTable(textOffsetX)
  100.     local tFrame, tTexts, tBacks, tCursor
  101.     local tText, tFront, tBack, tCursorMeta
  102.     local bBlink, bOnScreen = true, true
  103.     local nTextScaler = 4 / 6
  104.  
  105.     local function build()
  106.         tTexts = {}
  107.         tBacks = {}
  108.         local xSize = 4 * nScale
  109.         local ySize = 6 * nScale
  110.         tFrame = group.addRectangle( 0 , 0 , xSize * nWidth + 2, ySize * nHeight + 2, 0x111111FF)
  111.         for y = 1, nHeight do
  112.             tBacks[y] = {}
  113.             tTexts[y] = {}
  114.             for x = 1, nWidth do
  115.                 tBacks[y][x] = group.addRectangle( (x - 1) * xSize + 1 , (y - 1) * ySize + 1 , xSize, ySize, 0x7F664CFF)
  116.             end
  117.             for x = 1, nWidth do
  118.                 tTexts[y][x] = group.addText({(x - 1) * xSize + 1, (y - 1) * ySize + 1}, "A", 0x57A64EFF, nScale * nTextScaler)
  119.                 tTexts[y][x].x = (x - 1) * xSize + 1
  120.                 tTexts[y][x].y = (y - 1) * ySize + 1
  121.             end
  122.         end
  123.             tCursor = group.addGroup({0, 0})
  124.             tCursor.cursor = tCursor.addText({1, 1}, "_", 0xF0F0F0FF, nScale * nTextScaler)
  125.         if bFrameCursor then
  126.             tCursor.lines = tCursor.addLines({1, 1}, {1, ySize + 1}, {xSize + 1, ySize + 1}, {xSize + 1, 1}, 0xF0F0F0FF, 1)
  127.         end
  128.         bBlink = true
  129.         tText = {}
  130.         tFront = {}
  131.         tBack = {}
  132.         tCursorMeta = {}
  133.         for i in pairs(tHex) do
  134.             tRGBA[tHex[i]] = bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(i)), 8), 0xFF)
  135.         end
  136.     end
  137.  
  138.     local function destroy()
  139.         for y = 1, nHeight do
  140.             for x = 1, nWidth do tBacks[y][x].remove() end
  141.             for x = 1, nWidth do tTexts[y][x].remove() end
  142.         end
  143.         tFrame.remove()
  144.         tCursor.remove()
  145.     end
  146.    
  147.     local function redrawLine(nLine, bForce)
  148.         local text, front, back = buffer.getLine(nLine)
  149.         if bForce or tBack[nLine] ~= back then --dirty back
  150.             local rowBack = tBacks[nLine]
  151.             back:gsub("()(.)", function(x, c)
  152.                 if rowBack[x].color ~= tRGBA[c] then
  153.                     rowBack[x].setColor(tRGBA[c])
  154.                     rowBack[x].color = tRGBA[c]
  155.                 end
  156.             end)
  157.             tBack[nLine] = back
  158.         end
  159.         if bForce or tText[nLine] ~= text or tFront[nLine] ~= front then --dirty text
  160.             local rowText = tTexts[nLine]
  161.             front:gsub("()(.)", function(x, c)
  162.                 if rowText[x].color ~= tRGBA[c] then
  163.                     rowText[x].setColor(tRGBA[c])
  164.                     rowText[x].color = tRGBA[c]
  165.                 end
  166.             end)
  167.             text:gsub("()(.)", function(x, c)
  168.                 if rowText[x].text ~= c then
  169.                     rowText[x].setText(c)
  170.                     rowText[x].setPosition(rowText[x].x + (textOffsetX[c] or 0) * nTextScaler * nScale, rowText[x].y)
  171.                     rowText[x].text = c
  172.                 end
  173.             end)
  174.             tText[nLine] = text
  175.             tFront[nLine] = front
  176.         end
  177.     end
  178.  
  179.     local function redraw(bForce)
  180.         for y = 1, nHeight do
  181.             redrawLine(y, bForce)
  182.         end
  183.     end
  184.  
  185.     local function updateCursor()
  186.         local cx, cy = buffer.getCursorPos()
  187.         if tCursorMeta.x ~= cx or tCursorMeta.y ~= cy then
  188.             bOnScreen = cx > 0 and cx <= nWidth and cy > 0 and cy <= nHeight
  189.             tCursor.setPosition((cx - 1) * 4 * nScale, (cy - 1) * 6 * nScale)
  190.             tCursorMeta.x, tCursorMeta.y = cx, cy
  191.         end
  192.         local cColor = tRGBA[tHex[buffer.getTextColor()]]
  193.         if tCursorMeta.color ~= cColor then
  194.             tCursor.cursor.setColor(cColor)
  195.             tCursorMeta.color = cColor
  196.         end
  197.         local cBlink = buffer.getCursorBlink()
  198.         if tCursorMeta.blink ~= cBlink or tCursorMeta.bOnScreen ~= bOnScreen then
  199.             tCursor.cursor.setAlpha(bBlink and bOnScreen and cBlink and 0xFF or 0)
  200.             tCursorMeta.blink = cBlink
  201.             tCursorMeta.bOnScreen = bOnScreen
  202.         end
  203.     end
  204.    
  205.     local function updatePaletteColor(c)
  206.         if tHex[c] then
  207.             local col =  bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(c)), 8), 0xFF)
  208.             if tRGBA[tHex[c]] ~= col then
  209.                 tRGBA[tHex[c]] = col
  210.                 return true
  211.             end  
  212.         end
  213.         return false
  214.     end
  215.    
  216.     local function updatePalette()
  217.         local changed = false
  218.         for i in pairs(tHex) do
  219.             local col = bit.bor(bit.blshift(colors.packRGB(buffer.getPaletteColour(i)), 8), 0xFF)
  220.             if tRGBA[tHex[i]] ~= col then
  221.                 tRGBA[tHex[i]] = col
  222.                 changed = true
  223.             end
  224.         end
  225.         return changed
  226.     end
  227.  
  228.     local function blinker()
  229.         if math.floor(os.epoch("utc") / 400) % 2 == 0 ~= bBlink then
  230.             bBlink = not bBlink
  231.             tCursor.cursor.setAlpha(bBlink and bOnScreen and buffer.getCursorBlink() and 0xFF or 0)
  232.         end
  233.     end
  234.  
  235.    
  236.     local redrawDirtiers = {["clear"] = true, ["scroll"] = true }
  237.     local redrawLineDirtiers = {["write"] = true, ["blit"] = true, ["clearLine"] = true }
  238.     local cursorDirters = {["setCursorPos"] = true, ["setCursorBlink"] = true, ["setTextColor"] = true, ["setTextColour"] = true}
  239.     local paletteDirters = {["setPaletteColor"] = true, ["setPaletteColour"] = true}
  240.    
  241.     local canvasTerm = {}
  242.    
  243.     for i, k in pairs(buffer) do
  244.         if bDisableAutoRedraw then
  245.             canvasTerm[i] = k
  246.         elseif redrawDirtiers[i] then
  247.             canvasTerm[i] = function(...)
  248.                 local returns = table.pack(k(...))
  249.                 if bVisible then redraw() updateCursor() end
  250.                 return table.unpack(returns, 1, returns.n)
  251.             end
  252.         elseif redrawLineDirtiers[i] then
  253.             canvasTerm[i] = function(...)
  254.                 local returns = table.pack(k(...))
  255.                 if bVisible then redrawLine(select(2, buffer.getCursorPos())) updateCursor() end
  256.                 return table.unpack(returns, 1, returns.n)
  257.             end
  258.         elseif cursorDirters[i] then
  259.             canvasTerm[i] = function(...)
  260.                 local returns = table.pack(k(...))
  261.                 if bVisible then updateCursor() end
  262.                 return table.unpack(returns, 1, returns.n)
  263.             end
  264.         elseif paletteDirters[i] then
  265.             canvasTerm[i] = function(c, ...)
  266.                 local returns = table.pack(k(c, ...))
  267.                 local changed = updatePaletteColor(c)
  268.                 if bVisible then redraw(changed) updateCursor() end
  269.                 return table.unpack(returns, 1, returns.n)
  270.             end
  271.         else
  272.             canvasTerm[i] = k
  273.         end
  274.     end
  275.  
  276.     canvasTerm.setVisible = function( b )
  277.         expect(1, b, "boolean")
  278.         if bVisible ~= b then
  279.             bVisible = b
  280.             if bVisible then
  281.                 build()
  282.                 redraw()
  283.                 updateCursor()
  284.             else
  285.                 destroy()
  286.             end
  287.         end
  288.     end
  289.  
  290.     if bDisableAutoRedraw then
  291.  
  292.         canvasTerm.flush = function()
  293.             if bVisible then
  294.                 local changed = updatePalette()
  295.                 redraw(changed)
  296.                 updateCursor()
  297.             end
  298.         end
  299.  
  300.     end
  301.    
  302.     canvasTerm.loadLines = function(tWindow)
  303.         expect(1, tWindow, "table")
  304.         assert(tWindow.getLine, "Error. loadLones requires windows table with getLine supported")
  305.         local _, inY = tWindow.getSize()
  306.         local cX, xY = buffer.getCursorPos()
  307.         for i in pairs(tHex) do
  308.             buffer.setPaletteColour(i, tWindow.getPaletteColour(i))
  309.         end
  310.         for y = 1, math.min(nHeight, inY) do
  311.             buffer.setCursorPos(1, y)
  312.             buffer.blit(tWindow.getLine(y))
  313.         end
  314.         buffer.setCursorPos(cX, xY)
  315.         if bVisible then
  316.             local changed = updatePalette()
  317.             redraw(changed)
  318.             updateCursor()
  319.         end
  320.     end
  321.  
  322.     canvasTerm.blinker = function()
  323.         if bVisible then
  324.             blinker()
  325.         end
  326.     end
  327.  
  328.     canvasTerm.reposition = function( nNewX, nNewY, nNewWidth, nNewHeight, nNewScale, newParent )
  329.         if nNewX ~= nil or nNewY ~= nil then
  330.             expect(1, nNewX, "number")
  331.             expect(2, nNewY, "number")
  332.         end
  333.         if nNewWidth ~= nil or nNewHeight ~= nil then
  334.             expect(3, nNewWidth, "number")
  335.             expect(4, nNewHeight, "number")
  336.         end
  337.         expect(5, nNewScale, "number", "nil")
  338.         expect(6, newParent, "table", "nil")
  339.  
  340.         if bVisible then
  341.             destroy()
  342.         end
  343.  
  344.         nX = nNewX or nX
  345.         nY = nNewY or nY
  346.         nWidth = nNewWidth or nWidth
  347.         nHeight = nNewHeight or nHeight
  348.         nScale = nNewScale or nScale
  349.         parent = newParent or parent
  350.  
  351.         group.remove()
  352.         group = parent.addGroup({nX, nY})
  353.         bufferResize(nWidth, nHeight)
  354.  
  355.         if bVisible then
  356.             build()
  357.             redraw()
  358.             updateCursor()
  359.         end
  360.     end
  361.  
  362.     if bVisible then
  363.         build()
  364.         redraw()
  365.         updateCursor()
  366.     end
  367.  
  368.     return canvasTerm
  369. end
  370.  
  371. --### Finalizing
  372. return c
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement