Перейти к содержимому

ECS

Гуру
  • Публикации

    533
  • Зарегистрирован

  • Посещение

  • Победитель дней

    203

Сообщения, опубликованные пользователем ECS


  1. Раз уж речь зашла об идеях и медалях, то у нас была мыслишка накатать мод, рендерящий полученные награды в виде оверлея в дополнение к надетой броне, чтобы деды-ветераны форума имели возможность блеснуть ими не только на сайте. Единственной проблемой стало отсутствие личного форума и, разумеется, сервера

    • Нравится 2

  2. Нашел среди старых коммитов русскую версию. Для ознакомления сойдет, но учти, что в последних версиях либы очень многое изменилось, поэтому претензий никаких не предъявлять: https://github.com/IgorTimofeev/MineOS/blob/00f19b2c460bc82a4ae5fc593c367f0fbfd5ccdd/Documentation/GUI.md

    • Нравится 1

  3. Если приложение уже создано, то пкм - показать содержимое пакета - двойной клик по пикче - рисуешь - сохраняешь - пкм в пустоту - обновить - профит. Если приложения нет, то открываешь Picture Edit и вручную создаешь пикчу размером 8х4 пикселя. Это ж вообще элементарно, ты чо

    • Нравится 1

  4. и я допустим обновляю этот контейнер раз в секунду через Time:drawOnScreen(), но изменений я не вижу

     

    Ты не обновляешь контейнер раз в секунду, а отрисовываешь его содержимое раз в секунду - это большая разница. Пожалуйста, читай документацию более вдумчиво: в ней подробно описана концепция либы.

     

    Для твоей задачи логичнее всего было бы создать пустой GUI.object вместо GUI.label и присвоить ему метод отрисовки, выводящий os.time на экран:

    -- Создаем главный контейнер и добавляем в него темно-серую фоновую панель
    local mainContainer = GUI.fullScreenContainer()
    mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))
    
    -- Добавляем объект, выводящее в экранный буфер системное время каждую операцию отрисовки
    mainContainer:addChild(GUI.object(3, 2, 1, 1)).draw = function(object)
      local text = os.date("%d %b %Y %H:%M:%S", os.time())
      object.width = #text
      buffer.drawText(object.x, object.y, 0xFFFFFF, text)
    end
    
    -- Разово отрисовываем содержимое главного контейнера и запускаем обработку событий с интервалом 0
    mainContainer:drawOnScreen(true)
    mainContainer:startEventHandling(0)

    Однако если по каким-то причинам тебе требуется использовать именно GUI.label, то можно использовать обработчик событий, обновляющий текст лейбла при каждом событии:

    -- Создаем главный контейнер и добавляем в него темно-серую фоновую панел
    local mainContainer = GUI.fullScreenContainer()
    mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))
    
    -- Добавляем лейбл и создаем функцию-обработчик событий, которая при каждом событии (даже отсутствующем) будет обновлять текст лейбла и отрисовывать изменения на экране
    local label = mainContainer:addChild(GUI.label(3, 2, 10, 1, 0xFFFFFF, ""))
    mainContainer.eventHandler = function()
      label.text = os.date("%d %b %Y %H:%M:%S", os.time())
      mainContainer:drawOnScreen()
    end
    
    -- Разово вызываем созданную функцию для обновления текста
    mainContainer.eventHandler()
    -- Запускаем обработку событий с интервалом 0
    mainContainer:startEventHandling(0)
    

     

     

    сейчас же на вкладку надо нажимать по 2 раза, чтобы ее переключить

     

    Старая версия либы была лютым калом: она отрисовывала изменения в экранный буфер во время клика на вкладку, затем вызывала метод .onTouch, а после вновь отрисовывала изменения. Я счел этот подход неразумным, т.к. незачем нагружать компьютер без четких на то указаний. Короче, добавь строку mainContainer:drawOnScreen() в конец метода .onTouch для каждой вкладки - и все будет шоколадно

     

     

     

    То-есть у GUI.alert кнопка OK уезжает куда-то за пределы экрана вправо, а палитра вообще только наполовину помещается на экран, вроде это все проблемы

     

    Как говорится, ложь, говнокод и провокация. Скорее всего, ты сначала создаешь объекты под жирное разрешение экрана, затем изменяешь его на меньшее, позабыв изменить размеры объектов. Пруфскрипт:

    -- Изменяем разрешение экрана на требуемое
    buffer.setResolution(80, 50)
    
    local mainContainer = GUI.fullScreenContainer()
    mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))
    
    -- Добавляем селектор цвета
    local colorSelector = mainContainer:addChild(GUI.colorSelector(3, 2, 30, 3, 0xFF00FF, "Цвет"))
    colorSelector.onColorSelected = function()
    	GUI.alert("Ты выбрал новый цвет")
    end
    
    mainContainer:drawOnScreen(true)
    mainContainer:startEventHandling(0)

    Результат:

     

    jq3SLx3.gif

    • Нравится 1

  5. А как без вращения 3D точек сделать 3D рэйкастер?

     

    Берешь FOV камеры, а также ее текущий поворот, например, по горизонтали. Через цикл изменяешь угол будущего луча в диапазоне [поворот камеры - FOV / 2; поворот камеры + FOV / 2] с заранее рассчитанными шагом, равным ширина экрана / FOV (чтобы число лучей было равным ширине экрана). Далее необходимо прокастовать луч, следующий вдоль полученного угла вплоть до желаемой дистанции прорисовки. Поскольку рейкастинг всегда выполняется с определенной точностью, то разумнее всего будет разово высчитать итеративное смещение кусочка луча по оси X через sin(угол луча) * точность и по оси Y через cos. Полученные значения необходимо раз за разом прибавлять к точке старта луча, чтобы усмешно достигнуть конца. Ну, а далее проще простого: пробегаешься циклом по всей длине луча, попутно вычисляя мировые координаты вокселя по горизонтали. А затем повторяешь вышеописанное для вертикальной ориентации, проверяя наличие вокселя в обоих плоскостях и делая с ним все, что только заблагорассудится.

     

    Соль подобного подхода в том, что число тригонометрических операций будет эквивалентно ширина * высота экрана: и никаких тебе векторных вращений и бессмысленных расчетов. Цветами выделил инфу из этой пикчи:

     

    QgL3iAu.png?1

     

    Вот я поставил твои функции, но все равно как то не то

     

    Значит, ищи ошибку. Я твой софт не кодил, и дебажить его не намерен. Формулы выше я скопипастил с собственного 3D-движка для опенкомпов, который с удовольствием их кушает. К слову, несмотря на наличие динамического освещения с полной перспективной коррекции, реализован он безо всяких углов Эйлера, матриц, кватернионов и прочей чуши на чистой линейной математике уровня 9 класса

     

    Может, нужно использовать углы Эйлера или кватернионы?

     

    Зачем, заче-е-ем? К чему этот геморрой, когда сам алгоритм проще пареной репы? Подобное еще на Сегу во времена Вульфа кодили, слухом не слыхивая о подобной терминологии в компьютерной графике. Ты точно рейкастинг имеешь в виду, а не полноценное трехмерное проецирование со всеми вытекающими?

    • Нравится 3

  6. Интересно, для чего тебе вращение трехмерных векторов при написании рейкастера, если соль алгоритма именно в минимизации векторных расчетов и статичной производительности? Но хозяин - барин. Во-первых, на кой дьявол постоянно использовать конверсию из градусов в радианы? Сожрет же львиную долю ресурсов ЦП, а толку ноль. Юзай сразу радианы, а в человекопонятный градусный формат выводи лишь в интерфейсе. Во-вторых, очень много локальных переменных - рендерер загнется от "out of memory" на сценах с удаленной от точки каста геометрией, т.к. GC не успеет подтереть кучки локального навоза. В-третьих, незачем столь часто обращаться к таблице math и вычислять значения синусов: достаточно разово передать их в расчетную функцию. Также разумнее использовать векторные координаты в качестве последних аргументов функций, чтобы более эффективно передавать результат расчетов из одной функции в другую, хотя лично я бы вообще ограничился тремя локальными переменными для всех векторов и работал бы только с ними. В-четвертых, в нормализации углов особого смысла нет, т.к. тригонометрические функции автоматом работают с углами любого формата. Разве что если ты захочешь сделать заранее вычисленную таблицу косинусов/синусов и получать значения углов уже из нее - вот тогда да, приводить углы к диапазону [-2π; +2π] уже разумно, хотя на практике прирост производительности толком не заметен.

     

     

    -- На всякий, мало ли надо
    local TWO_PI = 2 * math.pi
    local function normalize(angle)
      angle = angle % TWO_PI
      return angle >= 0 and angle or angle + TWO_PI
    end
    
    local function rotateX(sin, cos, x, y, z)
      return
        x,
        cos * y - sin * z,
        sin * y + cos * z
    end
    
    local function rotateY(sin, cos, x, y, z)
      return
        cos * x + sin * z,
        y,
        cos * z - sin * x
    end
    
    local function rotateZ(sin, cos, x, y, z)
      return
        cos * x - sin * y,
        sin * x + cos * y,
        z
    end
    
    local function rotateXY(rx, ry, x, y, z)
      return rotateX(math.sin(rx), math.cos(rx), rotateY(math.sin(ry), math.cos(ry), x, y, z))
    end

     

     

    • Нравится 4

  7. Считаю нужным предложить правильную реализацию, которая работает за логарифмическое время (в миллионы или даже в миллиарды раз быстрее)

    Вставлю свои пять копеек, чтобы правильность была еще более правильной. Ох уж эта правильность... В общем, так как время, затрачиваемое на вызов функций в Lua значительно превышает время осуществления иных операций, следует использовать итеративный метод расчетов вместо рекурсивного. Кроме того, как вполне грамотно заметил товарищ с e-maxx.ru, для определения четности числа имеет смысл заменить "n % 2 == 0" на "n & 1 == 0". В итоге времечко сократится еще втрое. Собсна, пруф: https://ideone.com/t9Yv5F

    • Нравится 1

  8. Пробежимся по твоему скрипту: во-первых, что за странный бесконечный цикл в начале программы?

    while true do
      local adresses = {
      ...
    

    Метод :startEventHandling() автоматически обрабатывает все события бесконечно вплоть до завершения программы. Зачем нужен while true do? Нипанятна.
     
    Во-вторых, почему ты заносишь адреса компонентов реакторов вручную, да еще и по их аббревиатурам? Почему бы не использовать автоматическое получение proxy?

    local reactors = {}
    for address in component.list("reactor") do
      table.insert(reactors, component.proxy(address))
    end
    

    Если это сделано с какой-то определенной целью, например, ради работы с небольшой группой реакторов из большого множества, то прошу прощения.
     
    В-третьих, почему бы не использовать циклы для создания виджетов? Неужели самому не лень копипастить тонну однотипного кода?

    -- Разметка
    layout:setCellPosition(1, 1, layout:addChild(GUI.text(1, 1, clr_main, "№")))
    for i = 2, 7 do
      layout:setCellPosition(i, 1, layout:addChild(GUI.text(1, 1, clr_main, "№" .. (i - 1))))
    end
    
    -- Заполняем Layout начиная со 2 колонки и 2 столбца
    local column, row, counter = 2, 2, 1
    for address in component.list("reactor") do
      -- Добавляем виджет
      local widget = layout:setCellPosition(column, row, layout:addChild(GUI.text(1, 1, 0x0, "Реактор " .. counter)))
      -- Присваиваем виджету ссылку на proxy компонента реактора
      widget.reactorProxy = component.proxy(address)
      
      row, counter = row + 1, counter + 1
      if row > 10 then
        column, row, counter = column + 1, 2, 1
      end
    end

    В-четвертых, разумеется, изменений на экране у тебя нет. Как программа поймет, что состояние реакторов изменилось? Откуда она знает, что ты от нее хочешь? Накодь соответствующий функционал и наслаждайся. А если требуется обновлять температуру реакторов периодически, то используй нечто подобное:

    -- Создаем обработчик событий главного контейнера
    mainContainer.eventHandler = function(mainContainer, object, eventData)
      -- Если события нет, то следует обновить состояние реакторов и отрисовать изменения на экране
      if not eventData[1] then
        -- Перебираем все виджеты Layout начиная с 9 (все предыдущие - это чисто информация)
        for i = 9, #layout.children do
          -- Задаем им нужный цвет
          layout.children[i].color = layout.children[i].reactorProxy.producesEnergy() and 0xFF0000 or 0x00FF00
        end
        -- Отрисовываем изменения на экране
        mainContainer:drawOnScreen()
      end
    end
    
    -- События пуллятся раз в 1 секунду по аналогии с computer.pullSignal(timeout)
    mainContainer:startEventHandling(1)
    

    Насчет отрисовки содержимого контейнера на экран - тут вообще все просто. Это основа основ, присутствующая в каждом примере кода в документации к либе:

    mainContainer:drawOnScreen()

    И наконец, где отступы? Ну рили, 2018 год на дворе, эпоха Sublime/Atom/Notepad++, где отступыЫыЫыЫ?

    • Нравится 3

  9. @@vlad1vv1 Этой либе сто лет в обед, и использовать ее нет особого смысла ввиду наличия более продвинутых аналогов. Возьми GUI, меньше геморроя будет. Там тебе и примеры, и картиночки, и вообще до мелочей все расписано:

    https://github.com/IgorTimofeev/OpenComputers/blob/master/Documentation/GUI.md

     

    А для создания вертикального меню из кнопок воспользуйся одним из примеров в доке:

    local GUI = require("GUI")
    
    -------------------------------------------------
    
    local mainContainer = GUI.fullScreenContainer()
    mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))
    
    -- Создаем вертикально ориентированный список
    local verticalList = mainContainer:addChild(GUI.list(3, 2, 25, 30, 3, 0, 0xE1E1E1, 0x4B4B4B, 0xD2D2D2, 0x4B4B4B, 0x3366CC, 0xFFFFFF, false))
    verticalList:addItem("Hello world")
    verticalList:addItem("This is test").onTouch = function()
       -- Сделать что-то при нажатии
    end
    verticalList:addItem("Beautiful")
    verticalList:addItem("Like a shit")
    
    -------------------------------------------------
    
    mainContainer:drawOnScreen(true)
    mainContainer:startEventHandling()
    
    • Нравится 1

  10.  

     

    Почему бы не сделать автоматическую выгрузку неиспользуемых библиотек из озу?

     

    Давно хотел, однако побаивался делать "слабую" линковку из-за возможного упарывания опеносовских либ. Потестирую денек-другой с различным софтом как для опеноси, так и для майноси, и если все будет в порядке - то без проблем добавлю эту фичу с возможностью отключения.


  11. Переписан системный графический редактор. Каждый инструмент реализован в виде отдельного скрипта-модуля для удобства написания кастомных. Имеется возможность назначения инструменту горячей клавиши, краткого описания и создания меню с параметрами. Среди дефолтных инструментов стоит отдельно отметить редактор шрифта Брайля, заливку и выделение областей:

     

    eQQki1d.png?1

    • Нравится 5

  12.  

     

    Имхо, но как я помню такой код порождает новую функцию каждый вызов myTextBox.

     

    Да, безусловно. Надо было как-то схитрить и уместить эту фиговину в обещанные "10-20 строк", сэкономив одну на присваивание. Однако факта говнокодистости это не отменяет, нет-нет


  13. Но вообще странно что виджеты не переносят текст при превышении ширины

     

    Любую автоматизацию виджетов, потенциально ведущую к уменьшению производительности, я реализую в качестве опциональной и отключенной по умолчанию фичи. Надо - активируй по требованию, всего 12 буковок в аргументы написать, ерунда. А если оч оч лень писать или чем-то не устраивают стандартные виджеты - создай собственный, это буквально 10-20 строк кода:

    local function myTextBox(x, y, width, color, text)
      local lines = string.wrap({text}, width)
      local object = GUI.object(x, y, width, #lines)
    
      object.draw = function()
        for i = 1, #lines do
          buffer.text(objext.x, object.y + i - 1, color, lines[i])
        end
      end
    
      return object
    end
    
    • Нравится 1

  14. В магазин приложений добавлена возможность отправки личных сообщений. Это позволит не только обмениваться взаимными оскорблениями, но и предоставлять информацию разработчиками об работоспособности их говнокода не отходя от кассы:

     

    kOtHYrW.png?1

     

    7gVVjEJ.png?1

    • Нравится 4

  15. @@eu_tomat, плюсую, хотелось бы глянуть и потестировать исходник без минификации. Еще интересует возможность практического применении подобной методики: безусловно, упор на экономию памяти - дело благородное, однако, к примеру, для отрисовки изображений и буферизации экрана идея не сгодится ввиду необходимости постоянной распаковки/упаковки чисел, что катастрофически снизит производительность. Это не претензия к либе, а вопрос о ее применении "не на бумажке"


  16. g1hRv94.gif

     

    Захотелось написать очередную прошивку для EEPROM с интересным функционалом. Чтобы запихать этого монстра (в масштабах прошивок, разумеется) в 4-килобайтный лимит, пришлось пройти семь кругов ада, сожрать пуд соли и пролить ведро крови. Из фич отмечу следующие:

     

    •  Поддержка всех уровней видеокарт и мониторов

    •  Отображение подробной информации об имеющихся дисках

    •  Форматирование / переименование / выбор файловой системы в качестве загрузочного тома

    •  Аналог Internet Recovery, позволяющий запустить скрипт по указанной URL в качестве нативного кода для EEPROM

     

    Исходник без минификации: https://github.com/IgorTimofeev/MineOS/blob/master/EFI/Full.lua

    Команды для прошивки:

    wget https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/EFI/Minified.lua /EFI.lua -f
    flash /EFI.lua
    • Нравится 9

  17. Подскажите в чем я ошибся в своих измышизмах?

     

    В указанном примере робот проверяет отсутствие редстоун-сигнала, а затем, убедившись в этом, входит в бесконечный цикл на 26 строке. Чем больше циклов - тем больше жепы, все это дело можно заменить упрощенным вариантом:

    while true do
      if rs.getInput(sides.right) == 0 then
        print("Программа работает")
        robot.forward()
        os.sleep(1)
        robot.back()
        os.sleep(1)
      else
        print("Программа остановлена")
        break
      end
    end
    

    Мне надо что бы, например, если закончилась зарядка, робот прервал цикл, затем поехал заряжаться, после зарядки возобновил основной цикл

    Это решается созданием виртуальной системы координат и нескольких функций, изменяющих значение x/y/z робота при его перемещении, учитывая поворот. Сдвинулся вперед? Прибавь единичку. Уровень заряда маловат? Вычисляй оптимальную траекторию для возвращения на базу, сохраняй текущие координаты, возвращайся и заряжайся, а затем уже иди обратно и продолжай работу. Нечто подобное я писал давненько, если интересно - можешь ознакомиться: https://github.com/IgorTimofeev/OpenComputers/blob/master/Applications/Robot/robotAPI.lua

    • Нравится 2
×
×
  • Создать...