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
									
								
							 Submodule intern/aliases added at f95f3aa499
									
								
							
							
								
								
									
										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_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
									
								
							
							
						
						
									
										
											BIN
										
									
								
								result.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 18 KiB  | 
		Reference in New Issue
	
	Block a user