Compare commits

...

13 Commits

Author SHA1 Message Date
15c3b026a9 Correct include 2024-01-14 16:12:16 +00:00
13aadb4281 Add utility functions and fix bug when reallocating lines array 2024-01-14 02:44:35 +00:00
e4d3024364 Reorganise files 2024-01-14 02:17:11 +00:00
12cad4c711 Include screenshot as example 2024-01-14 02:12:32 +00:00
f3c334741b Update entry point 2024-01-14 02:10:32 +00:00
292df44840 Add drawing utilities 2024-01-14 02:10:21 +00:00
242151cbb4 Add .gitignore 2024-01-14 02:10:05 +00:00
fb707e1ce8 Add build scripts 2024-01-14 02:09:56 +00:00
6ce1707852 Add aliases submodule 2024-01-14 02:09:22 +00:00
536455416f Update README.md 2024-01-14 02:09:06 +00:00
99c6dbeecf Fix minor bug 2024-01-07 22:09:54 +00:00
41f12d2a98 Modified README 2023-01-28 22:44:58 +00:00
8c27f2b770 Added README 2023-01-28 22:44:07 +00:00
11 changed files with 378 additions and 47 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.cache
compile_commands.json
main

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "intern/aliases"]
path = intern/aliases
url = https://git.thewizardapprentice.com/abdelrahman/c-cpp-aliases.git

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# Arrow drawing test
Test drawing arrows in SDL.
To draw an arrow, click the mouse to start drawing, move your mouse to the desired location, then click again to finalise the drawing.
![Example result](./result.png)

3
build Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
bear -- ./compile $@

9
compile Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
CC=clang
CFLAGS="-g -Wall -Iinclude -Iintern $(pkg-config --cflags sdl2)"
LIBS="$(pkg-config --libs sdl2) -lm"
SRC=src/*.c
OUT=main
(set -x ; $CC $CFLAGS $LIBS $SRC -o $OUT)

55
include/drawing.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef DRAWING_H
#define DRAWING_H
#include "aliases/aliases.h"
#include <SDL_pixels.h>
#include <SDL_render.h>
#include <stdbool.h>
#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

1
intern/aliases Submodule

Submodule intern/aliases added at f95f3aa499

47
main.c
View File

@@ -1,47 +0,0 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
#include <stdbool.h>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
int main(void) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window =
SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool running = true;
SDL_Event event = {};
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

BIN
result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

184
src/drawing.c Normal file
View File

@@ -0,0 +1,184 @@
#include "drawing.h"
#include "aliases/aliases.h"
#include <SDL_render.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#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);
}

113
src/main.c Normal file
View File

@@ -0,0 +1,113 @@
#include "aliases/aliases.h"
#include "drawing.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_mouse.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
#include <stdbool.h>
#include <stdlib.h>
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
typedef struct state state_t;
struct state {
i32 mouse_x;
i32 mouse_y;
i32 current_origin_x;
i32 current_origin_y;
bool running;
bool drawing;
arrows_t arrows;
arrow_t tmp_arrow;
};
int main(void) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window =
SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
state_t st = {
.mouse_x = -1,
.mouse_y = -1,
.current_origin_x = -1,
.current_origin_y = -1,
.running = true,
.drawing = false,
.arrows = {0},
.tmp_arrow = {0},
};
SDL_Event event = {0};
if (!init_arrows_default(&(st.arrows))) {
return EXIT_FAILURE;
}
while (st.running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
st.running = false;
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT) {
if (st.drawing) {
add_arrow(&(st.arrows), st.tmp_arrow);
st.tmp_arrow = (arrow_t){0};
st.drawing = false;
} else {
st.current_origin_x = event.button.x;
st.current_origin_y = event.button.y;
st.drawing = true;
}
} else if (event.button.button == SDL_BUTTON_RIGHT) {
st.current_origin_x = -1;
st.current_origin_y = -1;
st.drawing = false;
}
break;
case SDL_MOUSEMOTION:
st.mouse_x = event.motion.x;
st.mouse_y = event.motion.y;
break;
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
if (st.drawing) {
st.tmp_arrow.origin = (point_t){st.current_origin_x, st.current_origin_y};
st.tmp_arrow.direction = (vec2_t){st.mouse_x - st.current_origin_x,
st.mouse_y - st.current_origin_y};
draw_arrow(renderer, &(st.tmp_arrow), (colour_t){.abgr = 0xff0000ff});
}
draw_arrows(renderer, &(st.arrows), (colour_t){.abgr = 0xff000000});
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}