Create HoloEdit.lua

This commit is contained in:
Igor Timofeev 2015-09-04 07:31:32 +03:00
parent ca15f0232b
commit d9e191dd7b

View File

@ -0,0 +1,781 @@
-- Hologram Editor
-- by NEO, Totoro
-- 10/14/2014, all right reserved =)
local unicode = require('unicode')
local event = require('event')
local term = require('term')
local fs = require('filesystem')
local com = require('component')
local gpu = com.gpu
-- Константы --
HOLOH = 32
HOLOW = 48
-- Цвета --
backcolor = 0x000000
forecolor = 0xFFFFFF
infocolor = 0x0066FF
errorcolor = 0xFF0000
helpcolor = 0x006600
graycolor = 0x080808
goldcolor = 0xFFDF00
-- *** --
-- загружаем доп. оборудование
function trytofind(name)
if com.isAvailable(name) then
return com.getPrimary(name)
else
return nil
end
end
local h = trytofind('hologram')
-- ========================================= H O L O G R A P H I C S ========================================= --
holo = {}
function set(x, y, z, value)
if holo[x] == nil then holo[x] = {} end
if holo[x][y] == nil then holo[x][y] = {} end
holo[x][y][z] = value
end
function get(x, y, z)
if holo[x] ~= nil and holo[x][y] ~= nil and holo[x][y][z] ~= nil then
return holo[x][y][z]
else
return 0
end
end
function save(filename)
-- сохраняем палитру
file = io.open(filename, 'wb')
for i=1, 3 do
for c=1, 3 do
file:write(string.char(colortable[i][c]))
end
end
-- сохраняем массив
for x=1, HOLOW do
for y=1, HOLOH do
for z=1, HOLOW, 4 do
a = get(x,y,z)
b = get(x,y,z+1)
c = get(x,y,z+2)
d = get(x,y,z+3)
byte = d*64 + c*16 + b*4 + a
file:write(string.char(byte))
end
end
end
file:close()
end
function load(filename)
if fs.exists(filename) then
file = io.open(filename, 'rb')
-- загружаем палитру
for i=1, 3 do
for c=1, 3 do
colortable[i][c] = string.byte(file:read(1))
end
setHexColor(i,colortable[i][1],
colortable[i][2],
colortable[i][3])
end
-- загружаем массив
holo = {}
for x=1, HOLOW do
for y=1, HOLOH do
for z=1, HOLOW, 4 do
byte = string.byte(file:read(1))
for i=0, 3 do
a = byte % 4
byte = math.floor(byte / 4)
if a ~= 0 then set(x,y,z+i, a) end
end
end
end
end
file:close()
return true
else
--print("[ОШИБКА] Файл "..filename.." не найден.")
return false
end
end
-- ============================================= G R A P H I C S ============================================= --
-- проверка разрешения экрана, для комфортной работы необходимо разрешение > HOLOW по высоте и ширине
OLDWIDTH, OLDHEIGHT = gpu.getResolution()
WIDTH, HEIGHT = gpu.maxResolution()
if HEIGHT < HOLOW+2 then
error("[ОШИБКА] Ваш монитор/видеокарта не поддерживает требуемое разрешение.")
else
WIDTH = HOLOW*2+40
HEIGHT = HOLOW+2
gpu.setResolution(WIDTH, HEIGHT)
end
gpu.setForeground(forecolor)
gpu.setBackground(backcolor)
-- рисуем линию
local strLine = "+"
for i=1, WIDTH do
strLine = strLine..'-'
end
function line(x1, x2, y)
gpu.set(x1,y,string.sub(strLine, 1, x2-x1))
gpu.set(x2,y,'+')
end
-- рисуем фрейм
function frame(x1, y1, x2, y2, caption)
line(x1, x2, y1)
line(x1, x2, y2)
if caption ~= nil then
gpu.set(x1+(x2-x1)/2-unicode.len(caption)/2, y1, caption)
end
end
-- рисуем сетку
local strGrid = ""
for i=1, HOLOW/2 do
strGrid = strGrid.."██ "
end
function drawGrid(x, y)
gpu.fill(0, y, MENUX, HOLOW, ' ')
gpu.setForeground(graycolor)
for i=0, HOLOW-1 do
if view>0 and i==HOLOH then
gpu.setForeground(forecolor)
line(1, MENUX-1, y+HOLOH)
break
end
gpu.set(x+(i%2)*2, y+i, strGrid)
end
if view == 0 then gpu.setForeground(forecolor) end
end
-- рисуем цветной прямоугольник
function drawRect(x, y, color)
gpu.set(x, y, "╓──────╖")
gpu.set(x, y+1, "║ ║")
gpu.set(x, y+2, "╙──────╜")
gpu.setForeground(color)
gpu.set(x+2, y+1, "████")
gpu.setForeground(forecolor)
end
MENUX = HOLOW*2+5
BUTTONW = 12
-- рисуем меню выбора "кисти"
function drawColorSelector()
frame(MENUX, 3, WIDTH-2, 16, "[ Палитра ]")
for i=0, 3 do
drawRect(MENUX+1+i*8, 5, hexcolortable[i])
end
gpu.set(MENUX+1, 10, "R:")
gpu.set(MENUX+1, 11, "G:")
gpu.set(MENUX+1, 12, "B:")
end
function drawColorCursor(force)
if brush.color*8 ~= brush.x then brush.x = brush.color*8 end
if force or brush.gx ~= brush.x then
gpu.set(MENUX+1+brush.gx, 8, " ")
if brush.gx < brush.x then brush.gx = brush.gx + 1 end
if brush.gx > brush.x then brush.gx = brush.gx - 1 end
gpu.set(MENUX+1+brush.gx, 8, " -^--^- ")
end
end
function drawLayerSelector()
frame(MENUX, 16, WIDTH-2, 28, "[ Слой ]")
gpu.set(MENUX+13, 18, "Уровень голограммы:")
gpu.set(MENUX+1, 23, "Направляющий уровень:")
end
function drawButtonsPanel()
frame(MENUX, 28, WIDTH-2, 36, "[ Управление ]")
end
function mainScreen()
term.clear()
frame(1,1, WIDTH, HEIGHT, "{ Hologram Editor }")
-- "холст"
drawLayer()
drawColorSelector()
drawColorCursor(true)
drawLayerSelector()
drawButtonsPanel()
buttonsDraw()
textboxesDraw()
-- "about" - коротко о создателях
gpu.setForeground(infocolor)
gpu.setBackground(graycolor)
gpu.set(MENUX+3, HEIGHT-11, " Hologram Editor v0.60 Beta ")
gpu.setForeground(forecolor)
gpu.set(MENUX+3, HEIGHT-10, " * * * ")
gpu.set(MENUX+3, HEIGHT-9, " Программисты: ")
gpu.set(MENUX+3, HEIGHT-8, " NEO, Totoro ")
gpu.set(MENUX+3, HEIGHT-7, " * * * ")
gpu.set(MENUX+3, HEIGHT-6, " Контакт: ")
gpu.set(MENUX+3, HEIGHT-5, " computercraft.ru/forum ")
gpu.setBackground(backcolor)
-- выход
gpu.set(MENUX, HEIGHT-2, "Выход: 'Q' или ")
end
-- =============================================== L A Y E R S =============================================== --
GRIDX = 3
GRIDY = 2
function drawLayer()
drawGrid(GRIDX, GRIDY)
-- вид сверху (y)
if view == 0 then
for x=1, HOLOW do
for z=1, HOLOW do
gn = get(x, ghost_layer, z)
n = get(x, layer, z)
if n == 0 and gn ~= 0 then
gpu.setForeground(darkhexcolors[gn])
gpu.set((GRIDX-2) + x*2, (GRIDY-1) + z, "░░")
end
if n ~= 0 then
gpu.setForeground(hexcolortable[n])
gpu.set((GRIDX-2) + x*2, (GRIDY-1) + z, "██")
end
end
end
-- вид спереди (z)
elseif view == 1 then
for x=1, HOLOW do
for y=1, HOLOH do
n = get(x, y, layer)
gn = get(x, y, ghost_layer)
if n == 0 and gn ~= 0 then
gpu.setForeground(darkhexcolors[gn])
gpu.set((GRIDX-2) + x*2, (GRIDY+HOLOH) - y, "░░")
end
if n ~= 0 then
gpu.setForeground(hexcolortable[n])
gpu.set((GRIDX-2) + x*2, (GRIDY+HOLOH) - y, "██")
end
end
end
-- вид сбоку (x)
else
for z=1, HOLOW do
for y=1, HOLOH do
gn = get(ghost_layer, y, z)
n = get(layer, y, z)
if n == 0 and gn ~= 0 then
gpu.setForeground(darkhexcolors[gn])
gpu.set((GRIDX+HOLOW*2) - z*2, (GRIDY+HOLOH) - y, "░░")
end
if n ~= 0 then
gpu.setForeground(hexcolortable[n])
gpu.set((GRIDX+HOLOW*2) - z*2, (GRIDY+HOLOH) - y, "██")
end
end
end
end
gpu.setForeground(forecolor)
-- for messages
repaint = false
end
function fillLayer()
for x=1, HOLOW do
for z=1, HOLOW do
set(x, layer, z, brush.color)
end
end
drawLayer()
end
function clearLayer()
for x=1, HOLOW do
if holo[x] ~= nil then holo[x][layer] = nil end
end
drawLayer()
end
-- ============================================== B U T T O N S ============================================== --
Button = {}
Button.__index = Button
function Button.new(func, x, y, text, color, width)
self = setmetatable({}, Button)
self.form = '[ '
if width == nil then width = 0
else width = (width - unicode.len(text))-4 end
for i=1, math.floor(width/2) do
self.form = self.form.. ' '
end
self.form = self.form..text
for i=1, math.ceil(width/2) do
self.form = self.form.. ' '
end
self.form = self.form..' ]'
self.func = func
self.x = x; self.y = y
self.color = color
self.visible = true
return self
end
function Button:draw(color)
if self.visible then
local color = color or self.color
gpu.setBackground(color)
if color > 0x888888 then gpu.setForeground(backcolor) end
gpu.set(self.x, self.y, self.form)
gpu.setBackground(backcolor)
if color > 0x888888 then gpu.setForeground(forecolor) end
end
end
function Button:click(x, y)
if self.visible then
if y == self.y then
if x >= self.x and x < self.x+unicode.len(self.form) then
self.func()
self:draw(self.color/2)
os.sleep(0.1)
self:draw()
return true
end
end
end
return false
end
buttons = {}
function buttonsNew(func, x, y, text, color, width)
table.insert(buttons, Button.new(func, x, y, text, color, width))
end
function buttonsDraw()
for i=1, #buttons do
buttons[i]:draw()
end
end
function buttonsClick(x, y)
for i=1, #buttons do
buttons[i]:click(x, y)
end
end
-- ================================ B U T T O N S F U N C T I O N A L I T Y ================================ --
function exit() running = false end
function nextLayer()
-- ограничения разные для разных видов/проекций
local limit = HOLOH
if view > 0 then limit = HOLOW end
if layer < limit then
layer = layer + 1
tb_layer:setValue(layer)
tb_layer:draw(true)
moveGhost()
drawLayer()
end
end
function prevLayer()
if layer > 1 then
layer = layer - 1
tb_layer:setValue(layer)
tb_layer:draw(true)
moveGhost()
drawLayer()
end
end
function setLayer(value)
local n = tonumber(value)
local limit = HOLOH
if view > 0 then limit = HOLOW end
if n == nil or n < 1 or n > limit then return false end
layer = n
moveGhost()
drawLayer()
return true
end
function nextGhost()
local limit = HOLOH
if view > 0 then limit = HOLOW end
if ghost_layer_below then
ghost_layer_below = false
if ghost_layer < limit then
ghost_layer = layer + 1
else ghost_layer = limit end
drawLayer()
else
if ghost_layer < limit then
ghost_layer = ghost_layer + 1
drawLayer()
end
end
end
function prevGhost()
if not ghost_layer_below then
ghost_layer_below = true
if layer > 1 then
ghost_layer = layer - 1
else ghost_layer = 1 end
drawLayer()
else
if ghost_layer > 1 then
ghost_layer = ghost_layer - 1
drawLayer()
end
end
end
function setGhostLayer(value)
local n = tonumber(value)
local limit = HOLOH
if view > 0 then limit = HOLOW end
if n == nil or n < 1 or n > limit then return false end
ghost_layer = n
drawLayer()
return true
end
function moveGhost()
if ghost_layer_below then
if layer > 1 then ghost_layer = layer - 1
else ghost_layer = 1 end
else
local limit = HOLOH
if view > 0 then limit = HOLOW end
if layer < limit then ghost_layer = layer + 1
else ghost_layer = limit end
end
end
function setFilename(str)
if str ~= nil and str ~= '' and unicode.len(str)<30 then
return true
else
return false
end
end
function setHexColor(n, r, g, b)
local hexcolor = rgb2hex(r,g,b)
hexcolortable[n] = hexcolor
darkhexcolors[n] = bit32.rshift(bit32.band(hexcolor, 0xfefefe), 1)
end
function rgb2hex(r,g,b)
return r*65536+g*256+b
end
function changeRed(value) return changeColor(1, value) end
function changeGreen(value) return changeColor(2, value) end
function changeBlue(value) return changeColor(3, value) end
function changeColor(rgb, value)
if value == nil then return false end
n = tonumber(value)
if n == nil or n < 0 or n > 255 then return false end
-- сохраняем данные в таблицу
colortable[brush.color][rgb] = n
setHexColor(brush.color, colortable[brush.color][1],
colortable[brush.color][2],
colortable[brush.color][3])
-- обновляем цвета на панельке
for i=0, 3 do
drawRect(MENUX+1+i*8, 5, hexcolortable[i])
end
return true
end
function moveSelector(num)
brush.color = num
tb_red:setValue(colortable[num][1]); tb_red:draw(true)
tb_green:setValue(colortable[num][2]); tb_green:draw(true)
tb_blue:setValue(colortable[num][3]); tb_blue:draw(true)
end
function setTopView()
view = 0
-- в виде сверху меньше слоев
if layer > HOLOH then layer = HOLOH end
drawLayer()
end
function setFrontView() view = 1; drawLayer() end
function setSideView() view = 2; drawLayer() end
function drawHologram()
-- проверка на наличие проектора
h = trytofind('hologram')
if h ~= nil then
local depth = h.maxDepth()
-- очищаем
h.clear()
-- отправляем палитру
if depth == 2 then
for i=1, 3 do
h.setPaletteColor(i, hexcolortable[i])
end
else
h.setPaletteColor(1, hexcolortable[1])
end
-- отправляем массив
for x=1, HOLOW do
for y=1, HOLOH do
for z=1, HOLOW do
n = get(x,y,z)
if n ~= 0 then
if depth == 2 then
h.set(x,y,z,n)
else
h.set(x,y,z,1)
end
end
end
end
end
end
end
function newHologram()
holo = {}
drawLayer()
end
function saveHologram()
local filename = tb_file:getValue()
if filename ~= FILE_REQUEST then
-- выводим предупреждение
showMessage('Файл сохраняется...', '[ Внимание ]', goldcolor)
-- добавляем фирменное расширение =)
if string.sub(filename, -3) ~= '.3d' then
filename = filename..'.3d'
end
-- сохраняем
save(filename)
-- выводим предупреждение
showMessage(' Файл сохранен! ', '[ Завершено ]', goldcolor)
repaint = true
end
end
function loadHologram()
local filename = tb_file:getValue()
if filename ~= FILE_REQUEST then
-- выводим предупреждение
showMessage('Файл загружается...', '[ Внимание ]', goldcolor)
-- добавляем фирменное расширение =)
if string.sub(filename, -3) ~= '.3d' then
filename = filename..'.3d'
end
-- загружаем
load(filename)
-- обновляем значения в текстбоксах
tb_red:setValue(colortable[brush.color][1]); tb_red:draw(true)
tb_green:setValue(colortable[brush.color][2]); tb_green:draw(true)
tb_blue:setValue(colortable[brush.color][3]); tb_blue:draw(true)
-- обновляем цвета на панельке
for i=0, 3 do
drawRect(MENUX+1+i*8, 5, hexcolortable[i])
end
-- обновляем слой
drawLayer()
end
end
-- ============================================ T E X T B O X E S ============================================ --
Textbox = {}
Textbox.__index = Textbox
function Textbox.new(func, x, y, value, width)
self = setmetatable({}, Textbox)
self.form = '>'
if width == nil then width = 10 end
for i=1, width-1 do
self.form = self.form..' '
end
self.func = func
self.value = tostring(value)
self.x = x; self.y = y
self.visible = true
return self
end
function Textbox:draw(content)
if self.visible then
if content then gpu.setBackground(graycolor) end
gpu.set(self.x, self.y, self.form)
if content then gpu.set(self.x+2, self.y, self.value) end
gpu.setBackground(backcolor)
end
end
function Textbox:click(x, y)
if self.visible then
if y == self.y then
if x >= self.x and x < self.x+unicode.len(self.form) then
self:draw(false)
term.setCursor(self.x+2, self.y)
value = string.sub(term.read({self.value}), 1, -2)
if self.func(value) then
self.value = value
end
self:draw(true)
return true
end
end
end
return false
end
function Textbox:setValue(value)
self.value = tostring(value)
end
function Textbox:getValue()
return self.value
end
textboxes = {}
function textboxesNew(func, x, y, value, width)
textbox = Textbox.new(func, x, y, value, width)
table.insert(textboxes, textbox)
return textbox
end
function textboxesDraw()
for i=1, #textboxes do
textboxes[i]:draw(true)
end
end
function textboxesClick(x, y)
for i=1, #textboxes do
textboxes[i]:click(x, y)
end
end
-- ============================================= M E S S A G E S ============================================= --
repaint = false
function showMessage(text, caption, color)
local x = WIDTH/2 - unicode.len(text)/2 - 4
local y = HEIGHT/2 - 2
gpu.fill(x, y, unicode.len(text)+8, 5, ' ')
frame(x, y, x+unicode.len(text)+7, y+4, caption)
gpu.setForeground(color)
gpu.set(x+4,y+2, text)
gpu.setForeground(forecolor)
end
-- =========================================== M A I N C Y C L E =========================================== --
-- инициализация
colortable = {{255, 0, 0}, {0, 255, 0}, {0, 102, 255}}
colortable[0] = {0, 0, 0}
hexcolortable = {}
darkhexcolors = {}
for i=0,3 do setHexColor(i, colortable[i][1], colortable[i][2], colortable[i][3]) end
brush = {color = 1, x = 8, gx = 8}
ghost_layer = 1
ghost_layer_below = true
layer = 1
view = 0
running = true
buttonsNew(exit, WIDTH-BUTTONW-2, HEIGHT-2, 'Выход', errorcolor, BUTTONW)
buttonsNew(drawLayer, MENUX+10, 14, 'Обновить', goldcolor, BUTTONW)
buttonsNew(prevLayer, MENUX+1, 19, '-', infocolor, 5)
buttonsNew(nextLayer, MENUX+7, 19, '+', infocolor, 5)
buttonsNew(setTopView, MENUX+1, 21, 'Сверху', infocolor, 10)
buttonsNew(setFrontView, MENUX+12, 21, 'Спереди', infocolor, 10)
buttonsNew(setSideView, MENUX+24, 21, 'Сбоку', infocolor, 9)
buttonsNew(prevGhost, MENUX+1, 24, 'Ниже', infocolor, 6)
buttonsNew(nextGhost, MENUX+10, 24, 'Выше', infocolor, 6)
buttonsNew(clearLayer, MENUX+1, 26, 'Очистить', infocolor, BUTTONW)
buttonsNew(fillLayer, MENUX+2+BUTTONW, 26, 'Залить', infocolor, BUTTONW)
buttonsNew(drawHologram, MENUX+8, 30, 'На проектор', goldcolor, 16)
buttonsNew(saveHologram, MENUX+1, 33, 'Сохранить', helpcolor, BUTTONW)
buttonsNew(loadHologram, MENUX+8+BUTTONW, 33, 'Загрузить', infocolor, BUTTONW)
buttonsNew(newHologram, MENUX+1, 35, 'Новый файл', infocolor, BUTTONW)
tb_red = textboxesNew(changeRed, MENUX+5, 10, '255', WIDTH-MENUX-7)
tb_green = textboxesNew(changeGreen, MENUX+5, 11, '0', WIDTH-MENUX-7)
tb_blue = textboxesNew(changeBlue, MENUX+5, 12, '0', WIDTH-MENUX-7)
tb_layer = textboxesNew(setLayer, MENUX+13, 19, '1', WIDTH-MENUX-15)
tb_ghostlayer = textboxesNew(setGhostLayer, MENUX+19, 24, ' ', WIDTH-MENUX-21)
FILE_REQUEST = 'Введите сюда имя файла'
tb_file = textboxesNew(setFilename, MENUX+1, 32, FILE_REQUEST, WIDTH-MENUX-3)
mainScreen()
while running do
if brush.x ~= brush.gx then name, add, x, y, b = event.pull(0.02)
else name, add, x, y, b = event.pull(1.0) end
if name == 'key_down' then
-- если нажата 'Q' - выходим
if y == 16 then break
elseif y == 41 then
moveSelector(0)
elseif y>=2 and y<=4 then
moveSelector(y-1)
elseif y == 211 then
clearLayer()
end
elseif name == 'touch' then
-- проверка GUI
buttonsClick(x, y)
textboxesClick(x, y)
-- выбор цвета
if x>MENUX+1 and x<MENUX+37 then
if y>4 and y<8 then
moveSelector(math.floor((x-MENUX-1)/8))
end
end
end
if name == 'touch' or name == 'drag' then
-- "рисование"
local limit = HOLOW
if view > 0 then limit = HOLOH end
if x >= GRIDX and x < GRIDX+HOLOW*2 then
if y >= GRIDY and y < GRIDY+limit then
-- перерисуем, если на экране был мессейдж
if repaint then drawLayer() end
-- рассчет клика
if view == 0 then
dx = math.floor((x-GRIDX)/2)+1; gx = dx
dy = layer; gy = ghost_layer
dz = y-GRIDY+1; gz = dz
elseif view == 1 then
dx = math.floor((x-GRIDX)/2)+1; gx = dx
dy = HOLOH - (y-GRIDY); gy = dy
dz = layer; gz = ghost_layer
else
dx = layer; gx = ghost_layer
dy = HOLOH - (y-GRIDY); gy = dy
dz = HOLOW - math.floor((x-GRIDX)/2); gz = dz
end
if b == 0 and brush.color ~= 0 then
set(dx, dy, dz, brush.color)
gpu.setForeground(hexcolortable[brush.color])
gpu.set(x-(x-GRIDX)%2, y, "██")
else
set(dx, dy, dz, 0)
gpu.setForeground(darkhexcolors[get(gx,gy,gz)])
gpu.set(x-(x-GRIDX)%2, y, "░░")
end
gpu.setForeground(forecolor)
end
end
end
drawColorCursor()
end
-- завершение
term.clear()
gpu.setResolution(OLDWIDTH, OLDHEIGHT)
gpu.setForeground(0xFFFFFF)
gpu.setBackground(0x000000)