diff options
| author | Clément Sibille <clements@lisible.xyz> | 2024-12-14 10:00:33 +0100 | 
|---|---|---|
| committer | Clement Sibille <clements@lisible.xyz> | 2024-12-15 15:53:39 +0100 | 
| commit | 0c3dce967f58bc54d83513b6cfdbf1acb55d510d (patch) | |
| tree | 47b7187efbf043243afbee1c30707e9dbb61d8ff | |
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | meson.build | 14 | ||||
| -rw-r--r-- | src/main.c | 128 | 
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; +}  | 
