diff src/main.c @ 27:30b6812fefdd

add cmake config
author Dennis C. M. <dennis@denniscm.com>
date Wed, 28 Jun 2023 08:58:44 +0100
parents main.c@8a5a7aee69ce
children dae463bbf5ca
line wrap: on
line diff
--- /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;
+}