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