Last active
January 22, 2026 09:07
-
-
Save s1as3r/f666798b187cc1c9643c609b3e3cdaa1 to your computer and use it in GitHub Desktop.
cubic bezier curves thingy in raylib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // clang-format off | |
| // cubic bezier curves thingy in raylib | |
| // gcc -Og -std=c11 -o bezier ./bezier.c -lraylib -lm -lpthread -ldl -lX11 -lGL -lrt | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <raylib.h> | |
| // clang-format on | |
| #define global static | |
| #define array_count(arr) (sizeof(arr) / sizeof(arr[0])) | |
| typedef int8_t i8; | |
| typedef int16_t i16; | |
| typedef int32_t i32; | |
| typedef int64_t i64; | |
| typedef uint8_t u8; | |
| typedef uint16_t u16; | |
| typedef uint32_t u32; | |
| typedef uint64_t u64; | |
| typedef float f32; | |
| typedef double f64; | |
| inline f32 lerp(f32 a, f32 b, f32 t) { return a + t * (b - a); } | |
| inline Vector2 lerpv(Vector2 a, Vector2 b, f32 t) { | |
| return (Vector2){lerp(a.x, b.x, t), lerp(a.y, b.y, t)}; | |
| } | |
| bool point_in_circle(Vector2 point, Vector2 circle_center, f32 radius) { | |
| f32 dx = (point.x - circle_center.x); | |
| f32 dy = (point.y - circle_center.y); | |
| return (dx * dx + dy * dy) <= (radius * radius); | |
| } | |
| void draw_bezier(Vector2 points[4], u32 n_segments, f32 thick) { | |
| f32 segment_size = 1.0f / (f32)n_segments; | |
| Vector2 last_point = points[0]; | |
| Vector2 p1, p2, p3, p4, p5, curr_point; | |
| f32 t = 0.0; | |
| for (u32 _i = 0; _i < n_segments; _i++) { | |
| t += segment_size; | |
| p1 = lerpv(points[0], points[1], t); | |
| p2 = lerpv(points[1], points[2], t); | |
| p3 = lerpv(points[2], points[3], t); | |
| p4 = lerpv(p1, p2, t); | |
| p5 = lerpv(p2, p3, t); | |
| curr_point = lerpv(p4, p5, t); | |
| // curr_point.x = powf(1.0f - t, 3.0f) * points[0].x + | |
| // 3.0f * powf(1.0f - t, 2.0f) * t * points[1].x + | |
| // 3.0f * powf(t, 2.0f) * (1.0f - t) * points[2].x + | |
| // powf(t, 3.0f) * points[3].x; | |
| // | |
| // curr_point.y = powf(1.0f - t, 3.0f) * points[0].y + | |
| // 3.0f * powf(1.0f - t, 2.0f) * t * points[1].y + | |
| // 3.0f * powf(t, 2.0f) * (1.0f - t) * points[2].y + | |
| // powf(t, 3.0f) * points[3].y; | |
| DrawLineEx(last_point, curr_point, thick, WHITE); | |
| last_point = curr_point; | |
| } | |
| } | |
| i32 main(void) { | |
| const i32 window_width = 1200, window_height = 800; | |
| SetConfigFlags(FLAG_MSAA_4X_HINT); | |
| // SetTraceLogLevel(LOG_DEBUG); | |
| InitWindow(window_width, window_height, "bezier"); | |
| SetTargetFPS(GetMonitorRefreshRate(GetCurrentMonitor())); | |
| Vector2 points[] = { | |
| {1000, 250}, // start | |
| {1000, 500}, // c1 | |
| {100, 500}, // c2 | |
| {100, 250}, // end | |
| }; | |
| const i32 n_points = array_count(points); | |
| const f32 point_radius = 10.0f; | |
| f32 curve_thick = 2.0f; | |
| u32 n_segments = 1; | |
| i32 drag_idx = -1; | |
| Vector2 mouse_pos; | |
| bool mouse_out_of_bounds = false; | |
| bool draw_control_lines = true; | |
| while (!WindowShouldClose()) { | |
| mouse_pos = (Vector2){(f32)GetMouseX(), (f32)GetMouseY()}; | |
| mouse_out_of_bounds = (mouse_pos.x < 0 || mouse_pos.x > window_width || | |
| mouse_pos.y < 0 || mouse_pos.y > window_height); | |
| if (IsKeyPressed(KEY_SPACE)) { | |
| draw_control_lines = !draw_control_lines; | |
| } | |
| if (IsKeyPressed(KEY_D) || IsKeyPressedRepeat(KEY_D)) { | |
| n_segments += 1; | |
| } | |
| if ((IsKeyPressed(KEY_A) || IsKeyPressedRepeat(KEY_A)) && n_segments > 0) { | |
| n_segments -= 1; | |
| } | |
| if (IsKeyPressed(KEY_W) || IsKeyPressedRepeat(KEY_W)) { | |
| curve_thick += 1.0f; | |
| } | |
| if ((IsKeyPressed(KEY_S) || IsKeyPressedRepeat(KEY_S)) && curve_thick > 0) { | |
| curve_thick -= 1.0f; | |
| } | |
| if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { | |
| for (i32 i = 0; i < n_points; i++) { | |
| if (point_in_circle(mouse_pos, points[i], point_radius)) { | |
| drag_idx = i; | |
| break; | |
| } | |
| } | |
| } | |
| if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || mouse_out_of_bounds) { | |
| TraceLog(LOG_DEBUG, "idx: %d | (%.0f, %.0f)", drag_idx, mouse_pos.x, | |
| mouse_pos.y); | |
| drag_idx = -1; | |
| } | |
| if (drag_idx != -1) { | |
| points[drag_idx] = mouse_pos; | |
| } | |
| BeginDrawing(); | |
| { | |
| ClearBackground(BLACK); | |
| if (draw_control_lines) { | |
| DrawLineV(points[0], points[1], GRAY); | |
| DrawLineV(points[2], points[3], GRAY); | |
| } | |
| draw_bezier(points, n_segments, curve_thick); | |
| for (u32 i = 0; i < 4; i++) { | |
| DrawCircleV(points[i], point_radius, BLUE); | |
| } | |
| DrawText(TextFormat("(A- | D+) segments: %u", n_segments), 10, 10, 20, | |
| WHITE); | |
| DrawText(TextFormat("(S- | W+) thickness: %.0f", curve_thick), 10, 32, 20, | |
| WHITE); | |
| } | |
| EndDrawing(); | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment