src/drawing.c
1#include "alba.h"
2#include "internal.h"
3
4#include <math.h>
5#include <stdio.h>
6#include <stdlib.h>
7
8#define PI 3.14159265358979323846
9#define MAX_ERROR 0.25
10
11void draw_triangles_indexed_render_pass(
12 RenderPassData* data,
13 const uint32_t num_vertices,
14 const AlbaVector* vertices,
15 const AlbaAttribute* attributes,
16 const uint32_t num_indices,
17 uint32_t* indices
18)
19{
20 const uint32_t offset = data->new_vertices.length;
21
22 dynarray_extend(&data->new_vertices, num_vertices, vertices);
23 dynarray_extend(&data->new_attributes, num_vertices, attributes);
24
25 if (offset > 0)
26 {
27 // Fix indices
28 for (uint32_t i = 0; i < num_indices; i++)
29 {
30 indices[i] += offset;
31 }
32 }
33 dynarray_extend(&data->new_indices, num_indices, indices);
34
35 data->dirty = 1;
36}
37
38void draw_triangles_indexed(
39 AlbaWindow* window,
40 const uint32_t num_vertices,
41 const AlbaVector* vertices,
42 const AlbaAttribute* attributes,
43 const uint32_t num_indices,
44 uint32_t* indices
45)
46{
47 draw_triangles_indexed_render_pass(&window->drawing, num_vertices, vertices, attributes, num_indices, indices);
48}
49
50void draw_triangles(
51 AlbaWindow* window,
52 const uint32_t num_vertices,
53 const AlbaVector* vertices,
54 const AlbaAttribute* attributes
55)
56{
57 uint32_t* indices = malloc(num_vertices * sizeof(uint32_t));
58 if (indices == NULL)
59 {
60 printf(
61 "error: unable to allocate %lu bytes for triangle indices\n",
62 num_vertices * sizeof(uint32_t)
63 );
64 return;
65 }
66
67 for (uint32_t i = 0; i < num_vertices; i++)
68 {
69 indices[i] = i;
70 }
71
72 draw_triangles_indexed(window, num_vertices, vertices, attributes, num_vertices, indices);
73
74 free(indices);
75}
76
77void draw_triangle(AlbaWindow* window, const AlbaVector vertices[3], const AlbaColor color)
78{
79 const AlbaAttribute attributes[] = {
80 color, NO_TEXTURE,
81 color, NO_TEXTURE,
82 color, NO_TEXTURE,
83 };
84 // Does not try to consider winding order
85 uint32_t indices[] = {0, 1, 2};
86 draw_triangles_indexed(window, 3, vertices, attributes, 3, indices);
87}
88
89void draw_quad(AlbaWindow* window, const AlbaVector vertices[4], const AlbaColor color)
90{
91 const AlbaAttribute attributes[] = {
92 color, NO_TEXTURE,
93 color, NO_TEXTURE,
94 color, NO_TEXTURE,
95 color, NO_TEXTURE,
96 };
97 // Does not try to consider winding order
98 uint32_t indices[] = {0, 1, 2, 0, 2, 3};
99 draw_triangles_indexed(window, 4, vertices, attributes, 6, indices);
100}
101
102void draw_rect_aa(
103 AlbaWindow* window,
104 const float x0, const float y0, const float x1, const float y1,
105 const AlbaColor color
106)
107{
108 if (x0 > x1)
109 {
110 fprintf(stderr, "error: invalid rect coordinates, x0 > x1: %f > %f\n", x0, x1);
111 exit(1);
112 }
113 if (y0 > y1)
114 {
115 fprintf(stderr, "error: invalid rect coordinates, y0 > y1: %f > %f\n", y0, y1);
116 exit(1);
117 }
118
119 const AlbaVector vertices[] = {
120 x0, y0,
121 x0, y1,
122 x1, y1,
123 x1, y0,
124 };
125 draw_quad(window, vertices, color);
126}
127
128void generate_regular_polygon(
129 DynArray* array, const uint32_t num_sides,
130 const float x, const float y, const float r)
131{
132 dynarray_reserve(array, array->length + num_sides);
133
134 const float step_angle = 2 * PI / num_sides;
135 for (uint32_t i = 0; i < num_sides; i++)
136 {
137 dynarray_append(
138 array,
139 &(AlbaVector){
140 x + r * sin(step_angle * i),
141 y - r * cos(step_angle * i)
142 }
143 );
144 }
145}
146
147void generate_circle(DynArray* array, const float x, const float y, const float r)
148{
149 const uint32_t num_sides = round(PI / acos(1 - MAX_ERROR / r));
150 generate_regular_polygon(array, num_sides, x, y, r);
151}
152
153void generate_rounded_rect_aa(
154 DynArray* array,
155 const float x0, const float y0, const float x1, const float y1,
156 float r
157)
158{
159 // Always a multiple of 4
160 const uint32_t num_sides = round((PI / acos(1 - MAX_ERROR / r)) / 4) * 4;
161 const uint32_t num_sides_per_corner = num_sides / 4;
162
163 const float step_angle = 2 * PI / num_sides;
164
165 if (x1 - x0 < r) r = (x1 - x0) / 2;
166 if (y1 - y0 < r) r = (y1 - y0) / 2;
167
168 dynarray_reserve(array, array->length + num_sides);
169
170 uint32_t i = 0;
171 for (; i < num_sides_per_corner + 1; i++)
172 {
173 dynarray_append(
174 array, &(AlbaVector){
175 x0 + r - r * sin(step_angle * i),
176 y0 + r - r * cos(step_angle * i)
177 });
178 }
179 i -= 1;
180
181 for (; i < 2 * num_sides_per_corner + 1; i++)
182 {
183 dynarray_append(
184 array, &(AlbaVector){
185 x0 + r - r * sin(step_angle * i),
186 y1 - r - r * cos(step_angle * i)
187 });
188 }
189 i -= 1;
190
191 for (; i < 3 * num_sides_per_corner + 1; i++)
192 {
193 dynarray_append(
194 array, &(AlbaVector){
195 x1 - r - r * sin(step_angle * i),
196 y1 - r - r * cos(step_angle * i)
197 });
198 }
199 i -= 1;
200
201 for (; i < 4 * num_sides_per_corner + 1; i++)
202 {
203 dynarray_append(
204 array, &(AlbaVector){
205 x1 - r - r * sin(step_angle * i),
206 y0 + r - r * cos(step_angle * i)
207 });
208 }
209}
210
211void draw_concave_polygon(AlbaWindow* window, const float x, const float y, const DynArray* contour,
212 const AlbaColor color)
213{
214 const uint32_t num_sides = contour->length;
215 const uint32_t num_triangles = num_sides - 2;
216
217 DynArray attributes = dynarray_new(sizeof(AlbaAttribute), num_sides);
218 for (uint32_t i = 0; i < num_sides; i++)
219 {
220 dynarray_append(&attributes, &(AlbaAttribute){color, NO_TEXTURE});
221 }
222
223 uint32_t* indices = malloc(num_triangles * 3 * sizeof(uint32_t));
224 for (uint32_t i = 0; i < num_triangles; i++)
225 {
226 indices[i * 3] = 0;
227 indices[i * 3 + 1] = i + 1;
228 indices[i * 3 + 2] = i + 2;
229 }
230
231 draw_triangles_indexed(
232 window,
233 num_sides, contour->data, attributes.data,
234 num_triangles * 3, indices
235 );
236
237 free(indices);
238}
239
240void draw_regular_polygon(
241 AlbaWindow* window, const uint32_t num_sides,
242 const float x, const float y, const float r,
243 const AlbaColor color
244)
245{
246 if (num_sides <= 2)
247 {
248 fprintf(stderr, "error: invalid number of sides in call to draw_regular_polygon: %d. "
249 "A polygon must have at least 3 sides\n", num_sides);
250 exit(1);
251 }
252
253 DynArray polygon = dynarray_new(sizeof(AlbaVector), 0);
254 generate_regular_polygon(&polygon, num_sides, x, y, r);
255 draw_concave_polygon(window, x, y, &polygon, color);
256 dynarray_release(&polygon);
257}
258
259void draw_circle(AlbaWindow* window, const float x, const float y, const float r, const AlbaColor color)
260{
261 DynArray circle = dynarray_new(sizeof(AlbaVector), 0);
262 generate_circle(&circle, x, y, r);
263 draw_concave_polygon(window, x, y, &circle, color);
264 dynarray_release(&circle);
265}
266
267
268void draw_rounded_rect_aa(
269 AlbaWindow* window,
270 const float x0, const float y0, const float x1, const float y1,
271 const float r, const AlbaColor color)
272{
273 if (r == 0)
274 {
275 draw_rect_aa(window, x0, y0, x1, y1, color);
276 return;
277 }
278
279 DynArray contour = dynarray_new(sizeof(AlbaVector), 0);
280 generate_rounded_rect_aa(&contour, x0, y0, x1, y1, r);
281 draw_concave_polygon(window, (x0 + x1) / 2, (y0 + y1) / 2, &contour, color);
282}