From e6bdeaef5012e58a16b4f8a74ca5429efc886de5 Mon Sep 17 00:00:00 2001 From: kolo Date: Thu, 5 Mar 2026 11:34:43 +0300 Subject: [PATCH] skelet --- CMakeLists.txt | 2 + include/App.hpp | 5 ++ include/SortHistory.hpp | 27 ++++++++ include/UI.hpp | 6 ++ src/App.cpp | 32 ++++++++- src/SortHistory.cpp | 31 +++++++++ src/UI.cpp | 142 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 include/SortHistory.hpp create mode 100644 src/SortHistory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7314a43..44753bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ if(WIN32) src/UI.cpp src/OperationsHistory.cpp src/ProgressMap.cpp + src/SortHistory.cpp src/sorters/BubbleSorter.cpp src/sorters/SelectionSorter.cpp src/sorters/InsertionSorter.cpp @@ -45,6 +46,7 @@ else() src/UI.cpp src/OperationsHistory.cpp src/ProgressMap.cpp + src/SortHistory.cpp src/sorters/BubbleSorter.cpp src/sorters/SelectionSorter.cpp src/sorters/InsertionSorter.cpp diff --git a/include/App.hpp b/include/App.hpp index 0ea764b..7bb353a 100644 --- a/include/App.hpp +++ b/include/App.hpp @@ -7,6 +7,7 @@ #include "UI.hpp" #include "OperationsHistory.hpp" #include "ProgressMap.hpp" +#include "SortHistory.hpp" class App { public: @@ -46,4 +47,8 @@ private: sf::Font bottomPanelFont_; bool bottomPanelFontLoaded_; bool showInfo_; + + SortHistory history_; + sf::Clock sortTimer_; + bool recordedThisRun_; }; diff --git a/include/SortHistory.hpp b/include/SortHistory.hpp new file mode 100644 index 0000000..88c834d --- /dev/null +++ b/include/SortHistory.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +struct SortRecord { + std::string algorithmName; + int arraySize; + int comparisons; + int swaps; + float normalizedTime; + float relativeSpeed; + + SortRecord() : arraySize(0), comparisons(0), swaps(0), normalizedTime(0.0f), relativeSpeed(1.0f) {} +}; + +class SortHistory { +public: + SortHistory(); + + void add(const SortRecord& record); + const std::deque& getRecords() const; + void clear(); + +private: + std::deque records_; + static const size_t MAX_RECORDS = 20; +}; diff --git a/include/UI.hpp b/include/UI.hpp index 7ca99f5..fac85f9 100644 --- a/include/UI.hpp +++ b/include/UI.hpp @@ -1,6 +1,7 @@ #pragma once #include #include "Sorter.hpp" +#include "SortHistory.hpp" #include class UI { @@ -10,6 +11,10 @@ public: void update(const Sorter& sorter, bool isPlaying, bool isFinished, int stepsPerFrame, const Array& array); void draw(sf::RenderWindow& window); void drawInfoOverlay(sf::RenderWindow& window); + void drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history); + void toggleHistory(); + void closeHistory(); + bool isHistoryOpen() const; private: sf::Font font_; @@ -24,4 +29,5 @@ private: sf::RectangleShape leftBackground_; sf::RectangleShape rightBackground_; bool fontLoaded_; + bool historyOpen_; }; diff --git a/src/App.cpp b/src/App.cpp index 101b680..6f9d25d 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -25,7 +25,8 @@ App::App() , lastComparisons_(0) , lastSwaps_(0) , bottomPanelFontLoaded_(false) - , showInfo_(false) { + , showInfo_(false) + , recordedThisRun_(false) { window_.setFramerateLimit(60); generateBeepSound(); beepSound_.setBuffer(beepBuffer_); @@ -125,6 +126,8 @@ void App::handleEvents() { lastComparisons_ = 0; lastSwaps_ = 0; stepsPerFrame_ = 1; + sortTimer_.restart(); + recordedThisRun_ = false; break; case sf::Keyboard::Q: @@ -133,6 +136,16 @@ void App::handleEvents() { case sf::Keyboard::I: showInfo_ = !showInfo_; + if (showInfo_) { + ui_.closeHistory(); + } + break; + + case sf::Keyboard::H: + ui_.toggleHistory(); + if (ui_.isHistoryOpen()) { + showInfo_ = false; + } break; default: @@ -201,6 +214,17 @@ void App::update(float dt) { isSweeping_ = true; sweepIndex_ = 0; sweepTimer_ = 0.0f; + + if (!recordedThisRun_) { + SortRecord record; + record.algorithmName = currentSorter_->getName(); + record.arraySize = array_.getSize(); + record.comparisons = array_.getComparisons(); + record.swaps = array_.getSwaps(); + record.normalizedTime = sortTimer_.getElapsedTime().asSeconds() * static_cast(stepsPerFrame_); + history_.add(record); + recordedThisRun_ = true; + } } } } @@ -292,6 +316,10 @@ void App::render() { ui_.drawInfoOverlay(window_); } + if (ui_.isHistoryOpen()) { + ui_.drawHistoryOverlay(window_, history_); + } + window_.display(); } @@ -369,4 +397,6 @@ void App::switchSorter(std::unique_ptr newSorter) { lastComparisons_ = 0; lastSwaps_ = 0; stepsPerFrame_ = 1; + sortTimer_.restart(); + recordedThisRun_ = false; } diff --git a/src/SortHistory.cpp b/src/SortHistory.cpp new file mode 100644 index 0000000..64c4dff --- /dev/null +++ b/src/SortHistory.cpp @@ -0,0 +1,31 @@ +#include "SortHistory.hpp" +#include + +SortHistory::SortHistory() { +} + +void SortHistory::add(const SortRecord& record) { + records_.push_back(record); + if (records_.size() > MAX_RECORDS) { + records_.pop_front(); + } + + float minNormalizedTime = std::numeric_limits::max(); + for (const auto& rec : records_) { + if (rec.normalizedTime < minNormalizedTime) { + minNormalizedTime = rec.normalizedTime; + } + } + + for (auto& rec : records_) { + rec.relativeSpeed = rec.normalizedTime / minNormalizedTime; + } +} + +const std::deque& SortHistory::getRecords() const { + return records_; +} + +void SortHistory::clear() { + records_.clear(); +} diff --git a/src/UI.cpp b/src/UI.cpp index 57cf923..26ec61c 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -3,7 +3,7 @@ #include #include -UI::UI() : fontLoaded_(false) { +UI::UI() : fontLoaded_(false), historyOpen_(false) { if (!font_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) { std::cerr << "Failed to load font" << std::endl; return; @@ -236,7 +236,7 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) { footer.setCharacterSize(30); footer.setScale(0.5f, 0.5f); footer.setFillColor(sf::Color(100, 100, 100)); - footer.setString(toSfStr(u8"Нажми [I] чтобы закрыть | [Q] выход | [Space] старт/пауза | [R] перемешать")); + footer.setString(toSfStr(u8"Нажми [I] чтобы закрыть | [H] история | [Q] выход | [Space] старт/пауза | [R] перемешать")); float totalHeight = 0.0f; totalHeight += title.getGlobalBounds().height + 10.0f; @@ -310,3 +310,141 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) { footer.setPosition(std::floor((windowWidth - footerBounds.width) * 0.5f), std::floor(currentY)); window.draw(footer); } + +void UI::toggleHistory() { + historyOpen_ = !historyOpen_; +} + +void UI::closeHistory() { + historyOpen_ = false; +} + +bool UI::isHistoryOpen() const { + return historyOpen_; +} + +void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history) { + if (!fontLoaded_) { + return; + } + + auto toSfStr = [](const char* utf8) -> sf::String { + std::string s(utf8); + return sf::String::fromUtf8(s.begin(), s.end()); + }; + + float windowWidth = static_cast(window.getSize().x); + float windowHeight = static_cast(window.getSize().y); + + sf::RectangleShape overlay(sf::Vector2f(windowWidth, windowHeight)); + overlay.setPosition(0.0f, 0.0f); + overlay.setFillColor(sf::Color(8, 10, 18, 230)); + window.draw(overlay); + + sf::Text title; + title.setFont(font_); + title.setCharacterSize(80); + title.setScale(0.5f, 0.5f); + title.setFillColor(sf::Color::White); + title.setString(toSfStr(u8"История сортировок [H — закрыть]")); + + const auto& records = history.getRecords(); + + float startY = 80.0f; + + sf::FloatRect titleBounds = title.getGlobalBounds(); + title.setPosition(std::floor((windowWidth - titleBounds.width) * 0.5f), std::floor(startY)); + window.draw(title); + startY += titleBounds.height + 30.0f; + + if (records.empty()) { + sf::Text emptyText; + emptyText.setFont(font_); + emptyText.setCharacterSize(40); + emptyText.setScale(0.5f, 0.5f); + emptyText.setFillColor(sf::Color(160, 160, 160)); + emptyText.setString(toSfStr(u8"Нет завершённых сортировок")); + + sf::FloatRect emptyBounds = emptyText.getGlobalBounds(); + emptyText.setPosition(std::floor((windowWidth - emptyBounds.width) * 0.5f), std::floor(windowHeight * 0.5f)); + window.draw(emptyText); + return; + } + + sf::Text header; + header.setFont(font_); + header.setCharacterSize(36); + header.setScale(0.5f, 0.5f); + header.setFillColor(sf::Color(0, 220, 255)); + header.setString(toSfStr(u8"Алгоритм Размер Сравнений Свапов Скорость")); + + float tableX = 100.0f; + header.setPosition(tableX, startY); + window.draw(header); + startY += header.getGlobalBounds().height + 15.0f; + + sf::RectangleShape separator(sf::Vector2f(windowWidth - 200.0f, 1.0f)); + separator.setPosition(tableX, startY); + separator.setFillColor(sf::Color(60, 70, 90)); + window.draw(separator); + startY += 10.0f; + + int displayCount = 0; + for (auto it = records.rbegin(); it != records.rend() && displayCount < 15; ++it, ++displayCount) { + const SortRecord& record = *it; + + float rowHeight = 25.0f; + bool isNewest = (displayCount == 0); + + sf::RectangleShape rowBg(sf::Vector2f(windowWidth - 200.0f, rowHeight)); + rowBg.setPosition(tableX, startY); + + if (isNewest) { + rowBg.setFillColor(sf::Color(30, 60, 90, 150)); + } else if (displayCount % 2 == 1) { + rowBg.setFillColor(sf::Color(15, 18, 25, 100)); + } else { + rowBg.setFillColor(sf::Color(0, 0, 0, 0)); + } + window.draw(rowBg); + + char algoBuffer[256]; + snprintf(algoBuffer, sizeof(algoBuffer), "%-20s %6d %9d %6d ", + record.algorithmName.c_str(), + record.arraySize, + record.comparisons, + record.swaps); + + sf::Text rowText; + rowText.setFont(font_); + rowText.setCharacterSize(32); + rowText.setScale(0.5f, 0.5f); + rowText.setFillColor(isNewest ? sf::Color(100, 255, 150) : sf::Color(200, 200, 200)); + rowText.setString(algoBuffer); + rowText.setPosition(tableX, startY + 2.0f); + window.draw(rowText); + + char speedBuffer[32]; + snprintf(speedBuffer, sizeof(speedBuffer), "%.2fx", record.relativeSpeed); + + sf::Text speedText; + speedText.setFont(font_); + speedText.setCharacterSize(32); + speedText.setScale(0.5f, 0.5f); + + if (record.relativeSpeed == 1.0f) { + speedText.setFillColor(sf::Color(100, 255, 120)); + } else if (record.relativeSpeed >= 2.0f) { + speedText.setFillColor(sf::Color(255, 160, 60)); + } else { + speedText.setFillColor(isNewest ? sf::Color(100, 255, 150) : sf::Color(200, 200, 200)); + } + + speedText.setString(speedBuffer); + float speedX = tableX + rowText.getGlobalBounds().width; + speedText.setPosition(speedX, startY + 2.0f); + window.draw(speedText); + + startY += rowHeight; + } +}