diff --git a/Applications.txt b/Applications.txt index fe7d5433..73293dbf 100644 --- a/Applications.txt +++ b/Applications.txt @@ -251,6 +251,12 @@ ----------------------------------------------------- Библиотеки -------------------------------------------------------------------------- + { + ["name"]="lib/doubleHeight.lua", + ["url"]="IgorTimofeev/OpenComputers/master/lib/doubleHeight.lua", + ["type"]="Library", + ["version"]=1.0, + }, { ["name"]="lib/json.lua", ["url"]="IgorTimofeev/OpenComputers/master/lib/json.lua", @@ -429,6 +435,14 @@ ["version"]=1.0, }, ----------------------------------------------------- Приложения без ресурсов -------------------------------------------------------------------------- + { + ["name"]="MineOS/Applications/Graph", + ["url"]="IgorTimofeev/OpenComputers/master/Applications/Graph/Graph.lua", + ["type"]="Application", + ["icon"]="IgorTimofeev/OpenComputers/master/Applications/Graph/Icon.pic", + ["createShortcut"]="desktop", + ["version"]=1.0, + }, { ["name"]="MineOS/Applications/FlappyBlock", ["url"]="IgorTimofeev/OpenComputers/master/Applications/FlappyBlock/FlappyBlock.lua", diff --git a/Applications/Graph/Graph.lua b/Applications/Graph/Graph.lua new file mode 100644 index 00000000..c3d5bcc0 --- /dev/null +++ b/Applications/Graph/Graph.lua @@ -0,0 +1,226 @@ + +_G.buffer = require("doubleBuffering") +buffer.start() +_G.doubleHeight = require("doubleHeight") +_G.unicode = require("unicode") +_G.event = require("event") +_G.ecs = require("ECSAPI") + +local xGraph, yGraph = math.floor(buffer.screen.width / 2), buffer.screen.height +local yDependencyString = "math.sin(x) * 5" +local graphScale = 4 +local graphResizeSpeed = 0.4 +local renderRange = 40 +local renderAccuracy = 0.4 +local axisColor = 0x333333 +local graphColor = 0x88FF88 +local selectionPointLineColor = 0x555555 +local selectionPointColor = 0x5555FF +local selectionTooltipTextColor = 0xFFFFFF +local buttonColor = 0xEEEEEE +local backgroundColor = 0x1b1b1b +local buttonTextColor = 0x1b1b1b +local buttonWidth = 20 +local selectedPoints = {{x = -5, y = 2}} + +------------------------------------------------------------------------------------------------------------------------------------------ +local buttons = {} + +local function assertString(x, yDependencyString) + local stro4ka = "local x = " .. x .. "; local y = " .. yDependencyString .. "; return y" + return pcall(load(stro4ka)) +end + +local function drawButtons() + buttons = {} + local buttonNames = {"Функция", "Масштаб", "Очистить точки", "Выход"} + local x, y = math.floor(buffer.screen.width / 2 - (#buttonNames * (buttonWidth + 2) - 2) / 2), buffer.screen.height - 4 + + for i = 1, #buttonNames do + buttons[buttonNames[i]] = { buffer.button(x, y, buttonWidth, 3, buttonColor, buttonTextColor, buttonNames[i]) } + x = x + buttonWidth + 2 + end +end + +local function drawHorizontalLine(x, y, x2, color) + for i = x, x2 do doubleHeight.set(i, y, color) end +end + +local function drawVerticalLine(x, y, y2, color) + for i = y, y2 do doubleHeight.set(x, i, color) end +end + +local function drawAxis() + drawHorizontalLine(1, yGraph, buffer.screen.width, axisColor) + drawVerticalLine(xGraph, 1, buffer.screen.height * 2, axisColor) +end + +local function limit(n) + if n > -300 and n < 300 then return true end +end + +local function drawGraph() + local xOld, yOld, xNew, yNew = math.huge, math.huge + for x = -renderRange, renderRange, renderAccuracy do + local success, result = assertString(x, yDependencyString) + if success then + if not (result ~= result) then + xNew, yNew = math.floor(xGraph + x * graphScale), math.floor(yGraph - result * graphScale) + + if limit(xOld) and limit(yOld) and limit(xNew) and limit(yNew) then + doubleHeight.line(xOld, yOld, xNew, yNew, graphColor) + end + + xOld, yOld = xNew, yNew + end + else + error(success, result) + end + end +end + +local function tooltip(x, y, tooltipColor, textColor, ...) + local stringArray = {...} + local maxTextLength = 0; for i = 1, #stringArray do maxTextLength = math.max(maxTextLength, unicode.len(stringArray[i])) end + + buffer.square(x, y, maxTextLength + 2, #stringArray, tooltipColor, textColor, " ") + x = x + 1 + for i = 1, #stringArray do + buffer.text(x, y, textColor, stringArray[i]) + y = y + 1 + end +end + +local function drawSelectedPoint(x, y, pointNumber) + local xOnScreen, yOnScreen = math.floor(xGraph + x * graphScale), math.floor(yGraph - y * graphScale) + + if xOnScreen <= xGraph then drawHorizontalLine(xOnScreen, yOnScreen, xGraph - 1, selectionPointLineColor) else drawHorizontalLine(xGraph + 1, yOnScreen, xOnScreen, selectionPointLineColor) end + if yOnScreen <= yGraph then drawVerticalLine(xOnScreen, yOnScreen, yGraph - 1, selectionPointLineColor) else drawVerticalLine(xOnScreen, yGraph + 1, yOnScreen, selectionPointLineColor) end + + doubleHeight.set(xOnScreen, yOnScreen, selectionPointColor) + + yOnScreen = math.ceil(yOnScreen / 2) + + tooltip(xOnScreen + 3, yOnScreen + 2, selectionPointLineColor, selectionTooltipTextColor, "Точка #" .. pointNumber, "x: " .. x, "y: " .. y) +end + +local function drawSelectedPoints() + if selectedPoints then + for i = 1, #selectedPoints do + drawSelectedPoint(selectedPoints[i].x, selectedPoints[i].y, i) + end + end +end + +local function drawAll() + buffer.clear(backgroundColor) + drawAxis() + drawGraph() + drawSelectedPoints() + drawButtons() + buffer.draw() +end + +local function clicked(x, y, object) + if x >= object[1] and y >= object[2] and x <= object[3] and y <= object[4] then return true end +end + +------------------------------------------------------------------------------------------------------------------------------------------ + +drawAll() + +local xMove, yMove +while true do + local e = {event.pull()} + if e[1] == "touch" then + if e[5] == 1 then + selectedPoints = selectedPoints or {} + table.insert(selectedPoints, { x = math.floor((e[3] - xGraph) / graphScale), y = math.floor((yGraph - e[4] * 2) / graphScale) }) + drawAll() + else + xMove, yMove = e[3], e[4] + + for key in pairs(buttons) do + if clicked(e[3], e[4], buttons[key]) then + buffer.button(buttons[key][1], buttons[key][2], buttonWidth, 3, graphColor, backgroundColor, key) + buffer.draw() + os.sleep(0.2) + drawAll() + + if key == "Функция" then + local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, + {"EmptyLine"}, + {"CenterText", ecs.colors.orange, "Функция"}, + {"EmptyLine"}, + {"Input", 0xFFFFFF, ecs.colors.orange, yDependencyString}, + {"EmptyLine"}, + {"CenterText", ecs.colors.orange, "Параметры рендера"}, + {"Slider", 0xFFFFFF, ecs.colors.orange, 1, 100, renderRange, "Диапазон: ", ""}, + {"Slider", 0xFFFFFF, ecs.colors.orange, 1, 100, renderAccuracy * 100, "Точность: ", "/100"}, + {"EmptyLine"}, + {"Button", {ecs.colors.orange, 0xffffff, "OK"}, {0x999999, 0xffffff, "Отмена"}} + ) + if data[4] == "OK" then + yDependencyString = data[1] + renderRange = tonumber(data[2]) + renderAccuracy = tonumber(data[3]) / 100 + drawAll() + end + elseif key == "Масштаб" then + local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, + {"EmptyLine"}, + {"CenterText", ecs.colors.orange, "Масштаб"}, + {"EmptyLine"}, + {"Slider", 0xFFFFFF, ecs.colors.orange, 1, 5000, math.floor(graphScale * 100), "", "%"}, + {"EmptyLine"}, + {"Button", {ecs.colors.orange, 0xffffff, "OK"}, {0x999999, 0xffffff, "Отмена"}} + ) + + if data[2] == "OK" then + graphScale = data[1] / 100 + drawAll() + end + elseif key == "Очистить точки" then + selectedPoints = nil + drawAll() + elseif key == "Выход" then + buffer.clear(0x262626) + buffer.draw() + return + end + + break + end + end + end + elseif e[1] == "drag" then + if e[5] ~= 1 then + local xDifference, yDifference = e[3] - xMove, e[4] - yMove + xGraph, yGraph = xGraph + xDifference, yGraph + yDifference * 2 + xMove, yMove = e[3], e[4] + drawAll() + end + elseif e[1] == "scroll" then + if e[5] == 1 then + graphScale = graphScale + graphResizeSpeed + else + graphScale = graphScale - graphResizeSpeed + if graphScale < graphResizeSpeed then graphScale = graphResizeSpeed end + end + drawAll() + elseif e[1] == "key_down" then + if e[4] == 28 then + selectedPoints = nil + drawAll() + end + end +end + + + + + + + + + diff --git a/Applications/Graph/Icon.pic b/Applications/Graph/Icon.pic new file mode 100644 index 00000000..9a02e45c Binary files /dev/null and b/Applications/Graph/Icon.pic differ diff --git a/lib/doubleHeight.lua b/lib/doubleHeight.lua new file mode 100644 index 00000000..a4af840f --- /dev/null +++ b/lib/doubleHeight.lua @@ -0,0 +1,80 @@ + +if not _G.buffer then _G.buffer = require("doubleBuffering") end +local doubleHeight = {} + +local upperPixel = "▀" +local lowerPixel = "▄" + +------------------------------------------------------------------------------------------------------------------------------------------ + +function doubleHeight.set(x, y, color) + if x >= 1 and x <= buffer.screen.width and y >= 1 and y <= buffer.screen.height * 2 then + local yFixed = math.ceil(y / 2) + local background, foreground, symbol = buffer.get(x, yFixed) + + if y % 2 == 0 then + if symbol == upperPixel then + buffer.set(x, yFixed, color, foreground, upperPixel) + else + buffer.set(x, yFixed, background, color, lowerPixel) + end + else + if symbol == lowerPixel then + buffer.set(x, yFixed, color, foreground, lowerPixel) + else + buffer.set(x, yFixed, background, color, upperPixel) + end + end + end +end + +local function swap(a, b) + return b, a +end + +function doubleHeight.line(x0, y0, x1, y1, color) + local steep = false; + + if math.abs(x0 - x1) < math.abs(y0 - y1 ) then + x0, y0 = swap(x0, y0) + x1, y1 = swap(x1, y1) + steep = true; + end + + if (x0 > x1) then + x0, x1 = swap(x0, x1) + y0, y1 = swap(y0, y1) + end + + local dx = x1 - x0; + local dy = y1 - y0; + local derror2 = math.abs(dy) * 2 + local error2 = 0; + local y = y0; + + for x = x0, x1, 1 do + if steep then + doubleHeight.set(y, x, color); + else + doubleHeight.set(x, y, color) + end + + error2 = error2 + derror2; + + if error2 > dx then + y = y + (y1 > y0 and 1 or -1); + error2 = error2 - dx * 2; + end + end +end + +------------------------------------------------------------------------------------------------------------------------------------------ + +return doubleHeight + + + + + + +