comparison 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
comparison
equal deleted inserted replaced
26:2945f5898960 27:30b6812fefdd
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <GL/glut.h>
4 #include <math.h>
5 #include <time.h>
6 #include <stdbool.h>
7 #include <ft2build.h>
8 #include FT_FREETYPE_H
9
10
11 #define WINDOW_WIDTH 1920
12 #define WINDOW_HEIGHT 1080
13 #define VPADDING 150
14 #define RECT_WIDTH 5
15 #define SPACE 1
16
17
18 /* Helper functions */
19
20 struct Element {
21 float value;
22 bool current;
23 };
24
25 struct Element *arr;
26 int arr_size;
27
28 void create_array() {
29 arr_size = WINDOW_WIDTH / (RECT_WIDTH + SPACE);
30 arr = malloc(arr_size * sizeof(struct Element));
31
32 float rect_increase = (WINDOW_HEIGHT - VPADDING * 2) / (float)(arr_size - 1);
33
34 for (int i = 1; i <= arr_size; i++) {
35 arr[i - 1].value = i * rect_increase;
36 arr[i - 1].current = false;
37 }
38 }
39
40
41 void swap_elements(int x, int y) {
42 struct Element temp = arr[x];
43 arr[x] = arr[y];
44 arr[y] = temp;
45 }
46
47
48 void randomize_array() {
49 srand(time(NULL));
50
51 // Fisher-Yates shuffle
52 for (int i = arr_size - 1; i > 0; i--) {
53 int j = rand() % (i + 1);
54
55 // Swap
56 swap_elements(i, j);
57 }
58 }
59
60
61 bool array_sorted() {
62 for (int i = 0; i < arr_size - 1; i++) {
63 if (arr[i].value > arr[i + 1].value) {
64 return false;
65 }
66 }
67
68 return true;
69 }
70
71
72 struct Algo {
73 char name[50];
74 void (*function)();
75 };
76
77 struct Algo algos[2];
78 int selected_algo = 0;
79
80 void algo_selector(int direction) {
81 int selection = selected_algo + direction;
82 int lower = 0;
83 int upper = (sizeof(algos) / sizeof(algos[0])) - 1;
84
85 if (selection >= lower && selection <= upper) {
86 selected_algo = selection;
87 }
88 }
89
90
91 /* Algorithms */
92
93 // Just some variables to store the state of the running algorithm
94 struct AlgoState {
95 int a;
96 int b;
97 int c;
98 };
99
100 struct AlgoState as;
101
102 void reset_state() {
103 as.a = 0;
104 as.b = 0;
105 as.c = 0;
106 }
107
108
109 void bubble_sort() {
110
111 /*
112 * a: Index of the current selection
113 * b: Index boundary of the sorted array
114 */
115
116 if (as.a < arr_size - 1 - as.b) {
117 arr[as.a].current = true;
118
119 if (arr[as.a].value > arr[as.a + 1].value) {
120 swap_elements(as.a + 1, as.a);
121 }
122
123 as.a++;
124 } else {
125 as.b++;
126 as.a = 0;
127 }
128 }
129
130
131 void selection_sort() {
132
133 /*
134 * a: Index of current selection
135 * b: Index of boundary of sorted array
136 * c: Index of the minimum element
137 */
138
139
140 if (as.a < arr_size) {
141 arr[as.a].current = true;
142
143 if (arr[as.a].value < arr[as.c].value) {
144
145 // Save new minimum
146 as.c = as.a;
147 }
148
149 as.a++;
150 } else {
151 swap_elements(as.b, as.c);
152
153 as.b++;
154 as.a = as.b;
155 as.c = as.a;
156 }
157 }
158
159
160 void quick_sort() {
161
162 }
163
164
165 void insertion_sort() {
166
167 }
168
169
170 void merge_sort() {
171
172 }
173
174
175 /* Render functions */
176
177 FT_Library ft_library;
178 FT_Face ft_face;
179
180 void render_text(int x, int y, char* text) {
181 for (const char *c = text; *c; c++) {
182
183 // Get glyph index from character code
184 FT_UInt glyph_index = FT_Get_Char_Index(ft_face, *c);
185
186 if (glyph_index == 0) {
187 fprintf(stderr, "Given character code has no glyph image in the face\n");
188 exit(1);
189 }
190
191 // Load glyph image
192 if (FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_DEFAULT)) {
193 fprintf(stderr, "Failed to load glyph.\n");
194 exit(1);
195 }
196
197 // Render glyph
198 if (FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL)) {
199 fprintf(stderr, "Failed to render glyph.\n");
200 exit(1);
201 }
202
203 FT_GlyphSlot slot = ft_face->glyph;
204 FT_Bitmap* glyph_bitmap = &slot->bitmap;
205
206 // Flip the bitmap vertically
207 unsigned char* flipped_bitmap = (unsigned char*)malloc(glyph_bitmap->width * glyph_bitmap->rows);
208
209 for (int row = 0; row < glyph_bitmap->rows; row++) {
210 unsigned char* src_row = glyph_bitmap->buffer + (row * glyph_bitmap->width);
211 unsigned char* dest_row = flipped_bitmap + ((glyph_bitmap->rows - row - 1) * glyph_bitmap->width);
212 memcpy(dest_row, src_row, glyph_bitmap->width);
213 }
214
215 glyph_bitmap->buffer = flipped_bitmap;
216
217 // Calculate the adjusted y position based on the glyph's bearing
218 int adjusted_y = y + (slot->bitmap_top - glyph_bitmap->rows);
219
220 glRasterPos2f(x, adjusted_y);
221 glDrawPixels(glyph_bitmap->width, glyph_bitmap->rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, glyph_bitmap->buffer);
222
223 x += slot->advance.x / 64;
224 }
225 }
226
227
228 int speed = 50;
229 int iter_counter = 0;
230
231 void display() {
232 glClear(GL_COLOR_BUFFER_BIT);
233
234 glBegin(GL_QUADS);
235
236 int x = 0;
237 for (int i = 0; i < arr_size; i++) {
238
239 if (arr[i].current) {
240 glColor3f(1.0, 1.0, 1.0);
241 } else {
242 glColor3f(1.0, 0.7569, 0.0);
243 }
244
245 // Bottom left
246 glVertex2f(x, VPADDING);
247
248 // Top left
249 glVertex2f(x, VPADDING + arr[i].value);
250
251 // Top right
252 glVertex2f(x + RECT_WIDTH, VPADDING + arr[i].value);
253
254 // Bottom right
255 glVertex2f(x + RECT_WIDTH, VPADDING);
256
257 x += RECT_WIDTH + SPACE;
258
259 arr[i].current = false;
260 }
261
262 glEnd();
263
264 // Render text
265 char text[256];
266
267 // Top: Column 1
268 sprintf(text, "Algorithm: %s", algos[selected_algo].name);
269 render_text(20, WINDOW_HEIGHT - 50, text);
270
271 sprintf(text, "Speed: %i", speed);
272 render_text(20, WINDOW_HEIGHT - 80, text);
273
274 // Top: Column 2
275 sprintf(text, "Number of elements: %i", arr_size);
276 render_text(500, WINDOW_HEIGHT - 50, text);
277
278 sprintf(text, "Iterations: %i", iter_counter);
279 render_text(500, WINDOW_HEIGHT - 80, text);
280
281
282 // Bottom: Column 1
283 render_text(20, VPADDING - 50, "Press a or s to select an algorithm.");
284 render_text(20, VPADDING - 80, "Press u or d to modify speed.");
285 render_text(20, VPADDING - 110, "Press r to randomize the array.");
286
287 // Bottom: Column 2
288 render_text(800, VPADDING - 50, "Press enter to run the algorithm.");
289 render_text(800, VPADDING - 80, "Press p to pause the algorithm.");
290
291 glutSwapBuffers();
292 }
293
294
295 /* Refresh function */
296
297
298 bool run;
299 int refresh_counter = 0;
300
301 void idle() {
302 if (run) {
303 algos[selected_algo].function();
304 refresh_counter++;
305 iter_counter++;
306
307 if (refresh_counter == speed) {
308 glutPostRedisplay();
309 refresh_counter = 0;
310 }
311
312 } else {
313 glutPostRedisplay();
314 }
315
316 if (array_sorted()) {
317 run = false;
318 }
319 }
320
321
322 /* User input handler */
323
324 void keyboard(unsigned char key, int x, int y) {
325
326 // s: Next algorithm
327 if (key == 115) {
328 algo_selector(1);
329 }
330
331 // a: Previous algorithm
332 if (key == 97) {
333 algo_selector(-1);
334 }
335
336 // r: Reset state
337 if (key == 114) {
338 randomize_array();
339
340 // Reset state
341 iter_counter = 0;
342 refresh_counter = 0;
343 run = false;
344
345 // Reset algo steps
346 reset_state();
347 }
348
349 // u: Increase speed
350 if (key == 117) {
351 speed++;
352 }
353
354 // d: reduce speed
355 if (key == 100) {
356 if (speed > 1) {
357 speed--;
358 }
359 }
360
361 // enter: Run program
362 if (key == 13) {
363 run = true;
364 }
365
366 // p: Pause program
367 if (key == 112) {
368 run = false;
369 }
370 }
371
372
373 /* Set up functions */
374
375 void setup_gl() {
376
377 // Set background dark
378 glClearColor(0.0, 0.0, 0.0, 1.0);
379
380 // Set point color and size to 1 pixel
381 glColor3f(1.0, 0.7569, 0.0);
382 glPointSize(5.0);
383
384 // Matrix projection and reset with identity
385 glMatrixMode(GL_PROJECTION);
386 glLoadIdentity();
387
388 /*
389 * Creates projection matrix
390 * x increases from left to right (0 to WINDOW_WIDTH)
391 * y increases from bottom to top (0 to WINDOW_HEIGHT)
392 */
393
394 gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT);
395
396 /*
397 * This fucking line... I spent a day rendering weird symbols
398 * because the padding that adds FreeType to each row of the bitmap
399 * does not match the padding expected by GL.
400 */
401
402 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
403 }
404
405
406 void setup_freetype() {
407
408 // Init library
409 if (FT_Init_FreeType(&ft_library)) {
410 fprintf(stderr, "Failed to initialize FreeType library\n");
411 exit(1);
412 }
413
414 // Load font
415 if (FT_New_Face(ft_library, "fonts/JetBrainsMono-Regular.ttf", 0, &ft_face)) {
416 fprintf(stderr, "Failed to load font\n");
417 exit(1);
418 }
419
420 // Set font size
421 if (FT_Set_Pixel_Sizes(ft_face, 0, 24)) {
422 fprintf(stderr, "Failed to set font size.\n");
423 FT_Done_Face(ft_face);
424 FT_Done_FreeType(ft_library);
425
426 exit(1);
427 }
428 }
429
430
431 int main(int argc, char** argv) {
432 strcpy(algos[0].name, "Bubble sort");
433 algos[0].function = &bubble_sort;
434
435 strcpy(algos[1].name, "Selection sort");
436 algos[1].function = &selection_sort;
437
438 create_array();
439
440 glutInit(&argc, argv);
441 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
442 glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
443 glutCreateWindow("Algorithm animator");
444
445 setup_gl();
446 setup_freetype();
447
448 glutDisplayFunc(display);
449 glutKeyboardFunc(keyboard);
450 glutIdleFunc(idle);
451 glutMainLoop();
452
453 free(arr);
454
455 FT_Done_Face(ft_face);
456 FT_Done_FreeType(ft_library);
457
458 return 0;
459 }