Рефакторинг ещё...
This commit is contained in:
@@ -3,6 +3,7 @@ const Canvas = @import("Canvas.zig");
|
|||||||
const CpuRenderEngine = @import("render/CpuRenderEngine.zig");
|
const CpuRenderEngine = @import("render/CpuRenderEngine.zig");
|
||||||
const RenderEngine = @import("render/RenderEngine.zig").RenderEngine;
|
const RenderEngine = @import("render/RenderEngine.zig").RenderEngine;
|
||||||
const Document = @import("models/Document.zig");
|
const Document = @import("models/Document.zig");
|
||||||
|
const random_document = @import("models/random_document.zig");
|
||||||
const basic_models = @import("models/basic_models.zig");
|
const basic_models = @import("models/basic_models.zig");
|
||||||
|
|
||||||
const WindowContext = @This();
|
const WindowContext = @This();
|
||||||
@@ -60,7 +61,7 @@ pub fn addNewDocument(self: *WindowContext) !void {
|
|||||||
const ptr = try self.allocator.create(OpenDocument);
|
const ptr = try self.allocator.create(OpenDocument);
|
||||||
errdefer self.allocator.destroy(ptr);
|
errdefer self.allocator.destroy(ptr);
|
||||||
OpenDocument.init(self.allocator, ptr);
|
OpenDocument.init(self.allocator, ptr);
|
||||||
try ptr.document.addRandomShapes(std.crypto.random);
|
try random_document.addRandomShapes(&ptr.document, std.crypto.random);
|
||||||
try self.documents.append(self.allocator, ptr);
|
try self.documents.append(self.allocator, ptr);
|
||||||
self.active_document_index = self.documents.items.len - 1;
|
self.active_document_index = self.documents.items.len - 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,43 +36,3 @@ pub fn addShape(self: *Document, parent: ?*Object, shape_kind: Object.ShapeKind)
|
|||||||
try self.addObject(obj);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,24 @@ pub const ShapeKind = enum {
|
|||||||
broken,
|
broken,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const default_common_data = [_]PropertyData{
|
||||||
|
.{ .position = .{ .x = 0, .y = 0 } },
|
||||||
|
.{ .angle = 0 },
|
||||||
|
.{ .scale = .{ .scale_x = 1, .scale_y = 1 } },
|
||||||
|
.{ .visible = true },
|
||||||
|
.{ .opacity = 1.0 },
|
||||||
|
.{ .locked = false },
|
||||||
|
.{ .stroke_rgba = 0x000000FF },
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const defaultCommonProperties: [default_common_data.len]Property = blk: {
|
||||||
|
var result: [default_common_data.len]Property = undefined;
|
||||||
|
for (default_common_data, &result) |d, *p| {
|
||||||
|
p.* = .{ .data = d };
|
||||||
|
}
|
||||||
|
break :blk result;
|
||||||
|
};
|
||||||
|
|
||||||
shape: ShapeKind,
|
shape: ShapeKind,
|
||||||
properties: std.ArrayList(Property),
|
properties: std.ArrayList(Property),
|
||||||
children: std.ArrayList(Object),
|
children: std.ArrayList(Object),
|
||||||
@@ -29,7 +47,8 @@ pub fn setProperty(self: *Object, allocator: std.mem.Allocator, prop: Property)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try self.properties.append(allocator, prop);
|
std.debug.print("Property not found: {s}\n", .{@tagName(prop.data)});
|
||||||
|
return error.PropertyNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addChild(self: *Object, allocator: std.mem.Allocator, template: Object) !void {
|
pub fn addChild(self: *Object, allocator: std.mem.Allocator, template: Object) !void {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ pub const Data = union(enum) {
|
|||||||
|
|
||||||
fill_rgba: u32,
|
fill_rgba: u32,
|
||||||
stroke_rgba: u32,
|
stroke_rgba: u32,
|
||||||
|
|
||||||
|
thickness: f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Property = struct {
|
pub const Property = struct {
|
||||||
@@ -45,33 +47,3 @@ pub const Property = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const default_common_data = [_]Data{
|
|
||||||
.{ .position = .{ .x = 0, .y = 0 } },
|
|
||||||
.{ .angle = 0 },
|
|
||||||
.{ .scale = .{ .scale_x = 1, .scale_y = 1 } },
|
|
||||||
.{ .visible = true },
|
|
||||||
.{ .opacity = 1.0 },
|
|
||||||
.{ .locked = false },
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const defaultCommonProperties: [default_common_data.len]Property = blk: {
|
|
||||||
var result: [default_common_data.len]Property = undefined;
|
|
||||||
for (default_common_data, &result) |d, *p| {
|
|
||||||
p.* = .{ .data = d };
|
|
||||||
}
|
|
||||||
break :blk result;
|
|
||||||
};
|
|
||||||
|
|
||||||
test "Property wrapper and Data" {
|
|
||||||
const p = Property{ .data = .{ .opacity = 0.5 } };
|
|
||||||
try std.testing.expect(p.data == .opacity);
|
|
||||||
try std.testing.expect(p.data.opacity == 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "common properties" {
|
|
||||||
try std.testing.expect(defaultCommonProperties[0].data == .position);
|
|
||||||
try std.testing.expect(defaultCommonProperties[0].data.position.x == 0);
|
|
||||||
try std.testing.expect(defaultCommonProperties[3].data == .visible);
|
|
||||||
try std.testing.expect(defaultCommonProperties[3].data.visible == true);
|
|
||||||
}
|
|
||||||
|
|||||||
128
src/models/random_document.zig
Normal file
128
src/models/random_document.zig
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Document = @import("Document.zig");
|
||||||
|
const Object = Document.Object;
|
||||||
|
const shape = @import("shape/shape.zig");
|
||||||
|
const basic_models = @import("basic_models.zig");
|
||||||
|
const Size = basic_models.Size;
|
||||||
|
const Point2 = basic_models.Point2;
|
||||||
|
const Scale2 = basic_models.Scale2;
|
||||||
|
const Radii = basic_models.Radii;
|
||||||
|
|
||||||
|
fn randFloat(rng: std.Random, min: f32, max: f32) f32 {
|
||||||
|
return min + (max - min) * rng.float(f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn randRgba(rng: std.Random) u32 {
|
||||||
|
const r = rng.int(u8);
|
||||||
|
const g = rng.int(u8);
|
||||||
|
const b = rng.int(u8);
|
||||||
|
const a: u8 = @intCast(rng.intRangeLessThan(usize, 128, 256));
|
||||||
|
return r | (@as(u32, g) << 8) | (@as(u32, b) << 16) | (@as(u32, a) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Случайно заполняет все доступные свойства объекта; позиция — в пределах документа.
|
||||||
|
fn randomizeObjectProperties(allocator: std.mem.Allocator, doc_size: *const Size, obj: *Object, rng: std.Random) !void {
|
||||||
|
const margin: f32 = 8;
|
||||||
|
const max_x = @max(0, doc_size.width - margin);
|
||||||
|
const max_y = @max(0, doc_size.height - margin);
|
||||||
|
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{
|
||||||
|
.position = .{
|
||||||
|
.x = randFloat(rng, margin, if (max_x > margin) max_x else margin),
|
||||||
|
.y = randFloat(rng, margin, if (max_y > margin) max_y else margin),
|
||||||
|
},
|
||||||
|
} });
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .angle = randFloat(rng, 0, 2 * std.math.pi) } });
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{
|
||||||
|
.scale = .{
|
||||||
|
.scale_x = randFloat(rng, 0.25, 2.0),
|
||||||
|
.scale_y = randFloat(rng, 0.25, 2.0),
|
||||||
|
},
|
||||||
|
} });
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .visible = rng.boolean() } });
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .opacity = randFloat(rng, 0.3, 1.0) } });
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .locked = rng.boolean() } });
|
||||||
|
|
||||||
|
const stroke = randRgba(rng);
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .stroke_rgba = stroke } });
|
||||||
|
obj.setProperty(allocator, .{ .data = .{ .fill_rgba = randRgba(rng) } }) catch {};
|
||||||
|
|
||||||
|
switch (obj.shape) {
|
||||||
|
.line => {
|
||||||
|
const len = randFloat(rng, 20, @min(doc_size.width, doc_size.height) * 0.5);
|
||||||
|
const angle = randFloat(rng, 0, 2 * std.math.pi);
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{
|
||||||
|
.end_point = .{
|
||||||
|
.x = std.math.cos(angle) * len,
|
||||||
|
.y = std.math.sin(angle) * len,
|
||||||
|
},
|
||||||
|
} });
|
||||||
|
},
|
||||||
|
.ellipse => {
|
||||||
|
const max_r = @min(120, @min(doc_size.width / 4, doc_size.height / 4));
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{
|
||||||
|
.radii = .{
|
||||||
|
.x = randFloat(rng, 8, @max(8, max_r)),
|
||||||
|
.y = randFloat(rng, 8, @max(8, max_r)),
|
||||||
|
},
|
||||||
|
} });
|
||||||
|
},
|
||||||
|
.broken => {
|
||||||
|
var points = std.ArrayList(Point2).empty;
|
||||||
|
const n = rng.intRangeLessThan(usize, 2, 9);
|
||||||
|
var x: f32 = 0;
|
||||||
|
var y: f32 = 0;
|
||||||
|
for (0..n) |_| {
|
||||||
|
try points.append(allocator, .{ .x = x, .y = y });
|
||||||
|
x += randFloat(rng, -40, 80);
|
||||||
|
y += randFloat(rng, -30, 60);
|
||||||
|
}
|
||||||
|
try obj.setProperty(allocator, .{ .data = .{ .points = points } });
|
||||||
|
},
|
||||||
|
.arc => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Создаёт в документе случайное количество фигур (в т.ч. вложенных).
|
||||||
|
/// У каждой фигуры все доступные свойства задаются случайно; позиция — в пределах документа.
|
||||||
|
/// Реализованные типы: line, ellipse, broken.
|
||||||
|
pub fn addRandomShapes(doc: *Document, rng: std.Random) !void {
|
||||||
|
const max_total: usize = 80;
|
||||||
|
var total_count: usize = 0;
|
||||||
|
const allocator = doc.allocator;
|
||||||
|
|
||||||
|
const n_root = rng.intRangeLessThan(usize, 1, 5);
|
||||||
|
for (0..n_root) |_| {
|
||||||
|
if (total_count >= max_total) break;
|
||||||
|
var obj = try shape.createObject(allocator, randomShapeKind(rng));
|
||||||
|
try randomizeObjectProperties(allocator, &doc.size, &obj, rng);
|
||||||
|
try doc.addObject(obj);
|
||||||
|
total_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stack = std.ArrayList(*Object).empty;
|
||||||
|
defer stack.deinit(allocator);
|
||||||
|
for (doc.objects.items) |*obj| {
|
||||||
|
try stack.append(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;
|
||||||
|
var child = try shape.createObject(allocator, randomShapeKind(rng));
|
||||||
|
try randomizeObjectProperties(allocator, &doc.size, &child, rng);
|
||||||
|
try obj.addChild(allocator, child);
|
||||||
|
total_count += 1;
|
||||||
|
}
|
||||||
|
for (obj.children.items[base_len..]) |*child| {
|
||||||
|
try stack.append(allocator, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,9 @@ pub const default_points = [_]Point2{
|
|||||||
|
|
||||||
/// Теги обязательных свойств (у ломаной нет const default_shape_properties, только default_points).
|
/// Теги обязательных свойств (у ломаной нет const default_shape_properties, только default_points).
|
||||||
pub fn getRequiredTags() []const std.meta.Tag(PropertyData) {
|
pub fn getRequiredTags() []const std.meta.Tag(PropertyData) {
|
||||||
return &[_]std.meta.Tag(PropertyData){.points};
|
return &[_]std.meta.Tag(PropertyData){
|
||||||
|
.points,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Добавляет к объекту свойства по умолчанию для ломаной (points из default_points).
|
/// Добавляет к объекту свойства по умолчанию для ломаной (points из default_points).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
const Object = @import("../Object.zig");
|
const Object = @import("../Object.zig");
|
||||||
const Property = @import("../Property.zig").Property;
|
const Property = @import("../Property.zig").Property;
|
||||||
const PropertyData = @import("../Property.zig").Data;
|
const PropertyData = @import("../Property.zig").Data;
|
||||||
const defaultCommonProperties = @import("../Property.zig").defaultCommonProperties;
|
const defaultCommonProperties = Object.defaultCommonProperties;
|
||||||
const basic_models = @import("../basic_models.zig");
|
const basic_models = @import("../basic_models.zig");
|
||||||
const line = @import("line.zig");
|
const line = @import("line.zig");
|
||||||
const ellipse = @import("ellipse.zig");
|
const ellipse = @import("ellipse.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user