From 680e07c976ed2993af08fb0c659c840df13dd420 Mon Sep 17 00:00:00 2001 From: Marto Date: Fri, 1 May 2026 07:49:07 +0200 Subject: [PATCH] Splitting road implementation preparation --- src/common/utils.zig | 24 ++++++++++++ src/infrastructure/node.zig | 6 +++ src/infrastructure/node_manager.zig | 15 +++++++- src/infrastructure/road.zig | 3 +- src/infrastructure/road_manager.zig | 22 +---------- src/simulator.zig | 58 +++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 src/common/utils.zig diff --git a/src/common/utils.zig b/src/common/utils.zig new file mode 100644 index 0000000..eac874a --- /dev/null +++ b/src/common/utils.zig @@ -0,0 +1,24 @@ +const Vector2 = @import("raylib").Vector2; + +const st = @import("structures.zig"); +const Node = @import("../infrastructure/node.zig").Node; + +/// Returns distance between two nodes, with the caveat that it doesn't do square root in the end; +/// that's because we are interested in relative distance and don't care for the precise number as long as it is in +/// correct relation to others +/// +/// This allows us to avoid the (square) rooting operation which is computationally expensive +pub fn calculate_length(start: Vector2, end: Vector2) f32 { + const x_diff = end.x - start.x; + const y_diff = end.y - start.y; + + return x_diff * x_diff + y_diff * y_diff; +} + +/// Comparator function that compares intersection proximity from the node in order to aid the sorting function +pub fn compareIntersections(ctx: *const Node, inter_a: st.IntersectionData, inter_b: st.IntersectionData) bool { + const distance_a = ctx.getRelativeInterDistance(inter_a); + const distance_b = ctx.getRelativeInterDistance(inter_b); + + return distance_a < distance_b; +} \ No newline at end of file diff --git a/src/infrastructure/node.zig b/src/infrastructure/node.zig index 1946d5d..a26f78c 100644 --- a/src/infrastructure/node.zig +++ b/src/infrastructure/node.zig @@ -4,6 +4,7 @@ const rl = @import("raylib"); const c = @import("../common/constants.zig"); const e = @import("../errors.zig"); const st = @import("../common/structures.zig"); +const ut = @import("../common/utils.zig"); const Road = @import("road.zig").Road; pub const Node = struct { @@ -77,6 +78,11 @@ pub const Node = struct { return e.Entity.NotFound; } + + /// Returns distance between the node and intersection object location + pub fn getRelativeInterDistance(self: *const Node, intersection: st.IntersectionData) f32 { + return ut.calculate_length(intersection.pos, self.pos); + } }; // TODO tests diff --git a/src/infrastructure/node_manager.zig b/src/infrastructure/node_manager.zig index e1f3821..444a5ba 100644 --- a/src/infrastructure/node_manager.zig +++ b/src/infrastructure/node_manager.zig @@ -50,11 +50,22 @@ pub const NodeManager = struct { } } + /// Tries to find a node which snapping radius covers the pos + /// + /// If it does it returns a reference to it, otherwise null + pub fn getNodeIfExists(self: *const NodeManager, pos: Vector2) ?*Node { + for (self.nodes.items) |node| { + if (node.withinSnapRadius(pos)) return node; + } + + return null; + } + /// Checks if there is a node pointer within the snap radius of pos coordinate; /// otherwise creates a new node and returns its pointer pub fn getSelectedNode(self: *NodeManager, allocator: std.mem.Allocator, pos: Vector2) !*Node { - for (self.nodes.items) |node| { - if (node.withinSnapRadius(pos)) return node; + if (self.getNodeIfExists(pos)) |node| { + return node; } // No node is within that position, so we must create a new one diff --git a/src/infrastructure/road.zig b/src/infrastructure/road.zig index af19395..9969b98 100644 --- a/src/infrastructure/road.zig +++ b/src/infrastructure/road.zig @@ -2,6 +2,7 @@ const rl = @import("raylib"); const c = @import("../common/constants.zig"); const st = @import("../common/structures.zig"); +const ut = @import("../common/utils.zig"); const Node = @import("node.zig").Node; pub const Road = struct { @@ -20,7 +21,7 @@ pub const Road = struct { .length = 0, }; - road.length = road.calculate_length(); + road.length = ut.calculate_length(start.pos, end.pos); return road; } diff --git a/src/infrastructure/road_manager.zig b/src/infrastructure/road_manager.zig index 21a2d33..026b1e8 100644 --- a/src/infrastructure/road_manager.zig +++ b/src/infrastructure/road_manager.zig @@ -1,8 +1,9 @@ const std = @import("std"); const rl = @import("raylib"); -const st = @import("../common/structures.zig"); const e = @import("../errors.zig"); +const st = @import("../common/structures.zig"); +const ut = @import("../common/utils.zig"); const Road = @import("road.zig").Road; const Node = @import("node.zig").Node; @@ -93,25 +94,6 @@ pub const RoadManager = struct { return null; } - - pub fn getIntersectingRoads(self: *const RoadManager, allocator: std.mem.Allocator, start: *const Node, end: *const Node) ![]st.IntersectionData { - var intersections: std.ArrayList(st.IntersectionData) = .empty; - const collision_point: rl.Vector2 = undefined; - - for (self.roads.items) |road| { - if (!rl.checkCollisionLines(start.pos, end.pos, road.nodes[0].pos, road.nodes[1].pos, &collision_point)) - continue; - - const intersection = st.IntersectionData { - .road = road, - .pos = collision_point, - }; - - try intersections.append(allocator, intersection); - } - - return intersections.toOwnedSlice(allocator); - } }; const Vector2 = @import("raylib").Vector2; diff --git a/src/simulator.zig b/src/simulator.zig index c65b149..5c556ac 100644 --- a/src/simulator.zig +++ b/src/simulator.zig @@ -3,7 +3,9 @@ const rl = @import("raylib"); const c = @import("common/constants.zig"); const st = @import("common/structures.zig"); +const ut = @import("common/utils.zig"); const Road = @import("infrastructure/road.zig").Road; +const Node = @import("infrastructure/node.zig").Node; const NodeManager = @import("infrastructure/node_manager.zig").NodeManager; const RoadManager = @import("infrastructure/road_manager.zig").RoadManager; @@ -143,11 +145,25 @@ pub const Simulator = struct { }; if (self.node_man.temp_node) |temp| { + // Prevents the road from being attached to 2 identical nodes (0 length road) if (temp.id == cur_node.id) return; + + // TODO replace with road splitting self.road_man.addRoad(self.allocator, temp, cur_node) catch |err| { std.debug.panic("Failed to add a new road or assigning its nodes: {}\n", .{err}); }; + const intersections = self.getIntersectingRoads(self.allocator, temp, cur_node) catch |err| { + std.debug.panic("Intersection selection failure: {}\n", .{err}); + }; + // DEBUG TODO REMOVE + std.debug.print("Displaying intersection position and the intersected road:\n", .{}); + for (0..intersections.len) |i| { + const int = intersections[i]; + std.debug.print("Road ID={d} Pos: ({d}, {d})\n", .{int.road.id, int.pos.x, int.pos.y}); + } + + self.node_man.temp_node = if (self.auto_continue) cur_node else null; return; } @@ -196,4 +212,46 @@ pub const Simulator = struct { self.highlighted_entity = null; } + + fn getIntersectingRoads(self: *const Simulator, allocator: std.mem.Allocator, start: *const Node, end: *const Node) ![]st.IntersectionData { + var intersections: std.ArrayList(st.IntersectionData) = .empty; + var collision_point: rl.Vector2 = undefined; + + outer: for (self.road_man.roads.items) |road| { + if (!rl.checkCollisionLines(start.pos, end.pos, road.nodes[0].pos, road.nodes[1].pos, &collision_point)) + continue; + + const intersection = st.IntersectionData { + .road = road, + .pos = collision_point, + }; + + // We put a 0 here, just to satisfy the constructor function + const node: Node = .init(0, intersection.pos); + // If the newly acquired intersection node is within the snapping radius of already existing nodes, + // we don't add it to the list + for (intersections.items) |inter_collision| { + if (node.withinSnapRadius(inter_collision.pos)) continue :outer; + } + + // If there is an existing node that covers our position within its snapping radius, + // then such position will not be saved as intersection + if (self.node_man.getNodeIfExists(node.pos) != null) continue :outer; + + try intersections.append(allocator, intersection); + } + + const sorted_intersection = try intersections.toOwnedSlice(allocator); + std.sort.block(st.IntersectionData, sorted_intersection, start, ut.compareIntersections); + + return sorted_intersection; + } + + fn splitRoadsByIntersections(self: *Simulator, intersections: st.IntersectionData, start: *Node, end: *Node) void { + _ = intersections; // autofix + _ = end; // autofix + _ = start; // autofix + _ = self; // autofix + + } }; \ No newline at end of file