const std = @import("std"); const basic_models = @import("basic_models.zig"); const Size = basic_models.Size; const Document = @This(); pub const Object = @import("Object.zig"); const shape = @import("shape/shape.zig"); size: Size, allocator: std.mem.Allocator, objects: std.ArrayList(Object), pub fn init(allocator: std.mem.Allocator, size: Size) Document { return .{ .size = size, .allocator = allocator, .objects = std.ArrayList(Object).empty, }; } pub fn deinit(self: *Document) void { for (self.objects.items) |*obj| obj.deinit(self.allocator); self.objects.deinit(self.allocator); } pub fn addObject(self: *Document, template: Object) !void { const obj = try template.clone(self.allocator); try self.objects.append(self.allocator, obj); } pub fn addShape(self: *Document, parent: ?*Object, shape_kind: Object.ShapeKind) !void { const obj = try shape.createObject(self.allocator, shape_kind); if (parent) |p| { try p.addChild(self.allocator, obj); } else { try self.addObject(obj); } } fn randomShapeKind(rng: std.Random) Object.ShapeKind { const shapes_implemented = [_]Object.ShapeKind{ .line, .ellipse, .broken }; return shapes_implemented[rng.intRangeLessThan(usize, 0, shapes_implemented.len)]; } /// Создаёт случайное количество фигур в документе (в т.ч. вложенных). /// Используются только реализованные типы: line, ellipse, broken. /// Ограничение max_total предотвращает экспоненциальный рост и переполнение. pub fn addRandomShapes(self: *Document, rng: std.Random) !void { const max_total: usize = 80; var total_count: usize = 0; const n_root = rng.intRangeLessThan(usize, 1, 5); for (0..n_root) |_| { if (total_count >= max_total) break; try self.addShape(null, randomShapeKind(rng)); total_count += 1; } var stack = std.ArrayList(*Object).empty; defer stack.deinit(self.allocator); for (self.objects.items) |*obj| { try stack.append(self.allocator, obj); } while (stack.pop()) |obj| { if (total_count >= max_total) continue; const n_children = rng.intRangeLessThan(usize, 0, 2); const base_len = obj.children.items.len; for (0..n_children) |_| { if (total_count >= max_total) break; try self.addShape(obj, randomShapeKind(rng)); total_count += 1; } // Пушим в стек только после всех append, чтобы не держать указатели при реаллокации obj.children for (obj.children.items[base_len..]) |*child| { try stack.append(self.allocator, child); } } }