Initial commit

This commit is contained in:
2019-05-09 22:17:29 -07:00
parent dcf8ebd034
commit 354b72f160
95 changed files with 3174 additions and 6 deletions

View File

@ -0,0 +1,10 @@
#include "./Animation.h"
#include "./Input.h"
#include "./Display.h"
#include <algorithm>
uint8_t
AnimatedNumber::value() const
{
return NSFastLED::lerp8by8(m_start, m_end, m_idx);
}

View File

@ -0,0 +1,147 @@
#pragma once
#include "FastLED/FastLED.h"
#include "./Figment.h"
#include <vector>
class Display;
struct AnimatedNumber {
void set(uint8_t v) {
set(value(), v);
}
void set(uint8_t start, uint8_t end) {
m_start = start;
m_end = end;
m_idx = 0;
}
uint8_t value() const;
void update() {
if (m_idx < 255) {
m_idx += 1;
}
}
AnimatedNumber() {}
AnimatedNumber(uint8_t v) : m_end(v) {}
AnimatedNumber& operator=(uint8_t v) {
set(v);
return *this;
}
AnimatedNumber& operator+=(uint8_t v) {
set(value()+v);
return *this;
}
operator uint8_t() const {
return value();
}
operator int() const {
return value();
}
operator unsigned int() const {
return value();
}
bool operator==(int other) const {
return value() == other;
}
uint8_t operator+(uint8_t other) const {
return value() + other;
}
int operator+(int other) const {
return value() + other;
}
private:
uint8_t m_idx = 255;
uint8_t m_start = 0;
uint8_t m_end = 0;
};
struct AnimatedRGB {
NSFastLED::CRGB start;
NSFastLED::CRGB end;
AnimatedNumber pos;
AnimatedRGB(const NSFastLED::CRGB& color)
: start(color), end(color) {}
AnimatedRGB() {}
AnimatedRGB& operator=(const NSFastLED::CRGB& rgb) {
start = *this;
end = rgb;
pos.set(0, 255);
return *this;
}
void update() {
pos.update();
}
operator NSFastLED::CRGB() const {
uint8_t red = NSFastLED::lerp8by8(start.red, end.red, pos);
uint8_t green = NSFastLED::lerp8by8(start.green, end.green, pos);
uint8_t blue = NSFastLED::lerp8by8(start.blue, end.blue, pos);
return NSFastLED::CRGB(red, green, blue);
}
};
template<typename T, int Size>
struct SpriteList {
void update() {
if (!m_enabled) return;
for(int i = 0; i < size; i++) {
animations[i].update();
}
}
void render(Display* dpy) const {
if (!m_enabled) return;
for(int i = 0; i < size; i++) {
animations[i].render(dpy);
}
}
void forEach(std::function<void(T&)> func) {
for(int i = 0; i < size; i++) {
func(animations[i]);
}
}
void forEach(std::function<void(const T&)> func) const {
for(int i = 0; i < size; i++) {
func(animations[i]);
}
}
void disable() {
m_enabled = false;
}
void enable() {
m_enabled = true;
}
void toggle() {
m_enabled = !m_enabled;
}
T& operator[](int idx) {
return animations[idx];
}
T animations[Size];
bool m_enabled = true;
static constexpr int size = Size;
using Type = T;
};

View File

