diff --git a/SimpleList.h b/SimpleList.h new file mode 100644 index 0000000..57904cb --- /dev/null +++ b/SimpleList.h @@ -0,0 +1,521 @@ +#ifndef SimpleList_h +#define SimpleList_h + +/* + =========================================== + Copyright (c) 2018 Stefan Kremser + github.com/spacehuhn + =========================================== + */ + +#include +#include +#include + +template +struct SimpleListNode { + T data; + SimpleListNode* next = NULL; +}; + +template +class SimpleList { + public: + SimpleList(); + SimpleList(int(*compare)(T & a, T & b)); + virtual ~SimpleList(); + + virtual void setCompare(int (* compare)(T& a, T& b)); + + virtual int size(); + virtual bool isSorted(); + virtual bool isEmpty(); + + virtual void add(int index, T obj); + virtual void add(T obj); + virtual void insert(T obj); + + virtual void replace(int index, T obj); + virtual void swap(int x, int y); + + virtual void remove(int index); + virtual void removeFirst(); + virtual void removeLast(); + + virtual bool has(T obj); + virtual int count(T obj); + + virtual T shift(); + virtual T pop(); + virtual T get(int index); + virtual T getFirst(); + virtual T getLast(); + + virtual void moveToEnd(); + + virtual int search(T obj); + virtual int searchNext(T obj); + virtual int binSearch(T obj); + + virtual void sort(); + virtual void clear(); + + protected: + int (* compare)(T& a, T& b) = NULL; + + int listSize = 0; + SimpleListNode* listBegin = NULL; + SimpleListNode* listEnd = NULL; + + // Helps get() method by saving last position + SimpleListNode* lastNodeGot = NULL; + int lastIndexGot = -1; + bool isCached = false; + + bool sorted = true; + + virtual SimpleListNode* getNode(int index); + virtual int binSearch(T obj, int lowerEnd, int upperEnd); +}; + +template +SimpleList::SimpleList() {} + +template +SimpleList::SimpleList(int(*compare)(T & a, T & b)) { + setCompare(compare); +} + +// Clear Nodes and free Memory +template +SimpleList::~SimpleList() { + clear(); +} + +template +void SimpleList::setCompare(int (* compare)(T& a, T& b)) { + this->compare = compare; + sort(); +} + +template +SimpleListNode* SimpleList::getNode(int index) { + if ((index < 0) || (index >= listSize)) return NULL; + + SimpleListNode* hNode = listBegin; + int c = 0; + + if (isCached && (index >= lastIndexGot)) { + c = lastIndexGot; + hNode = lastNodeGot; + } + + while (hNode && c < index) { + hNode = hNode->next; + c++; + } + + if (hNode) { + isCached = true; + lastIndexGot = c; + lastNodeGot = hNode; + } + + return hNode; +} + +template +int SimpleList::size() { + return listSize; +} + +template +bool SimpleList::isSorted() { + return sorted; +} + +template +bool SimpleList::isEmpty() { + return listSize == 0; +} + +template +void SimpleList::add(T obj) { + // create new node + SimpleListNode* newNode = new SimpleListNode(); + + newNode->data = obj; + + if (!listBegin) listBegin = newNode; + + if (listEnd) { + listEnd->next = newNode; + listEnd = newNode; + } else { + listEnd = newNode; + } + + listSize++; + sorted = false; +} + +template +void SimpleList::add(int index, T obj) { + if ((index < 0) || (index >= listSize)) { + return; + } + + SimpleListNode* newNode = new SimpleListNode(); + newNode->data = obj; + + if (index == 0) { + listBegin = newNode; + } else { + SimpleListNode* nodePrev = getNode(index - 1); + newNode->next = nodePrev->next; + nodePrev->next = newNode; + } + + listSize++; + sorted = false; +} + +template +void SimpleList::insert(T obj) { + if (!compare) { + add(obj); + return; + } + + if (!sorted) sort(); + + // create new node + SimpleListNode* newNode = new SimpleListNode(); + newNode->data = obj; + + if (listSize == 0) { + // add at start (first node) + listBegin = newNode; + + listEnd = newNode; + } else { + if (compare(obj, listEnd->data) >= 0) { + // add at end + listEnd->next = newNode; + listEnd = newNode; + } else if (compare(obj, listBegin->data) < 0) { + // add at start + newNode->next = listBegin; + listBegin = newNode; + } else { + // insertion sort + SimpleListNode* h = listBegin; + SimpleListNode* p = NULL; + bool found = false; + + // here a sequential search, because otherwise the previous node couldn't be accessed + while (h && !found) { + if (compare(obj, h->data) < 0) { + found = true; + } else { + p = h; + h = h->next; + } + } + newNode->next = h; + + if (p) p->next = newNode; + } + } + + listSize++; +} + +template +void SimpleList::replace(int index, T obj) { + if ((index >= 0) && (index < listSize)) { + getNode(index)->data = obj; + } +} + +template +void SimpleList::swap(int x, int y) { + // only continue when the index numbers are unequal and at least 0 + if ((x != y) && (x >= 0) && (y >= 0)) { + if (x > y) { // the first index should be smaller than the second. If not, swap them! + int h = x; + x = y; + y = h; + } + + // When data is small, copy it + if (sizeof(T) < 24) { + SimpleListNode* nodeA = getNode(x); + SimpleListNode* nodeB = getNode(y); + T h = nodeA->data; + nodeA->data = nodeB->data; + nodeB->data = h; + } + + // otherwise change the pointers + else { + // Example: a -> b -> c -> ... -> g -> h -> i + // we want to swap b with h + SimpleListNode* nodeA = getNode(x - 1); // x.prev + SimpleListNode* nodeB = getNode(x); // x + SimpleListNode* nodeC = getNode(x + 1); // x.next + SimpleListNode* nodeG = y - 1 == x ? nodeB : getNode(y - 1); // y.prev + SimpleListNode* nodeH = getNode(y); // y + SimpleListNode* nodeI = getNode(y + 1); // y.next + + // a -> h -> i b -> c -> ... -> g -> h -> i + if (nodeA) nodeA->next = nodeH; + else listBegin = nodeH; + + // a -> h -> c -> ... -> g -> h -> i b -> i + if (nodeH != nodeC) // when nodes between b and h exist + nodeH->next = nodeC; + else nodeH->next = nodeB; + + // a -> h -> i b -> i + nodeB->next = nodeI; + + if (!nodeI) listEnd = nodeB; + + // a -> h -> c -> ... -> g -> b -> i + if (nodeG != nodeB) // when more than 1 nodes between b and h exist + nodeG->next = nodeB; + } + } +} + +template +void SimpleList::remove(int index) { + if ((index < 0) || (index >= listSize)) return; + + SimpleListNode* nodePrev = getNode(index - 1); + SimpleListNode* nodeToDelete = getNode(index); + + if (index == 0) { + listBegin = nodeToDelete->next; + } else { + nodePrev->next = nodeToDelete->next; + + if (!nodePrev->next) listEnd = nodePrev; + } + + delete nodeToDelete; + + isCached = false; + + listSize--; +} + +template +void SimpleList::removeFirst() { + remove(0); +} + +template +void SimpleList::removeLast() { + remove(listSize - 1); +} + +template +bool SimpleList::has(T obj) { + return binSearch(obj) >= 0; +} + +template +int SimpleList::count(T obj) { + if (compare == NULL) return -1; + + int c = 0; + + for (int i = 0; i < listSize; i++) { + if (compare(obj, getNode(i)->data) == 0) c++; + } + + return c; +} + +template +T SimpleList::get(int index) { + SimpleListNode* h = getNode(index); + + if (h) return h->data; + + return T(); +} + +template +T SimpleList::getFirst() { + return get(0); +} + +template +T SimpleList::getLast() { + return get(listSize - 1); +} + +template +void SimpleList::moveToEnd() { + SimpleListNode* h = listBegin; + + if (!h) return; + + listBegin = listBegin->next; + listEnd->next = h; + + h->next = NULL; + listEnd = h; + + lastNodeGot = NULL; + lastIndexGot = -1; + isCached = false; + + sorted = false; +} + +template +int SimpleList::search(T obj) { + if (compare == NULL) return -1; + + int i = 0; + + SimpleListNode* hNode = getNode(i); + bool found = compare(obj, hNode->data) == 0; + + while (!found && i < listSize) { + i++; + hNode = getNode(i); + found = compare(obj, hNode->data) == 0; + } + + return found ? i : -1; +} + +template +int SimpleList::searchNext(T obj) { + if (compare == NULL) return -1; + + int i = lastIndexGot; + + SimpleListNode* hNode = lastNodeGot; + bool found = compare(obj, hNode->data) == 0; + + while (!found && i < listSize) { + i++; + hNode = getNode(i); + found = compare(obj, hNode->data) == 0; + } + + return found ? i : -1; +} + +template +int SimpleList::binSearch(T obj, int lowerEnd, int upperEnd) { + if (!compare || !sorted) return search(obj); + + if (!listBegin) return -1; + + int res; + int mid = (lowerEnd + upperEnd) / 2; + + SimpleListNode* hNode = getNode(0); + int hIndex = 0; + + while (lowerEnd <= upperEnd) { + hNode = lastNodeGot; + hIndex = lastIndexGot; + res = compare(obj, getNode(mid)->data); + + if (res == 0) { + return mid; + } else if (res < 0) { + // when going left, set cached node back to previous cached node + lastNodeGot = hNode; + lastIndexGot = hIndex; + isCached = true; + + upperEnd = mid - 1; + mid = (lowerEnd + upperEnd) / 2; + } else if (res > 0) { + lowerEnd = mid + 1; + mid = (lowerEnd + upperEnd) / 2; + } + } + + return -1; +} + +template +int SimpleList::binSearch(T obj) { + return binSearch(obj, 0, listSize - 1); +} + +template +T SimpleList::pop() { + T data = getLast(); + + removeLast(); + + return data; +} + +template +T SimpleList::shift() { + T data = getFirst(); + + removeFirst(); + + return data; +} + +template +void SimpleList::clear() { + while (listSize > 0) removeFirst(); + + listSize = 0; + listBegin = NULL; + listEnd = NULL; + + lastNodeGot = NULL; + lastIndexGot = -1; + isCached = false; + + sorted = true; +} + +template +void SimpleList::sort() { + if (compare == NULL) return; + + // selection sort (less swaps than insertion sort) + + int indexH; // index of node i + int indexMin; // index of next minimum node + + SimpleListNode* nodeMin; // next minimum node + SimpleListNode* nodeH; // helper node at index j + + for (int i = 0; i < listSize - 1; i++) { + nodeMin = getNode(i); + indexH = i; + indexMin = i; + + for (int j = i + 1; j < listSize; j++) { + nodeH = getNode(j); + + if (compare(nodeMin->data, nodeH->data) > 0) { + nodeMin = nodeH; + indexMin = j; + } + } + swap(indexH, indexMin); + } + + this->sorted = true; +} + +#endif // ifndef SimpleList_h diff --git a/config.sample.h b/config.sample.h index 769d5db..68395d5 100644 --- a/config.sample.h +++ b/config.sample.h @@ -28,4 +28,4 @@ uint8_t config_brightness = 20; // Can be overwritten via MQTT_TOPIC_BRIGHTNESS #define FPS 50 #define SHOW_TEXT_DELAY 100 -#define EFFECT_CYCLE_TIME 60000 +uint16_t config_effect_cycle_time = 300; // Time in seconds between cycling effects. diff --git a/effects.h b/effects.h index a29a228..e08b7e7 100644 --- a/effects.h +++ b/effects.h @@ -1,16 +1,3 @@ -class Effect { - protected: - Window window = {0, 0, LED_WIDTH, LED_HEIGHT}; // Use a full screen window per default. - public: - virtual void loop() = 0; - boolean supports_window = false; - virtual boolean can_be_shown_with_clock() { return false; }; - virtual boolean clock_as_mask() { return false; }; - void setWindow(Window win) { - window = win; - }; -}; - struct EffectEntry { char* name; Effect* effect; @@ -269,7 +256,7 @@ class Animation : public Effect { frameSince = millis(); } } - void set(int i, CRGB* color) { + void set(int i, CRGB* color) { setPixel(xOffset + (i % animation->w), yOffset + (i / animation->h), *color); } uint16_t frameDelay(AnimationData* animation, int frame) { @@ -376,3 +363,34 @@ class MatrixEffect : public Effect { for (int i=0; isize(); + new_effect = cycle_effects->get(new_id); + } while (&new_effect == &effect); + effect = new_effect; + } + + boolean can_be_shown_with_clock() { + return effect->can_be_shown_with_clock(); + }; + boolean clock_as_mask() { + return effect->clock_as_mask(); + }; + + CycleEffect() {}; + + void loop() { + if (!effect) changeEffect(); // If this is the first run, we have to select an effect first! + effect->loop(); + EVERY_N_SECONDS(config_effect_cycle_time) { + changeEffect(); + } + } +}; diff --git a/pitrix.ino b/pitrix.ino index 5286a55..bac5dcb 100644 --- a/pitrix.ino +++ b/pitrix.ino @@ -5,6 +5,7 @@ #include "FastLED.h" #include #include +#include "SimpleList.h" #include "config.h" CRGB leds[LED_COUNT]; @@ -27,14 +28,30 @@ typedef struct { uint8_t h; } Window; +class Effect { + protected: + Window window = {0, 0, LED_WIDTH, LED_HEIGHT}; // Use a full screen window per default. + public: + virtual void loop() = 0; + boolean supports_window = false; + virtual boolean can_be_shown_with_clock() { return false; }; + virtual boolean clock_as_mask() { return false; }; + void setWindow(Window win) { + window = win; + }; +}; + #include "functions.h" #include "text.h" #include "sprites.h" #include "animations.h" #include "tools.h" + +SimpleList* cycle_effects; + #include "effects.h" -#define NUM_EFFECTS 10 +#define NUM_EFFECTS 11 //EffectEntry effects[NUM_EFFECTS]; Sinematrix3 sinematrix3; BigClock big_clock; @@ -46,6 +63,7 @@ Animation anim_couple_rain(&animation_couple_rain, CRGB(0x000000), -8, -16); SingleDynamic single_dynamic; MultiDynamic multi_dynamic; MatrixEffect matrix; +CycleEffect effect_cycle; EffectEntry effects[NUM_EFFECTS] = { {"sinematrix3", (Effect *)&sinematrix3}, @@ -58,13 +76,23 @@ EffectEntry effects[NUM_EFFECTS] = { {"single_dynamic", (Effect *)&single_dynamic}, {"multi_dynamic", (Effect *)&multi_dynamic}, {"matrix", (Effect *)&matrix}, + {"cycle", (Effect *)&effect_cycle}, }; +void setup_cycle_effects() { + LOGln("Core * Setting up cycle_effects"); + cycle_effects = new SimpleList(); + cycle_effects->add(&sinematrix3); + cycle_effects->add(&single_dynamic); + cycle_effects->add(&multi_dynamic); + cycle_effects->add(&matrix); +} + void setup() { - // put your setup code here, to run once: Serial.begin(74880); Serial.println("Core * Starting"); + setup_cycle_effects(); wifi_setup(); ota_setup(); fastled_setup();