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}