Compare commits

...

59 Commits

Author SHA1 Message Date
f419378183 Update node struct to know about noodle connected to it 2024-02-28 23:52:04 +00:00
46a4802ce6 Double buffer the nodes to avoid drawing lag 2024-02-28 22:33:28 +00:00
4ed86611b5 Fix what happens when a noodle gets released 2024-02-27 23:12:59 +00:00
Abdelrahman Said
b447e9d5a1 Update .gitignore 2024-02-26 07:25:25 +00:00
Abdelrahman Said
65d69bd570 Update build script 2024-02-26 07:24:49 +00:00
9570f31dc7 Remove unused include 2024-02-25 23:23:16 +00:00
547764764c Switch to using if statements instead of switch in event handling code 2024-02-25 22:42:07 +00:00
29bcfabda9 Move the node drawing code to a function 2024-02-25 21:21:04 +00:00
f03dad33a2 Move noodle correctly when the node it's connected to moves 2024-02-25 21:11:01 +00:00
8078a72deb Add support for maintaining connected noodle when parent node moves 2024-02-25 21:04:10 +00:00
f2faa56e5f Handle ui_noodle return states and start introducing connected nodes 2024-02-25 20:52:44 +00:00
a68d6997a5 Add different return states for ui_noodle 2024-02-25 20:52:25 +00:00
373e48216d Reorder ui functions 2024-02-25 20:33:04 +00:00
7b7a913c46 Simplify the immediate mode functions and move node
connection logic to the application level
2024-02-25 20:31:11 +00:00
bc6bb1afdc Update the node struct 2024-02-25 20:31:01 +00:00
b847f87ee5 Move line_from_origin and aabb functions to the window code 2024-02-25 20:30:26 +00:00
4610561eff Remove code for connecting noodles 2024-02-25 13:14:14 +00:00
8501eb787e Support connecting noodle to another node and ensure it
stays connected when parent node moves
2024-02-25 12:33:13 +00:00
050bb355d0 Reset noodle to default position if not connected to another node 2024-02-25 00:46:02 +00:00
a86b025f7d Fix node type typo 2024-02-25 00:02:40 +00:00
2d31233a1e Add functionality to draw nodes with user-defined inputs count 2024-02-24 23:53:04 +00:00
30986e3c99 Update submodules 2024-02-24 23:04:55 +00:00
84cdb87a19 Update code to use stdlib 2024-02-24 20:45:09 +00:00
84873f1e98 Replace separate aliases and arena implementations with stdlib submodule 2024-02-24 20:44:44 +00:00
10ba3d642d Support drawing a node with connection that can be modified by user 2024-02-20 00:18:53 +00:00
d73275f04c Add line_from_origin function 2024-01-24 23:43:14 +00:00
f56e6b1bb1 Remove test code 2024-01-24 23:43:06 +00:00
c70f252349 Add absolute, radians and degrees math utilities 2024-01-24 23:42:40 +00:00
8275fce9c6 Update noodle drawing code 2024-01-24 22:44:36 +00:00
a58a315d48 Update test code 2024-01-24 22:44:20 +00:00
04855478fd Add function to calculate line direction 2024-01-24 22:43:55 +00:00
9139dae379 Add square macro 2024-01-24 22:43:36 +00:00
9f700c070d Update build script to link with math library 2024-01-24 22:43:17 +00:00
bdb4a52771 Reorganise window functions 2024-01-24 22:13:38 +00:00
afd09d7742 Remove unused include 2024-01-21 22:37:47 +00:00
924ed544aa Improve implementation of drawing the noodles 2024-01-21 22:37:09 +00:00
f72497e1b7 Use barycentric coordinates instead of half space for triangle fill 2024-01-21 22:36:36 +00:00
b329e0feb4 Add math utilities 2024-01-21 22:36:13 +00:00
53d0a4698f Fix moving behaviour 2024-01-21 19:39:25 +00:00
64f0328966 Rename data types and start implementing drawing quads in ui 2024-01-21 03:38:48 +00:00
6df11cfdeb Add quad drawing utilities 2024-01-21 01:20:00 +00:00
fa82203681 Add basic function to fill triangle 2024-01-21 00:19:21 +00:00
63f119c1b2 Refactor UI code into an immediate mode style (#1)
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Reviewed-on: #1
2024-01-20 22:22:17 +00:00
60d236c080 Focus window on mouse enter 2024-01-15 23:44:38 +00:00
571e734ebe Add op node when clicked and connect to proper function 2024-01-15 23:35:44 +00:00
680f1ec380 Ensure position of last clicked mouse is stored on every click 2024-01-15 23:29:13 +00:00
f541e3afc3 Fix button clicking logic 2024-01-15 23:27:41 +00:00
0ac799cec8 Add initial toolbox support 2024-01-15 22:38:58 +00:00
9d438d7347 Add basic comp ops implementations 2024-01-15 22:38:30 +00:00
6d7de03ae8 Add drawing buttons 2024-01-15 22:36:46 +00:00
f7449d8014 Remove aabb from the nodes module 2024-01-15 22:36:25 +00:00
c831fd23f7 Extract collision detection into a ui module 2024-01-15 22:35:31 +00:00
d114cfce99 Create toolbox window 2024-01-15 20:48:36 +00:00
9806a5c708 Add window position information 2024-01-15 20:48:21 +00:00
81fbff96b8 Filter events by window ID 2024-01-15 20:18:41 +00:00
10ff46cfe4 Update node_t struct definition 2024-01-15 20:03:55 +00:00
fc0e3524fe Add window ID 2024-01-15 20:03:25 +00:00
dacc3be970 Update code for detecting mouse hovering nodes 2024-01-15 19:12:21 +00:00
fd6e237647 Modify node border colours 2024-01-15 19:12:06 +00:00
18 changed files with 925 additions and 150 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.cache .cache
compile_commands.json compile_commands.json
main main
*.dSYM

6
.gitmodules vendored
View File

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

20
compile
View File

@@ -1,9 +1,21 @@
#!/bin/bash #!/bin/bash
CC=clang CC=clang
CFLAGS="-g -Wall -Iinclude -Iintern $(pkg-config --cflags sdl2)" CFLAGS="-g -Wall $(pkg-config --cflags sdl2)"
LIBS="$(pkg-config --libs sdl2)" LIBS="$(pkg-config --libs sdl2) -lm"
SRC=src/*.c INCLUDE="\
-Iinclude \
-Iintern/wizapp/aliases \
-Iintern/wizapp/cpath/include \
-Iintern/wizapp/dstr/include \
$(find intern/wizapp/mem/include -type d | xargs -I{} echo -n "-I{} ") \
"
SRC="\
intern/wizapp/cpath/src/*.c \
intern/wizapp/dstr/src/*.c \
intern/wizapp/mem/src/*/*.c \
src/*.c \
"
OUT=main OUT=main
(set -x ; $CC $CFLAGS $LIBS $SRC -o $OUT) (set -x ; $CC $CFLAGS $INCLUDE $LIBS $SRC -o $OUT)

View File

@@ -1,7 +1,7 @@
#ifndef COMPOSITOR_H #ifndef COMPOSITOR_H
#define COMPOSITOR_H #define COMPOSITOR_H
#include "aliases/aliases.h" #include "aliases.h"
i32 run_main_loop(void); i32 run_main_loop(void);

14
include/math_utils.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
#include "aliases.h"
#define square(x) (x * x)
#define absolute(x) (x < 0.0 ? x * -1.0 : x)
i32 min(i32 a, i32 b);
i32 max(i32 a, i32 b);
f64 radians(f64 degrees);
f64 degrees(f64 radians);
#endif // !MATH_UTILS_H

View File

@@ -1,35 +1,49 @@
#ifndef NODES_H #ifndef NODES_H
#define NODES_H #define NODES_H
#include "window.h" #include "aliases.h"
#include "ui.h"
#define MAX_NODES 1024 #define MAX_NODES 1024
#define IO_INPUT_COUNT 0
#define OP_INPUT_COUNT 2
#define EMPTY_NODE 0
#define NODE_START 1
#define CONNECTION_START 1
#define NODE_WIDTH 70 typedef i32 (*node_func)(i32 a, i32 b);
#define NODE_HEIGHT 20 typedef enum node_type node_type;
typedef union node_data node_data;
typedef struct node node;
typedef struct noodle noodle;
#define IO_NODE_FILL_COLOUR ((colour_t){.abgr = 0xff2c84b7}) enum node_type {
#define IO_NODE_BORDER_COLOUR ((colour_t){.abgr = 0xff122d5e}) NODE_TYPE_IO,
#define OP_NODE_FILL_COLOUR ((colour_t){.abgr = 0xffad6c3a}) NODE_TYPE_OP,
#define OP_NODE_BORDER_COLOUR ((colour_t){.abgr = 0xff592516})
typedef struct node node_t; COUNT_NODE_TYPES,
};
enum comp_ops { union node_data {
COMP_OP_ADD, const char *path;
COMP_OP_SUB, node_func func;
COMP_OP_MUL, };
COMP_OP_DIV,
COUNT_COMP_OPS, struct noodle {
line ln;
u64 connected_node;
u64 connection_idx;
}; };
struct node { struct node {
rect_t rect; rect rec;
const char *path; ui_elem_colours colours;
node_type type;
node_data data;
u64 inputs;
u64 connected;
noodle *noodles;
noodle **connected_noodles;
}; };
bool aabb(const node_t *node, i32 x, i32 y);
void draw_node(const window_t *wnd, const node_t *node);
#endif // !NODES_H #endif // !NODES_H

28
include/ops.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef COMP_OPS_H
#define COMP_OPS_H
#include "aliases.h"
#include "nodes.h"
enum comp_ops {
COMP_OP_ADD,
COMP_OP_SUB,
COMP_OP_MUL,
COMP_OP_DIV,
COUNT_COMP_OPS,
};
i32 comp_add(i32 a, i32 b);
i32 comp_sub(i32 a, i32 b);
i32 comp_mul(i32 a, i32 b);
i32 comp_div(i32 a, i32 b);
internal node_func ops[COUNT_COMP_OPS] = {
[COMP_OP_ADD] = comp_add,
[COMP_OP_SUB] = comp_sub,
[COMP_OP_MUL] = comp_mul,
[COMP_OP_DIV] = comp_div,
};
#endif // !COMP_OPS_H

77
include/ui.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef UI_H
#define UI_H
#include "SDL_events.h"
#include "aliases.h"
#include "window.h"
#include <stdbool.h>
#define MAX_UI_ELEMENTS 8192
#define BUTTON_WIDTH 100
#define BUTTON_HEIGHT 40
#define NODE_WIDTH 70
#define NODE_HEIGHT 20
#define DEFAULT_NOODLE_LENGTH 60
typedef enum ui_elem_type ui_elem_type;
typedef struct ui_elem ui_elem;
typedef struct ui_ctx ui_ctx;
typedef struct ui_elem_colours ui_elem_colours;
typedef enum noodle_action noodle_action;
enum ui_elem_type {
UI_ELEM_NODE,
UI_ELEM_NOODLE,
UI_ELEM_BUTTON,
COUNT_UI_ELEM,
};
struct ui_elem {
u64 id;
union {
rect rec;
line ln;
};
ui_elem_type type;
};
struct ui_elem_colours {
colour fill;
colour border;
};
enum noodle_action {
NOODLE_ACTION_NONE,
NOODLE_ACTION_DRAGGING,
NOODLE_ACTION_RELEASED,
};
struct ui_ctx {
u64 count;
i64 hovered;
i64 active;
i32 mouse_x;
i32 mouse_y;
i32 rel_x;
i32 rel_y;
bool mouse_down;
bool mouse_up;
const window *wnd;
ui_elem elements[MAX_UI_ELEMENTS];
};
void init_ui_ctx(ui_ctx *ctx);
void reset_ui_ctx(ui_ctx *ctx);
void handle_ui_events(const window *wnd, ui_ctx *ctx, const SDL_Event *event);
bool ui_button(const window *wnd, ui_ctx *ctx, rect rect,
ui_elem_colours colours);
bool ui_node(const window *wnd, ui_ctx *ctx, rect rect,
ui_elem_colours colours);
noodle_action ui_noodle(const window *wnd, ui_ctx *ctx, line ln,
ui_elem_colours colours, rect parent_node);
#endif // !UI_H

View File

@@ -1,17 +1,19 @@
#ifndef WINDOW_H #ifndef WINDOW_H
#define WINDOW_H #define WINDOW_H
#include "aliases/aliases.h" #include "aliases.h"
#include <SDL2/SDL_pixels.h> #include <SDL2/SDL_pixels.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>
typedef struct point point_t; typedef struct point point;
typedef struct line line_t; typedef point vec2;
typedef struct triangle triangle_t; typedef struct line line;
typedef struct rect rect_t; typedef struct triangle triangle;
typedef struct window window_t; typedef struct quad quad;
typedef struct rect rect;
typedef struct window window;
struct point { struct point {
i32 x; i32 x;
@@ -19,23 +21,33 @@ struct point {
}; };
struct line { struct line {
point_t p0; point p0;
point_t p1; point p1;
}; };
struct triangle { struct triangle {
point_t p0; point p0;
point_t p1; point p1;
point_t p2; point p2;
};
struct quad {
point p0;
point p1;
point p2;
point p3;
}; };
struct rect { struct rect {
point_t topleft; point topleft;
i32 w; i32 w;
i32 h; i32 h;
}; };
struct window { struct window {
u32 id;
u64 x;
u64 y;
u64 width; u64 width;
u64 height; u64 height;
const char *title; const char *title;
@@ -43,7 +55,7 @@ struct window {
SDL_Renderer *renderer; SDL_Renderer *renderer;
}; };
typedef struct colour colour_t; typedef struct colour colour;
struct colour { struct colour {
union { union {
u32 abgr; u32 abgr;
@@ -51,15 +63,24 @@ struct colour {
}; };
}; };
bool init_window(window_t *wnd, const char *title, u64 width, u64 height); bool init_window(window *wnd, const char *title, u32 width, u32 height, i32 x,
void cleanup_window(window_t *wnd); i32 y);
void clear_window(const window_t *wnd, colour_t colour); void cleanup_window(window *wnd);
void swap_buffers(const window_t *wnd); void clear_window(const window *wnd, colour colour);
void draw_point(const window_t *wnd, point_t p, colour_t colour); void swap_buffers(const window *wnd);
void draw_line(const window_t *wnd, const line_t *ln, colour_t colour);
void draw_triangle(const window_t *wnd, const triangle_t *triangle, vec2 line_direction(const line *ln);
colour_t colour);
void draw_rect(const window_t *wnd, const rect_t *rect, colour_t colour); void draw_point(const window *wnd, point p, colour colour);
void fill_rect(const window_t *wnd, const rect_t *rect, colour_t colour); void draw_line(const window *wnd, const line *ln, colour colour);
void draw_triangle(const window *wnd, triangle triangle, colour colour);
void draw_quad(const window *wnd, quad qd, colour colour);
void draw_rect(const window *wnd, rect rec, colour colour);
void fill_triangle(const window *wnd, triangle triangle, colour colour);
void fill_quad(const window *wnd, quad qd, colour colour);
void fill_rect(const window *wnd, rect rec, colour colour);
line line_from_origin(point origin, f64 angle, i32 line_length);
bool aabb(rect rec, i32 x, i32 y);
#endif // !WINDOW_H #endif // !WINDOW_H

Submodule intern/aliases deleted from f95f3aa499

1
intern/wizapp Submodule

Submodule intern/wizapp added at 7948d3fd1a

View File

@@ -1,5 +1,8 @@
#include "aliases/aliases.h" #include "aliases.h"
#include "mem_arena.h"
#include "nodes.h" #include "nodes.h"
#include "ops.h"
#include "ui.h"
#include "window.h" #include "window.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
@@ -13,119 +16,357 @@
#define WINDOW_WIDTH 1280 #define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720 #define WINDOW_HEIGHT 720
typedef struct compositor compositor_t; #define ARENA_CAPACITY 1 * 1024 * 1024
typedef struct compositor compositor;
struct compositor { struct compositor {
window_t windows[MAX_WINDOWS]; Arena *arena;
window windows[MAX_WINDOWS];
u32 active_window;
SDL_Event event; SDL_Event event;
bool running; bool running;
u64 mouse_x;
u64 mouse_y;
i64 node_hovered;
u64 count; u64 count;
node_t *nodes; node *nodes;
bool move_node; node *back_nodes;
ui_ctx ctx;
}; };
void add_node(compositor_t *comp, const char *path, i32 x, i32 y); internal void add_node(compositor *comp, node_type type, node_data data,
u64 inputs, i32 x, i32 y, ui_elem_colours colours);
internal void update_node_graph(compositor *comp, const window *wnd);
internal void draw_node_graph(compositor *comp, const window *wnd);
i32 run_main_loop(void) { i32 run_main_loop(void) {
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
compositor_t comp = {0}; compositor comp = {0};
comp.nodes = (node_t *)malloc(sizeof(node_t) * MAX_NODES);
window_t *main_window = &(comp.windows[0]); init_ui_ctx(&(comp.ctx));
mem_arena_init(&comp.arena, ARENA_CAPACITY);
if (!init_window(main_window, "Compositor", WINDOW_WIDTH, WINDOW_HEIGHT)) { comp.nodes = (node *)mem_arena_alloc(comp.arena, sizeof(node) * MAX_NODES);
comp.back_nodes =
(node *)mem_arena_alloc(comp.arena, sizeof(node) * MAX_NODES);
window *main_window = &(comp.windows[0]);
window *toolbox = &(comp.windows[1]);
if (!init_window(main_window, "Compositor", WINDOW_WIDTH, WINDOW_HEIGHT, -1,
-1)) {
SDL_Quit(); SDL_Quit();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
u32 toolbox_window_width = WINDOW_WIDTH / 7;
init_window(toolbox, "Toolbox", toolbox_window_width, WINDOW_HEIGHT,
main_window->x - toolbox_window_width, -1);
comp.running = true; comp.running = true;
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
colour_t bg_colour = {.abgr = 0xffffffff}; colour bg_colour = {.abgr = 0xffffffff};
ui_elem_colours button_colours = (ui_elem_colours){
.fill = (colour){.abgr = 0xff89a83c},
.border = (colour){.abgr = 0xff768432},
};
ui_elem_colours io_node_colours = (ui_elem_colours){
.fill = (colour){.abgr = 0xff2c84b7},
.border = (colour){.abgr = 0xff315c89},
};
ui_elem_colours op_node_colours = (ui_elem_colours){
.fill = (colour){.abgr = 0xffad6c3a},
.border = (colour){.abgr = 0xff8e4a33},
};
i32 toolbox_button_x = (toolbox->width - BUTTON_WIDTH) / 2;
while (comp.running) { while (comp.running) {
while (SDL_PollEvent(&(comp.event))) { while (SDL_PollEvent(&(comp.event))) {
switch (comp.event.type) { handle_ui_events(&(comp.windows[comp.active_window - 1]), &(comp.ctx),
case SDL_QUIT: &(comp.event));
if (comp.event.type == SDL_QUIT) {
comp.running = false; comp.running = false;
break; }
case SDL_MOUSEBUTTONDOWN:
if (comp.node_hovered != -1) { if (comp.event.type == SDL_WINDOWEVENT) {
comp.move_node = true; if (comp.event.window.event == SDL_WINDOWEVENT_CLOSE) {
comp.running = false;
} }
break; if (comp.event.window.event == SDL_WINDOWEVENT_ENTER) {
case SDL_MOUSEBUTTONUP: u32 id = comp.event.window.windowID;
comp.move_node = false; window *wnd = NULL;
break; for (u64 i = 0; i < MAX_WINDOWS; ++i) {
case SDL_MOUSEMOTION: window *window = &(comp.windows[i]);
comp.mouse_x = comp.event.motion.x;
comp.mouse_y = comp.event.motion.y;
if (comp.move_node) { if (id == window->id) {
i32 dx = comp.event.motion.xrel; comp.active_window = id;
i32 dy = comp.event.motion.yrel; wnd = window;
node_t *node = &(comp.nodes[comp.node_hovered]);
node->rect.topleft.x += dx;
node->rect.topleft.y += dy;
} else {
comp.node_hovered = -1;
for (u64 i = 0; i < comp.count; ++i) {
node_t *node = &(comp.nodes[i]);
if (aabb(node, comp.mouse_x, comp.mouse_y)) {
comp.node_hovered = i;
break; break;
} }
} }
}
break; if (wnd) {
case SDL_DROPFILE: SDL_RaiseWindow(wnd->window);
add_node(&comp, comp.event.drop.file, comp.mouse_x, comp.mouse_y); }
break; }
}
if (comp.event.type == SDL_DROPFILE) {
if (comp.event.drop.windowID == main_window->id) {
node_data data = (node_data){.path = comp.event.drop.file};
add_node(&comp, NODE_TYPE_IO, data, IO_INPUT_COUNT, comp.ctx.mouse_x,
comp.ctx.mouse_y, io_node_colours);
}
} }
} }
clear_window(main_window, bg_colour); update_node_graph(&comp, main_window);
for (u64 i = 0; i < comp.count; ++i) { for (u64 i = 0; i < MAX_WINDOWS; ++i) {
node_t *node = &(comp.nodes[i]); clear_window(&(comp.windows[i]), bg_colour);
draw_node(main_window, node);
} }
swap_buffers(main_window); for (u64 i = 0; i < COUNT_COMP_OPS; ++i) {
rect rect = {
.topleft.x = toolbox_button_x,
.topleft.y = i * (BUTTON_HEIGHT + 20) + 30,
.w = BUTTON_WIDTH,
.h = BUTTON_HEIGHT,
};
if (ui_button(toolbox, &(comp.ctx), rect, button_colours)) {
node_data data = (node_data){.func = ops[i]};
add_node(&comp, NODE_TYPE_OP, data, OP_INPUT_COUNT, comp.ctx.mouse_x,
comp.ctx.mouse_y, op_node_colours);
}
}
draw_node_graph(&comp, main_window);
for (u64 i = 0; i < MAX_WINDOWS; ++i) {
swap_buffers(&(comp.windows[i]));
}
reset_ui_ctx(&(comp.ctx));
}
for (u64 i = 0; i < MAX_WINDOWS; ++i) {
cleanup_window(&(comp.windows[i]));
} }
SDL_Quit(); SDL_Quit();
mem_arena_free(&comp.arena);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
void add_node(compositor_t *comp, const char *path, i32 x, i32 y) { internal void add_node(compositor *comp, node_type type, node_data data,
u64 inputs, i32 x, i32 y, ui_elem_colours colours) {
if (comp->count + 1 >= MAX_NODES) { if (comp->count + 1 >= MAX_NODES) {
return; return;
} }
comp->nodes[(comp->count)++] = (node_t){ u64 alloc_size = inputs * sizeof(noodle);
.rect = noodle *noodles = mem_arena_alloc(comp->arena, alloc_size);
(rect_t){ noodle *back_noodles = mem_arena_alloc(comp->arena, alloc_size);
.topleft.x = x, if (!noodles || !back_noodles) {
.topleft.y = y, return;
.w = NODE_WIDTH, }
.h = NODE_HEIGHT,
}, u64 connected_alloc_size = MAX_NODES * sizeof(noodle *);
.path = path, noodle **connected_noodles =
mem_arena_alloc(comp->arena, connected_alloc_size);
noodle **connected_back_noodles =
mem_arena_alloc(comp->arena, connected_alloc_size);
if (!connected_noodles || !connected_back_noodles) {
return;
}
rect rec = (rect){
.topleft.x = x,
.topleft.y = y,
.w = NODE_WIDTH,
.h = NODE_HEIGHT,
}; };
u64 idx = ++(comp->count);
comp->nodes[idx] = comp->back_nodes[idx] = (node){
.rec = rec,
.colours = colours,
.type = type,
.data.path = data.path,
.inputs = inputs,
.connected = 0,
.noodles = noodles,
.connected_noodles = connected_noodles,
};
comp->back_nodes[idx].noodles = back_noodles;
comp->back_nodes[idx].connected_noodles = connected_back_noodles;
}
internal void update_node_graph(compositor *comp, const window *wnd) {
for (u64 i = NODE_START; i <= comp->count; ++i) {
node *node_elem = &(comp->nodes[i]);
const node *back_node = &(comp->back_nodes[i]);
node_elem->rec = back_node->rec;
node_elem->connected = back_node->connected;
for (u64 j = 0; j < node_elem->inputs; ++j) {
noodle *ndl = &(node_elem->noodles[j]);
const noodle *back_ndl = &(back_node->noodles[j]);
*ndl = *back_ndl;
}
for (u64 j = CONNECTION_START; j <= back_node->connected; ++j) {
node_elem->connected_noodles[j] = back_node->connected_noodles[j];
}
}
}
internal void draw_node_graph(compositor *comp, const window *wnd) {
for (u64 i = NODE_START; i <= comp->count; ++i) {
node *node_elem = &(comp->nodes[i]);
node *back_node = &(comp->back_nodes[i]);
f64 angle = 90.0;
f64 angle_delta = 25.0;
i64 delta_multiplier = node_elem->inputs % 2 == 0 ? -1 : 0;
for (u64 j = 0; j < node_elem->inputs; ++j) {
f64 new_angle = angle + angle_delta * delta_multiplier;
noodle *ndl = &(node_elem->noodles[j]);
noodle *back_ndl = &(back_node->noodles[j]);
if (ndl->ln.p0.x == ndl->ln.p1.x && ndl->ln.p0.y == ndl->ln.p1.y) {
point origin = {node_elem->rec.topleft.x + node_elem->rec.w / 2,
node_elem->rec.topleft.y + node_elem->rec.h / 2};
back_ndl->ln =
line_from_origin(origin, new_angle, DEFAULT_NOODLE_LENGTH);
}
switch (ui_noodle(wnd, &(comp->ctx), ndl->ln, node_elem->colours,
node_elem->rec)) {
case NOODLE_ACTION_DRAGGING:
back_ndl->ln.p0.x += comp->ctx.rel_x;
back_ndl->ln.p0.y += comp->ctx.rel_y;
break;
case NOODLE_ACTION_RELEASED: {
bool connected = false;
bool disconnect = false;
for (u64 k = NODE_START; k <= comp->count; ++k) {
if (k == i) {
continue;
}
const node *nd = &(comp->nodes[k]);
node *back_node = &(comp->back_nodes[k]);
if (aabb(nd->rec, comp->ctx.mouse_x, comp->ctx.mouse_y)) {
point p0 = {nd->rec.topleft.x + nd->rec.w / 2,
nd->rec.topleft.y + nd->rec.h / 2};
back_ndl->ln.p0 = p0;
connected = true;
disconnect = back_ndl->connected_node != EMPTY_NODE &&
back_ndl->connected_node != k;
if (back_ndl->connected_node != k) {
back_ndl->connected_node = k;
u64 idx = ++(back_node->connected);
back_ndl->connection_idx = idx;
back_node->connected_noodles[idx] = back_ndl;
}
break;
} else {
if (back_ndl->connected_node != EMPTY_NODE) {
disconnect = true;
}
}
}
if (disconnect) {
u64 conntection_idx = back_ndl->connection_idx;
u64 node_idx = back_ndl->connected_node;
node *connected_node = &(comp->back_nodes[node_idx]);
if (conntection_idx == connected_node->connected) {
connected_node->connected_noodles[conntection_idx] = NULL;
} else {
connected_node->connected_noodles[conntection_idx] =
connected_node->connected_noodles[connected_node->connected];
connected_node->connected_noodles[connected_node->connected] = NULL;
connected_node->connected_noodles[conntection_idx]->connection_idx =
conntection_idx;
}
connected_node->connected -= 1;
}
if (!connected) {
back_ndl->ln.p0 = ndl->ln.p1;
back_ndl->connected_node = EMPTY_NODE;
}
break;
}
default:
break;
}
if (delta_multiplier > 0) {
angle = new_angle;
}
if (delta_multiplier == 0) {
delta_multiplier = -1;
} else {
delta_multiplier *= -1;
}
}
if (ui_node(wnd, &(comp->ctx), node_elem->rec, node_elem->colours)) {
back_node->rec.topleft.x += comp->ctx.rel_x;
back_node->rec.topleft.y += comp->ctx.rel_y;
for (u64 j = 0; j < node_elem->inputs; ++j) {
noodle *ndl = &(node_elem->noodles[j]);
noodle *back_ndl = &(back_node->noodles[j]);
if (ndl->connected_node == EMPTY_NODE) {
back_ndl->ln.p0.x += comp->ctx.rel_x;
back_ndl->ln.p0.y += comp->ctx.rel_y;
}
back_ndl->ln.p1.x += comp->ctx.rel_x;
back_ndl->ln.p1.y += comp->ctx.rel_y;
}
for (u64 j = CONNECTION_START; j <= back_node->connected; ++j) {
noodle *ndl = back_node->connected_noodles[j];
ndl->ln.p0.x += comp->ctx.rel_x;
ndl->ln.p0.y += comp->ctx.rel_y;
}
}
}
} }

View File

@@ -1,4 +1,4 @@
#include "aliases/aliases.h" #include "aliases.h"
#include "compositor.h" #include "compositor.h"
i32 main(void) { return run_main_loop(); } i32 main(void) { return run_main_loop(); }

10
src/math_utils.c Normal file
View File

@@ -0,0 +1,10 @@
#include "math_utils.h"
#include <math.h>
i32 min(i32 a, i32 b) { return a <= b ? a : b; }
i32 max(i32 a, i32 b) { return a >= b ? a : b; }
f64 radians(f64 degrees) { return degrees * M_PI / 180.0; }
f64 degrees(f64 radians) { return radians * 180.0 / M_PI; }

View File

@@ -1,13 +0,0 @@
#include "nodes.h"
#include "aliases/aliases.h"
#include <stdbool.h>
bool aabb(const node_t *node, i32 x, i32 y) {
return x > node->rect.topleft.x && x <= node->rect.topleft.x + node->rect.w &&
y > node->rect.topleft.y && y <= node->rect.topleft.y + node->rect.h;
}
void draw_node(const window_t *wnd, const node_t *node) {
fill_rect(wnd, &(node->rect), IO_NODE_FILL_COLOUR);
draw_rect(wnd, &(node->rect), IO_NODE_BORDER_COLOUR);
}

10
src/ops.c Normal file
View File

@@ -0,0 +1,10 @@
#include "ops.h"
#include "aliases.h"
i32 comp_add(i32 a, i32 b) { return a + b; }
i32 comp_sub(i32 a, i32 b) { return a - b; }
i32 comp_mul(i32 a, i32 b) { return a * b; }
i32 comp_div(i32 a, i32 b) { return a / b; }

253
src/ui.c Normal file
View File

@@ -0,0 +1,253 @@
#include "ui.h"
#include "SDL_events.h"
#include "aliases.h"
#include "math_utils.h"
#include "window.h"
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#define NOODLE_HALF_WIDTH 2
internal u64 get_id(ui_ctx *ctx);
void init_ui_ctx(ui_ctx *ctx) {
*ctx = (ui_ctx){0};
ctx->hovered = -1;
ctx->active = -1;
}
void reset_ui_ctx(ui_ctx *ctx) {
ctx->count = 0;
ctx->mouse_down = false;
ctx->mouse_up = false;
ctx->rel_x = 0;
ctx->rel_y = 0;
}
void handle_ui_events(const window *wnd, ui_ctx *ctx, const SDL_Event *event) {
if (event->type == SDL_MOUSEMOTION) {
if (wnd->id == event->motion.windowID) {
ctx->mouse_x = event->motion.x;
ctx->mouse_y = event->motion.y;
ctx->rel_x += event->motion.xrel;
ctx->rel_y += event->motion.yrel;
ctx->wnd = wnd;
}
}
if (event->type == SDL_MOUSEBUTTONDOWN) {
if (wnd->id == event->button.windowID) {
ctx->mouse_x = event->button.x;
ctx->mouse_y = event->button.y;
ctx->mouse_down = true;
ctx->wnd = wnd;
}
}
if (event->type == SDL_MOUSEBUTTONUP) {
if (wnd->id == event->button.windowID) {
ctx->mouse_x = event->button.x;
ctx->mouse_y = event->button.y;
ctx->mouse_up = true;
ctx->wnd = wnd;
}
}
}
bool ui_button(const window *wnd, ui_ctx *ctx, rect rec,
ui_elem_colours colours) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return false;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.rec = rec,
.type = UI_ELEM_BUTTON,
};
fill_rect(wnd, rec, colours.fill);
draw_rect(wnd, rec, colours.border);
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return false;
}
if (!aabb(rec, ctx->mouse_x, ctx->mouse_y)) {
return false;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
if (ctx->mouse_up && ctx->hovered == id && ctx->active == id) {
ctx->hovered = ctx->active = -1;
return true;
}
return false;
}
bool ui_node(const window *wnd, ui_ctx *ctx, rect rect,
ui_elem_colours colours) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return false;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.rec = rect,
.type = UI_ELEM_NODE,
};
fill_rect(wnd, rect, colours.fill);
draw_rect(wnd, rect, colours.border);
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return false;
}
if (ctx->mouse_up) {
if (ctx->hovered == ctx->active && ctx->hovered == id) {
ctx->hovered = ctx->active = -1;
}
return false;
}
if (ctx->hovered == id && ctx->active == id) {
return true;
}
if (!aabb(rect, ctx->mouse_x, ctx->mouse_y)) {
return false;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
return false;
}
noodle_action ui_noodle(const window *wnd, ui_ctx *ctx, line ln,
ui_elem_colours colours, rect parent_node) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return NOODLE_ACTION_NONE;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.ln = ln,
.type = UI_ELEM_NOODLE,
};
bool horizontal = ln.p0.y == ln.p1.y;
rect bounding_box = (rect){0};
if (horizontal) {
i32 x = min(ln.p0.x, ln.p1.x);
bounding_box.topleft = (point){x, ln.p0.y - NOODLE_HALF_WIDTH};
bounding_box.w = abs(ln.p1.x - ln.p0.x);
bounding_box.h = NOODLE_HALF_WIDTH * 2;
fill_rect(wnd, bounding_box, colours.fill);
} else {
vec2 direction = line_direction(&ln);
quad qd = (quad){0};
if (direction.x == 0) {
qd = (quad){
.p0 = (point){ln.p0.x - NOODLE_HALF_WIDTH, ln.p0.y},
.p1 = (point){ln.p0.x + NOODLE_HALF_WIDTH, ln.p0.y},
.p2 = (point){ln.p1.x - NOODLE_HALF_WIDTH, ln.p1.y},
.p3 = (point){ln.p1.x + NOODLE_HALF_WIDTH, ln.p1.y},
};
} else {
f32 slope = (f32)(direction.y) / direction.x;
f32 perpendicular_dy =
sqrtf(square(NOODLE_HALF_WIDTH) / (square((f32)slope) + 1.0f));
f32 perpendicular_dx = -1.0f * slope * perpendicular_dy;
qd = (quad){
.p0 = (point){ln.p0.x - perpendicular_dx, ln.p0.y - perpendicular_dy},
.p1 = (point){ln.p0.x + perpendicular_dx, ln.p0.y + perpendicular_dy},
.p2 = (point){ln.p1.x - perpendicular_dx, ln.p1.y - perpendicular_dy},
.p3 = (point){ln.p1.x + perpendicular_dx, ln.p1.y + perpendicular_dy},
};
}
fill_quad(wnd, qd, colours.fill);
i32 _min_x_1 = min(qd.p0.x, qd.p1.x);
i32 _min_x_2 = min(qd.p2.x, qd.p3.x);
i32 min_x = min(_min_x_1, _min_x_2);
i32 _max_x_1 = max(qd.p0.x, qd.p1.x);
i32 _max_x_2 = max(qd.p2.x, qd.p3.x);
i32 max_x = max(_max_x_1, _max_x_2);
i32 _min_y_1 = min(qd.p0.y, qd.p1.y);
i32 _min_y_2 = min(qd.p2.y, qd.p3.y);
i32 min_y = min(_min_y_1, _min_y_2);
i32 _max_y_1 = max(qd.p0.y, qd.p1.y);
i32 _max_y_2 = max(qd.p2.y, qd.p3.y);
i32 max_y = max(_max_y_1, _max_y_2);
bounding_box = (rect){
.topleft.x = min_x,
.topleft.y = min_y,
.w = max_x - min_x,
.h = max_y - min_y,
};
}
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return NOODLE_ACTION_NONE;
}
if (ctx->mouse_up) {
if (ctx->hovered == ctx->active && ctx->hovered == id) {
ctx->hovered = ctx->active = -1;
return NOODLE_ACTION_RELEASED;
}
return NOODLE_ACTION_NONE;
}
if (ctx->hovered == id && ctx->active == id) {
return NOODLE_ACTION_DRAGGING;
}
if (!aabb(bounding_box, ctx->mouse_x, ctx->mouse_y) ||
aabb(parent_node, ctx->mouse_x, ctx->mouse_y)) {
return NOODLE_ACTION_NONE;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
return NOODLE_ACTION_NONE;
}
internal u64 get_id(ui_ctx *ctx) {
// This will always keep the 0 slot empty
return ++(ctx->count);
}

View File

@@ -1,14 +1,20 @@
#include "window.h" #include "window.h"
#include "aliases/aliases.h" #include "aliases.h"
#include "math_utils.h"
#include <SDL2/SDL_rect.h> #include <SDL2/SDL_rect.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>
bool init_window(window_t *wnd, const char *title, u64 width, u64 height) { internal inline bool inside_triangle(triangle tri, point p);
bool init_window(window *wnd, const char *title, u32 width, u32 height, i32 x,
i32 y) {
i32 pos_x = x >= 0 ? x : SDL_WINDOWPOS_CENTERED;
i32 pos_y = y >= 0 ? y : SDL_WINDOWPOS_CENTERED;
wnd->window = wnd->window =
SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_CreateWindow(title, pos_x, pos_y, width, height, SDL_WINDOW_SHOWN);
width, height, SDL_WINDOW_SHOWN);
if (!(wnd->window)) { if (!(wnd->window)) {
return false; return false;
} }
@@ -20,14 +26,27 @@ bool init_window(window_t *wnd, const char *title, u64 width, u64 height) {
return false; return false;
} }
wnd->id = SDL_GetWindowID(wnd->window);
wnd->title = title; wnd->title = title;
wnd->width = width;
wnd->height = height; i32 xmp = -1;
i32 ymp = -1;
SDL_GetWindowPosition(wnd->window, &xmp, &ymp);
wnd->x = xmp;
wnd->y = ymp;
i32 wmp = -1;
i32 hmp = -1;
SDL_GetWindowSize(wnd->window, &wmp, &hmp);
wnd->width = wmp;
wnd->height = hmp;
return true; return true;
} }
void cleanup_window(window_t *wnd) { void cleanup_window(window *wnd) {
if (wnd->renderer) { if (wnd->renderer) {
SDL_DestroyRenderer(wnd->renderer); SDL_DestroyRenderer(wnd->renderer);
wnd->renderer = NULL; wnd->renderer = NULL;
@@ -38,54 +57,142 @@ void cleanup_window(window_t *wnd) {
wnd->window = NULL; wnd->window = NULL;
} }
wnd->width = wnd->height = 0; wnd->width = wnd->height = wnd->id = 0;
} }
void set_colour(const window_t *wnd, colour_t colour) { void set_colour(const window *wnd, colour colour) {
SDL_SetRenderDrawColor(wnd->renderer, colour.colour.r, colour.colour.g, SDL_SetRenderDrawColor(wnd->renderer, colour.colour.r, colour.colour.g,
colour.colour.b, colour.colour.a); colour.colour.b, colour.colour.a);
} }
void clear_window(const window_t *wnd, colour_t colour) { void clear_window(const window *wnd, colour colour) {
set_colour(wnd, colour); set_colour(wnd, colour);
SDL_RenderClear(wnd->renderer); SDL_RenderClear(wnd->renderer);
} }
void swap_buffers(const window_t *wnd) { SDL_RenderPresent(wnd->renderer); } void swap_buffers(const window *wnd) { SDL_RenderPresent(wnd->renderer); }
void draw_point(const window_t *wnd, point_t p, colour_t colour) { vec2 line_direction(const line *ln) {
return (vec2){
.x = ln->p1.x - ln->p0.x,
.y = ln->p1.y - ln->p0.y,
};
}
void draw_point(const window *wnd, point p, colour colour) {
set_colour(wnd, colour); set_colour(wnd, colour);
SDL_RenderDrawPoint(wnd->renderer, p.x, p.y); SDL_RenderDrawPoint(wnd->renderer, p.x, p.y);
} }
void draw_line(const window_t *wnd, const line_t *ln, colour_t colour) { void draw_line(const window *wnd, const line *ln, colour colour) {
set_colour(wnd, colour); set_colour(wnd, colour);
SDL_RenderDrawLine(wnd->renderer, ln->p0.x, ln->p0.y, ln->p1.x, ln->p1.y); SDL_RenderDrawLine(wnd->renderer, ln->p0.x, ln->p0.y, ln->p1.x, ln->p1.y);
} }
void draw_triangle(const window_t *wnd, const triangle_t *triangle, void draw_triangle(const window *wnd, triangle triangle, colour colour) {
colour_t colour) { line ln0 = {triangle.p0, triangle.p1};
line_t ln0 = {triangle->p0, triangle->p1}; line ln1 = {triangle.p0, triangle.p2};
line_t ln1 = {triangle->p0, triangle->p2}; line ln2 = {triangle.p1, triangle.p2};
line_t ln2 = {triangle->p1, triangle->p2};
draw_line(wnd, &ln0, colour); draw_line(wnd, &ln0, colour);
draw_line(wnd, &ln1, colour); draw_line(wnd, &ln1, colour);
draw_line(wnd, &ln2, colour); draw_line(wnd, &ln2, colour);
} }
void draw_rect(const window_t *wnd, const rect_t *rect, colour_t colour) { void draw_rect(const window *wnd, rect rec, colour colour) {
set_colour(wnd, colour); set_colour(wnd, colour);
SDL_Rect dst = {rect->topleft.x, rect->topleft.y, rect->w, rect->h}; SDL_Rect dst = {rec.topleft.x, rec.topleft.y, rec.w, rec.h};
SDL_RenderDrawRect(wnd->renderer, &dst); SDL_RenderDrawRect(wnd->renderer, &dst);
} }
void fill_rect(const window_t *wnd, const rect_t *rect, colour_t colour) { void draw_quad(const window *wnd, quad qd, colour colour) {
line l0 = (line){qd.p0, qd.p1};
line l1 = (line){qd.p1, qd.p3};
line l2 = (line){qd.p3, qd.p2};
line l3 = (line){qd.p2, qd.p0};
draw_line(wnd, &l0, colour);
draw_line(wnd, &l1, colour);
draw_line(wnd, &l2, colour);
draw_line(wnd, &l3, colour);
}
void fill_triangle(const window *wnd, triangle tri, colour colour) {
// Basic triangle filling algorithm inspired by
// https://web.archive.org/web/20050408192410/http://sw-shader.sourceforge.net/rasterizer.html
// but uses barycentric coordinates instead of half space
i32 x1 = tri.p0.x;
i32 x2 = tri.p1.x;
i32 x3 = tri.p2.x;
i32 y1 = tri.p0.y;
i32 y2 = tri.p1.y;
i32 y3 = tri.p2.y;
// Find bounding rect
i32 min_x = min(min(x1, x2), x3);
i32 max_x = max(max(x1, x2), x3);
i32 min_y = min(min(y1, y2), y3);
i32 max_y = max(max(y1, y2), y3);
for (i32 y = min_y; y < max_y; ++y) {
for (i32 x = min_x; x < max_x; ++x) {
if (inside_triangle(tri, (point){x, y})) {
draw_point(wnd, (point){x, y}, colour);
}
}
}
}
void fill_rect(const window *wnd, rect rec, colour colour) {
set_colour(wnd, colour); set_colour(wnd, colour);
SDL_Rect dst = {rect->topleft.x, rect->topleft.y, rect->w, rect->h}; SDL_Rect dst = {rec.topleft.x, rec.topleft.y, rec.w, rec.h};
SDL_RenderFillRect(wnd->renderer, &dst); SDL_RenderFillRect(wnd->renderer, &dst);
} }
void fill_quad(const window *wnd, quad qd, colour colour) {
triangle t0 = (triangle){qd.p0, qd.p1, qd.p2};
triangle t1 = (triangle){qd.p1, qd.p2, qd.p3};
fill_triangle(wnd, t0, colour);
fill_triangle(wnd, t1, colour);
}
line line_from_origin(point origin, f64 angle, i32 line_length) {
f64 rad = radians(angle);
f64 direction = angle / absolute(angle) * -1;
i32 adjacent = line_length * cos(rad) * direction; // dx
i32 opposite = line_length * sin(rad) * direction; // dy
return (line){
(point){origin.x + adjacent, origin.y + opposite},
origin,
};
}
bool aabb(rect rec, i32 x, i32 y) {
return x > rec.topleft.x && x <= rec.topleft.x + rec.w && y > rec.topleft.y &&
y <= rec.topleft.y + rec.h;
}
internal inline bool inside_triangle(triangle tri, point p) {
// Based on the following video:
// https://www.youtube.com/watch?v=HYAgJN3x4GA
f32 cy_min_ay = tri.p2.y - tri.p0.y;
f32 cx_min_ax = tri.p2.x - tri.p0.x;
f32 by_min_ay = tri.p1.y - tri.p0.y;
f32 bx_min_ax = tri.p1.x - tri.p0.x;
f32 w1 =
(tri.p0.x * cy_min_ay + (p.y - tri.p0.y) * cx_min_ax - p.x * cy_min_ay) /
(by_min_ay * cx_min_ax - bx_min_ax * cy_min_ay);
f32 w2 = (p.y - tri.p0.y - w1 * by_min_ay) / cy_min_ay;
return w1 >= 0.0f && w2 >= 0.0f && (w1 + w2) <= 1.0f;
}