praca_magisterska/code/csharp/Visualizer.cs

183 lines
6.8 KiB
C#
Raw Permalink Normal View History

2026-03-08 21:29:46 +01:00
/*
* Raylib GUI for shortest-path algorithm visualization (C#).
*
* Controls:
* 1 / 2 / 3 Dijkstra / Bellman-Ford / A*
* SPACE advance one step
* R reset
* Q / ESC quit
*/
using System;
using System.Collections.Generic;
using System.Numerics;
using Raylib_cs;
namespace ShortestPathViz;
static class Visualizer
{
const int W = 1100;
const int H = 700;
const float NodeR = 28f;
const int FSiz = 20;
const int FSizS = 16;
// Colours
static readonly Color BgCol = new(245, 245, 250, 255);
static readonly Color EdgeDef = new(160, 160, 160, 255);
static readonly Color EdgeRel = new( 60, 160, 60, 255);
static readonly Color EdgePath = new( 40, 180, 100, 255);
static readonly Color NodeDef = new(255, 255, 255, 255);
static readonly Color NodeVis = new(180, 220, 255, 255);
static readonly Color NodeCur = new(255, 220, 80, 255);
static readonly Color NodePath = new( 60, 200, 120, 255);
static readonly Color ColBlue = new( 60, 100, 220, 255);
static readonly Color ColOrange = new(240, 140, 40, 255);
static readonly Color BarCol = new(230, 230, 230, 255);
static readonly string[] AlgoNames =
["Dijkstra's Algorithm", "Bellman-Ford Algorithm", "A* Algorithm"];
delegate AlgoState InitFn();
delegate void StepFn(AlgoState st);
static readonly InitFn[] Inits =
[Algorithms.DijkstraInit, Algorithms.BellmanFordInit, Algorithms.AStarInit];
static readonly StepFn[] Steps =
[Algorithms.DijkstraStep, Algorithms.BellmanFordStep, Algorithms.AStarStep];
// ── Edge drawing ────────────────────────────────────────────────────
static void DrawEdge(Vector2 p1, Vector2 p2, Color col, float thick)
{
float dx = p2.X - p1.X, dy = p2.Y - p1.Y;
float len = MathF.Sqrt(dx * dx + dy * dy);
if (len < 1f) return;
float ux = dx / len, uy = dy / len;
var a = new Vector2(p1.X + ux * NodeR, p1.Y + uy * NodeR);
var b = new Vector2(p2.X - ux * NodeR, p2.Y - uy * NodeR);
Raylib.DrawLineEx(a, b, thick, col);
}
// ── Render ──────────────────────────────────────────────────────────
static void Render(AlgoState st, int algo)
{
Raylib.BeginDrawing();
Raylib.ClearBackground(BgCol);
// Title
string title = AlgoNames[algo];
int tw = Raylib.MeasureText(title, FSiz + 4);
Raylib.DrawText(title, W / 2 - tw / 2, 10, FSiz + 4, Color.Black);
// Path edge set
var pathEdges = new HashSet<(string, string)>();
for (int i = 0; i < st.Path.Count - 1; i++)
{
pathEdges.Add((st.Path[i], st.Path[i + 1]));
pathEdges.Add((st.Path[i + 1], st.Path[i]));
}
// Edges
foreach (var e in Graph.Edges)
{
var p1v = Graph.Nodes[e.Src];
var p2v = Graph.Nodes[e.Dst];
var p1 = new Vector2(p1v.X, p1v.Y);
var p2 = new Vector2(p2v.X, p2v.Y);
Color col = EdgeDef; float thick = 2f;
if (pathEdges.Contains((e.Src, e.Dst)))
{ col = EdgePath; thick = 5f; }
else if (st.RelaxedEdges.Contains((e.Src, e.Dst)) ||
st.RelaxedEdges.Contains((e.Dst, e.Src)))
{ col = EdgeRel; thick = 3f; }
DrawEdge(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 = MathF.Sqrt(dx * dx + dy * dy);
if (len > 0) { mx += (-dy / len) * 14; my += (dx / len) * 14; }
string wt = e.Weight.ToString();
int wtw = Raylib.MeasureText(wt, FSizS);
Raylib.DrawText(wt, (int)mx - wtw / 2, (int)my - FSizS / 2,
FSizS, Color.DarkGray);
}
// Nodes
foreach (var (name, pos) in Graph.Nodes)
{
Color col = NodeDef;
if (st.Path.Contains(name) && st.Finished)
col = NodePath;
else if (name == st.CurrentNode)
col = NodeCur;
else if (st.Visited.Contains(name))
col = NodeVis;
Raylib.DrawCircle(pos.X, pos.Y, NodeR, col);
Raylib.DrawCircleLines(pos.X, pos.Y, NodeR, Color.Black);
// Name
int nw = Raylib.MeasureText(name, FSiz);
Raylib.DrawText(name, pos.X - nw / 2, pos.Y - FSiz / 2 - 8,
FSiz, Color.Black);
// Distance
double d = st.Dist.GetValueOrDefault(name, double.PositiveInfinity);
string dtxt = double.IsPositiveInfinity(d) ? "d=inf" : $"d={d:F0}";
int dw = Raylib.MeasureText(dtxt, FSizS);
Raylib.DrawText(dtxt, pos.X - dw / 2, pos.Y + 8, FSizS, ColBlue);
// h(n) for A*
if (algo == 2)
{
string ht = $"h={Graph.Heuristic(name):F1}";
Raylib.DrawText(ht, pos.X + (int)NodeR + 4, pos.Y - 8,
FSizS, ColOrange);
}
}
// Info bar
Raylib.DrawRectangle(0, H - 80, W, 80, BarCol);
Raylib.DrawLine(0, H - 80, W, H - 80, Color.DarkGray);
Raylib.DrawText(st.Message, 20, H - 70, FSiz, Color.Black);
Raylib.DrawText("[1] Dijkstra [2] Bellman-Ford [3] A* " +
"[SPACE] Step [R] Reset [Q] Quit",
20, H - 35, FSizS, Color.DarkGray);
Raylib.EndDrawing();
}
// ── Main ────────────────────────────────────────────────────────────
static void Main()
{
Raylib.InitWindow(W, H, "Shortest Path Visualizer");
Raylib.SetTargetFPS(60);
int algo = 0;
AlgoState state = Inits[0]();
while (!Raylib.WindowShouldClose())
{
if (Raylib.IsKeyPressed(KeyboardKey.Q)) break;
if (Raylib.IsKeyPressed(KeyboardKey.One)) { algo = 0; state = Inits[0](); }
if (Raylib.IsKeyPressed(KeyboardKey.Two)) { algo = 1; state = Inits[1](); }
if (Raylib.IsKeyPressed(KeyboardKey.Three)) { algo = 2; state = Inits[2](); }
if (Raylib.IsKeyPressed(KeyboardKey.R)) state = Inits[algo]();
if (Raylib.IsKeyPressed(KeyboardKey.Space) && !state.Finished)
Steps[algo](state);
Render(state, algo);
}
Raylib.CloseWindow();
}
}