diff options
| author | Aiden Woodruff <aiden.woodruff@gmail.com> | 2023-12-25 16:57:10 -0600 |
|---|---|---|
| committer | Aiden Woodruff <aiden.woodruff@gmail.com> | 2023-12-25 16:57:10 -0600 |
| commit | cce67aeadd283a09356ae8101bfc6818924f4a86 (patch) | |
| tree | abdaa8ee1fbe24205089a2bf2dfe598c181d04f4 | |
| download | stars-cce67aeadd283a09356ae8101bfc6818924f4a86.tar.gz stars-cce67aeadd283a09356ae8101bfc6818924f4a86.tar.bz2 stars-cce67aeadd283a09356ae8101bfc6818924f4a86.zip | |
Initial commit
Signed-off-by: Aiden Woodruff <aiden.woodruff@gmail.com>
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CMakeLists.txt | 9 | ||||
| -rw-r--r-- | ConstellationApp.h | 36 | ||||
| -rw-r--r-- | ConstellationController.h | 81 | ||||
| -rw-r--r-- | ConstellationModel.h | 98 | ||||
| -rw-r--r-- | ConstellationView.h | 72 | ||||
| -rw-r--r-- | config.sh | 3 | ||||
| -rw-r--r-- | main.cc | 7 |
8 files changed, 307 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| build/ | |||
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..37b4227 --- /dev/null +++ b/CMakeLists.txt | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.8) | ||
| 2 | project(idkproject VERSION 0 LANGUAGES CXX) | ||
| 3 | |||
| 4 | find_package(SFML 2 REQUIRED COMPONENTS graphics) | ||
| 5 | |||
| 6 | add_executable(main main.cc) | ||
| 7 | target_compile_options(main PUBLIC -g) | ||
| 8 | target_link_libraries(main PRIVATE sfml-graphics) | ||
| 9 | target_compile_features(main PRIVATE cxx_auto_type cxx_range_for) \ No newline at end of file | ||
diff --git a/ConstellationApp.h b/ConstellationApp.h new file mode 100644 index 0000000..5fa9ce0 --- /dev/null +++ b/ConstellationApp.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #include "ConstellationModel.h" | ||
| 2 | #include "ConstellationView.h" | ||
| 3 | #include "ConstellationController.h" | ||
| 4 | |||
| 5 | namespace Constellation { | ||
| 6 | class App { | ||
| 7 | Model m; | ||
| 8 | View v; | ||
| 9 | Controller c; | ||
| 10 | int screenwidth, screenheight; | ||
| 11 | |||
| 12 | public: | ||
| 13 | /** | ||
| 14 | * @brief Construct a new App object. | ||
| 15 | * | ||
| 16 | * @param sw Screen width. | ||
| 17 | * @param sh Screen height. | ||
| 18 | * @param sr Star radius. | ||
| 19 | * @param n Star count. | ||
| 20 | * @param minc Minimum constellation number. | ||
| 21 | */ | ||
| 22 | App(int sw, int sh, int sr, int n, int minc): | ||
| 23 | m(n, minc, sw, sh, sr), v(sw, sh, sr), c(&m, &v) {} | ||
| 24 | |||
| 25 | void run() { | ||
| 26 | v.make_window(); | ||
| 27 | while (c.isRunning()) { | ||
| 28 | c.collect_input(); | ||
| 29 | c.advance_state(); | ||
| 30 | v.display(); | ||
| 31 | sf::sleep(sf::milliseconds(1)); | ||
| 32 | } | ||
| 33 | v.close_window(); | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | } | ||
diff --git a/ConstellationController.h b/ConstellationController.h new file mode 100644 index 0000000..24b5328 --- /dev/null +++ b/ConstellationController.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #include "ConstellationModel.h" | ||
| 2 | #include "ConstellationView.h" | ||
| 3 | |||
| 4 | #include <SFML/System.hpp> | ||
| 5 | |||
| 6 | #ifndef CONSTELLATION_CONTROLLER_H_ | ||
| 7 | #define CONSTELLATION_CONTROLLER_H_ | ||
| 8 | |||
| 9 | namespace Constellation { | ||
| 10 | class Controller { | ||
| 11 | Model *model; | ||
| 12 | View *view; | ||
| 13 | bool running; | ||
| 14 | |||
| 15 | enum class Mode : int { | ||
| 16 | idle = 0, | ||
| 17 | stars, | ||
| 18 | edges | ||
| 19 | } mode; | ||
| 20 | |||
| 21 | Model::EdgeIterator eIt; | ||
| 22 | Model::VertexIterator vIt; | ||
| 23 | |||
| 24 | public: | ||
| 25 | Controller(Model *m, View *v) : model(m), view(v), running(true), | ||
| 26 | mode(Mode::idle) {} | ||
| 27 | |||
| 28 | bool isRunning() const { return running; } | ||
| 29 | |||
| 30 | void collect_input() { | ||
| 31 | sf::Event event; | ||
| 32 | while (view->window->pollEvent(event)) { | ||
| 33 | if (event.type == sf::Event::Closed) { | ||
| 34 | running = false; | ||
| 35 | } else if (mode == Mode::idle) { | ||
| 36 | if (event.type == sf::Event::KeyReleased) { | ||
| 37 | if (event.key.code == sf::Keyboard::R) { | ||
| 38 | mode = Mode::stars; | ||
| 39 | model->randomize_stars(); | ||
| 40 | view->clear_stars(); | ||
| 41 | view->clear_edges(); | ||
| 42 | vIt = model->vbegin(); | ||
| 43 | } else if (event.key.code == sf::Keyboard::G) { | ||
| 44 | mode = Mode::edges; | ||
| 45 | model->generate_edges(); | ||
| 46 | view->clear_edges(); | ||
| 47 | eIt = model->ebegin(); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | void advance_state() { | ||
| 55 | switch (mode) { | ||
| 56 | case Mode::stars: | ||
| 57 | if (vIt != model->vend()) { | ||
| 58 | view->add_star(*vIt); | ||
| 59 | ++vIt; | ||
| 60 | } else { | ||
| 61 | mode = Mode::idle; | ||
| 62 | } | ||
| 63 | break; | ||
| 64 | case Mode::edges: | ||
| 65 | if (eIt != model->eend()) { | ||
| 66 | std::pair<sf::Vector2f,sf::Vector2f> e = model->edge_data(*eIt); | ||
| 67 | view->add_edge(sf::Vertex(e.first), sf::Vertex(e.second)); | ||
| 68 | ++eIt; | ||
| 69 | } else { | ||
| 70 | mode = Mode::idle; | ||
| 71 | } | ||
| 72 | break; | ||
| 73 | case Mode::idle: | ||
| 74 | default: | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | } // namespace Constellation | ||
| 80 | |||
| 81 | #endif // CONSTELLATION_CONTROLLER_H_ | ||
diff --git a/ConstellationModel.h b/ConstellationModel.h new file mode 100644 index 0000000..44b1b32 --- /dev/null +++ b/ConstellationModel.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | #include <random> | ||
| 2 | #include <vector> | ||
| 3 | #include <utility> | ||
| 4 | #include <iostream> | ||
| 5 | |||
| 6 | #include <SFML/System.hpp> | ||
| 7 | |||
| 8 | #ifndef CONSTELLATION_MODEL_H_ | ||
| 9 | #define CONSTELLATION_MODEL_H_ | ||
| 10 | |||
| 11 | namespace Constellation { | ||
| 12 | class Model { | ||
| 13 | using Edge = std::pair<int, int>; | ||
| 14 | |||
| 15 | int stars, min_constellations; | ||
| 16 | std::mt19937 engine; | ||
| 17 | std::uniform_int_distribution<int> x_dist, y_dist; | ||
| 18 | std::vector<sf::Vector2f> vertices; | ||
| 19 | std::vector<Edge> edges; | ||
| 20 | public: | ||
| 21 | Model(int n, int minc, int sw, int sh, int sr) : | ||
| 22 | stars(n), min_constellations(minc), | ||
| 23 | x_dist(sr, sw - sr), y_dist(sr, sh - sr) { | ||
| 24 | engine = std::mt19937(std::random_device{}()); | ||
| 25 | } | ||
| 26 | using EdgeIterator = std::vector<Edge>::const_iterator; | ||
| 27 | using VertexIterator = std::vector<sf::Vector2f>::const_iterator; | ||
| 28 | |||
| 29 | EdgeIterator ebegin() const { return edges.cbegin(); } | ||
| 30 | EdgeIterator eend() const { return edges.cend(); } | ||
| 31 | VertexIterator vbegin() const { return vertices.cbegin(); } | ||
| 32 | VertexIterator vend() const { return vertices.cend(); } | ||
| 33 | |||
| 34 | |||
| 35 | std::pair<sf::Vector2f, sf::Vector2f> edge_data(const Edge& e) { | ||
| 36 | return {vertices[e.first], vertices[e.second]}; | ||
| 37 | } | ||
| 38 | |||
| 39 | void randomize_stars () { | ||
| 40 | vertices.clear(); | ||
| 41 | edges.clear(); | ||
| 42 | for (int i = 0; i < stars; ++i) { | ||
| 43 | float x = x_dist(engine), y = y_dist(engine); | ||
| 44 | vertices.push_back({x, y}); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | private: | ||
| 49 | float distance (int u, int v) { | ||
| 50 | float xdiff = vertices[v].x - vertices[u].x, | ||
| 51 | ydiff = vertices[v].y - vertices[u].y; | ||
| 52 | return xdiff * xdiff + ydiff * ydiff; | ||
| 53 | } | ||
| 54 | public: | ||
| 55 | void generate_edges() { | ||
| 56 | if (vertices.empty()) return; | ||
| 57 | edges.clear(); | ||
| 58 | |||
| 59 | // Generate all possible edges. | ||
| 60 | std::vector<std::pair<float, Edge>> possible_edges; | ||
| 61 | possible_edges.reserve(stars * stars); | ||
| 62 | for (int i = 0; i < stars; ++i) { | ||
| 63 | for (int j = 0; j < stars; ++j) { | ||
| 64 | if (i != j) | ||
| 65 | possible_edges.push_back({distance(i, j), {i, j}}); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | // Sort in order of min distance. | ||
| 69 | std::sort(possible_edges.begin(), possible_edges.end()); | ||
| 70 | |||
| 71 | std::vector<std::vector<int>> v_sets(stars); // Sets of vertices. | ||
| 72 | std::vector<int> v_s(stars, -1); // Owning set of each vertex. | ||
| 73 | for (int i = 0; i < stars; ++i) { | ||
| 74 | v_sets[i].push_back(i); | ||
| 75 | v_s[i] = i; | ||
| 76 | } | ||
| 77 | |||
| 78 | // Loop through min edges and and connect disconnected components. | ||
| 79 | for (int p = 0, c = stars ; p < possible_edges.size() && | ||
| 80 | c > min_constellations; ++p) { | ||
| 81 | const Edge& e = possible_edges[p].second; | ||
| 82 | if (v_s[e.first] != v_s[e.second]) { | ||
| 83 | edges.push_back(e); | ||
| 84 | int V = v_s[e.first], U = v_s[e.second]; | ||
| 85 | for (int i = 0; i < v_sets[U].size(); ++i) { | ||
| 86 | int u = v_sets[U][i]; | ||
| 87 | v_s[u] = V; | ||
| 88 | v_sets[V].push_back(u); | ||
| 89 | } | ||
| 90 | v_sets[U].clear(); | ||
| 91 | c--; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | }; | ||
| 96 | } // namespace Constellation | ||
| 97 | |||
| 98 | #endif // CONSTELLATION_MODEL_H_ | ||
diff --git a/ConstellationView.h b/ConstellationView.h new file mode 100644 index 0000000..491afe3 --- /dev/null +++ b/ConstellationView.h | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | #include <iostream> | ||
| 2 | #include <vector> | ||
| 3 | |||
| 4 | #include <SFML/Graphics.hpp> | ||
| 5 | |||
| 6 | #ifndef CONSTELLATION_VIEW_H_ | ||
| 7 | #define CONSTELLATION_VIEW_H_ | ||
| 8 | |||
| 9 | namespace Constellation { | ||
| 10 | class View { | ||
| 11 | int width, height, star_radius; | ||
| 12 | sf::RenderWindow *window; | ||
| 13 | std::vector<sf::Vector2f> starPos; | ||
| 14 | sf::VertexArray edges; | ||
| 15 | sf::CircleShape star; | ||
| 16 | public: | ||
| 17 | View(int w, int h, int sr) : width(w), height(h), star_radius(sr), | ||
| 18 | window(nullptr), star(star_radius), edges(sf::Lines) { | ||
| 19 | star.setOrigin(star_radius, star_radius); | ||
| 20 | } | ||
| 21 | |||
| 22 | ~View() { | ||
| 23 | if (window) { | ||
| 24 | delete window; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | void make_window () { | ||
| 29 | if (!window) { | ||
| 30 | window = new sf::RenderWindow(sf::VideoMode(width, height), "Constellations"); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void close_window () { | ||
| 35 | window->close(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void add_star(const sf::Vector2f& v) { | ||
| 39 | starPos.push_back(v); | ||
| 40 | } | ||
| 41 | |||
| 42 | void clear_stars() { | ||
| 43 | starPos.clear(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void add_edge(const sf::Vertex& v, const sf::Vertex& u) { | ||
| 47 | edges.append(v); | ||
| 48 | edges.append(u); | ||
| 49 | } | ||
| 50 | |||
| 51 | void clear_edges() { | ||
| 52 | edges.clear(); | ||
| 53 | } | ||
| 54 | |||
| 55 | void print_done() { | ||
| 56 | std::cout << "Done." << std::endl; | ||
| 57 | } | ||
| 58 | |||
| 59 | void display() { | ||
| 60 | window->clear(); | ||
| 61 | window->draw(edges); | ||
| 62 | for (const sf::Vector2f& p : starPos) { | ||
| 63 | star.setPosition(p); | ||
| 64 | window->draw(star); | ||
| 65 | } | ||
| 66 | window->display(); | ||
| 67 | } | ||
| 68 | |||
| 69 | friend class Controller; | ||
| 70 | }; | ||
| 71 | } // namespace Constellation | ||
| 72 | #endif // CONSTELLATION_VIEW_H_ | ||
diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..58c7d2c --- /dev/null +++ b/config.sh | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/usr/bin/env sh | ||
| 2 | |||
| 3 | cmake -S . -B build | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include "ConstellationApp.h" | ||
| 2 | |||
| 3 | int main () { | ||
| 4 | Constellation::App app(1200, 675, 3, 400, 10); | ||
| 5 | app.run(); | ||
| 6 | return 0; | ||
| 7 | } | ||
