#include "drawing.h" #include "aliases/aliases.h" #include #include #include #include #include #define square(x) (x * x) typedef struct line line_t; struct line { point_t p0; point_t p1; }; typedef struct triangle triangle_t; struct triangle { point_t p0; point_t p1; point_t p2; }; INTERNAL void draw_line(SDL_Renderer *renderer, line_t ln); INTERNAL void draw_triangle(SDL_Renderer *renderer, triangle_t triangle); 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->capacity = new_capacity; } 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 triangle_t calculate_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; } return (triangle_t){arrow_tip, arrow_base_p0, arrow_base_p1}; } 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, }; line_t line = (line_t){arrow->origin, end}; if (vec2_magnitude(&(arrow->direction)) != 0) { triangle_t triangle = calculate_arrow_head(renderer, arrow, &end); draw_triangle(renderer, triangle); } draw_line(renderer, line); } INTERNAL void draw_line(SDL_Renderer *renderer, line_t ln) { SDL_RenderDrawLine(renderer, ln.p0.x, ln.p0.y, ln.p1.x, ln.p1.y); } INTERNAL void draw_triangle(SDL_Renderer *renderer, triangle_t triangle) { line_t ln0 = (line_t){triangle.p0, triangle.p1}; line_t ln1 = (line_t){triangle.p0, triangle.p2}; line_t ln2 = (line_t){triangle.p1, triangle.p2}; draw_line(renderer, ln0); draw_line(renderer, ln1); draw_line(renderer, ln2); }