Случайное дерево объектов
This commit is contained in:
@@ -60,6 +60,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 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,3 +40,43 @@ pub fn addShape(self: *Document, parent: ?*Object, shape: Object.ShapeKind) !voi
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ pub fn createLine(allocator: std.mem.Allocator) !Object {
|
|||||||
pub fn createBrokenLine(allocator: std.mem.Allocator) !Object {
|
pub fn createBrokenLine(allocator: std.mem.Allocator) !Object {
|
||||||
var obj = try createWithCommonProperties(allocator, .broken);
|
var obj = try createWithCommonProperties(allocator, .broken);
|
||||||
errdefer obj.deinit(allocator);
|
errdefer obj.deinit(allocator);
|
||||||
var points = std.ArrayList(Point2).init(allocator);
|
var points = std.ArrayList(Point2).empty;
|
||||||
try points.appendSlice(allocator, &.{
|
try points.appendSlice(allocator, &.{
|
||||||
.{ .x = 0, .y = 0 },
|
.{ .x = 0, .y = 0 },
|
||||||
.{ .x = 80, .y = 0 },
|
.{ .x = 80, .y = 0 },
|
||||||
|
|||||||
@@ -22,16 +22,8 @@ pub fn guiFrame(ctx: *WindowContext) bool {
|
|||||||
{
|
{
|
||||||
left_panel.leftPanel(ctx);
|
left_panel.leftPanel(ctx);
|
||||||
|
|
||||||
var back = dvui.box(
|
|
||||||
@src(),
|
|
||||||
.{ .dir = .horizontal },
|
|
||||||
.{ .expand = .both, .padding = dvui.Rect.all(12), .background = true },
|
|
||||||
);
|
|
||||||
{
|
|
||||||
right_panel.rightPanel(ctx);
|
right_panel.rightPanel(ctx);
|
||||||
}
|
}
|
||||||
back.deinit();
|
|
||||||
}
|
|
||||||
content_row.deinit();
|
content_row.deinit();
|
||||||
}
|
}
|
||||||
root.deinit();
|
root.deinit();
|
||||||
|
|||||||
@@ -1,14 +1,112 @@
|
|||||||
const dvui = @import("dvui");
|
const dvui = @import("dvui");
|
||||||
const WindowContext = @import("../WindowContext.zig");
|
const WindowContext = @import("../WindowContext.zig");
|
||||||
|
const Document = @import("../models/Document.zig");
|
||||||
|
const Object = Document.Object;
|
||||||
|
|
||||||
|
const panel_gap: f32 = 12;
|
||||||
|
const panel_padding: f32 = 5;
|
||||||
|
const panel_radius: f32 = 24;
|
||||||
|
const fill_color = dvui.Color.black.opacity(0.2);
|
||||||
|
|
||||||
|
fn shapeLabel(shape: Object.ShapeKind) []const u8 {
|
||||||
|
return switch (shape) {
|
||||||
|
.line => "Line",
|
||||||
|
.ellipse => "Ellipse",
|
||||||
|
.arc => "Arc",
|
||||||
|
.broken => "Broken line",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn objectTreeRow(obj: *const Object, depth: u32, row_id: *usize) void {
|
||||||
|
const id = row_id.*;
|
||||||
|
row_id.* += 1;
|
||||||
|
const indent_px = depth * 18;
|
||||||
|
var row = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .horizontal },
|
||||||
|
.{ .padding = dvui.Rect{ .x = @floatFromInt(indent_px) }, .id_extra = id },
|
||||||
|
);
|
||||||
|
{
|
||||||
|
dvui.labelNoFmt(@src(), shapeLabel(obj.shape), .{}, .{ .id_extra = id });
|
||||||
|
}
|
||||||
|
row.deinit();
|
||||||
|
for (obj.children.items) |*child| {
|
||||||
|
objectTreeRow(child, depth + 1, row_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn objectTree(ctx: *WindowContext) void {
|
||||||
|
const active_doc = ctx.activeDocument();
|
||||||
|
if (active_doc) |open_doc| {
|
||||||
|
const doc = &open_doc.document;
|
||||||
|
if (doc.objects.items.len == 0) {
|
||||||
|
dvui.label(@src(), "No objects", .{}, .{});
|
||||||
|
} else {
|
||||||
|
var row_id: usize = 0;
|
||||||
|
for (doc.objects.items) |*obj| {
|
||||||
|
objectTreeRow(obj, 0, &row_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dvui.label(@src(), "No document", .{}, .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn leftPanel(ctx: *WindowContext) void {
|
pub fn leftPanel(ctx: *WindowContext) void {
|
||||||
|
var padding = dvui.Rect.all(panel_gap);
|
||||||
|
padding.w = 0;
|
||||||
var panel = dvui.box(
|
var panel = dvui.box(
|
||||||
@src(),
|
@src(),
|
||||||
.{ .dir = .vertical },
|
.{ .dir = .vertical },
|
||||||
.{ .expand = .vertical, .min_size_content = .{ .w = 200 }, .background = true },
|
.{
|
||||||
|
.expand = .vertical,
|
||||||
|
.min_size_content = .{ .w = 220 },
|
||||||
|
.background = true,
|
||||||
|
.padding = padding,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
dvui.label(@src(), "Tools", .{}, .{});
|
// Верхняя часть: дерево объектов
|
||||||
|
var tree_section = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .vertical },
|
||||||
|
.{
|
||||||
|
.expand = .both,
|
||||||
|
.padding = dvui.Rect.all(panel_padding),
|
||||||
|
.corner_radius = dvui.Rect.all(panel_radius),
|
||||||
|
.color_fill = fill_color,
|
||||||
|
.background = true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{
|
||||||
|
dvui.label(@src(), "Objects", .{}, .{});
|
||||||
|
var scroll = dvui.scrollArea(
|
||||||
|
@src(),
|
||||||
|
.{ .vertical = .auto },
|
||||||
|
.{ .expand = .vertical, .background = false },
|
||||||
|
);
|
||||||
|
{
|
||||||
|
objectTree(ctx);
|
||||||
|
}
|
||||||
|
scroll.deinit();
|
||||||
|
}
|
||||||
|
tree_section.deinit();
|
||||||
|
|
||||||
|
// Нижняя часть: настройки
|
||||||
|
var settings_section = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .vertical },
|
||||||
|
.{
|
||||||
|
.expand = .horizontal,
|
||||||
|
.margin = .{ .y = 5 },
|
||||||
|
.padding = dvui.Rect.all(panel_padding),
|
||||||
|
.corner_radius = dvui.Rect.all(panel_radius),
|
||||||
|
.color_fill = fill_color,
|
||||||
|
.background = true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{
|
||||||
|
dvui.label(@src(), "Settings", .{}, .{});
|
||||||
|
|
||||||
const active_doc = ctx.activeDocument();
|
const active_doc = ctx.activeDocument();
|
||||||
if (active_doc) |doc| {
|
if (active_doc) |doc| {
|
||||||
@@ -26,5 +124,7 @@ pub fn leftPanel(ctx: *WindowContext) void {
|
|||||||
dvui.label(@src(), "No document", .{}, .{});
|
dvui.label(@src(), "No document", .{}, .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
settings_section.deinit();
|
||||||
|
}
|
||||||
panel.deinit();
|
panel.deinit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ const canvas_view = @import("canvas_view.zig");
|
|||||||
|
|
||||||
pub fn rightPanel(ctx: *WindowContext) void {
|
pub fn rightPanel(ctx: *WindowContext) void {
|
||||||
const fill_color = dvui.Color.black.opacity(0.25);
|
const fill_color = dvui.Color.black.opacity(0.25);
|
||||||
|
var back = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .horizontal },
|
||||||
|
.{ .expand = .both, .padding = dvui.Rect.all(12), .background = true },
|
||||||
|
);
|
||||||
|
{
|
||||||
var panel = dvui.box(
|
var panel = dvui.box(
|
||||||
@src(),
|
@src(),
|
||||||
.{ .dir = .vertical },
|
.{ .dir = .vertical },
|
||||||
@@ -27,12 +33,26 @@ pub fn rightPanel(ctx: *WindowContext) void {
|
|||||||
}
|
}
|
||||||
panel.deinit();
|
panel.deinit();
|
||||||
}
|
}
|
||||||
|
back.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
fn noDocView(ctx: *WindowContext) void {
|
fn noDocView(ctx: *WindowContext) void {
|
||||||
var center = dvui.box(
|
var center = dvui.box(
|
||||||
@src(),
|
@src(),
|
||||||
.{ .dir = .vertical },
|
.{ .dir = .vertical },
|
||||||
.{ .expand = .both, .padding = dvui.Rect.all(20) },
|
.{
|
||||||
|
.expand = .both,
|
||||||
|
.padding = dvui.Rect.all(20),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{
|
||||||
|
var box = dvui.box(
|
||||||
|
@src(),
|
||||||
|
.{ .dir = .vertical },
|
||||||
|
.{
|
||||||
|
.gravity_x = 0.5,
|
||||||
|
.gravity_y = 0.5,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
dvui.label(@src(), "No document open", .{}, .{});
|
dvui.label(@src(), "No document open", .{}, .{});
|
||||||
@@ -42,5 +62,7 @@ fn noDocView(ctx: *WindowContext) void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
box.deinit();
|
||||||
|
}
|
||||||
center.deinit();
|
center.deinit();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user