Files
graphe/render.c
Tiago Batista Cardoso 2d3865079d lol
2026-02-28 22:41:19 +01:00

225 lines
5.3 KiB
C

#include "render.h"
#include "algorithms.h"
#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;
}
static SDL_Color community_colors[] = {
{ 251, 73, 52, 255 }, // gruvbox red
{ 250, 189, 47, 255 }, // gruvbox yellow
{ 142, 192, 124, 255 }, // gruvbox green
{ 131, 165, 152, 255 }, // gruvbox aqua
{ 69, 133, 136, 255 }, // gruvbox blue
{ 211, 134, 155, 255 }, // gruvbox pink
{ 254, 128, 25, 255 }, // gruvbox orange
};
#define N_COLORS (sizeof(community_colors) / sizeof(community_colors[0]))
void render_graph(SDL_Renderer *renderer, const graph_t *graph,
VISUALIZATION_TYPE type, int k)
{
if (!renderer || !graph || !graph->adj_lists)
return;
int n = graph->n;
layout_node_t *layout = compute_layout(graph);
if (!layout)
return;
community_result_t *communities = NULL;
louvain_result_t *louvain = NULL;
cbla_result_t *cbla = NULL;
switch (type) {
case CLIQUE:
communities = find_k_clique_communities(graph, k);
break;
case LOUVAIN:
louvain = compute_louvain(graph);
break;
case CBLA:
cbla = cbla_community_detection(graph, k);
break;
}
SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255);
SDL_RenderClear(renderer);
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;
int c;
SDL_Color col;
switch (type) {
case CLIQUE:
c = communities->node_community[i];
col = (c == -1) ? (SDL_Color){ 150, 150, 150, 255 } :
community_colors[c % N_COLORS];
break;
case LOUVAIN:
c = louvain->node_community[i];
col = (c == -1) ? (SDL_Color){ 168, 153, 132, 255 } :
community_colors[c % N_COLORS];
break;
case CBLA:
c = cbla->node_community[i];
col = (c == -1) ? (SDL_Color){ 168, 153, 132, 255 } :
community_colors[c % N_COLORS];
break;
}
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, col.a);
// Node fill
draw_node(renderer, x, y, NODE_RADIUS);
}
if (communities != NULL) {
free_community_result(communities);
}
if (louvain != NULL) {
free_louvain_result(louvain);
}
if (cbla != NULL) {
free_cbla_result(cbla);
}
free(layout);
SDL_RenderPresent(renderer);
}