165 lines
3.8 KiB
C
165 lines
3.8 KiB
C
#include "structs.h"
|
|
#include <SDL2/SDL.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#define WINDOW_WIDTH 800
|
|
#define WINDOW_HEIGHT 600
|
|
#define NODE_RADIUS 5
|
|
#define ITERATIONS 1000 // layout simulation steps
|
|
#define COOLING 1 // temperature cooling rate
|
|
|
|
struct layout_node_t {
|
|
double x, y; // position
|
|
double vx, vy; // velocity / displacement
|
|
};
|
|
typedef struct layout_node_t layout_node_t;
|
|
|
|
// display a simple node of radius r
|
|
static void draw_node(SDL_Renderer *renderer, int cx, int cy, int r)
|
|
{
|
|
SDL_SetRenderDrawColor(renderer, 215, 153, 33, 255);
|
|
for (int dy = -r; dy <= r; dy++) {
|
|
int dx = (int)sqrt((double)(r * r - dy * dy));
|
|
SDL_RenderDrawLine(renderer, cx - dx, cy + dy, cx + dx,
|
|
cy + dy);
|
|
}
|
|
}
|
|
|
|
// compute a layout based on the fruchtermann-reingold algorithm.
|
|
static layout_node_t *compute_layout(const graph_t *graph)
|
|
{
|
|
int n = graph->n;
|
|
layout_node_t *nodes = malloc(n * sizeof(layout_node_t));
|
|
if (!nodes)
|
|
return NULL;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
nodes[i].x = (double)(rand() % (WINDOW_WIDTH - 100)) + 50;
|
|
nodes[i].y = (double)(rand() % (WINDOW_HEIGHT - 100)) + 50;
|
|
nodes[i].vx = 0;
|
|
nodes[i].vy = 0;
|
|
}
|
|
|
|
double area = (WINDOW_WIDTH - 200) * (WINDOW_HEIGHT - 200);
|
|
double k = sqrt(area / n);
|
|
double temp = WINDOW_WIDTH * 0.1;
|
|
|
|
for (int iter = 0; iter < ITERATIONS; iter++) {
|
|
for (int i = 0; i < n; i++)
|
|
nodes[i].vx = nodes[i].vy = 0.0;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
for (int j = i + 1; j < n; j++) {
|
|
double dx = nodes[i].x - nodes[j].x;
|
|
double dy = nodes[i].y - nodes[j].y;
|
|
double dist = sqrt(dx * dx + dy * dy);
|
|
if (dist < 1.0)
|
|
dist = 1.0;
|
|
|
|
double force = (k * k) / dist;
|
|
double fx = (dx / dist) * force;
|
|
double fy = (dy / dist) * force;
|
|
|
|
nodes[i].vx += fx;
|
|
nodes[i].vy += fy;
|
|
nodes[j].vx -= fx;
|
|
nodes[j].vy -= fy;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
node_t *neighbor = graph->adj_lists[i];
|
|
while (neighbor) {
|
|
int j = neighbor->id;
|
|
if (i < j) {
|
|
double dx = nodes[i].x - nodes[j].x;
|
|
double dy = nodes[i].y - nodes[j].y;
|
|
double dist = sqrt(dx * dx + dy * dy);
|
|
if (dist < 1.0)
|
|
dist = 1.0;
|
|
|
|
double force = (dist * dist) / k;
|
|
double fx = (dx / dist) * force;
|
|
double fy = (dy / dist) * force;
|
|
|
|
nodes[i].vx -= fx;
|
|
nodes[i].vy -= fy;
|
|
nodes[j].vx += fx;
|
|
nodes[j].vy += fy;
|
|
}
|
|
neighbor = neighbor->next;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
double disp = sqrt(nodes[i].vx * nodes[i].vx +
|
|
nodes[i].vy * nodes[i].vy);
|
|
if (disp < 1.0)
|
|
disp = 1.0;
|
|
|
|
double scale = fmin(disp, temp) / disp;
|
|
nodes[i].x += nodes[i].vx * scale;
|
|
nodes[i].y += nodes[i].vy * scale;
|
|
|
|
// Keep nodes within window bounds with padding
|
|
nodes[i].x = fmax(NODE_RADIUS + 10,
|
|
fmin(WINDOW_WIDTH - NODE_RADIUS - 10,
|
|
nodes[i].x));
|
|
nodes[i].y = fmax(NODE_RADIUS + 10,
|
|
fmin(WINDOW_HEIGHT - NODE_RADIUS - 10,
|
|
nodes[i].y));
|
|
}
|
|
|
|
temp *= COOLING;
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
void render_graph(SDL_Renderer *renderer, const graph_t *graph)
|
|
{
|
|
if (!renderer || !graph || !graph->adj_lists)
|
|
return;
|
|
|
|
int n = graph->n;
|
|
|
|
layout_node_t *layout = compute_layout(graph);
|
|
if (!layout)
|
|
return;
|
|
|
|
// Clear background
|
|
SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255);
|
|
SDL_RenderClear(renderer);
|
|
|
|
// Draw edges
|
|
SDL_SetRenderDrawColor(renderer, 189, 189, 189, 255);
|
|
for (int i = 0; i < n; i++) {
|
|
node_t *neighbor = graph->adj_lists[i];
|
|
|
|
while (neighbor) {
|
|
int j = neighbor->id;
|
|
if (i < j) {
|
|
SDL_RenderDrawLine(renderer, (int)layout[i].x,
|
|
(int)layout[i].y,
|
|
(int)layout[j].x,
|
|
(int)layout[j].y);
|
|
}
|
|
neighbor = neighbor->next;
|
|
}
|
|
}
|
|
|
|
// Draw nodes
|
|
for (int i = 0; i < n; i++) {
|
|
int x = (int)layout[i].x;
|
|
int y = (int)layout[i].y;
|
|
|
|
// Node fill
|
|
draw_node(renderer, x, y, NODE_RADIUS);
|
|
}
|
|
|
|
free(layout);
|
|
SDL_RenderPresent(renderer);
|
|
}
|