From e968ed327016962e0beb331573abc1c32f46b0aa Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Sat, 24 Feb 2024 02:09:37 +0200 Subject: [PATCH] lua: add math module lua: make all modules global by default doc: update docs --- docs/lua/controller.rst | 10 +- docs/lua/display.rst | 25 +- docs/lua/index.rst | 1 + docs/lua/math.rst | 369 ++++++++++++++++++ docs/lua/resources.rst | 16 +- docs/lua/utils.rst | 48 +-- docs/manual/lua.rst | 65 ++- sdk/lib/lilka/src/lua/lualilka_console.cpp | 11 +- sdk/lib/lilka/src/lua/lualilka_console.h | 3 +- sdk/lib/lilka/src/lua/lualilka_controller.cpp | 11 +- sdk/lib/lilka/src/lua/lualilka_controller.h | 3 +- sdk/lib/lilka/src/lua/lualilka_display.cpp | 36 +- sdk/lib/lilka/src/lua/lualilka_display.h | 3 +- sdk/lib/lilka/src/lua/lualilka_math.cpp | 285 ++++++++++++++ sdk/lib/lilka/src/lua/lualilka_math.h | 14 + sdk/lib/lilka/src/lua/lualilka_resources.cpp | 21 +- sdk/lib/lilka/src/lua/lualilka_resources.h | 3 +- sdk/lib/lilka/src/lua/lualilka_util.cpp | 38 +- sdk/lib/lilka/src/lua/lualilka_util.h | 3 +- sdk/lib/lilka/src/lua/luarunner.cpp | 61 +-- 20 files changed, 869 insertions(+), 157 deletions(-) create mode 100644 docs/lua/math.rst create mode 100644 sdk/lib/lilka/src/lua/lualilka_math.cpp create mode 100644 sdk/lib/lilka/src/lua/lualilka_math.h diff --git a/docs/lua/controller.rst b/docs/lua/controller.rst index a2c25939..f505b327 100644 --- a/docs/lua/controller.rst +++ b/docs/lua/controller.rst @@ -3,14 +3,15 @@ Функції для роботи з введенням. +.. contents:: + :local: + :depth: 1 + Приклад: .. code-block:: lua :linenos: - local controller = require("controller") - local display = require("display") - display.set_cursor(32, 32) while true do @@ -56,9 +57,6 @@ controller.get_state() .. code-block:: lua :linenos: - local controller = require("controller") - local display = require("display") - display.set_cursor(0, 32) while true do diff --git a/docs/lua/display.rst b/docs/lua/display.rst index d3a07718..bd460387 100644 --- a/docs/lua/display.rst +++ b/docs/lua/display.rst @@ -3,13 +3,15 @@ Основні функції для роботи з дисплеєм. +.. contents:: + :local: + :depth: 1 + Приклад: .. code-block:: lua :linenos: - local display = require("display") - display.set_cursor(0, 0) display.print("Hello,", "world!", 69, 420, "nice") @@ -53,8 +55,6 @@ color565(r, g, b) .. code-block:: lua :linenos: - local display = require("display") - local color = display.color565(255, 0, 0) display.draw_line(0, 0, 100, 100, color) @@ -94,8 +94,6 @@ set_font(font) .. code-block:: lua :linenos: - local display = require("display") - display.set_font("6x13") display.set_cursor(8, 32) display.print("Привіт,") @@ -183,8 +181,6 @@ draw_line(x1, y1, x2, y2, color) .. code-block:: lua :linenos: - local display = require("display") - local color = display.color565(255, 0, 0) display.draw_line(0, 0, 100, 100, color) @@ -283,20 +279,17 @@ draw_bitmap(id, x, y) :type id: number :param id: Ідентифікатор зображення, отриманий з :lua:func:`resources.load_bitmap`. :type x: number - :param x: Координата x. + :param x: Координата x верхнього лівого кута зображення. :type y: number - :param y: Координата y. + :param y: Координата y верхнього лівого кута зображення. Приклад використання: .. code-block:: lua :linenos: - local display = require("display") - local resources = require("resources") - local face = resources.load_bitmap("face.bmp", display.color565(0, 0, 0)) - display.draw_bitmap(face, 50, 80) + display.draw_bitmap(face, 50, 80) -- Виводить зображення на екран у точці (50, 80) set_buffered(value) ^^^^^^^^^^^^^^^^^^^ @@ -319,8 +312,6 @@ set_buffered(value) .. code-block:: lua :linenos: - local display = require("display") - -- Вимикаємо буферизацію екрану: display.set_buffered(false) @@ -337,8 +328,6 @@ set_buffered(value) .. code-block:: lua :linenos: - local display = require("display") - while true do -- Заповнюємо екран чорним кольором: display.fill_screen(display.color565(0, 0, 0)) diff --git a/docs/lua/index.rst b/docs/lua/index.rst index 048610b1..5d8cef1b 100644 --- a/docs/lua/index.rst +++ b/docs/lua/index.rst @@ -5,4 +5,5 @@ Lua API display controller resources + math utils diff --git a/docs/lua/math.rst b/docs/lua/math.rst new file mode 100644 index 00000000..d702d74d --- /dev/null +++ b/docs/lua/math.rst @@ -0,0 +1,369 @@ +``math`` - Арифметичні функції +------------------------------ + +Різні корисні функції. + +.. contents:: + :local: + :depth: 1 + +.. lua:module:: math + +random(a, [b]) +^^^^^^^^^^^^^^ + +.. lua:function:: random(a, [b]) + + Повертає випадкове число. + + :param a: Початок діапазону. + :type a: int + :param b: Кінець діапазону. + :type b: int + :rtype: int + + Якщо заданий лише один аргумент, повертає випадкове число в діапазоні [0;a] (включно). + + Якщо задані обидва аргументи, повертає випадкове число в діапазоні [a;b] (включно). + + Приклад використання: + + .. code-block:: lua + :linenos: + + local r = math.random(10, 20) + print(r) -- Виведе випадкове число від 10 до 20 (включно). + +clamp(x, min, max) +^^^^^^^^^^^^^^^^^^ + +.. lua:function:: clamp(x, min, max) + + Обмежує число в діапазоні. + + :param x: Число, яке потрібно обмежити. + :type x: number + :param min: Мінімальне значення. + :type min: number + :param max: Максимальне значення. + :type max: number + :rtype: number + + Повертає число ``x``, обмежене в діапазоні [min;max]. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.clamp(8.1, 10, 20) -- Виведе 10 + print(math.clamp(15.2, 10, 20) -- Виведе 15.2 + print(math.clamp(23.3, 10, 20) -- Виведе 20 + +lerp(min, max, x) +^^^^^^^^^^^^^^^^^ + +.. lua:function:: lerp(min, max, x) + + Лінійна інтерполяція. + + :param min: Мінімальне значення. + :type min: number + :param max: Максимальне значення. + :type max: number + :param x: Число, яке потрібно інтерполювати в діапазоні [0;1]. + :type x: number + :rtype: number + + Повертає число, яке лінійно інтерполюється між ``min`` та ``max`` відносно ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.lerp(0, 100, 0.5) -- Виведе 50 + print(math.lerp(0, 100, 0.25) -- Виведе 25 + print(math.lerp(0, 100, 0.7125) -- Виведе 71.25 + +map(x, in_min, in_max, out_min, out_max) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. lua:function:: map(x, in_min, in_max, out_min, out_max) + + Перетворення значення з одного діапазону в інший. + + :param x: Число, яке потрібно перетворити. + :type x: number + :param in_min: Мінімальне значення вхідного діапазону. + :type in_min: number + :param in_max: Максимальне значення вхідного діапазону. + :type in_max: number + :param out_min: Мінімальне значення вихідного діапазону. + :type out_min: number + :param out_max: Максимальне значення вихідного діапазону. + :type out_max: number + :rtype: number + + Повертає число ``x``, перетворене з діапазону [in_min;in_max] в діапазон [out_min;out_max]. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.map(50, 0, 100, 0, 1) -- Виведе 0.5 + print(math.map(25, 0, 100, 0, 1) -- Виведе 0.25 + print(math.map(71.25, 0, 100, 0, 1) -- Виведе 0.7125 + +dist(x1, y1, x2, y2) +^^^^^^^^^^^^^^^^^^^^ + +.. lua:function:: dist(x1, y1, x2, y2) + + Відстань між двома точками. + + :param x1: X-координата першої точки. + :type x1: number + :param y1: Y-координата першої точки. + :type y1: number + :param x2: X-координата другої точки. + :type x2: number + :param y2: Y-координата другої точки. + :type y2: number + :rtype: number + + Повертає відстань між точками (x1;y1) та (x2;y2). + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.dist(0, 0, 3, 4) -- Виведе 5 + print(math.dist(0, 0, 1, 1) -- Виведе 1.4142135623731 + print(math.dist(0, 0, 0, 0) -- Виведе 0 + +abs(x) +^^^^^^ + +.. lua:function:: abs(x) + + Модуль числа. + + :param x: Число, модуль якого потрібно знайти. + :type x: number + :rtype: number + + Повертає модуль числа ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.abs(-5) -- Виведе 5 + print(math.abs(5) -- Виведе 5 + print(math.abs(0) -- Виведе 0 + +sign(x) +^^^^^^^ + +.. lua:function:: sign(x) + + Знак числа. + + :param x: Число, знак якого потрібно знайти. + :type x: number + :rtype: int + + Повертає знак числа ``x``: -1, якщо число від'ємне, 0, якщо число дорівнює 0, 1, якщо число додатнє. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.sign(-5) -- Виведе -1 + print(math.sign(5) -- Виведе 1 + print(math.sign(0) -- Виведе 0 + +sqrt(x) +^^^^^^^ + +.. lua:function:: sqrt(x) + + Квадратний корінь. + + :param x: Число, квадратний корінь якого потрібно знайти. + :type x: number + :rtype: number + + Повертає квадратний корінь числа ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.sqrt(25) -- Виведе 5 + print(math.sqrt(16) -- Виведе 4 + print(math.sqrt(8) -- Виведе 2.8284271247462 + print(math.sqrt(0) -- Виведе 0 + +min(table) +^^^^^^^^^^ + +.. lua:function:: min(table) + + Мінімальне значення в таблиці. + + :param table: Таблиця чисел. + :type table: table + :rtype: number + + Повертає мінімальне значення в таблиці. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.min({1.1, 2.2, 3.3, 4.4, 5.5}) -- Виведе 1.1 + print(math.min({5, 4, 3, 2, 1}) -- Виведе 1 + print(math.min({-5, -4, -3, -2, -1}) -- Виведе -5 + +max(table) +^^^^^^^^^^ + +.. lua:function:: max(table) + + Максимальне значення в таблиці. + + :param table: Таблиця чисел. + :type table: table + :rtype: number + + Повертає максимальне значення в таблиці. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.max({1.1, 2.2, 3.3, 4.4, 5.5}) -- Виведе 5.5 + print(math.max({5, 4, 3, 2, 1}) -- Виведе 5 + print(math.max({-5, -4, -3, -2, -1}) -- Виведе -1 + +sum(table) +^^^^^^^^^^ + +.. lua:function:: sum(table) + + Сума всіх значень в таблиці. + + :param table: Таблиця чисел. + :type table: table + :rtype: number + + Повертає суму всіх значень в таблиці. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.sum({1, 2, 3, 4, 5}) -- Виведе 15 + print(math.sum({5.5, 4.5, 3.3, 2.2, 1.1}) -- Виведе 16.6 + print(math.sum({-5, -4, -3, -2, -1}) -- Виведе -15 + +avg(table) +^^^^^^^^^^ + +.. lua:function:: avg(table) + + Середнє значення в таблиці. + + :param table: Таблиця чисел. + :type table: table + :rtype: number + + Повертає середнє значення в таблиці. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.avg({1, 2, 3, 4, 5}) -- Виведе 3 + print(math.avg({5.5, 4.5, 3.3, 2.2, 1.1}) -- Виведе 3.32 + print(math.avg({-5, -4, -3, -2, -1}) -- Виведе -3 + +floor(x) +^^^^^^^^ + +.. lua:function:: floor(x) + + Округлення вниз. + + :param x: Число, яке потрібно округлити. + :type x: number + :rtype: int + + Повертає найбільше ціле число, яке менше або рівне ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.floor(5.5) -- Виведе 5 + print(math.floor(5.9) -- Виведе 5 + print(math.floor(-5.5) -- Виведе -6 + print(math.floor(-5.9) -- Виведе -6 + +ceil(x) +^^^^^^^ + +.. lua:function:: ceil(x) + + Округлення вгору. + + :param x: Число, яке потрібно округлити. + :type x: number + :rtype: int + + Повертає найменше ціле число, яке більше або рівне ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.ceil(5.5) -- Виведе 6 + print(math.ceil(5.9) -- Виведе 6 + print(math.ceil(-5.5) -- Виведе -5 + print(math.ceil(-5.9) -- Виведе -5 + +round(x) +^^^^^^^^ + +.. lua:function:: round(x) + + Округлення до найближчого цілого. + + :param x: Число, яке потрібно округлити. + :type x: number + :rtype: int + + Повертає найближче ціле число до ``x``. + + Приклад використання: + + .. code-block:: lua + :linenos: + + print(math.round(5.5) -- Виведе 6 + print(math.round(5.9) -- Виведе 6 + print(math.round(-5.5) -- Виведе -6 + print(math.round(-5.9) -- Виведе -6 diff --git a/docs/lua/resources.rst b/docs/lua/resources.rst index 531e7e30..2e633d47 100644 --- a/docs/lua/resources.rst +++ b/docs/lua/resources.rst @@ -3,14 +3,15 @@ Функції для роботи з ресурсами (зображеннями, звуками, тощо). +.. contents:: + :local: + :depth: 1 + Приклад: .. code-block:: lua :linenos: - local display = require("display") - local resources = require("resources") - local face = resources.load_bitmap("face.bmp", display.color565(0, 0, 0)) display.draw_bitmap(face, 50, 80) @@ -27,16 +28,15 @@ load_bitmap(filename, [transparent_color]) :param filename: Шлях до файлу з зображенням (відносно місця знаходження скрипта, що виконується). :type transparent_color: number :param transparent_color: Колір, який буде вважатися прозорим. Якщо цей параметр не вказаний, зображення виводиться без прозорості. + :rtype: table - Повертає ідентифікатор зображення, який можна використовувати для малювання зображення на екрані. + Повертає таблицю з ідентифікатором зображення (а також з його розмірами), яку можна використовувати для малювання зображення на екрані. Приклад використання: .. code-block:: lua :linenos: - local display = require("display") - local resources = require("resources") - local face = resources.load_bitmap("face.bmp", display.color565(0, 0, 0)) - display.draw_bitmap(face, 50, 80) + print(face.width, face.height) -- Виведе розміри зображення + display.draw_bitmap(face, 50, 80) -- Виведе зображення на екран у позицію (50, 80) diff --git a/docs/lua/utils.rst b/docs/lua/utils.rst index 3fad489b..d6b14f8d 100644 --- a/docs/lua/utils.rst +++ b/docs/lua/utils.rst @@ -3,48 +3,9 @@ Різні корисні функції. -.. code-block:: lua - :linenos: - - local utils = require("utils") - local display = require("display") - - display.set_cursor(0, 32) - - local r = utils.random(10, 20) - display.print("Випадкове число: " .. r) - - display.print("Зачекайте 1 секунду...") - utils.delay(1000) -- Затримує виконання програми на 1 секунду. - - r = utils.random(10, 20) - display.print("Нове випадкове число: " .. r) - -.. lua:module:: utils - -random(a, [b]) -^^^^^^^^^^^^^^ - -.. lua:function:: random(a, [b]) - - Повертає випадкове число. - - :type a: number - :type b: number - - Якщо заданий лише один аргумент, повертає випадкове число в діапазоні [0;a] (включно). - - Якщо задані обидва аргументи, повертає випадкове число в діапазоні [a;b] (включно). - - Приклад використання: - - .. code-block:: lua - :linenos: - - local utils = require("utils") - - local r = utils.random(10, 20) - print(r) -- Виведе випадкове число від 10 до 20 (включно). +.. contents:: + :local: + :depth: 1 delay(ms) ^^^^^^^^^ @@ -61,9 +22,6 @@ delay(ms) .. code-block:: lua :linenos: - local utils = require("utils") - local display = require("utils") - display.set_cursor(0, 32) display.print("Зачекайте 1 секунду...") utils.delay(1000) -- Затримує виконання програми на 1 секунду. diff --git a/docs/manual/lua.rst b/docs/manual/lua.rst index b2baab99..38b8e0aa 100644 --- a/docs/manual/lua.rst +++ b/docs/manual/lua.rst @@ -1,7 +1,7 @@ Написання програм на Lua ======================== -Основна прошивка Лілки має вбудований інтерпретатор мови програмування Lua. Це дозволяє писати та виконувати програми на Lua прямо з SD-картки, без необхідності компіляції чи перепрошивання Лілки. +Основна прошивка Лілки має вбудовану віртуальну машину мови програмування Lua. Це дозволяє писати та виконувати програми на Lua прямо з SD-картки, без необхідності компіляції чи перепрошивання Лілки. .. warning:: Якщо ви не знайомі з мовою Lua, рекомендуємо прочитати туторіал з Lua перед тим, як продовжувати: https://www.lua.org/pil/1.html @@ -10,16 +10,11 @@ Оскільки Lua сама по собі - це універсальна мова програмування, вона не має вбудованих функцій для роботи з дисплеєм, звуком, тощо. Тому для роботи з цими пристроями на Лілці використовуються спеціальні вбудовані модулі, які надають доступ до функцій пристроїв. -Модулі завантажуються за допомогою функції ``require``. Ось приклад простої програми на Lua, яка виводить текст "Hello, world!" на екран: +Модулі в Lua на Лілці завантажуються автоматично - не потрібно писати жодних ``require(...)``. Ось приклад простої програми на Lua, яка виводить текст "Hello, world!" на екран: .. code-block:: lua :linenos: - -- Завантажуємо модуль для роботи з дисплеєм: - local display = require("display") - -- Завантажуємо модуль, що містить корисні функції: - local utils = require("utils") - -- Заповнюємо екран чорним кольором: display.fill_screen(display.color565(0, 0, 0)) @@ -30,6 +25,62 @@ -- Чекаємо 2 секунди: utils.delay(2000) + -- На цьому програма автоматично завершиться. + Ви можете зберегти цей код у файл з розширенням ``.lua`` на SD-картці, а потім виконати його, обравши його в браузері SD-картки. Повний перелік доступних модулів та їх функцій можна знайти в розділі `Lua API `_. + +Написання ігор +-------------- + +В іграх важливо, щоб гра була плавною та виконувалася з певною кількістю кадрів на секунду. + +Для простих програм на Lua, які не вимагають точності в швидкості виконання, можна використовувати ``utils.delay(...)`` для затримки виконання програми. Однак це незручно для ігор. + +Для цього існує можливість визначити функцію ``_update()``. Якщо при запуску вашої програми Лілка знайде ці функції, вона буде викликати їх автоматично. + +``_update()`` викликається 30 разів на секунду, тому ви можете використовувати його для оновлення стану гри, обробки введення користувача, малювання графіки, тощо. + +``_update()`` повинен бути визначений у головному файлі програми, наприклад: + +.. code-block:: lua + :linenos: + + local ball_x = display.width / 2 + local ball_y = display.height / 2 + + local ball = resources.load_bitmap("ball.bmp", display.color565(255, 255, 255)) + + function _update() + local ball_speed_x = 0 + local ball_speed_y = 0 + + -- Обробляємо введення користувача: + if controller.up.pressed then + ball_speed_y = -4 + elseif controller.down.pressed then + ball_speed_y = 4 + end + if controller.left.pressed then + ball_speed_x = -4 + elseif controller.right.pressed then + ball_speed_x = 4 + end + + -- Оновлюємо стан гри: + ball_x = ball_x + ball_speed_x + ball_y = ball_y + ball_speed_y + + -- Малюємо графіку: + display.fill_screen(display.color565(0, 0, 0)) + display.draw_bitmap(ball, ball_x, ball_y) + + -- Оновлюємо екран: + display.render() + end + + -- Інші функції: + -- ... + +Цей код створить просту гру, в якій ви можете керувати м'ячем за допомогою стрілок на контролері. Кожен кадр гри м'яч переміщується на певну відстань, залежно від введення користувача, а потім малюється на екрані. diff --git a/sdk/lib/lilka/src/lua/lualilka_console.cpp b/sdk/lib/lilka/src/lua/lualilka_console.cpp index 01df4731..40a8625e 100644 --- a/sdk/lib/lilka/src/lua/lualilka_console.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_console.cpp @@ -27,9 +27,16 @@ static const luaL_Reg lualilka_console[] = { {NULL, NULL}, }; -int luaopen_lilka_console(lua_State* L) { +// int luaopen_lilka_console(lua_State* L) { +// luaL_newlib(L, lualilka_console); +// return 1; +// } + +int lualilka_console_register(lua_State* L) { + // Create global "console" table that contains all console functions luaL_newlib(L, lualilka_console); - return 1; + lua_setglobal(L, "console"); + return 0; } } // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_console.h b/sdk/lib/lilka/src/lua/lualilka_console.h index 06906d52..6acfb31d 100644 --- a/sdk/lib/lilka/src/lua/lualilka_console.h +++ b/sdk/lib/lilka/src/lua/lualilka_console.h @@ -6,7 +6,8 @@ namespace lilka { -int luaopen_lilka_console(lua_State* L); +// int luaopen_lilka_console(lua_State* L); +int lualilka_console_register(lua_State* L); } diff --git a/sdk/lib/lilka/src/lua/lualilka_controller.cpp b/sdk/lib/lilka/src/lua/lualilka_controller.cpp index 101a450e..4b19601d 100644 --- a/sdk/lib/lilka/src/lua/lualilka_controller.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_controller.cpp @@ -32,9 +32,16 @@ static const luaL_Reg lualilka_controller[] = { {NULL, NULL}, }; -int luaopen_lilka_controller(lua_State* L) { +// int luaopen_lilka_controller(lua_State* L) { +// luaL_newlib(L, lualilka_controller); +// return 1; +// } + +int lualilka_controller_register(lua_State* L) { + // Create global "controller" table that contains all controller functions luaL_newlib(L, lualilka_controller); - return 1; + lua_setglobal(L, "controller"); + return 0; } } // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_controller.h b/sdk/lib/lilka/src/lua/lualilka_controller.h index 76c2d9d6..647e25fb 100644 --- a/sdk/lib/lilka/src/lua/lualilka_controller.h +++ b/sdk/lib/lilka/src/lua/lualilka_controller.h @@ -6,7 +6,8 @@ namespace lilka { -int luaopen_lilka_controller(lua_State* L); +// int luaopen_lilka_controller(lua_State* L); +int lualilka_controller_register(lua_State* L); } diff --git a/sdk/lib/lilka/src/lua/lualilka_display.cpp b/sdk/lib/lilka/src/lua/lualilka_display.cpp index 04b4285a..7f1aec86 100644 --- a/sdk/lib/lilka/src/lua/lualilka_display.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_display.cpp @@ -236,9 +236,12 @@ int lualilka_display_fillArc(lua_State* L) { } int lualilka_display_drawBitmap(lua_State* L) { - // Args are bitmap handler, X & Y - // First argument is pointer to bitmap data - Bitmap* bitmap = (Bitmap*)lua_touserdata(L, 1); + // Args are bitmap table, X & Y + // First argument is table that contains bitmap width, height and pointer. We only need the pointer. + lua_getfield(L, 1, "pointer"); + Bitmap* bitmap = (Bitmap*)lua_touserdata(L, -1); + lua_pop(L, 1); + int16_t x = luaL_checkinteger(L, 2); int16_t y = luaL_checkinteger(L, 3); @@ -295,18 +298,33 @@ static const luaL_Reg lualilka_display[] = { {NULL, NULL}, }; -int luaopen_lilka_display(lua_State* L) { - // Set isBuffered to true by default in registry - lua_pushboolean(L, true); - lua_setfield(L, LUA_REGISTRYINDEX, "isBuffered"); - +// int luaopen_lilka_display(lua_State* L) { +// // Set isBuffered to true by default in registry +// lua_pushboolean(L, true); +// lua_setfield(L, LUA_REGISTRYINDEX, "isBuffered"); +// +// luaL_newlib(L, lualilka_display); +// // Add display width & height as library properties +// lua_pushinteger(L, LILKA_DISPLAY_WIDTH); +// lua_setfield(L, -2, "width"); +// lua_pushinteger(L, LILKA_DISPLAY_HEIGHT); +// lua_setfield(L, -2, "height"); +// return 1; +// } + +int lualilka_display_register(lua_State* L) { + // Create global "display" table that contains all display functions luaL_newlib(L, lualilka_display); // Add display width & height as library properties lua_pushinteger(L, LILKA_DISPLAY_WIDTH); lua_setfield(L, -2, "width"); lua_pushinteger(L, LILKA_DISPLAY_HEIGHT); lua_setfield(L, -2, "height"); - return 1; + // Set isBuffered to true by default in registry + lua_pushboolean(L, true); + lua_setfield(L, LUA_REGISTRYINDEX, "isBuffered"); + lua_setglobal(L, "display"); + return 0; } } // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_display.h b/sdk/lib/lilka/src/lua/lualilka_display.h index 743e6da8..387e1e6f 100644 --- a/sdk/lib/lilka/src/lua/lualilka_display.h +++ b/sdk/lib/lilka/src/lua/lualilka_display.h @@ -6,7 +6,8 @@ namespace lilka { -int luaopen_lilka_display(lua_State *L); +// int luaopen_lilka_display(lua_State *L); +int lualilka_display_register(lua_State *L); } diff --git a/sdk/lib/lilka/src/lua/lualilka_math.cpp b/sdk/lib/lilka/src/lua/lualilka_math.cpp new file mode 100644 index 00000000..fdfddeca --- /dev/null +++ b/sdk/lib/lilka/src/lua/lualilka_math.cpp @@ -0,0 +1,285 @@ +#include "lualilka_math.h" + +namespace lilka { + +int lualilka_math_random(lua_State* L) { + // If no args - return random value from 0 to max int + // If 1 arg - return random value from 0 to arg + // If 2 args - return random value from arg1 to arg2 + + int n = lua_gettop(L); + + if (n == 0) { + lua_pushinteger(L, random()); + return 1; + } else if (n == 1) { + int max = luaL_checkinteger(L, 1); + lua_pushinteger(L, random(max)); + return 1; + } else if (n == 2) { + int min = luaL_checkinteger(L, 1); + int max = luaL_checkinteger(L, 2); + lua_pushinteger(L, random(min, max)); + return 1; + } else { + return luaL_error(L, "Invalid number of arguments"); + } + + return 0; +} + +int lualilka_math_clamp(lua_State* L) { + int n = lua_gettop(L); + if (n != 3) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + float min = luaL_checknumber(L, 2); + float max = luaL_checknumber(L, 3); + + lua_pushnumber(L, value < min ? min : (value > max ? max : value)); + return 1; +} + +int lualilka_math_lerp(lua_State* L) { + int n = lua_gettop(L); + if (n != 3) { + return luaL_error(L, "Invalid number of arguments"); + } + + float a = luaL_checknumber(L, 1); + float b = luaL_checknumber(L, 2); + float t = luaL_checknumber(L, 3); + + lua_pushnumber(L, a + (b - a) * t); + return 1; +} + +int lualilka_math_map(lua_State* L) { + int n = lua_gettop(L); + if (n != 5) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + float in_min = luaL_checknumber(L, 2); + float in_max = luaL_checknumber(L, 3); + float out_min = luaL_checknumber(L, 4); + float out_max = luaL_checknumber(L, 5); + + lua_pushnumber(L, (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); + return 1; +} + +int lualilka_math_dist(lua_State* L) { + int n = lua_gettop(L); + if (n != 4) { + return luaL_error(L, "Invalid number of arguments"); + } + + float x1 = luaL_checknumber(L, 1); + float y1 = luaL_checknumber(L, 2); + float x2 = luaL_checknumber(L, 3); + float y2 = luaL_checknumber(L, 4); + + lua_pushnumber(L, sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))); + return 1; +} + +int lualilka_math_abs(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + lua_pushnumber(L, value < 0 ? -value : value); + return 1; +} + +int lualilka_math_sign(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + int value = luaL_checknumber(L, 1); + lua_pushinteger(L, value > 0 ? 1 : (value < 0 ? -1 : 0)); + return 1; +} + +int lualilka_math_sqrt(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + lua_pushnumber(L, sqrt(value)); + return 1; +} + +int assert_table_arg(lua_State* L, int index) { + // Check if there's only one argument, and if it's a table, and if it's not empty + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + if (!lua_istable(L, index)) { + return luaL_error(L, "Argument is not a table"); + } + + if (lua_rawlen(L, index) == 0) { + return luaL_error(L, "Table is empty"); + } + + return 0; +} + +int lualilka_math_min(lua_State* L) { + if (assert_table_arg(L, 1)) { + return 0; + } + + lua_pushinteger(L, 1); + lua_gettable(L, 1); + float min = luaL_checknumber(L, -1); + lua_pop(L, 1); + + int n = lua_gettop(L); + for (int i = 2; i <= n; i++) { + lua_pushinteger(L, i); + lua_gettable(L, 1); + float value = luaL_checknumber(L, -1); + if (value < min) { + min = value; + } + lua_pop(L, 1); + } + + lua_pushnumber(L, min); + return 1; +} + +int lualilka_math_max(lua_State* L) { + if (assert_table_arg(L, 1)) { + return 0; + } + + lua_pushinteger(L, 1); + lua_gettable(L, 1); + float max = luaL_checknumber(L, -1); + lua_pop(L, 1); + + int n = lua_gettop(L); + for (int i = 2; i <= n; i++) { + lua_pushinteger(L, i); + lua_gettable(L, 1); + float value = luaL_checknumber(L, -1); + if (value > max) { + max = value; + } + lua_pop(L, 1); + } + + lua_pushnumber(L, max); + return 1; +} + +int lualilka_math_sum(lua_State* L) { + if (assert_table_arg(L, 1)) { + return 0; + } + + float sum = 0; + + int n = lua_gettop(L); + for (int i = 1; i <= n; i++) { + lua_pushinteger(L, i); + lua_gettable(L, 1); + sum += luaL_checknumber(L, -1); + lua_pop(L, 1); + } + + lua_pushnumber(L, sum); + return 1; +} + +int lualilka_math_avg(lua_State* L) { + if (assert_table_arg(L, 1)) { + return 0; + } + + float sum = 0; + + int n = lua_gettop(L); + for (int i = 1; i <= n; i++) { + lua_pushinteger(L, i); + lua_gettable(L, 1); + sum += luaL_checknumber(L, -1); + lua_pop(L, 1); + } + + lua_pushnumber(L, sum / n); + return 1; +} + +int lualilka_math_floor(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + lua_pushinteger(L, floor(value)); + return 1; +} + +int lualilka_math_ceil(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + lua_pushinteger(L, ceil(value)); + return 1; +} + +int lualilka_math_round(lua_State* L) { + int n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "Invalid number of arguments"); + } + + float value = luaL_checknumber(L, 1); + lua_pushinteger(L, round(value)); + return 1; +} + +static const luaL_Reg lualilka_math[] = { + {"random", lualilka_math_random}, {"clamp", lualilka_math_clamp}, + {"lerp", lualilka_math_lerp}, {"map", lualilka_math_map}, + {"dist", lualilka_math_dist}, {"abs", lualilka_math_abs}, + {"sign", lualilka_math_sign}, {"sqrt", lualilka_math_sqrt}, + {"min", lualilka_math_min}, {"max", lualilka_math_max}, + {"sum", lualilka_math_sum}, {"avg", lualilka_math_avg}, + {"floor", lualilka_math_floor}, {"ceil", lualilka_math_ceil}, + {"round", lualilka_math_round}, {NULL, NULL}, +}; + +// int luaopen_lilka_math(lua_State* L) { +// luaL_newlib(L, lualilka_math); +// return 1; +// } + +int lualilka_math_register(lua_State* L) { + // Create global "math" table that contains all math functions + luaL_newlib(L, lualilka_math); + lua_setglobal(L, "math"); + return 0; +} + +} // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_math.h b/sdk/lib/lilka/src/lua/lualilka_math.h new file mode 100644 index 00000000..ce58ce6a --- /dev/null +++ b/sdk/lib/lilka/src/lua/lualilka_math.h @@ -0,0 +1,14 @@ +#ifndef LUALILKA_MATH_H +#define LUALILKA_MATH_H + +#include +#include + +namespace lilka { + +// int luaopen_lilka_math(lua_State *L); +int lualilka_math_register(lua_State* L); + +} + +#endif // LUALILKA_MATH_H diff --git a/sdk/lib/lilka/src/lua/lualilka_resources.cpp b/sdk/lib/lilka/src/lua/lualilka_resources.cpp index fe93f220..dfa1f7b4 100644 --- a/sdk/lib/lilka/src/lua/lualilka_resources.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_resources.cpp @@ -23,7 +23,7 @@ int lualilka_resources_loadBitmap(lua_State* L) { return luaL_error(L, "Failed to load bitmap %s", fullPath.c_str()); } - serial_log("Lua: loaded bitmap %s, width: %d, height: %d", path, bitmap->width, bitmap->height); + serial_log("lua: loaded bitmap %s, width: %d, height: %d", path, bitmap->width, bitmap->height); // Append bitmap to bitmaps table in registry lua_getfield(L, LUA_REGISTRYINDEX, "bitmaps"); @@ -31,8 +31,14 @@ int lualilka_resources_loadBitmap(lua_State* L) { lua_setfield(L, -2, path); lua_pop(L, 1); - // Return pointer to bitmap + // Create and return table that contains bitmap width, height and pointer + lua_newtable(L); + lua_pushinteger(L, bitmap->width); + lua_setfield(L, -2, "width"); + lua_pushinteger(L, bitmap->height); + lua_setfield(L, -2, "height"); lua_pushlightuserdata(L, bitmap); + lua_setfield(L, -2, "pointer"); return 1; } @@ -42,9 +48,16 @@ static const luaL_Reg lualilka_resources[] = { {NULL, NULL}, }; -int luaopen_lilka_resources(lua_State* L) { +// int luaopen_lilka_resources(lua_State* L) { +// luaL_newlib(L, lualilka_resources); +// return 1; +// } + +int lualilka_resources_register(lua_State* L) { + // Create global "resources" table that contains all resources functions luaL_newlib(L, lualilka_resources); - return 1; + lua_setglobal(L, "resources"); + return 0; } } // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_resources.h b/sdk/lib/lilka/src/lua/lualilka_resources.h index 83996cb4..c741f857 100644 --- a/sdk/lib/lilka/src/lua/lualilka_resources.h +++ b/sdk/lib/lilka/src/lua/lualilka_resources.h @@ -6,7 +6,8 @@ namespace lilka { -int luaopen_lilka_resources(lua_State* L); +// int luaopen_lilka_resources(lua_State* L); +int lualilka_resources_register(lua_State* L); } diff --git a/sdk/lib/lilka/src/lua/lualilka_util.cpp b/sdk/lib/lilka/src/lua/lualilka_util.cpp index bd265c52..71a1306e 100644 --- a/sdk/lib/lilka/src/lua/lualilka_util.cpp +++ b/sdk/lib/lilka/src/lua/lualilka_util.cpp @@ -2,32 +2,6 @@ namespace lilka { -int lualilka_util_random(lua_State* L) { - // If no args - return random value from 0 to max int - // If 1 arg - return random value from 0 to arg - // If 2 args - return random value from arg1 to arg2 - - int n = lua_gettop(L); - - if (n == 0) { - lua_pushinteger(L, random()); - return 1; - } else if (n == 1) { - int max = luaL_checkinteger(L, 1); - lua_pushinteger(L, random(max)); - return 1; - } else if (n == 2) { - int min = luaL_checkinteger(L, 1); - int max = luaL_checkinteger(L, 2); - lua_pushinteger(L, random(min, max)); - return 1; - } else { - return luaL_error(L, "Invalid number of arguments"); - } - - return 0; -} - int lualilka_util_delay(lua_State* L) { int ms = luaL_checkinteger(L, 1); delay(ms); @@ -35,14 +9,20 @@ int lualilka_util_delay(lua_State* L) { } static const luaL_Reg lualilka_util[] = { - {"random", lualilka_util_random}, {"delay", lualilka_util_delay}, {NULL, NULL}, }; -int luaopen_lilka_util(lua_State* L) { +// int luaopen_lilka_util(lua_State* L) { +// luaL_newlib(L, lualilka_util); +// return 1; +// } + +int lualilka_util_register(lua_State* L) { + // Create global "util" table that contains all util functions luaL_newlib(L, lualilka_util); - return 1; + lua_setglobal(L, "util"); + return 0; } } // namespace lilka diff --git a/sdk/lib/lilka/src/lua/lualilka_util.h b/sdk/lib/lilka/src/lua/lualilka_util.h index 2fd17337..4a302d06 100644 --- a/sdk/lib/lilka/src/lua/lualilka_util.h +++ b/sdk/lib/lilka/src/lua/lualilka_util.h @@ -6,7 +6,8 @@ namespace lilka { -int luaopen_lilka_util(lua_State* L); +// int luaopen_lilka_util(lua_State* L); +int lualilka_util_register(lua_State* L); } diff --git a/sdk/lib/lilka/src/lua/luarunner.cpp b/sdk/lib/lilka/src/lua/luarunner.cpp index 032c6e43..dbfb3c6e 100644 --- a/sdk/lib/lilka/src/lua/luarunner.cpp +++ b/sdk/lib/lilka/src/lua/luarunner.cpp @@ -5,61 +5,78 @@ #include "lualilka_display.h" #include "lualilka_console.h" #include "lualilka_controller.h" +#include "lualilka_math.h" #include "lualilka_util.h" #include "lualilka_resources.h" namespace lilka { int lua_run(String path) { - lilka::serial_log("Lua: init libs"); + lilka::serial_log("lua: init libs"); lua_State* L = luaL_newstate(); luaL_openlibs(L); - lilka::serial_log("Lua: init display"); - luaL_requiref(L, "display", luaopen_lilka_display, 1); - lua_pop(L, 1); - lilka::serial_log("Lua: init console"); - luaL_requiref(L, "console", luaopen_lilka_console, 1); - lua_pop(L, 1); - lilka::serial_log("Lua: init controller"); - luaL_requiref(L, "controller", luaopen_lilka_controller, 1); - lua_pop(L, 1); - lilka::serial_log("Lua: init util"); - luaL_requiref(L, "util", luaopen_lilka_util, 1); - lua_pop(L, 1); - lilka::serial_log("Lua: init resources"); - luaL_requiref(L, "resources", luaopen_lilka_resources, 1); - lua_pop(L, 1); + // lilka::serial_log("Lua: init display"); + // luaL_requiref(L, "display", luaopen_lilka_display, 1); + // lua_pop(L, 1); + // lilka::serial_log("Lua: init console"); + // luaL_requiref(L, "console", luaopen_lilka_console, 1); + // lua_pop(L, 1); + // lilka::serial_log("Lua: init controller"); + // luaL_requiref(L, "controller", luaopen_lilka_controller, 1); + // lua_pop(L, 1); + // lilka::serial_log("Lua: init math"); + // luaL_requiref(L, "math", luaopen_lilka_math, 1); + // lua_pop(L, 1); + // lilka::serial_log("Lua: init util"); + // luaL_requiref(L, "util", luaopen_lilka_util, 1); + // lua_pop(L, 1); + // lilka::serial_log("Lua: init resources"); + // luaL_requiref(L, "resources", luaopen_lilka_resources, 1); + // lua_pop(L, 1); + + lilka::serial_log("lua: init display"); + lualilka_display_register(L); + lilka::serial_log("lua: init console"); + lualilka_console_register(L); + lilka::serial_log("lua: init controller"); + lualilka_controller_register(L); + lilka::serial_log("lua: init math"); + lualilka_math_register(L); + lilka::serial_log("lua: init util"); + lualilka_util_register(L); + lilka::serial_log("lua: init resources"); + lualilka_resources_register(L); // Get dir name from path (without the trailing slash) String dir = path.substring(0, path.lastIndexOf('/')); - lilka::serial_log("Lua: script dir: %s", dir.c_str()); + lilka::serial_log("lua: script dir: %s", dir.c_str()); // Store dir in registry with "dir" key lua_pushstring(L, dir.c_str()); lua_setfield(L, LUA_REGISTRYINDEX, "dir"); - lilka::serial_log("Lua: init canvas"); + lilka::serial_log("lua: init canvas"); lilka::Canvas canvas; lilka::display.setFont(u8g2_font_10x20_t_cyrillic); canvas.setFont(u8g2_font_10x20_t_cyrillic); canvas.begin(); // Store canvas in registry with "canvas" key - lilka::serial_log("Lua: store canvas in registry"); + lilka::serial_log("lua: store canvas in registry"); lua_pushlightuserdata(L, &canvas); lua_setfield(L, LUA_REGISTRYINDEX, "canvas"); // Initialize table for bitmap pointers - lilka::serial_log("Lua: init memory for bitmaps"); + lilka::serial_log("lua: init memory for bitmaps"); lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, "bitmaps"); - lilka::serial_log("Lua: run script"); + lilka::serial_log("lua: run script"); int retCode = luaL_dofile(L, path.c_str()); if (retCode) { const char* err = lua_tostring(L, -1); lilka::ui_alert("Lua", String("Помилка: ") + err); } - lilka::serial_log("Lua: cleanup"); + lilka::serial_log("lua: cleanup"); // Free bitmaps from registry lua_getfield(L, LUA_REGISTRYINDEX, "bitmaps");