Add functions for parsing and drawing nft (#458)

This commit is contained in:
JakobDev 2020-06-25 10:08:35 +02:00 committed by GitHub
parent 9499654757
commit b54519d0e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 198 additions and 0 deletions

View File

@ -0,0 +1,107 @@
--- Provides utilities for working with "nft" images.
--
-- nft ("Nitrogen Fingers Text") is a file format for drawing basic images.
-- Unlike the images that @{paintutils.parseImage} uses, nft supports coloured
-- text.
--
-- @module cc.image.nft
-- @usage Load an image from `example.nft` and draw it.
--
-- local nft = require "cc.image.nft"
-- local image = assert(nft.load("example.nft"))
-- nft.draw(image)
local expect = require "cc.expect".expect
--- Parse an nft image from a string.
--
-- @tparam string image The image contents.
-- @return table The parsed image.
local function parse(image)
expect(1, image, "string")
local result = {}
local line = 1
local foreground = "0"
local background = "f"
local i, len = 1, #image
while i <= len do
local c = image:sub(i, i)
if c == "\31" and i < len then
i = i + 1
foreground = image:sub(i, i)
elseif c == "\30" and i < len then
i = i + 1
background = image:sub(i, i)
elseif c == "\n" then
if result[line] == nil then
result[line] = { text = "", foreground = "", background = "" }
end
line = line + 1
else
local next = image:find("[\n\30\31]", i) or #image + 1
local seg_len = next - i
local this_line = result[line]
if this_line == nil then
this_line = { foreground = "", background = "", text = "" }
result[line] = this_line
end
this_line.text = this_line.text .. image:sub(i, next - 1)
this_line.foreground = this_line.foreground .. foreground:rep(seg_len)
this_line.background = this_line.background .. background:rep(seg_len)
i = next - 1
end
i = i + 1
end
return result
end
--- Load an nft image from a file.
--
-- @tparam string path The file to load.
-- @treturn[1] table The parsed image.
-- @treturn[2] nil If the file does not exist or could not be loaded.
-- @treturn[2] string An error message explaining why the file could not be
-- loaded.
local function load(path)
expect(1, path, "string")
local file, err = io.open(path, "r")
if not file then return nil, err end
local result = file:read("*a")
file:close()
return parse(result)
end
--- Draw an nft image to the screen.
--
-- @tparam table image An image, as returned from @{load} or @{draw}.
-- @tparam number xPos The x position to start drawing at.
-- @tparam number xPos The y position to start drawing at.
-- @tparam[opt] term.Redirect target The terminal redirect to draw to. Defaults to the
-- current terminal.
local function draw(image, xPos, yPos, target)
expect(1, image, "table")
expect(2, xPos, "number")
expect(3, yPos, "number")
expect(4, target, "table", "nil")
if not target then target = term end
for y, line in ipairs(image) do
target.setCursorPos(xPos, yPos + y - 1)
target.blit(line.text, line.foreground, line.background)
end
end
return {
parse = parse,
load = load,
draw = draw,
}

View File

@ -0,0 +1,91 @@
local helpers = require "test_helpers"
describe("cc.image.nft", function()
local nft = require("cc.image.nft")
describe("parse", function()
it("validates arguments", function()
nft.parse("")
expect.error(nft.parse, nil):eq("bad argument #1 (expected string, got nil)")
end)
it("parses an empty string", function()
expect(nft.parse("")):same {}
end)
it("parses a string with no colours", function()
expect(nft.parse("Hello")):same { { text = "Hello", foreground = "00000", background = "fffff" } }
end)
it("handles background and foreground colours", function()
expect(nft.parse("\30a\31bHello"))
:same { { text = "Hello", foreground = "bbbbb", background = "aaaaa" } }
end)
it("parses multi-line files", function()
expect(nft.parse("Hello\nWorld")):same {
{ text = "Hello", foreground = "00000", background = "fffff" },
{ text = "World", foreground = "00000", background = "fffff" },
}
end)
it("handles empty lines", function()
expect(nft.parse("\n\n")):same {
{ text = "", foreground = "", background = "" },
{ text = "", foreground = "", background = "" },
}
end)
end)
describe("load", function()
it("validates arguments", function()
nft.load("")
expect.error(nft.load, nil):eq("bad argument #1 (expected string, got nil)")
end)
it("loads from a file", function()
local image = fs.open("/test-files/example.nft", "w")
image.write("\30aHello, world!")
image.close()
expect(nft.load("/test-files/example.nft")):same {
{ background = "aaaaaaaaaaaaa", foreground = "0000000000000", text = "Hello, world!" },
}
end)
it("fails on missing files", function()
expect({ nft.load("/test-files/not_a_file.nft") })
:same { nil, "/test-files/not_a_file.nft: No such file" }
end)
end)
describe("draw", function()
it("validates arguments", function()
expect.error(nft.draw, nil):eq("bad argument #1 (expected table, got nil)")
expect.error(nft.draw, {}, nil):eq("bad argument #2 (expected number, got nil)")
expect.error(nft.draw, {}, 1, nil):eq("bad argument #3 (expected number, got nil)")
expect.error(nft.draw, {}, 1, 1, false):eq("bad argument #4 (expected table, got boolean)")
end)
it("draws an image", function()
local win = helpers.with_window(7, 3, function()
nft.draw({
{ background = "aaaaa", foreground = "f000f", text = "Hello" },
}, 2, 2)
end)
expect(win.getLine(1)):eq(" ")
expect({ win.getLine(2) }):same { " Hello ", "0f000f0", "faaaaaf" }
expect(win.getLine(3)):eq(" ")
end)
it("draws an image to a custom redirect", function()
local win = window.create(term.current(), 1, 1, 5, 1, false)
nft.draw({
{ background = "aaaaa", foreground = "f000f", text = "Hello" },
}, 1, 1, win)
expect({ win.getLine(1) }):same { "Hello", "f000f", "aaaaa" }
end)
end)
end)