@ -0,0 +1,59 @@
#include "Display.h"
#include <math.h>
using namespace NSFastLED;
int
Display::pixelCount() const
{
return m_pixelCount;
}
CRGB*
Display::pixelBacking() const
{
return m_pixels;
}
void
Display::clear()
{
clear(CRGB(0, 0, 0));
}
void
Display::clear(const CRGB& color)
{
for(int i = 0; i < m_pixelCount;i++) {
m_pixels[i] = color;
}
}
CRGB&
Display::pixelAt(const PhysicalCoordinates coords)
{
return pixelAt(m_coordMap->physicalCoordsToIndex(coords));
}
CRGB&
Display::pixelAt(const VirtualCoordinates coords)
{
return pixelAt(m_coordMap->virtualToPhysicalCoords(coords));
}
CRGB&
Display::pixelAt(int idx)
{
const int kx = idx % pixelCount();
if (kx < 0) {
return m_pixels[pixelCount() + 1 + kx];
} else {
return m_pixels[kx];
}
}
const CoordinateMapping*
Display::coordinateMapping() const
{
return m_coordMap;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "FastLED/FastLED.h"
#include "Geometry.h"
struct CoordinateMapping {
virtual VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const = 0;
virtual PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const = 0;
virtual int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const = 0;
virtual unsigned int physicalPixelCount() const = 0;
};
struct LinearCoordinateMapping: CoordinateMapping {
unsigned int pixelCount = 1;
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};
}
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const {
return PhysicalCoordinates{NSFastLED::scale8(pixelCount, virtualCoords.x), 0};
}
int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const override {
return localCoords.x + startPixel;
}
unsigned int physicalPixelCount() const {
return pixelCount;
}
};
class Display {
public:
Display(NSFastLED::CRGB* pixels, int pixelCount, const CoordinateMapping* map)
: m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {}
NSFastLED::CRGB& pixelAt(const PhysicalCoordinates coords);
NSFastLED::CRGB& pixelAt(const VirtualCoordinates coords);
NSFastLED::CRGB& pixelAt(int idx);
int pixelCount() const;
NSFastLED::CRGB* pixelBacking() const;
const CoordinateMapping* coordinateMapping() const;
void clear();
void clear(const NSFastLED::CRGB& color);
void clear(VirtualCoordinates& start, VirtualCoordinates& end, const NSFastLED::CRGB& color);
private:
NSFastLED::CRGB* m_pixels;
int m_pixelCount;
const CoordinateMapping* m_coordMap;
};

View File

@ -0,0 +1,47 @@
#pragma once
#include "application.h"
#include <functional>
class Display;
class InputEvent;
class InputSource;
struct Task {
virtual void handleEvent(const InputEvent& event) {}
virtual void loop() = 0;
virtual void onStart() {};
virtual void onStop() {};
enum State {
Running,
Stopped,
};
Task() {}
Task(State initialState) : Task(0, initialState) {}
Task(const char* name) : Task(name, Running) {}
Task(const char* name, State initialState) : name(name), state(initialState) {}
void start() { Log.info("* Starting %s...", name); state = Running; onStart(); }
void stop() { Log.info("* Stopping %s...", name); onStop(); state = Stopped; }
const char* name = 0;
State state = Running;
};
struct Figment: public Task {
Figment() : Task() {}
Figment(State initialState) : Task(initialState) {}
Figment(const char* name) : Task(name) {}
Figment(const char* name, State initialState) : Task(name, initialState) {}
virtual void render(Display* dpy) const = 0;
};
struct FigmentFunc: public Figment {
FigmentFunc(std::function<void(Display*)> func) : Figment("lambda"), func(func) {}
void loop() override {}
void render(Display* dpy) const override {
func(dpy);
}
std::function<void(Display*)> func;
};

View File

@ -0,0 +1,8 @@
#pragma once
#include "./Display.h"
#include "./Input.h"
#include "./Figment.h"
#include "./Animation.h"
#include "./MainLoop.h"
#include "./Renderer.h"
#include "./Surface.h"

View File

@ -0,0 +1,39 @@
#pragma once
#include <stdint.h>
template<typename T> struct Coordinates {
Coordinates(T _x, T _y) : x(_x), y(_y) {}
T x;
T y;
};
struct VirtualCoordinates: Coordinates<uint8_t> {
VirtualCoordinates(uint8_t _x, uint8_t _y) : Coordinates(_x, _y) {}
};
struct PhysicalCoordinates: Coordinates<uint8_t> {
PhysicalCoordinates(uint8_t _x, uint8_t _y) : Coordinates(_x, _y) {}
};
template<typename T> struct Vector3d {
Vector3d(T _x, T _y, T _z) : x(_x), y(_y), z(_z) {}
Vector3d() : Vector3d(0, 0, 0) {}
T x;
T y;
T z;
T magnitude() const {
return abs(max(x, max(y, z)));
}
Vector3d<T> operator-(const Vector3d<T>& other) const {
return Vector3d<T>(x - other.x, y - other.y, z - other.z);
}
Vector3d<T> absolute() const {
return Vector3d<T>(abs(x), abs(y), abs(z));
}
};
typedef Vector3d<uint8_t> Vec3;

