· last year · Jul 09, 2024, 02:55 PM
1-- Code modified for the printer. Yes, i just copied paint
2
3-- Paint created by nitrogenfingers (edited by dan200)
4-- http://www.youtube.com/user/NitrogenFingers
5
6------------
7-- Fields --
8------------
9
10-- The width and height of the terminal
11local w, h = term.getSize()
12
13-- The selected colours on the left and right mouse button, and the colour of the canvas
14local leftColour, rightColour = colours.white, nil
15local canvasColour = colours.black
16
17-- The values stored in the canvas
18local canvas = {}
19
20-- The menu options
21local mChoices = { "Save", "Exit" }
22
23-- The message displayed in the footer bar
24local fMessage = ""
25
26-------------------------
27-- Initialisation --
28-------------------------
29
30-- Determine if we can even run this
31if not term.isColour() then
32 print("Requires an Advanced Computer")
33 return
34end
35
36-- Determines if the file exists, and can be edited on this computer
37local tArgs = { ... }
38if #tArgs == 0 then
39 local programName = arg[0] or fs.getName(shell.getRunningProgram())
40 print("Usage: " .. programName .. " <path>")
41 return
42end
43local sPath = shell.resolve(tArgs[1])
44local bReadOnly = fs.isReadOnly(sPath)
45if fs.exists(sPath) and fs.isDir(sPath) then
46 print("Cannot edit a directory.")
47 return
48end
49
50-- Create .nfp files by default
51if not fs.exists(sPath) and not string.find(sPath, "%.") then
52 local sExtension = settings.get("paint.default_extension")
53 if sExtension ~= "" and type(sExtension) == "string" then
54 sPath = sPath .. "." .. sExtension
55 end
56end
57
58
59---------------
60-- Functions --
61---------------
62
63local function getCanvasPixel(x, y)
64 if canvas[y] then
65 return canvas[y][x]
66 end
67 return nil
68end
69
70--[[
71 Converts a colour value to a text character
72 params: colour = the number to convert to a hex value
73 returns: a string representing the chosen colour
74]]
75local function getCharOf(colour)
76 -- Incorrect values always convert to nil
77 if type(colour) == "number" then
78 local value = math.floor(math.log(colour) / math.log(2)) + 1
79 if value >= 1 and value <= 16 then
80 return string.sub("0123456789abcdef", value, value)
81 end
82 end
83 return " "
84end
85
86--[[
87 Converts a text character to colour value
88 params: char = the char (from string.byte) to convert to number
89 returns: the colour number of the hex value
90]]
91local tColourLookup = {}
92for n = 1, 16 do
93 tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1)
94end
95local function getColourOf(char)
96 -- Values not in the hex table are transparent (canvas coloured)
97 return tColourLookup[char]
98end
99
100--[[
101 Loads the file into the canvas
102 params: path = the path of the file to open
103 returns: nil
104]]
105local function load(path)
106 -- Load the file
107 if fs.exists(path) then
108 local file = fs.open(sPath, "r")
109 local sLine = file.readLine()
110 while sLine do
111 local line = {}
112 for x = 1, w - 2 do
113 line[x] = getColourOf(string.byte(sLine, x, x))
114 end
115 table.insert(canvas, line)
116 sLine = file.readLine()
117 end
118 file.close()
119 end
120end
121
122--[[
123 Saves the current canvas to file
124 params: path = the path of the file to save
125 returns: true if save was successful, false otherwise
126]]
127local function save(path)
128 -- Open file
129 local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))
130 if not fs.exists(sDir) then
131 fs.makeDir(sDir)
132 end
133
134 local file, err = fs.open(path, "w")
135 if not file then
136 return false, err
137 end
138
139 -- Encode (and trim)
140 local tLines = {}
141 local nLastLine = 0
142 for y = 1, h - 1 do
143 local sLine = ""
144 local nLastChar = 0
145 for x = 1, w - 2 do
146 local c = getCharOf(getCanvasPixel(x, y))
147 sLine = sLine .. c
148 if c ~= " " then
149 nLastChar = x
150 end
151 end
152 sLine = string.sub(sLine, 1, nLastChar)
153 tLines[y] = sLine
154 if #sLine > 0 then
155 nLastLine = y
156 end
157 end
158
159 -- Save out
160 for n = 1, nLastLine do
161 file.writeLine(tLines[n])
162 end
163 file.close()
164 return true
165end
166
167--[[
168 Draws colour picker sidebar, the pallette and the footer
169 returns: nil
170]]
171local function drawInterface()
172 -- Footer
173 term.setCursorPos(1, h)
174 term.setBackgroundColour(colours.black)
175 term.setTextColour(colours.yellow)
176 term.clearLine()
177 term.write(fMessage)
178
179 -- Colour Picker
180 for i = 1, 16 do
181 term.setCursorPos(w - 1, i)
182 term.setBackgroundColour(2 ^ (i - 1))
183 term.write(" ")
184 end
185
186 term.setCursorPos(w - 1, 17)
187 term.setBackgroundColour(canvasColour)
188 term.setTextColour(colours.grey)
189 term.write("\127\127")
190
191 -- Left and Right Selected Colours
192 do
193 term.setCursorPos(w - 1, 18)
194 if leftColour ~= nil then
195 term.setBackgroundColour(leftColour)
196 term.write(" ")
197 else
198 term.setBackgroundColour(canvasColour)
199 term.setTextColour(colours.grey)
200 term.write("\127")
201 end
202 if rightColour ~= nil then
203 term.setBackgroundColour(rightColour)
204 term.write(" ")
205 else
206 term.setBackgroundColour(canvasColour)
207 term.setTextColour(colours.grey)
208 term.write("\127")
209 end
210 end
211
212 -- Padding
213 term.setBackgroundColour(canvasColour)
214 for i = 20, h - 1 do
215 term.setCursorPos(w - 1, i)
216 term.write(" ")
217 end
218end
219
220--[[
221 Converts a single pixel of a single line of the canvas and draws it
222 returns: nil
223]]
224local function drawCanvasPixel(x, y)
225 local pixel = getCanvasPixel(x, y)
226 if pixel then
227 term.setBackgroundColour(pixel or canvasColour)
228 term.setCursorPos(x, y)
229 term.write(" ")
230 else
231 term.setBackgroundColour(canvasColour)
232 term.setTextColour(colours.grey)
233 term.setCursorPos(x, y)
234 term.write("\127")
235 end
236end
237
238local color_hex_lookup = {}
239for i = 0, 15 do
240 color_hex_lookup[2 ^ i] = string.format("%x", i)
241end
242
243--[[
244 Converts each colour in a single line of the canvas and draws it
245 returns: nil
246]]
247local function drawCanvasLine(y)
248 local text, fg, bg = "", "", ""
249 for x = 1, w - 2 do
250 local pixel = getCanvasPixel(x, y)
251 if pixel then
252 text = text .. " "
253 fg = fg .. "0"
254 bg = bg .. color_hex_lookup[pixel or canvasColour]
255 else
256 text = text .. "\127"
257 fg = fg .. color_hex_lookup[colours.grey]
258 bg = bg .. color_hex_lookup[canvasColour]
259 end
260 end
261
262 term.setCursorPos(1, y)
263 term.blit(text, fg, bg)
264end
265
266--[[
267 Converts each colour in the canvas and draws it
268 returns: nil
269]]
270local function drawCanvas()
271 for y = 1, h - 1 do
272 drawCanvasLine(y)
273 end
274end
275
276local menu_choices = {
277 Save = function()
278 if bReadOnly then
279 fMessage = "Access denied"
280 return false
281 end
282 local success, err = save(sPath)
283 if success then
284 fMessage = "Saved to " .. sPath
285 else
286 if err then
287 fMessage = "Error saving to " .. err
288 else
289 fMessage = "Error saving to " .. sPath
290 end
291 end
292 return false
293 end,
294 Exit = function()
295 sleep(0) -- Super janky, but consumes stray "char" events from pressing Ctrl then E separately.
296 return true
297 end,
298}
299
300--[[
301 Draws menu options and handles input from within the menu.
302 returns: true if the program is to be exited; false otherwise
303]]
304local function accessMenu()
305 -- Selected menu option
306 local selection = 1
307
308 term.setBackgroundColour(colours.black)
309
310 while true do
311 -- Draw the menu
312 term.setCursorPos(1, h)
313 term.clearLine()
314 term.setTextColour(colours.white)
315 for k, v in pairs(mChoices) do
316 if selection == k then
317 term.setTextColour(colours.yellow)
318 term.write("[")
319 term.setTextColour(colours.white)
320 term.write(v)
321 term.setTextColour(colours.yellow)
322 term.write("]")
323 term.setTextColour(colours.white)
324 else
325 term.write(" " .. v .. " ")
326 end
327 end
328
329 -- Handle input in the menu
330 local id, param1, param2, param3 = os.pullEvent()
331 if id == "key" then
332 local key = param1
333
334 -- Handle menu shortcuts.
335 for _, menu_item in ipairs(mChoices) do
336 local k = keys[menu_item:sub(1, 1):lower()]
337 if k and k == key then
338 return menu_choices[menu_item]()
339 end
340 end
341
342 if key == keys.right then
343 -- Move right
344 selection = selection + 1
345 if selection > #mChoices then
346 selection = 1
347 end
348
349 elseif key == keys.left and selection > 1 then
350 -- Move left
351 selection = selection - 1
352 if selection < 1 then
353 selection = #mChoices
354 end
355
356 elseif key == keys.enter or key == keys.numPadEnter then
357 -- Select an option
358 return menu_choices[mChoices[selection]]()
359 elseif key == keys.leftCtrl or keys == keys.rightCtrl then
360 -- Cancel the menu
361 return false
362 end
363 elseif id == "mouse_click" then
364 local cx, cy = param2, param3
365 if cy ~= h then return false end -- Exit the menu
366
367 local nMenuPosEnd = 1
368 local nMenuPosStart = 1
369 for _, sMenuItem in ipairs(mChoices) do
370 nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1
371 if cx > nMenuPosStart and cx < nMenuPosEnd then
372 return menu_choices[sMenuItem]()
373 end
374 nMenuPosEnd = nMenuPosEnd + 1
375 nMenuPosStart = nMenuPosEnd
376 end
377 end
378 end
379end
380
381--[[
382 Runs the main thread of execution. Draws the canvas and interface, and handles
383 mouse and key events.
384 returns: nil
385]]
386local function handleEvents()
387 local programActive = true
388 while programActive do
389 local id, p1, p2, p3 = os.pullEvent()
390 if id == "redstone" then
391 save(tArgs[1])
392 return
393 end
394 if id == "mouse_click" or id == "mouse_drag" then
395 if p2 >= w - 1 and p3 >= 1 and p3 <= 17 then
396 if id ~= "mouse_drag" then
397 -- Selecting an items in the colour picker
398 if p3 <= 16 then
399 if p1 == 1 then
400 leftColour = 2 ^ (p3 - 1)
401 else
402 rightColour = 2 ^ (p3 - 1)
403 end
404 else
405 if p1 == 1 then
406 leftColour = nil
407 else
408 rightColour = nil
409 end
410 end
411 --drawCanvas()
412 drawInterface()
413 end
414 elseif p2 < w - 1 and p3 <= h - 1 then
415 -- Clicking on the canvas
416 local paintColour = nil
417 if p1 == 1 then
418 paintColour = leftColour
419 elseif p1 == 2 then
420 paintColour = rightColour
421 end
422 if not canvas[p3] then
423 canvas[p3] = {}
424 end
425 canvas[p3][p2] = paintColour
426
427 drawCanvasPixel(p2, p3)
428 elseif p3 == h and id == "mouse_click" then
429 -- Open menu
430 programActive = not accessMenu()
431 drawInterface()
432 end
433 elseif id == "key" then
434 if p1 == keys.leftCtrl or p1 == keys.rightCtrl then
435 programActive = not accessMenu()
436 drawInterface()
437 end
438 elseif id == "term_resize" then
439 w, h = term.getSize()
440 drawCanvas()
441 drawInterface()
442 end
443 end
444end
445
446-- Init
447load(sPath)
448drawCanvas()
449drawInterface()
450
451-- Main loop
452handleEvents()
453
454-- Shutdown
455term.setBackgroundColour(colours.black)
456term.setTextColour(colours.white)
457term.clear()
458term.setCursorPos(1, 1)
459