Original post: Sorting algorithm visualizer in C++ and SDL2
I followed the advice you gave me and improved my code. I also added two more sorting algorithms. I'm mainly looking for code readability advice (Built on Windows with cmake and ninja)
Demonstration
props to @Zeta for the demonstration
main.cpp
#include "Engine.h" #undef main int main() { try { // If the max number is higher than the window width it draws nothing other than a black screen :^) // Default draw method is line // Default sorting algorithm is bubble sort SortVis::Engine visualization( { 1024, 768 }, 1024, SortVis::Engine::SortAlgorithm::insertionSort, SortVis::Engine::DrawMethod::point ); visualization.run(); } catch (std::runtime_error& error) { std::cerr << error.what() << "\n"; } }
Engine.h
#ifndef ENGINE_H #define ENGINE_H #include "Coord.h" #include <SDL.h> #include <vector> #include <iostream> namespace SortVis { class Engine { public: enum class DrawMethod { line, point }; enum class SortAlgorithm { selectionSort, insertionSort, bubbleSort }; // Random number generation Engine() = delete; Engine(Coord windowSize, int maxNumber); Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm); Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm, DrawMethod method); Engine(Coord windowSize, int maxNumber, const char* windowTitle); Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm); Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method); // Load from file Engine(Coord windowSize, const char* pathToNumbersFile); Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm); Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm, DrawMethod method); Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle); Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm); Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method); ~Engine(); void run(); private: const Coord windowSize; const SortAlgorithm selectedSortAlgorithm = SortAlgorithm::bubbleSort; const DrawMethod selectedDrawMethod = DrawMethod::line; SDL_Window* window = nullptr; SDL_Renderer* renderer = nullptr; std::vector<int> numbers = { }; int columnWidth = 0; int maxValue = 0; bool running = true; void initWindow(Coord windowSize, const char* windowTitle); void initRenderer(); void calculateNumbers(); void loadFile(const char* pathToNumbersFile); void handleEvents(); void draw(); void drawSelection(); void drawColumns(); void drawPoints(); void step(); void stepBubbleSort(); void stepInsertionSort(); void stepSelectionSort(); std::vector<int> generateRandom(int maxNumber); }; } #endif // ENGINE_H
Engine.cpp
#include "Engine.h" #include <filesystem> #include <fstream> #include <random> #include <utility> #include <algorithm> #include <numeric> #include <string> // --- CONSTRUCTORS --- (fml) SortVis::Engine::Engine(Coord windowSize, int maxNumber) : windowSize(windowSize), numbers(generateRandom(maxNumber)) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm) : windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, int maxNumber, SortAlgorithm algorithm, DrawMethod method) : windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm), selectedDrawMethod(method) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle) : windowSize(windowSize), numbers(generateRandom(maxNumber)) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm) : windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, int maxNumber, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method) : windowSize(windowSize), numbers(generateRandom(maxNumber)), selectedSortAlgorithm(algorithm), selectedDrawMethod(method) { calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile) : windowSize(windowSize) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm) : windowSize(windowSize), selectedSortAlgorithm(algorithm) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, SortAlgorithm algorithm, DrawMethod method) : windowSize(windowSize), selectedSortAlgorithm(algorithm), selectedDrawMethod(method) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, "Sort visualizer"); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle) : windowSize(windowSize) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm) : windowSize(windowSize), selectedSortAlgorithm(algorithm) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } SortVis::Engine::Engine(Coord windowSize, const char* pathToNumbersFile, const char* windowTitle, SortAlgorithm algorithm, DrawMethod method) : windowSize(windowSize), selectedSortAlgorithm(algorithm), selectedDrawMethod(method) { if (!std::filesystem::exists(pathToNumbersFile)) { throw std::runtime_error("That file does not exist. Make sure the path is correct."); } else { loadFile(pathToNumbersFile); } calculateNumbers(); if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Could not initialize SDL"); } initWindow(windowSize, windowTitle); initRenderer(); } // --- END OF CONSTRUCTORS --- SortVis::Engine::~Engine() { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); } void SortVis::Engine::run() { // Sets render draw color to black SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); while (running) { handleEvents(); if (!std::is_sorted(numbers.begin(), numbers.end())) { step(); } draw(); } } void SortVis::Engine::step() { switch (selectedSortAlgorithm) { case SortAlgorithm::bubbleSort: stepBubbleSort(); break; case SortAlgorithm::insertionSort: stepInsertionSort(); break; case SortAlgorithm::selectionSort: stepSelectionSort(); break; default: break; } } void SortVis::Engine::stepBubbleSort() { static int i = 0; static int size = numbers.size(); for (int j = 0; j < size - i - 1; ++j) { if (numbers[j] > numbers[j + 1]) { std::swap(numbers[j], numbers[j + 1]); } } ++i; } void SortVis::Engine::stepInsertionSort() { static int i = 1; for (int j = i; j > 0 && numbers[j - 1] > numbers[j]; --j) { std::swap(numbers[j - 1], numbers[j]); } ++i; } void SortVis::Engine::stepSelectionSort() { static int i = 0; std::swap(numbers[i], numbers[std::min_element(numbers.begin() + i, numbers.end()) - numbers.begin()]); ++i; } void SortVis::Engine::draw() { SDL_RenderClear(renderer); drawSelection(); SDL_RenderPresent(renderer); } void SortVis::Engine::drawSelection() { switch (selectedDrawMethod) { case DrawMethod::line: drawColumns(); break; case DrawMethod::point: drawPoints(); break; default: break; } } void SortVis::Engine::drawColumns() { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_Rect column; for (int i = numbers.size(); i > 0; --i) { column.x = (i-1) * columnWidth; column.w = columnWidth; column.h = (numbers[i - 1] * windowSize.Y) / maxValue; column.y = windowSize.Y - column.h; SDL_RenderFillRect(renderer, &column); } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } void SortVis::Engine::drawPoints() { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // SDL_Point point; for (int i = numbers.size(); i > 0; --i) { // point.x = i - 1; // point.y = windowSize.Y - ((numbers[i - 1] * windowSize.Y) / maxValue); SDL_RenderDrawPoint(renderer, i - 1, windowSize.Y - ((numbers[i - 1] * windowSize.Y) / maxValue)); } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); } void SortVis::Engine::handleEvents() { SDL_Event Event; while (SDL_PollEvent(&Event)) { switch (Event.type) { case SDL_QUIT: running = false; break; default: break; } } } std::vector<int> SortVis::Engine::generateRandom(int maxNumber) { std::mt19937 seed(std::random_device{}()); std::vector<int> num(maxNumber); std::iota(num.begin(), num.end(), 0); std::shuffle(num.begin(), num.end(), seed); std::cout << "Generated random number sequence.\n"; return num; } void SortVis::Engine::calculateNumbers() { columnWidth = windowSize.X / numbers.size(); maxValue = *std::max_element(numbers.begin(), numbers.end()); } void SortVis::Engine::loadFile(const char* pathToNumbersFile) { std::ifstream NumbersFile(pathToNumbersFile); if (NumbersFile.is_open()) { std::string Number; while (std::getline(NumbersFile, Number)) { numbers.push_back(std::stoi(Number)); } } else { throw std::runtime_error("Couldn't open numbers file."); } if (numbers.empty()) { throw std::runtime_error("Numbers file is empty."); } std::cout << "Loaded numbers file.\n"; } void SortVis::Engine::initWindow(Coord windowSize, const char* windowTitle) { window = SDL_CreateWindow( windowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowSize.X, windowSize.Y, SDL_WINDOW_SHOWN ); if (window == nullptr) { throw std::runtime_error("Could not initialize SDL window"); } } void SortVis::Engine::initRenderer() { renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED ); if (renderer == nullptr) { throw std::runtime_error("Could not initialize SDL renderer"); } }
Coord.h
#ifndef COORD_H #define COORD_H namespace SortVis { struct Coord { int X; int Y; }; } #endif // COORD_H