mirror of
https://github.com/andrewchilicki/LumonMDR.git
synced 2025-07-01 18:17:27 -04:00
Init
This commit is contained in:
121
src/UI/UIManager.cpp
Normal file
121
src/UI/UIManager.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "UIManager.h"
|
||||
|
||||
#include "Image/ImageDisplay.h"
|
||||
#include "Widgets/IdleScreen.h"
|
||||
#include "Widgets/NumbersPanel.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "backends/imgui_impl_glfw.h"
|
||||
#include "backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace ColorValues
|
||||
{
|
||||
ImColor lumonBlue = ImColor(101,213,235,255);
|
||||
}
|
||||
|
||||
class UIManagerImpl : public UIManager
|
||||
{
|
||||
public:
|
||||
UIManagerImpl()
|
||||
{
|
||||
imageDisplay = createImageDisplay("./assets/");
|
||||
numbersPanel = createNumbersPanel(imageDisplay);
|
||||
idleScreen = createIdleScreen(imageDisplay);
|
||||
}
|
||||
|
||||
void init() final {
|
||||
// Initialize ImGui
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
// Initialize ImGui backends
|
||||
ImGui_ImplGlfw_InitForOpenGL(glfwGetCurrentContext(), true);
|
||||
ImGui_ImplOpenGL3_Init("#version 130");
|
||||
|
||||
numbersPanel->init();
|
||||
}
|
||||
|
||||
void update() final
|
||||
{
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Tab))
|
||||
{
|
||||
debugMode = !debugMode;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_I))
|
||||
{
|
||||
idleMode = !idleMode;
|
||||
}
|
||||
|
||||
if (idleMode)
|
||||
{
|
||||
idleScreen->update();
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
idleMode = false;
|
||||
numbersPanel->triggerLoadAnimation();
|
||||
}
|
||||
} else
|
||||
{
|
||||
numbersPanel->update();
|
||||
}
|
||||
}
|
||||
|
||||
void draw() final
|
||||
{
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImVec2 viewportSize = viewport->WorkSize;
|
||||
ImVec2 viewportPos = viewport->WorkPos;
|
||||
|
||||
static float settingsWidthRatio = 0.4f;
|
||||
|
||||
// Draw widgets
|
||||
ImGui::SetNextWindowPos(ImVec2(viewportPos.x, viewportPos.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 (idleMode)
|
||||
{
|
||||
idleScreen->drawIdleScreen();
|
||||
} else
|
||||
{
|
||||
numbersPanel->drawNumbersPanel();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (debugMode)
|
||||
{
|
||||
ImGui::SetNextWindowPos(viewportPos);
|
||||
ImGui::SetNextWindowSize(ImVec2(viewportSize.x * settingsWidthRatio, viewportSize.y));
|
||||
if (ImGui::Begin("Settings"))
|
||||
{
|
||||
numbersPanel->drawSettings();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup() final {
|
||||
// Cleanup ImGui
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ImageDisplay> imageDisplay;
|
||||
|
||||
std::shared_ptr<NumbersPanel> numbersPanel;
|
||||
std::shared_ptr<IdleScreen> idleScreen;
|
||||
|
||||
bool debugMode = false;
|
||||
bool idleMode = false;
|
||||
};
|
||||
|
||||
std::shared_ptr<UIManager> createUIManager()
|
||||
{
|
||||
return std::make_shared<UIManagerImpl>();
|
||||
}
|
25
src/UI/UIManager.h
Normal file
25
src/UI/UIManager.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
|
||||
#ifndef GLOBALS_H
|
||||
#define GLOBALS_H
|
||||
namespace ColorValues
|
||||
{
|
||||
extern ImColor lumonBlue;
|
||||
}
|
||||
#endif
|
||||
|
||||
class UIManager {
|
||||
public:
|
||||
virtual void init() = 0;
|
||||
|
||||
virtual void draw() = 0;
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual void cleanup() = 0;
|
||||
|
||||
virtual ~UIManager() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<UIManager> createUIManager();
|
89
src/UI/Widgets/IdleScreen.cpp
Normal file
89
src/UI/Widgets/IdleScreen.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "IdleScreen.h"
|
||||
|
||||
#include "ImageDisplay.h"
|
||||
#include <imgui.h>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include "../UIManager.h"
|
||||
|
||||
class IdleScreenImpl : public IdleScreen
|
||||
{
|
||||
public:
|
||||
explicit IdleScreenImpl(std::shared_ptr<ImageDisplay> imageDisplay_) : imageDisplay(std::move(imageDisplay_))
|
||||
{
|
||||
auto [width, height] = imageDisplay->getImageSize(logoPath);
|
||||
logoSize = ImVec2(width, height);
|
||||
}
|
||||
|
||||
void update() final
|
||||
{
|
||||
// Adjust logo scale and speed based on viewport size so it is consistent
|
||||
auto viewportSize = ImGui::GetMainViewport()->Size;
|
||||
if (lastViewportSize.x != viewportSize.x || lastViewportSize.y != viewportSize.y)
|
||||
{
|
||||
float displaySizeScalePrev = lastViewportSize.x/1280.f;
|
||||
float displaySizeScale = viewportSize.x/1280.f;
|
||||
float adjustedScale = displaySizeScale / displaySizeScalePrev;
|
||||
scale = adjustedScale * 0.5f;
|
||||
speed = adjustedScale * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
void drawIdleScreen() final
|
||||
{
|
||||
setLogoPosition();
|
||||
|
||||
ImGui::SetCursorPos(currentLogoPosition);
|
||||
imageDisplay->drawImGuiImage(logoPath, scale, ColorValues::lumonBlue);
|
||||
}
|
||||
|
||||
void setLogoPosition()
|
||||
{
|
||||
ImVec2 windowSize = ImGui::GetWindowSize();
|
||||
ImVec2 windowPos = ImGui::GetWindowPos();
|
||||
|
||||
ImVec2 minPosition = ImVec2(windowPos.x, windowPos.y);
|
||||
ImVec2 maxPosition = ImVec2(windowPos.x + windowSize.x - scale*logoSize.x, windowPos.y + windowSize.y - scale*logoSize.y);
|
||||
|
||||
if (currentLogoPosition.x >= maxPosition.x)
|
||||
{
|
||||
nextOffset.x = -1;
|
||||
} else if (currentLogoPosition.x < minPosition.x)
|
||||
{
|
||||
nextOffset.x = 1;
|
||||
}
|
||||
|
||||
if (currentLogoPosition.y >= maxPosition.y)
|
||||
{
|
||||
nextOffset.y = -1;
|
||||
} else if (currentLogoPosition.y < minPosition.y)
|
||||
{
|
||||
nextOffset.y = 1;
|
||||
}
|
||||
|
||||
currentLogoPosition = ImVec2(currentLogoPosition.x + speed*nextOffset.x, currentLogoPosition.y + speed*nextOffset.y);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ImageDisplay> imageDisplay;
|
||||
|
||||
float speed = 0.5f;
|
||||
float scale = 0.5f;
|
||||
|
||||
ImVec2 windowSize;
|
||||
ImVec2 windowPos;
|
||||
|
||||
std::string logoPath = "lumon-logo.png";
|
||||
ImVec2 logoSize;
|
||||
ImVec2 lastViewportSize = ImVec2(1280, 720);
|
||||
|
||||
ImVec2 currentLogoPosition;
|
||||
ImVec2 nextOffset = ImVec2(1,1);
|
||||
|
||||
};
|
||||
|
||||
std::shared_ptr<IdleScreen> createIdleScreen(const std::shared_ptr<ImageDisplay>& imageDisplay)
|
||||
{
|
||||
return std::make_shared<IdleScreenImpl>(imageDisplay);
|
||||
}
|
14
src/UI/Widgets/IdleScreen.h
Normal file
14
src/UI/Widgets/IdleScreen.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
class ImageDisplay;
|
||||
|
||||
class IdleScreen {
|
||||
public:
|
||||
virtual void update() = 0;
|
||||
virtual void drawIdleScreen() = 0;
|
||||
|
||||
virtual ~IdleScreen() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<IdleScreen> createIdleScreen(const std::shared_ptr<ImageDisplay>& imageDisplay);
|
472
src/UI/Widgets/NumbersPanel.cpp
Normal file
472
src/UI/Widgets/NumbersPanel.cpp
Normal file
@ -0,0 +1,472 @@
|
||||
#include "NumbersPanel.h"
|
||||
|
||||
#include "Numbers/NumberGrid.h"
|
||||
#include "ImageDisplay.h"
|
||||
#include "Settings.h"
|
||||
#include "../UIManager.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <json.hpp>
|
||||
#include "PerlinNoise.hpp"
|
||||
|
||||
class NumbersPanelImpl : public NumbersPanel
|
||||
{
|
||||
public:
|
||||
explicit NumbersPanelImpl(std::shared_ptr<ImageDisplay> imageDisplay) : imageDisplay(std::move(imageDisplay))
|
||||
{
|
||||
numberGrid = createNumberGrid(gridSize);
|
||||
|
||||
// Update max bad groups for each bin
|
||||
auto badGroups = numberGrid->getBadGroups();
|
||||
for (auto &[id, group] : badGroups)
|
||||
{
|
||||
bins[group->binIdx].maxBadGroups++;
|
||||
}
|
||||
|
||||
// Load settings
|
||||
if (auto loadedSettings = loadSettings(settingsSavePath))
|
||||
{
|
||||
displaySettings = loadedSettings->displaySettings;
|
||||
controlSettings = loadedSettings->controlSettings;
|
||||
std::cout << "Successfully loaded settings from disk." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void init() final
|
||||
{
|
||||
// Load custom font
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
font = io.Fonts->AddFontFromFileTTF("./assets/Montserrat-Bold.ttf", 50.f);
|
||||
io.Fonts->Build();
|
||||
if (font == nullptr)
|
||||
{
|
||||
font = ImGui::GetDefaultFont();
|
||||
std::cerr << "Failed to load 'Montserrat-Bold' font." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void update() final
|
||||
{
|
||||
numberGrid->update();
|
||||
}
|
||||
|
||||
void drawNumbersPanel() final
|
||||
{
|
||||
bool displaySizeChanged = updateDisplaySettings(displayPresets, displaySettings.globalScale);
|
||||
|
||||
ImVec2 mousePos = ImGui::GetIO().MousePos;
|
||||
ImVec2 windowSize = ImGui::GetWindowSize();
|
||||
ImVec2 windowPos = ImGui::GetWindowPos();
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Update viewport
|
||||
bool viewportChanged = updateViewport(windowSize);
|
||||
|
||||
// Draw Overlays
|
||||
drawGraphicOverlays(windowPos, windowSize, draw_list);
|
||||
|
||||
// Draw Grid
|
||||
auto numberRefiningToBin = drawNumbersGrid(windowPos, windowSize, mousePos, viewportChanged || displaySizeChanged);
|
||||
|
||||
// Draw Bins
|
||||
drawBins(windowPos, windowSize, draw_list, numberRefiningToBin);
|
||||
|
||||
}
|
||||
|
||||
void triggerLoadAnimation() final
|
||||
{
|
||||
// Reset 'regenerate scale' on all numbers
|
||||
for (auto &[x, gridY] : numberGrid->getGrid())
|
||||
{
|
||||
for (auto &[y, gridNumber] : gridY)
|
||||
{
|
||||
gridNumber->regenerateScale = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<int> drawNumbersGrid(const ImVec2& windowPos, const ImVec2& windowSize, const ImVec2& mousePos, bool updateDisplayInfos)
|
||||
{
|
||||
std::optional<int> refiningToBin = std::nullopt;
|
||||
for (auto &[x, gridY] : numberGrid->getGrid())
|
||||
{
|
||||
for (auto &[y, gridNumber] : gridY)
|
||||
{
|
||||
std::string numberToDraw = "numbers/" + std::to_string(gridNumber->num) + ".png";
|
||||
auto [width, height] = imageDisplay->getImageSize(numberToDraw);
|
||||
double badScale = gridNumber->badGroup ? gridNumber->badGroup->scale : 0.0;
|
||||
if (updateDisplayInfos)
|
||||
{
|
||||
ImVec2 localNumberPos = ImVec2((x * displaySettings.gridSpacing + panelOffset.x)*panelScale, (y * displaySettings.gridSpacing + panelOffset.y)*panelScale);
|
||||
gridNumber->displayInfos.centerX = localNumberPos.x + windowPos.x;
|
||||
gridNumber->displayInfos.centerY = localNumberPos.y + windowPos.y;
|
||||
|
||||
double baseNumberScale = gridNumber->regenerateScale*displaySettings.imageScale*panelScale;
|
||||
double widthOffset = (baseNumberScale*width/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.centerY + heightOffset < windowPos.y + windowSize.y - displayPresets.numberWindowBufferBottom && gridNumber->displayInfos.centerY - heightOffset > windowPos.y + displayPresets.numberWindowBufferTop;
|
||||
|
||||
}
|
||||
|
||||
if (!gridNumber->displayInfos.isVisible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto centerPos = ImVec2(gridNumber->displayInfos.centerX, gridNumber->displayInfos.centerY);
|
||||
|
||||
if (gridNumber->badGroup && gridNumber->badGroup->refined)
|
||||
{
|
||||
float startX = gridNumber->displayInfos.centerX;
|
||||
float startY = gridNumber->displayInfos.centerY;
|
||||
|
||||
if (gridNumber->displayInfos.refinedX == -1)
|
||||
{
|
||||
gridNumber->displayInfos.refinedX = startX;
|
||||
gridNumber->displayInfos.refinedY = startY;
|
||||
}
|
||||
|
||||
auto binIdx = gridNumber->badGroup->binIdx;
|
||||
if (binIdx > 4)
|
||||
{
|
||||
std::cout << "Error: Bin index greater than expected. Setting to max." << std::endl;
|
||||
binIdx = 4;
|
||||
}
|
||||
|
||||
float distX = bins[binIdx].pos.x - gridNumber->displayInfos.refinedX;
|
||||
float distY = bins[binIdx].pos.y - gridNumber->displayInfos.refinedY;
|
||||
float distance = sqrt(distX * distX + distY * distY);
|
||||
|
||||
const float moveSpeed = 3.0f;
|
||||
if (distance > moveSpeed)
|
||||
{
|
||||
float dirX = distX / distance;
|
||||
float dirY = distY / distance;
|
||||
gridNumber->displayInfos.refinedX += dirX * moveSpeed;
|
||||
gridNumber->displayInfos.refinedY += dirY * moveSpeed;
|
||||
centerPos = ImVec2(gridNumber->displayInfos.refinedX, gridNumber->displayInfos.refinedY);
|
||||
|
||||
refiningToBin = gridNumber->badGroup->binIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridNumber->badGroup.reset(); // No longer a bad number
|
||||
gridNumber->num = numberGrid->randomNumber(0,9);
|
||||
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 = gridNumber->badGroup ? ImVec4(255,0,0,255) : ImVec4(255,255,255,numberAlpha);
|
||||
if (gridNumber->badGroup && gridNumber->badGroup->isActive)
|
||||
{
|
||||
col = ImVec4(255,255,0,numberAlpha);
|
||||
}
|
||||
if (!revealMap)
|
||||
{
|
||||
col = ImVec4(255,255,255,numberAlpha);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)));
|
||||
imageDisplay->drawImGuiImage(numberToDraw, combinedScale, col);
|
||||
}
|
||||
}
|
||||
t += 1;
|
||||
|
||||
return refiningToBin;
|
||||
}
|
||||
|
||||
void drawBins(const ImVec2& windowPos, const ImVec2& windowSize, ImDrawList* drawList, std::optional<int> numberRefiningToBin)
|
||||
{
|
||||
std::string binPercentPath = "bins/bin-percent.png";
|
||||
auto [widthP, heightP] = imageDisplay->getImageSize(binPercentPath);
|
||||
for (auto &b : bins)
|
||||
{
|
||||
auto pos = b.updatePos(windowSize, windowPos, displayPresets.numberWindowBufferBottom - displayPresets.binOffset);
|
||||
|
||||
std::string binPath = "bins/bin0" + std::to_string(b.id) + ".png";
|
||||
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)));
|
||||
imageDisplay->drawImGuiImage(binPath, displayPresets.binImageScale, ColorValues::lumonBlue);
|
||||
|
||||
auto percentPos = ImVec2(pos.x, pos.y + displayPresets.binPercentBarOffset);
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
double percentD = double(b.badGroupsRefined) / double(b.maxBadGroups);
|
||||
int percentInt = lround(percentD * 100.f);
|
||||
std::string percentString = std::to_string(percentInt) + "%";
|
||||
drawList->AddText(font, displayPresets.fontSize, ImVec2(trCorner.x + 5.f, (trCorner.y + brCorner.y)/2.f - displayPresets.fontSize/2.f), ColorValues::lumonBlue, percentString.c_str());
|
||||
|
||||
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
|
||||
if (numberRefiningToBin && *numberRefiningToBin == b.id - 1)
|
||||
{
|
||||
std::string binOpenPath = "bins/bin-open.png";
|
||||
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)));
|
||||
imageDisplay->drawImGuiImage(binOpenPath, displayPresets.binImageScale, ColorValues::lumonBlue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawGraphicOverlays(const ImVec2& windowPos, const ImVec2& windowSize, ImDrawList* drawList)
|
||||
{
|
||||
// Header box
|
||||
ImVec2 headerBoxMin = ImVec2(windowPos.x + displayPresets.headerBoxBufferX, windowPos.y + displayPresets.headerBoxBufferY);
|
||||
ImVec2 headerBoxMax = ImVec2(windowPos.x + windowSize.x - displayPresets.headerBoxBufferX - displayPresets.headerImageOffsetX, windowPos.y + displayPresets.numberWindowBufferTop - displayPresets.lineGraphicsSpacing - displayPresets.headerBoxBufferY);
|
||||
drawList->AddRect(headerBoxMin, headerBoxMax, ColorValues::lumonBlue);
|
||||
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");
|
||||
// Lumon logo
|
||||
std::string logoPath = "lumon-logo.png";
|
||||
auto [widthH, heightH] = imageDisplay->getImageSize(logoPath);
|
||||
ImGui::SetCursorPos(ImVec2(headerBoxMax.x - (widthH*displayPresets.headerImageScale)/2.f, (headerBoxMax.y + headerBoxMin.y)/2.f - (heightH*displayPresets.headerImageScale)/2.f));
|
||||
imageDisplay->drawImGuiImage(logoPath, displayPresets.headerImageScale, ColorValues::lumonBlue);
|
||||
|
||||
// Horizontal lines
|
||||
auto drawLine = [&](const float y)
|
||||
{
|
||||
drawList->AddLine(ImVec2(windowPos.x, y), ImVec2(windowPos.x + windowSize.x, y), ColorValues::lumonBlue, displayPresets.lineThickness);
|
||||
};
|
||||
|
||||
float topLineY = windowPos.y + displayPresets.numberWindowBufferTop;
|
||||
drawLine(topLineY);
|
||||
drawLine(topLineY - displayPresets.lineGraphicsSpacing);
|
||||
|
||||
float bottomLineY = windowPos.y + windowSize.y - displayPresets.numberWindowBufferBottom;
|
||||
drawLine(bottomLineY);
|
||||
drawLine(bottomLineY + displayPresets.lineGraphicsSpacing);
|
||||
}
|
||||
|
||||
bool updateViewport(const ImVec2& windowSize)
|
||||
{
|
||||
static bool viewportInit = false;
|
||||
bool viewportChanged = !viewportInit;
|
||||
// Handle arrow key input
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
|
||||
{
|
||||
panelOffset.x += controlSettings.arrowSensitivity;
|
||||
viewportChanged = true;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow))
|
||||
{
|
||||
panelOffset.x -= controlSettings.arrowSensitivity;
|
||||
viewportChanged = true;
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow))
|
||||
{
|
||||
panelOffset.y += controlSettings.arrowSensitivity;
|
||||
viewportChanged = true;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow))
|
||||
{
|
||||
panelOffset.y -= controlSettings.arrowSensitivity;
|
||||
viewportChanged = true;
|
||||
}
|
||||
|
||||
// Handle zoom
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Comma))
|
||||
{
|
||||
panelScale -= controlSettings.zoomSensitivity;
|
||||
viewportChanged = true;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_Period))
|
||||
{
|
||||
panelScale += controlSettings.zoomSensitivity;
|
||||
viewportChanged = true;
|
||||
}
|
||||
// panelScale = std::clamp(panelScale, 0.8f, 1.8f);
|
||||
|
||||
if (viewportChanged)
|
||||
{
|
||||
// Clamp movement to within grid boundaries
|
||||
float gridWidthScaled = gridSize * displaySettings.gridSpacing * panelScale;
|
||||
float gridHeightScaled = gridSize * displaySettings.gridSpacing * panelScale;
|
||||
|
||||
float minOffsetX = -gridWidthScaled + windowSize.x;
|
||||
float maxOffsetX = 0;
|
||||
float minOffsetY = -gridHeightScaled + windowSize.y - displayPresets.numberWindowBufferTop;
|
||||
float maxOffsetY = displayPresets.numberWindowBufferTop;
|
||||
|
||||
if (!viewportInit)
|
||||
{
|
||||
// Start at center of grid
|
||||
panelOffset.x = (windowSize.x - gridWidthScaled) / 2.0f / panelScale;
|
||||
panelOffset.y = (windowSize.y - gridHeightScaled) / 2.0f / panelScale;
|
||||
}
|
||||
|
||||
panelOffset.x = std::clamp(panelOffset.x, minOffsetX / panelScale, maxOffsetX / panelScale);
|
||||
panelOffset.y = std::clamp(panelOffset.y, minOffsetY / panelScale, maxOffsetY / panelScale);
|
||||
}
|
||||
|
||||
viewportInit = true;
|
||||
return viewportChanged;
|
||||
}
|
||||
|
||||
void drawSettings() final
|
||||
{
|
||||
ImGui::SetWindowFontScale(displayPresets.settingsFontScale);
|
||||
if (ImGui::Button("Save Settings"))
|
||||
{
|
||||
saveSettings(Settings{displaySettings, controlSettings}, settingsSavePath);
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Display:");
|
||||
ImGui::InputFloat("Global Scale", &displaySettings.globalScale);
|
||||
ImGui::InputFloat("Numbers Image Scale", &displaySettings.imageScale);
|
||||
ImGui::InputFloat("Numbers Grid Spacing", &displaySettings.gridSpacing);
|
||||
ImGui::InputFloat("Mouse Scale Radius", &displaySettings.mouseScaleRadius);
|
||||
ImGui::InputFloat("Mouse Scale Multiplier", &displaySettings.mouseScaleMultiplier);
|
||||
ImGui::Text("Noise:");
|
||||
ImGui::InputFloat("Noise Speed", &displaySettings.noiseSpeed);
|
||||
ImGui::InputFloat("Noise Scale", &displaySettings.noiseScale);
|
||||
ImGui::InputFloat("Noise Scale Offset", &displaySettings.noiseScaleOffset);
|
||||
ImGui::Text("Controls:");
|
||||
ImGui::InputFloat("Arrow Sensitivity", &controlSettings.arrowSensitivity);
|
||||
ImGui::InputFloat("Zoom Sensitivity", &controlSettings.zoomSensitivity);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Debug:");
|
||||
ImGui::Checkbox("revealMap", &revealMap);
|
||||
}
|
||||
|
||||
bool updateDisplaySettings(PresetDisplaySettings &settings, float globalScale)
|
||||
{
|
||||
auto viewportSize = ImGui::GetMainViewport()->Size;
|
||||
if (lastViewportSize.x == viewportSize.x && lastViewportSize.y == viewportSize.y && lastGlobalScale == globalScale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
float displaySizeScalePrev = lastViewportSize.x/1280.f * lastGlobalScale;
|
||||
float displaySizeScale = viewportSize.x/1280.f * globalScale;
|
||||
float newScale = displaySizeScale / displaySizeScalePrev;
|
||||
settings.numberWindowBufferTop *= newScale;
|
||||
settings.numberWindowBufferBottom *= newScale;
|
||||
settings.headerImageScale *= newScale;
|
||||
settings.headerImageOffsetX *= newScale;
|
||||
settings.headerBoxBufferX *= newScale;
|
||||
settings.headerBoxBufferY *= newScale;
|
||||
settings.binImageScale *= newScale;
|
||||
settings.binOffset *= newScale;
|
||||
settings.binPercentBarOffset *= newScale;
|
||||
settings.fontSize *= newScale;
|
||||
settings.settingsFontScale *= newScale;
|
||||
settings.lineGraphicsSpacing *= newScale;
|
||||
settings.lineThickness *= newScale;
|
||||
|
||||
lastViewportSize = viewportSize;
|
||||
lastGlobalScale = globalScale;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
float getScaleFromCursor(const ImVec2& globalNumberPos, const ImVec2& mousePos) const
|
||||
{
|
||||
float distX = mousePos.x - globalNumberPos.x;
|
||||
float distY = mousePos.y - globalNumberPos.y;
|
||||
float distance = sqrt(distX * distX + distY * distY);
|
||||
|
||||
if (distance < displaySettings.mouseScaleRadius)
|
||||
{
|
||||
return 1.0f + (displaySettings.mouseScaleRadius - distance) / displaySettings.mouseScaleRadius * displaySettings.mouseScaleMultiplier;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
int gridSize = 100;
|
||||
std::shared_ptr<ImageDisplay> imageDisplay;
|
||||
std::shared_ptr<NumberGrid> numberGrid;
|
||||
|
||||
ImFont* font;
|
||||
|
||||
ImVec2 panelOffset = ImVec2(0,0);
|
||||
float panelScale = 0.3f;
|
||||
|
||||
std::string settingsSavePath = "./settings.json";
|
||||
DisplaySettings displaySettings;
|
||||
ControlSettings controlSettings;
|
||||
|
||||
PresetDisplaySettings displayPresets;
|
||||
|
||||
siv::PerlinNoise perlin{ 555 };
|
||||
int t = 0;
|
||||
|
||||
// Debug options
|
||||
bool revealMap = false;
|
||||
|
||||
struct Bin
|
||||
{
|
||||
int id;
|
||||
ImVec2 pos = ImVec2(0, 0);
|
||||
|
||||
int badGroupsRefined = 0;
|
||||
int maxBadGroups = 0;
|
||||
|
||||
ImVec2 updatePos(const ImVec2 &windowSize, const ImVec2 &windowPos, float offsetY)
|
||||
{
|
||||
pos = ImVec2(windowPos.x + (windowSize.x / 6)*id, windowPos.y + windowSize.y - offsetY);
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
std::array<Bin, 5> bins = {Bin{1}, Bin{2}, Bin{3}, Bin{4}, Bin{5}};
|
||||
|
||||
ImVec2 lastViewportSize = ImVec2(1280, 720);
|
||||
float lastGlobalScale = 1.f;
|
||||
};
|
||||
|
||||
std::shared_ptr<NumbersPanel> createNumbersPanel(const std::shared_ptr<ImageDisplay>& imageDisplay)
|
||||
{
|
||||
return std::make_shared<NumbersPanelImpl>(imageDisplay);
|
||||
}
|
20
src/UI/Widgets/NumbersPanel.h
Normal file
20
src/UI/Widgets/NumbersPanel.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
class ImageDisplay;
|
||||
|
||||
class NumbersPanel {
|
||||
public:
|
||||
virtual void init() = 0;
|
||||
|
||||
virtual void update() = 0;
|
||||
virtual void drawNumbersPanel() = 0;
|
||||
|
||||
virtual void triggerLoadAnimation() = 0;
|
||||
|
||||
virtual void drawSettings() = 0;
|
||||
|
||||
virtual ~NumbersPanel() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<NumbersPanel> createNumbersPanel(const std::shared_ptr<ImageDisplay>& imageDisplay);
|
132
src/UI/Widgets/Settings.h
Normal file
132
src/UI/Widgets/Settings.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <json.hpp>
|
||||
|
||||
struct PresetDisplaySettings
|
||||
{
|
||||
float numberWindowBufferTop = 125.f;
|
||||
float numberWindowBufferBottom = 115.f;
|
||||
|
||||
float headerImageScale = 0.25f;
|
||||
float headerImageOffsetX = 50.f;
|
||||
float headerBoxBufferX = 55.f;
|
||||
float headerBoxBufferY = 37.f;
|
||||
|
||||
float binImageScale = 0.75f;
|
||||
float binOffset = 40.f;
|
||||
float binPercentBarOffset = 50.f;
|
||||
float fontSize = 25.f;
|
||||
float settingsFontScale = 0.3f;
|
||||
|
||||
float lineGraphicsSpacing = 10.f;
|
||||
float lineThickness = 5.f;
|
||||
};
|
||||
|
||||
// Helpers
|
||||
inline std::optional<nlohmann::json> loadSettingsFromJson(const std::string& jsonPath)
|
||||
{
|
||||
try {
|
||||
std::ifstream file(jsonPath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error: Cannot open settings file: " << jsonPath << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
nlohmann::json json;
|
||||
file >> json;
|
||||
return json;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error loading json: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
inline void saveSettingsJson(const nlohmann::json& settingsJson, const std::string& jsonPath)
|
||||
{
|
||||
try {
|
||||
std::ofstream file(jsonPath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error: Cannot open file for writing: " << jsonPath << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
file << settingsJson.dump(4);
|
||||
file.flush();
|
||||
file.close();
|
||||
std::cout << "Settings saved to disk." << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error saving settings: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Numbers Panel settings
|
||||
struct DisplaySettings
|
||||
{
|
||||
float globalScale = 1.f;
|
||||
float imageScale = 0.75f;
|
||||
float gridSpacing = 200.f;
|
||||
|
||||
float mouseScaleRadius = 100.f;
|
||||
float mouseScaleMultiplier = 2.f;
|
||||
|
||||
float noiseSpeed = 0.004f;
|
||||
float noiseScale = 1.f;
|
||||
float noiseScaleOffset = 15;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(DisplaySettings,
|
||||
globalScale,
|
||||
imageScale,
|
||||
gridSpacing,
|
||||
mouseScaleRadius,
|
||||
mouseScaleMultiplier,
|
||||
noiseSpeed,
|
||||
noiseScale,
|
||||
noiseScaleOffset
|
||||
);
|
||||
};
|
||||
|
||||
struct ControlSettings
|
||||
{
|
||||
float arrowSensitivity = 25.f;
|
||||
float zoomSensitivity = 0.1f;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(ControlSettings, arrowSensitivity, zoomSensitivity);
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
DisplaySettings displaySettings;
|
||||
ControlSettings controlSettings;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Settings, displaySettings, controlSettings);
|
||||
};
|
||||
|
||||
inline std::optional<Settings> loadSettings(const std::string& jsonPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (auto json = loadSettingsFromJson(jsonPath))
|
||||
{
|
||||
return json->get<Settings>();
|
||||
}
|
||||
} catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Error loading settings: " << e.what() << std::endl;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
inline void saveSettings(const Settings& settings, const std::string& jsonPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
nlohmann::json json = settings;
|
||||
saveSettingsJson(json, jsonPath);
|
||||
} catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Error saving settings: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
91
src/main.cpp
Normal file
91
src/main.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "imgui.h"
|
||||
#include "backends/imgui_impl_glfw.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "UI/UIManager.h"
|
||||
|
||||
void glfw_error_callback(int error, const char* description) {
|
||||
std::cerr << "GLFW Error (" << error << "): " << description << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Set error callback
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
|
||||
// Initialize GLFW
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool fullscreen = false;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--full-screen") == 0) {
|
||||
fullscreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLFWwindow* window;
|
||||
if (fullscreen)
|
||||
{
|
||||
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor);
|
||||
window = glfwCreateWindow(mode->width, mode->height, "MDR Severance", primaryMonitor, nullptr);
|
||||
} else
|
||||
{
|
||||
window = glfwCreateWindow(1920, 1080, "MDR Severance", nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (!window) {
|
||||
std::cerr << "Failed to create GLFW window!" << std::endl;
|
||||
glfwTerminate();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the OpenGL context
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1); // Enable vsync
|
||||
|
||||
std::shared_ptr<UIManager> uiManager = createUIManager();
|
||||
uiManager->init();
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
// Poll events
|
||||
glfwPollEvents();
|
||||
|
||||
// Close application with 'ESCAPE' key
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
}
|
||||
|
||||
// Clear the screen
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Start ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// Draw
|
||||
uiManager->draw();
|
||||
|
||||
// Update
|
||||
uiManager->update();
|
||||
|
||||
// Render ImGui
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
// Swap buffers
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
uiManager->cleanup();
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user