Add cmc menu

This commit is contained in:
shylie 2026-01-13 02:36:25 -05:00
parent 8954e84608
commit fb000d7275
8 changed files with 289 additions and 124 deletions

View File

@ -16,6 +16,7 @@ add_executable(mtgcard
src/icons.cpp
src/menu.cpp
src/manamenu.cpp
src/cmcmenu.cpp
src/usb_descriptors.c

View File

@ -19,6 +19,7 @@ public:
uint16_t height);
PixelStream pixels();
void clear();
private:
uint8_t sck, tx, cs, dc;

View File

@ -3,6 +3,8 @@
#include "display.h"
#include <cmath>
#include <new>
#include <pico/stdlib.h>
class Menu
@ -14,6 +16,7 @@ public:
virtual ~Menu() = default;
virtual void onTick(float dt) = 0;
virtual void onResume() = 0;
virtual void onLeftPressed() = 0;
virtual void onLeftHeld() = 0;
@ -28,6 +31,66 @@ protected:
lib::Display* const display;
};
namespace ease
{
// adapted from easings.net
inline float inOutElastic(float x)
{
constexpr float c5 = (2 * 3.1415926f) / 4.5f;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
if (x < 0.5)
{
return -powf(2, 20 * x - 10) * sinf((20 * x - 11.125f) * c5) / 2.0f;
}
else
{
return powf(2, -20 * x + 10) * sinf((20 * x - 11.25f) * c5) / 2.0f + 1;
}
}
// adapted from easings.net
inline float outElastic(float x)
{
constexpr float c4 = (2 * 3.1415926f) / 3;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
return powf(2, -10 * x) * sinf((x * 10 - 0.75f) * c4) + 1;
}
// adapted from easings.net
inline float inElastic(float x)
{
constexpr float c4 = (2 * 3.1415926f) / 3;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
return -powf(2, 10 * x - 10) * sinf((x * 10 - 10.75f) * c4);
}
}
namespace menus
{
@ -38,6 +101,7 @@ public:
~ManaMenu() override = default;
void onTick(float dt) override;
void onResume() override;
void onLeftPressed() override;
void onLeftHeld() override;
@ -51,11 +115,68 @@ public:
private:
uint8_t current;
uint8_t previous;
bool going_left;
bool going_right;
float progress;
};
constexpr size_t MAX_MENU_SIZE = sizeof(ManaMenu);
class CMCMenu : public Menu
{
public:
explicit CMCMenu(lib::Display& display, uint8_t color);
~CMCMenu() override = default;
void onTick(float dt) override;
void onResume() override;
void onLeftPressed() override;
void onLeftHeld() override;
void onMenuPressed() override;
void onMenuHeld() override;
void onRightPressed() override;
void onRightHeld() override;
private:
uint8_t color;
uint8_t current;
uint8_t previous;
float progress;
};
namespace detail
{
union Menus
{
ManaMenu mana;
CMCMenu cmc;
};
}
constexpr size_t MAX_MENU_COUNT = 5;
extern uint8_t MENU_MEMORY[sizeof(detail::Menus) * MAX_MENU_COUNT];
extern int8_t current_menu;
inline Menu* get_current_menu()
{
return reinterpret_cast<Menu*>(MENU_MEMORY
+ current_menu * sizeof(detail::Menus));
}
template <typename T, typename... Args>
inline void push_menu(lib::Display& display, Args&&... args)
{
current_menu += 1;
new (get_current_menu()) T(display, args...);
}
inline void pop_menu()
{
get_current_menu()->~Menu();
current_menu -= 1;
get_current_menu()->onResume();
}
}

