AlphaMatrixPixels (сокращенно "AMPix") — это C++ библиотека для работы с LED-матрицами и создания визуальных эффектов. Библиотека предназначена для использования на Arduino и других встраиваемых системах, а также на десктопных платформах. Основные возможности: работа с матрицами пикселей в формате ARGB (альфа-канал + RGB), система рендеринга эффектов с интроспекцией свойств, поддержка fixed-point математики для работы без float на Arduino, система событий и связывания эффектов. Библиотека использует namespace amp, все классы имеют префикс cs и PascalCase именование (например, csMatrixPixels, csColorRGBA, csRenderPlasma). Эффекты рисуют в объект csMatrixPixels, который представляет 2D-матрицу пикселей с поддержкой альфа-блендинга и клиппинга.
csEffectBase— если эффект не рисует в матрицуcsRenderMatrixBase— если эффект рисует в матрицу пикселейcsRenderDynamic— если эффект рисует в матрицу И нужны свойстваscaleиspeed
Все эффекты используют систему интроспекции для внешнего доступа к свойствам в runtime. Это позволяет GUI, скриптам и другим системам получать список свойств, их типы, имена и указатели на данные без знания конкретного типа эффекта.
Основные методы:
getPropsCount()— возвращает количество свойств (нумерация с 1)getPropInfo(propNum, info)— заполняетcsPropInfoдля свойства с номеромpropNum, через эту структуру можно получить указатели на данные объектаpropChanged(propNum)— вызывается при изменении свойства (чтобы объект мог узнать что его данные изменили)
Структура csPropInfo:
valueType— тип значения (PropType::UInt8,PropType::Color, etc.)valuePtr— указатель на поле эффектаname— имя свойства для GUIreadOnly— только для чтенияdisabled— скрыто от GUI (по умолчаниюtrue)
void render(csRandGen& rand, tTime currTime) const override {
if (disabled || !matrix) {
return;
}
const csRect target = rectDest.intersect(matrix->getRect());
if (target.empty()) {
return;
}
// Применить scale и speed (если используете csRenderDynamic)
const float scaleF = scale.to_float();
const float invScaleF = (scaleF > 0.0f) ? (1.0f / scaleF) : 1.0f;
const float t = static_cast<float>(currTime) * 0.001f * speed.to_float();
// Отрисовка
const tMatrixPixelsCoord endX = target.x + to_coord(target.width);
const tMatrixPixelsCoord endY = target.y + to_coord(target.height);
for (tMatrixPixelsCoord y = target.y; y < endY; ++y) {
for (tMatrixPixelsCoord x = target.x; x < endX; ++x) {
// Ваша логика вычисления цвета
matrix->setPixel(x, y, color);
}
}
}void recalc(csRandGen& rand, tTime currTime) override {
if (disabled) {
return;
}
// Обновить внутреннее состояние (частицы, анимация и т.д.)
}Нужно только если нужно экспортировать свойства через систему интроспекции (для GUI, скриптов и т.д.). Если эффект используется только напрямую в коде, можно пропустить.
class csRenderMyEffect : public csRenderDynamic {
public:
// Поля эффекта (публичные)
csColorRGBA color{255, 255, 255, 255};
uint8_t intensity = 128;
// Константы свойств (нумерация с 1)
static constexpr uint8_t base = csRenderDynamic::propLast;
static constexpr uint8_t propIntensity = base + 1;
static constexpr uint8_t propLast = propIntensity;
// ... методы ...
};uint8_t getPropsCount() const override {
return propLast;
}void getPropInfo(uint8_t propNum, csPropInfo& info) override {
csRenderDynamic::getPropInfo(propNum, info);
switch (propNum) {
case propIntensity:
info.valueType = PropType::UInt8;
info.name = "Intensity";
info.valuePtr = &intensity;
info.readOnly = false;
info.disabled = false;
break;
case propColor:
info.valuePtr = &color;
info.disabled = false;
break;
case propScale:
case propSpeed:
info.disabled = false; // Включить стандартные свойства
break;
}
}Метод вызывается при изменении свойства через систему интроспекции. Используется для синхронизации полей, обновления кэша или валидации значений.
Важно:
- Вызывать метод базового класса только в
default(чтобы не срабатывала логика для подавленных свойств) - Нумерация свойств с 1
- Вызывается только при изменении через интроспекцию, не при прямом изменении полей
Пример:
void propChanged(uint8_t propNum) override {
switch (propNum) {
case propRectDest: {
// Синхронизировать связанные поля
const tMatrixPixelsSize minSize = math::min(rectDest.width, rectDest.height);
rectDest.width = minSize;
rectDest.height = minSize;
break;
}
case propIntensity:
// Обновить кэш
break;
default:
csRenderDynamic::propChanged(propNum);
break;
}
}Поля csPropInfo:
valueType— тип (PropType::UInt8,PropType::Color,PropType::Bool, etc.)valuePtr— указатель на поле эффектаname— имя для GUIreadOnly— только для чтенияdisabled— скрыто от GUI (по умолчаниюtrue)
1.5. Идентификация семейства класса (опционально):
static constexpr PropType ClassFamilyId = PropType::EffectUserArea;
PropType getClassFamily() const override {
return ClassFamilyId;
}
void* queryClassFamily(PropType familyId) override {
if (familyId == ClassFamilyId) {
return this;
}
return csRenderDynamic::queryClassFamily(familyId);
}class csRenderBlink : public csRenderMatrixBase {
public:
static constexpr uint8_t base = csRenderMatrixBase::propLast;
static constexpr uint8_t propBlinkSpeed = base + 1;
static constexpr uint8_t propLast = propBlinkSpeed;
csColorRGBA color{255, 255, 255, 255};
uint8_t blinkSpeed = 2;
uint8_t getPropsCount() const override {
return propLast;
}
void getPropInfo(uint8_t propNum, csPropInfo& info) override {
csRenderMatrixBase::getPropInfo(propNum, info);
switch (propNum) {
case propBlinkSpeed:
info.valueType = PropType::UInt8;
info.name = "Blink speed";
info.valuePtr = &blinkSpeed;
info.disabled = false;
break;
case propColor:
info.valuePtr = &color;
info.disabled = false;
break;
}
}
void render(csRandGen& /*rand*/, tTime currTime) const override {
if (disabled || !matrix) {
return;
}
const csRect target = rectDest.intersect(matrix->getRect());
if (target.empty()) {
return;
}
const float t = static_cast<float>(currTime) * 0.001f * static_cast<float>(blinkSpeed);
const float brightness = (sinf(t) + 1.0f) * 0.5f;
const uint8_t alpha = static_cast<uint8_t>(brightness * 255.0f);
const csColorRGBA blinkColor{alpha, color.r, color.g, color.b};
const tMatrixPixelsCoord endX = target.x + to_coord(target.width);
const tMatrixPixelsCoord endY = target.y + to_coord(target.height);
for (tMatrixPixelsCoord y = target.y; y < endY; ++y) {
for (tMatrixPixelsCoord x = target.x; x < endX; ++x) {
matrix->setPixel(x, y, blinkColor);
}
}
}
};- Нумерация свойств с 1 —
getPropsCount() == 5означает свойства 1, 2, 3, 4, 5 - Проверки в render() — всегда проверять
disabledиmatrixв начале - Клиппинг — использовать
rectDest.intersect(matrix->getRect()) - Поля публичные — по дизайну для производительности
- render() const — метод
const, но может изменятьmatrix(это нормально) - Область рендеринга —
renderRectAutosize = true(по умолчанию) автоматически устанавливаетrectDestравным размеру матрицы
- Выбран базовый класс
- Определены константы свойств (
base,propLast) - Переопределён
getPropsCount() - Переопределён
getPropInfo()для всех свойств - Реализован
render()с проверкамиdisabledиmatrix - Реализован
recalc()(если нужно) - Используется
rectDest.intersect(matrix->getRect())для клиппинга - Код добавлен в
src/matrix_render_efffects.hpp