daily pastebin goal
34%
SHARE
TWEET

holoEdit.lua

ben_mkiv Apr 2nd, 2018 (edited) 91 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --       Hologram Editor v0.7.1-b2
  2. -- 2017 (c) Totoro (aka MoonlightOwl)
  3. --         computercraft.ru
  4.  
  5. local unicode = require('unicode')
  6. local event = require('event')
  7. local term = require('term')
  8. local fs = require('filesystem')
  9. local shell = require('shell')
  10. local com = require('component')
  11. local gpu = com.gpu
  12.  
  13. --     Colors     --
  14. local color = {
  15.   back = 0x000000,
  16.   fore = 0xFFFFFF,
  17.   info = 0x335555,
  18.   error = 0xFF3333,
  19.   help = 0x336600,
  20.   gold = 0xFFCC33,
  21.   gray = 0x080808,
  22.   lightgray = 0x333333,
  23.   lightlightgray = 0x666666
  24. }
  25.  
  26. --     Buttons    --
  27. local keys = {
  28.   BACKSPACE = 14,
  29.   EXIT = 16,
  30.   ENTER = 28,
  31.   ERASER = 41,
  32.   CLEAR = 211
  33. }
  34.  
  35. --  Localization  --
  36. local loc = {
  37.   FILE_REQUEST = 'Enter file name',
  38.   ERROR_CAPTION = 'Error',
  39.   WARNING_CAPTION = 'Warning',
  40.   DONE_CAPTION = 'Done',
  41.   PROJECTOR_UNAVAILABLE_MESSAGE = 'Projector not found!',
  42.   SAVING_MESSAGE = 'Saving...',
  43.   SAVED_MESSAGE = 'The file was saved!',
  44.   LOADING_MESSAGE = 'Loading...',
  45.   LOADED_MESSAGE = 'Loaded successfully!',
  46.   TOO_LOW_RESOLUTION_ERROR = '[ERROR] Your display/GPU does not support 80×25+ resolution.',
  47.   TOO_LOW_SCREEN_TIER_ERROR = '[ERROR] You can use the Tier 2 GPU, but you need Tier 3 display anyway.',
  48.   FORMAT_READING_ERROR = 'Invalid file format!',
  49.   FILE_NOT_FOUND_ERROR = 'File not found!',
  50.   CANNOT_OPEN_ERROR = 'Cannot open the file!',
  51.   CANNOT_SAVE_ERROR = 'Cannot write to the file!',
  52.   PALETTE_FRAME = 'Palette',
  53.   VIEWPORT_FRAME = 'Projection',
  54.   UTILS_FRAME = 'Management',
  55.   LAYER_LABEL = 'Hologram layer:',
  56.   GHOST_LAYER_LABEL = '\'Ghost\' layer:',
  57.   PROGRAMMERS_LABEL = 'Developers:',
  58.   CONTACT_LABEL = 'Contact:',
  59.   EXIT_LABEL = "Quit: 'Q' or ",
  60.   EXIT_BUTTON = 'Exit',
  61.   REFRESH_BUTTON = 'Refresh',
  62.   TOP_BUTTON = 'Top',
  63.   FRONT_BUTTON = 'Front',
  64.   SIDE_BUTTON = 'Side',
  65.   BELOW_BUTTON = 'Below',
  66.   ABOVE_BUTTON = 'Above',
  67.   CLEAR_BUTTON = 'Clear',
  68.   FILL_BUTTON = 'Fill',
  69.   TO_PROJECTOR = 'To Projector',
  70.   FROM_PROJECTOR = 'From Projector',
  71.   SAVE_BUTTON = 'Save',
  72.   LOAD_BUTTON = 'Load',
  73.   NEW_FILE_BUTTON = 'New file'
  74. }
  75. --      ****      --
  76.  
  77.  
  78. -- Try to load a component safely
  79. local function trytofind(name)
  80.   if com.isAvailable(name) then
  81.     return com.getPrimary(name)
  82.   else
  83.     return nil
  84.   end
  85. end
  86.  
  87. -- Constants --
  88. local OLDWIDTH, OLDHEIGHT = gpu.getResolution()
  89. local WIDTH, HEIGHT = gpu.maxResolution()
  90. local FULLSIZE = true
  91. local HOLOW, HOLOH = 48, 32        -- hologram size
  92. local MENUX = HOLOW*2+5            -- right panel offset
  93. local BUTTONW = 12                 -- standart button width
  94. local GRIDX, GRIDY = 3, 2          -- grid offset
  95. local TOP = { width = HOLOW, height = HOLOW, depth = HOLOH }
  96. local FRONT = { width = HOLOW, height = HOLOH, depth = HOLOW }
  97. local SIDE = { width = HOLOW, height = HOLOH, depth = HOLOW }
  98.  
  99. local fastRendering = true
  100.  
  101. -- Interface variables --
  102. local buttons = {}
  103. local textboxes = {}
  104. local repaint = false
  105.  
  106. -- App state --
  107. local colortable = {}
  108. local hexcolortable = {}
  109. local darkhexcolors = {}
  110. local brush = {color = 1, x = 8, cx = 8, moving = false}
  111. local ghost_layer = 1
  112. local ghost_layer_below = true
  113. local layer = 1
  114. local view = TOP
  115. local running = true
  116.  
  117. -- Auxiliary functions --
  118. local function rgb2hex(r,g,b)
  119.   return r*65536+g*256+b
  120. end
  121. local function setHexColor(n, r, g, b)
  122.   local hexcolor = rgb2hex(r,g,b)
  123.   hexcolortable[n] = hexcolor
  124.   darkhexcolors[n] = bit32.rshift(bit32.band(hexcolor, 0xfefefe), 1)
  125. end
  126.  
  127. local _f = gpu.getForeground()
  128. local function foreground(color)
  129.   if color ~= _f then gpu.setForeground(color); _f = color end
  130. end
  131. local _b = gpu.getBackground()
  132. local function background(color)
  133.   if color ~= _b then gpu.setBackground(color); _b = color end
  134. end
  135.  
  136. -- ========================================= H O L O G R A P H I C S ========================================= --
  137. local holo = {}
  138. local function set(x, y, z, value)
  139.   if holo[x] == nil then holo[x] = {} end
  140.   if holo[x][y] == nil then holo[x][y] = {} end
  141.   holo[x][y][z] = value
  142. end
  143. local function get(x, y, z)
  144.   if holo[x] ~= nil and holo[x][y] ~= nil and holo[x][y][z] ~= nil then
  145.     return holo[x][y][z]
  146.   else
  147.     return 0
  148.   end
  149. end
  150.  
  151. local function getProjector(x, y, z)
  152.   return require("component").hologram.get(x, y, z)
  153. end
  154.  
  155. local writer = {}
  156. function writer:init(file)
  157.   self.buffer = {}
  158.   self.file = file
  159. end
  160. function writer:write(sym)
  161.   table.insert(self.buffer, sym)
  162.   if #self.buffer >= 4 then self:finalize() end
  163. end
  164. function writer:finalize()
  165.   if #self.buffer > 0 then
  166.     local byte = 0
  167.     for i=4, 1, -1 do
  168.       local x = self.buffer[i] or 0
  169.       byte = byte * 4 + x
  170.     end
  171.     self.file:write(string.char(byte))
  172.     self.buffer = {}
  173.   end
  174. end
  175.  
  176. local function toBinary(x)
  177.   local data = {}
  178.   while x > 0 do
  179.     table.insert(data, x % 2)
  180.     x = math.floor(x / 2)
  181.   end
  182.   return data
  183. end
  184.  
  185. local function save(filename, compressed)
  186.   -- saving the palette
  187.   local file = io.open(filename, 'wb')
  188.   if file ~= nil then
  189.     for i=1, 3 do
  190.       for c=1, 3 do
  191.         file:write(string.char(colortable[i][c]))
  192.       end
  193.     end
  194.     writer:init(file)
  195.     if compressed then
  196.       local function put(symbol, length)
  197.         if length > 0 then
  198.           writer:write(symbol)
  199.           local l = toBinary(length + 1)
  200.           l[#l] = nil
  201.           l[1] = l[1] + 2
  202.           for i=#l, 1, -1 do writer:write(l[i]) end
  203.         end
  204.       end
  205.       local len = 0
  206.       local sym = -1
  207.       -- saving compressed data
  208.       for x=1, HOLOW do
  209.         for y=1, HOLOH do
  210.           for z=1, HOLOW do
  211.             local a = get(x, y, z)
  212.             if sym == a then  -- next symbol of the sequence
  213.               len = len + 1
  214.             else              -- first symbol of the sequence
  215.               put(sym, len)
  216.               len = 1
  217.               sym = a
  218.             end
  219.           end
  220.         end
  221.       end
  222.       put(sym, len)  -- the last sequence
  223.     else
  224.       -- saving the data without compression
  225.       for x=1, HOLOW do
  226.         for y=1, HOLOH do
  227.           for z=1, HOLOW do
  228.             writer:write(get(x, y, z))
  229.           end
  230.         end
  231.       end
  232.     end
  233.     writer:finalize()
  234.     file:close()
  235.  
  236.     local cf = io.open(filename..".raw", "w")
  237.     cf:write(require("serialization").serialize(convert2fill()))
  238.     cf:close()
  239.  
  240.     return true
  241.   else
  242.     return false, filename..": "..loc.CANNOT_SAVE_ERROR
  243.   end
  244. end
  245.  
  246. local reader = {}
  247. function reader:init(file)
  248.   self.buffer = {}
  249.   self.file = file
  250. end
  251. function reader:read()
  252.   if #self.buffer == 0 then
  253.     if not self:fetch() then return nil end
  254.   end
  255.   -- get the last symbol from the buffer
  256.   local sym = self.buffer[#self.buffer]
  257.   self.buffer[#self.buffer] = nil
  258.   return sym
  259. end
  260. function reader:fetch()
  261.   self.buffer = {}
  262.   local char = file:read(1)
  263.   if char == nil then return false
  264.   else
  265.     local byte = string.byte(char)
  266.     for i=0, 3 do
  267.       local a = byte % 4
  268.       byte = math.floor(byte / 4)
  269.       self.buffer[4-i] = a   -- writing bytes in reversed order
  270.     end
  271.     return true
  272.   end
  273. end
  274.  
  275. local function load(filename, compressed)
  276.   local path = shell.resolve(filename, "")
  277.   if path ~= nil then
  278.     file = io.open(filename, 'rb')
  279.     if file ~= nil then
  280.       -- loading the palette
  281.       for i=1, 3 do
  282.         for c=1, 3 do
  283.           colortable[i][c] = string.byte(file:read(1))
  284.         end
  285.         setHexColor(i,colortable[i][1],
  286.           colortable[i][2],
  287.           colortable[i][3])
  288.       end
  289.       -- loading the data
  290.       holo = {}
  291.       reader:init(file)
  292.       if compressed then          -- reading compressed data
  293.         local x, y, z = 1, 1, 1
  294.         while true do
  295.           local a = reader:read() -- reading symbol value
  296.           if a == nil then file:close(); return true end
  297.           local len = 1
  298.           while true do           -- reading binary length value
  299.             local b = reader:read()
  300.             if b == nil then
  301.               file:close()
  302.               if a == 0 then return true
  303.               else return false, filename..": "..loc.FORMAT_READING_ERROR end
  304.             end
  305.             local fin = (b > 1)
  306.             if fin then b = b-2 end
  307.             len = bit32.lshift(len, 1)
  308.             len = len + b
  309.             if fin then break end
  310.           end
  311.           len = len - 1
  312.           -- write the sequence
  313.           for i=1, len do
  314.             -- write one voxel
  315.             if a ~= 0 then set(x,y,z, a) end
  316.             -- move the coordinates
  317.             z = z+1
  318.             if z > HOLOW then
  319.               y = y+1
  320.               if y > HOLOH then
  321.                 x = x+1
  322.                 if x > HOLOW then file:close(); return true end
  323.                 y = 1
  324.               end
  325.               z = 1
  326.             end
  327.           end
  328.         end
  329.       else                        -- reading uncompressed data
  330.         for x=1, HOLOW do
  331.           for y=1, HOLOH do
  332.             for z=1, HOLOW do
  333.               local a = reader:read()
  334.               if a ~= 0 and a ~= nil then
  335.                 set(x,y,z, a)
  336.               end
  337.             end
  338.           end
  339.         end
  340.       end
  341.       file:close()
  342.       return true
  343.     else
  344.       return false, filename..": "..loc.CANNOT_OPEN_ERROR
  345.     end
  346.   else
  347.     return false, filename..": "..loc.FILE_NOT_FOUND_ERROR
  348.   end
  349. end
  350.  
  351. function renderFillData(data)
  352.   local p = trytofind('hologram')
  353.   for color=1,#data do for i=1,#data[color] do
  354.     p.fill(data[color][i].x, data[color][i].z, data[color][i].min, data[color][i].max, color)
  355.   end end
  356. end
  357.  
  358. function convert2fill()
  359.   local rows = { {}, {}, {} }
  360.   for x=1,HOLOW do
  361.     for z=1,HOLOW do
  362.       local stroke = { start = 0, color = 0 }
  363.       for y=1,HOLOH do
  364.         if get(x, y, z) ~= 0 then
  365.           if stroke.start == 0 then
  366.             stroke.start = y
  367.             stroke.color = get(x, y, z)
  368.           elseif stroke.color ~= get(x, y, z) then
  369.             table.insert(rows[stroke.color], { x = x, z = z, min = stroke.start, max = y })
  370.             stroke = { start = 0, color = 0 }
  371.           end
  372.         else
  373.           if stroke.start ~= 0 then
  374.             table.insert(rows[stroke.color], { x = x, z = z, min = stroke.start, max = y-1 })
  375.           end
  376.           stroke = { start = 0, color = 0 }
  377.         end
  378.       end
  379.       if stroke.start ~= 0 then
  380.         table.insert(rows[stroke.color], { x = x, z = z, min = stroke.start, max = HOLOH })
  381.       end
  382.     end end
  383.  
  384.   return rows
  385. end
  386.  
  387. -- ============================================== B U T T O N S ============================================== --
  388. local Button = {}
  389. Button.__index = Button
  390. function Button.new(func, x, y, text, fore, back, width, nu)
  391.   self = setmetatable({}, Button)
  392.  
  393.   self.form = '[ '
  394.   if width == nil then width = 0
  395.   else width = (width - unicode.len(text))-4 end
  396.   for i=1, math.floor(width/2) do
  397.     self.form = self.form.. ' '
  398.   end
  399.   self.form = self.form..text
  400.   for i=1, math.ceil(width/2) do
  401.     self.form = self.form.. ' '
  402.   end
  403.   self.form = self.form..' ]'
  404.  
  405.   self.func = func
  406.  
  407.   self.x = math.floor(x); self.y = math.floor(y)
  408.   self.fore = fore
  409.   self.back = back
  410.   self.visible = true
  411.  
  412.   self.notupdate = nu or false
  413.  
  414.   return self
  415. end
  416. function Button:draw(fore, back)
  417.   if self.visible then
  418.     local fore = fore or self.fore
  419.     local back = back or self.back
  420.     foreground(fore)
  421.     background(back)
  422.     gpu.set(self.x, self.y, self.form)
  423.   end
  424. end
  425. function Button:click(x, y)
  426.   if self.visible then
  427.     if y == self.y then
  428.       if x >= self.x and x < self.x+unicode.len(self.form) then
  429.         self:draw(self.back, self.fore)
  430.         local data = self.func()
  431.         if not self.notupdate then self:draw() end
  432.         return true, data
  433.       end
  434.     end
  435.   end
  436.   return false
  437. end
  438.  
  439. local function buttonNew(buttons, func, x, y, text, fore, back, width, notupdate)
  440.   local button = Button.new(func, x, y, text, fore, back, width, notupdate)
  441.   table.insert(buttons, button)
  442.   return button
  443. end
  444. local function buttonsDraw(buttons)
  445.   for i=1, #buttons do
  446.     buttons[i]:draw()
  447.   end
  448. end
  449. local function buttonsClick(buttons, x, y)
  450.   for i=1, #buttons do
  451.     local ok, data = buttons[i]:click(x, y)
  452.     if ok then return data end
  453.   end
  454.   return nil
  455. end
  456.  
  457.  
  458. -- ============================================ T E X T B O X E S ============================================ --
  459. local Textbox = {}
  460. Textbox.__index = Textbox
  461. function Textbox.new(validator, func, x, y, width, value, defValue)
  462.   self = setmetatable({}, Textbox)
  463.   self.x = math.floor(x); self.y = math.floor(y)
  464.   self.width = width or BUTTONW
  465.   self.valueWidth = self.width - 3
  466.   self.form = '>' .. string.rep(' ', self.width - 1)
  467.   self.validator = validator
  468.   self.func = func
  469.   self.value = tostring(value)
  470.   self.defValue = tostring(defValue)
  471.   self.visible = true
  472.   self.active = false
  473.   return self
  474. end
  475. function Textbox:renderForm()
  476.   foreground(color.fore)
  477.   background(self.active and color.info or color.lightgray)
  478.   gpu.set(self.x, self.y, self.form)
  479. end
  480. function Textbox:renderValue(value)
  481.   foreground(color.fore)
  482.   background(self.active and color.info or color.lightgray)
  483.   local value = value or self.value
  484.   if unicode.len(value) > self.valueWidth then
  485.     value = string.sub(value, -self.valueWidth)
  486.   end
  487.   if self.active then value = value .. '_' end
  488.   gpu.set(self.x+2, self.y, tostring(value))
  489. end
  490. function Textbox:draw()
  491.   if self.visible then
  492.     self:renderForm()
  493.     if not self.active and (unicode.len(self.value) == 0) then
  494.       self:renderValue(self.defValue)
  495.     else
  496.       self:renderValue()
  497.     end
  498.   end
  499. end
  500. function Textbox:click(x, y)
  501.   if self.visible then
  502.     if y == self.y then
  503.       if x >= self.x and x < self.x+self.width then
  504.         -- textbox captures the "focus"
  505.         self.active = true
  506.         self:draw()
  507.         local value = self.value
  508.         while true do
  509.           local name, a, char, code = event.pull()
  510.           if name == 'key_down' then
  511.             if char > 30 then
  512.               local letter = unicode.char(char)
  513.               value = value .. letter
  514.               self:renderValue(value)
  515.             elseif code == keys.BACKSPACE then
  516.               if unicode.len(value) > 0 then
  517.                 value = unicode.sub(value, 1, -2)
  518.                 self:renderForm()
  519.                 self:renderValue(value)
  520.               end
  521.             elseif code == keys.ENTER then break end
  522.           elseif name == 'touch' then break end
  523.         end
  524.         -- textbox loses the "focus"
  525.         self.active = false
  526.         if self.validator(value) then
  527.           self.value = value
  528.           self.func(value)
  529.         end
  530.         self:draw()
  531.         return true
  532.       end
  533.     end
  534.   end
  535.   return false
  536. end
  537.  
  538. local function textboxNew(textboxes, validator, func, x, y, width, value, defValue)
  539.   textbox = Textbox.new(validator, func, x, y, width, value, defValue)
  540.   table.insert(textboxes, textbox)
  541.   return textbox
  542. end
  543. local function textboxesDraw(textboxes)
  544.   for i=1, #textboxes do
  545.     textboxes[i]:draw()
  546.   end
  547. end
  548. local function textboxesClick(textboxes, x, y)
  549.   for i=1, #textboxes do
  550.     textboxes[i]:click(x, y)
  551.   end
  552. end
  553.  
  554.  
  555. -- ============================================= G R A P H I C S ============================================= --
  556. local gridLine1, gridLine2, gridLine1s, gridLine2s = nil, nil, nil, nil
  557. local strLine = "+"
  558. local colorCursorY, colorCursorWidth = 8, 8
  559. local function initGraphics()
  560.   -- grid prefabs
  561.   if FULLSIZE then gridLine1 = string.rep("██  ", HOLOW/2)
  562.   else
  563.     gridLine1 = string.rep("▀", HOLOW/2)
  564.     gridLine2 = string.rep("▄", HOLOW/2)
  565.     gridLine1s = string.rep("▀", HOLOH/2)
  566.     gridLine2s = string.rep("▄", HOLOH/2)
  567.   end
  568.   -- lines prefabs
  569.   for i=1, WIDTH do
  570.     strLine = strLine..'-'
  571.   end
  572.   -- palette cursor params
  573.   if not FULLSIZE then
  574.     colorCursorY, colorCursorWidth = 1, 7
  575.   end
  576. end
  577.  
  578. -- draw a line
  579. local function line(x1, x2, y)
  580.   gpu.set(x1,y,string.sub(strLine, 1, x2-x1))
  581.   gpu.set(x2,y,'+')
  582. end
  583.  
  584. -- draw a frame
  585. local function frame(x1, y1, x2, y2, caption, nobottom)
  586.   line(x1, x2, y1)
  587.   if not nobottom then line(x1, x2, y2) end
  588.   if caption ~= nil then
  589.     gpu.set(x1 + math.ceil((x2-x1)/2) - math.ceil(unicode.len(caption)/2), y1, caption)
  590.   end
  591. end
  592.  
  593. -- draw a grid
  594. local function drawGrid(x, y)
  595.   foreground(color.gray)
  596.   background(color.back)
  597.   gpu.fill(0, y, MENUX, HOLOW, ' ')
  598.   if FULLSIZE then
  599.     for i = 0, view.height - 1 do
  600.       gpu.set(x + (i%2)*2, y + i, gridLine1)
  601.     end
  602.     if view.height + y < HEIGHT then
  603.       foreground(color.fore)
  604.       line(1, MENUX-1, y+HOLOH)
  605.     end
  606.   else
  607.     for i = 0, view.height - 1 do
  608.       if view == TOP then
  609.         if i%2 == 0 then gpu.set(x + i, y, gridLine1, true)
  610.         else gpu.set(x+i, y, gridLine2, true) end
  611.       else
  612.         if i%2 == 0 then gpu.set(x + i, y, gridLine1s, true)
  613.         else gpu.set(x+i, y, gridLine2s, true) end
  614.       end
  615.     end
  616.   end
  617. end
  618.  
  619. -- draw a colored rectangle
  620. local function drawRect(x, y, fill)
  621.   foreground(color.fore)
  622.   background(color.gray)
  623.   gpu.set(x, y,   "╓──────╖")
  624.   gpu.set(x, y+1, "║      ║")
  625.   gpu.set(x, y+2, "╙──────╜")
  626.   foreground(fill)
  627.   gpu.set(x+2, y+1, "████")
  628. end
  629. local function drawSmallRect(x, y, fill)
  630.   foreground(color.fore)
  631.   gpu.set(x, y,   "╓─────╖")
  632.   gpu.set(x, y+1, "║     ║")
  633.   gpu.set(x, y+2, "╙─────╜")
  634.   foreground(fill)
  635.   gpu.set(x+2, y+1, "███")
  636. end
  637.  
  638. -- draw palette selection menu
  639. local function drawPaletteFrame()
  640.   foreground(color.fore)
  641.   background(color.back)
  642.   if FULLSIZE then
  643.     frame(MENUX, 3, WIDTH-2, 16, "[ "..loc.PALETTE_FRAME.." ]", true)
  644.     for i=0, 3 do
  645.       drawRect(MENUX+1+i*colorCursorWidth, 5, hexcolortable[i])
  646.     end
  647.     foreground(0xFF0000); gpu.set(MENUX+1, 10, "R:")
  648.     foreground(0x00FF00); gpu.set(MENUX+1, 11, "G:")
  649.     foreground(0x0000FF); gpu.set(MENUX+1, 12, "B:")
  650.   else
  651.     for i=0, 3 do
  652.       drawSmallRect(MENUX+1+i*colorCursorWidth, 2, hexcolortable[i])
  653.     end
  654.     foreground(0xFF0000); gpu.set(MENUX+1, 5, "R:")
  655.     foreground(0x00FF00); gpu.set(MENUX+11, 5, "G:")
  656.     foreground(0x0000FF); gpu.set(MENUX+21, 5, "B:")
  657.   end
  658. end
  659. -- draw and move palette selector
  660. local function drawColorCursor(force)
  661.   if force or brush.moving then
  662.     foreground(color.fore)
  663.     background(color.back)
  664.     if FULLSIZE then gpu.set(MENUX+2+brush.cx, colorCursorY, "      ")
  665.     else gpu.set(MENUX+2+brush.cx, colorCursorY, "-----") end
  666.  
  667.     if brush.moving then
  668.       if brush.x ~= brush.color * colorCursorWidth then brush.x = brush.color*colorCursorWidth end
  669.       if brush.cx < brush.x then brush.cx = brush.cx + 1
  670.       elseif brush.cx > brush.x then brush.cx = brush.cx - 1
  671.       else brush.moving = false end
  672.     end
  673.  
  674.     if FULLSIZE then
  675.       background(color.lightgray)
  676.       gpu.set(MENUX+2+brush.cx, colorCursorY, ":^^^^:")
  677.     else gpu.set(MENUX+2+brush.cx, colorCursorY, ":vvv:") end
  678.   end
  679. end
  680. local function drawLayerFrame()
  681.   foreground(color.fore)
  682.   background(color.back)
  683.   if FULLSIZE then
  684.     frame(MENUX, 16, WIDTH-2, 28, "[ "..loc.VIEWPORT_FRAME.." ]", true)
  685.     gpu.set(MENUX+13, 18, loc.LAYER_LABEL)
  686.     gpu.set(MENUX+1, 23, loc.GHOST_LAYER_LABEL)
  687.   else
  688.     gpu.set(MENUX+1, 8, loc.LAYER_LABEL)
  689.   end
  690. end
  691. local function drawUtilsFrame()
  692.   foreground(color.fore)
  693.   background(color.back)
  694.   frame(MENUX, 28, WIDTH-2, 36, "[ "..loc.UTILS_FRAME.." ]")
  695. end
  696.  
  697. local function mainScreen()
  698.   foreground(color.fore)
  699.   background(color.back)
  700.   term.clear()
  701.   frame(1,1, WIDTH, HEIGHT, "{ Hologram Editor }", not FULLSIZE)
  702.   -- "canvas"
  703.   drawGrid(GRIDX, GRIDY)
  704.  
  705.   drawPaletteFrame()
  706.   drawLayerFrame()
  707.   drawUtilsFrame()
  708.  
  709.   drawColorCursor(true)
  710.   buttonsDraw(buttons)
  711.   textboxesDraw(textboxes)
  712.  
  713.   -- "about"
  714.   foreground(color.info)
  715.   background(color.gray)
  716.   if FULLSIZE then
  717.     gpu.set(MENUX+3, HEIGHT-11, "   Hologram Editor  v0.7.1-b1")
  718.     foreground(color.fore)
  719.     gpu.set(MENUX+3, HEIGHT-10, "            * * *            ")
  720.     gpu.set(MENUX+3, HEIGHT-8,  "  Totoro  (aka MoonlightOwl) ")
  721.     gpu.set(MENUX+3, HEIGHT-7,  "            * * *            ")
  722.     gpu.set(MENUX+3, HEIGHT-5,  "       computercraft.ru      ")
  723.     foreground(color.lightlightgray)
  724.     gpu.set(MENUX+3, HEIGHT-9,  " "..loc.PROGRAMMERS_LABEL..string.rep(' ', 28-unicode.len(loc.PROGRAMMERS_LABEL)))
  725.     gpu.set(MENUX+3, HEIGHT-6,  " "..loc.CONTACT_LABEL..string.rep(' ', 28-unicode.len(loc.CONTACT_LABEL)))
  726.     foreground(color.fore)
  727.     background(color.back)
  728.     gpu.set(MENUX+1, HEIGHT-2, loc.EXIT_LABEL)
  729.   else
  730.     gpu.set(MENUX+1, HEIGHT-2,  "  Totoro  computercraft.ru  ")
  731.     foreground(color.fore)
  732.     background(color.back)
  733.     gpu.set(MENUX+1, HEIGHT, loc.EXIT_LABEL)
  734.   end
  735. end
  736.  
  737.  
  738. -- ============================================= M E S S A G E S ============================================= --
  739. local function showMessage(text, caption, textcolor)
  740.   local caption = '[ '..caption..' ]'
  741.   local x = MENUX/2 - unicode.len(text)/2 - 4
  742.   local y = HEIGHT/2 - 2
  743.   foreground(color.fore)
  744.   background(color.back)
  745.   gpu.fill(x, y, unicode.len(text)+9, 5, ' ')
  746.   frame(x, y, x+unicode.len(text)+8, y+4, caption)
  747.   foreground(textcolor)
  748.   gpu.set(x+4,y+2, text)
  749.   -- "canvas" must be rerendered
  750.   repaint = true
  751. end
  752.  
  753.  
  754. -- =============================================== L A Y E R S =============================================== --
  755. local function project(x, y, layer, view)
  756.   if view == TOP then
  757.     return x, layer, y
  758.   elseif view == FRONT then
  759.     return x, HOLOH-y+1, layer
  760.   else
  761.     return layer, HOLOH-y+1, x
  762.   end
  763. end
  764. local function getVoxelColor(x, y, z, grid)
  765.   local voxel = get(x, y, z)
  766.   if voxel ~= 0 then return hexcolortable[voxel]
  767.   elseif grid then return color.gray
  768.   else return color.back end
  769. end
  770. local function drawVoxel(sx, sy, nogrid)
  771.   if FULLSIZE then
  772.     local voxel = get(project(sx, sy, layer, view))
  773.     local dx = (GRIDX-2) + sx*2
  774.     local dy = (GRIDY-1) + sy
  775.     if voxel ~= 0 then
  776.       foreground(hexcolortable[voxel])
  777.       gpu.set(dx, dy, "██")
  778.     else
  779.       local ghost = get(project(sx, sy, ghost_layer, view))
  780.       if ghost ~= 0 then
  781.         foreground(darkhexcolors[ghost])
  782.         gpu.set(dx, dy, "░░")
  783.       elseif not nogrid then
  784.         if (sx+sy)%2 == 0 then foreground(color.gray)
  785.         else foreground(color.back) end
  786.         gpu.set(dx, dy, "██")
  787.       end
  788.     end
  789.   else
  790.     local sxUp, syUp = sx, sy
  791.     if syUp%2 == 0 then syUp = syUp-1 end
  792.     local sxDown, syDown = sxUp, syUp + 1
  793.     local dx, dy = (GRIDX-1) + sxUp, (GRIDY-1) + math.ceil(syUp/2)
  794.     local a, b, c = project(sxUp, syUp, layer, view)
  795.     foreground(getVoxelColor(a, b, c, ((sxUp+syUp)%2 == 0)))
  796.     a, b, c = project(sxDown, syDown, layer, view)
  797.     background(getVoxelColor(a, b, c, ((sxDown+syDown)%2 == 0)))
  798.     gpu.set(dx, dy, "▀")
  799.   end
  800. end
  801.  
  802. function drawLayer()
  803.   drawGrid(GRIDX, GRIDY)
  804.   local step
  805.   if FULLSIZE then step = 1 else step = 2 end
  806.   for x = 1, view.width do
  807.     for y = 1, view.height, step do drawVoxel(x, y, true) end
  808.   end
  809.   -- no need to rerender the screen
  810.   repaint = false
  811. end
  812. local function fillLayer(value)
  813.   local value = value or brush.color
  814.   for x = 1, view.width do
  815.     for y = 1, view.height do
  816.       local vx, vy, vz = project(x, y, layer, view)
  817.       set(vx, vy, vz, value)
  818.     end
  819.   end
  820.   drawLayer()
  821. end
  822. local function clearLayer()
  823.   fillLayer(0)
  824. end
  825.  
  826.  
  827. -- ==================================== G U I   F U N C T I O N A L I T Y ==================================== --
  828. local function exit() running = false end
  829.  
  830. local function nextGhost()
  831.   if ghost_layer_below then
  832.     ghost_layer_below = false
  833.     if ghost_layer < view.depth then
  834.       ghost_layer = layer + 1
  835.     else ghost_layer = view.depth end
  836.     drawLayer()
  837.   else
  838.     if ghost_layer < view.depth then
  839.       ghost_layer = ghost_layer + 1
  840.       drawLayer()
  841.     end
  842.   end
  843.   tb_ghostlayer.value = '>'; tb_ghostlayer:draw()
  844. end
  845. local function prevGhost()
  846.   if not ghost_layer_below then
  847.     ghost_layer_below = true
  848.     if layer > 1 then
  849.       ghost_layer = layer - 1
  850.     else ghost_layer = 1 end
  851.     drawLayer()
  852.   else
  853.     if ghost_layer > 1 then
  854.       ghost_layer = ghost_layer - 1
  855.       drawLayer()
  856.     end
  857.   end
  858.   tb_ghostlayer.value = '<'; tb_ghostlayer:draw()
  859. end
  860. local function setGhostLayer(value)
  861.   local n = tonumber(value)
  862.   if n == nil or n < 1 or n > view.depth then return false end
  863.   ghost_layer = n
  864.   drawLayer()
  865.   return true
  866. end
  867. local function moveGhost()
  868.   if ghost_layer_below then
  869.     if layer > 1 then ghost_layer = layer - 1
  870.     else ghost_layer = 1 end
  871.   else
  872.     if layer < view.depth then ghost_layer = layer + 1
  873.     else ghost_layer = view.depth end
  874.   end
  875. end
  876.  
  877. local function nextLayer()
  878.   if layer < view.depth then
  879.     layer = layer + 1
  880.     tb_layer.value = layer
  881.     tb_layer:draw()
  882.     moveGhost()
  883.     drawLayer()
  884.   end
  885. end
  886. local function prevLayer()
  887.   if layer > 1 then
  888.     layer = layer - 1
  889.     tb_layer.value = layer
  890.     tb_layer:draw()
  891.     moveGhost()
  892.     drawLayer()
  893.   end
  894. end
  895. local function setLayer(value)
  896.   local n = tonumber(value)
  897.   if n == nil or n < 1 or n > view.depth then return false end
  898.   layer = n
  899.   moveGhost()
  900.   drawLayer()
  901.   tb_layer.value = layer
  902.   tb_layer:draw()
  903.   return true
  904. end
  905.  
  906. local function setFilename(str)
  907.   return str ~= nil and str ~= '' and unicode.len(str) < 30
  908. end
  909.  
  910. local function changeColor(rgb, value)
  911.   if value == nil then return false end
  912.   n = tonumber(value)
  913.   if n == nil or n < 0 or n > 255 then return false end
  914.   -- saving data to the table
  915.   colortable[brush.color][rgb] = n
  916.   setHexColor(brush.color, colortable[brush.color][1],
  917.     colortable[brush.color][2],
  918.     colortable[brush.color][3])
  919.   -- refresh colors palette
  920.   drawPaletteFrame()
  921.   return true
  922. end
  923. local function changeRed(value) return changeColor(1, value) end
  924. local function changeGreen(value) return changeColor(2, value) end
  925. local function changeBlue(value) return changeColor(3, value) end
  926.  
  927. local function moveSelector(num)
  928.   if num == 0 and brush.color ~= 0 then
  929.     tb_red.visible = false
  930.     tb_green.visible = false
  931.     tb_blue.visible = false
  932.     background(color.back)
  933.     if FULLSIZE then
  934.       gpu.fill(MENUX+3, 10, 45, 3, ' ')
  935.     else
  936.       gpu.set(MENUX+3, 5, '      ')
  937.       gpu.set(MENUX+13, 5, '      ')
  938.       gpu.set(MENUX+23, 5, '      ')
  939.     end
  940.   elseif num ~= 0 and brush.color == 0 then
  941.     tb_red.visible = true; tb_red:draw()
  942.     tb_green.visible = true; tb_green:draw()
  943.     tb_blue.visible = true; tb_blue:draw()
  944.   end
  945.   brush.color = num
  946.   brush.moving = true
  947.   tb_red.value = colortable[num][1]; tb_red:draw()
  948.   tb_green.value = colortable[num][2]; tb_green:draw()
  949.   tb_blue.value = colortable[num][3]; tb_blue:draw()
  950. end
  951.  
  952. local function setView(value, norefresh)
  953.   view = value
  954.   if layer > view.depth then layer = view.depth end
  955.   if not norefresh then drawLayer() end
  956. end
  957. local function setTopView(norefresh) setView(TOP, norefresh) end
  958. local function setFrontView() setView(FRONT) end
  959. local function setSideView() setView(SIDE) end
  960.  
  961. local function renderHologram(projector)
  962.   -- send the data
  963.   for x=1, HOLOW do
  964.     for y=1, HOLOH do
  965.       for z=1, HOLOW do
  966.         n = get(x,y,z)
  967.         if n ~= 0 then
  968.           if depth == 2 then
  969.             projector.set(x,y,z,n)
  970.           else
  971.             projector.set(x,y,z,1)
  972.           end
  973.         end
  974.       end
  975.     end
  976.   end
  977. end
  978.  
  979. local function drawHologram()
  980.   -- check for a projector availability
  981.   local projector = trytofind('hologram')
  982.   if projector ~= nil then
  983.     local depth = projector.maxDepth()
  984.     -- clean him up
  985.     projector.clear()
  986.     -- send the palette
  987.     if depth == 2 then
  988.       for i=1, 3 do
  989.         projector.setPaletteColor(i, hexcolortable[i])
  990.       end
  991.     else
  992.       projector.setPaletteColor(1, hexcolortable[1])
  993.     end
  994.  
  995.     if fastRendering then
  996.       renderFillData(convert2fill())
  997.     else
  998.       renderHologram(projector)
  999.     end
  1000.   else
  1001.     showMessage(loc.PROJECTOR_UNAVAILABLE_MESSAGE, loc.ERROR_CAPTION, color.error)
  1002.   end
  1003. end
  1004.  
  1005. local function newHologram()
  1006.   holo = {}
  1007.   drawLayer()
  1008. end
  1009.  
  1010. local function saveHologram()
  1011.   local filename = tb_file.value
  1012.   if filename ~= loc.FILE_REQUEST then
  1013.     -- show a warning
  1014.     showMessage(loc.SAVING_MESSAGE, loc.WARNING_CAPTION, color.gold)
  1015.     local compressed = true
  1016.     -- add our 'brand' file extensions =)
  1017.     if string.sub(filename, -3) == '.3d' then compressed = false
  1018.     elseif string.sub(filename, -4) ~= '.3dx' then
  1019.       filename = filename..'.3dx'
  1020.     end
  1021.     -- save
  1022.     local ok, message = save(filename, compressed)
  1023.     if ok then
  1024.       showMessage(loc.SAVED_MESSAGE, loc.DONE_CAPTION, color.gold)
  1025.     else
  1026.       showMessage(message, loc.ERROR_CAPTION, color.error)
  1027.     end
  1028.   end
  1029. end
  1030.  
  1031. local function loadHologram()
  1032.   local filename = tb_file.value
  1033.   if filename ~= loc.FILE_REQUEST then
  1034.     -- show a warning
  1035.     showMessage(loc.LOADING_MESSAGE, loc.WARNING_CAPTION, color.gold)
  1036.     local compressed = nil
  1037.     -- add our 'brand' file extensions =)
  1038.     if string.sub(filename, -3) == '.3d' then compressed = false
  1039.     elseif string.sub(filename, -4) == '.3dx' then compressed = true end
  1040.     -- load
  1041.     local ok, message = nil, nil
  1042.     if compressed ~= nil then
  1043.       ok, message = load(filename, compressed)
  1044.     else
  1045.       -- if no file extension vas specified, try both alternately
  1046.       ok, message = load(filename..'.3dx', true)
  1047.       if not ok then
  1048.         ok, message = load(filename..'.3d', false)
  1049.       end
  1050.     end
  1051.     if ok then
  1052.       -- refresh textboxes
  1053.       tb_red.value = colortable[brush.color][1]; tb_red:draw()
  1054.       tb_green.value = colortable[brush.color][2]; tb_green:draw()
  1055.       tb_blue.value = colortable[brush.color][3]; tb_blue:draw()
  1056.       -- refresh the palette
  1057.       drawPaletteFrame()
  1058.       -- reset the viewport
  1059.       setTopView(true)
  1060.       setLayer(1)
  1061.     else
  1062.       showMessage(message, loc.ERROR_CAPTION, color.error)
  1063.     end
  1064.   end
  1065. end
  1066.  
  1067. function dec2rgb(col)
  1068.   local rgb = {}
  1069.   local bit32 = require "bit32"
  1070.  
  1071.   rgb[1] = bit32.band(bit32.lshift(col, 16), 255)
  1072.   rgb[2] = bit32.band(bit32.lshift(col, 8), 255)
  1073.   rgb[3] = bit32.band(col, 255)
  1074.  
  1075.   return rgb
  1076. end
  1077.  
  1078.  
  1079. local function getHologram()
  1080.   -- show a warning
  1081.   showMessage(loc.LOADING_MESSAGE, loc.WARNING_CAPTION, color.gold)
  1082.   -- load
  1083.   local ok, message = nil, nil
  1084.  
  1085.   holo = {}
  1086.   for x=1, HOLOW do
  1087.     for y=1, HOLOH do
  1088.       for z=1, HOLOW do
  1089.         set(x, y, z, getProjector(x, y, z))
  1090.       end
  1091.     end
  1092.   end
  1093.  
  1094.   local projector = trytofind('hologram')
  1095.   if projector ~= nil then
  1096.     -- refresh textboxes
  1097.     tb_red.value = dec2rgb(projector.getPaletteColor(1))
  1098.     tb_green.value = dec2rgb(projector.getPaletteColor(2))
  1099.     tb_blue.value = dec2rgb(projector.getPaletteColor(3))
  1100.   end
  1101.  
  1102.   -- refresh the palette
  1103.   drawPaletteFrame()
  1104.  
  1105.   -- reset the viewport
  1106.   setTopView(true)
  1107.   setLayer(1)
  1108.   drawLayer()
  1109. end
  1110.  
  1111.  
  1112.  
  1113. -- =========================================== M A I N   C Y C L E =========================================== --
  1114. -- initialization
  1115. -- check screen resolution; you must have tier 2/3 GPU and tier 3 monitor to work
  1116. if HEIGHT < HOLOW/2 then
  1117.   error(loc.TOO_LOW_RESOLUTION_ERROR)
  1118. elseif HEIGHT < HOLOW+2 then
  1119.   com.screen.setPrecise(true)
  1120.   if not com.screen.isPrecise() then error(loc.TOO_LOW_SCREEN_TIER) end
  1121.   FULLSIZE = false
  1122.   MENUX = HOLOW + 2
  1123.   color.gray = color.lightgray
  1124.   GRIDX = 1
  1125.   GRIDY = 2
  1126.   BUTTONW = 9
  1127. else
  1128.   com.screen.setPrecise(false)
  1129.   WIDTH = HOLOW*2 + 40
  1130.   HEIGHT = HOLOW + 2
  1131. end
  1132. gpu.setResolution(WIDTH, HEIGHT)
  1133. foreground(color.fore)
  1134. background(color.back)
  1135.  
  1136. -- set default palette
  1137. colortable = {{255, 0, 0}, {0, 255, 0}, {0, 102, 255}}
  1138. colortable[0] = {0, 0, 0}  -- eraser
  1139. for i=0, 3 do setHexColor(i, colortable[i][1], colortable[i][2], colortable[i][3]) end
  1140.  
  1141. initGraphics()
  1142.  
  1143. -- generate interface
  1144. if FULLSIZE then
  1145.   buttonNew(buttons, exit, WIDTH-BUTTONW-2, HEIGHT-2, loc.EXIT_BUTTON, color.back, color.error, BUTTONW, true)
  1146.   buttonNew(buttons, drawLayer, MENUX+11, 14, loc.REFRESH_BUTTON, color.back, color.gold, BUTTONW)
  1147.   buttonNew(buttons, prevLayer, MENUX+1, 19, '-', color.fore, color.info, 5)
  1148.   buttonNew(buttons, nextLayer, MENUX+7, 19, '+', color.fore, color.info, 5)
  1149.   buttonNew(buttons, setTopView, MENUX+1, 21, loc.TOP_BUTTON, color.fore, color.info, 10)
  1150.   buttonNew(buttons, setFrontView, MENUX+12, 21, loc.FRONT_BUTTON, color.fore, color.info, 10)
  1151.   buttonNew(buttons, setSideView, MENUX+24, 21, loc.SIDE_BUTTON, color.fore, color.info, 9)
  1152.  
  1153.   buttonNew(buttons, prevGhost, MENUX+1, 24, loc.BELOW_BUTTON, color.fore, color.info, 6)
  1154.   buttonNew(buttons, nextGhost, MENUX+10, 24, loc.ABOVE_BUTTON, color.fore, color.info, 6)
  1155.  
  1156.   buttonNew(buttons, clearLayer, MENUX+1, 26, loc.CLEAR_BUTTON, color.fore, color.info, BUTTONW)
  1157.   buttonNew(buttons, fillLayer, MENUX+2+BUTTONW, 26, loc.FILL_BUTTON, color.fore, color.info, BUTTONW)
  1158.   buttonNew(buttons, getHologram, MENUX+8, 29, loc.FROM_PROJECTOR, color.back, color.gold, 16)
  1159.   buttonNew(buttons, drawHologram, MENUX+9, 30, loc.TO_PROJECTOR, color.back, color.gold, 16)
  1160.   buttonNew(buttons, saveHologram, MENUX+1, 33, loc.SAVE_BUTTON, color.fore, color.help, BUTTONW)
  1161.   buttonNew(buttons, loadHologram, MENUX+8+BUTTONW, 33, loc.LOAD_BUTTON, color.fore, color.info, BUTTONW)
  1162.   buttonNew(buttons, newHologram, MENUX+1, 35, loc.NEW_FILE_BUTTON, color.fore, color.info, BUTTONW)
  1163. else
  1164.   buttonNew(buttons, exit, WIDTH-BUTTONW-1, HEIGHT, loc.EXIT_BUTTON, color.back, color.error, BUTTONW, true)
  1165.   buttonNew(buttons, drawLayer, MENUX+9, 6, loc.REFRESH_BUTTON, color.back, color.gold, BUTTONW)
  1166.   buttonNew(buttons, prevLayer, MENUX+1, 9, '-', color.fore, color.info, 5)
  1167.   buttonNew(buttons, nextLayer, MENUX+7, 9, '+', color.fore, color.info, 5)
  1168.   buttonNew(buttons, setTopView, MENUX+1, 11, loc.TOP_BUTTON, color.fore, color.info, 8)
  1169.   buttonNew(buttons, setFrontView, MENUX+10, 12, loc.FRONT_BUTTON, color.fore, color.info, 8)
  1170.   buttonNew(buttons, setSideView, MENUX+20, 13, loc.SIDE_BUTTON, color.fore, color.info, 8)
  1171.  
  1172.   buttonNew(buttons, clearLayer, MENUX+1, 15, loc.CLEAR_BUTTON, color.fore, color.info, BUTTONW)
  1173.   buttonNew(buttons, fillLayer, MENUX+14, 15, loc.FILL_BUTTON, color.fore, color.info, BUTTONW)
  1174.  
  1175.   buttonNew(buttons, drawHologram, MENUX+7, 17, loc.TO_PROJECTOR, color.back, color.gold, 16)
  1176.   buttonNew(buttons, saveHologram, MENUX+1, 20, loc.SAVE_BUTTON, color.fore, color.help, BUTTONW)
  1177.   buttonNew(buttons, loadHologram, MENUX+16, 20, loc.LOAD_BUTTON, color.fore, color.info, BUTTONW)
  1178.   buttonNew(buttons, newHologram, MENUX+1, 21, loc.NEW_FILE_BUTTON, color.fore, color.info, BUTTONW)
  1179. end
  1180.  
  1181. local function isNumber(value)
  1182.   return tonumber(value) ~= nil
  1183. end
  1184. local function correctLayer(value)
  1185.   local n = tonumber(value)
  1186.   return n ~= nil and n > 0 and n <= view.depth
  1187. end
  1188.  
  1189. tb_red, tb_green, tb_blue, tb_layer, tb_ghostlayer, tb_file = nil, nil, nil, nil, nil, nil
  1190. if FULLSIZE then
  1191.   tb_red = textboxNew(textboxes, isNumber, changeRed, MENUX+5, 10, WIDTH-MENUX-7, '255')
  1192.   tb_green = textboxNew(textboxes, isNumber, changeGreen, MENUX+5, 11, WIDTH-MENUX-7, '0')
  1193.   tb_blue = textboxNew(textboxes, isNumber, changeBlue, MENUX+5, 12, WIDTH-MENUX-7, '0')
  1194.   tb_layer = textboxNew(textboxes, correctLayer, setLayer, MENUX+13, 19, WIDTH-MENUX-15, '1')
  1195.   tb_ghostlayer = textboxNew(textboxes, correctLayer, setGhostLayer, MENUX+19, 24, WIDTH-MENUX-21, '')
  1196.   tb_file = textboxNew(textboxes, function() return true end, setFilename, MENUX+1, 32, WIDTH-MENUX-3, '', loc.FILE_REQUEST)
  1197. else
  1198.   tb_red = textboxNew(textboxes, isNumber, changeRed, MENUX+3, 5, 6, '255')
  1199.   tb_green = textboxNew(textboxes, isNumber, changeGreen, MENUX+13, 5, 6, '0')
  1200.   tb_blue = textboxNew(textboxes, isNumber, changeBlue, MENUX+23, 5, 6, '0')
  1201.   tb_layer = textboxNew(textboxes, correctLayer, setLayer, MENUX+13, 9, WIDTH-MENUX-14, '1')
  1202.   tb_file = textboxNew(textboxes, function() return true end, setFilename, MENUX+1, 19, WIDTH-MENUX-2, '', loc.FILE_REQUEST)
  1203. end
  1204.  
  1205. mainScreen()
  1206. moveSelector(1)
  1207.  
  1208. local function delay(active) if active then return 0.02 else return 2.0 end end
  1209.  
  1210. while running do
  1211.   local name, add, x, y, button = event.pull(delay(brush.moving))
  1212.  
  1213.   if name == 'key_down' then
  1214.     -- if 'Q' was pressed - the quit app
  1215.     if y == keys.EXIT then
  1216.       exit()
  1217.     elseif y == keys.ERASER then
  1218.       moveSelector(0)
  1219.     elseif y >= 2 and y <= 4 then
  1220.       moveSelector(y - 1)
  1221.     elseif y == keys.CLEAR then
  1222.       clearLayer()
  1223.     end
  1224.   elseif name == 'touch' or name == 'drag' then
  1225.     -- rerender the screen after message box
  1226.     if repaint then drawLayer()
  1227.     else
  1228.       if name == 'touch' then
  1229.         -- check the GUI
  1230.         buttonsClick(buttons, math.ceil(x), math.ceil(y))
  1231.         textboxesClick(textboxes, math.ceil(x), math.ceil(y))
  1232.         -- select a color
  1233.         if x > MENUX+1 and x < MENUX+37 then
  1234.           if FULLSIZE then
  1235.             if y > 4 and y < 8 then
  1236.               moveSelector(math.floor((x-MENUX-1)/colorCursorWidth))
  1237.             end
  1238.           else
  1239.             if y > 1 and y < 4 and x < WIDTH-2 then
  1240.               moveSelector(math.floor((x-MENUX-1)/colorCursorWidth))
  1241.             end
  1242.           end
  1243.         end
  1244.       end
  1245.  
  1246.       -- "render"
  1247.       local dx, dy
  1248.       if FULLSIZE then
  1249.         if x >= GRIDX and x < GRIDX + view.width*2 then
  1250.           if y >= GRIDY and y < GRIDY + view.height then
  1251.             dx, dy = math.floor((x-GRIDX)/2)+1, math.floor(y-GRIDY+1)
  1252.           end
  1253.         end
  1254.       else
  1255.         if x >= (GRIDX - 1) and x <= GRIDX + view.width then
  1256.           if y >= (GRIDY - 1) and y <= GRIDY + view.height/2 then
  1257.             dx, dy = math.floor(x - GRIDX + 2), math.floor((y - GRIDY + 1) * 2) + 1
  1258.           end
  1259.         end
  1260.       end
  1261.       if dx ~= nil then
  1262.         local a, b, c = project(dx, dy, layer, view)
  1263.         if button == 0 then set(a, b, c, brush.color)
  1264.         else set(a, b, c, 0) end
  1265.         drawVoxel(dx, dy)
  1266.       end
  1267.     end
  1268.   end
  1269.  
  1270.   drawColorCursor()
  1271. end
  1272.  
  1273. -- finalization
  1274. foreground(0xFFFFFF)
  1275. background(0x000000)
  1276. gpu.setResolution(OLDWIDTH, OLDHEIGHT)
  1277. term.clear()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top