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->new_vertices.length / 2;
20
21 float_array_extend(&window->new_vertices, num_vertices * 2, vertices);
22 float_array_extend(&window->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 uint32_array_extend(&window->new_indices, num_indices, indices);
33
34 window->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 FloatArray* array, const uint32_t num_sides,
117 const float x, const float y, const float r)
118{
119 float_array_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 float_array_append(array, x + r * sin(step_angle * i));
125 float_array_append(array, y - r * cos(step_angle * i));
126 }
127}
128
129void generate_circle(FloatArray* array, const float x, const float y, const float r)
130{
131 const uint32_t num_sides = round(PI / acos(1 - MAX_ERROR / r));
132 generate_regular_polygon(array, num_sides, x, y, r);
133}
134
135void generate_rounded_rect_aa(
136 FloatArray* array,
137 const float x0, const float y0, const float x1, const float y1,
138 float r
139)
140{
141 // Always a multiple of 4
142 const uint32_t num_sides = round((PI / acos(1 - MAX_ERROR / r)) / 4) * 4;
143 const uint32_t num_sides_per_corner = num_sides / 4;
144
145 const float step_angle = 2 * PI / num_sides;
146
147 if (x1 - x0 < r) r = (x1 - x0) / 2;
148 if (y1 - y0 < r) r = (y1 - y0) / 2;
149
150 float_array_reserve(array, array->length + 4 + num_sides);
151
152 uint32_t i = 0;
153 for (; i < num_sides_per_corner + 1; i++)
154 {
155 float_array_append(array, x0 + r - r * sin(step_angle * i));
156 float_array_append(array, y0 + r - r * cos(step_angle * i));
157 }
158 i -= 1;
159
160 for (; i < 2 * num_sides_per_corner + 1; i++)
161 {
162 float_array_append(array, x0 + r - r * sin(step_angle * i));
163 float_array_append(array, y1 - r - r * cos(step_angle * i));
164 }
165 i -= 1;
166
167 for (; i < 3 * num_sides_per_corner + 1; i++)
168 {
169 float_array_append(array, x1 - r - r * sin(step_angle * i));
170 float_array_append(array, y1 - r - r * cos(step_angle * i));
171 }
172 i -= 1;
173
174 for (; i < 4 * num_sides_per_corner + 1; i++)
175 {
176 float_array_append(array, x1 - r - r * sin(step_angle * i));
177 float_array_append(array, y0 + r - r * cos(step_angle * i));
178 }
179}
180
181void draw_concave_polygon(AlbaWindow* window, const float x, const float y, FloatArray* contour, AlbaColor color)
182{
183 const uint32_t num_sides = contour->length / 2;
184 const uint32_t num_vertices = num_sides + 1;
185
186 float_array_append(contour, x);
187 float_array_append(contour, y);
188
189 FloatArray colors = {0};
190 float_array_reserve(&colors, num_vertices * 4);
191 for (uint32_t i = 0; i < num_vertices; i++)
192 {
193 float_array_extend(&colors, 4, (float*)&color);
194 }
195
196 uint32_t* indices = malloc(num_sides * 3 * sizeof(uint32_t));
197 for (uint32_t i = 0; i < num_sides; i++)
198 {
199 indices[i * 3] = num_vertices - 1;
200 indices[i * 3 + 1] = i;
201 indices[i * 3 + 2] = i + 1;
202 }
203 indices[num_sides * 3 - 1] = 0;
204
205 draw_triangles_indexed(window, num_sides + 1, contour->data, colors.data, num_sides * 3, indices);
206
207 free(indices);
208}
209
210void draw_regular_polygon(
211 AlbaWindow* window, const uint32_t num_sides,
212 const float x, const float y, const float r,
213 const AlbaColor color
214)
215{
216 if (num_sides <= 2)
217 {
218 printf("error: invalid number of sides in call to draw_regular_polygon: %d. "
219 "A polygon must have at least 3 sides\n", num_sides);
220 exit(1);
221 }
222
223 FloatArray polyon = {0};
224 generate_regular_polygon(&polyon, num_sides, x, y, r);
225 draw_concave_polygon(window, x, y, &polyon, color);
226 float_array_release(&polyon);
227}
228
229void draw_circle(AlbaWindow* window, const float x, const float y, const float r, const AlbaColor color)
230{
231 FloatArray circle = {0};
232 generate_circle(&circle, x, y, r);
233 draw_concave_polygon(window, x, y, &circle, color);
234 float_array_release(&circle);
235}
236
237
238void draw_rounded_rect_aa(
239 AlbaWindow* window,
240 const float x0, const float y0, const float x1, const float y1,
241 const float r, const AlbaColor color)
242{
243 if (r == 0)
244 {
245 draw_rect_aa(window, x0, y0, x1, y1, color);
246 return;
247 }
248
249 FloatArray contour = {0};
250 generate_rounded_rect_aa(&contour, x0, y0, x1, y1, r);
251 draw_concave_polygon(window, (x0 + x1) / 2, (y0 + y1) / 2, &contour, color);
252}