mirror of
https://github.com/koloideal/SortLab.git
synced 2026-06-10 02:15:31 +03:00
skelet
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
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<SortRecord>& getRecords() const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::deque<SortRecord> records_;
|
||||
static const size_t MAX_RECORDS = 20;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include "Sorter.hpp"
|
||||
#include "SortHistory.hpp"
|
||||
#include <string>
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
+31
-1
@@ -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<float>(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<Sorter> newSorter) {
|
||||
lastComparisons_ = 0;
|
||||
lastSwaps_ = 0;
|
||||
stepsPerFrame_ = 1;
|
||||
sortTimer_.restart();
|
||||
recordedThisRun_ = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "SortHistory.hpp"
|
||||
#include <limits>
|
||||
|
||||
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<float>::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<SortRecord>& SortHistory::getRecords() const {
|
||||
return records_;
|
||||
}
|
||||
|
||||
void SortHistory::clear() {
|
||||
records_.clear();
|
||||
}
|
||||
+140
-2
@@ -3,7 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
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<float>(window.getSize().x);
|
||||
float windowHeight = static_cast<float>(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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user