Files
Zivro/src/models/shape.zig
Roman Pytkov f1a0e84272 Refactor: Перенёс логику создания фигур
Я перенёс логику создания объектов фигур из `Document.zig` и `Object.zig` в новый модуль `shape.zig`. Это упрощает добавление новых фигур и улучшает организацию кода.
2026-02-24 19:59:57 +03:00

100 lines
4.1 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}