figments: docs++

This commit is contained in:
Torrie Fischer 2023-03-03 19:43:51 +01:00
parent 3eb145ba00
commit e837232872
4 changed files with 167 additions and 1 deletions

View File

@ -3,13 +3,38 @@
#include "Geometry.h" #include "Geometry.h"
/**
* Generic interface for converting between virtual and physical coordinates
*/
struct CoordinateMapping { struct CoordinateMapping {
/**
* Maps physical coordinates to virtual coordinates.
* Virtual coordinates range from 0 to 255. Physical coordinates are
* hardware-dependent.
*/
virtual VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const = 0; virtual VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const = 0;
/**
* Maps virtual coordinates to physical (aka, hardware) coordinates.
* Virtual coordinates range from 0 to 255. Physical coordinates are
* hardware-dependent.
*/
virtual PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const = 0; virtual PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const = 0;
/**
* Returns the index of the underlying FastLED array, i.e., the physical
* hardware pixel.
*/
virtual int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const = 0; virtual int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const = 0;
virtual unsigned int physicalPixelCount() const = 0; virtual unsigned int physicalPixelCount() const = 0;
}; };
/**
* Basic mapping for a contiguous 1-d linear display, eg, an LED strip.
*
* X value ranges from 0 to 255; all Y values are flattened to Y=0
*/
struct LinearCoordinateMapping: CoordinateMapping { struct LinearCoordinateMapping: CoordinateMapping {
unsigned int pixelCount = 1; unsigned int pixelCount = 1;
unsigned int startPixel = 0; unsigned int startPixel = 0;
@ -32,22 +57,51 @@ struct LinearCoordinateMapping: CoordinateMapping {
} }
}; };
/**
* The connection between the rendering engine and the physical hardware.
* Provides direct access to the underlying FastLED array.
*/
class Display { class Display {
public: public:
Display(CRGB* pixels, int pixelCount, const CoordinateMapping* map) Display(CRGB* pixels, int pixelCount, const CoordinateMapping* map)
: m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {} : m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {}
/**
* Returns the physical pixel at the given physical coordinates
*/
CRGB& pixelAt(const PhysicalCoordinates coords); CRGB& pixelAt(const PhysicalCoordinates coords);
/**
* Returns the physical pixel at the given virtual coordinates
*/
CRGB& pixelAt(const VirtualCoordinates coords); CRGB& pixelAt(const VirtualCoordinates coords);
/**
* Returns the physical pixel in the underlying FastLED array
*/
CRGB& pixelAt(int idx); CRGB& pixelAt(int idx);
/**
* Returns how many pixels are in this display
*/
int pixelCount() const; int pixelCount() const;
/**
* Returns the raw underlying FastLED array
*/
CRGB* pixelBacking() const; CRGB* pixelBacking() const;
const CoordinateMapping* coordinateMapping() const; const CoordinateMapping* coordinateMapping() const;
/**
* Sets every pixel to (0, 0, 0)
*/
void clear(); void clear();
/**
* Sets every pixel to the given display
*/
void clear(const CRGB& color); void clear(const CRGB& color);
void clear(VirtualCoordinates& start, VirtualCoordinates& end, const CRGB& color);
private: private:
CRGB* m_pixels; CRGB* m_pixels;

View File

@ -10,13 +10,36 @@ class Display;
class InputEvent; class InputEvent;
class InputSource; class InputSource;
/**
* A generic interface for anything that can be executed and respond to events.
*/
struct Loopable { struct Loopable {
/**
* Called by the MainLoop to process events
*/
virtual void handleEvent(const InputEvent& event) {} virtual void handleEvent(const InputEvent& event) {}
/**
* Called on every MainLoop tick
*/
virtual void loop() = 0; virtual void loop() = 0;
}; };
/**
* A Loopable that can be named and may be started or stopped in a MainLoop.
*/
struct Task : public virtual Loopable { struct Task : public virtual Loopable {
/**
* Implement in a subclass to run when the task is started
* The default implementation does nothing.
*/
virtual void onStart() {}; virtual void onStart() {};
/**
* Implement in a subclass to run when the task is stopped.
* The default implementation does nothing.
*/
virtual void onStop() {}; virtual void onStop() {};
enum State { enum State {
@ -27,27 +50,52 @@ struct Task : public virtual Loopable {
Task() {} Task() {}
explicit Task(const char* name) : name(name) {} explicit Task(const char* name) : name(name) {}
/**
* Starts the task and makes it schedulable
*/
void start() { state = Running; onStart(); } void start() { state = Running; onStart(); }
/**
* Stops the task and makes it unschedulable
*/
void stop() { onStop(); state = Stopped; } void stop() { onStop(); state = Stopped; }
/**
* A hacky way to determine if a task is a Figment subclass or not, without
* having to resort to RTTI
*/
virtual bool isFigment() const { return false; } virtual bool isFigment() const { return false; }
const char* name = ""; const char* name = "";
State state = Stopped; State state = Stopped;
}; };
/**
* Functional lambda interface for creating Tasks
*/
struct TaskFunc: public Task { struct TaskFunc: public Task {
TaskFunc(std::function<void()> func) : Task("lambda"), func(func) {} TaskFunc(std::function<void()> func) : Task("lambda"), func(func) {}
void loop() override {func();} void loop() override {func();}
std::function<void()> func; std::function<void()> func;
}; };
/**
* A Task with a graphical output
*/
struct Figment: public Task { struct Figment: public Task {
Figment() : Task() {} Figment() : Task() {}
explicit Figment(const char* name) : Task(name) {} explicit Figment(const char* name) : Task(name) {}
/**
* Called when the Figment should render its output to a display
*/
virtual void render(Display* dpy) const = 0; virtual void render(Display* dpy) const = 0;
bool isFigment() const override { return true; } bool isFigment() const override { return true; }
}; };
/**
* Functional lambda interface for creating Figments
*/
struct FigmentFunc: public Figment { struct FigmentFunc: public Figment {
FigmentFunc(std::function<void(Display*)> func) : Figment("lambda"), func(func) {} FigmentFunc(std::function<void(Display*)> func) : Figment("lambda"), func(func) {}
void loop() override {} void loop() override {}

View File

@ -1,20 +1,34 @@
#pragma once #pragma once
#include <array> #include <array>
/**
* A simple ring buffer structure
*/
template<typename T, int Size> template<typename T, int Size>
struct Ringbuf { struct Ringbuf {
Ringbuf() : m_head(0), m_tail(0) {} Ringbuf() : m_head(0), m_tail(0) {}
/**
* Clears the buffer's contents
*/
void clear() { void clear() {
m_head = 0; m_head = 0;
m_tail = 0; m_tail = 0;
} }
/**
* Returns the value of the next available item, if available
*/
T peek(int offset) const { T peek(int offset) const {
const int nextHead = (m_head + offset) % Size; const int nextHead = (m_head + offset) % Size;
return m_items[nextHead]; return m_items[nextHead];
} }
/**
* Removes and returns the next available item, if any.
*
* @return false if no data is available, true otherwise
*/
bool take(T& dest) { bool take(T& dest) {
if (m_head == m_tail) { if (m_head == m_tail) {
return false; return false;
@ -26,6 +40,10 @@ struct Ringbuf {
return true; return true;
} }
/**
* Inserts an item into the buffer. If the buffer is full, the oldest item
* is overwritten.
*/
void insert(const T& src) { void insert(const T& src) {
const int cur = m_tail; const int cur = m_tail;
const int nextTail = (m_tail + 1) % Size; const int nextTail = (m_tail + 1) % Size;
@ -37,6 +55,9 @@ struct Ringbuf {
m_items[cur] = src; m_items[cur] = src;
} }
/**
* Consumes the entire buffer and writes it to the given array.
*/
size_t write(T(&dest)[Size]) { size_t write(T(&dest)[Size]) {
int i = 0; int i = 0;
size_t ret = 0; size_t ret = 0;
@ -47,6 +68,9 @@ struct Ringbuf {
return ret; return ret;
} }
/**
* Consumes the entire buffer and writes it to the given output
*/
size_t write(Print& stream) { size_t write(Print& stream) {
T val; T val;
size_t ret = 0; size_t ret = 0;
@ -56,6 +80,9 @@ struct Ringbuf {
return ret; return ret;
} }
/**
* Returns how many items are available in the buffer
*/
size_t size() { size_t size() {
if (m_tail > m_head) { if (m_tail > m_head) {
return m_tail - m_head; return m_tail - m_head;

View File

@ -6,13 +6,34 @@
class Display; class Display;
/**
* A high performance and hardware-independent rendering surface which
* represents a full 2d display ranging from (0,0) to (255, 255), that may be
* rotated.
*
* Simple brushes and complex shaders may be applied to a Surface using
* @paintWith and @paintShader. The Surface will only execute each shader or
* brush once for each physical pixel in the display, allowing you to design
* display-independent graphics.
*/
class Surface { class Surface {
public: public:
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end); Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end);
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end, uint8_t rotation); Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end, uint8_t rotation);
/**
* Sets the entire surface to one solid color
*/
Surface& operator=(const CRGB& color); Surface& operator=(const CRGB& color);
/**
* Adds the given color to every pixel on the surface
*/
Surface& operator+=(const CRGB& color); Surface& operator+=(const CRGB& color);
/**
* OR operation that applies the given color to every pixel on the surface
*/
template<typename T> template<typename T>
Surface& operator|=(const T& val) { Surface& operator|=(const T& val) {
paintWith([&](CRGB& pixel) { paintWith([&](CRGB& pixel) {
@ -21,10 +42,26 @@ public:
return *this; return *this;
} }
/**
* A lambda function that maps coordinates to colors.
*/
using Shader = std::function<void(CRGB&, const VirtualCoordinates& virtPos, const PhysicalCoordinates& realPos, const VirtualCoordinates& surfacePos)>; using Shader = std::function<void(CRGB&, const VirtualCoordinates& virtPos, const PhysicalCoordinates& realPos, const VirtualCoordinates& surfacePos)>;
/**
* A simple lambda that is called for every pixel in a surface. Commonly
* used for solid fills; if you want to map pixel coordinates to colors or
* textures, you probably want to use a Shader instead.
*/
using BrushFunc = std::function<void(CRGB&)>; using BrushFunc = std::function<void(CRGB&)>;
/**
* Applies the given BrushFunc to every physical pixel in the surface
*/
void paintWith(BrushFunc func); void paintWith(BrushFunc func);
/**
* Applies the given Shader to every physical pixel in the surface
*/
void paintShader(Shader shader); void paintShader(Shader shader);
const PhysicalCoordinates start; const PhysicalCoordinates start;