View File

@ -0,0 +1,41 @@
#include "application.h"
#include "./Input.h"
#include "./MainLoop.h"
NSFastLED::CRGB
Variant::asRGB() const
{
return NSFastLED::CRGB(m_value.asRGB[0], m_value.asRGB[1], m_value.asRGB[2]);
}
const char*
Variant::asString() const
{
return m_value.asString;
}
int
Variant::asInt() const
{
return m_value.asInt;
}
void
InputSource::loop()
{
MainLoop::instance()->dispatch(read());
}
InputEvent
BufferedInputSource::read()
{
InputEvent ret = m_lastEvent;
m_lastEvent = InputEvent{};
return ret;
}
void
BufferedInputSource::setEvent(InputEvent &&evt)
{
m_lastEvent = std::move(evt);
}

95
firmware/Figments/Input.h Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include "application.h"
#include "./Geometry.h"
#include "./Figment.h"
#include "FastLED/FastLED.h"
typedef Vector3d<int> MotionVec;
struct Variant {
enum Type {
Null,
Integer,
String,
Color,
};
Variant(int v)
: type(Integer), m_value{.asInt=v} {}
Variant(const char* v)
: type(String), m_value{.asString=v} {}
Variant(const NSFastLED::CRGB &v)
: type(Color), m_value{.asRGB={v.r, v.g, v.b}} {}
Variant()
: type(Null) {}
Type type;
const char* asString() const;
NSFastLED::CRGB asRGB() const;
int asInt() const;
private:
union {
int asInt;
const char* asString;
uint8_t asRGB[3];
} m_value;
};
struct InputEvent: public Variant {
enum Intent {
None,
PowerToggle,
SetPower,
SetBrightness,
PreviousPattern,
NextPattern,
SetPattern,
SetColor,
Acceleration,
FirmwareUpdate,
NetworkStatus,
StartThing,
StopThing,
UserInput,
};
template<typename Value>
InputEvent(Intent s, Value v)
: Variant(v), intent(s) {}
InputEvent(Intent s)
: Variant(), intent(s) {}
InputEvent()
: Variant(), intent(None) {}
Intent intent;
};
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) {}
void loop() override;
virtual InputEvent read() = 0;
};
class BufferedInputSource: public InputSource {
public:
BufferedInputSource() : InputSource() {}
BufferedInputSource(const char* name) : InputSource(name) {}
InputEvent read() override;
protected:
void setEvent(InputEvent &&evt);
private:
InputEvent m_lastEvent;
};

View File

@ -0,0 +1,51 @@
#include "./MainLoop.h"
#include "./Input.h"
#include "./Figment.h"
void
MainLoop::dispatch(const InputEvent& evt)
{
if (evt.intent == InputEvent::None) {
return;
}
m_eventBuf.insert(evt);
}
void
MainLoop::loop()
{
InputEvent evt;
while (m_eventBuf.take(evt)) {
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
const bool jobState = (evt.intent == InputEvent::StartThing);
for(auto figmentJob: scheduler.tasks) {
if (strcmp(figmentJob->name, evt.asString()) == 0) {
if (jobState) {
figmentJob->start();
} else {
figmentJob->stop();
}
}
}
}
for(Task* task : scheduler) {
task->handleEvent(evt);
}
}
for(Task* task : scheduler) {
task->loop();
}
}
void
MainLoop::start()
{
Log.info("*** Starting %d tasks...", scheduler.tasks.size());
Serial.flush();
for(auto task: scheduler) {
task->start();
}
}
MainLoop* MainLoop::s_instance;

View File

