From 3e4f0124db3f65cabe68acbffb4dd1789235a767 Mon Sep 17 00:00:00 2001 From: Quinn Date: Thu, 22 Aug 2024 21:07:32 -0400 Subject: [PATCH] Completed the board manager rewrite --- include/BOARD-DEFINITIONS.h | 66 +++- include/Vector3D.h | 54 +++ lib/Animator/Animator.h | 353 ++++++++++++++++++ lib/Board/Board.h | 112 ++++-- lib/Board/BoardLayout.cpp | 38 -- lib/Board/BoardLayout.h | 69 ---- lib/Board/Color.h | 22 -- lib/Board/CubeStack.cpp | 61 --- lib/Board/CubeStack.h | 31 -- .../BoardDriver.h | 25 +- lib/BoardDriver/BoardDriverTypes.h | 10 + lib/BoardManager/BoardManager.h | 102 +++++ lib/ColorManager/ColorManager.cpp | 48 --- lib/ColorManager/ColorManager.h | 44 --- lib/Cube/Cube.cpp | 22 -- lib/Cube/Cube.h | 11 +- src/main.cpp | 6 - 17 files changed, 666 insertions(+), 408 deletions(-) create mode 100644 include/Vector3D.h create mode 100644 lib/Animator/Animator.h delete mode 100644 lib/Board/BoardLayout.cpp delete mode 100644 lib/Board/BoardLayout.h delete mode 100644 lib/Board/Color.h delete mode 100644 lib/Board/CubeStack.cpp delete mode 100644 lib/Board/CubeStack.h rename lib/{BoardReader => BoardDriver}/BoardDriver.h (95%) create mode 100644 lib/BoardDriver/BoardDriverTypes.h create mode 100644 lib/BoardManager/BoardManager.h delete mode 100644 lib/ColorManager/ColorManager.cpp delete mode 100644 lib/ColorManager/ColorManager.h delete mode 100644 lib/Cube/Cube.cpp diff --git a/include/BOARD-DEFINITIONS.h b/include/BOARD-DEFINITIONS.h index 0c80af9..d112779 100644 --- a/include/BOARD-DEFINITIONS.h +++ b/include/BOARD-DEFINITIONS.h @@ -4,32 +4,60 @@ */ #pragma once +#include +#include #include "PINOUT.h" -#include "CubeStack.h" +#include "BoardDriverTypes.h" // define the physical dimensions of the board -#define BOARD_WIDTH 3 -#define BOARD_LENGTH 3 -#define BOARD_HEIGHT 3 +static constexpr uint32_t BOARD_WIDTH{3}; +static constexpr uint32_t BOARD_LENGTH{3}; +static constexpr uint32_t BOARD_HEIGHT{3}; // 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 -CubeStack stack1(STACK1_ADC_PIN, STACK1_LED_PIN, BOARD_HEIGHT); -CubeStack stack2(STACK2_ADC_PIN, STACK2_LED_PIN, BOARD_HEIGHT); -CubeStack stack3(STACK3_ADC_PIN, STACK3_LED_PIN, BOARD_HEIGHT); -CubeStack stack4(STACK4_ADC_PIN, STACK4_LED_PIN, BOARD_HEIGHT); -CubeStack stack5(STACK5_ADC_PIN, STACK5_LED_PIN, BOARD_HEIGHT); -CubeStack stack6(STACK6_ADC_PIN, STACK6_LED_PIN, BOARD_HEIGHT); -CubeStack stack7(STACK7_ADC_PIN, STACK7_LED_PIN, BOARD_HEIGHT); -CubeStack stack8(STACK8_ADC_PIN, STACK8_LED_PIN, BOARD_HEIGHT); -CubeStack stack9(STACK9_ADC_PIN, STACK9_LED_PIN, BOARD_HEIGHT); +static BoardDriverTypes::CubeStack stack1{ + .adcPin=STACK1_ADC_PIN, + .ledPin=STACK1_LED_PIN +}; +static BoardDriverTypes::CubeStack stack2{ + .adcPin=STACK2_ADC_PIN, + .ledPin=STACK2_LED_PIN +}; +static BoardDriverTypes::CubeStack stack3{ + .adcPin=STACK3_ADC_PIN, + .ledPin=STACK3_LED_PIN +}; +static BoardDriverTypes::CubeStack stack4{ + .adcPin=STACK4_ADC_PIN, + .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 +}; -// define the array of stacks -CubeStack * stacks[] = { - &stack1, &stack2, &stack3, - &stack4, &stack5, &stack6, - &stack7, &stack8, &stack9 +static std::array stacks{ + stack1, stack2, stack3, + stack4, stack5, stack6, + stack7, stack8, stack9 }; \ No newline at end of file diff --git a/include/Vector3D.h b/include/Vector3D.h new file mode 100644 index 0000000..1858f3b --- /dev/null +++ b/include/Vector3D.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +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; +}; \ No newline at end of file diff --git a/lib/Animator/Animator.h b/lib/Animator/Animator.h new file mode 100644 index 0000000..37f384f --- /dev/null +++ b/lib/Animator/Animator.h @@ -0,0 +1,353 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 frame; + FillInterpolation fillInterpolation; + FrameInterpolation frameInterpolation; + std::chrono::milliseconds delay; +}; + +typedef std::array, Y_SIZE>, X_SIZE> Frame; + + +void StartAnimation(const std::vector &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 & 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(std::numeric_limits::max())}; + // scale the key frame values down to be within board coordinates + float keyFrame_X = static_cast(this->X_SIZE) * static_cast(keyFramePosition.x) / maxValue; + float keyFrame_Y = static_cast(this->Y_SIZE) * static_cast(keyFramePosition.y) / maxValue; + float keyFrame_Z = static_cast(this->Z_SIZE) * static_cast(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(keyFrame_X); + } + else{ + returnValue.x = static_cast(keyFrame_X) + 1; + } + + if(keyFrame_Y - std::floor(keyFrame_Y) < 0.5f){ + returnValue.y = static_cast(keyFrame_Y); + } + else{ + returnValue.y = static_cast(keyFrame_Y) + 1; + } + + if(keyFrame_Z - std::floor(keyFrame_Z) < 0.5f){ + returnValue.z = static_cast(keyFrame_Z); + } + else{ + returnValue.z = static_cast(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((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((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::max()}; + + Animation::Cell &CreateCell(float x_percent, float y_percent, float z_percent, V3D &color){ + float continuousMaxValue{static_cast(std::numeric_limits::max())}; + Animation::Cell cell{ + .position = V3D{ + static_cast(continuousMaxValue*x_percent), + static_cast(continuousMaxValue*y_percent), + static_cast(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 testAnimationSequence{ + noFillFrame, + closestColorFrame, + linearFillFrame, + squareFillFrame, + noFillFadeFrame, + closestFillFadeFrame, + linearFillFadeFrame, + squareFillFadeFrame + }; +} + + diff --git a/lib/Board/Board.h b/lib/Board/Board.h index 8ad0a65..4856f96 100644 --- a/lib/Board/Board.h +++ b/lib/Board/Board.h @@ -2,20 +2,25 @@ #include #include +#include #include "Cube.h" -#include "Color.h" +#include "Vector3D.h" -template +template class Board{ public: + enum PLANE_NORMAL : uint32_t{ + X = 0, + Y, + Z + }; + Board(); ~Board() = default; - constexpr uint32_t GetXSize() const{return X_SIZE;} - constexpr uint32_t GetYSize() const{return Y_SIZE;} - constexpr uint32_t GetZSize() const {return Z_SIZE;} - constexpr uint32_t GetNumberCubes() const{return X_SIZE * Y_SIZE * Z_SIZE;} + constexpr V3D &GetSize() const{return BOARD_DIMS;} + constexpr uint32_t GetNumberCubes() const{return BOARD_DIMS.x * BOARD_DIMS.y * BOARD_DIMS.z;} /** * @brief Returns a string in the format: @@ -28,13 +33,25 @@ class Board{ /** * @brief Returns a bool array representing the board */ - std::array &LinearizeBoard() const; + std::array &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: // this is a 3d array of cubes to represent the board. Good luck visualizing it @@ -53,13 +70,13 @@ class Board{ bool boardStateHasChanged; }; -template -Board::Board(){ +template +Board::Board(){ this->FillColor(Color(0,0,0)); } -template -void Board::ToStackString(String &stringBuffer) const{ +template +void Board::ToStackString(String &stringBuffer) const{ std::array linearizedBoard = this->LinearizeBoard(); stringBuffer += "!" + String(linearizedBoard[0]); @@ -71,10 +88,10 @@ void Board::ToStackString(String &stringBuffer) const{ stringBuffer += ";"; } -template -std::array & Board::LinearizeBoard() const{ +template +std::array & Board::LinearizeBoard() const{ // convert the board into one array where each entry represents the height of one stack - std::array linearizedBoard; + std::array linearizedBoard; 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++){ @@ -86,24 +103,63 @@ std::array & Board::Linearize return linearizedBoard; } -template -void Board::Update(){ - -} - -template -void Board::FillColor(const Color &color){ +template +void Board::FillColor(const V3D &color){ 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++){ - this->cubes[x][y][z].SetColor(color); + this->cubes[x][y][z].color = color; } } } } -template -void Board::SetCubeColor(uint32_t x_coord, uint32_t y_cord, uint32_t z_cord, const Color &color){ - this->cubes[x][y][z].SetColor(color); +template +void Board::SetCubeColor(const V3D &position, const V3D &color){ + this->cubes[position.x][position.y][position.z].color = color; +} + +template +void Board::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 +Cube * Board::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 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(); } diff --git a/lib/Board/BoardLayout.cpp b/lib/Board/BoardLayout.cpp deleted file mode 100644 index b4fee9e..0000000 --- a/lib/Board/BoardLayout.cpp +++ /dev/null @@ -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(); - } -} - diff --git a/lib/Board/BoardLayout.h b/lib/Board/BoardLayout.h deleted file mode 100644 index d8684de..0000000 --- a/lib/Board/BoardLayout.h +++ /dev/null @@ -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; - -}; \ No newline at end of file diff --git a/lib/Board/Color.h b/lib/Board/Color.h deleted file mode 100644 index 434b105..0000000 --- a/lib/Board/Color.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file Color.h - * @brief This file contains the color struct -*/ - -#pragma once -#include - -// 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}; -}; \ No newline at end of file diff --git a/lib/Board/CubeStack.cpp b/lib/Board/CubeStack.cpp deleted file mode 100644 index 7af12ab..0000000 --- a/lib/Board/CubeStack.cpp +++ /dev/null @@ -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((static_cast(cube.lastADCReading) * 0.9) + (static_cast(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(); -} \ No newline at end of file diff --git a/lib/Board/CubeStack.h b/lib/Board/CubeStack.h deleted file mode 100644 index 8a343e4..0000000 --- a/lib/Board/CubeStack.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @brief this manages a single cube stack and the lighting / detecting of how many cubes -*/ - -#pragma once - -#include -#include -#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(); -}; \ No newline at end of file diff --git a/lib/BoardReader/BoardDriver.h b/lib/BoardDriver/BoardDriver.h similarity index 95% rename from lib/BoardReader/BoardDriver.h rename to lib/BoardDriver/BoardDriver.h index 5ff3d09..649b5c9 100644 --- a/lib/BoardReader/BoardDriver.h +++ b/lib/BoardDriver/BoardDriver.h @@ -4,15 +4,10 @@ #include #include +#include #include "Cube.h" - -namespace BoardDriverTypes{ - struct CubeStack{ - uint8_t adcPin; - uint8_t ledPin; - } -}; +#include "BoardDriverTypes.h" template class BoardDriver{ @@ -21,6 +16,8 @@ public: BoardDriver(std::array &stacks, Adafruit_NeoPixel &pixelController); ~BoardDriver() = default; + void Init(); + uint32_t GetNumberCubes(uint32_t numXStacks, uint32_t X_COORD, uint32_t Y_COORD); uint32_t GetNumberCubes(uint32_t stackIndex); @@ -38,6 +35,16 @@ private: } }; +template +void BoardDriver::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 BoardDriver::BoardDriver(std::array &stacks, Adafruit_NeoPixel &pixelController): stacks(stacks), @@ -45,11 +52,7 @@ pixelController(pixelController) { for(uint32_t i = 0; i < NUM_STACKS; i++){ this->filteredReadings[i] = 0; - pinMode(this->stacks[i].ledPin, OUTPUT); } - - // begin doesn't really do anything besides setting the pinmode - this->pixelController.begin(); } template diff --git a/lib/BoardDriver/BoardDriverTypes.h b/lib/BoardDriver/BoardDriverTypes.h new file mode 100644 index 0000000..89382fb --- /dev/null +++ b/lib/BoardDriver/BoardDriverTypes.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace BoardDriverTypes{ + struct CubeStack{ + uint8_t adcPin; + uint8_t ledPin; + }; +}; \ No newline at end of file diff --git a/lib/BoardManager/BoardManager.h b/lib/BoardManager/BoardManager.h new file mode 100644 index 0000000..0639557 --- /dev/null +++ b/lib/BoardManager/BoardManager.h @@ -0,0 +1,102 @@ +#pragma once + +#include "Board.h" +#include "BoardDriver.h" +#include "Vector3D.h" + +template +class BoardManager{ + public: + BoardManager(BoardDriver &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 &driver; + Board board{}; + + bool hasBoardChanged{false}; + + void updateBoardColors(const V3D &column); + +}; + +template +void BoardManager::Init(){ + this->driver.Init(); +} + +template +void BoardManager::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 +void BoardManager::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 +void BoardManager::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); +} \ No newline at end of file diff --git a/lib/ColorManager/ColorManager.cpp b/lib/ColorManager/ColorManager.cpp deleted file mode 100644 index 95f4294..0000000 --- a/lib/ColorManager/ColorManager.cpp +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/lib/ColorManager/ColorManager.h b/lib/ColorManager/ColorManager.h deleted file mode 100644 index 375c4bf..0000000 --- a/lib/ColorManager/ColorManager.h +++ /dev/null @@ -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)} - }; -}; \ No newline at end of file diff --git a/lib/Cube/Cube.cpp b/lib/Cube/Cube.cpp deleted file mode 100644 index 7a271ba..0000000 --- a/lib/Cube/Cube.cpp +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/lib/Cube/Cube.h b/lib/Cube/Cube.h index 516ac77..8e2bc57 100644 --- a/lib/Cube/Cube.h +++ b/lib/Cube/Cube.h @@ -5,17 +5,10 @@ #pragma once -#include "Color.h" +#include "Vector3D.h" class Cube{ public: - Cube(); - Cube(); - - void SetColor(const Color &color); - - uint16_t lastADCReading{0}; - - Color color; + V3D color; bool isOccupied{false}; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index dd49238..dbdb8bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,9 +11,7 @@ // project specific libraries #include "BluetoothSerial.h" #include "SerialMessage.h" -#include "BoardLayout.h" #include "Color.h" -#include "ColorManager.h" #include "GlobalPrint.h" // -------------------------------------------------- @@ -29,10 +27,6 @@ uint32_t boardStateMaxUpdatePeriod{34}; // this is a little slower than 30fps // BluetoothSerial SerialBT; // BluetoothSerialMessage serialMessageBT(&SerialBT); 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 ----------------------