Advertisement
KotyaYT

HoloEdit

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