From 673a0b4a751a7e34fce22a3434cf844428b643dc Mon Sep 17 00:00:00 2001 From: TheWahlolly Date: Sun, 4 May 2025 15:16:54 +0300 Subject: [PATCH] v0.10.0 - Added text editor (which took me way too long) and too many other tweaks to name. --- halyde/apps/cd.lua | 2 +- halyde/apps/edit.lua | 321 +++++++++++++++++ halyde/apps/help.lua | 12 + halyde/apps/helpdb/cp.txt | 4 +- halyde/apps/helpdb/default.txt | 3 + halyde/apps/helpdb/help.txt | 2 +- halyde/apps/helpdb/ls.txt | 2 +- halyde/apps/helpdb/rm.txt | 2 +- halyde/apps/rtest3d.lua | 137 +++++++ halyde/config/shell.cfg | 3 + halyde/core/boot.lua | 2 +- halyde/core/cormgr.lua | 41 ++- halyde/core/evmgr.lua | 29 +- halyde/core/fullkb.lua | 5 +- halyde/core/shell.lua | 30 +- halyde/core/termlib.lua | 75 ++-- halyde/lib/filesystem.lua | 7 +- halyde/lib/raster.lua | 637 +++++++++++++++++---------------- home/longfile.txt | 67 ++++ 19 files changed, 997 insertions(+), 384 deletions(-) create mode 100644 halyde/apps/edit.lua create mode 100644 halyde/apps/rtest3d.lua create mode 100644 home/longfile.txt diff --git a/halyde/apps/cd.lua b/halyde/apps/cd.lua index aec22b3..b91f0d7 100644 --- a/halyde/apps/cd.lua +++ b/halyde/apps/cd.lua @@ -24,6 +24,6 @@ else if fs.exists(directory) and fs.isDirectory(directory) or fs.exists(shell.workingDirectory .. directory) and fs.isDirectory(shell.workingDirectory .. directory) then shell.workingDirectory = directory else - print("error: no such directory") + print("\27[91mNo such directory.") end end diff --git a/halyde/apps/edit.lua b/halyde/apps/edit.lua new file mode 100644 index 0000000..c0f7755 --- /dev/null +++ b/halyde/apps/edit.lua @@ -0,0 +1,321 @@ +local args = {...} +local file = args[1] +args = nil +local fs = import("filesystem") +local event = import("event") +local gpu = component.proxy(component.list("gpu")()) +local width, height = gpu.getResolution() +local scrollPosX, scrollPosY = 1, 1 +local cursorPosX, cursorPosY = 1, 1 +local cursorWhite = true +local changesMade = false +local renderBuffer = gpu.allocateBuffer() +local scrollSpeed = 5 +--local ocelot = component.proxy(component.list("ocelot")()) + +local function rawset(x, y, text) + termlib.cursorPosX = x + termlib.cursorPosY = y + print(text, false, false) +end + +local filestring, filepath, handle, data, tmpdata +if file then + if file:sub(1, 1) == "/" then + filepath = file + else + filepath = shell.workingDirectory .. file + end + handle, data, tmpdata = fs.open(filepath, "r"), "", nil + if fs.exists(filepath) then + filestring = filepath + repeat + tmpdata = handle:read(math.huge) + data = data .. (tmpdata or "") + until not tmpdata + tmpdata = {} + if data:gmatch("(.-)\n")() then + for line in data:gmatch("(.-)\n") do + local newLine = line:gsub("\r", "") -- this took me SO LONG TO FIGURE OUT AAAAAAAA I HATE CRLF I HATE CRLF I HATE CRLF + table.insert(tmpdata, newLine) + end + else + tmpdata = {data} + end + else + filepath = shell.workingDirectory .. file + filestring = "[NEW FILE]" + tmpdata = {""} + end +else + filepath = "" + filestring = "[NEW FILE]" + tmpdata = {""} +end +local function render() + gpu.setActiveBuffer(renderBuffer) + clear() + local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) + if realCursorX < 1 then + scrollPosX = scrollPosX + realCursorX - 1 + cursorPosX = 1 + realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) + end + for i = scrollPosY, height + scrollPosY - 3 do + rawset(1, i - scrollPosY + 1, (tmpdata[i] or ""):sub(scrollPosX)) + end + rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width)) + rawset(1, height, "\27[107m\27[30m^X\27[0m Exit \27[107m\27[30m^S\27[0m Save" .. string.rep(" ", width)) + local char = gpu.get(realCursorX, cursorPosY) + if cursorWhite then + rawset(realCursorX, cursorPosY, "\27[107m\27[30m" .. char .. "\27[0m") + else + rawset(realCursorX, cursorPosY, char) + end + gpu.bitblt() + gpu.setActiveBuffer(0) +end + +local renderFlag, cursorRenderFlag = false, false + +local function scrollUp() + cursorPosY = cursorPosY - 1 + cursorRenderFlag = true + cursorWhite = true + if cursorPosY < 1 then + renderFlag = true + scrollPosY = scrollPosY - 1 + cursorPosY = 1 + end + if scrollPosY < 1 then + renderFlag = false + scrollPosY = 1 + end + if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) < 1 then + renderFlag = true + end +end + +local function scrollDown() + cursorPosY = cursorPosY + 1 + cursorRenderFlag = true + cursorWhite = true + if cursorPosY + scrollPosY - 1 > #tmpdata then + renderFlag = false + cursorPosY = #tmpdata - scrollPosY + 1 + end + if cursorPosY > height - 2 then + renderFlag = true + scrollPosY = scrollPosY + 1 + cursorPosY = height - 2 + end + if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) < 1 then + renderFlag = true + end +end + +local function scrollLeft() + cursorRenderFlag = true + cursorWhite = true + if cursorPosX > 1 then + if cursorPosX <= unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2 then + cursorPosX = cursorPosX - 1 + elseif unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 1 > 1 then + cursorPosX = unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 1 + end + elseif scrollPosX > 1 then + scrollPosX = scrollPosX - 1 + renderFlag = true + end + if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) < 1 then + renderFlag = true + end +end + +local function scrollRight() + cursorRenderFlag = true + cursorWhite = true + if cursorPosX <= unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 1 then + cursorPosX = cursorPosX + 1 + end + if cursorPosX > width then + cursorPosX = width + scrollPosX = scrollPosX + 1 + renderFlag = true + end +end + +local function processEvent(args) + renderFlag, cursorRenderFlag = false, false + if args[1] == "key_down" then + local keycode = args[4] + local key = keyboard.keys[keycode] + if keyboard.ctrlDown then + return false, false, key + end + if key == "down" and cursorPosY < #tmpdata then + scrollDown() + end + if key == "up" then + scrollUp() + end + if key == "left" then + scrollLeft() + end + if key == "right" then + scrollRight() + end + if key == "enter" then + changesMade = true + renderFlag = true + cursorWhite = true + table.insert(tmpdata, cursorPosY + 1, tmpdata[cursorPosY]:sub(cursorPosX)) + tmpdata[cursorPosY] = tmpdata[cursorPosY]:sub(1, cursorPosX - 1) + cursorPosX = 1 + cursorPosY = cursorPosY + 1 + scrollPosX = 1 + if cursorPosY > height - 2 then + scrollPosY = scrollPosY + 1 + cursorPosY = height - 2 + end + end + if key == "back" then + changesMade = true + cursorRenderFlag = true + cursorWhite = true + if cursorPosY + scrollPosY - 1 > 1 then + cursorPosY = cursorPosY - 1 + if cursorPosY < 1 then + scrollPosY = scrollPosY - 1 + cursorPosY = 1 + end + if scrollPosY < 1 then + scrollPosY = 1 + end + cursorPosX = unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2 + if cursorPosX > width then + scrollPosX = cursorPosX - width + 1 + cursorPosX = width + end + tmpdata[cursorPosY] = tmpdata[cursorPosY] .. tmpdata[cursorPosY + 1] + table.remove(tmpdata, cursorPosY + 1) + renderFlag = true + else + tmpdata[cursorPosY] = tmpdata[cursorPosY]:sub(1, cursorPosX + scrollPosX - 3) .. tmpdata[cursorPosY]:sub(cursorPosX + scrollPosX - 1) + cursorPosX = math.min(cursorPosX - 1, unicode.wlen(tmpdata[cursorPosY]) + 1) + if cursorPosX < 1 then + cursorPosX = 1 + scrollPosX = scrollPosX - 1 + renderFlag = true + else + rawset(1, cursorPosY - scrollPosY + 1, tmpdata[cursorPosY]:sub(scrollPosX) .. " ") + end + end + end + if args[3] >= 32 and args[3] <= 126 then + changesMade = true + cursorRenderFlag = true + cursorWhite = true + tmpdata[cursorPosY] = tmpdata[cursorPosY]:sub(1, cursorPosX + scrollPosX - 2) .. unicode.char(args[3]) .. tmpdata[cursorPosY]:sub(cursorPosX + scrollPosX - 1) + cursorPosX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY])) + 1 + --ocelot.log(tostring(cursorPosX)) + if cursorPosX > width then + cursorPosX = width + scrollPosX = scrollPosX + 1 + renderFlag = true + else + rawset(1, cursorPosY - scrollPosY + 1, tmpdata[cursorPosY]:sub(scrollPosX)) + end + end + elseif args[1] == "scroll" then + if args[5] == 1 then + for i = 1, scrollSpeed do + scrollUp() + end + elseif args[5] == -1 and cursorPosY < #tmpdata then + for i = 1, scrollSpeed do + scrollDown() + end + end + end + return renderFlag, cursorRenderFlag +end + +local function save() + rawset(1, height - 1, "\27[107m\27[30m" .. string.rep(" ", width)) + termlib.cursorPosX = 1 + termlib.cursorPosY = height - 1 + local savepath = read(nil, "\27[107m\27[30mSave location: ", filepath) + if fs.exists(savepath) then + rawset(1, height - 1, "\27[107m\27[30m" .. string.rep(" ", width)) + local answer = read(nil, "\27[107m\27[30mFile already exists. Overwrite it? [Y/n] ") + if answer:lower() == "n" then + rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width)) + return + end + end + local handle, errorMessage = fs.open(savepath, "w") + if handle then + handle:write(table.concat(tmpdata, "\n")) + handle:close() + rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width)) + else + rawset(1, height - 1, "\27[107m\27[30mERROR: " .. errorMessage:gsub("\n", "") .. string.rep(" ", width)) + end + changesMade = false +end + +render() +while true do + local args = {event.pull(0.5)} + local renderFlag, cursorRenderFlag, specialKey = false, false, nil + local previousCursorX, previousCursorY = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2), cursorPosY + if args and args[1] then + cursorWhite = true + renderFlag, cursorRenderFlag, specialKey = processEvent(args) + if specialKey == "x" then + if changesMade then + termlib.cursorPosX = 1 + termlib.cursorPosY = height - 1 + local response = read(nil, "\27[107m\27[30mWould you like to save changes? [Y/n] ") + if response:lower() ~= "n" then + save() + end + end + gpu.freeAllBuffers() + clear() + return + end + if specialKey == "s" then + save() + end + repeat + args = {event.pull("key_down", 0)} + if args and args[1] then + processEvent(args) + end + until not args or not args[1] + else + cursorWhite = not cursorWhite + cursorRenderFlag = true + end + if cursorRenderFlag then + local char = gpu.get(previousCursorX, previousCursorY) + rawset(previousCursorX, previousCursorY, char) + local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) + if realCursorX < 1 then + scrollPosX = scrollPosX + realCursorX - 1 + cursorPosX = 1 + realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY]) - scrollPosX + 2) + end + local char = gpu.get(realCursorX, cursorPosY) + if cursorWhite then + rawset(realCursorX, cursorPosY, "\27[107m\27[30m" .. char .. "\27[0m") + else + rawset(realCursorX, cursorPosY, char) + end + end + if renderFlag then + render() + end +end diff --git a/halyde/apps/help.lua b/halyde/apps/help.lua index 8db9fe2..47cd216 100644 --- a/halyde/apps/help.lua +++ b/halyde/apps/help.lua @@ -21,6 +21,18 @@ if fs.exists("/halyde/apps/helpdb/" .. command .. ".txt") then data = data .. (tmpdata or "") until not tmpdata print(data) + local aliases = table.copy(shell.aliases) + if table.find(aliases, command) then + local aliasIndex = table.find(aliases, command) + local aliasString = "Aliases:\n " .. aliasIndex + aliases[aliasIndex] = nil + while table.find(aliases, command) do + aliasIndex = table.find(aliases, command) + aliasString = aliasString .. ", " .. aliasIndex + aliases[aliasIndex] = nil + end + print(aliasString) + end else print("Could not find help file for: " .. command .. ".") end diff --git a/halyde/apps/helpdb/cp.txt b/halyde/apps/helpdb/cp.txt index 9d828c8..e478126 100644 --- a/halyde/apps/helpdb/cp.txt +++ b/halyde/apps/helpdb/cp.txt @@ -2,8 +2,8 @@ Usage: cp [FLAGS] [SOURCE] [DESTINATION] Copies a file. -o, --overwrite Allows any file that might be at the destination to be overwritten. - SOURCE Specifies the file to be copied. - DESTINATION Specifies the path to copy the file to. + SOURCE Specifies the file to be copied. + DESTINATION Specifies the path to copy the file to. Examples: cp /home/a.txt /b.txt Copies the file at /home/a.txt to /b.txt. diff --git a/halyde/apps/helpdb/default.txt b/halyde/apps/helpdb/default.txt index 09176a6..051f919 100644 --- a/halyde/apps/helpdb/default.txt +++ b/halyde/apps/helpdb/default.txt @@ -13,3 +13,6 @@ All current Halyde shell commands: You can get additional information on any app or command by running: help [COMMAND] + +In the help documentation, an asterisk (*) next to an argument means it is optional. +This is excluding flags, which are all optional. diff --git a/halyde/apps/helpdb/help.txt b/halyde/apps/helpdb/help.txt index 561a765..d648e98 100644 --- a/halyde/apps/helpdb/help.txt +++ b/halyde/apps/helpdb/help.txt @@ -1,7 +1,7 @@ Usage: help [COMMAND] Displays info on the command specified, or a list of commands if one is not specified. - COMMAND Command to display information on. + COMMAND* Command to display information on. Examples: help Displays a list of all default commands available. diff --git a/halyde/apps/helpdb/ls.txt b/halyde/apps/helpdb/ls.txt index b1d681b..0c8034d 100644 --- a/halyde/apps/helpdb/ls.txt +++ b/halyde/apps/helpdb/ls.txt @@ -2,7 +2,7 @@ Usage: ls [PATH] Lists all files and directories in the specified path, or in the shell working directory if the path isn't specified. Directories are shown in yellow, executable files are shown in green, and other files are shown in white. - PATH Path to the folder to list files and directories from. + PATH* Path to the folder to list files and directories from. Examples: ls Lists all files and directories from the current shell working directory. diff --git a/halyde/apps/helpdb/rm.txt b/halyde/apps/helpdb/rm.txt index ac9c315..82495e4 100644 --- a/halyde/apps/helpdb/rm.txt +++ b/halyde/apps/helpdb/rm.txt @@ -7,4 +7,4 @@ Removes files and directories. Examples: rm a.txt Removes a.txt in the current shell working directory. - rm -r -f /halyde/core/ Removes everything in /halyde/core forcedly and recursively. Note that trying this on a real machine will remove critical Halyde system files and cause it to stop working. + rm -r -f /halyde/core/ Removes everything in /halyde/core/ forcedly and recursively. diff --git a/halyde/apps/rtest3d.lua b/halyde/apps/rtest3d.lua new file mode 100644 index 0000000..2002706 --- /dev/null +++ b/halyde/apps/rtest3d.lua @@ -0,0 +1,137 @@ +local raster = import("raster") +local event = import("event") + +-- Initialize the 3D renderer for a spinning cube +-- Using the raster library for drawing + +-- Screen dimensions +local SCREEN_WIDTH, SCREEN_HEIGHT = component.invoke(component.list("gpu")(), "getResolution") +SCREEN_WIDTH, SCREEN_HEIGHT = SCREEN_WIDTH * 2, SCREEN_HEIGHT * 4 +local CENTER_X = SCREEN_WIDTH / 2 +local CENTER_Y = SCREEN_HEIGHT / 2 + +-- Cube properties +local CUBE_SIZE = 10 +local increment = 0 +local WHITE = 0xFFFFFF +local ROTATION_SPEED = 0.1 + +-- 3D cube vertices (centered at origin) +local vertices = { + {-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 0: left bottom back + {CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 1: right bottom back + {CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 2: right top back + {-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 3: left top back + {-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 4: left bottom front + {CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 5: right bottom front + {CUBE_SIZE, CUBE_SIZE, CUBE_SIZE}, -- 6: right top front + {-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE} -- 7: left top front +} + +-- Cube edges defined by vertex indices +local edges = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, -- back face + {4, 5}, {5, 6}, {6, 7}, {7, 4}, -- front face + {0, 4}, {1, 5}, {2, 6}, {3, 7} -- connecting edges +} + +-- Projection parameters +local FOV = 256 -- Field of view (distance from camera to screen) +local Z_OFFSET = 300 -- Distance from camera to cube center + +-- Initialize rotation angles +local angleX, angleY, angleZ = 0, 0, 0 + +-- Matrix multiplication function (apply rotation to a 3D point) +local function rotatePoint(x, y, z) + -- Rotation around X axis + local cosX, sinX = math.cos(angleX), math.sin(angleX) + local y1 = y * cosX - z * sinX + local z1 = y * sinX + z * cosX + + -- Rotation around Y axis + local cosY, sinY = math.cos(angleY), math.sin(angleY) + local x1 = x * cosY + z1 * sinY + local z2 = -x * sinY + z1 * cosY + + -- Rotation around Z axis + local cosZ, sinZ = math.cos(angleZ), math.sin(angleZ) + local x2 = x1 * cosZ - y1 * sinZ + local y2 = x1 * sinZ + y1 * cosZ + + return x2, y2, z2 +end + +-- Perspective projection function (3D to 2D) +local function projectPoint(x, y, z) + -- Apply perspective projection + local scale = FOV / (z + Z_OFFSET) + local x2d = x * scale + CENTER_X + local y2d = y * scale + CENTER_Y + + return x2d, y2d +end + +-- Render a single frame +local function renderFrame() + increment = increment + 0.05 + CUBE_SIZE = (math.sin(increment) + 1) * 25 + vertices = { + {-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 0: left bottom back + {CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 1: right bottom back + {CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 2: right top back + {-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 3: left top back + {-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 4: left bottom front + {CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 5: right bottom front + {CUBE_SIZE, CUBE_SIZE, CUBE_SIZE}, -- 6: right top front + {-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE} -- 7: left top front + } + -- Update rotation angles + raster.clear() + angleX = angleX + ROTATION_SPEED + angleY = angleY + ROTATION_SPEED * 0.7 + angleZ = angleZ + ROTATION_SPEED * 0.5 + + -- Project all vertices + local projectedPoints = {} + for i, vertex in ipairs(vertices) do + -- Rotate the point + local x, y, z = rotatePoint(vertex[1], vertex[2], vertex[3]) + + -- Project the point to 2D + local x2d, y2d = projectPoint(x, y, z) + projectedPoints[i] = {x2d, y2d} + end + + -- Draw all edges + for _, edge in ipairs(edges) do + local p1 = projectedPoints[edge[1] + 1] -- +1 because Lua indices start at 1 + local p2 = projectedPoints[edge[2] + 1] + + -- Draw the line + raster.drawLine(p1[1], p1[2], p2[1], p2[2], WHITE) + end + + -- Render the frame + raster.update() +end + +-- Main program +function main() + -- Initialize raster engine + raster.init() + + -- Main loop (assume this is called repeatedly by the host environment) + while true do + renderFrame() + if event.pull("key_down", 0) then + raster.free() + break + end + end + -- Return a reference to renderFrame so it can be called for animation + return renderFrame +end + +-- Start the program +return main() diff --git a/halyde/config/shell.cfg b/halyde/config/shell.cfg index 3cc8922..2750607 100644 --- a/halyde/config/shell.cfg +++ b/halyde/config/shell.cfg @@ -9,8 +9,11 @@ local shellcfg = { ["rename"] = "mv", ["ren"] = "mv", ["dir"] = "ls", + ["list"] = "ls", ["man"] = "help", ["del"] = "rm", + ["delete"] = "rm", + ["remove"] = "rm", [".."] = "cd .." }, ["defaultWorkingDirectory"] = "/home/" -- the working directory that gets set when halyde starts } diff --git a/halyde/core/boot.lua b/halyde/core/boot.lua index 5565300..9d114fc 100644 --- a/halyde/core/boot.lua +++ b/halyde/core/boot.lua @@ -1,7 +1,7 @@ local loadfile = ... local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile) -_G._OSVERSION = "Halyde 0.9.0" +_G._OSVERSION = "Halyde 0.10.0" function _G.import(module, ...) local args = table.pack(...) diff --git a/halyde/core/cormgr.lua b/halyde/core/cormgr.lua index 3f7e4f2..6ba210b 100644 --- a/halyde/core/cormgr.lua +++ b/halyde/core/cormgr.lua @@ -4,10 +4,25 @@ _G.cormgr.corList = {} --local ocelot = component.proxy(component.list("ocelot")()) local filesystem = import("filesystem") +local gpu = component.proxy(component.list("gpu")()) -function _G.cormgr.loadCoroutine(path) +function _G.cormgr.loadCoroutine(path, ...) + local args = {...} local cor = coroutine.create(function() - import(path) + local result, errorMessage = xpcall(function(...) + import(...) + end, function(errorMessage) + return errorMessage .. "\n \n" .. debug.traceback() + end, path, table.unpack(args)) + if not result then + if print then + gpu.freeAllBuffers() + print("\n\27[91m" .. errorMessage) + else + error(errorMessage) + end + end + --import(path, table.unpack(args)) end) table.insert(_G.cormgr.corList, cor) end @@ -22,16 +37,20 @@ end local function runCoroutines() for i = 1, #_G.cormgr.corList do - local result, errorMessage = coroutine.resume(_G.cormgr.corList[i]) - if not result then - handleError(errorMessage) + if cormgr.corList[i] then + local result, errorMessage = coroutine.resume(cormgr.corList[i]) + if cormgr.corList[i] then + if not result then + handleError(errorMessage) + end + if coroutine.status(cormgr.corList[i]) == "dead" then + table.remove(cormgr.corList, i) + i = i - 1 + end + --computer.pullSignal(0) + --coroutine.yield() + end end - if coroutine.status(_G.cormgr.corList[i]) == "dead" then - table.remove(_G.cormgr.corList, i) - i = i - 1 - end - --computer.pullSignal(0) - --coroutine.yield() end end diff --git a/halyde/core/evmgr.lua b/halyde/core/evmgr.lua index 10568c1..9aae9f7 100644 --- a/halyde/core/evmgr.lua +++ b/halyde/core/evmgr.lua @@ -2,6 +2,9 @@ _G.evmgr = {} _G.evmgr.eventQueue = {} local maxEventQueueLength = 10 -- increase if events start getting dropped +keyboard.ctrlDown = false +keyboard.altDown = false + --local ocelot = component.proxy(component.list("ocelot")()) while true do @@ -11,6 +14,30 @@ while true do if args and args[1] then --ocelot.log("Sending signal "..args..","..computer.uptime()) table.insert(evmgr.eventQueue, args) + if keyboard then + if args[1] == "key_down" then + local keycode = args[4] + local key = keyboard.keys[keycode] + if key == "lcontrol" then + keyboard.ctrlDown = true + elseif key == "lmenu" then + keyboard.altDown = true + elseif key == "c" and keyboard.ctrlDown and keyboard.altDown then + if print then + print("\n\27[91mCoroutine "..tostring(#cormgr.corList).." killed.") + end + cormgr.corList[#cormgr.corList] = nil + end + elseif args[1] == "key_up" then + local keycode = args[4] + local key = keyboard.keys[keycode] + if key == "lcontrol" then + keyboard.ctrlDown = false + elseif key == "lmenu" then + keyboard.altDown = false + end + end + end while #evmgr.eventQueue > maxEventQueueLength do --ocelot.log("Queue length breach, removing first signal") table.remove(evmgr.eventQueue, 1) @@ -19,4 +46,4 @@ while true do until not args or not args[1] --ocelot.log("done") coroutine.yield() -end \ No newline at end of file +end diff --git a/halyde/core/fullkb.lua b/halyde/core/fullkb.lua index bb1e14a..e9c64de 100644 --- a/halyde/core/fullkb.lua +++ b/halyde/core/fullkb.lua @@ -1,5 +1,4 @@ -_G.keyboard = {pressedChars = {}, pressedCodes = {}} -_G.keyboard.keys = {} +_G.keyboard = {["keys"] = {}} keyboard.keys["1"] = 0x02 keyboard.keys["2"] = 0x03 @@ -141,4 +140,4 @@ setmetatable(keyboard.keys, end end end -}) \ No newline at end of file +}) diff --git a/halyde/core/shell.lua b/halyde/core/shell.lua index 3928005..ed7a14b 100644 --- a/halyde/core/shell.lua +++ b/halyde/core/shell.lua @@ -3,11 +3,22 @@ import("/halyde/core/termlib.lua") local event = import("event") --local ocelot = component.proxy(component.list("ocelot")()) local filesystem = import("filesystem") +local gpu = component.proxy(component.list("gpu")()) _G.shell = {} _G.shell.workingDirectory = shellcfg["defaultWorkingDirectory"] _G.shell.aliases = shellcfg["aliases"] +local function runAsCoroutine(path, ...) + --ocelot.log("running " .. path .. " as coroutine") + cormgr.loadCoroutine(path, ...) + local corIndex = #cormgr.corList + local cor = cormgr.corList[#cormgr.corList] + repeat + coroutine.yield() + until cormgr.corList[corIndex] ~= cor +end + function _G.shell.run(command) checkArg(1, command, "string") if shell.aliases[command:match("[^ ]+")] then @@ -48,14 +59,14 @@ function _G.shell.run(command) foundfile = true local path = args[1] table.remove(args, 1) - import(path, table.unpack(args)) + runAsCoroutine(path, table.unpack(args)) else for _, item in pairs(shellcfg["path"]) do if filesystem.exists(item..args[1]) then foundfile = true local path = item..args[1] table.remove(args, 1) - import(path, table.unpack(args)) + runAsCoroutine(path, table.unpack(args)) break else -- try to look for it without the file extension local files = filesystem.list(item) @@ -63,15 +74,7 @@ function _G.shell.run(command) if args[1] == file:match("(.+)%.[^%.]+$") then foundfile = true table.remove(args, 1) - local function runCommand() - import(item..file, table.unpack(args)) - end - local result, reason = xpcall(runCommand, function(errMsg) - return errMsg .. "\n\n" .. debug.traceback() - end) - if not result then - print("\27[91m" .. reason) - end + runAsCoroutine(item..file, table.unpack(args)) break end end @@ -87,9 +90,10 @@ print(shellcfg["startupMessage"]) while true do coroutine.yield() -- print(shell.workingDirectory .. " >") - print(shellcfg["prompt"]:format(shell.workingDirectory),false) + --print(shellcfg["prompt"]:format(shell.workingDirectory),false) -- termlib.cursorPosX = #(shell.workingDirectory .. " > ") -- termlib.cursorPosY = termlib.cursorPosY - 1 - local shellCommand = read("shell") + local shellCommand = read("shell", shellcfg["prompt"]:format(shell.workingDirectory)) shell.run(shellCommand) + gpu.freeAllBuffers() end diff --git a/halyde/core/termlib.lua b/halyde/core/termlib.lua index 9dea799..10861b4 100644 --- a/halyde/core/termlib.lua +++ b/halyde/core/termlib.lua @@ -1,6 +1,7 @@ local event = import("event") --local keyboard = import("keyboard") +--local ocelot = component.proxy(component.list("ocelot")()) local gpu = component.proxy(component.list("gpu")()) -- replace with component.gpu once implemented _G.termlib = {} termlib.cursorPosX = 1 @@ -41,7 +42,7 @@ gpu.setForeground(defaultForegroundColor) gpu.setBackground(defaultBackgroundColor) local function scrollDown() - if gpu.copy(0,1,width,height,0,-1) then + if gpu.copy(1,1,width,height,0,-1) then local prevForeground = gpu.getForeground() local prevBackground = gpu.getBackground() gpu.setForeground(defaultForegroundColor) @@ -61,7 +62,7 @@ local function newLine() end end -function parseCodeNumbers(code) +local function parseCodeNumbers(code) o = {} for num in code:sub(3,-2):gmatch("[^;]+") do table.insert(o,tonumber(num)) @@ -69,13 +70,16 @@ function parseCodeNumbers(code) return o end -function _G.print(text,endNewLine) +function _G.print(text, endNewLine, wordWrap) -- you don't know how tiring this was just for ANSI escape code support - if endNewLine==nil then + if endNewLine == nil then endNewLine = true end + if wordWrap == nil then + wordWrap = true + end if not text or not tostring(text) then return @@ -93,7 +97,7 @@ function _G.print(text,endNewLine) end gpu.set(termlib.cursorPosX,termlib.cursorPosY,section) termlib.cursorPosX = termlib.cursorPosX+unicode.wlen(section) - if termlib.cursorPosX>width then + if termlib.cursorPosX>width and wordWrap then newLine() end section="" @@ -162,13 +166,18 @@ end function _G.clear() local xRes, yRes = gpu.getResolution() + gpu.setForeground(defaultForegroundColor) + gpu.setBackground(defaultBackgroundColor) gpu.fill(1,1,xRes,yRes," ") termlib.cursorPosX, termlib.cursorPosY = 1, 1 end -function _G.read(readHistoryType) +function _G.read(readHistoryType, prefix, defaultText) checkArg(1, readHistoryType, "string", "nil") - local curtext = "" + checkArg(2, prefix, "string", "nil") + checkArg(3, defaultText, "string", "nil") + local curtext = defaultText or "" + local prefix = prefix or "" local RHIndex if readHistoryType then if not termlib.readHistory[readHistoryType] then @@ -179,13 +188,10 @@ function _G.read(readHistoryType) RHIndex = #termlib.readHistory[readHistoryType] -- read history index end local cursorPosX, cursorPosY = termlib.cursorPosX, termlib.cursorPosY + print(prefix .. curtext .. "\27[107m ", false) local cursorWhite = true while true do - if cursorWhite then - print("\27[107m ", false) - else - print(" ", false) - end + --ocelot.log(curtext) termlib.cursorPosX = termlib.cursorPosX - 1 local args = {event.pull("key_down", 0.5)} if args[4] then @@ -194,51 +200,56 @@ function _G.read(readHistoryType) local key = keyboard.keys[keycode] if key == "up" and readHistoryType then termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(curtext .. " ", false) + print(prefix .. curtext .. " ", false) RHIndex = RHIndex - 1 if RHIndex <= 0 then RHIndex = 1 end termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(termlib.readHistory[readHistoryType][RHIndex] .. string.rep(" ", unicode.wlen(curtext) - unicode.wlen(termlib.readHistory[readHistoryType][RHIndex])), false) + print(prefix .. termlib.readHistory[readHistoryType][RHIndex] .. string.rep(" ", unicode.wlen(curtext) - unicode.wlen(termlib.readHistory[readHistoryType][RHIndex])), false) curtext = termlib.readHistory[readHistoryType][RHIndex] end if key == "down" and readHistoryType then termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(curtext .. " ", false) + print(prefix .. curtext .. " ", false) RHIndex = RHIndex + 1 if RHIndex > #termlib.readHistory[readHistoryType] then RHIndex = #termlib.readHistory[readHistoryType] end termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(termlib.readHistory[readHistoryType][RHIndex] .. string.rep(" ", unicode.wlen(curtext) - unicode.wlen(termlib.readHistory[readHistoryType][RHIndex])), false) + print(prefix .. termlib.readHistory[readHistoryType][RHIndex] .. string.rep(" ", unicode.wlen(curtext) - unicode.wlen(termlib.readHistory[readHistoryType][RHIndex])), false) curtext = termlib.readHistory[readHistoryType][RHIndex] end + if key == "back" then + curtext = curtext:sub(1, #curtext-1) + termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY + print(prefix .. curtext.." ", false) + end + if key == "enter" then + termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY + print(prefix .. curtext .. " ") + if readHistoryType then + while #termlib.readHistory[readHistoryType] > 50 do + table.remove(termlib.readHistory[readHistoryType], 1) + end + end + return curtext + end if args[3] >= 32 and args[3] <= 126 then curtext = curtext .. (unicode.char(args[3]) or "") if readHistoryType then termlib.readHistory[readHistoryType][RHIndex] = curtext end - else - if key == "back" then - curtext = curtext:sub(1, #curtext-1) - termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(curtext.." ", false) - elseif key == "enter" then - termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(curtext .. " ") - if readHistoryType then - while #termlib.readHistory[readHistoryType] > 50 do - table.remove(termlib.readHistory[readHistoryType], 1) - end - end - return curtext - end end termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY - print(curtext, false) + print(prefix .. curtext, false) else cursorWhite = not cursorWhite end + if cursorWhite then + print("\27[107m ", false) + else + print(" ", false) + end end end diff --git a/halyde/lib/filesystem.lua b/halyde/lib/filesystem.lua index a5c2b72..542bc94 100644 --- a/halyde/lib/filesystem.lua +++ b/halyde/lib/filesystem.lua @@ -48,7 +48,12 @@ function filesystem.open(path, mode) -- opens a file and returns its handle return nil, "invalid handle type" end local address, absPath = filesystem.processPath(path) - local handle = component.invoke(address, "open", absPath, mode) + local handleArgs = {component.invoke(address, "open", absPath, mode)} + local handle = handleArgs[1] + if not handle then + return table.unpack(handleArgs) + end + handleArgs = nil local properHandle = {} properHandle.handle = handle properHandle.address = address diff --git a/halyde/lib/raster.lua b/halyde/lib/raster.lua index a3719b5..889c8f4 100644 --- a/halyde/lib/raster.lua +++ b/halyde/lib/raster.lua @@ -1,11 +1,11 @@ local raster = { - ["units"]={}, - ["defaultBackgroundColor"]=0x000000, - ["defaultForegroundColor"]=0xFFFFFF, - ["displayWidth"]=0, - ["displayHeight"]=0, - ["charWidth"]=0, - ["charHeight"]=0 + ["units"]={}, + ["defaultBackgroundColor"]=0x000000, + ["defaultForegroundColor"]=0xFFFFFF, + ["displayWidth"]=0, + ["displayHeight"]=0, + ["charWidth"]=0, + ["charHeight"]=0 } -- local ocelot = component.proxy(component.list("ocelot")()) @@ -19,393 +19,398 @@ local renderBuffer = nil -- braille rendering function raster.units.charToBraille(x,y) - return x*2,y*4 + return x*2,y*4 end function raster.units.brailleToChar(x,y) - return math.ceil(x/2),math.ceil(y/4) + return math.ceil(x/2),math.ceil(y/4) end function raster.init(width, height, bgcolor) - -- NOTE: Width and height are in characters, not pixels in braille. - -- If the width and height are nil, the entire screen will be used. - if width==nil and height==nil then - width, height = gpu.getResolution() - end + -- NOTE: Width and height are in characters, not pixels in braille. + -- If the width and height are nil, the entire screen will be used. + if width==nil and height==nil then + width, height = gpu.getResolution() + end - for i = 1, width*height do - chunksAffected[i] = true - end - raster.charWidth = width - raster.charHeight = height + for i = 1, width*height do + chunksAffected[i] = true + end + raster.charWidth = width + raster.charHeight = height - width, height = raster.units.charToBraille(width, height) - - bgcolor = bgcolor or raster.defaultBackgroundColor; + width, height = raster.units.charToBraille(width, height) + + bgcolor = bgcolor or raster.defaultBackgroundColor - for i = 1, width*height do - display[i] = bgcolor - end - raster.displayWidth = width - raster.displayHeight = height + raster.displayWidth = width + raster.displayHeight = height - pcall(function() - renderBuffer = gpu.allocateBuffer() - end) + pcall(function() + renderBuffer = gpu.allocateBuffer() + end) end function raster.set(x, y, color) - if x<1 or x>raster.displayWidth or y<1 or y>raster.displayHeight then - return false - end + if x<1 or x>raster.displayWidth or y<1 or y>raster.displayHeight then + return false + end - color = color or raster.defaultForegroundColor - local i = x+y*raster.displayWidth - display[i] = color + color = color or raster.defaultForegroundColor + local i = x+y*raster.displayWidth + display[i] = color - local ci = math.floor((x-1)/2)+math.floor((y-1)/4)*raster.charWidth+1 - -- ocelot.log(x..","..y..":"..ci) - chunksAffected[ci] = true + local ci = math.floor((x-1)/2)+math.floor((y-1)/4)*raster.charWidth+1 + -- ocelot.log(x..","..y..":"..ci) + chunksAffected[ci] = true - return true + return true end function raster.get(x, y) - local i = x+y*raster.displayWidth - return display[i] or 0 + local i = x+y*raster.displayWidth + return display[i] or 0 end local function stats(arr) - local out = {} - for i=1,#arr do - local v = arr[i] - if out[v]==nil then - out[v]=1 - else - out[v] = out[v] + 1 - end + local out = {} + for i=1,#arr do + local v = arr[i] + if out[v]==nil then + out[v]=1 + else + out[v] = out[v] + 1 end - return out + end + return out end local function getKeys(t) - local keys = {} - for key, _ in pairs(t) do - table.insert(keys, key) - end - return keys + local keys = {} + for key, _ in pairs(t) do + table.insert(keys, key) + end + return keys end local function colorDifference(a,b) - return ((a>>16)&255)-((b>>16)&255)+((a>>8)&255)-((b>>8)&255)+(a&255)-(b&255) + return ((a>>16)&255)-((b>>16)&255)+((a>>8)&255)-((b>>8)&255)+(a&255)-(b&255) end local function limitTwoColors(arr) - local colors = getKeys(stats(arr)) - for i=1,#arr do - local v=arr[i] - if v==colors[1] then - arr[i]=0 - goto continue - elseif v==colors[2] then - arr[i]=1 - goto continue - else - --error("Pixel is not in the two colors (raster.lua:90)") - -- get closest color so atleast it kinda shows - if colorDifference(v,colors[1]) -dy then - err = err - dy - x1 = x1 + sx - end - - if e2 < dx then - err = err + dx - y1 = y1 + sy - end + if x1 == x2 and y1 == y2 then + break end + + local e2 = 2 * err + + if e2 > -dy then + err = err - dy + x1 = x1 + sx + end + + if e2 < dx then + err = err + dx + y1 = y1 + sy + end + end end function raster.drawRect(x1,y1,x2,y2,col) - x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2) - if x1 > x2 then x1, x2 = x2, x1 end - if y1 > y2 then y1, y2 = y2, y1 end - for x=x1,x2 do - raster.set(x,y1,col) - raster.set(x,y2,col) - end - for y=y1+1,y2-1 do - raster.set(x1,y,col) - raster.set(x2,y,col) - end + x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2) + if x1 > x2 then x1, x2 = x2, x1 end + if y1 > y2 then y1, y2 = y2, y1 end + for x=x1,x2 do + raster.set(x,y1,col) + raster.set(x,y2,col) + end + for y=y1+1,y2-1 do + raster.set(x1,y,col) + raster.set(x2,y,col) + end end function raster.fillRect(x1,y1,x2,y2,col) - x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2) - if x1 > x2 then x1, x2 = x2, x1 end - if y1 > y2 then y1, y2 = y2, y1 end - for x=x1,x2 do - for y=y1,y2 do - raster.set(x,y,col) - end + x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2) + if x1 > x2 then x1, x2 = x2, x1 end + if y1 > y2 then y1, y2 = y2, y1 end + for x=x1,x2 do + for y=y1,y2 do + raster.set(x,y,col) end + end end function raster.drawCircle(xc, yc, radius, color) - xc=math.floor(xc) - yc=math.floor(yc) - radius=math.floor(radius) - local x = 0 - local y = radius - local d = 3 - 2 * radius + xc=math.floor(xc) + yc=math.floor(yc) + radius=math.floor(radius) + local x = 0 + local y = radius + local d = 3 - 2 * radius + + while y >= x do + -- Draw 8 symmetric points + raster.set(xc + x, yc + y, color) + raster.set(xc - x, yc + y, color) + raster.set(xc + x, yc - y, color) + raster.set(xc - x, yc - y, color) + raster.set(xc + y, yc + x, color) + raster.set(xc - y, yc + x, color) + raster.set(xc + y, yc - x, color) + raster.set(xc - y, yc - x, color) - while y >= x do - -- Draw 8 symmetric points - raster.set(xc + x, yc + y, color) - raster.set(xc - x, yc + y, color) - raster.set(xc + x, yc - y, color) - raster.set(xc - x, yc - y, color) - raster.set(xc + y, yc + x, color) - raster.set(xc - y, yc + x, color) - raster.set(xc + y, yc - x, color) - raster.set(xc - y, yc - x, color) - - if d < 0 then - d = d + 4 * x + 6 - else - d = d + 4 * (x - y) + 10 - y = y - 1 - end - x = x + 1 + if d < 0 then + d = d + 4 * x + 6 + else + d = d + 4 * (x - y) + 10 + y = y - 1 end + x = x + 1 + end end function raster.drawEllipse(x1, y1, x2, y2, color) - if x1 > x2 then x1, x2 = x2, x1 end - if y1 > y2 then y1, y2 = y2, y1 end - - local xc = math.floor((x1 + x2) / 2) - local yc = math.floor((y1 + y2) / 2) - - local a = math.floor((x2 - x1) / 2) - local b = math.floor((y2 - y1) / 2) - - if a <= 0 or b <= 0 then - return + if x1 > x2 then x1, x2 = x2, x1 end + if y1 > y2 then y1, y2 = y2, y1 end + + local xc = math.floor((x1 + x2) / 2) + local yc = math.floor((y1 + y2) / 2) + + local a = math.floor((x2 - x1) / 2) + local b = math.floor((y2 - y1) / 2) + + if a <= 0 or b <= 0 then + return + end + + if a == b then + raster.drawCircle(xc, yc, a, color) + return + end + + if a <= 1 and b <= 1 then + raster.set(xc, yc, color) + return + elseif a <= 1 then + for y = yc - b, yc + b do + raster.set(xc, y, color) end - - if a == b then - raster.drawCircle(xc, yc, a, color) - return + return + elseif b <= 1 then + for x = xc - a, xc + a do + raster.set(x, yc, color) end + return + end + + local x = 0 + local y = b + local a2 = a * a + local b2 = b * b + + local d1 = b2 - (a2 * b) + (0.25 * a2) + local dx = 2 * b2 * x + local dy = 2 * a2 * y + + while dx < dy do + raster.set(xc + x, yc + y, color) + raster.set(xc - x, yc + y, color) + raster.set(xc + x, yc - y, color) + raster.set(xc - x, yc - y, color) - if a <= 1 and b <= 1 then - raster.set(xc, yc, color) - return - elseif a <= 1 then - for y = yc - b, yc + b do - raster.set(xc, y, color) - end - return - elseif b <= 1 then - for x = xc - a, xc + a do - raster.set(x, yc, color) - end - return + if d1 < 0 then + x = x + 1 + dx = dx + (2 * b2) + d1 = d1 + dx + b2 + else + x = x + 1 + y = y - 1 + dx = dx + (2 * b2) + dy = dy - (2 * a2) + d1 = d1 + dx - dy + b2 end + end + + local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2 + + while y >= 0 do + raster.set(xc + x, yc + y, color) + raster.set(xc - x, yc + y, color) + raster.set(xc + x, yc - y, color) + raster.set(xc - x, yc - y, color) - local x = 0 - local y = b - local a2 = a * a - local b2 = b * b - - local d1 = b2 - (a2 * b) + (0.25 * a2) - local dx = 2 * b2 * x - local dy = 2 * a2 * y - - while dx < dy do - raster.set(xc + x, yc + y, color) - raster.set(xc - x, yc + y, color) - raster.set(xc + x, yc - y, color) - raster.set(xc - x, yc - y, color) - - if d1 < 0 then - x = x + 1 - dx = dx + (2 * b2) - d1 = d1 + dx + b2 - else - x = x + 1 - y = y - 1 - dx = dx + (2 * b2) - dy = dy - (2 * a2) - d1 = d1 + dx - dy + b2 - end - end - - local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2 - - while y >= 0 do - raster.set(xc + x, yc + y, color) - raster.set(xc - x, yc + y, color) - raster.set(xc + x, yc - y, color) - raster.set(xc - x, yc - y, color) - - if d2 > 0 then - y = y - 1 - dy = dy - (2 * a2) - d2 = d2 - dy + a2 - else - y = y - 1 - x = x + 1 - dx = dx + (2 * b2) - dy = dy - (2 * a2) - d2 = d2 + dx - dy + a2 - end + if d2 > 0 then + y = y - 1 + dy = dy - (2 * a2) + d2 = d2 - dy + a2 + else + y = y - 1 + x = x + 1 + dx = dx + (2 * b2) + dy = dy - (2 * a2) + d2 = d2 + dx - dy + a2 end + end end function raster.fillCircle(x, y, r, color) - x, y = math.floor(x + 0.5), math.floor(y + 0.5) - r = math.floor(r + 0.5) - - if r <= 0 then return end - - local minX, maxX = x - r, x + r - local minY, maxY = y - r, y + r - - for py = minY, maxY do - for px = minX, maxX do - local dx, dy = px - x, py - y - local distSquared = dx*dx + dy*dy - - if distSquared <= r*r then - raster.set(px, py, color) - end - end + x, y = math.floor(x + 0.5), math.floor(y + 0.5) + r = math.floor(r + 0.5) + + if r <= 0 then return end + + local minX, maxX = x - r, x + r + local minY, maxY = y - r, y + r + + for py = minY, maxY do + for px = minX, maxX do + local dx, dy = px - x, py - y + local distSquared = dx*dx + dy*dy + + if distSquared <= r*r then + raster.set(px, py, color) + end end + end end function raster.fillEllipse(x1, y1, x2, y2, color) - local centerX = (x1 + x2) / 2 - local centerY = (y1 + y2) / 2 - - local a = math.abs(x2 - x1) / 2 - local b = math.abs(y2 - y1) / 2 - - centerX = math.floor(centerX + 0.5) - centerY = math.floor(centerY + 0.5) - a = math.floor(a + 0.5) - b = math.floor(b + 0.5) - - if a <= 0 or b <= 0 then return end - - if a == b then - raster.fillCircle(centerX, centerY, a, color) - return - end - - local minX = centerX - a - local maxX = centerX + a - local minY = centerY - b - local maxY = centerY + b - - for y = minY, maxY do - for x = minX, maxX do - local dx = x - centerX - local dy = y - centerY - local value = (dx*dx)/(a*a) + (dy*dy)/(b*b) - - if value <= 1 then - raster.set(x, y, color) - end - end + local centerX = (x1 + x2) / 2 + local centerY = (y1 + y2) / 2 + + local a = math.abs(x2 - x1) / 2 + local b = math.abs(y2 - y1) / 2 + + centerX = math.floor(centerX + 0.5) + centerY = math.floor(centerY + 0.5) + a = math.floor(a + 0.5) + b = math.floor(b + 0.5) + + if a <= 0 or b <= 0 then return end + + if a == b then + raster.fillCircle(centerX, centerY, a, color) + return + end + + local minX = centerX - a + local maxX = centerX + a + local minY = centerY - b + local maxY = centerY + b + + for y = minY, maxY do + for x = minX, maxX do + local dx = x - centerX + local dy = y - centerY + local value = (dx*dx)/(a*a) + (dy*dy)/(b*b) + + if value <= 1 then + raster.set(x, y, color) + end end + end end -return raster \ No newline at end of file +return raster diff --git a/home/longfile.txt b/home/longfile.txt new file mode 100644 index 0000000..411cf31 --- /dev/null +++ b/home/longfile.txt @@ -0,0 +1,67 @@ +This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! +This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! +This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! +This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! +This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! This is a long file! +This is a long line! This is a long line! This is a long line! This is a long line! This is a long line! This is a long line! This is a long line! This is a long line! This is a long line! +This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! This is a longer line! + +This is a short line! + +This is a short line! + +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This has a lot of lines! +This is the last line!