Compare commits
No commits in common. "f2ce84a002b351a36b079451b4b391efdf156f51" and "d975d7d207b1838ecbca936b86343767521bdf7f" have entirely different histories.
f2ce84a002
...
d975d7d207
@ -16,8 +16,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"knolleary/pubsubclient": "2.8",
|
"knolleary/pubsubclient": "2.8",
|
||||||
"bblanchon/ArduinoJson": "7.3.0",
|
"bblanchon/ArduinoJson": "7.3.0",
|
||||||
"me-no-dev/ESPAsyncWebServer": "1.2.4",
|
"me-no-dev/ESPAsyncWebServer": "1.2.4"
|
||||||
"adafruit/Adafruit NeoPixel": "1.12.4"
|
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"include": "src/patrix"
|
"include": "src/patrix"
|
||||||
|
|||||||
@ -2,9 +2,8 @@
|
|||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.filesystem = littlefs
|
lib_deps =
|
||||||
lib_deps = knolleary/pubsubclient@2.8
|
knolleary/pubsubclient@2.8
|
||||||
bblanchon/ArduinoJson@7.3.0
|
bblanchon/ArduinoJson@7.3.0
|
||||||
me-no-dev/ESPAsyncWebServer@1.2.4
|
me-no-dev/ESPAsyncWebServer@1.2.4
|
||||||
https://github.com/adafruit/Adafruit_NeoPixel
|
|
||||||
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\"
|
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\"
|
||||||
@ -1,41 +1,21 @@
|
|||||||
#ifndef PATRIX_NODE_TEST_H
|
#ifndef NODE_TEST_H
|
||||||
#define PATRIX_NODE_TEST_H
|
#define NODE_TEST_H
|
||||||
|
|
||||||
#include <patrix/core/Config.h>
|
|
||||||
#include <patrix/display/DisplayMatrix.h>
|
#include <patrix/display/DisplayMatrix.h>
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
Config config("/test.json");
|
DisplayMatrix<32, 8> display;
|
||||||
|
|
||||||
DisplayMatrix<32, 8> display(27);
|
class NodeTest final : public Node {
|
||||||
|
|
||||||
class NodeTest final : public PatrixNode {
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit NodeTest() : PatrixNode(true, true, true) {
|
explicit NodeTest() : Node(true, true, true) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
config.read();
|
|
||||||
display.setup();
|
|
||||||
display.setBrightness(6);
|
|
||||||
|
|
||||||
display.clear();
|
|
||||||
|
|
||||||
display.foreground = Blue;
|
|
||||||
display.printf("Test");
|
display.printf("Test");
|
||||||
|
|
||||||
display.foreground = Red;
|
|
||||||
display.cursorX = 0;
|
|
||||||
display.cursorY = 7;
|
|
||||||
display.drawLine(32, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() override {
|
|
||||||
config.loop();
|
|
||||||
display.loop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
auto node = NodeTest();
|
auto node = NodeTest();
|
||||||
|
|
||||||
PatrixNode &patrixGetNode() {
|
Node &patrixGetNode() {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/library.json
Normal file
47
src/library.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "Patrix",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Patrick Haßel",
|
||||||
|
"email": "development@patrick-hassel.de",
|
||||||
|
"maintainer": true,
|
||||||
|
"url": "https://patrick-hassel.de/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Patrix library",
|
||||||
|
"homepage": "https://patrick-hassel.de/",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"owner": "knolleary",
|
||||||
|
"name": "pubsubclient",
|
||||||
|
"version": "2.8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "bblanchon",
|
||||||
|
"name": "ArduinoJson",
|
||||||
|
"version": "7.3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "me-no-dev",
|
||||||
|
"name": "ESPAsyncWebServer",
|
||||||
|
"version": "1.2.4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"export": {
|
||||||
|
"exclude": [
|
||||||
|
"src/demo/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"patrix"
|
||||||
|
],
|
||||||
|
"platforms": [
|
||||||
|
"espressif8266",
|
||||||
|
"espressif32"
|
||||||
|
],
|
||||||
|
"frameworks": [
|
||||||
|
"arduino"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -12,6 +12,6 @@
|
|||||||
#include <patrix/core/mqtt.h>
|
#include <patrix/core/mqtt.h>
|
||||||
#include <patrix/core/system.h>
|
#include <patrix/core/system.h>
|
||||||
#include <patrix/core/wifi.h>
|
#include <patrix/core/wifi.h>
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,106 +0,0 @@
|
|||||||
#ifndef PATRIX_CONFIG_H
|
|
||||||
#define PATRIX_CONFIG_H
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <LittleFS.h>
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "filesystem.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
#define CONFIG_WRITE_DELAY_MILLIS (30 * 1000)
|
|
||||||
|
|
||||||
class Config final {
|
|
||||||
|
|
||||||
const String path;
|
|
||||||
|
|
||||||
JsonDocument config;
|
|
||||||
|
|
||||||
unsigned long dirty = 0;
|
|
||||||
|
|
||||||
bool doForceNextHexBuffer = true;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit Config(String path) : path(std::move(path)) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
~Config() = default;
|
|
||||||
|
|
||||||
void read() {
|
|
||||||
fsMount();
|
|
||||||
auto file = LittleFS.open(path, "r");
|
|
||||||
if (!file) {
|
|
||||||
error("failed to open file for config: %s", path.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!deserialize(file)) {
|
|
||||||
config.clear();
|
|
||||||
config = config.to<JsonObject>();
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write() const {
|
|
||||||
fsMount();
|
|
||||||
auto write = LittleFS.open(path, "w");
|
|
||||||
if (!write) {
|
|
||||||
error("failed to open file for config write: %s", path.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto size = measureJson(config);
|
|
||||||
if (serializeJson(config, write) == size) {
|
|
||||||
char buffer[256];
|
|
||||||
serializeJson(config, buffer, sizeof buffer);
|
|
||||||
info("config written: %s => %s", path.c_str(), buffer);
|
|
||||||
} else {
|
|
||||||
error("failed to write config: %s", path.c_str());
|
|
||||||
}
|
|
||||||
write.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
if (dirty != 0 && millis() - dirty > CONFIG_WRITE_DELAY_MILLIS) {
|
|
||||||
dirty = 0;
|
|
||||||
write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T get(const char *key, T fallback) {
|
|
||||||
if (config[key].is<T>()) {
|
|
||||||
return config[key].as<T>();
|
|
||||||
}
|
|
||||||
warn("config key \"%s\" not found!", key);
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void set(const char *key, T value) {
|
|
||||||
config[key] = value;
|
|
||||||
dirty = max(1UL, millis()); // avoid special value zero (=> not dirty)
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool deserialize(File file) {
|
|
||||||
if (deserializeJson(config, file) != DeserializationError::Ok) {
|
|
||||||
error("failed to deserialize config: %s", file.path());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!config.is<JsonObject>()) {
|
|
||||||
error("config not a json-object: %s", file.path());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char buffer[256];
|
|
||||||
serializeJson(config, buffer, sizeof buffer);
|
|
||||||
info("config loaded: %s => %s", path.c_str(), buffer);
|
|
||||||
config = config.as<JsonObject>();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
#include <patrix/core/clock.h>
|
#include <patrix/core/clock.h>
|
||||||
#include <patrix/core/log.h>
|
#include <patrix/core/log.h>
|
||||||
#include <patrix/core/wifi.h>
|
#include <patrix/core/wifi.h>
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
#include "esp32/rom/rtc.h"
|
#include "esp32/rom/rtc.h"
|
||||||
|
|
||||||
|
|||||||
@ -1,64 +0,0 @@
|
|||||||
#include "filesystem.h"
|
|
||||||
|
|
||||||
#include <FS.h>
|
|
||||||
#include <LittleFS.h>
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
auto fsMounted = false;
|
|
||||||
|
|
||||||
bool fsMount() {
|
|
||||||
if (!fsMounted) {
|
|
||||||
fsMounted = LittleFS.begin(true);
|
|
||||||
}
|
|
||||||
if (fsMounted) {
|
|
||||||
info("filesystem mounted: %3d%% used (%d bytes)", static_cast<int>(round(100.0 * LittleFS.usedBytes() / LittleFS.totalBytes())), LittleFS.usedBytes());
|
|
||||||
} else {
|
|
||||||
error("failed to mount filesystem");
|
|
||||||
}
|
|
||||||
return fsMounted;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fsIsMounted() {
|
|
||||||
return fsMounted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsList(FS& fs, const char *path, const String& indent) { // NOLINT(*-no-recursion)
|
|
||||||
auto dir = fs.open(path);
|
|
||||||
if (!dir) {
|
|
||||||
error("not found: %s", path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!dir.isDirectory()) {
|
|
||||||
error("not a directory: %s", path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info("LS: %s %4s %s", indent.c_str(), "", dir.name());
|
|
||||||
|
|
||||||
const auto indent2 = indent + " ";
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto file = dir.openNextFile();
|
|
||||||
if (!file) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
fsList(fs, file.path());
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
dir.rewindDirectory();
|
|
||||||
while (true) {
|
|
||||||
auto file = dir.openNextFile();
|
|
||||||
if (!file) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!file.isDirectory()) {
|
|
||||||
info("LS: %s %4d %s", indent2.c_str(), file.size(), file.name());
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
dir.close();
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#ifndef PATRIX_FILESYSTEM_H
|
|
||||||
#define PATRIX_FILESYSTEM_H
|
|
||||||
|
|
||||||
#include <FS.h>
|
|
||||||
#include <WString.h>
|
|
||||||
|
|
||||||
bool fsMount();
|
|
||||||
|
|
||||||
bool fsIsMounted();
|
|
||||||
|
|
||||||
void fsList(FS& fs, const char *path, const String& indent = "");
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <patrix/core/log.h>
|
#include <patrix/core/log.h>
|
||||||
#include <patrix/core/system.h>
|
#include <patrix/core/system.h>
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +1,18 @@
|
|||||||
#ifndef DISPLAY_MATRIX_H
|
#ifndef DISPLAY_MATRIX_H
|
||||||
#define DISPLAY_MATRIX_H
|
#define DISPLAY_MATRIX_H
|
||||||
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
|
||||||
|
|
||||||
#include "DisplayMatrix_FontCommon.h"
|
#include "DisplayMatrix_FontCommon.h"
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
class DisplayMatrix {
|
class DisplayMatrix {
|
||||||
|
|
||||||
Adafruit_NeoPixel leds;
|
Color matrix[H][W] = {};
|
||||||
|
|
||||||
RGB matrix[H][W] = {};
|
|
||||||
|
|
||||||
bool dirty = true;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RGBA foreground = White;
|
Color foreground = White;
|
||||||
|
|
||||||
RGBA background = Transparent;
|
Color background = Transparent;
|
||||||
|
|
||||||
uint8_t brightness = 10;
|
|
||||||
|
|
||||||
uint8_t alpha = 255;
|
uint8_t alpha = 255;
|
||||||
|
|
||||||
@ -28,45 +20,13 @@ public:
|
|||||||
|
|
||||||
int cursorY = 0;
|
int cursorY = 0;
|
||||||
|
|
||||||
// basic ----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
explicit DisplayMatrix(const int pin): leds(W * H, pin) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
leds.begin();
|
|
||||||
leds.setBrightness(brightness);
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
if (dirty) {
|
|
||||||
for (auto y = 0; y < H; y++) {
|
|
||||||
for (auto x = 0; x < W; x++) {
|
|
||||||
auto rgb = matrix[y][x];
|
|
||||||
leds.setPixelColor(y * W + x, rgb.r, rgb.g, rgb.b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
leds.show();
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBrightness(const uint8_t brightness) {
|
|
||||||
if (leds.getBrightness() != brightness) {
|
|
||||||
leds.setBrightness(brightness);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw -----------------------------------------------------------------------------------------
|
// draw -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void fillRect(int w = W, int h = H);
|
void fillRect(int w = W, int h = H) const;
|
||||||
|
|
||||||
void drawLine(int w, int h, int thickness = 1);
|
void drawLine(int w = W, int h = H, int thickness = 1) const;
|
||||||
|
|
||||||
// print ----------------------------------------------------------------------------------------
|
// print ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -88,16 +48,15 @@ public:
|
|||||||
|
|
||||||
void print(bool **s, size_t w, size_t h);
|
void print(bool **s, size_t w, size_t h);
|
||||||
|
|
||||||
void print(SymbolRGBA8x8 s);
|
void print(SymbolRGB8x8 s);
|
||||||
|
|
||||||
void print(RGBA **s, size_t w, size_t h);
|
void print(Color **s, size_t w, size_t h);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ReSharper disable once CppUnusedIncludeDirective
|
// ReSharper disable CppUnusedIncludeDirective
|
||||||
#include <patrix/display/DisplayMatrix_draw.h>
|
#include <patrix/display/DisplayMatrix_draw.h>
|
||||||
|
|
||||||
// ReSharper disable once CppUnusedIncludeDirective
|
|
||||||
#include <patrix/display/DisplayMatrix_print.h>
|
#include <patrix/display/DisplayMatrix_print.h>
|
||||||
|
// ReSharper restore CppUnusedIncludeDirective
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -8,6 +8,6 @@ typedef bool Symbol2[5][2];
|
|||||||
typedef bool Symbol3[5][3];
|
typedef bool Symbol3[5][3];
|
||||||
typedef bool Symbol4[5][4];
|
typedef bool Symbol4[5][4];
|
||||||
typedef bool Symbol5[5][5];
|
typedef bool Symbol5[5][5];
|
||||||
typedef RGBA SymbolRGBA8x8[8][8];
|
typedef Color SymbolRGB8x8[8][8];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -246,14 +246,14 @@ Symbol5 FONT_CHAR_PERCENT = {
|
|||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
const auto B = Black;
|
const auto B = Black;
|
||||||
RGBA M{204, 0, 250};
|
Color M{204, 0, 250};
|
||||||
RGBA m{224, 121, 250};
|
Color m{224, 121, 250};
|
||||||
RGBA g{130, 213, 0};
|
Color g{130, 213, 0};
|
||||||
RGBA G{ 48, 89, 55};
|
Color G{ 48, 89, 55};
|
||||||
RGBA F{ 75, 202, 0};
|
Color F{ 75, 202, 0};
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
SymbolRGBA8x8 FONT_CREEPER = {
|
SymbolRGB8x8 FONT_CREEPER = {
|
||||||
{g, F, F, F, g, g, g, F},
|
{g, F, F, F, g, g, g, F},
|
||||||
{F, F, F, F, F, g, F, F},
|
{F, F, F, F, F, g, F, F},
|
||||||
{F, G, G, g, g, G, G, F},
|
{F, G, G, g, g, G, G, F},
|
||||||
@ -264,7 +264,7 @@ SymbolRGBA8x8 FONT_CREEPER = {
|
|||||||
{F, g, G, g, g, G, F, F},
|
{F, g, G, g, g, G, F, F},
|
||||||
};
|
};
|
||||||
|
|
||||||
SymbolRGBA8x8 FONT_ENDERMAN = {
|
SymbolRGB8x8 FONT_ENDERMAN = {
|
||||||
{B, B, B, B, B, B, B, B},
|
{B, B, B, B, B, B, B, B},
|
||||||
{B, B, B, B, B, B, B, B},
|
{B, B, B, B, B, B, B, B},
|
||||||
{B, B, B, B, B, B, B, B},
|
{B, B, B, B, B, B, B, B},
|
||||||
|
|||||||
@ -37,7 +37,7 @@ extern Symbol2 FONT_CHAR_LT;
|
|||||||
extern Symbol2 FONT_CHAR_GT;
|
extern Symbol2 FONT_CHAR_GT;
|
||||||
extern Symbol4 FONT_ERROR_;
|
extern Symbol4 FONT_ERROR_;
|
||||||
extern Symbol5 FONT_CHAR_PERCENT;
|
extern Symbol5 FONT_CHAR_PERCENT;
|
||||||
extern SymbolRGBA8x8 FONT_CREEPER;
|
extern SymbolRGB8x8 FONT_CREEPER;
|
||||||
extern SymbolRGBA8x8 FONT_ENDERMAN;
|
extern SymbolRGB8x8 FONT_ENDERMAN;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
#include "DisplayMatrix_common.h"
|
#include "DisplayMatrix_common.h"
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
const RGBA Transparent { 0, 0, 0, 0};
|
const Color Transparent { 0, 0, 0, 0};
|
||||||
const RGBA Black { 0, 0, 0, 255};
|
const Color Black { 0, 0, 0, 255};
|
||||||
const RGBA Red {255, 0, 0, 255};
|
const Color Red {255, 0, 0, 255};
|
||||||
const RGBA Yellow {255, 255, 0, 255};
|
const Color Yellow {255, 255, 0, 255};
|
||||||
const RGBA Green { 0, 255, 0, 255};
|
const Color Green { 0, 255, 0, 255};
|
||||||
const RGBA Cyan { 0, 255, 255, 255};
|
const Color Cyan { 0, 255, 255, 255};
|
||||||
const RGBA Blue { 0, 0, 255, 255};
|
const Color Blue { 0, 0, 255, 255};
|
||||||
const RGBA Magenta {255, 0, 255, 255};
|
const Color Magenta {255, 0, 255, 255};
|
||||||
const RGBA Gray {127, 127, 127, 255};
|
const Color Gray {127, 127, 127, 255};
|
||||||
const RGBA White {255, 255, 255, 255};
|
const Color White {255, 255, 255, 255};
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|||||||
@ -8,43 +8,35 @@
|
|||||||
|
|
||||||
#define countof(a) (sizeof(a)/sizeof(a[0]))
|
#define countof(a) (sizeof(a)/sizeof(a[0]))
|
||||||
|
|
||||||
struct RGBA {
|
struct Color {
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
uint8_t g;
|
uint8_t g;
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
uint8_t a;
|
uint8_t a;
|
||||||
};
|
|
||||||
|
|
||||||
struct RGB {
|
void blend(const Color other, const uint8_t alpha) {
|
||||||
uint8_t r;
|
const auto thisFactor = other.a * alpha / (255.0 * 255.0);
|
||||||
uint8_t g;
|
const auto otherFactor = 1.0 - thisFactor;
|
||||||
uint8_t b;
|
blend(&this->r, thisFactor, other.r, otherFactor);
|
||||||
|
blend(&this->g, thisFactor, other.g, otherFactor);
|
||||||
void operator =(const RGBA& color) {
|
blend(&this->b, thisFactor, other.b, otherFactor);
|
||||||
blend(&this->r, color.r, color.a);
|
|
||||||
blend(&this->g, color.g, color.a);
|
|
||||||
blend(&this->b, color.b, color.a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
static void blend(uint8_t *thisValue, const double thisFactor, const uint8_t otherValue, const double otherFactor) {
|
||||||
|
*thisValue = static_cast<uint8_t>(round(max(0.0, min(255.0, (*thisValue * thisFactor + otherValue * otherFactor) / 2.0))));
|
||||||
static void blend(uint8_t *target, const uint8_t color, const uint8_t alpha) {
|
|
||||||
const auto factor = alpha / 255.0;
|
|
||||||
const auto newValue = (*target * (1 - factor) + color * factor) / 2.0;
|
|
||||||
*target = static_cast<uint8_t>(round(max(0.0, min(255.0, newValue))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const RGBA Transparent;
|
extern const Color Transparent;
|
||||||
extern const RGBA Black;
|
extern const Color Black;
|
||||||
extern const RGBA Red;
|
extern const Color Red;
|
||||||
extern const RGBA Yellow;
|
extern const Color Yellow;
|
||||||
extern const RGBA Green;
|
extern const Color Green;
|
||||||
extern const RGBA Cyan;
|
extern const Color Cyan;
|
||||||
extern const RGBA Blue;
|
extern const Color Blue;
|
||||||
extern const RGBA Magenta;
|
extern const Color Magenta;
|
||||||
extern const RGBA Gray;
|
extern const Color Gray;
|
||||||
extern const RGBA White;
|
extern const Color White;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -5,35 +5,28 @@ template<int W, int H>
|
|||||||
void DisplayMatrix<W, H>::clear() {
|
void DisplayMatrix<W, H>::clear() {
|
||||||
const auto backup = foreground;
|
const auto backup = foreground;
|
||||||
foreground = Black;
|
foreground = Black;
|
||||||
fillRect();
|
this->fillRect();
|
||||||
foreground = backup;
|
foreground = backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
void DisplayMatrix<W, H>::fillRect(const int w, const int h) {
|
void DisplayMatrix<W, H>::fillRect(const int w, const int h) const {
|
||||||
if (w < 1 || h < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto y = cursorY; y < cursorY + h; ++y) {
|
for (auto y = cursorY; y < cursorY + h; ++y) {
|
||||||
for (auto x = cursorX; x < cursorX + w; ++x) {
|
for (auto x = cursorX; x < cursorX + w; ++x) {
|
||||||
matrix[y][x] = foreground;
|
matrix[y][x].blend(foreground, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
void DisplayMatrix<W, H>::drawLine(const int w, const int h, const int thickness) {
|
void DisplayMatrix<W, H>::drawLine(const int w, const int h, const int thickness) const {
|
||||||
if (w < 1 && h < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (w >= h) {
|
if (w >= h) {
|
||||||
const auto m = static_cast<double>(h) / w;
|
const auto m = static_cast<double>(h) / w;
|
||||||
for (auto t = 0; t < thickness; ++t) {
|
for (auto t = 0; t < thickness; ++t) {
|
||||||
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
||||||
for (auto x = 0; x < w; ++x) {
|
for (auto x = 0; x < w; ++x) {
|
||||||
const auto y = static_cast<int>(round(offset + x * m));
|
const auto y = static_cast<int>(round(offset + x * m));
|
||||||
matrix[cursorY + y][cursorX + x] = foreground;
|
matrix[cursorY + y][cursorX + x].blend(foreground, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -42,11 +35,10 @@ void DisplayMatrix<W, H>::drawLine(const int w, const int h, const int thickness
|
|||||||
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
||||||
for (auto y = 0; y < w; ++y) {
|
for (auto y = 0; y < w; ++y) {
|
||||||
const auto x = static_cast<int>(round(offset + y * m));
|
const auto x = static_cast<int>(round(offset + y * m));
|
||||||
matrix[cursorY + y][cursorX + x] = foreground;
|
matrix[cursorY + y][cursorX + x].blend(foreground, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -125,10 +125,10 @@ void DisplayMatrix<W, H>::print(bool **s, const size_t w, const size_t h) {
|
|||||||
for (auto x = 0; x < w; x++) {
|
for (auto x = 0; x < w; x++) {
|
||||||
const auto c = s[y][x];
|
const auto c = s[y][x];
|
||||||
if (c) {
|
if (c) {
|
||||||
matrix[y][x] = foreground;
|
matrix[y][x].blend(foreground, alpha);
|
||||||
xMax = x > xMax ? x : xMax;
|
xMax = x > xMax ? x : xMax;
|
||||||
} else {
|
} else {
|
||||||
matrix[y][x] = background;
|
matrix[y][x].blend(background, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,16 +136,16 @@ void DisplayMatrix<W, H>::print(bool **s, const size_t w, const size_t h) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
void DisplayMatrix<W, H>::print(SymbolRGBA8x8 s) {
|
void DisplayMatrix<W, H>::print(SymbolRGB8x8 s) {
|
||||||
print(reinterpret_cast<RGBA **>(s),countof(SymbolRGBA8x8),countof(SymbolRGBA8x8[0]));
|
print(reinterpret_cast<Color **>(s),countof(SymbolRGB8x8),countof(SymbolRGB8x8[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
void DisplayMatrix<W, H>::print(RGBA **s, const size_t w, const size_t h) {
|
void DisplayMatrix<W, H>::print(Color **s, const size_t w, const size_t h) {
|
||||||
for (auto y = 0; y < h; y++) {
|
for (auto y = 0; y < h; y++) {
|
||||||
for (auto x = 0; x < w; x++) {
|
for (auto x = 0; x < w; x++) {
|
||||||
const auto color = s[y][x];
|
const auto color = s[y][x];
|
||||||
matrix[y][x] = color;
|
matrix[y][x].blend(color, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cursorX += w;
|
cursorX += w;
|
||||||
|
|||||||
4
src/patrix/node/Node.cpp
Normal file
4
src/patrix/node/Node.cpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
// ReSharper disable once CppUseAuto
|
||||||
|
Node patrixNode = patrixGetNode();
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#ifndef PATRIX_NODE_H
|
#ifndef NODE_H
|
||||||
#define PATRIX_NODE_H
|
#define NODE_H
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
class PatrixNode {
|
class Node {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -13,14 +13,14 @@ public:
|
|||||||
|
|
||||||
const bool waitForClock;
|
const bool waitForClock;
|
||||||
|
|
||||||
explicit PatrixNode(const bool waitForWiFi, const bool waitForOTA, const bool waitForClock)
|
explicit Node(const bool waitForWiFi, const bool waitForOTA, const bool waitForClock)
|
||||||
: waitForWiFi(waitForWiFi),
|
: waitForWiFi(waitForWiFi),
|
||||||
waitForOTA(waitForOTA),
|
waitForOTA(waitForOTA),
|
||||||
waitForClock(waitForClock) {
|
waitForClock(waitForClock) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~PatrixNode() = default;
|
virtual ~Node() = default;
|
||||||
|
|
||||||
virtual void setup() {}
|
virtual void setup() {}
|
||||||
|
|
||||||
@ -30,8 +30,8 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PatrixNode patrixNode;
|
extern Node patrixNode;
|
||||||
|
|
||||||
PatrixNode& patrixGetNode();
|
Node& patrixGetNode();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#include <patrix/node/PatrixNode.h>
|
|
||||||
|
|
||||||
// ReSharper disable once CppUseAuto
|
|
||||||
PatrixNode patrixNode = patrixGetNode();
|
|
||||||
Loading…
Reference in New Issue
Block a user