If the code hasn't been touched in this long, its probably release-worthy.
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <FastLED.h>
|
||||
#include "./Figment.h"
|
||||
#include <vector>
|
||||
|
||||
class Display;
|
||||
|
||||
@ -24,6 +23,18 @@ struct AnimatedNumber {
|
||||
}
|
||||
}
|
||||
|
||||
void update(uint8_t speed) {
|
||||
if (255 - speed >= m_idx) {
|
||||
m_idx += speed;
|
||||
} else {
|
||||
m_idx = 255;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFinished() const {
|
||||
return m_idx == 255;
|
||||
}
|
||||
|
||||
AnimatedNumber() {}
|
||||
AnimatedNumber(uint8_t v) : m_end(v) {}
|
||||
|
||||
|
@ -15,11 +15,11 @@ struct LinearCoordinateMapping: CoordinateMapping {
|
||||
unsigned int startPixel = 0;
|
||||
LinearCoordinateMapping() {}
|
||||
LinearCoordinateMapping(unsigned int count, unsigned int start) : pixelCount(count), startPixel(start) {}
|
||||
VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const {
|
||||
return VirtualCoordinates{(uint8_t)((localCoords.x) / pixelCount), 0};
|
||||
VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const override {
|
||||
return VirtualCoordinates{map8(localCoords.x, 0, pixelCount), 0};
|
||||
}
|
||||
|
||||
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const {
|
||||
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const override {
|
||||
return PhysicalCoordinates{scale8(pixelCount, virtualCoords.x), 0};
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ struct LinearCoordinateMapping: CoordinateMapping {
|
||||
return localCoords.x + startPixel;
|
||||
}
|
||||
|
||||
unsigned int physicalPixelCount() const {
|
||||
unsigned int physicalPixelCount() const override {
|
||||
return pixelCount;
|
||||
}
|
||||
};
|
||||
|
@ -22,15 +22,15 @@ struct Task : public virtual Loopable {
|
||||
};
|
||||
|
||||
Task() {}
|
||||
Task(State initialState) : Task(0, initialState) {}
|
||||
Task(const char* name) : Task(name, Running) {}
|
||||
explicit Task(State initialState) : Task(0, initialState) {}
|
||||
explicit Task(const char* name) : Task(name, Running) {}
|
||||
Task(const char* name, State initialState) : name(name), state(initialState) {}
|
||||
|
||||
void start() { state = Running; onStart(); }
|
||||
void stop() { onStop(); state = Stopped; }
|
||||
virtual bool isFigment() const { return false; }
|
||||
|
||||
const char* name = 0;
|
||||
const char* name = "";
|
||||
State state = Running;
|
||||
};
|
||||
|
||||
@ -42,8 +42,8 @@ struct TaskFunc: public Task {
|
||||
|
||||
struct Figment: public Task {
|
||||
Figment() : Task() {}
|
||||
Figment(State initialState) : Task(initialState) {}
|
||||
Figment(const char* name) : Task(name) {}
|
||||
explicit Figment(State initialState) : Task(initialState) {}
|
||||
explicit Figment(const char* name) : Task(name) {}
|
||||
Figment(const char* name, State initialState) : Task(name, initialState) {}
|
||||
virtual void render(Display* dpy) const = 0;
|
||||
bool isFigment() const override { return true; }
|
||||
|
@ -8,8 +8,8 @@ template<typename T> struct Coordinates {
|
||||
T y;
|
||||
};
|
||||
|
||||
struct VirtualCoordinates: Coordinates<uint16_t> {
|
||||
VirtualCoordinates(uint16_t _x, uint16_t _y) : Coordinates(_x, _y) {}
|
||||
struct VirtualCoordinates: Coordinates<uint8_t> {
|
||||
VirtualCoordinates(uint8_t _x, uint8_t _y) : Coordinates(_x, _y) {}
|
||||
};
|
||||
|
||||
struct PhysicalCoordinates: Coordinates<uint16_t> {
|
||||
|
@ -20,10 +20,63 @@ Variant::asInt() const
|
||||
return m_value.asInt;
|
||||
}
|
||||
|
||||
bool
|
||||
Variant::asBool() const
|
||||
{
|
||||
return (bool)m_value.asInt;
|
||||
}
|
||||
|
||||
void
|
||||
InputSource::init()
|
||||
{
|
||||
#ifdef CONFIG_THREADED_INPUTS
|
||||
m_queue = xQueueCreate(32, sizeof(InputEvent));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_THREADED_INPUTS
|
||||
void
|
||||
InputSource::readThread(void* data)
|
||||
{
|
||||
InputSource* self = static_cast<InputSource*>(data);
|
||||
while(true) {
|
||||
InputEvent evt = self->read();
|
||||
if (evt.intent != InputEvent::None) {
|
||||
xQueueSend(m_queue, &evt, 0)
|
||||
}
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
InputSource::onStart()
|
||||
{
|
||||
#ifdef CONFIG_THREADED_INPUTS
|
||||
m_threadLoop = MainLoop::instance();
|
||||
xTaskCreate(
|
||||
&InputSource::readThread,
|
||||
name,
|
||||
1000,
|
||||
this,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
InputSource::loop()
|
||||
{
|
||||
#ifndef CONFIG_THREADED_INPUTS
|
||||
MainLoop::instance()->dispatch(read());
|
||||
#else
|
||||
InputEvent evt;
|
||||
xQueueReceive(m_queue, &evt, 0);
|
||||
if (evt.intent != InputEvent::None) {
|
||||
MainLoop::instance()->dispatch(evt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
InputEvent
|
||||
|
@ -32,6 +32,7 @@ struct Variant {
|
||||
const char* asString() const;
|
||||
CRGB asRGB() const;
|
||||
int asInt() const;
|
||||
bool asBool() const;
|
||||
|
||||
private:
|
||||
union {
|
||||
@ -74,6 +75,8 @@ struct InputEvent: public Variant {
|
||||
|
||||
// Timekeeping
|
||||
ScheduleChange,
|
||||
Beat,
|
||||
BeatDetect,
|
||||
|
||||
// Task management
|
||||
StartThing,
|
||||
@ -87,6 +90,8 @@ struct InputEvent: public Variant {
|
||||
|
||||
// Firmware events
|
||||
FirmwareUpdate,
|
||||
|
||||
ReadyToRoll,
|
||||
};
|
||||
|
||||
template<typename Value>
|
||||
@ -102,14 +107,26 @@ struct InputEvent: public Variant {
|
||||
Intent intent;
|
||||
};
|
||||
|
||||
struct MainLoop;
|
||||
|
||||
class InputSource: public Task {
|
||||
public:
|
||||
InputSource() : Task() {}
|
||||
InputSource(const char* name) : Task(name) {}
|
||||
InputSource(Task::State initialState) : Task(initialState) {}
|
||||
InputSource(const char* name, Task::State initialState) : Task(name, initialState) {}
|
||||
InputSource() : Task() {init();}
|
||||
explicit InputSource(const char* name) : Task(name) {init();}
|
||||
explicit InputSource(Task::State initialState) : Task(initialState) {init();}
|
||||
InputSource(const char* name, Task::State initialState) : Task(name, initialState) {init();}
|
||||
void loop() override;
|
||||
void onStart() override;
|
||||
virtual InputEvent read() = 0;
|
||||
private:
|
||||
void init();
|
||||
#ifdef CONFIG_THREADED_INPUTS
|
||||
static void readThread(void* data);
|
||||
MainLoop* m_threadLoop;
|
||||
static uint8_t m_threadBuf[sizeof(InputEvent) * 32];
|
||||
QueueHandle_t m_queue;
|
||||
StaticQueue_t m_threadQueue;
|
||||
#endif
|
||||
};
|
||||
|
||||
class InputFunc : public InputSource {
|
||||
@ -128,7 +145,6 @@ private:
|
||||
|
||||
class BufferedInputSource: public InputSource {
|
||||
public:
|
||||
BufferedInputSource() : InputSource() {}
|
||||
BufferedInputSource(const char* name) : InputSource(name) {}
|
||||
InputEvent read() override;
|
||||
|
||||
|
@ -16,6 +16,7 @@ MainLoop::dispatch(const InputEvent& evt)
|
||||
void
|
||||
MainLoop::loop()
|
||||
{
|
||||
s_instance = this;
|
||||
InputEvent evt;
|
||||
while (m_eventBuf.take(evt)) {
|
||||
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
|
||||
@ -23,10 +24,10 @@ MainLoop::loop()
|
||||
for(auto figmentJob: scheduler.tasks) {
|
||||
if (!strcmp(figmentJob->name, evt.asString())) {
|
||||
if (jobState) {
|
||||
//Log.notice("Starting %s", figmentJob->name);
|
||||
Log.trace("Starting task %s", figmentJob->name);
|
||||
figmentJob->start();
|
||||
} else {
|
||||
//Log.notice("Stopping %s", figmentJob->name);
|
||||
Log.trace("Stopping task %s", figmentJob->name);
|
||||
figmentJob->stop();
|
||||
}
|
||||
}
|
||||
@ -34,6 +35,9 @@ MainLoop::loop()
|
||||
}
|
||||
|
||||
for(Task* task : scheduler) {
|
||||
if (evt.intent == InputEvent::SetPower) {
|
||||
Log.notice("Event %s", task->name);
|
||||
}
|
||||
task->handleEvent(evt);
|
||||
}
|
||||
}
|
||||
@ -44,10 +48,18 @@ MainLoop::loop()
|
||||
Task* slowestTask = NULL;
|
||||
for(Task* task : scheduler) {
|
||||
//unsigned int start = millis();
|
||||
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
|
||||
unsigned int start = ESP.getCycleCount();
|
||||
#else
|
||||
unsigned int start = millis();
|
||||
#endif
|
||||
Log.verbose("Running %s", task->name);
|
||||
task->loop();
|
||||
//unsigned int runtime = millis() - start;
|
||||
unsigned int runtime = ESP.getCycleCount() - start;
|
||||
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
|
||||
unsigned int runtime = (ESP.getCycleCount() - start) / 160000;
|
||||
#else
|
||||
unsigned int runtime = millis() - start;
|
||||
#endif
|
||||
frameSpeed += runtime;
|
||||
taskCount++;
|
||||
if (runtime > slowest) {
|
||||
@ -57,19 +69,22 @@ MainLoop::loop()
|
||||
}
|
||||
frameSpeed = millis() - frameStart;
|
||||
if (frameSpeed >= 23) {
|
||||
Log.notice("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestTask->name, slowest/160000);
|
||||
const char* slowestName = (slowestTask->name ? slowestTask->name : "(Unnamed)");
|
||||
Log.warning("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestTask->name, slowest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainLoop::start()
|
||||
{
|
||||
s_instance = this;
|
||||
Log.notice("*** Starting %d tasks...", scheduler.tasks.size());
|
||||
Serial.flush();
|
||||
for(auto task: scheduler) {
|
||||
Log.notice("** Starting %s", task->name);
|
||||
task->start();
|
||||
}
|
||||
dispatch(InputEvent::ReadyToRoll);
|
||||
}
|
||||
|
||||
MainLoop* MainLoop::s_instance;
|
||||
|
@ -55,7 +55,7 @@ struct MainLoop {
|
||||
Scheduler scheduler;
|
||||
|
||||
MainLoop(std::vector<Task*> &&tasks)
|
||||
: scheduler(std::move(tasks)) {s_instance = this;}
|
||||
: scheduler(std::move(tasks)) {}
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
@ -63,7 +63,7 @@ struct MainLoop {
|
||||
static MainLoop* instance() { return s_instance; }
|
||||
|
||||
private:
|
||||
Ringbuf<InputEvent, 10> m_eventBuf;
|
||||
Ringbuf<InputEvent, 32> m_eventBuf;
|
||||
|
||||
static MainLoop* s_instance;
|
||||
};
|
||||
|
12
lib/Figments/Perfcounter.h
Normal file
12
lib/Figments/Perfcounter.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoLog.h>
|
||||
|
||||
struct PerfCounter {
|
||||
PerfCounter(const char* name) {}
|
||||
~PerfCounter() {}
|
||||
/*PerfCounter(const char* name) : start(millis()), name(name) {}
|
||||
~PerfCounter() {Log.notice("%s: %d", name, millis() - start);}*/
|
||||
uint16_t start;
|
||||
const char* name;
|
||||
};
|
@ -9,11 +9,18 @@ Renderer::loop()
|
||||
for(Display* dpy : m_displays) {
|
||||
for(Figment* figment : m_figments) {
|
||||
if (figment->state == Task::Running) {
|
||||
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
|
||||
unsigned int frameStart = ESP.getCycleCount();
|
||||
#endif
|
||||
Log.verbose("Render %s", figment->name);
|
||||
figment->render(dpy);
|
||||
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
|
||||
unsigned int runtime = (ESP.getCycleCount() - frameStart) / 160000;
|
||||
#else
|
||||
unsigned int runtime = 0;
|
||||
#endif
|
||||
if (runtime >= 8) {
|
||||
Log.notice("SLOW RENDER: %s took %dms!", figment->name, runtime);
|
||||
Log.warning("SLOW RENDER: %s took %dms!", figment->name, runtime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ class Display;
|
||||
|
||||
struct Renderer: public Task {
|
||||
public:
|
||||
Renderer(std::vector<Display*> displays, const std::vector<Figment*> &figments) : Task("Renderer"), m_figments(figments), m_displays(displays) {}
|
||||
Renderer(std::vector<Display*>&& displays, const std::vector<Figment*> &figments) : Task("Renderer"), m_figments(figments), m_displays(std::move(displays)) {}
|
||||
|
||||
void loop() override;
|
||||
void onStart() override;
|
||||
|
@ -10,6 +10,11 @@ struct Ringbuf {
|
||||
m_tail = 0;
|
||||
}
|
||||
|
||||
T peek(int offset) const {
|
||||
const int nextHead = (m_head + offset) % Size;
|
||||
return m_items[nextHead];
|
||||
}
|
||||
|
||||
bool take(T& dest) {
|
||||
if (m_head == m_tail) {
|
||||
return false;
|
||||
@ -41,6 +46,22 @@ struct Ringbuf {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t write(Print& stream) {
|
||||
T val;
|
||||
size_t ret = 0;
|
||||
while(take(val)) {
|
||||
stream.write(val);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
if (m_tail > m_head) {
|
||||
return m_tail - m_head;
|
||||
}
|
||||
return m_tail + (Size - m_head);
|
||||
}
|
||||
private:
|
||||
int m_head = 0;
|
||||
int m_tail = 0;
|
||||
|
@ -1,12 +1,29 @@
|
||||
#include "./Surface.h"
|
||||
#include "./Display.h"
|
||||
#include <ArduinoLog.h>
|
||||
#include "Perfcounter.h"
|
||||
|
||||
Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end)
|
||||
: start(dpy->coordinateMapping()->virtualToPhysicalCoords(start)),
|
||||
end(dpy->coordinateMapping()->virtualToPhysicalCoords(end)),
|
||||
virtStart(start),
|
||||
virtEnd(end),
|
||||
m_display(dpy)
|
||||
{
|
||||
//assert(start.x <= end.x);
|
||||
//assert(start.y <= end.y);
|
||||
}
|
||||
|
||||
Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end, uint8_t rotation)
|
||||
: start(dpy->coordinateMapping()->virtualToPhysicalCoords(start)),
|
||||
end(dpy->coordinateMapping()->virtualToPhysicalCoords(end)),
|
||||
virtStart(start),
|
||||
virtEnd(end),
|
||||
m_display(dpy),
|
||||
m_rotation(rotation)
|
||||
{
|
||||
//assert(start.x <= end.x);
|
||||
//assert(start.y <= end.y);
|
||||
}
|
||||
|
||||
Surface&
|
||||
@ -30,11 +47,27 @@ Surface::operator+=(const CRGB& color)
|
||||
void
|
||||
Surface::paintWith(std::function<void(CRGB&)> func)
|
||||
{
|
||||
//Log.verbose("Painting startx=%d endx=%d starty=%d endy=%d", start.x, end.x, start.y, end.y);
|
||||
for(auto x = start.x; x <= end.x; x++) {
|
||||
for(auto y = start.y; y <= end.y; y++) {
|
||||
//Log.verbose("x=%d y=%d", x, y);
|
||||
func(m_display->pixelAt(PhysicalCoordinates{x, y}));
|
||||
paintShader([=](CRGB& pixel, const VirtualCoordinates&, const PhysicalCoordinates&, const VirtualCoordinates&){ func(pixel); });
|
||||
}
|
||||
|
||||
void
|
||||
Surface::paintShader(Surface::Shader shader)
|
||||
{
|
||||
PerfCounter _("paintShader");
|
||||
const uint16_t width = end.x - start.x + 1;
|
||||
const uint16_t height = end.y - start.y + 1;
|
||||
const uint8_t xMod = 255 / width;
|
||||
const uint8_t yMod = 255 / height;
|
||||
for(auto x = 0; x < width; x++) {
|
||||
for(auto y = 0; y < height; y++) {
|
||||
PhysicalCoordinates coords{x + start.x, y + start.y};
|
||||
VirtualCoordinates virtCoords{m_display->coordinateMapping()->physicalToVirtualCoords(coords)};
|
||||
VirtualCoordinates surfaceCoords{xMod * x, yMod * y};
|
||||
//Log.notice("width=%d height=%d vx=%d vy=%d sx=%d sy=%d x=%d y=%d px=%d py=%d", width, height, start.x, start.y, x, y, coords.x, coords.y);
|
||||
// 256 = 1.0
|
||||
// 128 = 0.0
|
||||
// 0 = 1.0
|
||||
shader(m_display->pixelAt(coords), virtCoords, coords, surfaceCoords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <FastLED.h>
|
||||
#include "./Geometry.h"
|
||||
#include <functional>
|
||||
@ -7,15 +9,30 @@ class Display;
|
||||
class Surface {
|
||||
public:
|
||||
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end);
|
||||
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end, uint8_t rotation);
|
||||
|
||||
Surface& operator=(const CRGB& color);
|
||||
Surface& operator+=(const CRGB& color);
|
||||
template<typename T>
|
||||
Surface& operator|=(const T& val) {
|
||||
paintWith([&](CRGB& pixel) {
|
||||
pixel |= val;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void paintWith(std::function<void(CRGB&)> func);
|
||||
using Shader = std::function<void(CRGB&, const VirtualCoordinates& virtPos, const PhysicalCoordinates& realPos, const VirtualCoordinates& surfacePos)>;
|
||||
using BrushFunc = std::function<void(CRGB&)>;
|
||||
|
||||
void paintWith(BrushFunc func);
|
||||
void paintShader(Shader shader);
|
||||
|
||||
const PhysicalCoordinates start;
|
||||
const PhysicalCoordinates end;
|
||||
const VirtualCoordinates virtStart;
|
||||
const VirtualCoordinates virtEnd;
|
||||
|
||||
private:
|
||||
Display* m_display;
|
||||
uint8_t m_rotation = 0;
|
||||
};
|
||||
|
8
lib/Figments/library.json
Normal file
8
lib/Figments/library.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Figments",
|
||||
"version": "0.3.0",
|
||||
"description": "An embedded graphics rendering engine",
|
||||
"keywords": ["FastLED", "esp32", "esp8266"],
|
||||
"frameworks": ["arduino"],
|
||||
"platforms": ["espressif32", "espressif8266"]
|
||||
}
|
Reference in New Issue
Block a user