mirror of
https://github.com/koloideal/SortLab.git
synced 2026-06-10 10:25:30 +03:00
skelet
This commit is contained in:
@@ -25,6 +25,7 @@ if(WIN32)
|
|||||||
src/UI.cpp
|
src/UI.cpp
|
||||||
src/OperationsHistory.cpp
|
src/OperationsHistory.cpp
|
||||||
src/ProgressMap.cpp
|
src/ProgressMap.cpp
|
||||||
|
src/SortHistory.cpp
|
||||||
src/sorters/BubbleSorter.cpp
|
src/sorters/BubbleSorter.cpp
|
||||||
src/sorters/SelectionSorter.cpp
|
src/sorters/SelectionSorter.cpp
|
||||||
src/sorters/InsertionSorter.cpp
|
src/sorters/InsertionSorter.cpp
|
||||||
@@ -45,6 +46,7 @@ else()
|
|||||||
src/UI.cpp
|
src/UI.cpp
|
||||||
src/OperationsHistory.cpp
|
src/OperationsHistory.cpp
|
||||||
src/ProgressMap.cpp
|
src/ProgressMap.cpp
|
||||||
|
src/SortHistory.cpp
|
||||||
src/sorters/BubbleSorter.cpp
|
src/sorters/BubbleSorter.cpp
|
||||||
src/sorters/SelectionSorter.cpp
|
src/sorters/SelectionSorter.cpp
|
||||||
src/sorters/InsertionSorter.cpp
|
src/sorters/InsertionSorter.cpp
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "UI.hpp"
|
#include "UI.hpp"
|
||||||
#include "OperationsHistory.hpp"
|
#include "OperationsHistory.hpp"
|
||||||
#include "ProgressMap.hpp"
|
#include "ProgressMap.hpp"
|
||||||
|
#include "SortHistory.hpp"
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
public:
|
public:
|
||||||
@@ -46,4 +47,8 @@ private:
|
|||||||
sf::Font bottomPanelFont_;
|
sf::Font bottomPanelFont_;
|
||||||
bool bottomPanelFontLoaded_;
|
bool bottomPanelFontLoaded_;
|
||||||
bool showInfo_;
|
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
|
#pragma once
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include "Sorter.hpp"
|
#include "Sorter.hpp"
|
||||||
|
#include "SortHistory.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class UI {
|
class UI {
|
||||||
@@ -10,6 +11,10 @@ public:
|
|||||||
void update(const Sorter& sorter, bool isPlaying, bool isFinished, int stepsPerFrame, const Array& array);
|
void update(const Sorter& sorter, bool isPlaying, bool isFinished, int stepsPerFrame, const Array& array);
|
||||||
void draw(sf::RenderWindow& window);
|
void draw(sf::RenderWindow& window);
|
||||||
void drawInfoOverlay(sf::RenderWindow& window);
|
void drawInfoOverlay(sf::RenderWindow& window);
|
||||||
|
void drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history);
|
||||||
|
void toggleHistory();
|
||||||
|
void closeHistory();
|
||||||
|
bool isHistoryOpen() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sf::Font font_;
|
sf::Font font_;
|
||||||
@@ -24,4 +29,5 @@ private:
|
|||||||
sf::RectangleShape leftBackground_;
|
sf::RectangleShape leftBackground_;
|
||||||
sf::RectangleShape rightBackground_;
|
sf::RectangleShape rightBackground_;
|
||||||
bool fontLoaded_;
|
bool fontLoaded_;
|
||||||
|
bool historyOpen_;
|
||||||
};
|
};
|
||||||
|
|||||||
+31
-1
@@ -25,7 +25,8 @@ App::App()
|
|||||||
, lastComparisons_(0)
|
, lastComparisons_(0)
|
||||||
, lastSwaps_(0)
|
, lastSwaps_(0)
|
||||||
, bottomPanelFontLoaded_(false)
|
, bottomPanelFontLoaded_(false)
|
||||||
, showInfo_(false) {
|
, showInfo_(false)
|
||||||
|
, recordedThisRun_(false) {
|
||||||
window_.setFramerateLimit(60);
|
window_.setFramerateLimit(60);
|
||||||
generateBeepSound();
|
generateBeepSound();
|
||||||
beepSound_.setBuffer(beepBuffer_);
|
beepSound_.setBuffer(beepBuffer_);
|
||||||
@@ -125,6 +126,8 @@ void App::handleEvents() {
|
|||||||
lastComparisons_ = 0;
|
lastComparisons_ = 0;
|
||||||
lastSwaps_ = 0;
|
lastSwaps_ = 0;
|
||||||
stepsPerFrame_ = 1;
|
stepsPerFrame_ = 1;
|
||||||
|
sortTimer_.restart();
|
||||||
|
recordedThisRun_ = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case sf::Keyboard::Q:
|
case sf::Keyboard::Q:
|
||||||
@@ -133,6 +136,16 @@ void App::handleEvents() {
|
|||||||
|
|
||||||
case sf::Keyboard::I:
|
case sf::Keyboard::I:
|
||||||
showInfo_ = !showInfo_;
|
showInfo_ = !showInfo_;
|
||||||
|
if (showInfo_) {
|
||||||
|
ui_.closeHistory();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case sf::Keyboard::H:
|
||||||
|
ui_.toggleHistory();
|
||||||
|
if (ui_.isHistoryOpen()) {
|
||||||
|
showInfo_ = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -201,6 +214,17 @@ void App::update(float dt) {
|
|||||||
isSweeping_ = true;
|
isSweeping_ = true;
|
||||||
sweepIndex_ = 0;
|
sweepIndex_ = 0;
|
||||||
sweepTimer_ = 0.0f;
|
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_);
|
ui_.drawInfoOverlay(window_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui_.isHistoryOpen()) {
|
||||||
|
ui_.drawHistoryOverlay(window_, history_);
|
||||||
|
}
|
||||||
|
|
||||||
window_.display();
|
window_.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,4 +397,6 @@ void App::switchSorter(std::unique_ptr<Sorter> newSorter) {
|
|||||||
lastComparisons_ = 0;
|
lastComparisons_ = 0;
|
||||||
lastSwaps_ = 0;
|
lastSwaps_ = 0;
|
||||||
stepsPerFrame_ = 1;
|
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 <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
UI::UI() : fontLoaded_(false) {
|
UI::UI() : fontLoaded_(false), historyOpen_(false) {
|
||||||
if (!font_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) {
|
if (!font_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) {
|
||||||
std::cerr << "Failed to load font" << std::endl;
|
std::cerr << "Failed to load font" << std::endl;
|
||||||
return;
|
return;
|
||||||
@@ -236,7 +236,7 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) {
|
|||||||
footer.setCharacterSize(30);
|
footer.setCharacterSize(30);
|
||||||
footer.setScale(0.5f, 0.5f);
|
footer.setScale(0.5f, 0.5f);
|
||||||
footer.setFillColor(sf::Color(100, 100, 100));
|
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;
|
float totalHeight = 0.0f;
|
||||||
totalHeight += title.getGlobalBounds().height + 10.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));
|
footer.setPosition(std::floor((windowWidth - footerBounds.width) * 0.5f), std::floor(currentY));
|
||||||
window.draw(footer);
|
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