From ff28dbb2f55596fdd675ee0caf156e8f8c0dac2d Mon Sep 17 00:00:00 2001 From: kolo Date: Tue, 3 Mar 2026 21:14:39 +0300 Subject: [PATCH] skelet --- CMakeLists.txt | 1 + include/App.hpp | 9 +- include/OperationsHistory.hpp | 11 ++- include/ProgressMap.hpp | 13 +++ include/UI.hpp | 1 + src/App.cpp | 110 +++++++++++++-------- src/OperationsHistory.cpp | 29 +++--- src/ProgressMap.cpp | 82 ++++++++++++++++ src/UI.cpp | 178 +++++++++++++++++++++++++++++++++- 9 files changed, 376 insertions(+), 58 deletions(-) create mode 100644 include/ProgressMap.hpp create mode 100644 src/ProgressMap.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 980e4de..64aee58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(SortLab src/Sorter.cpp src/UI.cpp src/OperationsHistory.cpp + src/ProgressMap.cpp src/sorters/BubbleSorter.cpp src/sorters/SelectionSorter.cpp src/sorters/InsertionSorter.cpp diff --git a/include/App.hpp b/include/App.hpp index b1200f9..0ea764b 100644 --- a/include/App.hpp +++ b/include/App.hpp @@ -6,6 +6,7 @@ #include "Sorter.hpp" #include "UI.hpp" #include "OperationsHistory.hpp" +#include "ProgressMap.hpp" class App { public: @@ -16,7 +17,7 @@ private: void handleEvents(); void update(float dt); void render(); - void renderHistogram(); + void renderDeltaHistogram(); void switchSorter(std::unique_ptr newSorter); void generateBeepSound(); void playBeep(float pitch); @@ -26,6 +27,7 @@ private: std::unique_ptr currentSorter_; UI ui_; OperationsHistory opsHistory_; + ProgressMap progressMap_; bool isPlaying_; float timeSinceLastStep_; int stepsPerFrame_; @@ -41,6 +43,7 @@ private: size_t lastComparisons_; size_t lastSwaps_; - sf::Font histogramFont_; - bool histogramFontLoaded_; + sf::Font bottomPanelFont_; + bool bottomPanelFontLoaded_; + bool showInfo_; }; diff --git a/include/OperationsHistory.hpp b/include/OperationsHistory.hpp index 02d35f7..2d28062 100644 --- a/include/OperationsHistory.hpp +++ b/include/OperationsHistory.hpp @@ -4,14 +4,15 @@ class OperationsHistory { public: - OperationsHistory(size_t maxSamples = 300); + OperationsHistory(size_t maxSamples = 400); - void record(size_t comparisons); + void record(size_t currentComparisons); void reset(); - const std::deque& getHistory() const; - size_t getMaxValue() const; + const std::deque& getDeltaHistory() const; + size_t getMaxDelta() const; private: - std::deque compareHistory_; + std::deque deltaHistory_; size_t maxSamples_; + size_t lastComparisons_; }; diff --git a/include/ProgressMap.hpp b/include/ProgressMap.hpp new file mode 100644 index 0000000..b4e6990 --- /dev/null +++ b/include/ProgressMap.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include "Array.hpp" + +class ProgressMap { +public: + ProgressMap(); + + void draw(sf::RenderWindow& window, const Array& array, sf::Font& font, bool fontLoaded); + +private: + sf::Color getColorForState(Array::State state) const; +}; diff --git a/include/UI.hpp b/include/UI.hpp index 4bc741f..7ca99f5 100644 --- a/include/UI.hpp +++ b/include/UI.hpp @@ -9,6 +9,7 @@ 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); private: sf::Font font_; diff --git a/src/App.cpp b/src/App.cpp index c839a04..9312c25 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -10,7 +10,8 @@ App::App() , array_(100) , currentSorter_(std::make_unique()) , ui_() - , opsHistory_(300) + , opsHistory_(400) + , progressMap_() , isPlaying_(false) , timeSinceLastStep_(0.0f) , stepsPerFrame_(1) @@ -20,14 +21,15 @@ App::App() , sweepDelay_(0.005f) , lastComparisons_(0) , lastSwaps_(0) - , histogramFontLoaded_(false) { + , bottomPanelFontLoaded_(false) + , showInfo_(false) { window_.setFramerateLimit(60); generateBeepSound(); beepSound_.setBuffer(beepBuffer_); beepSound_.setVolume(100.0f); - if (histogramFont_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) { - histogramFontLoaded_ = true; + if (bottomPanelFont_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) { + bottomPanelFontLoaded_ = true; } } @@ -109,6 +111,10 @@ void App::handleEvents() { window_.close(); break; + case sf::Keyboard::I: + showInfo_ = !showInfo_; + break; + default: break; } @@ -182,8 +188,11 @@ void App::update(float dt) { void App::render() { window_.clear(sf::Color(10, 12, 20)); - const float histogramHeight = 120.0f; - const float availableHeight = window_.getSize().y - histogramHeight; + const float topUIHeight = 80.0f; + const float bottomPanelHeight = 140.0f; + const float mainAreaTop = topUIHeight; + const float mainAreaBottom = window_.getSize().y - bottomPanelHeight; + const float mainAreaHeight = mainAreaBottom - mainAreaTop; float width = window_.getSize().x / static_cast(array_.getSize()); float maxVal = static_cast(array_.getSize()); @@ -191,9 +200,9 @@ void App::render() { sf::VertexArray vertices(sf::Quads); for (int i = 0; i < array_.getSize(); ++i) { - float height = (array_.getValue(i) / maxVal) * (availableHeight * 0.75f); + float height = (array_.getValue(i) / maxVal) * (mainAreaHeight * 0.9f); float x = i * width; - float y = availableHeight - height; + float y = mainAreaBottom - height; sf::Color barColor; bool useGradient = false; @@ -230,51 +239,71 @@ void App::render() { static_cast(bottomColor.b + (topColor.b - bottomColor.b) * normalizedValue * 0.6f) ); - vertices.append(sf::Vertex(sf::Vector2f(x, availableHeight), gradientBottom)); - vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, availableHeight), gradientBottom)); + vertices.append(sf::Vertex(sf::Vector2f(x, mainAreaBottom), gradientBottom)); + vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, mainAreaBottom), gradientBottom)); vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, y), gradientTop)); vertices.append(sf::Vertex(sf::Vector2f(x, y), gradientTop)); } else { - vertices.append(sf::Vertex(sf::Vector2f(x, availableHeight), barColor)); - vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, availableHeight), barColor)); + vertices.append(sf::Vertex(sf::Vector2f(x, mainAreaBottom), barColor)); + vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, mainAreaBottom), barColor)); vertices.append(sf::Vertex(sf::Vector2f(x + width - 1.0f, y), barColor)); vertices.append(sf::Vertex(sf::Vector2f(x, y), barColor)); } } window_.draw(vertices); - renderHistogram(); + + sf::RectangleShape separator(sf::Vector2f(window_.getSize().x, 1.0f)); + separator.setPosition(0.0f, mainAreaBottom); + separator.setFillColor(sf::Color(40, 45, 60)); + window_.draw(separator); + + renderDeltaHistogram(); + progressMap_.draw(window_, array_, bottomPanelFont_, bottomPanelFontLoaded_); + + sf::RectangleShape verticalSeparator(sf::Vector2f(1.0f, bottomPanelHeight)); + verticalSeparator.setPosition(1066.0f, mainAreaBottom); + verticalSeparator.setFillColor(sf::Color(40, 45, 60)); + window_.draw(verticalSeparator); + ui_.draw(window_); + if (showInfo_) { + ui_.drawInfoOverlay(window_); + } + window_.display(); } -void App::renderHistogram() { - const float histogramHeight = 120.0f; - const float histogramY = window_.getSize().y - histogramHeight; +void App::renderDeltaHistogram() { + const float startX = 0.0f; + const float startY = 760.0f; + const float width = 1066.0f; + const float height = 140.0f; - sf::RectangleShape background(sf::Vector2f(window_.getSize().x, histogramHeight)); - background.setPosition(0.0f, histogramY); - background.setFillColor(sf::Color(15, 18, 30)); + sf::RectangleShape background(sf::Vector2f(width, height)); + background.setPosition(startX, startY); + background.setFillColor(sf::Color(12, 15, 25)); window_.draw(background); - const auto& history = opsHistory_.getHistory(); + const auto& deltaHistory = opsHistory_.getDeltaHistory(); - if (!history.empty()) { - size_t maxValue = opsHistory_.getMaxValue(); - if (maxValue == 0) maxValue = 1; + if (!deltaHistory.empty()) { + size_t maxDelta = opsHistory_.getMaxDelta(); + if (maxDelta == 0) maxDelta = 1; - float barWidth = window_.getSize().x / 300.0f; + float barWidth = width / 400.0f; + float availableHeight = height - 25.0f; sf::VertexArray histogramBars(sf::Quads); - for (size_t i = 0; i < history.size(); ++i) { - float barHeight = (static_cast(history[i]) / static_cast(maxValue)) * (histogramHeight - 30.0f); - float x = i * barWidth; - float y = histogramY + histogramHeight - barHeight - 5.0f; + for (size_t i = 0; i < deltaHistory.size(); ++i) { + float barHeight = (static_cast(deltaHistory[i]) / static_cast(maxDelta)) * availableHeight; + float x = startX + i * barWidth; + float y = startY + height - barHeight; - float normalizedValue = static_cast(history[i]) / static_cast(maxValue); - sf::Color lowColor(0, 120, 255); - sf::Color highColor(0, 220, 255); + float normalizedValue = static_cast(deltaHistory[i]) / static_cast(maxDelta); + sf::Color lowColor(0, 80, 200); + sf::Color highColor(0, 240, 255); sf::Color barColor( static_cast(lowColor.r + (highColor.r - lowColor.r) * normalizedValue), @@ -282,8 +311,8 @@ void App::renderHistogram() { static_cast(lowColor.b + (highColor.b - lowColor.b) * normalizedValue) ); - histogramBars.append(sf::Vertex(sf::Vector2f(x, histogramY + histogramHeight - 5.0f), barColor)); - histogramBars.append(sf::Vertex(sf::Vector2f(x + barWidth, histogramY + histogramHeight - 5.0f), barColor)); + histogramBars.append(sf::Vertex(sf::Vector2f(x, startY + height), barColor)); + histogramBars.append(sf::Vertex(sf::Vector2f(x + barWidth, startY + height), barColor)); histogramBars.append(sf::Vertex(sf::Vector2f(x + barWidth, y), barColor)); histogramBars.append(sf::Vertex(sf::Vector2f(x, y), barColor)); } @@ -291,13 +320,18 @@ void App::renderHistogram() { window_.draw(histogramBars); } - if (histogramFontLoaded_) { + sf::RectangleShape baseline(sf::Vector2f(width, 1.0f)); + baseline.setPosition(startX, startY + height - 1.0f); + baseline.setFillColor(sf::Color(60, 70, 90)); + window_.draw(baseline); + + if (bottomPanelFontLoaded_) { sf::Text label; - label.setFont(histogramFont_); - label.setString("Comparisons over time"); - label.setCharacterSize(14); + label.setFont(bottomPanelFont_); + label.setString("Delta Comparisons / step"); + label.setCharacterSize(13); label.setFillColor(sf::Color(180, 180, 180)); - label.setPosition(10.0f, histogramY + 5.0f); + label.setPosition(startX + 10.0f, startY + 5.0f); window_.draw(label); } } diff --git a/src/OperationsHistory.cpp b/src/OperationsHistory.cpp index 88b2fd0..4ae3465 100644 --- a/src/OperationsHistory.cpp +++ b/src/OperationsHistory.cpp @@ -2,29 +2,36 @@ #include OperationsHistory::OperationsHistory(size_t maxSamples) - : maxSamples_(maxSamples) { + : maxSamples_(maxSamples), lastComparisons_(0) { } -void OperationsHistory::record(size_t comparisons) { - compareHistory_.push_back(comparisons); +void OperationsHistory::record(size_t currentComparisons) { + size_t delta = 0; + if (currentComparisons >= lastComparisons_) { + delta = currentComparisons - lastComparisons_; + } - if (compareHistory_.size() > maxSamples_) { - compareHistory_.pop_front(); + deltaHistory_.push_back(delta); + lastComparisons_ = currentComparisons; + + if (deltaHistory_.size() > maxSamples_) { + deltaHistory_.pop_front(); } } void OperationsHistory::reset() { - compareHistory_.clear(); + deltaHistory_.clear(); + lastComparisons_ = 0; } -const std::deque& OperationsHistory::getHistory() const { - return compareHistory_; +const std::deque& OperationsHistory::getDeltaHistory() const { + return deltaHistory_; } -size_t OperationsHistory::getMaxValue() const { - if (compareHistory_.empty()) { +size_t OperationsHistory::getMaxDelta() const { + if (deltaHistory_.empty()) { return 1; } - return *std::max_element(compareHistory_.begin(), compareHistory_.end()); + return *std::max_element(deltaHistory_.begin(), deltaHistory_.end()); } diff --git a/src/ProgressMap.cpp b/src/ProgressMap.cpp new file mode 100644 index 0000000..135a3de --- /dev/null +++ b/src/ProgressMap.cpp @@ -0,0 +1,82 @@ +#include "ProgressMap.hpp" +#include + +ProgressMap::ProgressMap() { +} + +void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& font, bool fontLoaded) { + const float startX = 1066.0f; + const float startY = 760.0f; + const float width = 534.0f; + const float height = 140.0f; + + sf::RectangleShape background(sf::Vector2f(width, height)); + background.setPosition(startX, startY); + background.setFillColor(sf::Color(12, 15, 25)); + window.draw(background); + + if (fontLoaded) { + sf::Text label; + label.setFont(font); + label.setString("Progress Map"); + label.setCharacterSize(13); + label.setFillColor(sf::Color(180, 180, 180)); + label.setPosition(startX + 10.0f, startY + 5.0f); + window.draw(label); + } + + int arraySize = array.getSize(); + int cols = static_cast(std::sqrt(arraySize)); + int rows = (arraySize + cols - 1) / cols; + + float availableWidth = width - 20.0f; + float availableHeight = height - 30.0f; + + float squareSizeW = availableWidth / static_cast(cols); + float squareSizeH = availableHeight / static_cast(rows); + float squareSize = std::min(squareSizeW, squareSizeH); + + if (squareSize < 1.0f) squareSize = 1.0f; + + float gridWidth = cols * squareSize; + float gridHeight = rows * squareSize; + float offsetX = startX + (width - gridWidth) * 0.5f; + float offsetY = startY + 30.0f + (availableHeight - gridHeight) * 0.5f; + + sf::VertexArray squares(sf::Quads); + + for (int i = 0; i < arraySize; ++i) { + int col = i % cols; + int row = i / cols; + + float x = offsetX + col * squareSize; + float y = offsetY + row * squareSize; + + sf::Color color = getColorForState(array.getState(i)); + + float gap = 1.0f; + float actualSize = squareSize - gap; + + squares.append(sf::Vertex(sf::Vector2f(x, y), color)); + squares.append(sf::Vertex(sf::Vector2f(x + actualSize, y), color)); + squares.append(sf::Vertex(sf::Vector2f(x + actualSize, y + actualSize), color)); + squares.append(sf::Vertex(sf::Vector2f(x, y + actualSize), color)); + } + + window.draw(squares); +} + +sf::Color ProgressMap::getColorForState(Array::State state) const { + switch (state) { + case Array::State::NORMAL: + return sf::Color(100, 120, 150); + case Array::State::COMPARE: + return sf::Color(0, 220, 255); + case Array::State::SWAP: + return sf::Color(255, 120, 30); + case Array::State::SORTED: + return sf::Color(50, 220, 120); + default: + return sf::Color(100, 120, 150); + } +} diff --git a/src/UI.cpp b/src/UI.cpp index aac61e4..3071aba 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -1,3 +1,4 @@ +#pragma execution_character_set("utf-8") #include "UI.hpp" #include @@ -86,7 +87,25 @@ void UI::draw(sf::RenderWindow& window) { return; } - float leftWidth = 350.0f; + float maxWidth = 0.0f; + + sf::FloatRect algoBounds = algorithmText_.getLocalBounds(); + sf::FloatRect stateBounds = stateText_.getLocalBounds(); + sf::FloatRect timeBounds = timeComplexityText_.getLocalBounds(); + sf::FloatRect spaceBounds = spaceComplexityText_.getLocalBounds(); + sf::FloatRect compBounds = comparisonsText_.getLocalBounds(); + sf::FloatRect swapBounds = swapsText_.getLocalBounds(); + sf::FloatRect speedBounds = speedText_.getLocalBounds(); + + maxWidth = std::max(maxWidth, algoBounds.width); + maxWidth = std::max(maxWidth, stateBounds.width); + maxWidth = std::max(maxWidth, timeBounds.width); + maxWidth = std::max(maxWidth, spaceBounds.width); + maxWidth = std::max(maxWidth, compBounds.width); + maxWidth = std::max(maxWidth, swapBounds.width); + maxWidth = std::max(maxWidth, speedBounds.width); + + float leftWidth = maxWidth + 24.0f; float leftHeight = 215.0f; leftBackground_.setSize(sf::Vector2f(leftWidth, leftHeight)); leftBackground_.setPosition(5.0f, 5.0f); @@ -113,3 +132,160 @@ void UI::draw(sf::RenderWindow& window) { window.draw(speedText_); window.draw(controlsText_); } + +void UI::drawInfoOverlay(sf::RenderWindow& window) { + 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(36); + title.setFillColor(sf::Color::White); + title.setString(toSfStr(u8"SortLab — визуализатор алгоритмов сортировки")); + + sf::Text subtitle; + subtitle.setFont(font_); + subtitle.setCharacterSize(18); + subtitle.setFillColor(sf::Color(160, 160, 160)); + subtitle.setString(toSfStr(u8"Учебный инструмент для наглядного изучения алгоритмов сортировки")); + + sf::Text section1; + section1.setFont(font_); + section1.setCharacterSize(20); + section1.setFillColor(sf::Color(0, 220, 255)); + section1.setString(toSfStr(u8"О программе")); + + sf::Text body1; + body1.setFont(font_); + body1.setCharacterSize(16); + body1.setFillColor(sf::Color(200, 200, 200)); + body1.setString(toSfStr(u8"SortLab визуализирует работу алгоритмов сортировки в реальном времени.\nКаждый столбик — это элемент массива. Высота = значение элемента.\nЦвета: серый — обычный, голубой — сравнение, оранжевый — перестановка, зелёный — готово.")); + + sf::Text section2; + section2.setFont(font_); + section2.setCharacterSize(20); + section2.setFillColor(sf::Color(0, 220, 255)); + section2.setString(toSfStr(u8"Алгоритмы и сложность")); + + sf::Text body2; + body2.setFont(font_); + body2.setCharacterSize(16); + body2.setFillColor(sf::Color(200, 200, 200)); + body2.setString(toSfStr(u8"[1] Bubble Sort Время: O(n²) / O(n²) / O(n²) Память: O(1)\n[2] Selection Sort Время: O(n²) / O(n²) / O(n²) Память: O(1)\n[3] Insertion Sort Время: O(n) / O(n²) / O(n²) Память: O(1)\n[4] Merge Sort Время: O(n log n) / O(n log n) / ... Память: O(n)\n[5] Quick Sort Время: O(n log n) / O(n log n) / ... Память: O(log n)")); + + sf::Text section3; + section3.setFont(font_); + section3.setCharacterSize(20); + section3.setFillColor(sf::Color(0, 220, 255)); + section3.setString(toSfStr(u8"Δ Гистограмма (внизу слева)")); + + sf::Text body3; + body3.setFont(font_); + body3.setCharacterSize(16); + body3.setFillColor(sf::Color(200, 200, 200)); + body3.setString(toSfStr(u8"Показывает количество сравнений на каждом шаге сортировки.\nВысокий пик = много сравнений за один шаг.\nBubble Sort даёт ровную линию, Quick Sort — резкие пики в начале.")); + + sf::Text section4; + section4.setFont(font_); + section4.setCharacterSize(20); + section4.setFillColor(sf::Color(0, 220, 255)); + section4.setString(toSfStr(u8"Карта прогресса (внизу справа)")); + + sf::Text body4; + body4.setFont(font_); + body4.setCharacterSize(16); + body4.setFillColor(sf::Color(200, 200, 200)); + body4.setString(toSfStr(u8"Миниатюрная копия всего массива в виде сетки цветных квадратиков.\nПозволяет видеть глобальный прогресс сортировки даже при большом массиве.\nЗелёные квадраты = отсортированные элементы.")); + + sf::Text footer; + footer.setFont(font_); + footer.setCharacterSize(15); + footer.setFillColor(sf::Color(100, 100, 100)); + footer.setString(toSfStr(u8"Нажми [I] чтобы закрыть | [Q] выход | [Space] старт/пауза | [R] перемешать")); + + float totalHeight = 0.0f; + totalHeight += title.getLocalBounds().height + 10.0f; + totalHeight += subtitle.getLocalBounds().height + 30.0f; + totalHeight += section1.getLocalBounds().height + 6.0f; + totalHeight += body1.getLocalBounds().height + 24.0f; + totalHeight += section2.getLocalBounds().height + 6.0f; + totalHeight += body2.getLocalBounds().height + 24.0f; + totalHeight += section3.getLocalBounds().height + 6.0f; + totalHeight += body3.getLocalBounds().height + 24.0f; + totalHeight += section4.getLocalBounds().height + 6.0f; + totalHeight += body4.getLocalBounds().height + 30.0f; + totalHeight += footer.getLocalBounds().height; + + float startY = (windowHeight - totalHeight) * 0.5f; + if (startY < 20.0f) startY = 20.0f; + + float currentY = startY; + + sf::FloatRect titleBounds = title.getLocalBounds(); + title.setPosition((windowWidth - titleBounds.width) * 0.5f, currentY); + window.draw(title); + currentY += titleBounds.height + 10.0f; + + sf::FloatRect subtitleBounds = subtitle.getLocalBounds(); + subtitle.setPosition((windowWidth - subtitleBounds.width) * 0.5f, currentY); + window.draw(subtitle); + currentY += subtitleBounds.height + 30.0f; + + sf::FloatRect section1Bounds = section1.getLocalBounds(); + section1.setPosition((windowWidth - section1Bounds.width) * 0.5f, currentY); + window.draw(section1); + currentY += section1Bounds.height + 6.0f; + + sf::FloatRect body1Bounds = body1.getLocalBounds(); + body1.setPosition((windowWidth - body1Bounds.width) * 0.5f, currentY); + window.draw(body1); + currentY += body1Bounds.height + 24.0f; + + sf::FloatRect section2Bounds = section2.getLocalBounds(); + section2.setPosition((windowWidth - section2Bounds.width) * 0.5f, currentY); + window.draw(section2); + currentY += section2Bounds.height + 6.0f; + + sf::FloatRect body2Bounds = body2.getLocalBounds(); + body2.setPosition((windowWidth - body2Bounds.width) * 0.5f, currentY); + window.draw(body2); + currentY += body2Bounds.height + 24.0f; + + sf::FloatRect section3Bounds = section3.getLocalBounds(); + section3.setPosition((windowWidth - section3Bounds.width) * 0.5f, currentY); + window.draw(section3); + currentY += section3Bounds.height + 6.0f; + + sf::FloatRect body3Bounds = body3.getLocalBounds(); + body3.setPosition((windowWidth - body3Bounds.width) * 0.5f, currentY); + window.draw(body3); + currentY += body3Bounds.height + 24.0f; + + sf::FloatRect section4Bounds = section4.getLocalBounds(); + section4.setPosition((windowWidth - section4Bounds.width) * 0.5f, currentY); + window.draw(section4); + currentY += section4Bounds.height + 6.0f; + + sf::FloatRect body4Bounds = body4.getLocalBounds(); + body4.setPosition((windowWidth - body4Bounds.width) * 0.5f, currentY); + window.draw(body4); + currentY += body4Bounds.height + 30.0f; + + sf::FloatRect footerBounds = footer.getLocalBounds(); + footer.setPosition((windowWidth - footerBounds.width) * 0.5f, currentY); + window.draw(footer); +}