98
src/cmcmenu.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "icons.h"
#include "menu.h"
#include "pixelstream.h"
using namespace menus;
CMCMenu::CMCMenu(lib::Display& display, uint8_t color) :
Menu(display),
color(color),
current(2),
previous(1),
progress(0.7f)
{
display.clear();
}
void CMCMenu::onTick(float dt)
{
constexpr float CENTER_X = 120;
constexpr float CENTER_Y = 160;
constexpr float DT_SCALE = 1.4f;
constexpr uint8_t STEP = 48;
constexpr uint8_t ERASE_DATA[39 * 5 * 3] = {};
if (progress < 1.0f)
{
for (int8_t dcmc = -1; dcmc <= 1; dcmc++)
{
const int8_t dir = (previous > current ? 1 : -1);
const int8_t cmc_to_draw = current + dir * dcmc;
if (cmc_to_draw < 0 || cmc_to_draw > 20)
{
continue;
}
const float offset
= dir * ease::outElastic(progress) * STEP + (dir * (dcmc - 1)) * STEP;
// left
{
display->set_update_area(CENTER_X - 20 + offset, CENTER_Y - 20, 5, 39);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 39 * 5 * 3);
}
// right
{
display->set_update_area(CENTER_X - 20 + offset + 37, CENTER_Y - 20, 5,
39);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 39 * 5 * 3);
}
// draw new
{
display->set_update_area(CENTER_X - 15 + offset, CENTER_Y - 15, 31,
32);
auto pixels = display->pixels();
pixels.write(icon::NUMBER_ICONS[cmc_to_draw], icon::LENGTH);
}
}
progress += dt * DT_SCALE;
if (progress >= 0.75f)
{
progress = 1.0f;
}
}
}
void CMCMenu::onResume() {}
void CMCMenu::onLeftPressed()
{
if (progress >= 1 && current > 0)
{
previous = current;
current -= 1;
progress = 0;
}
}
void CMCMenu::onLeftHeld() {}
void CMCMenu::onMenuPressed() { pop_menu(); }
void CMCMenu::onMenuHeld() {}
void CMCMenu::onRightPressed()
{
if (progress >= 1 && current < 20)
{
previous = current;
current += 1;
progress = 0;
}
}
void CMCMenu::onRightHeld() {}

View File

@ -100,6 +100,17 @@ void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width,
PixelStream Display::pixels() { return PixelStream(*this); }
void Display::clear()
{
set_update_area(0, 0, 240, 320);
auto stream = pixels();
for (int i = 0; i < 240 * 320; i++)
{
stream.write(0, 0, 0);
}
}
void Display::send_command(uint8_t command)
{
send_command(command, nullptr, 0);

View File

@ -4,7 +4,6 @@
#include "menu.h"
#include "pixelstream.h"
#include <new>
#include <pico/binary_info.h>
#include <pico/stdlib.h>
#include <tusb.h>
@ -27,9 +26,9 @@ bi_decl(bi_ptr_int32(0x1111, 1, FLASH_RX, 28));
bi_decl(bi_ptr_int32(0x1111, 1, FLASH_CS, 29));
bi_decl(bi_program_feature_group(0x1111, 2, "Buttons"));
bi_decl(bi_ptr_int32(0x1111, 2, BUTTON_LEFT, 20));
bi_decl(bi_ptr_int32(0x1111, 2, BUTTON_LEFT, 8));
bi_decl(bi_ptr_int32(0x1111, 2, BUTTON_MIDDLE, 0));
bi_decl(bi_ptr_int32(0x1111, 2, BUTTON_RIGHT, 8));
bi_decl(bi_ptr_int32(0x1111, 2, BUTTON_RIGHT, 20));
Display display(DISPLAY_SCK, DISPLAY_TX, DISPLAY_CS, DISPLAY_DC, 120000000);
Flash flash(FLASH_SCK, FLASH_TX, FLASH_RX, FLASH_CS, 70000000);
@ -46,7 +45,6 @@ enum class RX
struct
{
struct
{
uint8_t buffer[32];
@ -68,27 +66,6 @@ struct
int32_t card_index = 0;
} rx;
uint8_t
MENU_MEMORY[menus::MAX_MENU_SIZE * 5]; // menu stack is at most 5 menus deep
int8_t current_menu = -1;
Menu* get_current_menu()
{
return reinterpret_cast<Menu*>(MENU_MEMORY
+ current_menu * menus::MAX_MENU_SIZE);
}
template <typename T> void push_menu()
{
current_menu += 1;
new (get_current_menu()) T(display);
}
void pop_menu()
{
get_current_menu()->~Menu();
current_menu -= 1;
}
}
int main()
@ -113,7 +90,7 @@ int main()
absolute_time_t last_press = get_absolute_time();
absolute_time_t previous_time = last_press;
push_menu<menus::ManaMenu>();
menus::push_menu<menus::ManaMenu>(display);
while (true)
{
@ -121,31 +98,31 @@ int main()
float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f;
if (!gpio_get(BUTTON_LEFT)
&& absolute_time_diff_us(last_press, current) > 1000 * 150)
&& absolute_time_diff_us(last_press, current) > 1000 * 1000)
{
get_current_menu()->onLeftPressed();
menus::get_current_menu()->onLeftPressed();
last_press = current;
}
if (!gpio_get(BUTTON_MIDDLE)
&& absolute_time_diff_us(last_press, current) > 1000 * 150)
&& absolute_time_diff_us(last_press, current) > 1000 * 1000)
{
get_current_menu()->onMenuPressed();
menus::get_current_menu()->onMenuPressed();
last_press = current;
}
if (!gpio_get(BUTTON_RIGHT)
&& absolute_time_diff_us(last_press, current) > 1000 * 150)
&& absolute_time_diff_us(last_press, current) > 1000 * 1000)
{
get_current_menu()->onRightPressed();
menus::get_current_menu()->onRightPressed();
last_press = current;
}
tud_task();
get_current_menu()->onTick(dt);
menus::get_current_menu()->onTick(dt);
previous_time = current;
}

