graph.cpp (5457B)
1 #include "../include/graph.hpp" 2 3 #include <raylib.h> 4 5 #include <cstddef> 6 #include <cstdint> 7 #include <stdexcept> 8 9 #include "../include/constants.hpp" 10 #include "../include/utils.hpp" 11 #include "../include/vertex.hpp" 12 13 Graph::Graph(std::size_t edgeCount, std::size_t vertCount, uint32_t xMax, 14 uint32_t yMax, std::uint32_t seed) { 15 if (edgeCount > 0 && vertCount <= 1) { 16 throw std::invalid_argument("This graph does not support self-loops."); 17 } 18 if (xMax <= 0 || yMax <= 0) { 19 throw std::invalid_argument("xMax and yMax must be > 0."); 20 } 21 22 std::mt19937 rng{seed}; 23 std::uniform_int_distribution<std::size_t> pick1(0, vertCount - 1); 24 25 std::uniform_int_distribution<std::uint32_t> pick2(0, xMax - 1); 26 std::uniform_int_distribution<std::uint32_t> pick3(0, yMax - 1); 27 28 vertices.reserve(vertCount); 29 edges.resize(vertCount); 30 31 for (std::size_t i = 0; i < vertCount; ++i) { 32 Vector2 rnd = Vector2{(float)pick2(rng), (float)pick3(rng)}; 33 Vertex v{rnd, VERTEX_RENDER_SIZE}; 34 this->vertices.push_back(v); 35 } 36 for (std::size_t i = 0; i < edgeCount; ++i) { 37 std::size_t idx1 = 0; 38 std::size_t idx2 = 0; 39 // no self-edges 40 while (idx1 == idx2) { 41 idx1 = pick1(rng); 42 idx2 = pick1(rng); 43 } 44 45 double distance = 46 distanceSquared(vertices[idx1].position, vertices[idx2].position); 47 48 // this maintains the invariant that the first vertex of the edge is the 49 // current one. 50 Edge e1{idx1, idx2, distance, i}; 51 Edge e2{idx2, idx1, distance, i}; 52 this->edges[idx1].push_back(e1); 53 this->edges[idx2].push_back(e2); 54 } 55 } 56 57 std::string Graph::toString() noexcept { 58 std::string result = "edges: {"; 59 60 for (auto edgesV : this->edges) { 61 for (auto edge : edgesV) { 62 result += edge.toString(); 63 } 64 } 65 66 result += "}"; 67 68 result += "\nvertices: {"; 69 70 for (auto vertex : this->vertices) { 71 result += vertex.toString(); 72 } 73 74 result += "}"; 75 return result; 76 } 77 78 // only render newly traversed edges / nodes. 79 void Graph::renderUnrenderedTraversed() noexcept { 80 for (auto* edgesT : edgesToRender) { 81 auto& edge = *edgesT; 82 std::size_t idx1 = edge.v1Index; 83 std::size_t idx2 = edge.v2Index; 84 auto v1 = vertices[idx1].position; 85 auto v2 = vertices[idx2].position; 86 DrawLineEx(v1, v2, EDGE_REDNER_SIZE, WHITE); 87 } 88 edgesToRender = {}; 89 90 for (auto* vertex : verticesToRender) { 91 vertex->render(); 92 } 93 verticesToRender = {}; 94 } 95 96 void Graph::render() noexcept { 97 std::vector<Edge> visited{}; 98 for (auto& edgesT : this->edges) { 99 for (auto& edge : edgesT) { 100 if (edge.v1Index < edge.v2Index) { 101 continue; // since edges are tracked twice with v1 102 // it's safe to skip one of them. 103 } 104 std::size_t idx1 = edge.v1Index; 105 std::size_t idx2 = edge.v2Index; 106 auto v1 = vertices[idx1].position; 107 auto v2 = vertices[idx2].position; 108 if (edge.traversed) { 109 visited.push_back(edge); 110 } else { 111 DrawLineEx(v1, v2, EDGE_REDNER_SIZE, DARKERGRAY); 112 } 113 } 114 } 115 116 for (auto& vertex : this->vertices) { 117 vertex.render(); 118 } 119 120 // ensure we draw visited over unvisited for better looks 121 for (auto& edge : visited) { 122 if (edge.v1Index < edge.v2Index) { 123 continue; // since edges are tracked twice with v1 124 // it's safe to skip one of them. 125 } 126 std::size_t idx1 = edge.v1Index; 127 std::size_t idx2 = edge.v2Index; 128 auto v1 = vertices[idx1].position; 129 auto v2 = vertices[idx2].position; 130 DrawLineEx(v1, v2, EDGE_REDNER_SIZE, WHITE); 131 } 132 } 133 134 void Graph::traverseVertexIdx(std::size_t idx) { 135 this->vertices[idx].visited = true; 136 verticesToRender.push_back(&vertices[idx]); 137 } 138 139 // we assume the current vertex is already marked as traversed so we check to 140 // see if the other is here. 141 std::vector<Edge>* Graph::getEdgesWithUnvisitedVertices(std::size_t idx) { 142 std::vector<Edge>* result = new std::vector<Edge>{}; 143 for (auto edge : edges[idx]) { 144 if (!vertices[edge.v2Index].visited && !edge.traversed) { 145 result->push_back(edge); 146 } 147 } 148 return result; 149 } 150 151 std::vector<Edge> Graph::getEdgesOfVertexIdx(std::size_t idx) { 152 return this->edges[idx]; 153 } 154 155 void Graph::setEdgeTraversed(Edge e) { 156 std::size_t source = e.v1Index; 157 std::size_t destination = e.v2Index; 158 159 if (source == destination) { 160 return; 161 } 162 163 auto& cEdges = edges[source]; 164 165 for (auto& edge : cEdges) { 166 if (edge.v2Index == destination || edge.v1Index == destination) { 167 edge.traversed = true; 168 } 169 } 170 171 auto& oEdges = edges[destination]; 172 173 for (auto& edge : oEdges) { 174 if (edge.v2Index == source || edge.v1Index == source) { 175 edge.traversed = true; 176 edgesToRender.push_back(&edge); 177 } 178 } 179 } 180 181 Vertex Graph::getVertex(std::size_t idx) { 182 // idx can't be negative bc size_t 183 if (idx >= vertices.size()) { 184 throw std::invalid_argument("idx out of bounds for vertex list"); 185 } 186 return vertices[idx]; 187 } 188 189 std::size_t Graph::getVertexCount() const noexcept { return vertices.size(); }