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}