Paint

From ComputerCraft Wiki
Jump to: navigation, search
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. This program, along with Advanced Computers is NOT available in Tekkit Classic.

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)

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox