Skip to content

Commit 5438ac1

Browse files
SirJosh3917SirJosh3917
authored andcommitted
readme & comments/cleanup
1 parent b67fe3b commit 5438ac1

File tree

10 files changed

+146
-11
lines changed

10 files changed

+146
-11
lines changed

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Sorting Visualizer [![Nightly Build](https://github.com/SirJosh3917/SortingVisualizer/workflows/Nightly%20Build/badge.svg)](https://github.com/SirJosh3917/SortingVisualizer/actions)
2+
3+
<p align=center>
4+
<img src="./assets/sorting.gif"/>
5+
</p>
6+
7+
This sorting visualizer was a simple project to introduce myself to
8+
9+
- How a modern C++ project is architected
10+
- How to use CMake
11+
- How to organize a project
12+
- How to setup CI/CD for a C++ project
13+
- How to write C++20 code
14+
15+
and it was a successful learning project. Overall, the take-away feeling I have from this project is that Rust is just C++, but if designed properly without baggage.
16+
17+
## Download
18+
19+
To download, please [download the build artifacts from the latest CI/CD run](https://github.com/SirJosh3917/SortingVisualizer/actions). After clicking the name of the latest run, scroll down and you should see an "Artifacts" section.
20+
21+
## About
22+
23+
This project, which is primarily a learning experiment to figure out the proper way to structure a C++ program, can hopefully be used to help others design and structure their own C++ SFML projects. As such, this project tries to be as simple as possible in that regard so that the reader can understand the concepts and introduce their own abstractions or bloat around the core process.
24+
25+
- This project is written in C++ 20. [SFML](https://www.sfml-dev.org/) was chosen to create the sorting display with.
26+
27+
- The resulting binary is statically linked to SFML so that there is no need for the user to install it, and on Windows builds, the standard C++ library is statically linked to as well so that users do not have to install the VC++ runtime.
28+
29+
- A `.clang-format` file, and the accompanying [Clang-Format VSCode extension](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) is used to ensure style consistency across the codebase.
30+
31+
- CMake is used to automatically generate and build the project for each platform.
32+
33+
- Github Actions is used to build and create artifacts of the program on every new commit.
34+
35+
## Building
36+
37+
Because this project statically links SFML, SFML needs to be built from source with static linking in mind. Please [check out the Github Actions file](https://github.com/SirJosh3917/SortingVisualizer/blob/main/.github/workflows/nightly_build.yml) for the most up-to-date steps to build the project.
38+
39+
1. Install SFML dependencies
40+
41+
On Ubuntu flavors of Linux, this involves installing `libxcursor-dev mesa-common-dev libfreetype6-dev libxrandr-dev libudev-dev libogg-dev libflac-dev libvorbis-dev libopenal-dev`.
42+
43+
No dependencies are needed on Windows.
44+
45+
2. Build SFML from source
46+
47+
Clone the SFML github repository, enable flags for static linking, and build. On Linux, build with the `-DBUILD_SHARED_LIBS=FALSE` flag only to statically link SFML. On Windows, build with the `-DSFML_USE_STATIC_STD_LIBS=TRUE -DBUILD_SHARED_LIBS=FALSE` flags to statically link SFML and the VC++ runtime.
48+
49+
```bash
50+
# Linux
51+
git clone https://github.com/SFML/SFML --depth 1
52+
pushd SFML
53+
cmake -B build -DBUILD_SHARED_LIBS=FALSE
54+
cmake --build build --config Release
55+
sudo cmake --install build --config Release
56+
popd
57+
58+
# Windows
59+
git clone https://github.com/SFML/SFML --depth 1
60+
pushd SFML
61+
cmake -B build -DSFML_USE_STATIC_STD_LIBS=TRUE -DBUILD_SHARED_LIBS=FALSE
62+
cmake --build build --config Release
63+
cmake --install build --config Release
64+
popd
65+
rm -Recurse -Force SFML
66+
```
67+
68+
3. Build Sorting Visualizer
69+
70+
Finally, use CMake to build Sorting Visualizer. Make sure you've ran `cmake --install` when building SFML in the previous step, otherwise it won't be installed in your path.
71+
72+
```shell
73+
cmake -B build
74+
cmake --build build --config Release
75+
cmake --install build --config Release
76+
```

assets/sorting.gif

3.62 MB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#pragma once
22
#include <SortingVisualizer/Collection.hpp>
33

4+
/**
5+
* Performs bubble sort.
6+
*/
47
const void bubbleSort(Collection &collection);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#pragma once
22
#include <SortingVisualizer/Collection.hpp>
33

4+
/**
5+
* Performs Quicksort. As an implementation detail, it uses Hoare's partitioning algorithm.
6+
*/
47
const void quickSort(Collection &collection, int64_t low = 0, int64_t high = -1);

include/SortingVisualizer/Bar.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
#include <stdint.h>
44
#include <vector>
55

6+
/**
7+
* Representation of a bar - it has a value, and a color.
8+
*/
69
struct Bar
710
{
811
uint64_t value;
912
sf::Color color;
1013
};
1114

15+
/**
16+
* Converts a series of values into a series of bars.
17+
*/
1218
std::vector<Bar> toBars(std::vector<uint64_t> values);

include/SortingVisualizer/Collection.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ enum class Order
1212
EQUAL
1313
};
1414

15+
/**
16+
* Metadata that tracks when a comparison was made.
17+
*/
1518
struct Comparison
1619
{
1720
std::size_t leftIdx;
1821
std::size_t rightIdx;
1922
};
2023

24+
/**
25+
* Metadata that tracks when a swap was performed.
26+
*/
2127
struct Swap
2228
{
2329
std::size_t leftIdx;
@@ -28,12 +34,22 @@ struct Swap
2834
using SingleDecision = std::variant<Comparison, Swap>;
2935
using Decision = std::variant<SingleDecision, std::vector<SingleDecision>>;
3036

37+
/**
38+
* Some weird class that allows the parallel sorting magic crud or something,,,
39+
*/
3140
class InternalDecisions
3241
{
3342
public:
3443
std::vector<std::variant<SingleDecision, std::vector<InternalDecisions>>> decisions;
3544
};
3645

46+
/**
47+
* A class that encapsulates its contents, and exists as a container to
48+
*
49+
* - Abstractly perform operations, such as comparing and swapping
50+
* - Record the performed operations to play back later
51+
* - Will play back the performed operation with the maximum amount of requested parallelism
52+
*/
3753
class Collection
3854
{
3955
private:

include/SortingVisualizer/Display.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#include <memory>
77
#include <stdint.h>
88

9+
/**
10+
* Class that contains bars, and can draw the bars. It also allows setting the bars from another thread, so that
11+
* the sorting and drawing are independent of one another.
12+
*/
913
class Display
1014
{
1115
private:

src/SortingVisualizer/Algorithms/QuickSort.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const std::size_t hoarePartition(Collection &collection, int64_t low, int64_t high)
44
{
5+
// why, yes i just copied this code from the internet - how could you tell?
56
auto pivot = low;
67
auto left = low - 1;
78
auto right = high + 1;
@@ -29,13 +30,12 @@ const std::size_t hoarePartition(Collection &collection, int64_t low, int64_t hi
2930
return right;
3031
}
3132

32-
const void w(Collection &c) {}
33-
3433
const void quickSort(Collection &collection, int64_t low, int64_t high)
3534
{
3635
if (high == -1) high = collection.length() - 1;
3736
if (low >= high) return;
3837

38+
// don't yell at me for the formatting i'm too lazy to configure clang-tidy
3939
auto pivot = hoarePartition(collection, low, high);
4040
collection.doParallel(
4141
{[low, pivot](Collection &c) { quickSort(c, low, pivot); }, [pivot, high](Collection &c) { quickSort(c, pivot + 1, high); }});

src/SortingVisualizer/Collection.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ const uint64_t Collection::max() const { return *std::max_element(this->values.b
1111

1212
std::vector<uint64_t> Collection::contents() const { return this->values; }
1313

14+
////////////////////
15+
// TODO: CLEAN UP //
16+
////////////////////
17+
//
18+
// see, the joke is that i call it a "TODO" but don't plan to ever do it
19+
// and holy hell is this a god-class of a method
20+
//
21+
// see i could explain so much and clean up so much code but it's just way easier if i
22+
// just, y'know
23+
// ...
24+
// didn't :)
25+
//
26+
// have fun!
27+
1428
std::vector<Decision> Collection::getDecisions(const std::size_t parallelWorkers) const
1529
{
1630
std::vector<Decision> resultantDecisions;
@@ -146,7 +160,7 @@ void Collection::followThroughDecision(const SingleDecision &decision)
146160
}
147161
else
148162
{
149-
// other is a comparison we don't care about
163+
// if the decision is a comparison, we don't have any bar state to update
150164
}
151165
}
152166

src/SortingVisualizer/main.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <SFML/Graphics.hpp>
2-
// #include <SortingVisualizer/Algorithms/BubbleSort.hpp>
32
#include <SortingVisualizer/Algorithms/QuickSort.hpp>
43
#include <SortingVisualizer/Bar.hpp>
54
#include <SortingVisualizer/Collection.hpp>
@@ -12,6 +11,15 @@
1211
#include <thread>
1312
#include <tuple>
1413

14+
////////////////////
15+
// TODO: CLEAN UP //
16+
////////////////////
17+
//
18+
// see, the joke is that i call it a "TODO" but don't plan to ever do it
19+
// as a sidenote i made comments every so often so it's obvious what should be put into a function if i ever decide to not feel lazy
20+
// (narrator: he never did)
21+
22+
// helper method for sorter below
1523
void handleSingleDecision(SingleDecision &decision, std::vector<Bar> &bars, std::vector<std::size_t> &coloredBars)
1624
{
1725
if (std::holds_alternative<Swap>(decision))
@@ -36,8 +44,10 @@ void handleSingleDecision(SingleDecision &decision, std::vector<Bar> &bars, std:
3644
}
3745
}
3846

47+
// worker on a separate thread that will update the display's bars
3948
void sorter(std::tuple<Collection *, Display *> container)
4049
{
50+
// boiler plate of container -> collection & display
4151
auto collectionPtr = std::get<0>(container);
4252
auto displayPtr = std::get<1>(container);
4353

@@ -47,13 +57,16 @@ void sorter(std::tuple<Collection *, Display *> container)
4757
auto collection = *collectionPtr;
4858
auto display = *displayPtr;
4959

60+
// sort the collection
5061
auto bars = toBars(collection.contents());
5162
quickSort(collection);
5263

64+
// display the sorting
5365
for (auto &decision : collection.getDecisions(1000))
5466
{
5567
std::vector<std::size_t> coloredBars;
5668

69+
// change colors of bars, and add them to coloredBars to change them back later
5770
if (std::holds_alternative<std::vector<SingleDecision>>(decision))
5871
{
5972
auto decisions = std::get<std::vector<SingleDecision>>(decision);
@@ -88,14 +101,15 @@ int main()
88101
// seed RNG
89102
std::srand(std::time(0));
90103

104+
// setup window
91105
sf::ContextSettings settings;
92106
settings.antialiasingLevel = 4;
93107
settings.majorVersion = 3;
94108

95109
auto window = sf::RenderWindow(sf::VideoMode(800, 600), "Sorting Visualizer", sf::Style::Default, settings);
96110
window.setVerticalSyncEnabled(true);
97-
window.setFramerateLimit(60);
98111

112+
// setup collection of values
99113
std::vector<uint64_t> values;
100114
auto len = 100;
101115
for (auto i = 1; i <= len; ++i)
@@ -105,13 +119,14 @@ int main()
105119
std::shuffle(values.begin(), values.end(), std::mt19937(std::random_device()()));
106120
auto collection = Collection(values);
107121

122+
// setup display
108123
auto display = Display(window, collection);
109124

110-
auto dirty = true;
111-
125+
// launch sorter on another thread
112126
sf::Thread sortAlgo(&sorter, std::make_tuple<Collection *, Display *>(&collection, &display));
113127
sortAlgo.launch();
114128

129+
// event loop thing
115130
while (window.isOpen())
116131
{
117132
sf::Event event;
@@ -122,17 +137,15 @@ int main()
122137
// SFML views are useful in games, but for me, i'd prefer if i was dealing with everything
123138
// as if it were regular window coordinates
124139
window.setView(sf::View(sf::FloatRect(0.0f, 0.0f, event.size.width, event.size.height)));
125-
126-
// we modified the view, we want to guarantee a re-render
127-
dirty = true;
128140
}
129141
else if (event.type == sf::Event::LostFocus)
130142
{
143+
// no reason to update the window at 60+fps if it's not in focus
131144
window.setFramerateLimit(12);
132145
}
133146
else if (event.type == sf::Event::GainedFocus)
134147
{
135-
window.setFramerateLimit(60);
148+
window.setFramerateLimit(0);
136149
}
137150
else if (event.type == sf::Event::Closed)
138151
{

0 commit comments

Comments
 (0)