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_draw_call(
 12    DrawCall* draw_call,
 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 = draw_call->new_vertices.length;
 21
 22    alba_array_extend(&draw_call->new_vertices, num_vertices, vertices);
 23    alba_array_extend(&draw_call->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    alba_array_extend(&draw_call->new_indices, num_indices, indices);
 34
 35    draw_call->buffers_dirty = 1;
 36}
 37
 38void alba_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_draw_call(
 48        alba_array_last(&window->draw_calls),
 49        num_vertices, vertices, attributes,
 50        num_indices, indices
 51    );
 52}
 53
 54void alba_draw_triangles(
 55    AlbaWindow* window,
 56    const uint32_t num_vertices,
 57    const AlbaVector* vertices,
 58    const AlbaAttribute* attributes
 59)
 60{
 61    uint32_t* indices = malloc(num_vertices * sizeof(uint32_t));
 62    if (indices == NULL)
 63    {
 64        printf(
 65            "error: unable to allocate %lu bytes for triangle indices\n",
 66            num_vertices * sizeof(uint32_t)
 67        );
 68        return;
 69    }
 70
 71    for (uint32_t i = 0; i < num_vertices; i++)
 72    {
 73        indices[i] = i;
 74    }
 75
 76    alba_draw_triangles_indexed(window, num_vertices, vertices, attributes, num_vertices, indices);
 77
 78    free(indices);
 79}
 80
 81void alba_draw_triangle(AlbaWindow* window, const AlbaVector vertices[3], const AlbaColor color)
 82{
 83    const AlbaAttribute attributes[] = {
 84        color, NO_TEXTURE,
 85        color, NO_TEXTURE,
 86        color, NO_TEXTURE,
 87    };
 88    // Does not try to consider winding order
 89    uint32_t indices[] = {0, 1, 2};
 90    alba_draw_triangles_indexed(window, 3, vertices, attributes, 3, indices);
 91}
 92
 93void alba_draw_quad(AlbaWindow* window, const AlbaVector vertices[4], const AlbaColor color)
 94{
 95    const AlbaAttribute attributes[] = {
 96        color, NO_TEXTURE,
 97        color, NO_TEXTURE,
 98        color, NO_TEXTURE,
 99        color, NO_TEXTURE,
100    };
101    // Does not try to consider winding order
102    uint32_t indices[] = {0, 1, 2, 0, 2, 3};
103    alba_draw_triangles_indexed(window, 4, vertices, attributes, 6, indices);
104}
105
106void alba_draw_rect_aa(
107    AlbaWindow* window,
108    const float x0, const float y0, const float x1, const float y1,
109    const AlbaColor color
110)
111{
112    if (x0 > x1)
113    {
114        fprintf(stderr, "error: invalid rect coordinates, x0 > x1: %f > %f\n", x0, x1);
115        exit(1);
116    }
117    if (y0 > y1)
118    {
119        fprintf(stderr, "error: invalid rect coordinates, y0 > y1: %f > %f\n", y0, y1);
120        exit(1);
121    }
122
123    const AlbaVector vertices[] = {
124        x0, y0,
125        x0, y1,
126        x1, y1,
127        x1, y0,
128    };
129    alba_draw_quad(window, vertices, color);
130}
131
132void alba_generate_regular_polygon(
133    AlbaArray* array, const uint32_t num_sides,
134    const float x, const float y, const float r)
135{
136    alba_array_reserve(array, array->length + num_sides);
137
138    const float step_angle = 2 * PI / num_sides;
139    for (uint32_t i = 0; i < num_sides; i++)
140    {
141        alba_array_append(
142            array,
143            &(AlbaVector){
144                x + r * sin(step_angle * i),
145                y - r * cos(step_angle * i)
146            }
147        );
148    }
149}
150
151void alba_generate_circle(AlbaArray* array, const float x, const float y, const float r)
152{
153    const uint32_t num_sides = round(PI / acos(1 - MAX_ERROR / r));
154    alba_generate_regular_polygon(array, num_sides, x, y, r);
155}
156
157void alba_generate_rounded_rect_aa(
158    AlbaArray* array,
159    const float x0, const float y0, const float x1, const float y1,
160    float r
161)
162{
163    // Always a multiple of 4
164    const uint32_t num_sides = round((PI / acos(1 - MAX_ERROR / r)) / 4) * 4;
165    const uint32_t num_sides_per_corner = num_sides / 4;
166
167    const float step_angle = 2 * PI / num_sides;
168
169    if (x1 - x0 < r) r = (x1 - x0) / 2;
170    if (y1 - y0 < r) r = (y1 - y0) / 2;
171
172    alba_array_reserve(array, array->length + num_sides);
173
174    uint32_t i = 0;
175    for (; i < num_sides_per_corner + 1; i++)
176    {
177        alba_array_append(
178            array, &(AlbaVector){
179                x0 + r - r * sin(step_angle * i),
180                y0 + r - r * cos(step_angle * i)
181            });
182    }
183    i -= 1;
184
185    for (; i < 2 * num_sides_per_corner + 1; i++)
186    {
187        alba_array_append(
188            array, &(AlbaVector){
189                x0 + r - r * sin(step_angle * i),
190                y1 - r - r * cos(step_angle * i)
191            });
192    }
193    i -= 1;
194
195    for (; i < 3 * num_sides_per_corner + 1; i++)
196    {
197        alba_array_append(
198            array, &(AlbaVector){
199                x1 - r - r * sin(step_angle * i),
200                y1 - r - r * cos(step_angle * i)
201            });
202    }
203    i -= 1;
204
205    for (; i < 4 * num_sides_per_corner + 1; i++)
206    {
207        alba_array_append(
208            array, &(AlbaVector){
209                x1 - r - r * sin(step_angle * i),
210                y0 + r - r * cos(step_angle * i)
211            });
212    }
213}
214
215void alba_draw_concave_polygon(AlbaWindow* window, const float x, const float y, const AlbaArray* contour,
216                               const AlbaColor color)
217{
218    const uint32_t num_sides = contour->length;
219    const uint32_t num_triangles = num_sides - 2;
220
221    AlbaArray attributes = alba_create_array(sizeof(AlbaAttribute), num_sides);
222    for (uint32_t i = 0; i < num_sides; i++)
223    {
224        alba_array_append(&attributes, &(AlbaAttribute){color, NO_TEXTURE});
225    }
226
227    uint32_t* indices = malloc(num_triangles * 3 * sizeof(uint32_t));
228    for (uint32_t i = 0; i < num_triangles; i++)
229    {
230        indices[i * 3] = 0;
231        indices[i * 3 + 1] = i + 1;
232        indices[i * 3 + 2] = i + 2;
233    }
234
235    alba_draw_triangles_indexed(
236        window,
237        num_sides, contour->data, attributes.data,
238        num_triangles * 3, indices
239    );
240
241    free(indices);
242}
243
244void alba_draw_regular_polygon(
245    AlbaWindow* window, const uint32_t num_sides,
246    const float x, const float y, const float r,
247    const AlbaColor color
248)
249{
250    if (num_sides <= 2)
251    {
252        fprintf(stderr, "error: invalid number of sides in call to draw_regular_polygon: %d. "
253                "A polygon must have at least 3 sides\n", num_sides);
254        exit(1);
255    }
256
257    AlbaArray polygon = alba_create_array(sizeof(AlbaVector), 0);
258    alba_generate_regular_polygon(&polygon, num_sides, x, y, r);
259    alba_draw_concave_polygon(window, x, y, &polygon, color);
260    alba_array_release(&polygon);
261}
262
263void alba_draw_circle(AlbaWindow* window, const float x, const float y, const float r, const AlbaColor color)
264{
265    AlbaArray circle = alba_create_array(sizeof(AlbaVector), 0);
266    alba_generate_circle(&circle, x, y, r);
267    alba_draw_concave_polygon(window, x, y, &circle, color);
268    alba_array_release(&circle);
269}
270
271
272void alba_draw_rounded_rect_aa(
273    AlbaWindow* window,
274    const float x0, const float y0, const float x1, const float y1,
275    const float r, const AlbaColor color
276)
277{
278    if (r == 0)
279    {
280        alba_draw_rect_aa(window, x0, y0, x1, y1, color);
281        return;
282    }
283
284    AlbaArray contour = alba_create_array(sizeof(AlbaVector), 0);
285    alba_generate_rounded_rect_aa(&contour, x0, y0, x1, y1, r);
286    alba_draw_concave_polygon(window, (x0 + x1) / 2, (y0 + y1) / 2, &contour, color);
287}