const std = @import("std"); const Object = @import("../Object.zig"); const Property = @import("../Property.zig").Property; const PropertyData = @import("../Property.zig").Data; const defaultCommonProperties = Object.defaultCommonProperties; const basic_models = @import("../basic_models.zig"); const line = @import("line.zig"); const ellipse = @import("ellipse.zig"); const broken = @import("broken.zig"); const arc = @import("arc.zig"); pub const Rect = basic_models.Rectf; /// Создаёт объект с дефолтными общими и фигурными свойствами. 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 { 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 = try getLocalBounds(&line_obj); 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 = try getLocalBounds(&ellipse_obj); 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 = try getLocalBounds(&broken_obj); 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); }