diff --git a/drawing.c b/drawing.c new file mode 100644 index 0000000..367b58e --- /dev/null +++ b/drawing.c @@ -0,0 +1,156 @@ +#include "drawing.h" +#include "aliases/aliases.h" +#include +#include +#include +#include +#include + +#define square(x) (x * x) + +bool init_arrows_arr(arrows_t *lines, u64 capacity) { + u64 size = capacity * sizeof(arrow_t); + + lines->lines = (arrow_t *)malloc(size); + if (!(lines->lines)) { + return false; + } + + memset(lines->lines, 0, size); + + lines->count = 0; + lines->capacity = capacity; + + return true; +} + +void add_arrow(arrows_t *lines, arrow_t ln) { + if (lines->count == lines->capacity) { + arrow_t *ptr = lines->lines; + + u64 new_capacity = + (lines->capacity + DEFAULT_ARROWS_CAPACITY) * sizeof(arrow_t); + + lines->lines = (arrow_t *)realloc(lines->lines, new_capacity); + if (!(lines->lines)) { + lines->lines = ptr; + return; + } + } + + lines->lines[(lines->count)++] = (arrow_t){ + (point_t){ln.origin.x, ln.origin.y}, + (vec2_t){ln.direction.x, ln.direction.y}, + }; +} + +void draw_arrows(SDL_Renderer *renderer, const arrows_t *arrows, + colour_t colour) { + for (u64 i = 0; i < arrows->count; ++i) { + const arrow_t *ln = &(arrows->lines[i]); + + draw_arrow(renderer, ln, colour); + } +} + +INTERNAL f32 vec2_magnitude(const vec2_t *vec) { + return sqrtf((f32)vec->x * vec->x + (f32)vec->y * vec->y); +} + +INTERNAL void draw_arrow_head(SDL_Renderer *renderer, const arrow_t *arrow, + const point_t *arrow_end) { + // m = line_slope + // dx = change_in_x + // dy = change_in_y + // + // m = dy / dx + // + // for two perpendicular lines, their slopes would be m and -1/m + // + // To draw a perpendicular line with fixed length: + // dy / dx = -1 / m + // (rearranged) => dx = -m * dy + // + // From Pythagorean theorem: + // let c = desired_length + // + // c^2 = dx^2 + dy^2 + // (dx substituted) => c^2 = (-m * dy)^2 + dy^2 + // c^2 = m^2 * dy^2 + dy^2 + // c^2 = dy^2 * (m^2 + 1) + // (rearranged) dy^2 = c^2 / (m^2 + 1) + // dy = sqrt(c^2 / (m^2 + 1)) + // + // Based on this answer from StackOverflow: + // https://stackoverflow.com/a/57065334 + + vec2_t unit_vector = {1, 1}; + if (arrow->direction.y != 0) { + unit_vector.y = arrow->direction.y / abs(arrow->direction.y); + } + + point_t arrow_tip = (point_t){-1, -1}; + point_t arrow_base_p0 = (point_t){-1, -1}; + point_t arrow_base_p1 = (point_t){-1, -1}; + f32 half_length = 20.0f; + + if (arrow->direction.x == 0) { + arrow_tip.x = arrow_end->x; + arrow_tip.y = arrow_end->y + half_length * unit_vector.y; + arrow_base_p0.x = arrow_end->x - half_length; + arrow_base_p0.y = arrow_end->y; + arrow_base_p1.x = arrow_end->x + half_length; + arrow_base_p1.y = arrow_end->y; + } else { + unit_vector.x = arrow->direction.x / abs(arrow->direction.x); + + // Calcualte arrow tip + f32 dx0_squared = square((f32)(arrow->direction.x)); + f32 dy0_squared = square((f32)(arrow->direction.y)); + + f32 dy = sqrtf(square(half_length) / (dx0_squared / dy0_squared + 1.0f)) * + unit_vector.y; + f32 dx = (arrow->direction.y != 0) + ? dy * arrow->direction.x / arrow->direction.y + : half_length * unit_vector.x; + + // Draw perpendicular line + f32 slope = (f32)(arrow->direction.y) / arrow->direction.x; + + f32 perpendicular_dy = + sqrtf(square(half_length) / (square((f32)slope) + 1.0f)); + f32 perpendicular_dx = -1.0f * slope * perpendicular_dy; + + arrow_tip.x = arrow_end->x + dx; + arrow_tip.y = arrow_end->y + dy; + arrow_base_p0.x = arrow_end->x - perpendicular_dx; + arrow_base_p0.y = arrow_end->y - perpendicular_dy; + arrow_base_p1.x = arrow_end->x + perpendicular_dx; + arrow_base_p1.y = arrow_end->y + perpendicular_dy; + } + + SDL_RenderDrawLine(renderer, arrow_end->x, arrow_end->y, arrow_base_p0.x, + arrow_base_p0.y); + SDL_RenderDrawLine(renderer, arrow_end->x, arrow_end->y, arrow_base_p1.x, + arrow_base_p1.y); + SDL_RenderDrawLine(renderer, arrow_tip.x, arrow_tip.y, arrow_base_p0.x, + arrow_base_p0.y); + SDL_RenderDrawLine(renderer, arrow_tip.x, arrow_tip.y, arrow_base_p1.x, + arrow_base_p1.y); +} + +void draw_arrow(SDL_Renderer *renderer, const arrow_t *arrow, colour_t colour) { + SDL_SetRenderDrawColor(renderer, colour.colour.r, colour.colour.g, + colour.colour.b, colour.colour.a); + + point_t end = (point_t){ + arrow->origin.x + arrow->direction.x, + arrow->origin.y + arrow->direction.y, + }; + + if (vec2_magnitude(&(arrow->direction)) != 0) { + draw_arrow_head(renderer, arrow, &end); + } + + SDL_RenderDrawLine(renderer, arrow->origin.x, arrow->origin.y, end.x, end.y); +} diff --git a/drawing.h b/drawing.h new file mode 100644 index 0000000..9ca5f1b --- /dev/null +++ b/drawing.h @@ -0,0 +1,55 @@ +#ifndef DRAWING_H +#define DRAWING_H + +#include "aliases/aliases.h" +#include +#include +#include + +#define DEFAULT_ARROWS_CAPACITY 1024 + +#define init_arrows_default(arrows) \ + init_arrows_arr(arrows, DEFAULT_ARROWS_CAPACITY) +#define init_arrows_with_capacity(arrows, capacity) \ + init_arrows_arr(arrows, capacity) + +typedef struct colour colour_t; +struct colour { + union { + u32 abgr; + SDL_Color colour; + }; +}; + +typedef struct point point_t; +struct point { + i32 x; + i32 y; +}; + +typedef struct vec2 vec2_t; +struct vec2 { + i32 x; + i32 y; +}; + +typedef struct arrow arrow_t; +struct arrow { + point_t origin; + vec2_t direction; +}; + +typedef struct arrows arrows_t; +struct arrows { + arrow_t *lines; + u64 count; + u64 capacity; +}; + +bool init_arrows_arr(arrows_t *lines, u64 capacity); +void add_arrow(arrows_t *lines, arrow_t ln); +void draw_arrows(SDL_Renderer *renderer, const arrows_t *arrows, + colour_t colour); +void draw_arrow(SDL_Renderer *renderer, const arrow_t *arrow, colour_t colour); + +#endif // !DRAWING_H