View File

@ -9,62 +9,6 @@ namespace
constexpr uint8_t COLORS[] = { 0, 0b1, 0b10, 0b100, 0b1000, 0b10000 };
// adapted from easings.net
float easeInOutElastic(float x)
{
constexpr float c5 = (2 * 3.1415926f) / 4.5f;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
if (x < 0.5)
{
return -powf(2, 20 * x - 10) * sinf((20 * x - 11.125f) * c5) / 2.0f;
}
else
{
return powf(2, -20 * x + 10) * sinf((20 * x - 11.25f) * c5) / 2.0f + 1;
}
}
// adapted from easings.net
float easeOutElastic(float x)
{
constexpr float c4 = (2 * 3.1415926f) / 3;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
return powf(2, -10 * x) * sinf((x * 10 - 0.75f) * c4) + 1;
}
// adapted from easings.net
float easeInElastic(float x)
{
constexpr float c4 = (2 * 3.1415926f) / 3;
if (x == 0)
{
return 0;
}
if (x == 1)
{
return 1;
}
return -powf(2, 10 * x - 10) * sinf((x * 10 - 10.75f) * c4);
}
}
using namespace menus;
@ -73,8 +17,8 @@ ManaMenu::ManaMenu(lib::Display& display) :
Menu(display),
current(0),
previous(0),
going_left(false),
progress(0.99f)
going_right(true),
progress(0.7f)
{
}
@ -93,7 +37,7 @@ void ManaMenu::onTick(float dt)
if (progress < 1.0f)
{
const float offset
= (1.0f - easeOutElastic(progress)) * MAX_OFFSET + RADIUS;
= (1.0f - ease::outElastic(progress)) * MAX_OFFSET + RADIUS;
// erase old
{
@ -125,8 +69,8 @@ void ManaMenu::onTick(float dt)
else if (progress < 2.0f)
{
const float offset
= (going_left ? -1 : 1) * STEP * easeOutElastic(progress - 1)
+ STEP / 2.0f + (going_left ? 2 * STEP : 0);
= (going_right ? -1 : 1) * STEP * ease::outElastic(progress - 1)
+ STEP / 2.0f + (going_right ? 2 * STEP : 0);
// erase previous
for (int i = 0; i < sizeof(COLORS) / sizeof(*COLORS); i++)
@ -193,7 +137,8 @@ void ManaMenu::onTick(float dt)
}
else if (progress < 3.0f)
{
const float offset = easeOutElastic(progress - 2.0f) * MAX_OFFSET + RADIUS;
const float offset
= ease::outElastic(progress - 2.0f) * MAX_OFFSET + RADIUS;
// erase old
{
@ -223,6 +168,12 @@ void ManaMenu::onTick(float dt)
}
}
}
void ManaMenu::onResume()
{
display->clear();
progress = 0.7f;
going_right = true;
}
void ManaMenu::onLeftPressed()
{
@ -231,33 +182,7 @@ void ManaMenu::onLeftPressed()
return;
}
going_left = true;
previous = current;
if (current == 0)
{
current = 5;
}
else
{
current -= 1;
}
progress = 0.0f;
}
void ManaMenu::onLeftHeld() {}
void ManaMenu::onMenuPressed() {}
void ManaMenu::onMenuHeld() {}
void ManaMenu::onRightPressed()
{
if (progress < 3.0f)
{
return;
}
going_left = false;
going_right = false;
previous = current;
if (current == 5)
@ -271,4 +196,30 @@ void ManaMenu::onRightPressed()
progress = 0.0f;
}
void ManaMenu::onLeftHeld() {}
void ManaMenu::onMenuPressed() { push_menu<CMCMenu>(*display, current); }
void ManaMenu::onMenuHeld() {}
void ManaMenu::onRightPressed()
{
if (progress < 3.0f)
{
return;
}
going_right = true;
previous = current;
if (current == 0)
{
current = 5;
}
else
{
current -= 1;
}
progress = 0.0f;
}
void ManaMenu::onRightHeld() {}

View File

@ -1,5 +1,10 @@
#include "menu.h"
#include "pixelstream.h"
decltype(menus::current_menu) menus::current_menu;
decltype(menus::MENU_MEMORY) menus::MENU_MEMORY;
Menu::Menu(lib::Display& display) :
display(&display)
{