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