Difference between revisions of "Paint"

From ComputerCraft Wiki
Jump to: navigation, search
m (Not Relevent)
m (Code: you don't need code tags before a indented code segment, though it really is a lot better to just use <pre> here (and switching tabs to spaces since MediaWiki's tabs look terrible (8 spaces))
Line 8: Line 8:
 
The paint program uses the following code:
 
The paint program uses the following code:
  
<code>
+
<pre>
-- Paint created by nitrogenfingers (edited by dan200)
+
-- Paint created by nitrogenfingers (edited by dan200)
-- http://www.youtube.com/user/NitrogenFingers
+
-- http://www.youtube.com/user/NitrogenFingers
+
 
------------
+
------------
-- Fields --
+
-- Fields --
------------
+
------------
+
 
-- The width and height of the terminal
+
-- The width and height of the terminal
local w,h = term.getSize()
+
local w,h = term.getSize()
+
 
-- The selected colours on the left and right mouse button, and the colour of the canvas
+
-- The selected colours on the left and right mouse button, and the colour of the canvas
local leftColour, rightColour = colours.white, nil
+
local leftColour, rightColour = colours.white, nil
local canvasColour = colours.black
+
local canvasColour = colours.black
+
 
-- The values stored in the canvas
+
-- The values stored in the canvas
local canvas = {}
+
local canvas = {}
+
 
-- The menu options
+
-- The menu options
local mChoices = { "Save","Exit" }
+
local mChoices = { "Save","Exit" }
+
 
-- The message displayed in the footer bar
+
-- The message displayed in the footer bar
local fMessage = "Press Ctrl to access menu"
+
local fMessage = "Press Ctrl to access menu"
+
 
-------------------------
+
-------------------------
-- Initialisation --
+
-- Initialisation --
-------------------------
+
-------------------------
+
 
-- Determine if we can even run this
+
-- Determine if we can even run this
if not term.isColour() then
+
if not term.isColour() then
print("Requires an Advanced Computer")
+
    print("Requires an Advanced Computer")
return
+
    return
end
+
end
+
 
-- Determines if the file exists, and can be edited on this computer
+
-- Determines if the file exists, and can be edited on this computer
local tArgs = {...}
+
local tArgs = {...}
if #tArgs == 0 then
+
if #tArgs == 0 then
print("Usage: paint <path>")
+
    print("Usage: paint <path>")
return
+
    return
end
+
end
local sPath = shell.resolve(tArgs[1])
+
local sPath = shell.resolve(tArgs[1])
local bReadOnly = fs.isReadOnly(sPath)
+
local bReadOnly = fs.isReadOnly(sPath)
if fs.exists(sPath) and fs.isDir(sPath) then
+
if fs.exists(sPath) and fs.isDir(sPath) then
print("Cannot edit a directory.")
+
    print("Cannot edit a directory.")
return
+
    return
end
+
end
+
 
---------------
+
---------------
-- Functions --
+
-- Functions --
---------------
+
---------------
+
 
local function getCanvasPixel( x, y )
+
local function getCanvasPixel( x, y )
    if canvas[y] then
+
if canvas[y] then
        return canvas[y][x]
+
return canvas[y][x]
    end
+
end
    return nil
+
return nil
end
+
end
+
 
--[[
+
--[[
Converts a colour value to a text character
+
    Converts a colour value to a text character
params: colour = the number to convert to a hex value
+
    params: colour = the number to convert to a hex value
returns: a string representing the chosen colour
+
    returns: a string representing the chosen colour
]]
+
]]
local function getCharOf( colour )
+
local function getCharOf( colour )
-- Incorrect values always convert to nil
+
    -- Incorrect values always convert to nil
if type(colour) == "number" then
+
    if type(colour) == "number" then
local value = math.floor( math.log(colour) / math.log(2) ) + 1
+
        local value = math.floor( math.log(colour) / math.log(2) ) + 1
if value >= 1 and value <= 16 then
+
        if value >= 1 and value <= 16 then
return string.sub( "0123456789abcdef", value, value )
+
            return string.sub( "0123456789abcdef", value, value )
end
+
        end
end
+
    end
return " "
+
    return " "
end
+
end  
+
 
