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)