Compare commits

...

10 Commits

Author SHA1 Message Date
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
10 changed files with 310 additions and 7 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 -Iintern $(pkg-config --cflags sdl2)"
LIBS="$(pkg-config --libs sdl2) -lm"
SRC=*.c
OUT=main
(set -x ; $CC $CFLAGS $LIBS $SRC -o $OUT)

156
drawing.c Normal file
View File

@ -0,0 +1,156 @@
#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)
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);
}

55
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

@ -0,0 +1 @@
Subproject commit f95f3aa499910286876c1f2cdceea8146ebcf7b1

80
main.c
View File

@ -1,11 +1,27 @@
#include "SDL_mouse.h"
#include "aliases/aliases.h"
#include "drawing.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
#include <stdbool.h>
#include <stdlib.h>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#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);
@ -17,15 +33,55 @@ int main(void) {
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool running = true;
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 = {};
SDL_Event event = {0};
while (running) {
if (!init_arrows_default(&(st.arrows))) {
return EXIT_FAILURE;
}
while (st.running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
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;
}
}
@ -34,6 +90,16 @@ int main(void) {
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);
}
@ -43,5 +109,5 @@ int main(void) {
SDL_Quit();
return 0;
return EXIT_SUCCESS;
}

BIN
result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB