const std = @import("std"); const Object = @import("Object.zig"); const Property = @import("Property.zig").Property; const PropertyData = @import("Property.zig").Data; const defaultCommonProperties = @import("Property.zig").defaultCommonProperties; const basic_models = @import("basic_models.zig"); const line = @import("shape/line.zig"); const ellipse = @import("shape/ellipse.zig"); const broken = @import("shape/broken.zig"); const arc = @import("shape/arc.zig"); pub const Rect = basic_models.Rect; /// Создаёт объект с общими свойствами по умолчанию и специфичными для типа фигуры. pub fn createObject(allocator: std.mem.Allocator, shape_kind: Object.ShapeKind) !Object { var obj = try createWithCommonProperties(allocator, shape_kind); errdefer obj.deinit(allocator); switch (shape_kind) { .line => try line.appendDefaultShapeProperties(allocator, &obj), .ellipse => try ellipse.appendDefaultShapeProperties(allocator, &obj), .broken => try broken.appendDefaultShapeProperties(allocator, &obj), .arc => try arc.appendDefaultShapeProperties(allocator, &obj), } return obj; } fn createWithCommonProperties(allocator: std.mem.Allocator, shape_kind: Object.ShapeKind) !Object { var properties_list = std.ArrayList(Property).empty; errdefer properties_list.deinit(allocator); for (defaultCommonProperties) |prop| try properties_list.append(allocator, prop); return .{ .shape = shape_kind, .properties = properties_list, .children = std.ArrayList(Object).empty, }; } /// Проверяет, что объект имеет ожидаемый тип и все требуемые для этого типа свойства. pub fn ensure(obj: *const Object, expected_kind: Object.ShapeKind) !void { if (obj.shape != expected_kind) return error.WrongShapeKind; const tags = requiredTagsFor(expected_kind); for (tags) |tag| { if (obj.getProperty(tag) == null) return error.MissingRequiredProperty; } } fn requiredTagsFor(kind: Object.ShapeKind) []const std.meta.Tag(PropertyData) { return switch (kind) { .line => line.getRequiredTags(), .ellipse => ellipse.getRequiredTags(), .broken => broken.getRequiredTags(), .arc => arc.getRequiredTags(), }; } /// Локальные границы объекта (AABB в своих координатах). pub fn getLocalBounds(obj: *const Object) ?Rect { ensure(obj, obj.shape) catch return null; return switch (obj.shape) { .line => line.getLocalBounds(obj), .ellipse => ellipse.getLocalBounds(obj), .broken => broken.getLocalBounds(obj), .arc => arc.getLocalBounds(obj), }; } test "getLocalBounds" { const shape = @This(); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var line_obj = try shape.createObject(allocator, .line); defer line_obj.deinit(allocator); const line_bounds = getLocalBounds(&line_obj); try std.testing.expect(line_bounds != null); try std.testing.expect(line_bounds.?.x == 0); try std.testing.expect(line_bounds.?.y == 0); try std.testing.expect(line_bounds.?.w == 100); try std.testing.expect(line_bounds.?.h == 0); var ellipse_obj = try shape.createObject(allocator, .ellipse); defer ellipse_obj.deinit(allocator); const ellipse_bounds = getLocalBounds(&ellipse_obj); try std.testing.expect(ellipse_bounds != null); try std.testing.expect(ellipse_bounds.?.x == -50); try std.testing.expect(ellipse_bounds.?.y == -50); try std.testing.expect(ellipse_bounds.?.w == 100); try std.testing.expect(ellipse_bounds.?.h == 100); var broken_obj = try shape.createObject(allocator, .broken); defer broken_obj.deinit(allocator); const broken_bounds = getLocalBounds(&broken_obj); try std.testing.expect(broken_bounds != null); try std.testing.expect(broken_bounds.?.x == 0); try std.testing.expect(broken_bounds.?.y == 0); try std.testing.expect(broken_bounds.?.w == 80); try std.testing.expect(broken_bounds.?.h == 60); }