Skip to content

Instantly share code, notes, and snippets.

@s1as3r
Last active January 22, 2026 09:07
Show Gist options
  • Select an option

  • Save s1as3r/f666798b187cc1c9643c609b3e3cdaa1 to your computer and use it in GitHub Desktop.

Select an option

Save s1as3r/f666798b187cc1c9643c609b3e3cdaa1 to your computer and use it in GitHub Desktop.
cubic bezier curves thingy in raylib
// 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