praca_magisterska/code/cpp/visualizer.cpp

174 lines
5.9 KiB
C++
Raw Normal View History

2026-03-08 21:29:46 +01:00
/*
* Raylib GUI for shortest-path algorithm visualization.
*
* Controls:
* 1 / 2 / 3 Dijkstra / Bellman-Ford / A*
* SPACE advance one step
* R reset
* Q / ESC quit
*/
#include "algorithms.h"
#include "raylib.h"
#include <algorithm>
#include <cmath>
#include <string>
static const int W = 1100;
static const int H = 700;
static const float NODE_R = 28.0f;
static const int FSIZ = 20;
static const int FSIZ_S = 16;
// Colours
static const Color BG_COL = {245, 245, 250, 255};
static const Color EDGE_DEF = {160, 160, 160, 255};
static const Color EDGE_REL = { 60, 160, 60, 255};
static const Color EDGE_PATH = { 40, 180, 100, 255};
static const Color NODE_DEF = {255, 255, 255, 255};
static const Color NODE_VIS = {180, 220, 255, 255};
static const Color NODE_CUR = {255, 220, 80, 255};
static const Color NODE_PATH = { 60, 200, 120, 255};
static const Color COL_BLUE = { 60, 100, 220, 255};
static const Color COL_ORANGE = {240, 140, 40, 255};
static const Color BAR_COL = {230, 230, 230, 255};
static const char* ALGO_NAMES[] = {
"Dijkstra's Algorithm",
"Bellman-Ford Algorithm",
"A* Algorithm",
};
// ─── Edge drawing (clipped to node radius) ──────────────────────────────
static void draw_edge(Vector2 p1, Vector2 p2, Color col, float thick) {
float dx = p2.x - p1.x, dy = p2.y - p1.y;
float len = sqrtf(dx * dx + dy * dy);
if (len < 1.0f) return;
float ux = dx / len, uy = dy / len;
Vector2 a = {p1.x + ux * NODE_R, p1.y + uy * NODE_R};
Vector2 b = {p2.x - ux * NODE_R, p2.y - uy * NODE_R};
DrawLineEx(a, b, thick, col);
}
// ─── Render ─────────────────────────────────────────────────────────────
static void render(const AlgoState& st, int algo) {
BeginDrawing();
ClearBackground(BG_COL);
// Title
const char* title = ALGO_NAMES[algo];
int tw = MeasureText(title, FSIZ + 4);
DrawText(title, W / 2 - tw / 2, 10, FSIZ + 4, BLACK);
// Build path edge set
std::set<std::pair<std::string, std::string>> path_edges;
for (size_t i = 0; i + 1 < st.path.size(); i++) {
path_edges.insert({st.path[i], st.path[i + 1]});
path_edges.insert({st.path[i + 1], st.path[i]});
}
// Edges
for (auto& e : EDGES) {
auto p1v = NODES.at(e.src);
auto p2v = NODES.at(e.dst);
Vector2 p1 = {(float)p1v.x, (float)p1v.y};
Vector2 p2 = {(float)p2v.x, (float)p2v.y};
Color col = EDGE_DEF; float thick = 2.0f;
if (path_edges.count({e.src, e.dst})) {
col = EDGE_PATH; thick = 5.0f;
} else if (st.relaxed_edges.count({e.src, e.dst}) ||
st.relaxed_edges.count({e.dst, e.src})) {
col = EDGE_REL; thick = 3.0f;
}
draw_edge(p1, p2, col, thick);
// Weight label
float mx = (p1.x + p2.x) / 2;
float my = (p1.y + p2.y) / 2;
float dx = p2.x - p1.x, dy = p2.y - p1.y;
float len = sqrtf(dx * dx + dy * dy);
if (len > 0) { mx += (-dy / len) * 14; my += (dx / len) * 14; }
const char* wt = TextFormat("%d", e.weight);
int wtw = MeasureText(wt, FSIZ_S);
DrawText(wt, (int)mx - wtw / 2, (int)my - FSIZ_S / 2, FSIZ_S, DARKGRAY);
}
// Nodes
for (auto& [name, pos] : NODES) {
Color col = NODE_DEF;
if (std::find(st.path.begin(), st.path.end(), name) != st.path.end()
&& st.finished)
col = NODE_PATH;
else if (name == st.current_node)
col = NODE_CUR;
else if (st.visited.count(name))
col = NODE_VIS;
DrawCircle(pos.x, pos.y, NODE_R, col);
DrawCircleLines(pos.x, pos.y, NODE_R, BLACK);
// Name
int nw = MeasureText(name.c_str(), FSIZ);
DrawText(name.c_str(), pos.x - nw / 2, pos.y - FSIZ / 2 - 8,
FSIZ, BLACK);
// Distance
double d = INF_VAL;
auto it = st.dist.find(name);
if (it != st.dist.end()) d = it->second;
const char* dtxt = (d >= INF_VAL) ? "d=inf" : TextFormat("d=%d", (int)d);
int dw = MeasureText(dtxt, FSIZ_S);
DrawText(dtxt, pos.x - dw / 2, pos.y + 8, FSIZ_S, COL_BLUE);
// h(n) for A*
if (algo == 2) {
const char* ht = TextFormat("h=%.1f", heuristic(name));
DrawText(ht, pos.x + (int)NODE_R + 4, pos.y - 8, FSIZ_S, COL_ORANGE);
}
}
// Info bar
DrawRectangle(0, H - 80, W, 80, BAR_COL);
DrawLine(0, H - 80, W, H - 80, DARKGRAY);
DrawText(st.message.c_str(), 20, H - 70, FSIZ, BLACK);
DrawText("[1] Dijkstra [2] Bellman-Ford [3] A* "
"[SPACE] Step [R] Reset [Q] Quit",
20, H - 35, FSIZ_S, DARKGRAY);
EndDrawing();
}
// ─── Main ───────────────────────────────────────────────────────────────
int main() {
InitWindow(W, H, "Shortest Path Visualizer");
SetTargetFPS(60);
int algo = 0;
AlgoState state = dijkstra_init();
using StepFn = void(*)(AlgoState&);
using InitFn = AlgoState(*)();
InitFn inits[] = {dijkstra_init, bellman_ford_init, astar_init};
StepFn steps[] = {dijkstra_step, bellman_ford_step, astar_step};
while (!WindowShouldClose()) {
if (IsKeyPressed(KEY_Q)) break;
if (IsKeyPressed(KEY_ONE)) { algo = 0; state = inits[0](); }
if (IsKeyPressed(KEY_TWO)) { algo = 1; state = inits[1](); }
if (IsKeyPressed(KEY_THREE)) { algo = 2; state = inits[2](); }
if (IsKeyPressed(KEY_R)) { state = inits[algo](); }
if (IsKeyPressed(KEY_SPACE) && !state.finished)
steps[algo](state);
render(state, algo);
}
CloseWindow();
return 0;
}