Asioron

charts

Dec 12th, 2018
229
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --original http://computercraft.ru/topic/1883-biblioteka-dlya-sozdaniya-gistogramm-i-progressbarov
  2.  
  3. local bit32 = require("bit32")
  4. local component = require("component")
  5.  
  6. local sides = {
  7.   TOP = 1,
  8.   BOTTOM = 2,
  9.   LEFT = 3,
  10.   RIGHT = 4,
  11.   "TOP",
  12.   "BOTTOM",
  13.   "LEFT",
  14.   "RIGHT"
  15. }
  16.  
  17. local function patch(base, head)
  18.   for k, v in pairs(head) do
  19.     if type(v) ~= "table" or type(base[k]) ~= "table" then
  20.       base[k] = v
  21.     else
  22.       patch(base[k], v)
  23.     end
  24.   end
  25. end
  26.  
  27. local Histogram
  28.  
  29. do
  30.   local characters = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
  31.   local meta = {}
  32.   meta.__index = meta
  33.  
  34.   local function calcHeight(height, perc)
  35.     return math.floor(perc * height * 8)
  36.   end
  37.  
  38.   local function getBarChars(height, halfHeight, totalHeight)
  39.     if height < 0 then
  40.       height = -height
  41.       local spaces = math.floor(height / 8)
  42.       local part = characters[8 - (height - spaces * 8) + 1]
  43.       if spaces * 8 == height then
  44.         part = ""
  45.       end
  46.       local blocks = totalHeight - halfHeight
  47.       return characters[9]:rep(blocks) .. characters[1]:rep(spaces) .. part
  48.     end
  49.     local blocks = math.floor(height / 8)
  50.     local part = characters[height - blocks * 8 + 1]
  51.     if blocks * 8 == height then
  52.       part = ""
  53.     end
  54.     local spaces = halfHeight - blocks - (part ~= "" and 1 or 0)
  55.     return characters[1]:rep(spaces) .. part .. characters[9]:rep(blocks)
  56.   end
  57.  
  58.   function meta:draw(container)
  59.     if self.max == self.min and self.max == 0 then
  60.       error("min and max are both 0!")
  61.     end
  62.     local loopStart, loopEnd = 1, container.width
  63.     if self.align == sides.RIGHT then
  64.       loopEnd = #self.values
  65.       loopStart = loopEnd - container.width + 1
  66.     end
  67.     local max = self.max - self.min
  68.     local min = 0
  69.     local bar = 1
  70.  
  71.     local levelY = self.level.y
  72.     if levelY > 0 and levelY < 1 then
  73.       levelY = levelY * container.height
  74.     elseif levelY < 0 then
  75.       levelY = container.height + levelY + 1
  76.     end
  77.     levelY = math.floor(levelY)
  78.     if levelY > container.height then
  79.       levelY = container.height
  80.     end
  81.  
  82.     local levelV = self.level.value or self.min
  83.     if levelV < self.min or levelV > self.max then
  84.       error("invalid level value set!")
  85.     end
  86.  
  87.     for i = loopStart, loopEnd do
  88.       local value = self.values[i] or levelV
  89.       if value < self.min or value > self.max then
  90.         error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. value)
  91.       end
  92.       local v = value - levelV
  93.       local halfH = v < 0 and levelY or container.height - levelY
  94.  
  95.       local perc
  96.       if v < 0 then
  97.         perc = (levelV + value) / (levelV - self.min)
  98.       else
  99.         perc = (value - levelV) / (self.max - levelV)
  100.       end
  101.       if v == 0 and max == 0 then
  102.         perc = 1
  103.       end
  104.  
  105.       local height = calcHeight(halfH, perc)
  106.       local chars = getBarChars(height, halfH, container.height)
  107.  
  108.       local fg, bg = self.colorFunc(i, perc, value, self, container)
  109.       fg = fg or container.fg
  110.       bg = bg or container.bg
  111.  
  112.       if v < 0 then
  113.         fg, bg = bg, fg
  114.       end
  115.  
  116.       if container.gpu.getForeground() ~= fg then
  117.         container.gpu.setForeground(fg)
  118.       end
  119.  
  120.       if container.gpu.getBackground() ~= bg then
  121.         container.gpu.setBackground(bg)
  122.       end
  123.  
  124.       container.gpu.set(container:getX() + bar - 1,
  125.                         container:getY(),
  126.                         chars,
  127.                         true)
  128.       bar = bar + 1
  129.     end
  130.   end
  131.  
  132.   Histogram = function(tbl)
  133.     local obj = {
  134.       values = {},
  135.       align = sides.LEFT,
  136.       colorFunc = function() end,
  137.       min = 0,
  138.       max = 1,
  139.       level = {
  140.         y = 0,
  141.         value = nil
  142.       }
  143.     }
  144.     if tbl then
  145.       patch(obj, tbl)
  146.     end
  147.     return setmetatable(obj, meta)
  148.   end
  149. end
  150.  
  151. local ProgressBar
  152. do
  153.   local meta = {}
  154.   meta.__index = meta
  155.   local charsV = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
  156.   local charsH = {" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"}
  157.  
  158.   local function calcLength(perc, max)
  159.     return math.floor(perc * 8 * max), max * 8
  160.   end
  161.  
  162.   local function getChars(direction, len, max)
  163.     if direction == sides.RIGHT then
  164.       local blocks = math.floor(len / 8)
  165.       local part = charsH[len - blocks * 8 + 1]
  166.       if blocks * 8 == len then
  167.         part = ""
  168.       end
  169.       local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
  170.       return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
  171.     elseif direction == sides.LEFT then
  172.       local spaces = math.floor(len / 8)
  173.       local part = charsH[9 - (len - spaces * 8)]
  174.       if spaces * 8 == len then
  175.         part = ""
  176.       end
  177.       local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
  178.       return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
  179.     elseif direction == sides.TOP then
  180.       local blocks = math.floor(len / 8)
  181.       local part = charsV[len - blocks * 8 + 1]
  182.       if blocks * 8 == len then
  183.         part = ""
  184.       end
  185.       local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
  186.       return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
  187.     elseif direction == sides.BOTTOM then
  188.       local spaces = math.floor(len / 8)
  189.       local part = charsV[9 - (len - spaces * 8)]
  190.       if spaces * 8 == len then
  191.         part = ""
  192.       end
  193.       local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
  194.       return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
  195.     end
  196.   end
  197.  
  198.   function meta:draw(container)
  199.     local min = 0
  200.     local max = self.max - self.min
  201.     if min == max and min == 0 then
  202.       error("min and max values are the same")
  203.     end
  204.     local value = self.value - self.min
  205.     if not (value >= min and value <= max) then
  206.       error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. self.value)
  207.     end
  208.     local perc = value / max
  209.     local maxLength = container.width
  210.     if self.direction == sides.TOP or self.direction == sides.BOTTOM then
  211.       maxLength = container.height
  212.     end
  213.     local chars = getChars(self.direction, calcLength(perc, maxLength))
  214.     local fg, bg = self.colorFunc(self.value, perc, self, container)
  215.     fg = fg or container.fg
  216.     bg = bg or container.bg
  217.     if self.direction == sides.LEFT or self.direction == sides.BOTTOM then
  218.       fg, bg = bg, fg
  219.     end
  220.     if container.gpu.getForeground() ~= fg then
  221.       container.gpu.setForeground(fg)
  222.     end
  223.     if container.gpu.getBackground() ~= bg then
  224.       container.gpu.setBackground(bg)
  225.     end
  226.     if self.direction == sides.TOP or self.direction == sides.BOTTOM then
  227.       for x = 1, container.width, 1 do
  228.         container.gpu.set(container:getX() + x - 1, container:getY(), chars, true)
  229.       end
  230.     else
  231.       for y = 1, container.height, 1 do
  232.         container.gpu.set(container:getX(), container:getY() + y - 1, chars)
  233.       end
  234.     end
  235.   end
  236.  
  237.   ProgressBar = function(tbl)
  238.     local obj = {
  239.       direction = sides.RIGHT,
  240.       min = 0,
  241.       max = 1,
  242.       value = 0,
  243.       colorFunc = function(value, perc, prbar, container) end
  244.     }
  245.     if tbl then
  246.       patch(obj, tbl)
  247.     end
  248.     return setmetatable(obj, meta)
  249.   end
  250. end
  251.  
  252. local Container
  253. do
  254.   local meta = {}
  255.   meta.__index = meta
  256.  
  257.   function meta:draw()
  258.     if self.payload then
  259.       local fg = self.gpu.getForeground()
  260.       local bg = self.gpu.getBackground()
  261.       if fg ~= self.fg then
  262.         self.gpu.setForeground(self.fg)
  263.       end
  264.       if bg ~= self.bg then
  265.         self.gpu.setBackground(self.bg)
  266.       end
  267.  
  268.       self.gpu.fill(self.x, self.y, self.width, self.height, " ")
  269.       self.payload:draw(self)
  270.  
  271.       if self.gpu.getForeground() ~= fg then
  272.         self.gpu.setForeground(fg)
  273.       end
  274.       if self.gpu.getBackground() ~= bg then
  275.         self.gpu.setBackground(bg)
  276.       end
  277.     end
  278.   end
  279.  
  280.   function meta:getX()
  281.     return self.x + self.payloadX - 1
  282.   end
  283.  
  284.   function meta:getY()
  285.     return self.y + self.payloadY - 1
  286.   end
  287.  
  288.   Container = function(tbl)
  289.     local obj = {
  290.       gpu = component.gpu,
  291.       fg = 0xffffff,
  292.       bg = 0x000000,
  293.       x = 1,
  294.       y = 1,
  295.       payloadX = 1,
  296.       payloadY = 1,
  297.       width = 80,
  298.       height = 25,
  299.       payload = nil
  300.     }
  301.     if tbl then
  302.       patch(obj, tbl)
  303.     end
  304.     return setmetatable(obj, meta)
  305.   end
  306. end
  307.  
  308. return {
  309.   sides = sides,
  310.   Container = Container,
  311.   Histogram = Histogram,
  312.   ProgressBar = ProgressBar
  313. }
RAW Paste Data