This commit is contained in:
Tiago Batista Cardoso
2026-02-24 18:28:01 +01:00
parent 3928bee6c6
commit 2736884f46
2 changed files with 16 additions and 29 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

View File

@@ -6,17 +6,20 @@
#define WINDOW_WIDTH 800 #define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600 #define WINDOW_HEIGHT 600
#define NODE_RADIUS 15 #define NODE_RADIUS 5
#define ITERATIONS 500 // layout simulation steps #define ITERATIONS 1000 // layout simulation steps
#define COOLING 0.97 // temperature cooling rate #define COOLING 1 // temperature cooling rate
typedef struct { struct layout_node_t {
double x, y; // position double x, y; // position
double vx, vy; // velocity / displacement double vx, vy; // velocity / displacement
} layout_node_t; };
typedef struct layout_node_t layout_node_t;
static void draw_circle(SDL_Renderer *renderer, int cx, int cy, int r) // 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++) { for (int dy = -r; dy <= r; dy++) {
int dx = (int)sqrt((double)(r * r - dy * dy)); int dx = (int)sqrt((double)(r * r - dy * dy));
SDL_RenderDrawLine(renderer, cx - dx, cy + dy, cx + dx, SDL_RenderDrawLine(renderer, cx - dx, cy + dy, cx + dx,
@@ -24,7 +27,7 @@ static void draw_circle(SDL_Renderer *renderer, int cx, int cy, int r)
} }
} }
// Fruchterman-Reingold force-directed layout // compute a layout based on the fruchtermann-reingold algorithm.
static layout_node_t *compute_layout(const graph_t *graph) static layout_node_t *compute_layout(const graph_t *graph)
{ {
int n = graph->n; int n = graph->n;
@@ -32,8 +35,6 @@ static layout_node_t *compute_layout(const graph_t *graph)
if (!nodes) if (!nodes)
return NULL; return NULL;
// Seed random positions
srand((unsigned)time(NULL));
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
nodes[i].x = (double)(rand() % (WINDOW_WIDTH - 100)) + 50; nodes[i].x = (double)(rand() % (WINDOW_WIDTH - 100)) + 50;
nodes[i].y = (double)(rand() % (WINDOW_HEIGHT - 100)) + 50; nodes[i].y = (double)(rand() % (WINDOW_HEIGHT - 100)) + 50;
@@ -42,15 +43,13 @@ static layout_node_t *compute_layout(const graph_t *graph)
} }
double area = (WINDOW_WIDTH - 200) * (WINDOW_HEIGHT - 200); double area = (WINDOW_WIDTH - 200) * (WINDOW_HEIGHT - 200);
double k = sqrt(area / n); // optimal distance between nodes double k = sqrt(area / n);
double temp = WINDOW_WIDTH * 0.1; // initial temperature double temp = WINDOW_WIDTH * 0.1;
for (int iter = 0; iter < ITERATIONS; iter++) { for (int iter = 0; iter < ITERATIONS; iter++) {
// Reset displacements
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
nodes[i].vx = nodes[i].vy = 0.0; nodes[i].vx = nodes[i].vy = 0.0;
// Repulsive forces (all pairs)
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) { for (int j = i + 1; j < n; j++) {
double dx = nodes[i].x - nodes[j].x; double dx = nodes[i].x - nodes[j].x;
@@ -70,7 +69,6 @@ static layout_node_t *compute_layout(const graph_t *graph)
} }
} }
// Attractive forces (edges only)
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
node_t *neighbor = graph->adj_lists[i]; node_t *neighbor = graph->adj_lists[i];
while (neighbor) { while (neighbor) {
@@ -95,7 +93,6 @@ static layout_node_t *compute_layout(const graph_t *graph)
} }
} }
// Apply displacements, clamped to temperature
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
double disp = sqrt(nodes[i].vx * nodes[i].vx + double disp = sqrt(nodes[i].vx * nodes[i].vx +
nodes[i].vy * nodes[i].vy); nodes[i].vy * nodes[i].vy);
@@ -115,7 +112,6 @@ static layout_node_t *compute_layout(const graph_t *graph)
nodes[i].y)); nodes[i].y));
} }
// Cool the temperature
temp *= COOLING; temp *= COOLING;
} }
@@ -134,11 +130,11 @@ void render_graph(SDL_Renderer *renderer, const graph_t *graph)
return; return;
// Clear background // Clear background
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
// Draw edges // Draw edges
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); SDL_SetRenderDrawColor(renderer, 189, 189, 189, 255);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
node_t *neighbor = graph->adj_lists[i]; node_t *neighbor = graph->adj_lists[i];
@@ -160,17 +156,7 @@ void render_graph(SDL_Renderer *renderer, const graph_t *graph)
int y = (int)layout[i].y; int y = (int)layout[i].y;
// Node fill // Node fill
SDL_SetRenderDrawColor(renderer, 100, 149, 237, 255); draw_node(renderer, x, y, NODE_RADIUS);
draw_circle(renderer, x, y, NODE_RADIUS);
// Node border
SDL_SetRenderDrawColor(renderer, 200, 220, 255, 255);
for (int deg = 0; deg < 360; deg++) {
double a = deg * M_PI / 180.0;
SDL_RenderDrawPoint(renderer,
x + (int)(NODE_RADIUS * cos(a)),
y + (int)(NODE_RADIUS * sin(a)));
}
} }
free(layout); free(layout);