Advertisement
TheProdigy22

better paintutils

Nov 29th, 2020
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.38 KB | None | 0 0
  1. --- An API for advanced systems which can draw pixels and lines, load and draw
  2. -- image files. You can use the `colors` API for easier color manipulation.
  3. --
  4. -- @module paintutils
  5.  
  6. local expect = dofile("rom/modules/main/cc/expect.lua").expect
  7.  
  8. local function toBlit(color)
  9. return string.format("%x", math.floor(math.log(color) / math.log(2)))
  10. end
  11.  
  12. local function drawPixelInternal(xPos, yPos)
  13. term.setCursorPos(xPos, yPos)
  14. term.write(" ")
  15. end
  16.  
  17. local tColourLookup = {}
  18. for n = 1, 16 do
  19. tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1)
  20. end
  21.  
  22. local function parseLine(tImageArg, sLine)
  23. local tLine = {}
  24. for x = 1, sLine:len() do
  25. tLine[x] = tColourLookup[string.byte(sLine, x, x)] or 0
  26. end
  27. table.insert(tImageArg, tLine)
  28. end
  29.  
  30. -- Sorts pairs of startX/startY/endX/endY such that the start is always the min
  31. local function sortCoords(startX, startY, endX, endY)
  32. local minX, maxX, minY, maxY
  33.  
  34. if startX <= endX then
  35. minX, maxX = startX, endX
  36. else
  37. minX, maxX = endX, startX
  38. end
  39.  
  40. if startY <= endY then
  41. minY, maxY = startY, endY
  42. else
  43. minY, maxY = endY, startY
  44. end
  45.  
  46. return minX, maxX, minY, maxY
  47. end
  48.  
  49. --- Parses an image from a multi-line string
  50. --
  51. -- @tparam string image The string containing the raw-image data.
  52. -- @treturn table The parsed image data, suitable for use with
  53. -- @{paintutils.drawImage}.
  54. local function parseImage(image)
  55. expect(1, image, "string")
  56. local tImage = {}
  57. for sLine in (image .. "\n"):gmatch("(.-)\n") do
  58. parseLine(tImage, sLine)
  59. end
  60. return tImage
  61. end
  62.  
  63. --- Loads an image from a file.
  64. --
  65. -- You can create a file suitable for being loaded using the `paint` program.
  66. --
  67. -- @tparam string path The file to load.
  68. --
  69. -- @treturn table|nil The parsed image data, suitable for use with
  70. -- @{paintutils.drawImage}, or `nil` if the file does not exist.
  71. -- @usage Load an image and draw it.
  72. --
  73. -- local image = paintutils.loadImage("test-image.nfp")
  74. -- paintutils.drawImage(image, term.getCursorPos())
  75. local function loadImage(path)
  76. expect(1, path, "string")
  77.  
  78. if fs.exists(path) then
  79. local file = io.open(path, "r")
  80. local sContent = file:read("*a")
  81. file:close()
  82. return parseImage(sContent)
  83. end
  84. return nil
  85. end
  86.  
  87. --- Draws a single pixel to the current term at the specified position.
  88. --
  89. -- Be warned, this may change the position of the cursor and the current
  90. -- background colour. You should not expect either to be preserved.
  91. --
  92. -- @tparam number xPos The x position to draw at, where 1 is the far left.
  93. -- @tparam number yPos The y position to draw at, where 1 is the very top.
  94. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
  95. -- the current background colour if not specified.
  96. local function drawPixel(xPos, yPos, colour)
  97. expect(1, xPos, "number")
  98. expect(2, yPos, "number")
  99. expect(3, colour, "number", "nil")
  100.  
  101. if colour then
  102. term.setBackgroundColor(colour)
  103. end
  104. return drawPixelInternal(xPos, yPos)
  105. end
  106.  
  107. --- Draws a straight line from the start to end position.
  108. --
  109. -- Be warned, this may change the position of the cursor and the current
  110. -- background colour. You should not expect either to be preserved.
  111. --
  112. -- @tparam number startX The starting x position of the line.
  113. -- @tparam number startY The starting y position of the line.
  114. -- @tparam number endX The end x position of the line.
  115. -- @tparam number endY The end y position of the line.
  116. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
  117. -- the current background colour if not specified.
  118. -- @usage paintutils.drawLine(2, 3, 30, 7, colors.red)
  119. local function drawLine(startX, startY, endX, endY, colour)
  120. expect(1, startX, "number")
  121. expect(2, startY, "number")
  122. expect(3, endX, "number")
  123. expect(4, endY, "number")
  124. expect(5, colour, "number", "nil")
  125.  
  126. startX = math.floor(startX)
  127. startY = math.floor(startY)
  128. endX = math.floor(endX)
  129. endY = math.floor(endY)
  130.  
  131. if colour then
  132. term.setBackgroundColor(colour)
  133. end
  134. if startX == endX and startY == endY then
  135. drawPixelInternal(startX, startY)
  136. return
  137. end
  138.  
  139. local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
  140.  
  141. -- TODO: clip to screen rectangle?
  142.  
  143. local xDiff = maxX - minX
  144. local yDiff = maxY - minY
  145.  
  146. if xDiff > math.abs(yDiff) then
  147. local y = minY
  148. local dy = yDiff / xDiff
  149. for x = minX, maxX do
  150. drawPixelInternal(x, math.floor(y + 0.5))
  151. y = y + dy
  152. end
  153. else
  154. local x = minX
  155. local dx = xDiff / yDiff
  156. if maxY >= minY then
  157. for y = minY, maxY do
  158. drawPixelInternal(math.floor(x + 0.5), y)
  159. x = x + dx
  160. end
  161. else
  162. for y = minY, maxY, -1 do
  163. drawPixelInternal(math.floor(x + 0.5), y)
  164. x = x - dx
  165. end
  166. end
  167. end
  168. end
  169.  
  170. --- Draws the outline of a box on the current term from the specified start
  171. -- position to the specified end position.
  172. --
  173. -- Be warned, this may change the position of the cursor and the current
  174. -- background colour. You should not expect either to be preserved.
  175. --
  176. -- @tparam number startX The starting x position of the line.
  177. -- @tparam number startY The starting y position of the line.
  178. -- @tparam number endX The end x position of the line.
  179. -- @tparam number endY The end y position of the line.
  180. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
  181. -- the current background colour if not specified.
  182. -- @usage paintutils.drawBox(2, 3, 30, 7, colors.red)
  183. local function drawBox(startX, startY, endX, endY, nColour)
  184. expect(1, startX, "number")
  185. expect(2, startY, "number")
  186. expect(3, endX, "number")
  187. expect(4, endY, "number")
  188. expect(5, nColour, "number", "nil")
  189.  
  190. startX = math.floor(startX)
  191. startY = math.floor(startY)
  192. endX = math.floor(endX)
  193. endY = math.floor(endY)
  194.  
  195. if nColour then
  196. term.setBackgroundColor(nColour) -- Maintain legacy behaviour
  197. else
  198. nColour = term.getBackgroundColour()
  199. end
  200. local colourHex = toBlit(nColour)
  201.  
  202. if startX == endX and startY == endY then
  203. drawPixelInternal(startX, startY)
  204. return
  205. end
  206.  
  207. local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
  208. local width = maxX - minX + 1
  209.  
  210. for y = minY, maxY do
  211. if y == minY or y == maxY then
  212. term.setCursorPos(minX, y)
  213. term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
  214. else
  215. term.setCursorPos(minX, y)
  216. term.blit(" ", colourHex, colourHex)
  217. term.setCursorPos(maxX, y)
  218. term.blit(" ", colourHex, colourHex)
  219. end
  220. end
  221. end
  222.  
  223. --- Draws a filled box on the current term from the specified start position to
  224. -- the specified end position.
  225. --
  226. -- Be warned, this may change the position of the cursor and the current
  227. -- background colour. You should not expect either to be preserved.
  228. --
  229. -- @tparam number startX The starting x position of the line.
  230. -- @tparam number startY The starting y position of the line.
  231. -- @tparam number endX The end x position of the line.
  232. -- @tparam number endY The end y position of the line.
  233. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
  234. -- the current background colour if not specified.
  235. -- @usage paintutils.drawFilledBox(2, 3, 30, 7, colors.red)
  236. local function drawFilledBox(startX, startY, endX, endY, nColour)
  237. expect(1, startX, "number")
  238. expect(2, startY, "number")
  239. expect(3, endX, "number")
  240. expect(4, endY, "number")
  241. expect(5, nColour, "number", "nil")
  242.  
  243. startX = math.floor(startX)
  244. startY = math.floor(startY)
  245. endX = math.floor(endX)
  246. endY = math.floor(endY)
  247.  
  248. if nColour then
  249. term.setBackgroundColor(nColour) -- Maintain legacy behaviour
  250. else
  251. nColour = term.getBackgroundColour()
  252. end
  253. local colourHex = toBlit(nColour)
  254.  
  255. if startX == endX and startY == endY then
  256. drawPixelInternal(startX, startY)
  257. return
  258. end
  259.  
  260. local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
  261. local width = maxX - minX + 1
  262.  
  263. for y = minY, maxY do
  264. term.setCursorPos(minX, y)
  265. term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
  266. end
  267. end
  268.  
  269. --- Draw an image loaded by @{paintutils.parseImage} or @{paintutils.loadImage}.
  270. --
  271. -- @tparam table image The parsed image data.
  272. -- @tparam number xPos The x position to start drawing at.
  273. -- @tparam number xPos The y position to start drawing at.
  274. local function drawImage(image, xPos, yPos)
  275. expect(1, image, "table")
  276. expect(2, xPos, "number")
  277. expect(3, yPos, "number")
  278. for y = 1, #image do
  279. local tLine = image[y]
  280. for x = 1, #tLine do
  281. if tLine[x] > 0 then
  282. term.setBackgroundColor(tLine[x])
  283. drawPixelInternal(x + xPos - 1, y + yPos - 1)
  284. end
  285. end
  286. end
  287. end
  288.  
  289. return {
  290. parseImage=parseImage,
  291. loadImage=loadImage,
  292. drawPixel=drawPixel,
  293. drawLine=drawLine,
  294. drawBox=drawBox,
  295. drawFilledBox=drawFilledBox,
  296. drawImage=drawImage
  297. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement