const std = @import("std"); const rl = @import("raylib"); const st = @import("../common/structures.zig"); const e = @import("../errors.zig"); const Road = @import("road.zig").Road; const Node = @import("node.zig").Node; pub const RoadManager = struct { next_id: usize, roads: std.ArrayList(*Road), pub fn init() RoadManager { return .{ .next_id = 0, .roads = .empty, }; } /// Deinitialises every road (pointer) and then the list itself pub fn deinit(self: *RoadManager, allocator: std.mem.Allocator) void { for (self.roads.items) |road| { road.deinit(allocator); } self.roads.deinit(allocator); } /// Draws all the roads in the list, sends the information ahead whether the road drawn should be highlighted pub fn draw(self: *const RoadManager, highlighted_road: ?*Road) void { for (self.roads.items) |road| { const is_highlighted = if (highlighted_road) |h_road| road == h_road else false; road.draw(is_highlighted); } } /// Function which creates the road object, its pointer, adds it to the list /// and then also references that same road to the bounding nodes pub fn addRoad(self: *RoadManager, allocator: std.mem.Allocator, start: *Node, end: *Node) !void { const road: Road = .init(self.getNextID(), start, end); const road_ptr = try allocator.create(Road); road_ptr.* = road; try self.roads.append(allocator, road_ptr); const ref = self.roads.items[self.roads.items.len - 1]; try start.referenceRoad(allocator, ref); try end.referenceRoad(allocator, ref); } /// Returns the id, and increases it by one; used for generating ID's for new entities fn getNextID(self: *RoadManager) usize { const id = self.next_id; self.next_id += 1; return id; } /// Deinits all the roads, clears them but not deiniting the list itself; also resets the next ID var pub fn clear(self: *RoadManager, allocator: std.mem.Allocator) void { for (self.roads.items) |road| { road.deinit(allocator); } self.roads.clearRetainingCapacity(); self.next_id = 0; } /// Removes the references of the road, from the nodes that bound that road /// /// Then it deinitialises the road and removes it from the list /// /// Will return an error if the road itself is not present in the list pub fn deleteRoad(self: *RoadManager, allocator: std.mem.Allocator, road_to_delete: *Road) !void { // unreference the road from its bounding functions road_to_delete.unreferenceNodes() catch |err| { std.debug.panic("Failed to unreference the road from its nodes: {}\n", .{err}); }; for (0..self.roads.items.len) |i| { if (self.roads.items[i] != road_to_delete) continue; road_to_delete.deinit(allocator); _ = self.roads.swapRemove(i); return; } return e.Entity.NotFound; } /// Returns if pos is pointing at a road, or null if it isn't at any pub fn getHighlightedRoad(self: *const RoadManager, pos: Vector2) ?*Road { for (self.roads.items) |road| { if (road.isHighlighted(pos)) return road; } 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; const expect = std.testing.expect; test "id tracking" { var gpa: std.heap.DebugAllocator(.{}) = .init; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var road_man: RoadManager = .init(); defer road_man.deinit(allocator); const n = 5; const start: Node = .init(0, .{.x = 0, .y = 0}); const start_ptr = try allocator.create(Node); start_ptr.* = start; const end: Node = .init(1, .{.x = 100, .y = 100}); const end_ptr = try allocator.create(Node); end_ptr.* = end; defer { start_ptr.deinit(allocator); end_ptr.deinit(allocator); allocator.destroy(start_ptr); allocator.destroy(end_ptr); } for (0..n) |_| { try road_man.addRoad(allocator, start_ptr, end_ptr); } try expect(road_man.next_id == n); try expect(road_man.roads.items.len == n); } // TODO tests // force resize pointer test // add, remove road // destroy road and then verify the nodes do not have a pointer to it