From 3e572d77816ba958f5796f86eaf7a891ab18e6b9 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Tue, 12 Mar 2024 21:55:27 +0200 Subject: [PATCH] lib: update docs on display transforms & pivots --- sdk/lib/lilka/src/lilka/display.h | 47 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/sdk/lib/lilka/src/lilka/display.h b/sdk/lib/lilka/src/lilka/display.h index aec1ace3..ccc50602 100644 --- a/sdk/lib/lilka/src/lilka/display.h +++ b/sdk/lib/lilka/src/lilka/display.h @@ -176,8 +176,8 @@ class Display : public Arduino_ST7789 { /// Намалювати зображення. /// @param image Вказівник на зображення (об'єкт класу `lilka::Image`). - /// @param x Координата X лівого верхнього кута зображення. - /// @param y Координата Y лівого верхнього кута зображення. + /// @param x Координата X осі зображення. + /// @param y Координата Y осі зображення. /// /// Приклад використання: /// @@ -194,8 +194,8 @@ class Display : public Arduino_ST7789 { void drawImage(Image* image, int16_t x, int16_t y); /// Намалювати зображення з афінними перетвореннями. /// @param image Вказівник на зображення (об'єкт класу `lilka::Image`). - /// @param x Координата X лівого верхнього кута зображення. - /// @param y Координата Y лівого верхнього кута зображення. + /// @param x Координата X осі зображення. + /// @param y Координата Y осі зображення. /// @param transform Об'єкт класу `lilka::Transform`, який містить матрицю перетворення. /// @note Зверніть увагу, що перетворення - це повільніше, ніж звичайне малювання зображення, оскільки обчислює координати пікселів "на льоту". Використовуйте його лише тоді, коли не можете заздалегідь створити обернені копії зображеня за допомогою методів `lilka::Image::rotate`, `lilka::Image::flipX` та `lilka::Image::flipY`. /// @see lilka::Transform @@ -251,14 +251,15 @@ class Display : public Arduino_ST7789 { /// Клас для роботи з графічним буфером. /// -/// При частому перемальовуванні екрану без використання буфера, може спостерігатися мерехтіння. +/// При частому перемальовуванні екрану без використання буфера може спостерігатися мерехтіння. /// Наприклад, якщо використовувати метод `fillScreen` для очищення екрану перед кожним викликом `print`, /// то текст буде мерехтіти. /// /// Щоб уникнути цього, можна використовувати буфер. Цей клас дозволяє малювати графічні об'єкти на буфері, /// а потім відобразити його на екрані за допомогою методу `lilka::display.renderCanvas`. /// -/// Такий підхід дозволяє зменшити мерехтіння, але збільшує використання пам'яті. Він називається "буферизація". +/// Такий підхід дозволяє зменшити мерехтіння, але збільшує використання пам'яті. Він називається "буферизація", +/// оскільки ми спершу малюємо на буфері, а тоді відображаємо буфер на екрані. /// /// Цей клас, як і `Display`, є підкласом `Arduino_GFX` з бібліотеки `Arduino_GFX_Library`. /// Це означає, що майже всі методи, які доступні в `Display`, також доступні в `Canvas`. @@ -273,11 +274,11 @@ class Display : public Arduino_ST7789 { /// } /// /// void loop() { -/// lilka::Canvas canvas; +/// lilka::Canvas canvas; // Створити новий Canvas зі стандартним розміром (розмір дисплею) /// int y = 100; /// while (1) { /// canvas.fillScreen(lilka::display.color565(0, 0, 0)); // Заповнити буфер чорним кольором -/// canvas.setCursor(32, 0); +/// canvas.setCursor(32, y); /// canvas.setTextColor(lilka::display.color565(0, 0, 0)); // Білий текст /// canvas.print("Привіт, Лілка!"); /// lilka::display.renderCanvas(&canvas); // Відобразити буфер на екрані - жодного мерехтіння! @@ -312,6 +313,8 @@ class Canvas : public Arduino_Canvas { /// Містить розміри, прозорий колір та пікселі зображення (в 16-бітному форматі, 5-6-5). /// Пікселі зберігаються в рядку зліва направо, зверху вниз. /// +/// Вісь зображення - це точка, яка вказує на центр зображення. Це дозволяє вам встановити точку, відносно якої буде відображатися зображення, а також навколо якої буде відбуватися перетворення зображення. +/// /// @note Основна відмінність Image від поняття "bitmap" погялає в тому, що Image містить масив пікселів, розміри зображення і прозорий колір, в той час як "bitmap" - це просто масив пікселів. class Image { public: @@ -319,6 +322,10 @@ class Image { ~Image(); /// Обернути зображення на заданий кут (в градусах) і записати результат в `dest`. /// + /// Цей метод, а також методи `flipX` та `flipY`, зручно використовувати для створення обернених та віддзеркалених копій зображення, якщо ви заздалегідь знаєте, які варіанти зображення вам знадобляться. + /// + /// Замість них можна використовувати клас `lilka::Transform` та його методи, які дозволяють виконувати та комбінувати складніші перетворення "на льоту", але такі перетворення є повільнішими. + /// /// @param angle Кут обертання в градусах. /// @param dest Вказівник на Image, в яке буде записано обернуте зображення. /// @param blankColor 16-бітний колір (5-6-5), який буде використаний для заповнення пікселів, які виходять за межі зображення. @@ -339,6 +346,7 @@ class Image { /// delete image; /// delete rotatedImage; /// @endcode + /// @see Display::drawImageTransformed, Canvas::drawImageTransformed, Transform void rotate(int16_t angle, Image* dest, int32_t blankColor); /// Віддзеркалити зображення по горизонталі і записати результат в `dest`. void flipX(Image* dest); @@ -358,7 +366,9 @@ class Image { /// Афінні перетворення - це перетворення, які зберігають паралельність ліній. /// Вони включають в себе обертання, масштабування та віддзеркалення. /// -/// Наприклад, ось цей код обертає зображення на 30 градусів та віддзеркалює його по горизонталі: +/// Перетворення - це всього лиш матриця 2x2. Застосування перетворення до вектора - це множення цього вектора на матрицю перетворення. Магія! +/// +/// Наприклад, ось цей код обертає зображення на 30 градусів і тоді віддзеркалює його по горизонталі: /// /// @code /// lilka::Transform transform = lilka::Transform().rotate(30).flipX(); @@ -374,23 +384,38 @@ class Transform { /// /// Оскільки на екрані вісь Y вказує вниз, обертання буде здійснено за годинниковою стрілкою. /// @param angle Кут обертання в градусах. + /// @return Нове перетворення. Transform rotate(int16_t angle); /// Масштабувати по X та Y. /// /// Щоб віддзеркалити зображення, використайте від'ємні значення (наприклад, -1). /// @param scaleX Масштаб по X. /// @param scaleY Масштаб по Y. + /// @return Нове перетворення. Transform scale(float scaleX, float scaleY); - /// Застосувати інше перетворення до цього. + /// Помножити це перетворення на інше. + /// + /// @note Зверніть увагу: при комбінації перетворень порядок важливий, і він є оберненим до порядку множення матриць! В результаті цього, перетворення other буде виконано **перед** поточним перетворенням. Тобто якщо в вас є деякі перетворення `A` та `B`, то перетворення `A.multiply(B)` утворить нове перетворення, в якому спочатку виконається перетворення `B`, а потім `A`. + /// @note Для уникнення плутанини рекомендуємо використовувати більш високорівневі методи, такі як `rotate` та `scale`. /// @param other Інше перетворення. + /// @return Перетворення, яке є результатом застосування цього перетворення після `other` перетворення. + /// @code + /// lilka::Transform rotate30 = lilka::Transform().rotate(30); // обернути на 30 градусів + /// lilka::Transform scale2x = lilka::Transform().scale(2, 2); // збільшити в 2 рази + /// lilka::Transform scaleThenRotate = rotate30.multiply(scale2x); // результат - це перетворення "спочатку збільшити, а потім обернути", а не навпаки! + /// @endcode Transform multiply(Transform other); /// Інвертувати перетворення. /// @note Інвертне перетворення - це таке перетворення, яке скасує це перетворення, тобто є зворотнім до цього. + /// @return Інвертоване перетворення. Transform inverse(); - int_vector_t apply(int_vector_t vector); + /// Перетворити вектор, використовуючи це перетворення. + /// @param vector Вхідний вектор. + /// @return Результат перетворення. + int_vector_t transform(int_vector_t vector); // Матриця перетворення float matrix[2][2]; // [рядок][стовпець]