Difference between revisions of "Paint"
From ComputerCraft Wiki
m (Added some links and a bit more info/history) |
KnightMiner (Talk | contribs) m (...) |
||
| (4 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
[[File:AdvancedComputerPaint.png|frame|right|An advanced computer running Paint, an exclusive program]] | [[File:AdvancedComputerPaint.png|frame|right|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_(API)|paintutils library]]. | + | 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_(API)|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: | ||
| + | |||
| + | <pre> | ||
| + | -- 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) | ||
| + | </pre> | ||
[[Category:Programs]] | [[Category:Programs]] | ||
Latest revision as of 18:10, 12 November 2015
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)
