#include "icons.h" #include "menu.h" #include "pixelstream.h" #include 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() {}