abg

Animated background for X11
git clone git://git.laack.co/abg.git
Log | Files | Refs | README | LICENSE

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(); }