const std = @import("std"); const basic_models = @import("basic_models.zig"); const Size_f = basic_models.Size_f; const Document = @This(); pub const Object = @import("Object.zig"); const shape = @import("shape/shape.zig"); size: Size_f, objects: std.ArrayList(Object), next_object_id: u64, pub fn init(size: Size_f) Document { return .{ .size = size, .objects = std.ArrayList(Object).empty, .next_object_id = 1, }; } pub fn deinit(self: *Document, allocator: std.mem.Allocator) void { for (self.objects.items) |*obj| obj.deinit(allocator); self.objects.deinit(allocator); } pub fn clone(self: *const Document, allocator: std.mem.Allocator) !Document { var objects_list = std.ArrayList(Object).empty; errdefer { for (objects_list.items) |*obj| obj.deinit(allocator); objects_list.deinit(allocator); } var next_id = self.next_object_id; for (self.objects.items) |obj| { try objects_list.append(allocator, try obj.clone(allocator, &next_id)); } return .{ .size = self.size, .objects = objects_list, .next_object_id = next_id, }; } pub fn addObject(self: *Document, allocator: std.mem.Allocator, template: Object) !void { const obj = try template.clone(allocator, &self.next_object_id); try self.objects.append(allocator, obj); } /// Добавляет объект в документ: как ребёнка родителя (если id найден), иначе в корень. pub fn addObjectUnderParentId(self: *Document, allocator: std.mem.Allocator, parent_id: ?u64, template: Object) !void { if (parent_id) |id| { if (self.findObjectById(id)) |parent| { try parent.addChild(allocator, template, &self.next_object_id); return; } } try self.addObject(allocator, template); } pub fn addShape(self: *Document, allocator: std.mem.Allocator, parent: ?*Object, shape_kind: Object.ShapeKind) !void { const obj = try shape.createObject(allocator, shape_kind); if (parent) |p| { try p.addChild(allocator, obj, &self.next_object_id); } else { try self.addObject(allocator, obj); } } /// Удаляет объект из документа (из корня или из детей родителя). Возвращает true, если объект был найден и удалён. pub fn removeObject(self: *Document, allocator: std.mem.Allocator, obj: *Object) bool { for (self.objects.items, 0..) |*item, i| { if (item == obj) { var removed = self.objects.orderedRemove(i); removed.deinit(allocator); return true; } if (removeFromChildren(allocator, &item.children, obj)) return true; } return false; } /// Удаляет объект по id. Возвращает true, если объект был найден и удалён. pub fn removeObjectById(self: *Document, allocator: std.mem.Allocator, obj_id: u64) bool { for (self.objects.items, 0..) |*item, i| { if (item.id == obj_id) { var removed = self.objects.orderedRemove(i); removed.deinit(allocator); return true; } if (removeFromChildrenById(allocator, &item.children, obj_id)) return true; } return false; } pub fn findObjectById(self: *Document, obj_id: u64) ?*Object { for (self.objects.items) |*item| { if (item.id == obj_id) return item; if (findInChildrenById(&item.children, obj_id)) |found| return found; } return null; } fn removeFromChildren(allocator: std.mem.Allocator, children: *std.ArrayList(Object), obj: *Object) bool { for (children.items, 0..) |*item, i| { if (item == obj) { var removed = children.orderedRemove(i); removed.deinit(allocator); return true; } if (removeFromChildren(allocator, &item.children, obj)) return true; } return false; } fn removeFromChildrenById(allocator: std.mem.Allocator, children: *std.ArrayList(Object), obj_id: u64) bool { for (children.items, 0..) |*item, i| { if (item.id == obj_id) { var removed = children.orderedRemove(i); removed.deinit(allocator); return true; } if (removeFromChildrenById(allocator, &item.children, obj_id)) return true; } return false; } fn findInChildrenById(children: *std.ArrayList(Object), obj_id: u64) ?*Object { for (children.items) |*item| { if (item.id == obj_id) return item; if (findInChildrenById(&item.children, obj_id)) |found| return found; } return null; }