From e8372328725b9800d697a6eef5a70d0207236f3c Mon Sep 17 00:00:00 2001 From: Torrie Fischer Date: Fri, 3 Mar 2023 19:43:51 +0100 Subject: [PATCH] figments: docs++ --- lib/Figments/Display.h | 56 +++++++++++++++++++++++++++++++++++++++++- lib/Figments/Figment.h | 48 ++++++++++++++++++++++++++++++++++++ lib/Figments/Ringbuf.h | 27 ++++++++++++++++++++ lib/Figments/Surface.h | 37 ++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 1 deletion(-) diff --git a/lib/Figments/Display.h b/lib/Figments/Display.h index c96a201..475be12 100644 --- a/lib/Figments/Display.h +++ b/lib/Figments/Display.h @@ -3,13 +3,38 @@ #include "Geometry.h" +/** + * Generic interface for converting between virtual and physical coordinates + */ 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; + + /** + * 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; + + /** + * Returns the index of the underlying FastLED array, i.e., the physical + * hardware pixel. + */ virtual int physicalCoordsToIndex(const PhysicalCoordinates localCoords) 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 { unsigned int pixelCount = 1; 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 { public: Display(CRGB* pixels, int pixelCount, const CoordinateMapping* map) : m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {} + /** + * Returns the physical pixel at the given physical coordinates + */ CRGB& pixelAt(const PhysicalCoordinates coords); + + /** + * Returns the physical pixel at the given virtual coordinates + */ CRGB& pixelAt(const VirtualCoordinates coords); + + /** + * Returns the physical pixel in the underlying FastLED array + */ CRGB& pixelAt(int idx); + /** + * Returns how many pixels are in this display + */ int pixelCount() const; + + /** + * Returns the raw underlying FastLED array + */ CRGB* pixelBacking() const; const CoordinateMapping* coordinateMapping() const; + + /** + * Sets every pixel to (0, 0, 0) + */ void clear(); + + /** + * Sets every pixel to the given display + */ void clear(const CRGB& color); - void clear(VirtualCoordinates& start, VirtualCoordinates& end, const CRGB& color); private: CRGB* m_pixels; diff --git a/lib/Figments/Figment.h b/lib/Figments/Figment.h index 3f237dd..87d7af7 100644 --- a/lib/Figments/Figment.h +++ b/lib/Figments/Figment.h @@ -10,13 +10,36 @@ class Display; class InputEvent; class InputSource; +/** + * A generic interface for anything that can be executed and respond to events. + */ struct Loopable { + + /** + * Called by the MainLoop to process events + */ virtual void handleEvent(const InputEvent& event) {} + + /** + * Called on every MainLoop tick + */ virtual void loop() = 0; }; +/** + * A Loopable that can be named and may be started or stopped in a MainLoop. + */ struct Task : public virtual Loopable { + /** + * Implement in a subclass to run when the task is started + * The default implementation does nothing. + */ virtual void onStart() {}; + + /** + * Implement in a subclass to run when the task is stopped. + * The default implementation does nothing. + */ virtual void onStop() {}; enum State { @@ -27,27 +50,52 @@ struct Task : public virtual Loopable { Task() {} explicit Task(const char* name) : name(name) {} + /** + * Starts the task and makes it schedulable + */ void start() { state = Running; onStart(); } + + /** + * Stops the task and makes it unschedulable + */ 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; } const char* name = ""; State state = Stopped; }; +/** + * Functional lambda interface for creating Tasks + */ struct TaskFunc: public Task { TaskFunc(std::function func) : Task("lambda"), func(func) {} void loop() override {func();} std::function func; }; +/** + * A Task with a graphical output + */ struct Figment: public Task { Figment() : Task() {} 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; bool isFigment() const override { return true; } }; +/** + * Functional lambda interface for creating Figments + */ struct FigmentFunc: public Figment { FigmentFunc(std::function func) : Figment("lambda"), func(func) {} void loop() override {} diff --git a/lib/Figments/Ringbuf.h b/lib/Figments/Ringbuf.h index f3f3e07..955c505 100644 --- a/lib/Figments/Ringbuf.h +++ b/lib/Figments/Ringbuf.h @@ -1,20 +1,34 @@ #pragma once #include +/** + * A simple ring buffer structure + */ template struct Ringbuf { Ringbuf() : m_head(0), m_tail(0) {} + /** + * Clears the buffer's contents + */ void clear() { m_head = 0; m_tail = 0; } + /** + * Returns the value of the next available item, if available + */ T peek(int offset) const { const int nextHead = (m_head + offset) % Size; 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) { if (m_head == m_tail) { return false; @@ -26,6 +40,10 @@ struct Ringbuf { return true; } + /** + * Inserts an item into the buffer. If the buffer is full, the oldest item + * is overwritten. + */ void insert(const T& src) { const int cur = m_tail; const int nextTail = (m_tail + 1) % Size; @@ -37,6 +55,9 @@ struct Ringbuf { m_items[cur] = src; } + /** + * Consumes the entire buffer and writes it to the given array. + */ size_t write(T(&dest)[Size]) { int i = 0; size_t ret = 0; @@ -47,6 +68,9 @@ struct Ringbuf { return ret; } + /** + * Consumes the entire buffer and writes it to the given output + */ size_t write(Print& stream) { T val; size_t ret = 0; @@ -56,6 +80,9 @@ struct Ringbuf { return ret; } + /** + * Returns how many items are available in the buffer + */ size_t size() { if (m_tail > m_head) { return m_tail - m_head; diff --git a/lib/Figments/Surface.h b/lib/Figments/Surface.h index 28400c5..62351d7 100644 --- a/lib/Figments/Surface.h +++ b/lib/Figments/Surface.h @@ -6,13 +6,34 @@ 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 { public: Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end); 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); + + /** + * Adds the given color to every pixel on the surface + */ Surface& operator+=(const CRGB& color); + + /** + * OR operation that applies the given color to every pixel on the surface + */ template Surface& operator|=(const T& val) { paintWith([&](CRGB& pixel) { @@ -21,10 +42,26 @@ public: return *this; } + /** + * A lambda function that maps coordinates to colors. + */ using Shader = std::function; + + /** + * 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; + /** + * Applies the given BrushFunc to every physical pixel in the surface + */ void paintWith(BrushFunc func); + + /** + * Applies the given Shader to every physical pixel in the surface + */ void paintShader(Shader shader); const PhysicalCoordinates start;