Mercurial > public > algo-animator
view main.c @ 20:fc44102980fb
change rectangle color to white when algorithm is selecting it
author | Dennis C. M. <dennis@denniscm.com> |
---|---|
date | Mon, 26 Jun 2023 18:12:03 +0100 |
parents | a027304ec75d |
children | 8a5a7aee69ce |
line wrap: on
line source
#include <stdio.h> #include <stdlib.h> #include <GL/glut.h> #include <math.h> #include <time.h> #include <stdbool.h> #include <ft2build.h> #include FT_FREETYPE_H #define WINDOW_HEIGHT 1080 #define WINDOW_WIDTH 1920 #define VPADDING 150 #define RECT_WIDTH 5 #define SPACE 1 /* Global variables */ FT_Library ft_library; FT_Face ft_face; struct Algo { char name[50]; void (*function)(); }; struct Algo algos[2]; int selected_algo = 0; int speed = 1; int refresh_counter = 0; int iter_counter = 0; int arr_size; struct Element { float value; bool current; }; struct Element *arr; bool run; /* Algorithms */ // Just some variables to store the state of the running algorithm struct AlgoState { int a; int b; int c; }; struct AlgoState as = {0, 0, 0}; void swap_elements(int x, int y) { struct Element temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; } void bubble_sort() { /* * a: Index of the current selection * b: Index boundary of the sorted array */ if (as.a < arr_size - 1 - as.b) { arr[as.a].current = true; if (arr[as.a].value > arr[as.a + 1].value) { swap_elements(as.a + 1, as.a); } as.a++; } else { as.b++; as.a = 0; } } void selection_sort() { /* * a: Index of current selection * b: Index of boundary of sorted array * c: Index of the minimum element */ if (as.a < arr_size) { arr[as.a].current = true; if (arr[as.a].value < arr[as.c].value) { // Save new minimum as.c = as.a; } as.a++; } else { swap_elements(as.b, as.c); as.b++; as.a = as.b; as.c = as.a; } } /* Helper functions */ void create_array() { arr_size = WINDOW_WIDTH / (RECT_WIDTH + SPACE); arr = malloc(arr_size * sizeof(struct Element)); float rect_increase = (WINDOW_HEIGHT - VPADDING * 2) / (float)(arr_size - 1); for (int i = 1; i <= arr_size; i++) { arr[i - 1].value = i * rect_increase; arr[i - 1].current = false; } } void randomize_array() { srand(time(NULL)); // Fisher-Yates shuffle for (int i = arr_size - 1; i > 0; i--) { int j = rand() % (i + 1); // Swap swap_elements(i, j); } } bool array_sorted() { for (int i = 0; i < arr_size - 1; i++) { if (arr[i].value > arr[i + 1].value) { return false; } } return true; } void algo_selector(int direction) { int selection = selected_algo + direction; int lower = 0; int upper = (sizeof(algos) / sizeof(algos[0])) - 1; if (selection >= lower && selection <= upper) { selected_algo = selection; } } /* Render functions */ void render_text(int x, int y, char* text) { for (const char *c = text; *c; c++) { // Get glyph index from character code FT_UInt glyph_index = FT_Get_Char_Index(ft_face, *c); if (glyph_index == 0) { fprintf(stderr, "Given character code has no glyph image in the face\n"); exit(1); } // Load glyph image if (FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_DEFAULT)) { fprintf(stderr, "Failed to load glyph.\n"); exit(1); } // Render glyph if (FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL)) { fprintf(stderr, "Failed to render glyph.\n"); exit(1); } FT_GlyphSlot slot = ft_face->glyph; FT_Bitmap* glyph_bitmap = &slot->bitmap; // Flip the bitmap vertically unsigned char* flipped_bitmap = (unsigned char*)malloc(glyph_bitmap->width * glyph_bitmap->rows); for (int row = 0; row < glyph_bitmap->rows; row++) { unsigned char* src_row = glyph_bitmap->buffer + (row * glyph_bitmap->width); unsigned char* dest_row = flipped_bitmap + ((glyph_bitmap->rows - row - 1) * glyph_bitmap->width); memcpy(dest_row, src_row, glyph_bitmap->width); } glyph_bitmap->buffer = flipped_bitmap; // Calculate the adjusted y position based on the glyph's bearing int adjusted_y = y + (slot->bitmap_top - glyph_bitmap->rows); glRasterPos2f(x, adjusted_y); glDrawPixels(glyph_bitmap->width, glyph_bitmap->rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, glyph_bitmap->buffer); x += slot->advance.x / 64; } } void display() { glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); int x = 0; for (int i = 0; i < arr_size; i++) { if (arr[i].current) { glColor3f(1.0, 1.0, 1.0); } else { glColor3f(1.0, 0.7569, 0.0); } // Bottom left glVertex2f(x, VPADDING); // Top left glVertex2f(x, VPADDING + arr[i].value); // Top right glVertex2f(x + RECT_WIDTH, VPADDING + arr[i].value); // Bottom right glVertex2f(x + RECT_WIDTH, VPADDING); x += RECT_WIDTH + SPACE; arr[i].current = false; } glEnd(); // Render text char text[256]; // Top: Column 1 sprintf(text, "Algorithm: %s", algos[selected_algo].name); render_text(20, WINDOW_HEIGHT - 50, text); sprintf(text, "Speed: %i", speed); render_text(20, WINDOW_HEIGHT - 80, text); // Top: Column 2 sprintf(text, "Number of elements: %i", arr_size); render_text(500, WINDOW_HEIGHT - 50, text); sprintf(text, "Iterations: %i", iter_counter); render_text(500, WINDOW_HEIGHT - 80, text); // Bottom: Column 1 render_text(20, VPADDING - 50, "Press a or s to select an algorithm."); render_text(20, VPADDING - 80, "Press u or d to modify speed."); render_text(20, VPADDING - 110, "Press r to randomize the array."); // Bottom: Column 2 render_text(800, VPADDING - 50, "Press enter to run the algorithm."); glutSwapBuffers(); } /* Refresh function */ void idle() { if (run) { algos[selected_algo].function(); refresh_counter++; iter_counter++; if (refresh_counter == speed) { glutPostRedisplay(); refresh_counter = 0; } } else { glutPostRedisplay(); } if (array_sorted()) { run = false; } } /* User input handler */ void keyboard(unsigned char key, int x, int y) { // s: Next algorithm if (key == 115) { algo_selector(1); } // a: Previous algorithm if (key == 97) { algo_selector(-1); } // r: Reset state if (key == 114) { randomize_array(); // Reset state iter_counter = 0; refresh_counter = 0; run = false; // Reset algo steps as = (struct AlgoState){0, 0}; } // u: Increase speed if (key == 117) { speed++; } // d: reduce speed if (key == 100) { if (speed > 1) { speed--; } } // enter: Run program if (key == 13) { run = true; } // p: Pause program if (key == 112) { run = false; } } /* Set up functions */ void setup_gl() { // Set background dark glClearColor(0.0, 0.0, 0.0, 1.0); // Set point color and size to 1 pixel glColor3f(1.0, 0.7569, 0.0); glPointSize(5.0); // Matrix projection and reset with identity glMatrixMode(GL_PROJECTION); glLoadIdentity(); /* * Creates projection matrix * x increases from left to right (0 to WINDOW_WIDTH) * y increases from bottom to top (0 to WINDOW_HEIGHT) */ gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT); /* * This fucking line... I spent a day rendering weird symbols * because the padding that adds FreeType to each row of the bitmap * does not match the padding expected by GL. */ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } void setup_freetype() { // Init library if (FT_Init_FreeType(&ft_library)) { fprintf(stderr, "Failed to initialize FreeType library\n"); exit(1); } // Load font if (FT_New_Face(ft_library, "fonts/JetBrainsMono-Regular.ttf", 0, &ft_face)) { fprintf(stderr, "Failed to load font\n"); exit(1); } // Set font size if (FT_Set_Pixel_Sizes(ft_face, 0, 24)) { fprintf(stderr, "Failed to set font size.\n"); FT_Done_Face(ft_face); FT_Done_FreeType(ft_library); exit(1); } } int main(int argc, char** argv) { strcpy(algos[0].name, "Bubble sort"); algos[0].function = &bubble_sort; strcpy(algos[1].name, "Selection sort"); algos[1].function = &selection_sort; create_array(); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); glutCreateWindow("Algorithm animator"); setup_gl(); setup_freetype(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutIdleFunc(idle); glutMainLoop(); free(arr); FT_Done_Face(ft_face); FT_Done_FreeType(ft_library); return 0; }