1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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;
}
|