--[[
+
--[[
Converts a text character to colour value
+
    Converts a text character to colour value
params: char = the char (from string.byte) to convert to number
+
    params: char = the char (from string.byte) to convert to number
returns: the colour number of the hex value
+
    returns: the colour number of the hex value
]]
+
]]
local tColourLookup = {}
+
local tColourLookup = {}
for n=1,16 do
+
for n=1,16 do
tColourLookup[ string.byte( "0123456789abcdef",n,n ) ] = 2^(n-1)
+
    tColourLookup[ string.byte( "0123456789abcdef",n,n ) ] = 2^(n-1)
end
+
end
local function getColourOf( char )
+
local function getColourOf( char )
-- Values not in the hex table are transparent (canvas coloured)
+
    -- Values not in the hex table are transparent (canvas coloured)
return tColourLookup[char]
+
    return tColourLookup[char]
end
+
end
+
 
--[[  
+
--[[  
Loads the file into the canvas
+
    Loads the file into the canvas
params: path = the path of the file to open
+
    params: path = the path of the file to open
returns: nil
+
    returns: nil
]]
+
]]
local function load(path)
+
local function load(path)
-- Load the file
+
    -- Load the file
if fs.exists(path) then
+
    if fs.exists(path) then
local file = fs.open(sPath, "r")
+
        local file = fs.open(sPath, "r")
local sLine = file.readLine()
+
        local sLine = file.readLine()
while sLine do
+
        while sLine do
local line = {}
+
            local line = {}
for x=1,w-2 do
+
            for x=1,w-2 do
line[x] = getColourOf( string.byte(sLine,x,x) )
+
                line[x] = getColourOf( string.byte(sLine,x,x) )
end
+
            end
table.insert( canvas, line )
+
            table.insert( canvas, line )
sLine = file.readLine()
+
            sLine = file.readLine()
end
+
        end
file.close()
+
        file.close()
end
+
    end
end
+
end
+
 
--[[   
+
--[[   
Saves the current canvas to file   
+
    Saves the current canvas to file   
params: path = the path of the file to save
+
    params: path = the path of the file to save
returns: true if save was successful, false otherwise
+
    returns: true if save was successful, false otherwise
]]
+
]]
local function save(path)
+
local function save(path)
    -- Open file
+
-- Open file
local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))
+
    local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))
if not fs.exists(sDir) then
+
    if not fs.exists(sDir) then
fs.makeDir(sDir)
+
        fs.makeDir(sDir)
end
+
    end
+
 
local file = fs.open( path, "w" )
+
    local file = fs.open( path, "w" )
if not file then
+
    if not file then
    return false
+
        return false
end
+
    end
+
 
    -- Encode (and trim)
+
-- Encode (and trim)
local tLines = {}
+
    local tLines = {}
local nLastLine = 0
+
    local nLastLine = 0
for y=1,h-1 do
+
    for y=1,h-1 do
    local sLine = ""
+
        local sLine = ""
    local nLastChar = 0
+
        local nLastChar = 0
for x=1,w-2 do
+
        for x=1,w-2 do
    local c = getCharOf( getCanvasPixel( x, y ) )
+
            local c = getCharOf( getCanvasPixel( x, y ) )
    sLine = sLine .. c
+
            sLine = sLine .. c
    if c ~= " " then
+
            if c ~= " " then
        nLastChar = x
+
                nLastChar = x
    end
+
            end
end
+
        end
sLine = string.sub( sLine, 1, nLastChar )
+
        sLine = string.sub( sLine, 1, nLastChar )
tLines[y] = sLine
+
        tLines[y] = sLine
if string.len( sLine ) > 0 then
+
        if string.len( sLine ) > 0 then
    nLastLine = y
+
            nLastLine = y
end
+
        end
end
+
    end
+
 
    -- Save out
+
-- Save out
for n=1,nLastLine do
+
    for n=1,nLastLine do
        file.writeLine( tLines[ n ] )
+
        file.writeLine( tLines[ n ] )
end
+
    end
file.close()
+
    file.close()
return true
+
    return true
end
+
end
+
 
--[[   
+
--[[   
Draws colour picker sidebar, the pallette and the footer
+
    Draws colour picker sidebar, the pallette and the footer
returns: nil
+
    returns: nil
]]
+
]]
local function drawInterface()
+
local function drawInterface()
-- Footer
+
    -- Footer
term.setCursorPos(1, h)
+
    term.setCursorPos(1, h)
term.setBackgroundColour(colours.black)
+
    term.setBackgroundColour(colours.black)
term.setTextColour(colours.yellow)
+
    term.setTextColour(colours.yellow)
