This commit is contained in:
andrewchilicki
2025-02-08 16:37:29 -05:00
parent 7e0e9d2592
commit 1485f1325e
9 changed files with 183 additions and 257 deletions

View File

@ -25,8 +25,7 @@ public:
void drawImGuiImage(const std::string& imagePath, float scale, std::optional<ImVec4> tint) final void drawImGuiImage(const std::string& imagePath, float scale, std::optional<ImVec4> tint) final
{ {
auto filePath = assetDir + imagePath; auto filePath = assetDir + imagePath;
if (auto image = getImageForFile(filePath)) if (auto image = getImageForFile(filePath)) {
{
ImGui::Image((ImTextureID)(intptr_t)image->texture, ImVec2(image->width*scale, image->height*scale), ImGui::Image((ImTextureID)(intptr_t)image->texture, ImVec2(image->width*scale, image->height*scale),
ImVec2(0,0), ImVec2(1,1), tint.value_or(ImVec4(1,1,1,1))); ImVec2(0,0), ImVec2(1,1), tint.value_or(ImVec4(1,1,1,1)));
} }
@ -35,8 +34,7 @@ public:
std::pair<int, int> getImageSize(const std::string &imagePath) final std::pair<int, int> getImageSize(const std::string &imagePath) final
{ {
auto filePath = assetDir + imagePath; auto filePath = assetDir + imagePath;
if (auto image = getImageForFile(filePath)) if (auto image = getImageForFile(filePath)) {
{
return std::make_pair(image->width, image->height); return std::make_pair(image->width, image->height);
} }
return std::make_pair(0, 0); return std::make_pair(0, 0);
@ -44,8 +42,7 @@ public:
std::optional<Image> getImageForFile(const std::string& filePath) std::optional<Image> getImageForFile(const std::string& filePath)
{ {
if (auto it = imageCache.find(filePath); it != imageCache.end()) if (auto it = imageCache.find(filePath); it != imageCache.end()) {
{
// Return from cache // Return from cache
return it->second; return it->second;
} }
@ -58,8 +55,7 @@ public:
{ {
// Load texture from file // Load texture from file
FILE* f = fopen(filePath.c_str(), "rb"); FILE* f = fopen(filePath.c_str(), "rb");
if (f == NULL) if (f == NULL) {
{
std::cerr << "Failed to open file " << filePath << std::endl; std::cerr << "Failed to open file " << filePath << std::endl;
return std::nullopt; return std::nullopt;
} }
@ -96,8 +92,9 @@ public:
int image_width = 0; int image_width = 0;
int image_height = 0; int image_height = 0;
unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL, 4); unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL, 4);
if (image_data == NULL) if (image_data == NULL) {
return false; return false;
}
GLuint image_texture; GLuint image_texture;
glGenTextures(1, &image_texture); glGenTextures(1, &image_texture);

View File

@ -26,46 +26,36 @@ public:
bool activeGroupStillVisible = false; bool activeGroupStillVisible = false;
bool newActiveBadGroup = false; bool newActiveBadGroup = false;
for (auto it = badGroups.begin(); it != badGroups.end(); ) for (auto it = badGroups.begin(); it != badGroups.end(); ) {
{ if (it->second->numberIds.empty()) {
if (it->second->numberIds.empty())
{
it = badGroups.erase(it); it = badGroups.erase(it);
} } else {
else
{
++it; ++it;
} }
} }
// Update visible groups and check if active group is still visible // Update visible groups and check if active group is still visible
for (const auto &[groupId, badGroup] : badGroups) for (const auto &[groupId, badGroup] : badGroups) {
{ for (const auto &numId : badGroup->numberIds) {
for (const auto &numId : badGroup->numberIds)
{
auto num = numberIdMap.at(numId); auto num = numberIdMap.at(numId);
if (num->displayInfos.isVisible) if (num->displayInfos.isVisible) {
{
visibleBadGroups.emplace(groupId); visibleBadGroups.emplace(groupId);
if (badGroup->isActive && groupId == *activeBadGroup) if (badGroup->isActive && groupId == *activeBadGroup) {
{
activeGroupStillVisible = true; activeGroupStillVisible = true;
} }
} }
} }
} }
if (activeBadGroup && !activeGroupStillVisible) if (activeBadGroup && !activeGroupStillVisible) {
{
activeBadGroup.reset(); activeBadGroup.reset();
newActiveBadGroup = true; newActiveBadGroup = true;
newBadGroupCountdown = randomNumber(5, 15) * 100; newBadGroupCountdown = randomNumber(5, 15) * 100;
} }
// Select a new active group if necessary // Select a new active group if necessary
if (!activeBadGroup && !visibleBadGroups.empty() && newBadGroupCountdown == 0) if (!activeBadGroup && !visibleBadGroups.empty() && newBadGroupCountdown == 0) {
{
auto randomIndex = randomNumber(0, static_cast<int>(visibleBadGroups.size()) - 1); auto randomIndex = randomNumber(0, static_cast<int>(visibleBadGroups.size()) - 1);
auto it = visibleBadGroups.begin(); auto it = visibleBadGroups.begin();
std::advance(it, randomIndex); std::advance(it, randomIndex);
@ -73,39 +63,27 @@ public:
} }
// Update active groups / their scale // Update active groups / their scale
for (const auto &[groupId, badGroup] : badGroups) for (const auto &[groupId, badGroup] : badGroups) {
{
badGroup->isActive = activeBadGroup && groupId == *activeBadGroup; badGroup->isActive = activeBadGroup && groupId == *activeBadGroup;
if (badGroup->isActive) if (badGroup->isActive) {
{ if (newActiveBadGroup) {
if (newActiveBadGroup)
{
badGroup->scale = 0; badGroup->scale = 0;
} } else {
else if (!badGroup->reachedMax) {
{ if (badGroup->scale < 0.23) {
if (!badGroup->reachedMax)
{
if (badGroup->scale < 0.23)
{
badGroup->scale += (0.0001 * randomNumber(1, 10)); badGroup->scale += (0.0001 * randomNumber(1, 10));
} }
} else } else {
{
badGroup->scale -= (0.0001 * randomNumber(1, 10)); badGroup->scale -= (0.0001 * randomNumber(1, 10));
} }
if (badGroup->scale >= 0.23) if (badGroup->scale >= 0.23) {
{ if (!badGroup->superActive || badGroup->scale >= 0.24) {
if (!badGroup->superActive || badGroup->scale >= 0.24)
{
badGroup->reachedMax = true; badGroup->reachedMax = true;
} else } else {
{
badGroup->scale += 0.00001; badGroup->scale += 0.00001;
} }
} else if (badGroup->scale <= 0.0) } else if (badGroup->scale <= 0.0) {
{
badGroup->isActive = false; badGroup->isActive = false;
badGroup->superActive = false; badGroup->superActive = false;
badGroup->reachedMax = false; badGroup->reachedMax = false;
@ -113,24 +91,20 @@ public:
newBadGroupCountdown = randomNumber(5, 15) * 100; newBadGroupCountdown = randomNumber(5, 15) * 100;
} }
} }
} else } else {
{
badGroup->scale = 0; badGroup->scale = 0;
} }
} }
if (newBadGroupCountdown > 0) if (newBadGroupCountdown > 0) {
{
newBadGroupCountdown--; newBadGroupCountdown--;
} }
} }
NumberPtr getGridNumber(int x, int y) final NumberPtr getGridNumber(int x, int y) final
{ {
if (auto itr = grid.find(x); itr != grid.end()) if (auto itr = grid.find(x); itr != grid.end()) {
{ if (auto itr2 = itr->second.find(y); itr2 != itr->second.end()) {
if (auto itr2 = itr->second.find(y); itr2 != itr->second.end())
{
return itr2->second; return itr2->second;
} }
} }
@ -139,8 +113,7 @@ public:
NumberPtr getGridNumber(int id) final NumberPtr getGridNumber(int id) final
{ {
if (auto itr = numberIdMap.find(id); itr != numberIdMap.end()) if (auto itr = numberIdMap.find(id); itr != numberIdMap.end()) {
{
return itr->second; return itr->second;
} }
return nullptr; return nullptr;
@ -170,16 +143,13 @@ private:
{ {
int numberId = 0; int numberId = 0;
std::set<int> badNumbers; std::set<int> badNumbers;
for (int x = 0; x < size; x++) for (int x = 0; x < size; x++) {
{ for (int y = 0; y < size; y++) {
for (int y = 0; y < size; y++)
{
grid[x][y] = std::make_shared<Number>(numberId, x, y, randomNumber(0,9), randomBool()); grid[x][y] = std::make_shared<Number>(numberId, x, y, randomNumber(0,9), randomBool());
numberIdMap[numberId] = grid[x][y]; numberIdMap[numberId] = grid[x][y];
// Determine if bad // Determine if bad
if (perlinBadNumbers.noise2D_01(x*badScale,y*badScale) > badThresh) if (perlinBadNumbers.noise2D_01(x*badScale,y*badScale) > badThresh) {
{
badNumbers.insert(numberId); badNumbers.insert(numberId);
} }
@ -188,44 +158,34 @@ private:
} }
// Assign 'bad groups' // Assign 'bad groups'
auto checkAdjacent = [&](int x, int y) -> BadGroupPtr auto checkAdjacent = [&](int x, int y) -> BadGroupPtr {
{ if (auto gridNum = getGridNumber(x, y)) {
if (auto gridNum = getGridNumber(x, y))
{
return gridNum->badGroup; return gridNum->badGroup;
} }
return nullptr; return nullptr;
}; };
int badGroup = 0; int badGroup = 0;
for (const auto &badNumId : badNumbers) for (const auto &badNumId : badNumbers) {
{
auto gridNumber = numberIdMap.at(badNumId); auto gridNumber = numberIdMap.at(badNumId);
if (!gridNumber->badGroup) if (!gridNumber->badGroup) {
{ for (int checkX = -1; checkX <= 1; checkX++) {
for (int checkX = -1; checkX <= 1; checkX++) for (int checkY = -1; checkY <= 1; checkY++) {
{ if (checkX == 0 && checkY == 0) {
for (int checkY = -1; checkY <= 1; checkY++)
{
if (checkX == 0 && checkY == 0)
{
continue; continue;
} }
if (auto badGroupPtr = checkAdjacent(gridNumber->gridX + checkX, gridNumber->gridY + checkY)) if (auto badGroupPtr = checkAdjacent(gridNumber->gridX + checkX, gridNumber->gridY + checkY)) {
{
gridNumber->badGroup = badGroupPtr; gridNumber->badGroup = badGroupPtr;
gridNumber->badGroup->numberIds.emplace_back(gridNumber->id); gridNumber->badGroup->numberIds.emplace_back(gridNumber->id);
break; break;
} }
} }
if (gridNumber->badGroup) if (gridNumber->badGroup) {
{
break; break;
} }
} }
if (!gridNumber->badGroup) if (!gridNumber->badGroup) {
{
gridNumber->badGroup = std::make_shared<BadGroup>(badGroup++, std::vector{gridNumber->id}, randomNumber(0,4)); gridNumber->badGroup = std::make_shared<BadGroup>(badGroup++, std::vector{gridNumber->id}, randomNumber(0,4));
badGroups.emplace(badGroup, gridNumber->badGroup); badGroups.emplace(badGroup, gridNumber->badGroup);
} }

View File

@ -11,7 +11,7 @@
namespace ColorValues namespace ColorValues
{ {
ImColor lumonBlue = ImColor(101,213,235,255); ImColor lumonBlue = ImColor(157,227,235,255);
} }
class UIManagerImpl : public UIManager class UIManagerImpl : public UIManager
@ -39,26 +39,25 @@ public:
void update() final void update() final
{ {
if (ImGui::IsKeyPressed(ImGuiKey_Tab)) // Toggle settings mode with 'TAB'
{ if (ImGui::IsKeyPressed(ImGuiKey_Tab)) {
debugMode = !debugMode; settingsMode = !settingsMode;
} }
if (ImGui::IsKeyPressed(ImGuiKey_I)) // Toggle idle mode with 'I'
{ if (ImGui::IsKeyPressed(ImGuiKey_I)) {
idleMode = !idleMode; idleMode = !idleMode;
} }
if (idleMode) if (idleMode) {
{
idleScreen->update(); idleScreen->update();
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{ // Exit idle mode with 'LEFT CLICK'
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
idleMode = false; idleMode = false;
numbersPanel->triggerLoadAnimation(); numbersPanel->triggerLoadAnimation();
} }
} else } else {
{
numbersPanel->update(); numbersPanel->update();
} }
} }
@ -76,22 +75,18 @@ public:
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, viewportSize.y)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x, viewportSize.y));
if (ImGui::Begin("Main", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) if (ImGui::Begin("Main", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse))
{ {
if (idleMode) if (idleMode) {
{
idleScreen->drawIdleScreen(); idleScreen->drawIdleScreen();
} else } else {
{
numbersPanel->drawNumbersPanel(); numbersPanel->drawNumbersPanel();
} }
} }
ImGui::End(); ImGui::End();
if (debugMode) if (settingsMode) {
{
ImGui::SetNextWindowPos(viewportPos); ImGui::SetNextWindowPos(viewportPos);
ImGui::SetNextWindowSize(ImVec2(viewportSize.x * settingsWidthRatio, viewportSize.y)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x * settingsWidthRatio, viewportSize.y));
if (ImGui::Begin("Settings")) if (ImGui::Begin("Settings")) {
{
numbersPanel->drawSettings(); numbersPanel->drawSettings();
} }
ImGui::End(); ImGui::End();
@ -107,11 +102,10 @@ public:
private: private:
std::shared_ptr<ImageDisplay> imageDisplay; std::shared_ptr<ImageDisplay> imageDisplay;
std::shared_ptr<NumbersPanel> numbersPanel; std::shared_ptr<NumbersPanel> numbersPanel;
std::shared_ptr<IdleScreen> idleScreen; std::shared_ptr<IdleScreen> idleScreen;
bool debugMode = false; bool settingsMode = false;
bool idleMode = false; bool idleMode = false;
}; };

View File

@ -13,10 +13,8 @@ namespace ColorValues
class UIManager { class UIManager {
public: public:
virtual void init() = 0; virtual void init() = 0;
virtual void draw() = 0; virtual void draw() = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void cleanup() = 0; virtual void cleanup() = 0;
virtual ~UIManager() = default; virtual ~UIManager() = default;

View File

@ -3,7 +3,6 @@
#include "ImageDisplay.h" #include "ImageDisplay.h"
#include <imgui.h> #include <imgui.h>
#include <iostream> #include <iostream>
#include <ostream>
#include "../UIManager.h" #include "../UIManager.h"
@ -18,10 +17,9 @@ public:
void update() final void update() final
{ {
// Adjust logo scale and speed based on viewport size so it is consistent // Want scale proportionally to the window size
auto viewportSize = ImGui::GetMainViewport()->Size; auto viewportSize = ImGui::GetMainViewport()->Size;
if (lastViewportSize.x != viewportSize.x || lastViewportSize.y != viewportSize.y) if (lastViewportSize.x != viewportSize.x || lastViewportSize.y != viewportSize.y) {
{
float displaySizeScalePrev = lastViewportSize.x/1280.f; float displaySizeScalePrev = lastViewportSize.x/1280.f;
float displaySizeScale = viewportSize.x/1280.f; float displaySizeScale = viewportSize.x/1280.f;
float adjustedScale = displaySizeScale / displaySizeScalePrev; float adjustedScale = displaySizeScale / displaySizeScalePrev;
@ -46,19 +44,15 @@ public:
ImVec2 minPosition = ImVec2(windowPos.x, windowPos.y); ImVec2 minPosition = ImVec2(windowPos.x, windowPos.y);
ImVec2 maxPosition = ImVec2(windowPos.x + windowSize.x - scale*logoSize.x, windowPos.y + windowSize.y - scale*logoSize.y); ImVec2 maxPosition = ImVec2(windowPos.x + windowSize.x - scale*logoSize.x, windowPos.y + windowSize.y - scale*logoSize.y);
if (currentLogoPosition.x >= maxPosition.x) if (currentLogoPosition.x >= maxPosition.x) {
{
nextOffset.x = -1; nextOffset.x = -1;
} else if (currentLogoPosition.x < minPosition.x) } else if (currentLogoPosition.x < minPosition.x) {
{
nextOffset.x = 1; nextOffset.x = 1;
} }
if (currentLogoPosition.y >= maxPosition.y) if (currentLogoPosition.y >= maxPosition.y) {
{
nextOffset.y = -1; nextOffset.y = -1;
} else if (currentLogoPosition.y < minPosition.y) } else if (currentLogoPosition.y < minPosition.y) {
{
nextOffset.y = 1; nextOffset.y = 1;
} }

View File

@ -23,14 +23,12 @@ public:
// Update max bad groups for each bin // Update max bad groups for each bin
auto badGroups = numberGrid->getBadGroups(); auto badGroups = numberGrid->getBadGroups();
for (auto &[id, group] : badGroups) for (auto &[id, group] : badGroups) {
{
bins[group->binIdx].maxBadGroups++; bins[group->binIdx].maxBadGroups++;
} }
// Load settings // Load settings
if (auto loadedSettings = loadSettings(settingsSavePath)) if (auto loadedSettings = loadSettings(settingsSavePath)) {
{
displaySettings = loadedSettings->displaySettings; displaySettings = loadedSettings->displaySettings;
controlSettings = loadedSettings->controlSettings; controlSettings = loadedSettings->controlSettings;
std::cout << "Successfully loaded settings from disk." << std::endl; std::cout << "Successfully loaded settings from disk." << std::endl;
@ -43,8 +41,7 @@ public:
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
font = io.Fonts->AddFontFromFileTTF("./assets/Montserrat-Bold.ttf", 50.f); font = io.Fonts->AddFontFromFileTTF("./assets/Montserrat-Bold.ttf", 50.f);
io.Fonts->Build(); io.Fonts->Build();
if (font == nullptr) if (font == nullptr) {
{
font = ImGui::GetDefaultFont(); font = ImGui::GetDefaultFont();
std::cerr << "Failed to load 'Montserrat-Bold' font." << std::endl; std::cerr << "Failed to load 'Montserrat-Bold' font." << std::endl;
} }
@ -82,29 +79,26 @@ public:
void triggerLoadAnimation() final void triggerLoadAnimation() final
{ {
// Reset 'regenerate scale' on all numbers // Reset 'regenerate scale' on all numbers
for (auto &[x, gridY] : numberGrid->getGrid()) for (auto &[x, gridY] : numberGrid->getGrid()) {
{ for (auto &[y, gridNumber] : gridY) {
for (auto &[y, gridNumber] : gridY)
{
gridNumber->regenerateScale = 0.f; gridNumber->regenerateScale = 0.f;
} }
} }
} }
private: private:
std::optional<int> drawNumbersGrid(const ImVec2& windowPos, const ImVec2& windowSize, const ImVec2& mousePos, bool updateDisplayInfos) std::optional<int> drawNumbersGrid(const ImVec2& windowPos, const ImVec2& windowSize, const ImVec2& mousePos, bool updateDisplayInfos)
{ {
std::optional<int> refiningToBin = std::nullopt; std::optional<int> refiningToBin = std::nullopt;
for (auto &[x, gridY] : numberGrid->getGrid()) for (auto &[x, gridY] : numberGrid->getGrid()) {
{ for (auto &[y, gridNumber] : gridY) {
for (auto &[y, gridNumber] : gridY)
{
std::string numberToDraw = "numbers/" + std::to_string(gridNumber->num) + ".png"; std::string numberToDraw = "numbers/" + std::to_string(gridNumber->num) + ".png";
auto [width, height] = imageDisplay->getImageSize(numberToDraw); auto [width, height] = imageDisplay->getImageSize(numberToDraw);
double badScale = gridNumber->badGroup ? gridNumber->badGroup->scale : 0.0; double badScale = gridNumber->badGroup ? gridNumber->badGroup->scale : 0.0;
if (updateDisplayInfos)
{ if (updateDisplayInfos) {
// Only need to update when viewport has changed
ImVec2 localNumberPos = ImVec2((x * displaySettings.gridSpacing + panelOffset.x)*panelScale, (y * displaySettings.gridSpacing + panelOffset.y)*panelScale); ImVec2 localNumberPos = ImVec2((x * displaySettings.gridSpacing + panelOffset.x)*panelScale, (y * displaySettings.gridSpacing + panelOffset.y)*panelScale);
gridNumber->displayInfos.centerX = localNumberPos.x + windowPos.x; gridNumber->displayInfos.centerX = localNumberPos.x + windowPos.x;
gridNumber->displayInfos.centerY = localNumberPos.y + windowPos.y; gridNumber->displayInfos.centerY = localNumberPos.y + windowPos.y;
@ -114,30 +108,72 @@ private:
double heightOffset = (baseNumberScale*height/2.f); double heightOffset = (baseNumberScale*height/2.f);
gridNumber->displayInfos.isVisible = gridNumber->displayInfos.centerX + widthOffset < windowPos.x + windowSize.x && gridNumber->displayInfos.centerX - widthOffset > windowPos.x && gridNumber->displayInfos.isVisible = gridNumber->displayInfos.centerX + widthOffset < windowPos.x + windowSize.x && gridNumber->displayInfos.centerX - widthOffset > windowPos.x &&
gridNumber->displayInfos.centerY + heightOffset < windowPos.y + windowSize.y - displayPresets.numberWindowBufferBottom && gridNumber->displayInfos.centerY - heightOffset > windowPos.y + displayPresets.numberWindowBufferTop; gridNumber->displayInfos.centerY + heightOffset < windowPos.y + windowSize.y - displayPresets.numberWindowBufferBottom && gridNumber->displayInfos.centerY - heightOffset > windowPos.y + displayPresets.numberWindowBufferTop;
} }
if (!gridNumber->displayInfos.isVisible) // Don't draw numbers out of viewport
{ if (!gridNumber->displayInfos.isVisible) {
continue; continue;
} }
auto centerPos = ImVec2(gridNumber->displayInfos.centerX, gridNumber->displayInfos.centerY); auto centerPos = ImVec2(gridNumber->displayInfos.centerX, gridNumber->displayInfos.centerY);
if (gridNumber->badGroup && gridNumber->badGroup->refined) // Animate number on screen
{ float numberAlpha = 255;
if (gridNumber->regenerateScale < 1.f) {
gridNumber->regenerateScale += numberGrid->randomNumber(0,10)*0.001f;
numberAlpha = static_cast<int>(std::clamp(gridNumber->regenerateScale*2.f*255.f, 0.f, 255.f));
}
// Offset from noise scale
double noiseScale = perlin.noise3D((x * displaySettings.noiseScale), (y * displaySettings.noiseScale), t*displaySettings.noiseSpeed);
if (gridNumber->displayInfos.horizontalOffset) {
centerPos.x += noiseScale*displaySettings.noiseScaleOffset;
} else {
centerPos.y += noiseScale*displaySettings.noiseScaleOffset;
}
// Colour
auto col = ColorValues::lumonBlue.Value;
col.w = numberAlpha;
if (revealMap && gridNumber->badGroup) {
col = gridNumber->badGroup->isActive ? ImVec4(255,255,0,numberAlpha) : ImVec4(255,0,0,255);
}
// Scale from mouse hovering
auto numberScale = getScaleFromCursor(centerPos, mousePos);
// Handle if part of bad group
if (gridNumber->badGroup) {
if (gridNumber->badGroup->isActive) {
// Make number 'super active'
if (numberScale > 1.0f) {
gridNumber->badGroup->superActive = true;
}
// Mark as refined on 'LEFT CLICK'
if (numberScale >= (0.5f + displaySettings.mouseScaleMultiplier) && ImGui::IsKeyDown(ImGuiKey_MouseLeft)) {
gridNumber->badGroup->refined = true;
bins[gridNumber->badGroup->binIdx].badGroupsRefined++;
}
}
// Add jitter to 'super active' bad numbers
if (gridNumber->badGroup->superActive) {
centerPos.x += numberGrid->randomNumber(-10, 10)*badScale;
centerPos.y += numberGrid->randomNumber(-10, 10)*badScale;
}
// Animate position if number has been refined
if (gridNumber->badGroup->refined) {
float startX = gridNumber->displayInfos.centerX; float startX = gridNumber->displayInfos.centerX;
float startY = gridNumber->displayInfos.centerY; float startY = gridNumber->displayInfos.centerY;
if (gridNumber->displayInfos.refinedX == -1) if (gridNumber->displayInfos.refinedX == -1) {
{
gridNumber->displayInfos.refinedX = startX; gridNumber->displayInfos.refinedX = startX;
gridNumber->displayInfos.refinedY = startY; gridNumber->displayInfos.refinedY = startY;
} }
auto binIdx = gridNumber->badGroup->binIdx; auto binIdx = gridNumber->badGroup->binIdx;
if (binIdx > 4) if (binIdx > 4) {
{
std::cout << "Error: Bin index greater than expected. Setting to max." << std::endl; std::cout << "Error: Bin index greater than expected. Setting to max." << std::endl;
binIdx = 4; binIdx = 4;
} }
@ -146,72 +182,23 @@ private:
float distY = bins[binIdx].pos.y - gridNumber->displayInfos.refinedY; float distY = bins[binIdx].pos.y - gridNumber->displayInfos.refinedY;
float distance = sqrt(distX * distX + distY * distY); float distance = sqrt(distX * distX + distY * distY);
const float moveSpeed = 3.0f; if (distance > displaySettings.refinedToBinSpeed) {
if (distance > moveSpeed)
{
float dirX = distX / distance; float dirX = distX / distance;
float dirY = distY / distance; float dirY = distY / distance;
gridNumber->displayInfos.refinedX += dirX * moveSpeed; gridNumber->displayInfos.refinedX += dirX * displaySettings.refinedToBinSpeed;
gridNumber->displayInfos.refinedY += dirY * moveSpeed; gridNumber->displayInfos.refinedY += dirY * displaySettings.refinedToBinSpeed;
centerPos = ImVec2(gridNumber->displayInfos.refinedX, gridNumber->displayInfos.refinedY); centerPos = ImVec2(gridNumber->displayInfos.refinedX, gridNumber->displayInfos.refinedY);
refiningToBin = gridNumber->badGroup->binIdx; refiningToBin = gridNumber->badGroup->binIdx;
} } else {
else
{
gridNumber->badGroup.reset(); // No longer a bad number gridNumber->badGroup.reset(); // No longer a bad number
gridNumber->num = numberGrid->randomNumber(0,9); gridNumber->num = numberGrid->randomNumber(0,9);
gridNumber->regenerateScale = 0.f; gridNumber->regenerateScale = 0.f;
} }
} }
float numberAlpha = 255;
if (gridNumber->regenerateScale < 1.f)
{
gridNumber->regenerateScale += numberGrid->randomNumber(0,10)*0.001f;
numberAlpha = static_cast<int>(std::clamp(gridNumber->regenerateScale*2.f*255.f, 0.f, 255.f));
}
// Offset from noise scale
double noiseScale = perlin.noise3D((x * displaySettings.noiseScale), (y * displaySettings.noiseScale), t*displaySettings.noiseSpeed);
if (gridNumber->displayInfos.horizontalOffset)
{
centerPos.x += noiseScale*displaySettings.noiseScaleOffset;
} else
{
centerPos.y += noiseScale*displaySettings.noiseScaleOffset;
}
// Colour
auto col = ColorValues::lumonBlue.Value;
col.w = numberAlpha;
if (revealMap && gridNumber->badGroup)
{
col = gridNumber->badGroup->isActive ? ImVec4(255,255,0,numberAlpha) : ImVec4(255,0,0,255);
}
// Scale from mouse hovering
auto numberScale = getScaleFromCursor(centerPos, mousePos);
if (gridNumber->badGroup && gridNumber->badGroup->isActive)
{
if (numberScale > 1.0f)
{
gridNumber->badGroup->superActive = true;
}
if (numberScale >= (0.5f + displaySettings.mouseScaleMultiplier) && ImGui::IsKeyDown(ImGuiKey_MouseLeft))
{
gridNumber->badGroup->refined = true;
bins[gridNumber->badGroup->binIdx].badGroupsRefined++;
}
}
if (gridNumber->badGroup && gridNumber->badGroup->superActive)
{
centerPos.x += numberGrid->randomNumber(-10, 10)*badScale;
centerPos.y += numberGrid->randomNumber(-10, 10)*badScale;
} }
// Draw number
float combinedScale = gridNumber->regenerateScale*displaySettings.imageScale*numberScale*panelScale + badScale; float combinedScale = gridNumber->regenerateScale*displaySettings.imageScale*numberScale*panelScale + badScale;
ImGui::SetCursorPos(ImVec2(centerPos.x - ImGui::GetWindowPos().x - ((width*combinedScale)/2.f), centerPos.y - ImGui::GetWindowPos().y - ((height*combinedScale)/2.f))); ImGui::SetCursorPos(ImVec2(centerPos.x - ImGui::GetWindowPos().x - ((width*combinedScale)/2.f), centerPos.y - ImGui::GetWindowPos().y - ((height*combinedScale)/2.f)));
imageDisplay->drawImGuiImage(numberToDraw, combinedScale, col); imageDisplay->drawImGuiImage(numberToDraw, combinedScale, col);
@ -226,10 +213,11 @@ private:
{ {
std::string binPercentPath = "bins/bin-percent.png"; std::string binPercentPath = "bins/bin-percent.png";
auto [widthP, heightP] = imageDisplay->getImageSize(binPercentPath); auto [widthP, heightP] = imageDisplay->getImageSize(binPercentPath);
for (auto &b : bins) for (auto &b : bins) {
{ // Scale based on viewport size
auto pos = b.updatePos(windowSize, windowPos, displayPresets.numberWindowBufferBottom - displayPresets.binOffset); auto pos = b.updatePos(windowSize, windowPos, displayPresets.numberWindowBufferBottom - displayPresets.binOffset);
// Draw bin images
std::string binPath = "bins/bin0" + std::to_string(b.id) + ".png"; std::string binPath = "bins/bin0" + std::to_string(b.id) + ".png";
auto [width, height] = imageDisplay->getImageSize(binPath); auto [width, height] = imageDisplay->getImageSize(binPath);
ImGui::SetCursorPos(ImVec2(pos.x - windowPos.x - (width*displayPresets.binImageScale/2.f), pos.y - windowPos.y - (height*displayPresets.binImageScale/2.f))); ImGui::SetCursorPos(ImVec2(pos.x - windowPos.x - (width*displayPresets.binImageScale/2.f), pos.y - windowPos.y - (height*displayPresets.binImageScale/2.f)));
@ -239,6 +227,7 @@ private:
ImGui::SetCursorPos(ImVec2(percentPos.x - windowPos.x - (widthP*displayPresets.binImageScale/2.f), percentPos.y - windowPos.y - (heightP*displayPresets.binImageScale/2.f))); ImGui::SetCursorPos(ImVec2(percentPos.x - windowPos.x - (widthP*displayPresets.binImageScale/2.f), percentPos.y - windowPos.y - (heightP*displayPresets.binImageScale/2.f)));
imageDisplay->drawImGuiImage(binPercentPath, displayPresets.binImageScale, ColorValues::lumonBlue); imageDisplay->drawImGuiImage(binPercentPath, displayPresets.binImageScale, ColorValues::lumonBlue);
// Draw percentage bar and text
ImVec2 trCorner = ImVec2(percentPos.x - (widthP*displayPresets.binImageScale/2.f), percentPos.y - (heightP*displayPresets.binImageScale/2.f)); ImVec2 trCorner = ImVec2(percentPos.x - (widthP*displayPresets.binImageScale/2.f), percentPos.y - (heightP*displayPresets.binImageScale/2.f));
ImVec2 brCorner = ImVec2(percentPos.x + (widthP*displayPresets.binImageScale/2.f), percentPos.y + (heightP*displayPresets.binImageScale/2.f)); ImVec2 brCorner = ImVec2(percentPos.x + (widthP*displayPresets.binImageScale/2.f), percentPos.y + (heightP*displayPresets.binImageScale/2.f));
@ -249,9 +238,8 @@ private:
drawList->AddRectFilled(trCorner, ImVec2(trCorner.x + ((brCorner.x - trCorner.x)* percentD), brCorner.y), ImColor(ColorValues::lumonBlue.Value.x, ColorValues::lumonBlue.Value.y, ColorValues::lumonBlue.Value.z, 0.3f)); drawList->AddRectFilled(trCorner, ImVec2(trCorner.x + ((brCorner.x - trCorner.x)* percentD), brCorner.y), ImColor(ColorValues::lumonBlue.Value.x, ColorValues::lumonBlue.Value.y, ColorValues::lumonBlue.Value.z, 0.3f));
// Bin open // Animate bin open
if (numberRefiningToBin && *numberRefiningToBin == b.id - 1) if (numberRefiningToBin && *numberRefiningToBin == b.id - 1) {
{
std::string binOpenPath = "bins/bin-open.png"; std::string binOpenPath = "bins/bin-open.png";
auto [widthO, heightO] = imageDisplay->getImageSize(binOpenPath); auto [widthO, heightO] = imageDisplay->getImageSize(binOpenPath);
ImGui::SetCursorPos(ImVec2(pos.x - windowPos.x - (widthO*displayPresets.binImageScale/2.f), pos.y - windowPos.y - (heightO*displayPresets.binImageScale/2.f) - (height*displayPresets.binImageScale))); ImGui::SetCursorPos(ImVec2(pos.x - windowPos.x - (widthO*displayPresets.binImageScale/2.f), pos.y - windowPos.y - (heightO*displayPresets.binImageScale/2.f) - (height*displayPresets.binImageScale)));
@ -268,6 +256,7 @@ private:
drawList->AddRect(headerBoxMin, headerBoxMax, ColorValues::lumonBlue); drawList->AddRect(headerBoxMin, headerBoxMax, ColorValues::lumonBlue);
ImVec2 headerTextPos = ImVec2(headerBoxMin.x + 25.f, (headerBoxMin.y+headerBoxMax.y)/2.f - displayPresets.fontSize/2.f); ImVec2 headerTextPos = ImVec2(headerBoxMin.x + 25.f, (headerBoxMin.y+headerBoxMax.y)/2.f - displayPresets.fontSize/2.f);
drawList->AddText(font, displayPresets.fontSize, headerTextPos, ColorValues::lumonBlue, "@andrewchilicki"); drawList->AddText(font, displayPresets.fontSize, headerTextPos, ColorValues::lumonBlue, "@andrewchilicki");
// Lumon logo // Lumon logo
std::string logoPath = "lumon-logo.png"; std::string logoPath = "lumon-logo.png";
auto [widthH, heightH] = imageDisplay->getImageSize(logoPath); auto [widthH, heightH] = imageDisplay->getImageSize(logoPath);
@ -275,8 +264,7 @@ private:
imageDisplay->drawImGuiImage(logoPath, displayPresets.headerImageScale, ColorValues::lumonBlue); imageDisplay->drawImGuiImage(logoPath, displayPresets.headerImageScale, ColorValues::lumonBlue);
// Horizontal lines // Horizontal lines
auto drawLine = [&](const float y) auto drawLine = [&](const float y) {
{
drawList->AddLine(ImVec2(windowPos.x, y), ImVec2(windowPos.x + windowSize.x, y), ColorValues::lumonBlue, displayPresets.lineThickness); drawList->AddLine(ImVec2(windowPos.x, y), ImVec2(windowPos.x + windowSize.x, y), ColorValues::lumonBlue, displayPresets.lineThickness);
}; };
@ -294,40 +282,33 @@ private:
static bool viewportInit = false; static bool viewportInit = false;
bool viewportChanged = !viewportInit; bool viewportChanged = !viewportInit;
// Handle arrow key input // Handle arrow key input
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
{
panelOffset.x += controlSettings.arrowSensitivity; panelOffset.x += controlSettings.arrowSensitivity;
viewportChanged = true; viewportChanged = true;
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) } else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
{
panelOffset.x -= controlSettings.arrowSensitivity; panelOffset.x -= controlSettings.arrowSensitivity;
viewportChanged = true; viewportChanged = true;
} }
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
{
panelOffset.y += controlSettings.arrowSensitivity; panelOffset.y += controlSettings.arrowSensitivity;
viewportChanged = true; viewportChanged = true;
} else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) } else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
{
panelOffset.y -= controlSettings.arrowSensitivity; panelOffset.y -= controlSettings.arrowSensitivity;
viewportChanged = true; viewportChanged = true;
} }
// Handle zoom // Handle zoom
if (ImGui::IsKeyPressed(ImGuiKey_Comma)) if (ImGui::IsKeyPressed(ImGuiKey_Comma)) {
{
panelScale -= controlSettings.zoomSensitivity; panelScale -= controlSettings.zoomSensitivity;
viewportChanged = true; viewportChanged = true;
} else if (ImGui::IsKeyPressed(ImGuiKey_Period)) } else if (ImGui::IsKeyPressed(ImGuiKey_Period)) {
{
panelScale += controlSettings.zoomSensitivity; panelScale += controlSettings.zoomSensitivity;
viewportChanged = true; viewportChanged = true;
} }
// panelScale = std::clamp(panelScale, 0.8f, 1.8f); panelScale = std::clamp(panelScale, displaySettings.minZoomScale, displaySettings.maxZoomScale);
if (viewportChanged) if (viewportChanged) {
{
// Clamp movement to within grid boundaries // Clamp movement to within grid boundaries
float gridWidthScaled = gridSize * displaySettings.gridSpacing * panelScale; float gridWidthScaled = gridSize * displaySettings.gridSpacing * panelScale;
float gridHeightScaled = gridSize * displaySettings.gridSpacing * panelScale; float gridHeightScaled = gridSize * displaySettings.gridSpacing * panelScale;
@ -337,8 +318,7 @@ private:
float minOffsetY = -gridHeightScaled + windowSize.y - displayPresets.numberWindowBufferTop; float minOffsetY = -gridHeightScaled + windowSize.y - displayPresets.numberWindowBufferTop;
float maxOffsetY = displayPresets.numberWindowBufferTop; float maxOffsetY = displayPresets.numberWindowBufferTop;
if (!viewportInit) if (!viewportInit) {
{
// Start at center of grid // Start at center of grid
panelOffset.x = (windowSize.x - gridWidthScaled) / 2.0f / panelScale; panelOffset.x = (windowSize.x - gridWidthScaled) / 2.0f / panelScale;
panelOffset.y = (windowSize.y - gridHeightScaled) / 2.0f / panelScale; panelOffset.y = (windowSize.y - gridHeightScaled) / 2.0f / panelScale;
@ -355,8 +335,7 @@ private:
void drawSettings() final void drawSettings() final
{ {
ImGui::SetWindowFontScale(displayPresets.settingsFontScale); ImGui::SetWindowFontScale(displayPresets.settingsFontScale);
if (ImGui::Button("Save Settings")) if (ImGui::Button("Save Settings")) {
{
saveSettings(Settings{displaySettings, controlSettings}, settingsSavePath); saveSettings(Settings{displaySettings, controlSettings}, settingsSavePath);
} }
ImGui::Separator(); ImGui::Separator();
@ -366,6 +345,9 @@ private:
ImGui::InputFloat("Numbers Grid Spacing", &displaySettings.gridSpacing); ImGui::InputFloat("Numbers Grid Spacing", &displaySettings.gridSpacing);
ImGui::InputFloat("Mouse Scale Radius", &displaySettings.mouseScaleRadius); ImGui::InputFloat("Mouse Scale Radius", &displaySettings.mouseScaleRadius);
ImGui::InputFloat("Mouse Scale Multiplier", &displaySettings.mouseScaleMultiplier); ImGui::InputFloat("Mouse Scale Multiplier", &displaySettings.mouseScaleMultiplier);
ImGui::InputFloat("Min Scale Multiplier", &displaySettings.minZoomScale);
ImGui::InputFloat("Max Scale Multiplier", &displaySettings.maxZoomScale);
ImGui::InputFloat("Refined to Bin Speed", &displaySettings.refinedToBinSpeed);
ImGui::Text("Noise:"); ImGui::Text("Noise:");
ImGui::InputFloat("Noise Speed", &displaySettings.noiseSpeed); ImGui::InputFloat("Noise Speed", &displaySettings.noiseSpeed);
ImGui::InputFloat("Noise Scale", &displaySettings.noiseScale); ImGui::InputFloat("Noise Scale", &displaySettings.noiseScale);
@ -380,11 +362,12 @@ private:
bool updateDisplaySettings(PresetDisplaySettings &settings, float globalScale) bool updateDisplaySettings(PresetDisplaySettings &settings, float globalScale)
{ {
// Want preset display settings to scale proportionally to the window size
auto viewportSize = ImGui::GetMainViewport()->Size; auto viewportSize = ImGui::GetMainViewport()->Size;
if (lastViewportSize.x == viewportSize.x && lastViewportSize.y == viewportSize.y && lastGlobalScale == globalScale) if (lastViewportSize.x == viewportSize.x && lastViewportSize.y == viewportSize.y && lastGlobalScale == globalScale) {
{
return false; return false;
} }
float displaySizeScalePrev = lastViewportSize.x/1280.f * lastGlobalScale; float displaySizeScalePrev = lastViewportSize.x/1280.f * lastGlobalScale;
float displaySizeScale = viewportSize.x/1280.f * globalScale; float displaySizeScale = viewportSize.x/1280.f * globalScale;
float newScale = displaySizeScale / displaySizeScalePrev; float newScale = displaySizeScale / displaySizeScalePrev;
@ -415,8 +398,7 @@ private:
float distY = mousePos.y - globalNumberPos.y; float distY = mousePos.y - globalNumberPos.y;
float distance = sqrt(distX * distX + distY * distY); float distance = sqrt(distX * distX + distY * distY);
if (distance < displaySettings.mouseScaleRadius) if (distance < displaySettings.mouseScaleRadius) {
{
return 1.0f + (displaySettings.mouseScaleRadius - distance) / displaySettings.mouseScaleRadius * displaySettings.mouseScaleMultiplier; return 1.0f + (displaySettings.mouseScaleRadius - distance) / displaySettings.mouseScaleRadius * displaySettings.mouseScaleMultiplier;
} }
return 1.0f; return 1.0f;
@ -443,6 +425,7 @@ private:
// Debug options // Debug options
bool revealMap = false; bool revealMap = false;
// TODO - Move to class
struct Bin struct Bin
{ {
int id; int id;
@ -451,8 +434,7 @@ private:
int badGroupsRefined = 0; int badGroupsRefined = 0;
int maxBadGroups = 0; int maxBadGroups = 0;
ImVec2 updatePos(const ImVec2 &windowSize, const ImVec2 &windowPos, float offsetY) ImVec2 updatePos(const ImVec2 &windowSize, const ImVec2 &windowPos, float offsetY) {
{
pos = ImVec2(windowPos.x + (windowSize.x / 6)*id, windowPos.y + windowSize.y - offsetY); pos = ImVec2(windowPos.x + (windowSize.x / 6)*id, windowPos.y + windowSize.y - offsetY);
return pos; return pos;
} }

View File

@ -6,14 +6,13 @@ class ImageDisplay;
class NumbersPanel { class NumbersPanel {
public: public:
virtual void init() = 0; virtual void init() = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void drawNumbersPanel() = 0; virtual void drawNumbersPanel() = 0;
virtual void drawSettings() = 0;
virtual void triggerLoadAnimation() = 0; virtual void triggerLoadAnimation() = 0;
virtual void drawSettings() = 0;
virtual ~NumbersPanel() = default; virtual ~NumbersPanel() = default;
}; };

View File

@ -7,6 +7,7 @@
struct PresetDisplaySettings struct PresetDisplaySettings
{ {
// Preset sizing and offsets for UI
float numberWindowBufferTop = 125.f; float numberWindowBufferTop = 125.f;
float numberWindowBufferBottom = 115.f; float numberWindowBufferBottom = 115.f;
@ -57,8 +58,7 @@ inline void saveSettingsJson(const nlohmann::json& settingsJson, const std::stri
file.flush(); file.flush();
file.close(); file.close();
std::cout << "Settings saved to disk." << std::endl; std::cout << "Settings saved to disk." << std::endl;
} } catch (const std::exception& e) {
catch (const std::exception& e) {
std::cerr << "Error saving settings: " << e.what() << std::endl; std::cerr << "Error saving settings: " << e.what() << std::endl;
} }
} }
@ -72,20 +72,27 @@ struct DisplaySettings
float mouseScaleRadius = 100.f; float mouseScaleRadius = 100.f;
float mouseScaleMultiplier = 2.f; float mouseScaleMultiplier = 2.f;
float maxZoomScale = 0.8f;
float minZoomScale = 0.2f;
float noiseSpeed = 0.004f; float noiseSpeed = 0.004f;
float noiseScale = 1.f; float noiseScale = 1.f;
float noiseScaleOffset = 15; float noiseScaleOffset = 15;
float refinedToBinSpeed = 3.0f;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(DisplaySettings, NLOHMANN_DEFINE_TYPE_INTRUSIVE(DisplaySettings,
globalScale, globalScale,
imageScale, imageScale,
gridSpacing, gridSpacing,
mouseScaleRadius, mouseScaleRadius,
mouseScaleMultiplier, mouseScaleMultiplier,
maxZoomScale,
minZoomScale,
noiseSpeed, noiseSpeed,
noiseScale, noiseScale,
noiseScaleOffset noiseScaleOffset,
refinedToBinSpeed
); );
}; };
@ -107,26 +114,21 @@ struct Settings
inline std::optional<Settings> loadSettings(const std::string& jsonPath) inline std::optional<Settings> loadSettings(const std::string& jsonPath)
{ {
try try {
{ if (auto json = loadSettingsFromJson(jsonPath)) {
if (auto json = loadSettingsFromJson(jsonPath))
{
return json->get<Settings>(); return json->get<Settings>();
} }
} catch (const std::exception& e) } catch (const std::exception& e) {
{
std::cerr << "Error loading settings: " << e.what() << std::endl; std::cerr << "Error loading settings: " << e.what() << std::endl;
} }
return std::nullopt; return std::nullopt;
} }
inline void saveSettings(const Settings& settings, const std::string& jsonPath) inline void saveSettings(const Settings& settings, const std::string& jsonPath)
{ {
try try {
{
nlohmann::json json = settings; nlohmann::json json = settings;
saveSettingsJson(json, jsonPath); saveSettingsJson(json, jsonPath);
} catch (const std::exception& e) } catch (const std::exception& e) {
{
std::cerr << "Error saving settings: " << e.what() << std::endl; std::cerr << "Error saving settings: " << e.what() << std::endl;
} }
} }

View File

@ -1,11 +1,11 @@
#include "UI/UIManager.h"
#include "imgui.h" #include "imgui.h"
#include "backends/imgui_impl_glfw.h" #include "backends/imgui_impl_glfw.h"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <imgui_impl_opengl3.h> #include <imgui_impl_opengl3.h>
#include <iostream> #include <iostream>
#include "UI/UIManager.h"
void glfw_error_callback(int error, const char* description) { void glfw_error_callback(int error, const char* description) {
std::cerr << "GLFW Error (" << error << "): " << description << std::endl; std::cerr << "GLFW Error (" << error << "): " << description << std::endl;
} }