#if 0 /* #/ ================================================================ #/ #/ graph.c #/ #/ ================================================================ #/ #/ Self-compilation shell script #/ SRC=${0##*./} BIN=${SRC%.*} gcc \ -Wall -Wextra -Werror -pedantic \ -Wno-old-style-declaration \ -Wno-missing-braces \ -Wno-unused-variable \ -Wno-unused-but-set-variable \ -Wno-unused-parameter \ -Wno-overlength-strings \ -O3 \ -fsanitize=undefined,address,leak -mshstk \ -lX11 -lm \ -o $BIN $SRC && \ ./$BIN $@ && rm $BIN exit $? # */ #endif #include "../graphics.c" enum { MAX_NUM_NODES = 1024, MAX_NUM_EDGES = 1024, }; typedef struct { b8 enabled; f64 x; f64 y; f64 radius; b8 hover; } Node; typedef struct { b8 enabled; i64 src; i64 dst; f64 width; b8 hover; } Edge; typedef struct { Node nodes[MAX_NUM_NODES]; Edge edges[MAX_NUM_EDGES]; } World; World world = {0}; b8 check_node_index(i64 node_index) { return node_index >= 0 && node_index < MAX_NUM_NODES && world.nodes[node_index].enabled; } b8 check_edge_index(i64 edge_index) { return edge_index >= 0 && edge_index < MAX_NUM_EDGES && world.edges[edge_index].enabled && check_node_index(world.edges[edge_index].src) && check_node_index(world.edges[edge_index].dst); } void draw_node(i64 node_index) { assert(check_node_index(node_index)); Node n = world.nodes[node_index]; u32 color = 0; // black color if (n.hover) color = 0x007f00; // green color fill_ellipse( OP_SET, // set pixels color, n.x - n.radius, n.y - n.radius, n.radius * 2, n.radius * 2 ); } void draw_edge(i64 edge_index) { assert(check_edge_index(edge_index)); Edge e = world.edges[edge_index]; Node n0 = world.nodes[e.src]; Node n1 = world.nodes[e.dst]; u32 color = 0x7f7f7f; // grey color if (e.hover) color = 0x007f00; // green color fill_line( OP_SET, // set pixels color, n0.x, n0.y, n1.x, n1.y, e.width ); } void draw_graph(void) { // draw all edges for (i64 i = 0; i < MAX_NUM_EDGES; ++i) if (world.edges[i].enabled) draw_edge(i); // draw all nodes for (i64 i = 0; i < MAX_NUM_NODES; ++i) if (world.nodes[i].enabled) draw_node(i); } void update_node(i64 node_index) { assert(check_node_index(node_index)); Node n = world.nodes[node_index]; f64 cx = platform.cursor_x - n.x; f64 cy = platform.cursor_y - n.y; world.nodes[node_index].hover = cx * cx + cy * cy <= n.radius * n.radius; } void update_edge(i64 edge_index) { assert(check_edge_index(edge_index)); Edge e = world.edges[edge_index]; Node n0 = world.nodes[e.src]; Node n1 = world.nodes[e.dst]; f64 cx = platform.cursor_x - n0.x; f64 cy = platform.cursor_y - n0.y; // normalized line direction vector f64 rx = n1.x - n0.x; f64 ry = n1.y - n0.y; f64 r = sqrt(rx * rx + ry * ry); if (r >= EPSILON) { rx /= r; ry /= r; } // tangent f64 tx = -ry; f64 ty = rx; // distance to the line (dot-product) f64 d = cx * tx + cy * ty; // distance to the tangent line f64 l = cx * rx + cy * ry; world.edges[edge_index].hover = d <= e.width / 2 && d >= -e.width / 2 && l >= 0 && l <= r; } void update_graph(void) { b8 hover_node = 0; // update all nodes for (i64 i = 0; i < MAX_NUM_NODES; ++i) if (world.nodes[i].enabled) { update_node(i); if (world.nodes[i].hover) hover_node = 1; } // update all edges for (i64 i = 0; i < MAX_NUM_EDGES; ++i) if (world.edges[i].enabled) { if (hover_node) world.edges[i].hover = 0; else update_edge(i); } } i32 main(i32 argc, c8 **argv) { (void) argc; (void) argv; platform = (Platform) { .title = "Graph", .frame_width = 960, .frame_height = 720, }; p_init(); world.nodes[0] = (Node) { .enabled = 1, .x = 100, .y = 100, .radius = 50, }; world.nodes[1] = (Node) { .enabled = 1, .x = 300, .y = 100, .radius = 50, }; world.nodes[2] = (Node) { .enabled = 1, .x = 120, .y = 300, .radius = 60, }; world.edges[0] = (Edge) { .enabled = 1, .src = 0, .dst = 1, .width = 30, }; world.edges[1] = (Edge) { .enabled = 1, .src = 0, .dst = 2, .width = 30, }; world.edges[2] = (Edge) { .enabled = 1, .src = 1, .dst = 2, .width = 30, }; while (!platform.done) { p_wait_events(); fill_rectangle(OP_SET, 0xffffff, 0, 0, platform.frame_width, platform.frame_height); update_graph(); draw_graph(); p_render_frame(); } p_cleanup(); return 0; }