term.clearLine()
+
    term.clearLine()
term.write(fMessage)
+
    term.write(fMessage)
+
   
-- Colour Picker
+
    -- Colour Picker
for i=1,16 do
+
    for i=1,16 do
term.setCursorPos(w-1, i)
+
        term.setCursorPos(w-1, i)
term.setBackgroundColour( 2^(i-1) )
+
        term.setBackgroundColour( 2^(i-1) )
term.write("  ")
+
        term.write("  ")
end
+
    end
+
 
term.setCursorPos(w-1, 17)
+
    term.setCursorPos(w-1, 17)
term.setBackgroundColour( canvasColour )
+
    term.setBackgroundColour( canvasColour )
term.setTextColour( colours.grey )
+
    term.setTextColour( colours.grey )
term.write("XX")
+
    term.write("XX")
+
           
-- Left and Right Selected Colours
+
    -- Left and Right Selected Colours
for i=18,18 do
+
    for i=18,18 do
term.setCursorPos(w-1, i)
+
        term.setCursorPos(w-1, i)
if leftColour ~= nil then
+
        if leftColour ~= nil then
term.setBackgroundColour( leftColour )
+
            term.setBackgroundColour( leftColour )
term.write(" ")
+
            term.write(" ")
else
+
        else
term.setBackgroundColour( canvasColour )
+
            term.setBackgroundColour( canvasColour )
term.setTextColour( colours.grey )
+
            term.setTextColour( colours.grey )
term.write("X")
+
            term.write("X")
end
+
        end
if rightColour ~= nil then
+
        if rightColour ~= nil then
term.setBackgroundColour( rightColour )
+
            term.setBackgroundColour( rightColour )
term.write(" ")
+
            term.write(" ")
else
+
        else
term.setBackgroundColour( canvasColour )
+
            term.setBackgroundColour( canvasColour )
term.setTextColour( colours.grey )
+
            term.setTextColour( colours.grey )
term.write("X")
+
            term.write("X")
end
+
        end
end
+
    end
+
 
-- Padding
+
    -- Padding
term.setBackgroundColour( canvasColour )
+
    term.setBackgroundColour( canvasColour )
for i=20,h-1 do
+
    for i=20,h-1 do
term.setCursorPos(w-1, i)
+
        term.setCursorPos(w-1, i)
term.write("  ")
+
        term.write("  ")
end
+
    end
end
+
end
+
 
--[[   
+
--[[   
Converts a single pixel of a single line of the canvas and draws it
+
    Converts a single pixel of a single line of the canvas and draws it
returns: nil
+
    returns: nil
]]
+
]]
local function drawCanvasPixel( x, y )
+
local function drawCanvasPixel( x, y )
local pixel = getCanvasPixel( x, y )
+
    local pixel = getCanvasPixel( x, y )
if pixel then
+
    if pixel then
term.setBackgroundColour( pixel or canvasColour )
+
        term.setBackgroundColour( pixel or canvasColour )
term.setCursorPos(x, y)
+
        term.setCursorPos(x, y)
term.write(" ")
+
        term.write(" ")
else
+
    else
term.setBackgroundColour( canvasColour )
+
        term.setBackgroundColour( canvasColour )
term.setTextColour( colours.grey )
+
        term.setTextColour( colours.grey )
term.setCursorPos(x, y)
+
        term.setCursorPos(x, y)
        term.write("-")
+
term.write("-")
end
+
    end
end
+
end
+
 
--[[   
+
--[[   
Converts each colour in a single line of the canvas and draws it
+
    Converts each colour in a single line of the canvas and draws it
returns: nil
+
    returns: nil
]]
+
]]
local function drawCanvasLine( y )
+
local function drawCanvasLine( y )
for x = 1, w-2 do
+
    for x = 1, w-2 do
drawCanvasPixel( x, y )
+
        drawCanvasPixel( x, y )
end
+
    end
end
+
end
+
 
--[[   
+
--[[   
Converts each colour in the canvas and draws it
+
    Converts each colour in the canvas and draws it
returns: nil
+
    returns: nil
]]
+
]]
local function drawCanvas()
+
local function drawCanvas()
for y = 1, h-1 do
+
    for y = 1, h-1 do
drawCanvasLine( y )
+
        drawCanvasLine( y )
end
+
    end
end
+
end
+
 
