From 74262db40f53acd3a91e4e63d56466733f4db267 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Sun, 5 Apr 2026 20:56:43 +0200 Subject: [PATCH] cleanup --- src/data.zig | 43 +++++--------- src/main.zig | 157 +++++++-------------------------------------------- 2 files changed, 33 insertions(+), 167 deletions(-) diff --git a/src/data.zig b/src/data.zig index 6ef758b..cc571b9 100644 --- a/src/data.zig +++ b/src/data.zig @@ -1,19 +1,19 @@ const std = @import("std"); -const Type = enum { +pub const Type = enum { ailment, recipe, ingredient, }; -const NodeID = enum { +pub const NodeID = enum { sellery_juice, salt_craving, }; -const Node = struct { - name: []const u8, - description: []const u8, +pub const Node = struct { + name: [:0]const u8, + description: [:0]const u8, type: Type, }; @@ -23,28 +23,7 @@ const Edge = struct { dest: NodeID, }; -fn validated(arr: std.EnumArray(NodeID, Node)) std.EnumArray(NodeID, Node) { - for (std.enums.values(NodeID)) |id| { - const desc = arr.get(id).description; - var i: usize = 0; - while (i + 1 < desc.len) : (i += 1) { - if (desc[i] == '[' and desc[i + 1] == '[') { - const start = i + 2; - var j = start; - while (j + 1 < desc.len and !(desc[j] == ']' and desc[j + 1] == ']')) : (j += 1) {} - const ref = desc[start..j]; - if (std.meta.stringToEnum(NodeID, ref) == null) { - @compileLog("Unknown node reference: ", ref); - @compileError("Invalid wiki link in node description (see log above)"); - } - i = j + 1; - } - } - } - return arr; -} - -pub const nodes = validated(std.EnumArray(NodeID, Node).init(.{ +pub const nodes = std.EnumArray(NodeID, Node).init(.{ .sellery_juice = .{ .name = "Sellery Juice", .description = "A healing drink made from celery.", @@ -52,8 +31,12 @@ pub const nodes = validated(std.EnumArray(NodeID, Node).init(.{ }, .salt_craving = .{ .name = "Salt craving", - .description = "Can be caused by sodium deficincy. Can be fixed with [[sellery_juice]].", - .type = .recipe, + .description = "Can be caused by sodium deficincy. Can be fixed with " ++ linkTo(.sellery_juice) ++ ".", + .type = .ailment, }, -})); +}); var edges: []const Edge = &.{}; + +fn linkTo(node: NodeID) []const u8 { + return "[[" ++ @tagName(node) ++ "]]"; +} diff --git a/src/main.zig b/src/main.zig index 19911ca..65cd067 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,9 +3,6 @@ const rl = @import("raylib"); const builtin = @import("builtin"); const math = std.math; const data = @import("data.zig"); -comptime { - _ = data.nodes; -} const NODE_R: f32 = 42.0; @@ -25,125 +22,45 @@ const ORBIT_R: f32 = 220.0; // ── Data model ─────────────────────────────────────────────────────────────── -const NodeKind = enum { body_part, ailment, recipe, ingredient, property }; - -const NodeDef = struct { - id: u8, - name: [:0]const u8, - kind: NodeKind, - links: []const u8, -}; - -/// All content is hardcoded and will be embedded in the binary at compile time. -const all_nodes = [_]NodeDef{ - // Body systems (0–4) - .{ .id = 0, .name = "Digestive System", .kind = .body_part, .links = &.{ 10, 11, 12 } }, - .{ .id = 1, .name = "Immune System", .kind = .body_part, .links = &.{ 13, 14 } }, - .{ .id = 2, .name = "Nervous System", .kind = .body_part, .links = &.{15} }, - .{ .id = 3, .name = "Circulatory", .kind = .body_part, .links = &.{ 13, 14 } }, - .{ .id = 4, .name = "Endocrine", .kind = .body_part, .links = &.{ 14, 15 } }, - // Ailments (10–15) - .{ .id = 10, .name = "Poor Digestion", .kind = .ailment, .links = &.{ 20, 21 } }, - .{ .id = 11, .name = "Constipation", .kind = .ailment, .links = &.{ 20, 22 } }, - .{ .id = 12, .name = "Bloating", .kind = .ailment, .links = &.{21} }, - .{ .id = 13, .name = "Inflammation", .kind = .ailment, .links = &.{ 22, 23 } }, - .{ .id = 14, .name = "Fatigue", .kind = .ailment, .links = &.{ 21, 23 } }, - .{ .id = 15, .name = "Brain Fog", .kind = .ailment, .links = &.{23} }, - // Recipes (20–23) - .{ .id = 20, .name = "Meat Smoothie", .kind = .recipe, .links = &.{ 30, 31, 32 } }, - .{ .id = 21, .name = "Raw Milk Kefir", .kind = .recipe, .links = &.{ 33, 34 } }, - .{ .id = 22, .name = "Raw Egg Blend", .kind = .recipe, .links = &.{ 31, 32, 34 } }, - .{ .id = 23, .name = "Honey-Butter Mix", .kind = .recipe, .links = &.{ 35, 36 } }, - // Ingredients (30–36) - .{ .id = 30, .name = "Raw Meat", .kind = .ingredient, .links = &.{ 40, 41 } }, - .{ .id = 31, .name = "Raw Eggs", .kind = .ingredient, .links = &.{ 40, 42 } }, - .{ .id = 32, .name = "Lemon Juice", .kind = .ingredient, .links = &.{43} }, - .{ .id = 33, .name = "Raw Milk", .kind = .ingredient, .links = &.{ 42, 44 } }, - .{ .id = 34, .name = "Kefir Grains", .kind = .ingredient, .links = &.{44} }, - .{ .id = 35, .name = "Raw Honey", .kind = .ingredient, .links = &.{ 41, 43 } }, - .{ .id = 36, .name = "Raw Butter", .kind = .ingredient, .links = &.{ 41, 44 } }, - // Properties (40–44) - .{ .id = 40, .name = "High Protein", .kind = .property, .links = &.{} }, - .{ .id = 41, .name = "Energy Dense", .kind = .property, .links = &.{} }, - .{ .id = 42, .name = "Probiotic", .kind = .property, .links = &.{} }, - .{ .id = 43, .name = "Alkalizing", .kind = .property, .links = &.{} }, - .{ .id = 44, .name = "Enzyme Rich", .kind = .property, .links = &.{} }, -}; - -const root_ids = [_]u8{ 0, 1, 2, 3, 4 }; - -fn findNode(id: u8) ?*const NodeDef { - for (&all_nodes) |*n| { - if (n.id == id) return n; - } - return null; -} - -fn kindColor(kind: NodeKind) rl.Color { +fn kindColor(kind: data.Type) rl.Color { return switch (kind) { - .body_part => rl.Color.init(70, 150, 215, 255), .ailment => rl.Color.init(210, 75, 65, 255), .recipe => rl.Color.init(75, 190, 110, 255), .ingredient => rl.Color.init(215, 170, 50, 255), - .property => rl.Color.init(170, 90, 215, 255), }; } // ── Navigation state ────────────────────────────────────────────────────────── -const VisNode = struct { id: u8, x: f32, y: f32, is_center: bool }; +const VisNode = struct { id: data.NodeID, x: f32, y: f32, is_center: bool }; const MAX_STACK = 16; const MAX_VIS = 24; -var nav_stack: [MAX_STACK]u8 = undefined; -var nav_depth: usize = 0; +var nav_stack: [MAX_STACK]data.NodeID = undefined; var vis: [MAX_VIS]VisNode = undefined; var vis_count: usize = 0; -var hovered_id: ?u8 = null; +var hovered_id: ?data.NodeID = null; var ready: bool = false; fn buildVis() void { vis_count = 0; - if (nav_depth == 0) { - // Root view: body systems arranged in a circle - const n = root_ids.len; - const r: f32 = 200.0; - for (root_ids, 0..) |id, i| { - const angle = (2.0 * math.pi * @as(f32, @floatFromInt(i))) / - @as(f32, @floatFromInt(n)) - math.pi / 2.0; - vis[vis_count] = .{ - .id = id, - .x = centerX() + r * @cos(angle), - .y = centerY() + r * @sin(angle), - .is_center = false, - }; - vis_count += 1; - } - } else { - // Drill-down view: selected node at centre, its links in orbit - const cur_id = nav_stack[nav_depth - 1]; - const cur = findNode(cur_id) orelse return; - vis[vis_count] = .{ .id = cur_id, .x = centerX(), .y = centerY(), .is_center = true }; + // Root view: body systems arranged in a circle + const r: f32 = 200.0; + for (std.enums.values(data.NodeID), 0..) |id, i| { + const angle = (2.0 * math.pi * @as(f32, @floatFromInt(i))) / + @as(f32, @floatFromInt(std.enums.values(data.NodeID).len)) - math.pi / 2.0; + vis[vis_count] = .{ + .id = id, + .x = centerX() + r * @cos(angle), + .y = centerY() + r * @sin(angle), + .is_center = false, + }; vis_count += 1; - const n = cur.links.len; - if (n > 0) { - for (cur.links, 0..) |link_id, i| { - const angle = (2.0 * math.pi * @as(f32, @floatFromInt(i))) / - @as(f32, @floatFromInt(n)) - math.pi / 2.0; - vis[vis_count] = .{ - .id = link_id, - .x = centerX() + ORBIT_R * @cos(angle), - .y = centerY() + ORBIT_R * @sin(angle), - .is_center = false, - }; - vis_count += 1; - } - } } } -fn visNodeAt(mx: f32, my: f32) ?u8 { +fn visNodeAt(mx: f32, my: f32) ?data.NodeID { for (vis[0..vis_count]) |vn| { const dx = mx - vn.x; const dy = my - vn.y; @@ -160,29 +77,11 @@ fn update() void { const mx: f32 = @floatFromInt(rl.getMouseX()); const my: f32 = @floatFromInt(rl.getMouseY()); hovered_id = visNodeAt(mx, my); - - if (rl.isMouseButtonPressed(.left)) { - if (hovered_id) |id| { - const is_cur = nav_depth > 0 and nav_stack[nav_depth - 1] == id; - if (!is_cur and nav_depth < MAX_STACK) { - nav_stack[nav_depth] = id; - nav_depth += 1; - buildVis(); - } - } - } - - if (rl.isMouseButtonPressed(.right) or rl.isKeyPressed(.b)) { - if (nav_depth > 0) { - nav_depth -= 1; - buildVis(); - } - } } fn drawScene() void { // Edges from centre to orbit - if (nav_depth > 0 and vis_count > 1) { + if (vis_count > 1) { const c = vis[0]; for (vis[1..vis_count]) |vn| { rl.drawLineEx( @@ -196,8 +95,8 @@ fn drawScene() void { // Nodes for (vis[0..vis_count]) |vn| { - const node = findNode(vn.id) orelse continue; - const col = kindColor(node.kind); + const node = data.nodes.get(vn.id); + const col = kindColor(node.@"type"); const hov = hovered_id != null and hovered_id.? == vn.id; const r = if (hov) NODE_R + 5.0 else NODE_R; const ix: i32 = @intFromFloat(vn.x); @@ -229,28 +128,12 @@ fn drawHUD() void { const by: i32 = 10; rl.drawText("Body Systems", bx, by, 20, rl.Color.gray); bx += rl.measureText("Body Systems", 20); - for (nav_stack[0..nav_depth]) |id| { - rl.drawText(" > ", bx, by, 20, rl.Color.dark_gray); - bx += rl.measureText(" > ", 20); - const node = findNode(id) orelse continue; - rl.drawText(node.name, bx, by, 20, rl.Color.ray_white); - bx += rl.measureText(node.name, 20); - } - - // Navigation hint at bottom - const hint: [:0]const u8 = if (nav_depth > 0) - "[B] / right-click to go back" - else - "Click a body system to explore"; - rl.drawText(hint, 10, screenH() - 28, 18, rl.Color.dark_gray); // Colour legend (top-right) - const legend = [_]struct { kind: NodeKind, label: [:0]const u8 }{ - .{ .kind = .body_part, .label = "Body System" }, + const legend = [_]struct { kind: data.Type, label: [:0]const u8 }{ .{ .kind = .ailment, .label = "Ailment" }, .{ .kind = .recipe, .label = "Recipe" }, .{ .kind = .ingredient, .label = "Ingredient" }, - .{ .kind = .property, .label = "Property" }, }; const lx: i32 = screenW() - 140; var ly: i32 = 10;