card-device-firmware/src/manamenu.cpp
2026-01-12 18:12:16 -05:00

275 lines
5.6 KiB
C++

#include "icons.h"
#include "menu.h"
#include "pixelstream.h"
#include <cmath>
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;
ManaMenu::ManaMenu(lib::Display& display) :
Menu(display),
current(0),
previous(0),
going_left(false),
progress(0.99f)
{
}
void ManaMenu::onTick(float dt)
{
constexpr float PI = 3.1415926f;
constexpr float STEP = 2 * PI / 6.0f;
constexpr float RADIUS = 75;
constexpr float CENTER_X = 120;
constexpr float CENTER_Y = 190;
constexpr float MAX_OFFSET = RADIUS / 1.5f;
constexpr float DT_SCALE_ROTATE = 1.0f;
constexpr float DT_SCALE_UPDOWN = 1.6f;
constexpr uint8_t ERASE_DATA[38 * 5 * 3] = {};
if (progress < 1.0f)
{
const float offset
= (1.0f - easeOutElastic(progress)) * MAX_OFFSET + RADIUS;
// erase old
{
display->set_update_area(CENTER_X - 20, CENTER_Y - 20 - offset, 38, 5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
{
display->set_update_area(CENTER_X - 20, CENTER_Y - 15 + 30 - offset, 38,
5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
// write new
{
display->set_update_area(CENTER_X - 15, CENTER_Y - 15 - offset, 31, 32);
auto pixels = display->pixels();
const uint8_t* data = icon::wubrgc(COLORS[previous]);
pixels.write(data, icon::LENGTH);
}
progress += dt * DT_SCALE_UPDOWN;
if (progress > 0.75f)
{
progress = 1.0f;
}
}
else if (progress < 2.0f)
{
const float offset
= (going_left ? -1 : 1) * STEP * easeOutElastic(progress - 1)
+ STEP / 2.0f + (going_left ? 2 * STEP : 0);
// erase previous
for (int i = 0; i < sizeof(COLORS) / sizeof(*COLORS); i++)
{
const float angle = i * STEP - offset;
const float x = cosf(angle) * RADIUS;
const float y = sinf(angle) * RADIUS;
const int left = x - 20 + CENTER_X;
const int top = y - 20 + CENTER_Y;
// top rectangle
{
display->set_update_area(left, top, 38, 5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
// bottom rectangle
{
display->set_update_area(left, top + 36, 38, 5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
// left rectangle
{
display->set_update_area(left, top + 3, 5, 38);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
// right rectangle
{
display->set_update_area(left + 36, top + 3, 5, 38);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
}
progress += dt * DT_SCALE_ROTATE;
if (progress > 1.75f)
{
progress = 2.0f;
}
// write new
for (int i = 0; i < sizeof(COLORS) / sizeof(*COLORS); i++)
{
const float angle = (i - current) * STEP - offset;
const float x = cosf(angle) * RADIUS;
const float y = sinf(angle) * RADIUS;
const int left = x - 15 + CENTER_X;
const int top = y - 15 + CENTER_Y;
display->set_update_area(left, top, 31, 32);
auto pixels = display->pixels();
const uint8_t* data = icon::wubrgc(COLORS[i]);
pixels.write(data, icon::LENGTH);
}
}
else if (progress < 3.0f)
{
const float offset = easeOutElastic(progress - 2.0f) * MAX_OFFSET + RADIUS;
// erase old
{
display->set_update_area(CENTER_X - 20, CENTER_Y - 20 - offset, 38, 5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
{
display->set_update_area(CENTER_X - 20, CENTER_Y - 15 + 30 - offset, 38,
5);
auto pixels = display->pixels();
pixels.write(ERASE_DATA, 38 * 5 * 3);
}
// write new
{
display->set_update_area(CENTER_X - 15, CENTER_Y - 15 - offset, 31, 32);
auto pixels = display->pixels();
const uint8_t* data = icon::wubrgc(COLORS[current]);
pixels.write(data, icon::LENGTH);
}
progress += dt * DT_SCALE_UPDOWN;
if (progress > 2.75f)
{
progress = 3.0f;
}
}
}
void ManaMenu::onLeftPressed()
{
if (progress < 3.0f)
{
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;
previous = current;
if (current == 5)
{
current = 0;
}
else
{
current += 1;
}
progress = 0.0f;
}
void ManaMenu::onRightHeld() {}