Mercurial > public > algo-animator
changeset 27:30b6812fefdd
add cmake config
author | Dennis C. M. <dennis@denniscm.com> |
---|---|
date | Wed, 28 Jun 2023 08:58:44 +0100 |
parents | 2945f5898960 |
children | 99592fae8ea1 |
files | .gitignore CMakeLists.txt main.c src/main.c |
diffstat | 4 files changed, 480 insertions(+), 459 deletions(-) [+] |
line wrap: on
line diff
--- a/.gitignore Tue Jun 27 20:17:52 2023 +0100 +++ b/.gitignore Wed Jun 28 08:58:44 2023 +0100 @@ -1,3 +1,5 @@ +build + # Created by https://www.toptal.com/developers/gitignore/api/c # Edit at https://www.toptal.com/developers/gitignore?templates=c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Wed Jun 28 08:58:44 2023 +0100 @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.22) +project(algo_animator) + +set(SOURCES src/main.c) +set(FREETYPE_DIR /usr/local/include/freetype2) + +include_directories(${FREETYPE_DIR}) + +add_executable(algo_animator ${SOURCES}) + +target_link_libraries(algo_animator + glut + GL + GLU + m + freetype +) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/fonts DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/build)
--- a/main.c Tue Jun 27 20:17:52 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,459 +0,0 @@ -#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_WIDTH 1920 -#define WINDOW_HEIGHT 1080 -#define VPADDING 150 -#define RECT_WIDTH 5 -#define SPACE 1 - - -/* Helper functions */ - -struct Element { - float value; - bool current; -}; - -struct Element *arr; -int arr_size; - -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 swap_elements(int x, int y) { - struct Element temp = arr[x]; - arr[x] = arr[y]; - arr[y] = temp; -} - - -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; -} - - -struct Algo { - char name[50]; - void (*function)(); -}; - -struct Algo algos[2]; -int selected_algo = 0; - -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; - } -} - - -/* Algorithms */ - -// Just some variables to store the state of the running algorithm -struct AlgoState { - int a; - int b; - int c; -}; - -struct AlgoState as; - -void reset_state() { - as.a = 0; - as.b = 0; - as.c = 0; -} - - -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; - } -} - - -void quick_sort() { - -} - - -void insertion_sort() { - -} - - -void merge_sort() { - -} - - -/* Render functions */ - -FT_Library ft_library; -FT_Face ft_face; - -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; - } -} - - -int speed = 50; -int iter_counter = 0; - -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."); - render_text(800, VPADDING - 80, "Press p to pause the algorithm."); - - glutSwapBuffers(); -} - - -/* Refresh function */ - - -bool run; -int refresh_counter = 0; - -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 - reset_state(); - } - - // 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; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Wed Jun 28 08:58:44 2023 +0100 @@ -0,0 +1,459 @@ +#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_WIDTH 1920 +#define WINDOW_HEIGHT 1080 +#define VPADDING 150 +#define RECT_WIDTH 5 +#define SPACE 1 + + +/* Helper functions */ + +struct Element { + float value; + bool current; +}; + +struct Element *arr; +int arr_size; + +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 swap_elements(int x, int y) { + struct Element temp = arr[x]; + arr[x] = arr[y]; + arr[y] = temp; +} + + +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; +} + + +struct Algo { + char name[50]; + void (*function)(); +}; + +struct Algo algos[2]; +int selected_algo = 0; + +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; + } +} + + +/* Algorithms */ + +// Just some variables to store the state of the running algorithm +struct AlgoState { + int a; + int b; + int c; +}; + +struct AlgoState as; + +void reset_state() { + as.a = 0; + as.b = 0; + as.c = 0; +} + + +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; + } +} + + +void quick_sort() { + +} + + +void insertion_sort() { + +} + + +void merge_sort() { + +} + + +/* Render functions */ + +FT_Library ft_library; +FT_Face ft_face; + +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; + } +} + + +int speed = 50; +int iter_counter = 0; + +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."); + render_text(800, VPADDING - 80, "Press p to pause the algorithm."); + + glutSwapBuffers(); +} + + +/* Refresh function */ + + +bool run; +int refresh_counter = 0; + +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 + reset_state(); + } + + // 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; +}