@ -0,0 +1,105 @@
#pragma once
#include <vector>
#include <algorithm>
#include "./Input.h"
class Task;
class InputSource;
struct Scheduler {
std::vector<Task*> tasks;
bool operator==(const Scheduler& other) const {
return tasks == other.tasks;
}
Scheduler(std::vector<Task*> &&tasks)
: tasks(std::move(tasks))
{}
struct iterator: public std::iterator<std::input_iterator_tag, Task*> {
Scheduler& queue;
int idx = 0;
explicit iterator(Scheduler& queue) : queue(queue), idx(nextEnabled(0)) {}
iterator(Scheduler& queue, int start) : queue(queue), idx(nextEnabled(start)) {}
iterator& operator++() {
while (idx < queue.tasks.size() && !queue.tasks[idx]->state == Task::Running) {
idx++;
}
idx = nextEnabled(idx+1);
return *this;
}
int nextEnabled(int start) const {
for(int pos = start; pos < queue.tasks.size();pos++) {
if (queue.tasks[pos]->state == Task::Running) {
return pos;
}
}
return queue.tasks.size();
}
iterator operator++(int) {iterator ret = *this; ++(*this); return ret;}
bool operator==(iterator other) const { return idx == other.idx && queue == other.queue; }
bool operator!=(iterator other) const { return !(*this == other); }
Task* operator*() const { return queue.tasks[idx]; }
};
iterator begin() { return iterator(*this); }
iterator end() { return iterator(*this, tasks.size()); }
};
template<typename T, int Size>
struct Ringbuf {
Ringbuf() : m_head(0), m_tail(0) {}
void clear() {
m_head = 0;
m_tail = 0;
}
bool take(T& dest) {
if (m_head == m_tail) {
return false;
}
const int cur = m_head;
const int nextHead = (m_head + 1) % Size;
m_head = nextHead;
dest = m_items[cur];
return true;
}
void insert(const T& src) {
const int cur = m_tail;
const int nextTail = (m_tail + 1) % Size;
if (nextTail == m_head) {
return;
} else {
m_tail = nextTail;
}
m_items[cur] = src;
}
private:
int m_head = 0;
int m_tail = 0;
std::array<T, Size> m_items;
};
struct MainLoop {
Scheduler scheduler;
MainLoop(std::vector<Task*> &&tasks)
: scheduler(std::move(tasks)) {s_instance = this;}
void start();
void loop();
void dispatch(const InputEvent& event);
static MainLoop* instance() { return s_instance; }
private:
Ringbuf<InputEvent, 10> m_eventBuf;
static MainLoop* s_instance;
};

View File

@ -0,0 +1,24 @@
#include "./Renderer.h"
#include "./Display.h"
void
Renderer::loop()
{
for(Display* dpy : m_displays) {
for(Figment* figment : m_figments) {
if (figment->state == Task::Running) {
figment->render(dpy);
}
};
}
NSFastLED::FastLED.show();
}
void
Renderer::onStart()
{
for(Display* dpy : m_displays) {
dpy->clear();
}
NSFastLED::FastLED.show();
}

View File

@ -0,0 +1,16 @@
#include "./Figment.h"
#include <vector>
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) {}
void loop() override;
void onStart() override;
private:
const std::vector<Figment*> m_figments;
const std::vector<Display*> m_displays;
};

View File

View File

@ -0,0 +1,37 @@
#include "./Surface.h"
#include "./Display.h"
Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end)
: start(dpy->coordinateMapping()->virtualToPhysicalCoords(start)),
end(dpy->coordinateMapping()->virtualToPhysicalCoords(end)),
m_display(dpy)
{
}
Surface&
Surface::operator=(const NSFastLED::CRGB& color)
{
paintWith([&](NSFastLED::CRGB& pixel) {
pixel = color;
});
return *this;
}
Surface&
Surface::operator+=(const NSFastLED::CRGB& color)
{
paintWith([&](NSFastLED::CRGB& pixel) {
pixel += color;
});
return *this;
}
void
Surface::paintWith(std::function<void(NSFastLED::CRGB&)> func)
{
for(uint8_t x = start.x; x <= end.x; x++) {
for(uint8_t y = start.y; y <= end.y; y++) {
func(m_display->pixelAt(PhysicalCoordinates{x, y}));
}
}
}

View File

@ -0,0 +1,20 @@
#include "FastLED/FastLED.h"
#include "./Geometry.h"
class Display;
class Surface {
public:
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end);
Surface& operator=(const NSFastLED::CRGB& color);
Surface& operator+=(const NSFastLED::CRGB& color);
void paintWith(std::function<void(NSFastLED::CRGB&)> func);
const PhysicalCoordinates start;
const PhysicalCoordinates end;
private:
Display* m_display;
};