Compare commits
10 Commits
883773d07c
...
12cad4c711
Author | SHA1 | Date | |
---|---|---|---|
12cad4c711 | |||
f3c334741b | |||
292df44840 | |||
242151cbb4 | |||
fb707e1ce8 | |||
6ce1707852 | |||
536455416f | |||
99c6dbeecf | |||
41f12d2a98 | |||
8c27f2b770 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.cache
|
||||||
|
compile_commands.json
|
||||||
|
main
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal 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
7
README.md
Normal 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.
|
||||||
|
|
||||||
|

|
9
compile
Executable file
9
compile
Executable 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
156
drawing.c
Normal 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
55
drawing.h
Normal 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
1
intern/aliases
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit f95f3aa499910286876c1f2cdceea8146ebcf7b1
|
80
main.c
80
main.c
@ -1,11 +1,27 @@
|
|||||||
|
#include "SDL_mouse.h"
|
||||||
|
#include "aliases/aliases.h"
|
||||||
|
#include "drawing.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_events.h>
|
#include <SDL2/SDL_events.h>
|
||||||
#include <SDL2/SDL_render.h>
|
#include <SDL2/SDL_render.h>
|
||||||
#include <SDL2/SDL_video.h>
|
#include <SDL2/SDL_video.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define WINDOW_WIDTH 800
|
#define WINDOW_WIDTH 1280
|
||||||
#define WINDOW_HEIGHT 600
|
#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) {
|
int main(void) {
|
||||||
SDL_Init(SDL_INIT_EVERYTHING);
|
SDL_Init(SDL_INIT_EVERYTHING);
|
||||||
@ -17,15 +33,55 @@ int main(void) {
|
|||||||
SDL_Renderer *renderer = SDL_CreateRenderer(
|
SDL_Renderer *renderer = SDL_CreateRenderer(
|
||||||
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
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)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_QUIT:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,6 +90,16 @@ int main(void) {
|
|||||||
|
|
||||||
SDL_RenderClear(renderer);
|
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_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,5 +109,5 @@ int main(void) {
|
|||||||
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
BIN
result.png
Normal file
BIN
result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Loading…
x
Reference in New Issue
Block a user