Advertisement
Symmetryc

Grapher

Nov 23rd, 2013
368
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.83 KB | None | 0 0
  1. -- Grapher, By Symmetryc
  2.  
  3. -- Buffer
  4. local max_x, max_y = term.getSize()
  5. local function copy(_t, _loc)
  6.     local t = _loc or {}
  7.     for k, v in pairs(_t) do
  8.         t[k] = type(v) == "table" and copy(v) or v
  9.     end
  10.     return t
  11. end
  12. local equal = function(_t1, _t2)
  13.     for i = 1, 3 do
  14.         if _t1[i] ~= _t2[i] then
  15.             return false
  16.         end
  17.     end
  18.     return true
  19. end
  20. local round = function(_n)
  21.     return math.floor(_n + 0.5)
  22. end
  23. local index = function(_t)
  24.     return function(self, key)
  25.         self[key] = setmetatable({}, {
  26.             __index = function(self2, key2)
  27.                 self2[key2] = copy(_t)
  28.                 return self2[key2]
  29.             end;
  30.         })
  31.         return self[key]
  32.     end
  33. end
  34. local cache, buffer
  35. buffer = {
  36.     init = function(_t)
  37.         cache = setmetatable({}, {
  38.             __index = index(_t)
  39.         })
  40.     end;
  41.     new_fn = function(_f, _color)
  42.         return setmetatable(buffer.new(), {
  43.             __index = function(self, key)
  44.                 self[key] = setmetatable({}, {
  45.                     __index = function(self2, key2)
  46.                         self2[key2] = setmetatable({}, {
  47.                             __index = function(self3, key3)
  48.                                 return round(_f(key)) == key2 and (key3 == 1 and _color or key3 == 3 and (cache[key][key2][3] or " ")) or nil
  49.                             end;
  50.                         })
  51.                         return self2[key2]
  52.                     end;
  53.                 })
  54.                 return self[key]
  55.             end;
  56.         })
  57.     end;
  58.     new = function()
  59.         return setmetatable(
  60.             {
  61.                 rect = function(self, _x, _y, _x2, _y2, _color)
  62.                     for x = _x, _x2 do
  63.                         for y = _y, _y2 do
  64.                             local pixel = self[x][y]
  65.                             pixel[1] = _color
  66.                             pixel[3] = pixel[3] or " "
  67.                         end
  68.                     end
  69.                     return self
  70.                 end;
  71.                 line = function(self, _x, _y, _x2, _y2, _color, _int)
  72.                     if _x - _x2 == 0 or _y - _y2 == 0 then
  73.                         return self:rect(_x, _y, _x2, _y2, _color)
  74.                     end
  75.                     local slope = (_y - _y2) / (_x - _x2)
  76.                     local y_int = _y - (_x * slope)
  77.                     local f = function(x)
  78.                         return x * slope + y_int
  79.                     end
  80.                     return self:fn(f, _x, _x2, _color, _int or 1/slope)
  81.                 end;
  82.                 write = function(self, _x, _y, _text, _color, _back)
  83.                     local i = 0
  84.                     for x = _x, _x + #_text - 1 do
  85.                         i = i + 1
  86.                         local pixel = self[x][_y]
  87.                         pixel[1] = _back or pixel[1]
  88.                         pixel[2] = _color or pixel[2]
  89.                         pixel[3] = _text:sub(i, i)
  90.                     end
  91.                 end;
  92.                 draw = function(self)
  93.                     buffer.draw(self:render())
  94.                     return self
  95.                 end;
  96.                 render = function(self)
  97.                     local screen = setmetatable({}, {__index = index({})})
  98.                     for x = 1, max_x do
  99.                         local t1, t2, t3 = screen[x], self[x], cache[x]
  100.                         for y = 1, max_y do
  101.                             local p1, p2, p3 = t1[y], t2[y], t3[y]
  102.                             for i = 1, 3 do
  103.                                 p1[i] = p2[i] or p3[i]
  104.                             end
  105.                         end
  106.                     end
  107.                     return screen
  108.                 end;
  109.             },
  110.             {
  111.                 __index = index({});
  112.             }
  113.         )
  114.     end;
  115.     draw = function(_t)
  116.         local pos_x, pos_y = term.getCursorPos()
  117.         local ins = {"setBackgroundColor", "setTextColor", "write"}
  118.         for x = 1, max_x do
  119.             local t1, t2 = _t[x], cache[x]
  120.             for y = 1, max_y do
  121.                 local p1, p2 = t1[y], t2[y]
  122.                 if not equal(p1, p2) then
  123.                     term.setCursorPos(x, y)
  124.                     for i = 1, 3 do
  125.                         term[ins[i]](p1[i] or p2[i])
  126.                         p2[i] = p1[i]
  127.                     end
  128.                 end
  129.             end
  130.         end
  131.         term.setCursorPos(pos_x, pos_y)
  132.         return _t
  133.     end;
  134. }
  135.  
  136. -- General
  137. local x, y = term.getSize()
  138. buffer.init({colors.white, colors.lightGray, " "})
  139. term.setBackgroundColor(colors.white)
  140. term.clear()
  141.  
  142. -- help Buffer
  143. local help = buffer.new()
  144. help:line(3, y - 1, x - 1, y - 1, colors.black)
  145. help:line(x - 1, 3, x - 1, y - 1, colors.black)
  146. help:rect(2, 2, x - 2, y - 2, colors.lightBlue)
  147. help:write(math.floor(x / 2 - 2), 3, "Help", colors.white)
  148. local help_text = {
  149.     "^ + H : Help";
  150.     "^ + Q : Quit";
  151.     "^ + WASD : Move Grid";
  152.     "Arrow Up / Down : Change Function Number";
  153.     "Enter : Render Graph";
  154.     "Scroll : Change Zoom";
  155.     "Normal Text : Creating Functions";
  156.     "*Note* When creating functions, you can not";
  157.     "only use normal math i.e. \"f(01) = x ^ 2 * 3\",";
  158.     "but you can also use any standard Lua Math";
  159.     "Library functions by omitting the \"math.\"";
  160.     "i.e. \"f(02) = atan2(x, x^2) + sin(abs(x))\"";
  161. }
  162. for i = 1, #help_text do
  163.     help:write(4, i + 4, help_text[i], colors[i < 8 and "white" or "red"])
  164. end
  165. help = help:render()
  166.  
  167. -- grid Buffer
  168. local grid = buffer.new()
  169. local chars = {{" ", "-"}, {"|", "+"}}
  170. for i = 1, x do
  171.     for j = 1, y - 2 do
  172.         grid[i][j] = {colors.white, colors.lightGray, chars[i % 2 == 0 and 1 or 2][j % 2 == 0 and 1 or 2]}
  173.     end
  174. end
  175. grid[1][1] = {colors.white, colors.black, "#"}
  176. grid:write(1, y - 1, ("-"):rep(x), colors.black, colors.white)
  177. grid = grid:render()
  178.  
  179. -- Setting Up Graph
  180. local input = buffer.new()
  181. local color = {
  182.     colors.lime;
  183.     colors.lightBlue;
  184.     colors.orange;
  185.     colors.magenta;
  186.     colors.yellow;
  187.     colors.red;
  188. }
  189. local flist = {}
  190. local f = 1
  191. for i = 1, 99 do
  192.     flist[i] = ""
  193. end
  194. local graph = {
  195.     new = function(self, _str, _n)
  196.         local func = loadstring("return ".._str:gsub("(%a%w+)", "math.%1"))
  197.         if not func then
  198.             flist[_n] = "error!"
  199.             self.cursor = 0
  200.             self:update_info()
  201.             return
  202.         end
  203.         self[_n] = buffer.new_fn(function(input_x)
  204.             return setfenv(func, {
  205.                 x = (input_x + self.pos[1] - 1) / self.zoom;
  206.                 math = _G.math;
  207.             })() * -self.zoom + self.pos[2] + 1
  208.         end, color[_n % #color])
  209.         self:draw()
  210.     end;
  211.     zoom = 1;
  212.     pos = {0, 0};
  213.     cursor = 0;
  214.     cursor_pos = function(self)
  215.         return #self:info_str() - #flist[f] + self.cursor + 1
  216.     end;
  217.     info_str = function(self)
  218.         return "("..self.pos[1]..","..self.pos[2]..") | 1:"..self.zoom.." | f("..("0"):rep(2 - #(f..""))..f..")="..flist[f]
  219.     end;
  220.     update_info = function(self)
  221.         local info = x - #self:info_str()
  222.         input:write(1, y, self:info_str()..(" "):rep(info > 0 and info or 0), colors.black)
  223.         input:draw()
  224.     end;
  225.     draw = function(self)
  226.         buffer.draw(grid)
  227.         for k, v in pairs(self) do
  228.             if type(v) == "table" and v.draw then
  229.                 flist[k] = pcall(v.draw, v) and flist[k] or "error!"
  230.             end
  231.         end
  232.         self.cursor = 0
  233.         self:update_info()
  234.     end;
  235.     draw_help = function(self)
  236.         term.setCursorBlink(false)
  237.         buffer.draw(help)
  238.         os.pullEvent("mouse_click")
  239.         buffer.draw(grid)
  240.         self:draw()
  241.         term.setCursorBlink(true)
  242.     end;
  243. }
  244.  
  245. -- Actual Program
  246. graph:draw_help()
  247. local main = function()
  248.     while true do
  249.         term.setCursorPos(graph:cursor_pos(), y)
  250.         os.queueEvent("")
  251.         os.pullEvent()
  252.         local event, p1, p2, p3 = os.pullEvent()
  253.         if event == "mouse_scroll" then
  254.             graph.zoom = graph.zoom * 2 ^ p1
  255.             graph:draw()
  256.         elseif event == "key" then
  257.             os.startTimer(0)
  258.             local e1, e2 = os.pullEvent()
  259.             if e1 == "char" and os.pullEvent("timer") then
  260.                 flist[f] = flist[f]:sub(1, graph.cursor)..e2..flist[f]:sub(graph.cursor + 1)
  261.                 graph.cursor = graph.cursor + 1
  262.                 graph:update_info()
  263.             else
  264.                 if p1 == keys.backspace and graph.cursor > 0 then
  265.                     flist[f] = flist[f]:sub(1, graph.cursor - 1)..flist[f]:sub(graph.cursor + 1)
  266.                     graph.cursor = graph.cursor - 1
  267.                     graph:update_info()
  268.                 elseif p1 == keys.up and f < 99 or p1 == keys.down and f > 1 then
  269.                     f = f + (p1 == keys.up and 1 or -1)
  270.                     graph.cursor = 0
  271.                     graph:update_info()
  272.                 elseif p1 == keys.enter then
  273.                     graph:new(flist[f], f)
  274.                 elseif p1 == keys.right and graph.cursor < #flist[f] or p1 == keys.left and graph.cursor > 0 then
  275.                     graph.cursor = graph.cursor + (p1 == keys.right and 1 or -1)
  276.                 elseif p1 == keys.q then
  277.                     term.setCursorBlink(false)
  278.                     term.setBackgroundColor(colors.black)
  279.                     term.setTextColor(colors.lightBlue)
  280.                     local end_text = "Thank you for using Grapher!"
  281.                     term.clear()
  282.                     term.setCursorPos((x - #end_text) / 2, 1)
  283.                     print(end_text)
  284.                     break
  285.                 elseif p1 == keys.h then
  286.                     graph:draw_help()
  287.                 elseif p1 == keys.a or p1 == keys.d or p1 == keys.s or p1 == keys.w then
  288.                     graph.pos[1] = graph.pos[1] + (p1 == keys.a and -1 or p1 == keys.d and 1 or 0)
  289.                     graph.pos[2] = graph.pos[2] + (p1 == keys.s and -1 or p1 == keys.w and 1 or 0)
  290.                     graph:draw()
  291.                 end
  292.             end
  293.         end
  294.     end
  295. end
  296. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement