#include "render.h" #include "algorithms.h" #include "structs.h" #include #include #include #include #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); }