--[[
+
--[[
Draws menu options and handles input from within the menu.
+
    Draws menu options and handles input from within the menu.
returns: true if the program is to be exited; false otherwise
+
    returns: true if the program is to be exited; false otherwise
]]
+
]]
local function accessMenu()
+
local function accessMenu()
-- Selected menu option
+
    -- Selected menu option
local selection = 1
+
    local selection = 1
+
   
term.setBackgroundColour(colours.black)
+
    term.setBackgroundColour(colours.black)
while true do
+
    while true do
-- Draw the menu
+
        -- Draw the menu
term.setCursorPos(1,h)
+
        term.setCursorPos(1,h)
term.clearLine()
+
        term.clearLine()
term.setTextColour(colours.white)
+
        term.setTextColour(colours.white)
for k,v in pairs(mChoices) do
+
        for k,v in pairs(mChoices) do
if selection==k then  
+
            if selection==k then  
term.setTextColour(colours.yellow)
+
                term.setTextColour(colours.yellow)
local ox,_ = term.getCursorPos()
+
                local ox,_ = term.getCursorPos()
term.write("["..string.rep(" ",#v).."]")
+
                term.write("["..string.rep(" ",#v).."]")
term.setCursorPos(ox+1,h)
+
                term.setCursorPos(ox+1,h)
term.setTextColour(colours.white)
+
                term.setTextColour(colours.white)
term.write(v)
+
                term.write(v)
term.setCursorPos(term.getCursorPos()+1,h)
+
                term.setCursorPos(term.getCursorPos()+1,h)
else
+
            else
term.write(" "..v.." ")
+
                term.write(" "..v.." ")
end
+
            end
end
+
        end
+
       
-- Handle input in the menu
+
        -- Handle input in the menu
local id,key = os.pullEvent("key")
+
        local id,key = os.pullEvent("key")
if id == "key" then
+
        if id == "key" then
-- S and E are shortcuts
+
            -- S and E are shortcuts
if key == keys.s then
+
            if key == keys.s then
selection = 1
+
                selection = 1
key = keys.enter
+
                key = keys.enter
elseif key == keys.e then
+
            elseif key == keys.e then
selection = 2
+
                selection = 2
key = keys.enter
+
                key = keys.enter
end
+
            end
+
       
if key == keys.right then
+
            if key == keys.right then
-- Move right
+
                -- Move right
selection = selection + 1
+
                selection = selection + 1
if selection > #mChoices then
+
                if selection > #mChoices then
selection = 1
+
                    selection = 1
end
+
                end
+
               
elseif key == keys.left and selection > 1 then
+
            elseif key == keys.left and selection > 1 then
-- Move left
+
                -- Move left
selection = selection - 1
+
                selection = selection - 1
if selection < 1 then
+
                if selection < 1 then
selection = #mChoices
+
                    selection = #mChoices
end
+
                end
+
               
elseif key == keys.enter then
+
            elseif key == keys.enter then
-- Select an option
+
                -- Select an option
if mChoices[selection]=="Save" then  
+
                if mChoices[selection]=="Save" then  
if bReadOnly then  
+
                    if bReadOnly then  
fMessage = "Access Denied"
+
                        fMessage = "Access Denied"
return false
+
                        return false
end
+
                    end
local success = save(sPath)
+
                    local success = save(sPath)
if success then
+
                    if success then
fMessage = "Saved to "..sPath
+
                        fMessage = "Saved to "..sPath
else
+
                    else
fMessage = "Error saving to "..sPath
+
                        fMessage = "Error saving to "..sPath
end
+
                    end
return false
+
                    return false
elseif mChoices[selection]=="Exit" then  
+
                elseif mChoices[selection]=="Exit" then  
return true
+
                    return true
end
+
                end
elseif key == keys.leftCtrl or keys == keys.rightCtrl then
+
            elseif key == keys.leftCtrl or keys == keys.rightCtrl then
-- Cancel the menu
+
                -- Cancel the menu
return false  
+
                return false  
end
+
            end
end
+
        end
end
+
    end
end
+
end
+
 
--[[   
+
--[[   
Runs the main thread of execution. Draws the canvas and interface, and handles
+
    Runs the main thread of execution. Draws the canvas and interface, and handles
mouse and key events.
+
    mouse and key events.
returns: nil
+
    returns: nil
]]
+
]]
local function handleEvents()
+
local function handleEvents()
local programActive = true
+
    local programActive = true
while programActive do
+
    while programActive do
local id,p1,p2,p3 = os.pullEvent()
+
        local id,p1,p2,p3 = os.pullEvent()
if id=="mouse_click" or id=="mouse_drag" then
+
        if id=="mouse_click" or id=="mouse_drag" then
if p2 >= w-1 and p3 >= 1 and p3 <= 17 then
+
            if p2 >= w-1 and p3 >= 1 and p3 <= 17 then
if id ~= "mouse_drag" then
+
                if id ~= "mouse_drag" then
-- Selecting an items in the colour picker
+
                    -- Selecting an items in the colour picker
if p3 <= 16 then
+
                    if p3 <= 16 then
if p1==1 then
+
                        if p1==1 then
leftColour = 2^(p3-1)
+
                            leftColour = 2^(p3-1)
else
+
                        else
rightColour = 2^(p3-1)
+
                            rightColour = 2^(p3-1)
end
+
                        end
else
+
                    else
if p1==1 then
+
                        if p1==1 then
leftColour = nil
+
                            leftColour = nil
else
+
                        else
rightColour = nil
+
                            rightColour = nil
end
+
                        end
end
+
                    end
--drawCanvas()
+
                    --drawCanvas()
drawInterface()
+
                    drawInterface()
end
+
                end
elseif p2 < w-1 and p3 <= h-1 then
+
            elseif p2 < w-1 and p3 <= h-1 then
-- Clicking on the canvas
+
                -- Clicking on the canvas
local paintColour = nil
+
                local paintColour = nil
if p1==1 then
+
                if p1==1 then
paintColour = leftColour
+
                    paintColour = leftColour
elseif p1==2 then
+
                elseif p1==2 then
paintColour = rightColour
+
                    paintColour = rightColour
end
+
                end
if not canvas[p3] then
+
                if not canvas[p3] then
                    canvas[p3] = {}
+
canvas[p3] = {}
    end
+
            end
                canvas[p3][p2] = paintColour
+
canvas[p3][p2] = paintColour
+
 
drawCanvasPixel( p2, p3 )
+
                drawCanvasPixel( p2, p3 )
end
+
            end
elseif id=="key" then
+
        elseif id=="key" then
if p1==keys.leftCtrl or p1==keys.rightCtrl then
+
            if p1==keys.leftCtrl or p1==keys.rightCtrl then
programActive = not accessMenu()
+
                programActive = not accessMenu()
drawInterface()
+
                drawInterface()
end
+
            end
elseif id=="term_resize" then
+
        elseif id=="term_resize" then
    w,h = term.getSize()
+
            w,h = term.getSize()
            drawCanvas()
+
drawCanvas()
            drawInterface()
+
drawInterface()
        end
+
end
end
+
    end
end
+
end
+
 
-- Init
+
-- Init
load(sPath)
+
load(sPath)
drawCanvas()
+
drawCanvas()
drawInterface()
+
drawInterface()
+
 
-- Main loop
+
-- Main loop
handleEvents()
+
handleEvents()
+
 
-- Shutdown
+
-- Shutdown
term.setBackgroundColour(colours.black)
+
term.setBackgroundColour(colours.black)
term.setTextColour(colours.white)
+
term.setTextColour(colours.white)
term.clear()
+
term.clear()
term.setCursorPos(1,1)
+
term.setCursorPos(1,1)
</code>
+
</pre>
 +
 
  
 
[[Category:Programs]]
 
[[Category:Programs]]

Revision as of 18:05, 12 November 2015

An advanced computer running Paint, an exclusive program

Paint is a program, created by NitrogenFingers, added in ComputerCraft 1.45. It requires an Advanced Computer, and is used for making images that may be drawn later with the paintutils library.

Running paint on a monitor requires using an advanced monitor. For instructions, see monitor (program).

Code

The paint program uses the following code:

-- Paint created by nitrogenfingers (edited by dan200)
-- http://www.youtube.com/user/NitrogenFingers

------------
-- Fields --
------------

-- The width and height of the terminal
local w,h = term.getSize()

-- The selected colours on the left and right mouse button, and the colour of the canvas
local leftColour, rightColour = colours.white, nil
local canvasColour = colours.black

-- The values stored in the canvas
local canvas = {}

-- The menu options
local mChoices = { "Save","Exit" }

-- The message displayed in the footer bar
local fMessage = "Press Ctrl to access menu"

-------------------------
-- Initialisation --
-------------------------

-- Determine if we can even run this
if not term.isColour() then
    print("Requires an Advanced Computer")
    return
end

-- Determines if the file exists, and can be edited on this computer
local tArgs = {...}
if #tArgs == 0 then
    print("Usage: paint <path>")
    return
end
local sPath = shell.resolve(tArgs[1])
local bReadOnly = fs.isReadOnly(sPath)
if fs.exists(sPath) and fs.isDir(sPath) then
    print("Cannot edit a directory.")
    return
end

---------------
-- Functions --
---------------

local function getCanvasPixel( x, y )
if canvas[y] then
return canvas[y][x]
end
return nil
end

--[[
    Converts a colour value to a text character
    params: colour = the number to convert to a hex value
    returns: a string representing the chosen colour
]]
local function getCharOf( colour )
    -- Incorrect values always convert to nil
    if type(colour) == "number" then
        local value = math.floor( math.log(colour) / math.log(2) ) + 1
        if value >= 1 and value <= 16 then
            return string.sub( "0123456789abcdef", value, value )
        end
    end
    return " "
end    

--[[
    Converts a text character to colour value
    params: char = the char (from string.byte) to convert to number
    returns: the colour number of the hex value
]]
local tColourLookup = {}
for n=1,16 do
    tColourLookup[ string.byte( "0123456789abcdef",n,n ) ] = 2^(n-1)
end
local function getColourOf( char )
    -- Values not in the hex table are transparent (canvas coloured)
    return tColourLookup[char]
end

--[[ 
    Loads the file into the canvas
    params: path = the path of the file to open
    returns: nil
]]
local function load(path)
    -- Load the file
    if fs.exists(path) then
        local file = fs.open(sPath, "r")
        local sLine = file.readLine()
        while sLine do
            local line = {}
            for x=1,w-2 do
                line[x] = getColourOf( string.byte(sLine,x,x) )
            end
            table.insert( canvas, line )
            sLine = file.readLine()
        end
        file.close()
    end
end

--[[  
    Saves the current canvas to file  
    params: path = the path of the file to save
    returns: true if save was successful, false otherwise
]]
local function save(path)
-- Open file
    local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))
    if not fs.exists(sDir) then
        fs.makeDir(sDir)
    end

    local file = fs.open( path, "w" )
    if not file then
        return false
    end

-- Encode (and trim)
    local tLines = {}
    local nLastLine = 0
    for y=1,h-1 do
        local sLine = ""
        local nLastChar = 0
        for x=1,w-2 do
            local c = getCharOf( getCanvasPixel( x, y ) )
            sLine = sLine .. c
            if c ~= " " then
                nLastChar = x
            end
        end
        sLine = string.sub( sLine, 1, nLastChar )
        tLines[y] = sLine
        if string.len( sLine ) > 0 then
            nLastLine = y
        end
    end

-- Save out
    for n=1,nLastLine do
        file.writeLine( tLines[ n ] )
    end
    file.close()
    return true
end

--[[  
    Draws colour picker sidebar, the pallette and the footer
    returns: nil
]]
local function drawInterface()
    -- Footer
    term.setCursorPos(1, h)
    term.setBackgroundColour(colours.black)
    term.setTextColour(colours.yellow)
    term.clearLine()
    term.write(fMessage)
    
    -- Colour Picker
    for i=1,16 do
        term.setCursorPos(w-1, i)
        term.setBackgroundColour( 2^(i-1) )
        term.write("  ")
    end

    term.setCursorPos(w-1, 17)
    term.setBackgroundColour( canvasColour )
    term.setTextColour( colours.grey )
    term.write("XX")
            
    -- Left and Right Selected Colours
    for i=18,18 do
        term.setCursorPos(w-1, i)
        if leftColour ~= nil then
            term.setBackgroundColour( leftColour )
            term.write(" ")
        else
            term.setBackgroundColour( canvasColour )
            term.setTextColour( colours.grey )
            term.write("X")
        end
        if rightColour ~= nil then
            term.setBackgroundColour( rightColour )
            term.write(" ")
        else
            term.setBackgroundColour( canvasColour )
            term.setTextColour( colours.grey )
            term.write("X")
        end
    end

    -- Padding
    term.setBackgroundColour( canvasColour )
    for i=20,h-1 do
        term.setCursorPos(w-1, i)
        term.write("  ")
    end
end

--[[  
    Converts a single pixel of a single line of the canvas and draws it
    returns: nil
]]
local function drawCanvasPixel( x, y )
    local pixel = getCanvasPixel( x, y )
    if pixel then
        term.setBackgroundColour( pixel or canvasColour )
        term.setCursorPos(x, y)
        term.write(" ")
    else
        term.setBackgroundColour( canvasColour )
        term.setTextColour( colours.grey )
        term.setCursorPos(x, y)
term.write("-")
    end
end

--[[  
    Converts each colour in a single line of the canvas and draws it
    returns: nil
]]
local function drawCanvasLine( y )
    for x = 1, w-2 do
        drawCanvasPixel( x, y )
    end
end

--[[  
    Converts each colour in the canvas and draws it
    returns: nil
]]
local function drawCanvas()
    for y = 1, h-1 do
        drawCanvasLine( y )
    end
end

--[[
    Draws menu options and handles input from within the menu.
    returns: true if the program is to be exited; false otherwise
]]
local function accessMenu()
    -- Selected menu option
    local selection = 1
    
    term.setBackgroundColour(colours.black)
    while true do
        -- Draw the menu
        term.setCursorPos(1,h)
        term.clearLine()
        term.setTextColour(colours.white)
        for k,v in pairs(mChoices) do
            if selection==k then 
                term.setTextColour(colours.yellow)
                local ox,_ = term.getCursorPos()
                term.write("["..string.rep(" ",#v).."]")
                term.setCursorPos(ox+1,h)
                term.setTextColour(colours.white)
                term.write(v)
                term.setCursorPos(term.getCursorPos()+1,h)
            else
                term.write(" "..v.." ")
            end
        end
        
        -- Handle input in the menu
        local id,key = os.pullEvent("key")
        if id == "key" then
            -- S and E are shortcuts
            if key == keys.s then
                selection = 1
                key = keys.enter
            elseif key == keys.e then
                selection = 2
                key = keys.enter
            end
        
            if key == keys.right then
                -- Move right
                selection = selection + 1
                if selection > #mChoices then
                    selection = 1
                end
                
            elseif key == keys.left and selection > 1 then
                -- Move left
                selection = selection - 1
                if selection < 1 then
                    selection = #mChoices
                end
                
            elseif key == keys.enter then
                -- Select an option
                if mChoices[selection]=="Save" then 
                    if bReadOnly then 
                        fMessage = "Access Denied"
                        return false
                    end
                    local success = save(sPath)
                    if success then
                        fMessage = "Saved to "..sPath
                    else
                        fMessage = "Error saving to "..sPath
                    end
                    return false
                elseif mChoices[selection]=="Exit" then 
                    return true
                end
            elseif key == keys.leftCtrl or keys == keys.rightCtrl then
                -- Cancel the menu
                return false 
            end
        end
    end
end

--[[  
    Runs the main thread of execution. Draws the canvas and interface, and handles
    mouse and key events.
    returns: nil
]]
local function handleEvents()
    local programActive = true
    while programActive do
        local id,p1,p2,p3 = os.pullEvent()
        if id=="mouse_click" or id=="mouse_drag" then
            if p2 >= w-1 and p3 >= 1 and p3 <= 17 then
                if id ~= "mouse_drag" then
                    -- Selecting an items in the colour picker
                    if p3 <= 16 then
                        if p1==1 then
                            leftColour = 2^(p3-1)
                        else
                            rightColour = 2^(p3-1)
                        end
                    else
                        if p1==1 then
                            leftColour = nil
                        else
                            rightColour = nil
                        end
                    end
                    --drawCanvas()
                    drawInterface()
                end
            elseif p2 < w-1 and p3 <= h-1 then
                -- Clicking on the canvas
                local paintColour = nil
                if p1==1 then
                    paintColour = leftColour
                elseif p1==2 then
                    paintColour = rightColour
                end
                if not canvas[p3] then
canvas[p3] = {}
            end
canvas[p3][p2] = paintColour

                drawCanvasPixel( p2, p3 )
            end
        elseif id=="key" then
            if p1==keys.leftCtrl or p1==keys.rightCtrl then
                programActive = not accessMenu()
                drawInterface()
            end
        elseif id=="term_resize" then
            w,h = term.getSize()
drawCanvas()
drawInterface()
end
    end
end

-- Init
load(sPath)
drawCanvas()
drawInterface()

-- Main loop
handleEvents()

-- Shutdown
term.setBackgroundColour(colours.black)
term.setTextColour(colours.white)
term.clear()
term.setCursorPos(1,1)