summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClément Sibille <clements@lisible.xyz>2024-12-14 10:00:33 +0100
committerClement Sibille <clements@lisible.xyz>2024-12-15 15:53:39 +0100
commit0c3dce967f58bc54d83513b6cfdbf1acb55d510d (patch)
tree47b7187efbf043243afbee1c30707e9dbb61d8ff
Initial commitHEADmain
-rw-r--r--.gitignore2
-rw-r--r--README.md18
-rw-r--r--meson.build14
-rw-r--r--src/main.c128
4 files changed, 162 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bc95448
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.cache/
+build/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b197c5f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# Bezier curve drawing utility
+
+Simple tool that allows drawing a cubic bezier curve by placing its control
+points.
+
+## Building
+
+This utility depends solely on SDL3. As of now (2024-12-14) there is no official
+release of SDL3, so you might need to build it yourself.
+
+To build the program using meson, run the following commands.
+
+```sh
+meson setup build # Generate the build directory
+meson compile -C build # Compile the program
+```
+
+The resulting executagble will be stored in the build/ directory.
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..f477d8d
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,14 @@
+project('bezier', 'c', default_options: ['c_std=c17', 'warning_level=3'])
+
+cc = meson.get_compiler('c')
+
+sdl_dep = dependency('SDL3')
+m_lib = cc.find_library('m')
+m_dep = declare_dependency(dependencies: m_lib)
+
+
+executable(
+ 'bezier',
+ ['src/main.c'],
+ dependencies: [sdl_dep, m_dep]
+)
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..9014f22
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,128 @@
+#include <SDL3/SDL.h>
+#include <math.h>
+#include <stdio.h>
+
+#define LOG(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+
+#define QUADRATIC_BEZIER_CURVE_CONTROL_POINT_COUNT 4
+#define CONTROL_POINT_RECTANGLE_SIZE 20.f
+
+struct vec2 {
+ float x;
+ float y;
+};
+struct vec2 vec2_add(struct vec2 lhs, struct vec2 rhs) {
+ return (struct vec2){.x = lhs.x + rhs.x, .y = lhs.y + rhs.y};
+}
+
+struct vec2 vec2_mul_scalar(struct vec2 lhs, float rhs) {
+ return (struct vec2){.x = lhs.x * rhs, .y = lhs.y * rhs};
+}
+
+struct vec2 bezier_quadratic(
+ float t, const struct vec2
+ control_points[QUADRATIC_BEZIER_CURVE_CONTROL_POINT_COUNT]) {
+
+ struct vec2 a = vec2_mul_scalar(control_points[0], powf(1.f - t, 3.f));
+ struct vec2 b =
+ vec2_mul_scalar(control_points[1], 3 * powf(1.f - t, 2.f) * t);
+ struct vec2 c = vec2_mul_scalar(control_points[2], 3 * (1.f - t) * t * t);
+ struct vec2 d = vec2_mul_scalar(control_points[3], t * t * t);
+ return vec2_add(a, vec2_add(b, vec2_add(c, d)));
+}
+
+struct state {
+ struct vec2 control_points[QUADRATIC_BEZIER_CURVE_CONTROL_POINT_COUNT];
+ int set_control_point_count;
+};
+void state_render(const struct state *state, SDL_Renderer *renderer) {
+ for (int set_control_point_index = 0;
+ set_control_point_index < state->set_control_point_count;
+ set_control_point_index++) {
+ const struct vec2 *set_control_point =
+ &state->control_points[set_control_point_index];
+ SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
+ SDL_RenderFillRect(
+ renderer,
+ &(const SDL_FRect){
+ set_control_point->x - CONTROL_POINT_RECTANGLE_SIZE / 2.f,
+ set_control_point->y - CONTROL_POINT_RECTANGLE_SIZE / 2.f,
+ CONTROL_POINT_RECTANGLE_SIZE, CONTROL_POINT_RECTANGLE_SIZE});
+ }
+
+ if (state->set_control_point_count ==
+ QUADRATIC_BEZIER_CURVE_CONTROL_POINT_COUNT) {
+ float prev_t = 0.f;
+ for (int ti = 0; ti < 100; ti += 1) {
+ float t = ti / 100.f;
+ struct vec2 first_point = bezier_quadratic(prev_t, state->control_points);
+ struct vec2 second_point = bezier_quadratic(t, state->control_points);
+ SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
+ SDL_RenderLine(renderer, first_point.x, first_point.y, second_point.x,
+ second_point.y);
+ prev_t = t;
+ }
+ }
+}
+void state_on_click(struct state *state, struct vec2 position) {
+ if (state->set_control_point_count <
+ QUADRATIC_BEZIER_CURVE_CONTROL_POINT_COUNT) {
+ state->control_points[state->set_control_point_count++] = position;
+ }
+}
+
+int main(void) {
+ bool sdl_initialized = SDL_Init(SDL_INIT_VIDEO);
+ SDL_Window *window = NULL;
+ if (sdl_initialized) {
+ window = SDL_CreateWindow("bezier", 1280, 720, 0);
+ }
+
+ SDL_Renderer *renderer = NULL;
+ if (window) {
+ renderer = SDL_CreateRenderer(window, NULL);
+ }
+
+ struct state state = {0};
+
+ if (renderer) {
+ bool running = true;
+ while (running) {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ bool quit = event.type == SDL_EVENT_QUIT;
+ bool escape_pressed =
+ event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE;
+
+ if (quit || escape_pressed) {
+ running = false;
+ } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN &&
+ event.button.button == SDL_BUTTON_LEFT) {
+ state_on_click(
+ &state, (struct vec2){.x = event.button.x, .y = event.button.y});
+ }
+ }
+
+ SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
+ SDL_RenderClear(renderer);
+ state_render(&state, renderer);
+ SDL_RenderPresent(renderer);
+ }
+ }
+
+ if (renderer) {
+ SDL_DestroyRenderer(renderer);
+ }
+ if (window) {
+ SDL_DestroyWindow(window);
+ }
+ if (sdl_initialized) {
+ SDL_Quit();
+ }
+
+ return 0;
+}
Go back to lisible.xyz