Completed the board manager rewrite
This commit is contained in:
@@ -4,32 +4,60 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "PINOUT.h"
|
#include "PINOUT.h"
|
||||||
#include "CubeStack.h"
|
#include "BoardDriverTypes.h"
|
||||||
|
|
||||||
// define the physical dimensions of the board
|
// define the physical dimensions of the board
|
||||||
#define BOARD_WIDTH 3
|
static constexpr uint32_t BOARD_WIDTH{3};
|
||||||
#define BOARD_LENGTH 3
|
static constexpr uint32_t BOARD_LENGTH{3};
|
||||||
#define BOARD_HEIGHT 3
|
static constexpr uint32_t BOARD_HEIGHT{3};
|
||||||
|
|
||||||
// define the number of stacks
|
// define the number of stacks
|
||||||
#define NUMBER_STACKS BOARD_WIDTH * BOARD_LENGTH
|
static constexpr uint32_t NUMBER_STACKS{BOARD_WIDTH * BOARD_LENGTH};
|
||||||
|
|
||||||
// define the CubeStacks
|
// define the CubeStacks
|
||||||
CubeStack stack1(STACK1_ADC_PIN, STACK1_LED_PIN, BOARD_HEIGHT);
|
static BoardDriverTypes::CubeStack stack1{
|
||||||
CubeStack stack2(STACK2_ADC_PIN, STACK2_LED_PIN, BOARD_HEIGHT);
|
.adcPin=STACK1_ADC_PIN,
|
||||||
CubeStack stack3(STACK3_ADC_PIN, STACK3_LED_PIN, BOARD_HEIGHT);
|
.ledPin=STACK1_LED_PIN
|
||||||
CubeStack stack4(STACK4_ADC_PIN, STACK4_LED_PIN, BOARD_HEIGHT);
|
};
|
||||||
CubeStack stack5(STACK5_ADC_PIN, STACK5_LED_PIN, BOARD_HEIGHT);
|
static BoardDriverTypes::CubeStack stack2{
|
||||||
CubeStack stack6(STACK6_ADC_PIN, STACK6_LED_PIN, BOARD_HEIGHT);
|
.adcPin=STACK2_ADC_PIN,
|
||||||
CubeStack stack7(STACK7_ADC_PIN, STACK7_LED_PIN, BOARD_HEIGHT);
|
.ledPin=STACK2_LED_PIN
|
||||||
CubeStack stack8(STACK8_ADC_PIN, STACK8_LED_PIN, BOARD_HEIGHT);
|
};
|
||||||
CubeStack stack9(STACK9_ADC_PIN, STACK9_LED_PIN, BOARD_HEIGHT);
|
static BoardDriverTypes::CubeStack stack3{
|
||||||
|
.adcPin=STACK3_ADC_PIN,
|
||||||
// define the array of stacks
|
.ledPin=STACK3_LED_PIN
|
||||||
CubeStack * stacks[] = {
|
};
|
||||||
&stack1, &stack2, &stack3,
|
static BoardDriverTypes::CubeStack stack4{
|
||||||
&stack4, &stack5, &stack6,
|
.adcPin=STACK4_ADC_PIN,
|
||||||
&stack7, &stack8, &stack9
|
.ledPin=STACK4_LED_PIN
|
||||||
|
};
|
||||||
|
static BoardDriverTypes::CubeStack stack5{
|
||||||
|
.adcPin=STACK5_ADC_PIN,
|
||||||
|
.ledPin=STACK5_LED_PIN
|
||||||
|
};
|
||||||
|
static BoardDriverTypes::CubeStack stack6{
|
||||||
|
.adcPin=STACK6_ADC_PIN,
|
||||||
|
.ledPin=STACK6_LED_PIN
|
||||||
|
};
|
||||||
|
static BoardDriverTypes::CubeStack stack7{
|
||||||
|
.adcPin=STACK7_ADC_PIN,
|
||||||
|
.ledPin=STACK7_LED_PIN
|
||||||
|
};
|
||||||
|
static BoardDriverTypes::CubeStack stack8{
|
||||||
|
.adcPin=STACK8_ADC_PIN,
|
||||||
|
.ledPin=STACK8_LED_PIN
|
||||||
|
};
|
||||||
|
static BoardDriverTypes::CubeStack stack9{
|
||||||
|
.adcPin=STACK9_ADC_PIN,
|
||||||
|
.ledPin=STACK9_LED_PIN
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::array<BoardDriverTypes::CubeStack, NUMBER_STACKS> stacks{
|
||||||
|
stack1, stack2, stack3,
|
||||||
|
stack4, stack5, stack6,
|
||||||
|
stack7, stack8, stack9
|
||||||
};
|
};
|
||||||
54
include/Vector3D.h
Normal file
54
include/Vector3D.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
class V3D{
|
||||||
|
public:
|
||||||
|
V3D(uint32_t x=0, uint32_t y=0, uint32_t z=0):
|
||||||
|
x(x),
|
||||||
|
y(y),
|
||||||
|
z(z){}
|
||||||
|
|
||||||
|
V3D& operator=(const V3D &other){
|
||||||
|
this->x = other.x;
|
||||||
|
this->y = other.y;
|
||||||
|
this->z = other.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D& operator+(const V3D &other){
|
||||||
|
V3D vector{};
|
||||||
|
vector.x = this->x + other.x;
|
||||||
|
vector.y = this->y + other.y;
|
||||||
|
vector.z = this->z + other.z;
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D& operator-(const V3D &other){
|
||||||
|
V3D vector{};
|
||||||
|
vector.x = this->x - other.x;
|
||||||
|
vector.y = this->y - other.y;
|
||||||
|
vector.z = this->z - other.z;
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D operator/(const uint32_t scalar){
|
||||||
|
V3D vector{};
|
||||||
|
vector.x = this->x / scalar;
|
||||||
|
vector.y = this->y / scalar;
|
||||||
|
vector.z = this->z / scalar;
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const V3D &other){
|
||||||
|
return this->x == other.x && this->y == other.y && this->z == other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
float magnitude(){
|
||||||
|
return std::sqrt(this->x * this->x + this->y * this->y + this->z * this-> z);
|
||||||
|
}
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
uint32_t z;
|
||||||
|
};
|
||||||
353
lib/Animator/Animator.h
Normal file
353
lib/Animator/Animator.h
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
#include "Vector3D.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Animation{
|
||||||
|
public:
|
||||||
|
// for cube spots which aren't defined in a key frame,
|
||||||
|
// you can have the controller automatically interpolate a color
|
||||||
|
enum FillInterpolation{
|
||||||
|
NO_FILL, // if not specified, the cube color will be black
|
||||||
|
CLOSEST_COLOR, // The cube color will be the same color as the cube closest to it
|
||||||
|
LINEAR_WEIGHTED_DISTANCE, // the cube color will be an average of all specified cube colors weighted by the linear distance to this cube
|
||||||
|
SQUARE_WEIGHTED_DISTANCE // same as linear, but further colors have exponentially less impact on the color
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FrameInterpolation{
|
||||||
|
SNAP, // After the delay, snap to the next key frame
|
||||||
|
FADE // over the course of the delay, fade to the next frame
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cell{
|
||||||
|
V3D position;
|
||||||
|
V3D color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this contains all of the information to specify exactly how a single frame should look and fade to the next frame
|
||||||
|
struct AnimationFrame{
|
||||||
|
std::vector<Cell> frame;
|
||||||
|
FillInterpolation fillInterpolation;
|
||||||
|
FrameInterpolation frameInterpolation;
|
||||||
|
std::chrono::milliseconds delay;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::array<std::array<std::array<V3D, Z_SIZE>, Y_SIZE>, X_SIZE> Frame;
|
||||||
|
|
||||||
|
|
||||||
|
void StartAnimation(const std::vector<AnimationFrame> &animationSequence){
|
||||||
|
this->animationSequence = animationSequence;
|
||||||
|
this->animationIndex = 0;
|
||||||
|
this->timeElapsed = std::chrono::milliseconds(0);
|
||||||
|
|
||||||
|
if(animationSequence.size() == 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(animationSequence.size() == 1){
|
||||||
|
this->uncompressFrame(animationSequence[0], this->startFrame);
|
||||||
|
this->uncompressFrame(animationSequence[0], this->endFrame);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this->uncompressFrame(animationSequence[0], this->startFrame);
|
||||||
|
this->uncompressFrame(animationSequence[1], this->endFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame &RunAnimation(std::chrono::milliseconds timePassed){
|
||||||
|
auto delayTime = this->animationSequence[this->animationIndex].delay;
|
||||||
|
this->timeElapsed += timePassed;
|
||||||
|
Frame interpolatedFrame;
|
||||||
|
|
||||||
|
// load in the next frame if we're done with this transition
|
||||||
|
if(this->timeElapsed >= delayTime){
|
||||||
|
this->incrimentAnimationIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't do frame interpolations if we're doing snap fades
|
||||||
|
if(this->animationSequence[this->animationIndex].frameInterpolation == FrameInterpolation::SNAP){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// linearly interpolate between the two uncompressed frames
|
||||||
|
for(uint32_t x = 0; x < this->X_SIZE; x++){
|
||||||
|
for(uint32_t y = 0; y < this->Y_SIZE; y++){
|
||||||
|
for(uint32_t z = 0; z < this->Z_SIZE; z++){
|
||||||
|
V3D startColor{this->startFrame[x][y][z]};
|
||||||
|
V3D endColor{this->endFrame[x][y][z]};
|
||||||
|
V3D difference{endColor - startColor};
|
||||||
|
V3D interpolatedColor = this->timeElapsed.count() * difference / delayTime.count() + startColor;
|
||||||
|
interpolatedFrame[x][y][z] = interpolatedColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLoop(bool isLooping){
|
||||||
|
this->isLooping = isLooping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t X_SIZE{3};
|
||||||
|
uint32_t Y_SIZE{3};
|
||||||
|
uint32_t Z_SIZE{3};
|
||||||
|
bool isLooping{true};
|
||||||
|
// these are the uncompressed frames you get by following the key colors and interpolation instructions of an animation frame
|
||||||
|
Frame startFrame;
|
||||||
|
Frame endFrame;
|
||||||
|
std::chrono::milliseconds timeElapsed;
|
||||||
|
|
||||||
|
const std::vector<AnimationFrame> & animationSequence;
|
||||||
|
uint32_t animationIndex{0};
|
||||||
|
|
||||||
|
void incrimentAnimationIndex(){
|
||||||
|
if(this->animationIndex < animationSequence.size() - 1){
|
||||||
|
this->animationIndex++;
|
||||||
|
this->timeElapsed = std::chrono::millis(0);
|
||||||
|
this->uncompressFrame(this->animationSequence[this->animationIndex], this->startFrame);
|
||||||
|
this->uncompressFrame(this->animationSequence[this->animationIndex + 1], this->endFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uncompressFrame(const AnimationFrame &keyFrame, Frame &frameBuffer){
|
||||||
|
for(uint32_t x = 0; x < X_SIZE; x++){
|
||||||
|
for(uint32_t y = 0; y < Y_SIZE; y++){
|
||||||
|
for(uint32_t z = 0; z < Z_SIZE; z++){
|
||||||
|
frameBuffer[x][y][z] = getInterpolatedColor(keyFrame, V3D(x, y, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &getInterpolatedColor(const AnimationFrame &keyFrame, V3D position){
|
||||||
|
switch(keyFrame.fillInterpolation){
|
||||||
|
case FillInterpolation::NO_FILL:
|
||||||
|
return noFillInterpolate(keyFrame, position);
|
||||||
|
case FillInterpolation::CLOSEST_COLOR:
|
||||||
|
return closestColorInterpolate(keyFrame, position);
|
||||||
|
case FillInterpolation::LINEAR_WEIGHTED_DISTANCE:
|
||||||
|
return linearInterpolate(keyFrame, position);
|
||||||
|
case FillInterpolation::SQUARE_WEIGHTED_DISTANCE:
|
||||||
|
return squareInterpolate(keyFrame, position);
|
||||||
|
default:
|
||||||
|
return V3D{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &keyFrame2BoardCoords(V3D &keyFramePosition){
|
||||||
|
V3D returnValue{};
|
||||||
|
float maxValue{static_cast<float>(std::numeric_limits<uint32_t>::max())};
|
||||||
|
// scale the key frame values down to be within board coordinates
|
||||||
|
float keyFrame_X = static_cast<float>(this->X_SIZE) * static_cast<float>(keyFramePosition.x) / maxValue;
|
||||||
|
float keyFrame_Y = static_cast<float>(this->Y_SIZE) * static_cast<float>(keyFramePosition.y) / maxValue;
|
||||||
|
float keyFrame_Z = static_cast<float>(this->Z_SIZE) * static_cast<float>(keyFramePosition.z) / maxValue;
|
||||||
|
|
||||||
|
// carefully quantize the float values back into ints with a precise rounding operation
|
||||||
|
if(keyFrame_X - std::floor(keyFrame_X) < 0.5f){
|
||||||
|
returnValue.x = static_cast<uint32_t>(keyFrame_X);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
returnValue.x = static_cast<uint32_t>(keyFrame_X) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(keyFrame_Y - std::floor(keyFrame_Y) < 0.5f){
|
||||||
|
returnValue.y = static_cast<uint32_t>(keyFrame_Y);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
returnValue.y = static_cast<uint32_t>(keyFrame_Y) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(keyFrame_Z - std::floor(keyFrame_Z) < 0.5f){
|
||||||
|
returnValue.z = static_cast<uint32_t>(keyFrame_Z);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
returnValue.z = static_cast<uint32_t>(keyFrame_Z) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &noFillInterpolate(const AnimationFrame &keyFrame, V3D position){
|
||||||
|
V3D returnColor{};
|
||||||
|
for(Cell cell : keyFrame.frame){
|
||||||
|
if(keyFrame2BoardCoords(cell.position) == position){
|
||||||
|
returnColor = cell.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &closestColorInterpolate(const AnimationFrame &keyFrame, V3D position){
|
||||||
|
V3D returnColor{};
|
||||||
|
float closestDistance = (keyframe.frame[0].position - position).mag();
|
||||||
|
for(Cell cell : keyFrame.frame){
|
||||||
|
float distance = (keyFrame2BoardCoords(cell.position) - position).mag();
|
||||||
|
if(distance < closestDistance){
|
||||||
|
returnColor = cell.color;
|
||||||
|
closestDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &linearInterpolate(const AnimationFrame &keyFrame, V3D position){
|
||||||
|
V3D returnColor{};
|
||||||
|
|
||||||
|
for(Cell cell : keyFrame.frame){
|
||||||
|
uint32_t distance = static_cast<uint32_t>((keyFrame2BoardCoords(cell.position) - position).mag());
|
||||||
|
if(distance == 0) distance = 1;
|
||||||
|
returnColor = returnColor + cell.color / distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnColor = returnColor / keyFrame.frame.size();
|
||||||
|
|
||||||
|
return returnColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
V3D &squareInterpolate(const AnimationFrame &keyFrame, V3D position){
|
||||||
|
V3D returnColor{};
|
||||||
|
|
||||||
|
for(Cell cell : keyFrame.frame){
|
||||||
|
uint32_t distance = static_cast<uint32_t>((keyFrame2BoardCoords(cell.position) - position).mag());
|
||||||
|
distance *= distance;
|
||||||
|
if(distance == 0) distance = 1;
|
||||||
|
returnColor = returnColor + cell.color / distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnColor = returnColor / keyFrame.frame.size();
|
||||||
|
|
||||||
|
return returnColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// let's make some test animation frames
|
||||||
|
|
||||||
|
namespace TestFrames{
|
||||||
|
V3D red{255,0,0};
|
||||||
|
V3D green{0,255,0};
|
||||||
|
V3D blue{0,0,255};
|
||||||
|
uint32_t maxValue{std::numeric_limits<uint32_t>::max()};
|
||||||
|
|
||||||
|
Animation::Cell &CreateCell(float x_percent, float y_percent, float z_percent, V3D &color){
|
||||||
|
float continuousMaxValue{static_cast<float>(std::numeric_limits<uint32_t>::max())};
|
||||||
|
Animation::Cell cell{
|
||||||
|
.position = V3D{
|
||||||
|
static_cast<uint32_t>(continuousMaxValue*x_percent),
|
||||||
|
static_cast<uint32_t>(continuousMaxValue*y_percent),
|
||||||
|
static_cast<uint32_t>(continuousMaxValue*z_percent)
|
||||||
|
},
|
||||||
|
.color = color
|
||||||
|
};
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame noFillFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::NO_FILL;
|
||||||
|
.frameInterpolation = FrameInterpolation::SNAP;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame closestColorFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::CLOSEST_COLOR;
|
||||||
|
.frameInterpolation = FrameInterpolation::SNAP;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame linearFillFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::LINEAR_WEIGHTED_DISTANCE;
|
||||||
|
.frameInterpolation = FrameInterpolation::SNAP;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame squareFillFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::SQUARE_WEIGHTED_DISTANCE;
|
||||||
|
.frameInterpolation = FrameInterpolation::SNAP;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame noFillFadeFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::NO_FILL;
|
||||||
|
.frameInterpolation = FrameInterpolation::FADE;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame closestColorFadeFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::CLOSEST_COLOR;
|
||||||
|
.frameInterpolation = FrameInterpolation::FADE;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame linearFillFadeFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::LINEAR_WEIGHTED_DISTANCE;
|
||||||
|
.frameInterpolation = FrameInterpolation::FADE;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::AnimationFrame squareFillFadeFrame{
|
||||||
|
.frame = {
|
||||||
|
CreateCell(0,0,0,red),
|
||||||
|
CreateCell(0.5,0.5,0.5,green),
|
||||||
|
CreateCell(1,1,1,blue)
|
||||||
|
};
|
||||||
|
.fillInterpolation = FillInterpolation::SQUARE_WEIGHTED_DISTANCE;
|
||||||
|
.frameInterpolation = FrameInterpolation::FADE;
|
||||||
|
.delay = std::chrono::millis(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AnimationFrame> testAnimationSequence{
|
||||||
|
noFillFrame,
|
||||||
|
closestColorFrame,
|
||||||
|
linearFillFrame,
|
||||||
|
squareFillFrame,
|
||||||
|
noFillFadeFrame,
|
||||||
|
closestFillFadeFrame,
|
||||||
|
linearFillFadeFrame,
|
||||||
|
squareFillFadeFrame
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2,20 +2,25 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <WString.h>
|
||||||
|
|
||||||
#include "Cube.h"
|
#include "Cube.h"
|
||||||
#include "Color.h"
|
#include "Vector3D.h"
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
class Board{
|
class Board{
|
||||||
public:
|
public:
|
||||||
|
enum PLANE_NORMAL : uint32_t{
|
||||||
|
X = 0,
|
||||||
|
Y,
|
||||||
|
Z
|
||||||
|
};
|
||||||
|
|
||||||
Board();
|
Board();
|
||||||
~Board() = default;
|
~Board() = default;
|
||||||
|
|
||||||
constexpr uint32_t GetXSize() const{return X_SIZE;}
|
constexpr V3D &GetSize() const{return BOARD_DIMS;}
|
||||||
constexpr uint32_t GetYSize() const{return Y_SIZE;}
|
constexpr uint32_t GetNumberCubes() const{return BOARD_DIMS.x * BOARD_DIMS.y * BOARD_DIMS.z;}
|
||||||
constexpr uint32_t GetZSize() const {return Z_SIZE;}
|
|
||||||
constexpr uint32_t GetNumberCubes() const{return X_SIZE * Y_SIZE * Z_SIZE;}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a string in the format:
|
* @brief Returns a string in the format:
|
||||||
@@ -28,13 +33,25 @@ class Board{
|
|||||||
/**
|
/**
|
||||||
* @brief Returns a bool array representing the board
|
* @brief Returns a bool array representing the board
|
||||||
*/
|
*/
|
||||||
std::array<uint32_t, X_SIZE * Y_SIZE> &LinearizeBoard() const;
|
std::array<uint32_t, BOARD_DIMS.x * BOARD_DIMS.y> &LinearizeBoard() const;
|
||||||
|
|
||||||
void Update();
|
void FillColor(const V3D &color);
|
||||||
|
|
||||||
void FillColor(const Color &color);
|
void SetCubeColor(const V3D &position, const V3D &color);
|
||||||
|
|
||||||
void SetCubeColor(uint32_t x_coord, uint32_t y_cord, uint32_t z_cord, const Color &color);
|
void SetCubeOccupation(const V3D &position, bool occupation);
|
||||||
|
|
||||||
|
bool BoardStateChanged(){return this->boardStateHasChanged;}
|
||||||
|
void ClearBoardStateChanged(){this->boardStateHasChanged = false;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a column along any axis
|
||||||
|
* @param column .z specifies the normal direction of the plane (see PLANE_NORMAL), and
|
||||||
|
* the x,y values specify the location of the column in that plane
|
||||||
|
* to fill. IE To fill one stack at 0,2 I would say give V3D(0,2,PLANE_NORMAL::Z)
|
||||||
|
* @returns an array of cubes along that column
|
||||||
|
*/
|
||||||
|
Cube * SliceBoard(const V3D &column);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// this is a 3d array of cubes to represent the board. Good luck visualizing it
|
// this is a 3d array of cubes to represent the board. Good luck visualizing it
|
||||||
@@ -53,13 +70,13 @@ class Board{
|
|||||||
bool boardStateHasChanged;
|
bool boardStateHasChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
Board<X_SIZE, Y_SIZE, Z_SIZE>::Board(){
|
Board<BOARD_DIMS>::Board(){
|
||||||
this->FillColor(Color(0,0,0));
|
this->FillColor(Color(0,0,0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
void Board<X_SIZE, Y_SIZE, Z_SIZE>::ToStackString(String &stringBuffer) const{
|
void Board<BOARD_DIMS>::ToStackString(String &stringBuffer) const{
|
||||||
std::array<uint32_t, X_SIZE*Y_SIZE> linearizedBoard = this->LinearizeBoard();
|
std::array<uint32_t, X_SIZE*Y_SIZE> linearizedBoard = this->LinearizeBoard();
|
||||||
|
|
||||||
stringBuffer += "!" + String(linearizedBoard[0]);
|
stringBuffer += "!" + String(linearizedBoard[0]);
|
||||||
@@ -71,10 +88,10 @@ void Board<X_SIZE, Y_SIZE, Z_SIZE>::ToStackString(String &stringBuffer) const{
|
|||||||
stringBuffer += ";";
|
stringBuffer += ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
std::array<uint32_t, X_SIZE * Y_SIZE> & Board<X_SIZE, Y_SIZE, Z_SIZE>::LinearizeBoard() const{
|
std::array<uint32_t, BOARD_DIMS.x * BOARD_DIMS.y> & Board<BOARD_DIMS>::LinearizeBoard() const{
|
||||||
// convert the board into one array where each entry represents the height of one stack
|
// convert the board into one array where each entry represents the height of one stack
|
||||||
std::array<uint32_t, X_SIZE*Y_SIZE> linearizedBoard;
|
std::array<uint32_t, BOARD_DIMS.x * BOARD_DIMS.y> linearizedBoard;
|
||||||
for(uint32_t x{0}; x < X_SIZE; x++){
|
for(uint32_t x{0}; x < X_SIZE; x++){
|
||||||
for(uint32_t y{0}; y < Y_SIZE; y++){
|
for(uint32_t y{0}; y < Y_SIZE; y++){
|
||||||
for(uint32_t z{0}; z < Z_SIZE; z++){
|
for(uint32_t z{0}; z < Z_SIZE; z++){
|
||||||
@@ -86,24 +103,63 @@ std::array<uint32_t, X_SIZE * Y_SIZE> & Board<X_SIZE, Y_SIZE, Z_SIZE>::Linearize
|
|||||||
return linearizedBoard;
|
return linearizedBoard;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
void Board<X_SIZE, Y_SIZE, Z_SIZE>::Update(){
|
void Board<BOARD_DIMS>::FillColor(const V3D &color){
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
|
||||||
void Board<X_SIZE, Y_SIZE, Z_SIZE>::FillColor(const Color &color){
|
|
||||||
for(uint32_t x{0}; x < X_SIZE; x++){
|
for(uint32_t x{0}; x < X_SIZE; x++){
|
||||||
for(uint32_t y{0}; y < Y_SIZE; y++){
|
for(uint32_t y{0}; y < Y_SIZE; y++){
|
||||||
for(uint32_t z{0}; z < Z_SIZE; z++){
|
for(uint32_t z{0}; z < Z_SIZE; z++){
|
||||||
this->cubes[x][y][z].SetColor(color);
|
this->cubes[x][y][z].color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <uint32_t X_SIZE, uint32_t Y_SIZE, uint32_t Z_SIZE>
|
template <V3D &BOARD_DIMS>
|
||||||
void Board<X_SIZE, Y_SIZE, Z_SIZE>::SetCubeColor(uint32_t x_coord, uint32_t y_cord, uint32_t z_cord, const Color &color){
|
void Board<BOARD_DIMS>::SetCubeColor(const V3D &position, const V3D &color){
|
||||||
this->cubes[x][y][z].SetColor(color);
|
this->cubes[position.x][position.y][position.z].color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
void Board<BOARD_DIMS>::SetCubeOccupation(const V3D &position, bool occupation){
|
||||||
|
bool oldOccupation{this->cubes[position.x][position.y][position.z].occupied};
|
||||||
|
this->cubes[position.x][position.y][position.z].occupied = occupation;
|
||||||
|
if(occupation != oldOccupation) this->boardStateHasChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
Cube * Board<BOARD_DIMS>::SliceBoard(const V3D &column){
|
||||||
|
uint32_t columnLength{0};
|
||||||
|
V3D indexIncriment{};
|
||||||
|
V3D position{};
|
||||||
|
switch(column.z){
|
||||||
|
case Board::PLANE_NORMAL::X:
|
||||||
|
columnLength = BOARD_DIMS.x;
|
||||||
|
indexIncriment.x = 1;
|
||||||
|
position.z = column.x;
|
||||||
|
position.y = column.y;
|
||||||
|
break;
|
||||||
|
case Board::PLANE_NORMAL::Y:
|
||||||
|
columnLength = BOARD_DIMS.Y;
|
||||||
|
indexIncriment.y = 1;
|
||||||
|
position.x = column.x;
|
||||||
|
position.z = column.y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case Board::PLANE_NORMAL::Z:
|
||||||
|
columnLength = BOARD_DIMS.Z;
|
||||||
|
indexIncriment.z = 1;
|
||||||
|
position.x = column.x;
|
||||||
|
position.y = column.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Cube *, columnLength> columnSlice;
|
||||||
|
for(uint32_t i = 0; i < columnLength; i++){
|
||||||
|
V3D cubePosition = indexIncriment * i + position;
|
||||||
|
columnSlice[i] = &(this->cubes[cubePosition.x][cubePosition.y][cubePosition.z]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return columnSlice.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
#include "BoardLayout.h"
|
|
||||||
|
|
||||||
uint8_t BoardLayout::GetNumberStacks(){
|
|
||||||
return this->boardWidth * this->boardHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoardLayout::SetStackColors(uint8_t stackNum, Color * colors){
|
|
||||||
CubeStack * stack = this->stacks[stackNum];
|
|
||||||
stack->SetLEDColors(colors, this->boardHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BoardLayout::BoardStateHasChanged(){
|
|
||||||
uint16_t boardState[this->boardWidth * this->boardLength];
|
|
||||||
this->GetBoardState(boardState);
|
|
||||||
|
|
||||||
// compare the board state to the last board state
|
|
||||||
for(int i = 0; i < (this->boardWidth * this->boardLength); i++){
|
|
||||||
uint16_t stackState = boardState[i];
|
|
||||||
uint16_t lastStackState = (this->lastBoardState)[i];
|
|
||||||
if(stackState != lastStackState){
|
|
||||||
// copy the board state into the last board state
|
|
||||||
for(int k = 0; k < (this->boardWidth * this->boardLength); k++){
|
|
||||||
this->lastBoardState[k] = boardState[k];
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoardLayout::GetBoardState(uint16_t * boardStateBuffer){
|
|
||||||
for(int i = 0; i < (this->boardLength * this->boardWidth); i++){
|
|
||||||
CubeStack * stack = this->stacks[i];
|
|
||||||
stack->SendLEDData(); // Enable this if you want to constantly stream LED data
|
|
||||||
boardStateBuffer[i] = stack->GetNumberCubes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/**
|
|
||||||
* @brief This is the full board manager which handles the state of every stack on the board
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CubeStack.h"
|
|
||||||
#include "Color.h"
|
|
||||||
|
|
||||||
class BoardLayout{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief BoardLayout COnstructor
|
|
||||||
*/
|
|
||||||
BoardLayout(uint8_t boardWidth, uint8_t boardLength, uint8_t boardHeight, CubeStack ** stacks) :
|
|
||||||
boardWidth(boardWidth),
|
|
||||||
boardLength(boardLength),
|
|
||||||
boardHeight(boardHeight),
|
|
||||||
stacks(stacks)
|
|
||||||
{
|
|
||||||
this->lastBoardState = new uint16_t[boardWidth * boardLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if our board state has changed
|
|
||||||
* @return true if the board state has changed, false otherwise
|
|
||||||
*/
|
|
||||||
bool BoardStateHasChanged();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Number of Stacks
|
|
||||||
* @return the number of stacks
|
|
||||||
*/
|
|
||||||
uint8_t GetNumberStacks();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the LED Colors
|
|
||||||
* @param stackNum the stack index you would like to address.
|
|
||||||
* From top left to bottom right, the stack numbers are as follows:
|
|
||||||
* | 0 1 2 |
|
|
||||||
* | 3 4 5 |
|
|
||||||
* | 6 7 8 |
|
|
||||||
* @param Colors the array of colors to set the LEDs in a stack to
|
|
||||||
*/
|
|
||||||
void SetStackColors(uint8_t stackNum, Color * colors);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the board population state
|
|
||||||
* @param boardStateBuffer the buffer to write the board state to. It must be at least boardWidth * boardLength in length
|
|
||||||
*/
|
|
||||||
void GetBoardState(uint16_t * boardStateBuffer);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t boardWidth;
|
|
||||||
uint8_t boardLength;
|
|
||||||
uint8_t boardHeight;
|
|
||||||
/*
|
|
||||||
An array of arrays of stacks
|
|
||||||
[ [stack1, stack2, stack3],
|
|
||||||
[stack4, stack5, stack6],
|
|
||||||
[stack7, stack8, stack9] ]
|
|
||||||
etc
|
|
||||||
*/
|
|
||||||
CubeStack ** stacks;
|
|
||||||
|
|
||||||
// records the last known board state
|
|
||||||
uint16_t * lastBoardState;
|
|
||||||
|
|
||||||
};
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Color.h
|
|
||||||
* @brief This file contains the color struct
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
// store a color
|
|
||||||
struct Color{
|
|
||||||
public:
|
|
||||||
// create a constructor for this struct
|
|
||||||
Color(uint8_t red, uint8_t green, uint8_t blue) :
|
|
||||||
red(red),
|
|
||||||
green(green),
|
|
||||||
blue(blue)
|
|
||||||
{}
|
|
||||||
Color() = default;
|
|
||||||
uint8_t red{0};
|
|
||||||
uint8_t green{0};
|
|
||||||
uint8_t blue{0};
|
|
||||||
};
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#include "CubeStack.h"
|
|
||||||
|
|
||||||
CubeStack::CubeStack(uint16_t ADCPin, uint16_t ledPin, uint8_t numLEDs){
|
|
||||||
this->ADCPin = ADCPin;
|
|
||||||
this->blockLights = *(new Adafruit_NeoPixel(numLEDs*2, ledPin, NEO_GRB + NEO_KHZ800));
|
|
||||||
this->ledColors = new Color[numLEDs];
|
|
||||||
this->numLEDs = numLEDs;
|
|
||||||
|
|
||||||
// initialize the LED colors to off
|
|
||||||
for(int i = 0; i < numLEDs; i++){
|
|
||||||
this->ledColors[i] = *(new Color(0, 0, 0));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CubeStack::GetNumberCubes(Cube &cube){
|
|
||||||
// read the ADC and return the number of cubes
|
|
||||||
/*
|
|
||||||
0 cubes: 1 : 4095-3400
|
|
||||||
1 cube: 1/2 3400-2500
|
|
||||||
2 cubes: 1/3 2500-1850
|
|
||||||
3 cubes: 1/4 1850-0
|
|
||||||
*/
|
|
||||||
uint16_t value = analogRead(cube.ADCPin);
|
|
||||||
uint16_t lowPassADCRead = static_cast<uint16_t>((static_cast<float>(cube.lastADCReading) * 0.9) + (static_cast<float>(value) * 0.1));
|
|
||||||
|
|
||||||
// temporary definitions to define value ranges:
|
|
||||||
uint16_t zeroCubesHigh = 4095;
|
|
||||||
uint16_t zeroCubesLow = 3400;
|
|
||||||
uint16_t oneCubeLow = 2500;
|
|
||||||
uint16_t twoCubesLow = 1850;
|
|
||||||
uint16_t threeCubesLow = 0;
|
|
||||||
|
|
||||||
uint8_t stackHeight = 0;
|
|
||||||
|
|
||||||
if(lowPassADCRead >= zeroCubesLow && lowPassADCRead <= zeroCubesHigh){
|
|
||||||
stackHeight = 0;
|
|
||||||
}
|
|
||||||
else if(lowPassADCRead >= oneCubeLow){
|
|
||||||
stackHeight = 1;
|
|
||||||
}
|
|
||||||
else if(lowPassADCRead >= twoCubesLow){
|
|
||||||
stackHeight = 2;
|
|
||||||
}
|
|
||||||
else if(lowPassADCRead >= threeCubesLow){
|
|
||||||
stackHeight = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stackHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CubeStack::SendLEDData(){
|
|
||||||
// we always initialize before we do anything because other CubeStacks could be hogging the hardware
|
|
||||||
// between our writes
|
|
||||||
this->blockLights.begin();
|
|
||||||
// set the LED colors
|
|
||||||
for(int i = 0; i < this->numLEDs; i++){
|
|
||||||
this->blockLights.setPixelColor(i*2, this->blockLights.Color(this->ledColors[i].red, this->ledColors[i].green, this->ledColors[i].blue));
|
|
||||||
this->blockLights.setPixelColor((i*2 + 1), this->blockLights.Color(this->ledColors[i].red, this->ledColors[i].green, this->ledColors[i].blue));
|
|
||||||
}
|
|
||||||
this->blockLights.show();
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* @brief this manages a single cube stack and the lighting / detecting of how many cubes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
|
||||||
#include "Color.h"
|
|
||||||
#include "Cube.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace CubeStack{
|
|
||||||
/**
|
|
||||||
* @brief Construct a new Cube Stack object
|
|
||||||
* @param ADCPin the pin that the ADC is connected to
|
|
||||||
* @param ledPin the pin that the LED is connected to
|
|
||||||
*/
|
|
||||||
CubeStack(uint16_t ADCPin, uint16_t ledPin, uint8_t numLEDs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the number of cubes in the stack
|
|
||||||
* @return the number of cubes in the stack
|
|
||||||
*/
|
|
||||||
uint32_t GetNumberCubes(Cube &cube);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sends the LED data to the LED strip
|
|
||||||
*/
|
|
||||||
void SendLEDData();
|
|
||||||
};
|
|
||||||
@@ -4,15 +4,10 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "Cube.h"
|
#include "Cube.h"
|
||||||
|
#include "BoardDriverTypes.h"
|
||||||
namespace BoardDriverTypes{
|
|
||||||
struct CubeStack{
|
|
||||||
uint8_t adcPin;
|
|
||||||
uint8_t ledPin;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <uint32_t NUM_STACKS>
|
template <uint32_t NUM_STACKS>
|
||||||
class BoardDriver{
|
class BoardDriver{
|
||||||
@@ -21,6 +16,8 @@ public:
|
|||||||
BoardDriver(std::array<BoardDriverTypes::CubeStack, NUM_STACKS> &stacks, Adafruit_NeoPixel &pixelController);
|
BoardDriver(std::array<BoardDriverTypes::CubeStack, NUM_STACKS> &stacks, Adafruit_NeoPixel &pixelController);
|
||||||
~BoardDriver() = default;
|
~BoardDriver() = default;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
|
||||||
uint32_t GetNumberCubes(uint32_t numXStacks, uint32_t X_COORD, uint32_t Y_COORD);
|
uint32_t GetNumberCubes(uint32_t numXStacks, uint32_t X_COORD, uint32_t Y_COORD);
|
||||||
uint32_t GetNumberCubes(uint32_t stackIndex);
|
uint32_t GetNumberCubes(uint32_t stackIndex);
|
||||||
|
|
||||||
@@ -38,6 +35,16 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<uint32_t NUM_STACKS>
|
||||||
|
void BoardDriver<NUM_STACKS>::Init(){
|
||||||
|
for(uint32_t i = 0; i < NUM_STACKS; i++){
|
||||||
|
pinMode(this->stacks[i].ledPin, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin doesn't really do anything besides setting the pinmode
|
||||||
|
this->pixelController.begin();
|
||||||
|
}
|
||||||
|
|
||||||
template<uint32_t NUM_STACKS>
|
template<uint32_t NUM_STACKS>
|
||||||
BoardDriver<NUM_STACKS>::BoardDriver(std::array<BoardDriverTypes::CubeStack, NUM_STACKS> &stacks, Adafruit_NeoPixel &pixelController):
|
BoardDriver<NUM_STACKS>::BoardDriver(std::array<BoardDriverTypes::CubeStack, NUM_STACKS> &stacks, Adafruit_NeoPixel &pixelController):
|
||||||
stacks(stacks),
|
stacks(stacks),
|
||||||
@@ -45,11 +52,7 @@ pixelController(pixelController)
|
|||||||
{
|
{
|
||||||
for(uint32_t i = 0; i < NUM_STACKS; i++){
|
for(uint32_t i = 0; i < NUM_STACKS; i++){
|
||||||
this->filteredReadings[i] = 0;
|
this->filteredReadings[i] = 0;
|
||||||
pinMode(this->stacks[i].ledPin, OUTPUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin doesn't really do anything besides setting the pinmode
|
|
||||||
this->pixelController.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint32_t NUM_STACKS>
|
template<uint32_t NUM_STACKS>
|
||||||
10
lib/BoardDriver/BoardDriverTypes.h
Normal file
10
lib/BoardDriver/BoardDriverTypes.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace BoardDriverTypes{
|
||||||
|
struct CubeStack{
|
||||||
|
uint8_t adcPin;
|
||||||
|
uint8_t ledPin;
|
||||||
|
};
|
||||||
|
};
|
||||||
102
lib/BoardManager/BoardManager.h
Normal file
102
lib/BoardManager/BoardManager.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Board.h"
|
||||||
|
#include "BoardDriver.h"
|
||||||
|
#include "Vector3D.h"
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
class BoardManager{
|
||||||
|
public:
|
||||||
|
BoardManager(BoardDriver<BOARD_WIDTH*BOARD_LENGTH> &boardDriver):
|
||||||
|
driver(boardDriver){};
|
||||||
|
|
||||||
|
~BoardManager() = default;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
|
||||||
|
void SetCubeColor(const V3D &position, const V3D &color);
|
||||||
|
|
||||||
|
void SetColumnColor(const V3D &position, const V3D &color);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill a column along any axis with a color
|
||||||
|
* @param column .z specifies the normal direction of the plane (see PLANE_NORMAL), and
|
||||||
|
* the x,y values specify the location of the column in that plane
|
||||||
|
* to fill. IE To fill one stack at 0,2 I would say give V3D(0,2,PLANE_NORMAL::Z)
|
||||||
|
* @param color the color you want to fill the column with
|
||||||
|
*/
|
||||||
|
void FillColumnColor(const V3D &column, const V3D &color);
|
||||||
|
|
||||||
|
bool HasBoardChanged(){return this->hasBoardChanged;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BoardDriver<BOARD_WIDTH*BOARD_LENGTH> &driver;
|
||||||
|
Board<BOARD_DIMS> board{};
|
||||||
|
|
||||||
|
bool hasBoardChanged{false};
|
||||||
|
|
||||||
|
void updateBoardColors(const V3D &column);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
void BoardManager<BOARD_DIMS>::Init(){
|
||||||
|
this->driver.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
void BoardManager<BOARD_DIMS>::Update(){
|
||||||
|
// update the occupied cubes on the board and the cube colors
|
||||||
|
for(uint32_t x = 0; x < BOARD_DIMS.x; x++){
|
||||||
|
for(uint32_t y = 0; y < BOARD_DIMS.y; y++){
|
||||||
|
uint32_t numCubes{this->driver.GetNumberCubes(i)};
|
||||||
|
for(uint32_t z = 0; z < BOARD_DIMS.z; z++){
|
||||||
|
V3D cubePosition{x, y, z};
|
||||||
|
bool isOccupied{z < numCubes};
|
||||||
|
if(this->board.SetCubeOccupation(cubePosition, i < numCubes) != isOccupied){
|
||||||
|
this->board.boardStateHasChanged = true;
|
||||||
|
}
|
||||||
|
cubePosition.z = Board::PLANE_NORMAL::Z;
|
||||||
|
this->driver.UpdateStackLEDs(i, this->board.SliceBoard(cubePosition), BOARD_DIMS.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the colors
|
||||||
|
for(uint32_t i = 0; i < BOARD_DIMS.x * BOARD_DIMS.y; i++){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
void BoardManager<BOARD_DIMS>::updateBoardColors(const V3D &column){
|
||||||
|
Cube * cubeSlice{this->board.SliceBoard(column)};
|
||||||
|
uint32_t numCubes{0};
|
||||||
|
|
||||||
|
switch(column.z){
|
||||||
|
case Board::PLANE_NORMAL::X:
|
||||||
|
numCubes = BOARD_DIMS.x;
|
||||||
|
break;
|
||||||
|
case Board::PLANE_NORMAL::Y:
|
||||||
|
numCubes = BOARD_DIMS.y;
|
||||||
|
break;
|
||||||
|
case Board::PLANE_NORMAL::Z:
|
||||||
|
numCubes = BOARD_DIMS.z;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->driver.UpdateStackLEDs(BOARD_DIMS.x, cubeSlice, numCubes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <V3D &BOARD_DIMS>
|
||||||
|
void BoardManager<BOARD_DIMS>::SetCubeColor(const V3D &position, const V3D &color){
|
||||||
|
this->board.SetCubeColor(position, color);
|
||||||
|
V3D slice{position.x, position.y, Board::PLANE_NORMAL::Z};
|
||||||
|
this->updateBoardColors(slice);
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#include "ColorManager.h"
|
|
||||||
|
|
||||||
void ColorManager::Update(){
|
|
||||||
if(!(this->enabled)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// go through our colors and have them fade from r->g->b->r
|
|
||||||
for(uint8_t i = 0; i < 9; i++){
|
|
||||||
for(uint8_t j = 0; j < 3; j++){
|
|
||||||
Color * color = this->colors[i][j];
|
|
||||||
// fade from red to green
|
|
||||||
if(color->red > 0 && color->green >= 0 && color->blue == 0){
|
|
||||||
color->red--;
|
|
||||||
color->green++;
|
|
||||||
}
|
|
||||||
// fade from green to blue
|
|
||||||
else if(color->green > 0 && color->blue >= 0 && color->red == 0){
|
|
||||||
color->green--;
|
|
||||||
color->blue++;
|
|
||||||
}
|
|
||||||
// fade from blue to red
|
|
||||||
else if(color->blue > 0 && color->red >= 0 && color->green == 0){
|
|
||||||
color->blue--;
|
|
||||||
color->red++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the colors
|
|
||||||
for(uint8_t i = 0; i < 9; i++){
|
|
||||||
Color temp_colors[3] = {*(this->colors[i][0]), *(this->colors[i][1]), *(this->colors[i][2])};
|
|
||||||
this->board->SetStackColors(i, temp_colors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColorManager::Enable(bool enable){
|
|
||||||
this->enabled = enable;
|
|
||||||
|
|
||||||
if(this->enabled == false){
|
|
||||||
// set all the colors to black
|
|
||||||
Color black(0, 0, 0);
|
|
||||||
Color temp_colors[3] = {black, black, black};
|
|
||||||
// set the colors
|
|
||||||
for(uint8_t i = 0; i < 9; i++){
|
|
||||||
this->board->SetStackColors(i, temp_colors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file ColorManager.h
|
|
||||||
* @brief Generate pretty colors for the board and make it do something when unity isn't controlling it
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BoardLayout.h"
|
|
||||||
#include "Color.h"
|
|
||||||
|
|
||||||
class ColorManager{
|
|
||||||
public:
|
|
||||||
ColorManager(BoardLayout * board) :
|
|
||||||
board(board)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Allows the color manager to update the board colors
|
|
||||||
*/
|
|
||||||
void Update();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enables or disables the color manager
|
|
||||||
* @param enable true to enable, false to disable
|
|
||||||
*/
|
|
||||||
void Enable(bool enable);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
BoardLayout * board;
|
|
||||||
bool enabled{true};
|
|
||||||
|
|
||||||
Color * colors[9][3] = {
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)},
|
|
||||||
{new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255)}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#include "Cube.h"
|
|
||||||
|
|
||||||
Cube::Cube(uint8_t ADCPin, uint8_t ledPin)
|
|
||||||
: ADCPin(ADCPin),
|
|
||||||
ledPin(ledPin)
|
|
||||||
{
|
|
||||||
Color black(0,0,0);
|
|
||||||
this->SetColor(black);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cube::Cube(uint8_t ADCPin, uint8_t ledPin, const Color &color)
|
|
||||||
: ADCPin(ADCPin),
|
|
||||||
ledPin(ledPin)
|
|
||||||
{
|
|
||||||
this->SetColor(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::SetColor(const Color &color){
|
|
||||||
this->color.red = color.red;
|
|
||||||
this->color.green = color.green;
|
|
||||||
this->color.blue = color.blue;
|
|
||||||
}
|
|
||||||
@@ -5,17 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Color.h"
|
#include "Vector3D.h"
|
||||||
|
|
||||||
class Cube{
|
class Cube{
|
||||||
public:
|
public:
|
||||||
Cube();
|
V3D color;
|
||||||
Cube();
|
|
||||||
|
|
||||||
void SetColor(const Color &color);
|
|
||||||
|
|
||||||
uint16_t lastADCReading{0};
|
|
||||||
|
|
||||||
Color color;
|
|
||||||
bool isOccupied{false};
|
bool isOccupied{false};
|
||||||
};
|
};
|
||||||
@@ -11,9 +11,7 @@
|
|||||||
// project specific libraries
|
// project specific libraries
|
||||||
#include "BluetoothSerial.h"
|
#include "BluetoothSerial.h"
|
||||||
#include "SerialMessage.h"
|
#include "SerialMessage.h"
|
||||||
#include "BoardLayout.h"
|
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
#include "ColorManager.h"
|
|
||||||
#include "GlobalPrint.h"
|
#include "GlobalPrint.h"
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
@@ -29,10 +27,6 @@ uint32_t boardStateMaxUpdatePeriod{34}; // this is a little slower than 30fps
|
|||||||
// BluetoothSerial SerialBT;
|
// BluetoothSerial SerialBT;
|
||||||
// BluetoothSerialMessage serialMessageBT(&SerialBT);
|
// BluetoothSerialMessage serialMessageBT(&SerialBT);
|
||||||
SerialMessage<500, 10> serialMessage(&Serial);
|
SerialMessage<500, 10> serialMessage(&Serial);
|
||||||
BoardLayout board(BOARD_WIDTH, BOARD_LENGTH, BOARD_HEIGHT, stacks);
|
|
||||||
|
|
||||||
// Temporary thing until we can get bluetooth color management working on the quest
|
|
||||||
ColorManager colorManager(&board);
|
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// ----------------- FUNCTIONS ----------------------
|
// ----------------- FUNCTIONS ----------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user