diff --git a/.vscode/launch.json b/.vscode/launch.json index c5017d3..1b3e5f1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,6 +16,7 @@ "request": "launch", "program": "${workspaceFolder}/zig-out/bin/Zivro", "cwd": "${workspaceFolder}", + "MIMode": "gdb", "preLaunchTask": "zig: build" }, ] diff --git a/src/Canvas.zig b/src/Canvas.zig index b2df2c6..4915a7b 100644 --- a/src/Canvas.zig +++ b/src/Canvas.zig @@ -123,8 +123,7 @@ pub fn getZoomedImageSize(self: Canvas) Rect_i { }; } -/// Возвращает координаты точки контента в координатах документа. Всегда возвращает точку, -/// даже если она за пределами документа +/// Точка контента -> координаты документа. pub fn contentPointToDocument(self: Canvas, content_point: dvui.Point, natural_scale: f32) Point2_f { const img = self.getZoomedImageSize(); const px_x = content_point.x * natural_scale - @as(f32, @floatFromInt(img.x)); @@ -135,7 +134,7 @@ pub fn contentPointToDocument(self: Canvas, content_point: dvui.Point, natural_s }; } -/// Возвращает true, если точка контента лежит внутри холста (документа). +/// Точка контента внутри холста. pub fn isContentPointOnDocument(self: Canvas, content_point: dvui.Point, natural_scale: f32) bool { const img = self.getZoomedImageSize(); const left_n = @as(f32, @floatFromInt(img.x)) / natural_scale; diff --git a/src/models/Object.zig b/src/models/Object.zig index 0c6b56f..e73cf85 100644 --- a/src/models/Object.zig +++ b/src/models/Object.zig @@ -17,7 +17,7 @@ const default_common_data = [_]PropertyData{ .{ .visible = true }, .{ .opacity = 1.0 }, .{ .locked = false }, - .{ .stroke_rgba = 0x000000FF }, // 0xRRGGBBAA: чёрный, полная непрозрачность + .{ .stroke_rgba = 0x000000FF }, // чёрный, полная непрозрачность .{ .thickness = 2.0 }, }; diff --git a/src/models/Property.zig b/src/models/Property.zig index 20ff542..dace7db 100644 --- a/src/models/Property.zig +++ b/src/models/Property.zig @@ -19,9 +19,9 @@ pub const Data = union(enum) { points: std.ArrayList(Point2_f), - /// Цвет заливки: u32 в формате 0xRRGGBBAA (R старший байт, A младший). + /// Цвет заливки, 0xRRGGBBAA. fill_rgba: u32, - /// Цвет обводки: u32 в формате 0xRRGGBBAA (R старший байт, A младший). + /// Цвет обводки, 0xRRGGBBAA. stroke_rgba: u32, thickness: f32, diff --git a/src/models/basic_models.zig b/src/models/basic_models.zig index 92e753a..a7ff739 100644 --- a/src/models/basic_models.zig +++ b/src/models/basic_models.zig @@ -20,7 +20,6 @@ pub const Point2_f = struct { y: f32 = 0, }; -/// Целочисленная точка (например, координаты в буфере пикселей). pub const Point2_i = struct { x: i32 = 0, y: i32 = 0, @@ -36,7 +35,6 @@ pub const Scale2_f = struct { scale_y: f32 = 1, }; -/// Прямоугольник в координатах документа (f32), например локальные границы объекта. pub const Rect_f = struct { x: f32 = 0, y: f32 = 0, diff --git a/src/models/shape/arc.zig b/src/models/shape/arc.zig index 12bc5a6..63e2274 100644 --- a/src/models/shape/arc.zig +++ b/src/models/shape/arc.zig @@ -4,19 +4,19 @@ const PropertyData = @import("../Property.zig").Data; const Rect_f = @import("../basic_models.zig").Rect_f; const shape_mod = @import("shape.zig"); -/// Теги обязательных свойств (заглушка: arc пока не реализован). +/// Теги обязательных свойств (arc не реализован). pub fn getRequiredTags() []const std.meta.Tag(PropertyData) { return &[_]std.meta.Tag(PropertyData){}; } -/// Добавляет свойства по умолчанию для дуги (заглушка). +/// Добавляет свойства по умолчанию для дуги. pub fn appendDefaultShapeProperties(allocator: std.mem.Allocator, obj: *Object) !void { _ = allocator; _ = obj; return error.ArcNotImplemented; } -/// Локальные границы дуги (заглушка: пока не реализовано). +/// Локальные границы дуги (не реализовано). pub fn getLocalBounds(obj: *const Object) !Rect_f { try shape_mod.ensure(obj, .arc); return error.ArcNotImplemented; diff --git a/src/models/shape/broken.zig b/src/models/shape/broken.zig index 983a4f8..382e162 100644 --- a/src/models/shape/broken.zig +++ b/src/models/shape/broken.zig @@ -6,28 +6,28 @@ const Point2_f = @import("../basic_models.zig").Point2_f; const Rect_f = @import("../basic_models.zig").Rect_f; const shape_mod = @import("shape.zig"); -/// Точки ломаной по умолчанию (для создания). +/// Точки ломаной по умолчанию. pub const default_points = [_]Point2_f{ .{ .x = 0, .y = 0 }, .{ .x = 80, .y = 0 }, .{ .x = 80, .y = 60 }, }; -/// Теги обязательных свойств (у ломаной нет const default_shape_properties, только default_points). +/// Теги обязательных свойств. pub fn getRequiredTags() []const std.meta.Tag(PropertyData) { return &[_]std.meta.Tag(PropertyData){ .points, }; } -/// Добавляет к объекту свойства по умолчанию для ломаной (points из default_points). +/// Добавляет к объекту свойства по умолчанию для ломаной. pub fn appendDefaultShapeProperties(allocator: std.mem.Allocator, obj: *Object) !void { var points = std.ArrayList(Point2_f).empty; try points.appendSlice(allocator, &default_points); try obj.properties.append(allocator, .{ .data = .{ .points = points } }); } -/// Локальные границы ломаной: AABB по всем точкам. +/// Локальные границы: AABB по точкам. pub fn getLocalBounds(obj: *const Object) !Rect_f { try shape_mod.ensure(obj, .broken); const p = obj.getProperty(.points).?; diff --git a/src/models/shape/ellipse.zig b/src/models/shape/ellipse.zig index 61a2daf..055319d 100644 --- a/src/models/shape/ellipse.zig +++ b/src/models/shape/ellipse.zig @@ -5,12 +5,12 @@ const PropertyData = @import("../Property.zig").Data; const Rect_f = @import("../basic_models.zig").Rect_f; const shape_mod = @import("shape.zig"); -/// Свойства фигуры по умолчанию (для создания и проверки типа). Теги для ensure выводятся отсюда. +/// Свойства фигуры по умолчанию. pub const default_shape_properties = [_]Property{ .{ .data = .{ .radii = .{ .x = 50, .y = 50 } } }, }; -/// Теги обязательных свойств (выводятся из default_shape_properties). +/// Теги обязательных свойств. pub fn getRequiredTags() []const std.meta.Tag(PropertyData) { return &([_]std.meta.Tag(PropertyData){std.meta.activeTag(default_shape_properties[0].data)}); } diff --git a/src/models/shape/line.zig b/src/models/shape/line.zig index c3ebf0d..9c3323f 100644 --- a/src/models/shape/line.zig +++ b/src/models/shape/line.zig @@ -5,12 +5,12 @@ const PropertyData = @import("../Property.zig").Data; const Rect_f = @import("../basic_models.zig").Rect_f; const shape_mod = @import("shape.zig"); -/// Свойства фигуры по умолчанию (для создания и проверки типа). Теги для ensure выводятся отсюда. +/// Свойства фигуры по умолчанию. pub const default_shape_properties = [_]Property{ .{ .data = .{ .end_point = .{ .x = 100, .y = 0 } } }, }; -/// Теги обязательных свойств (выводятся из default_shape_properties). +/// Теги обязательных свойств. pub fn getRequiredTags() []const std.meta.Tag(PropertyData) { return &([_]std.meta.Tag(PropertyData){std.meta.activeTag(default_shape_properties[0].data)}); } @@ -20,7 +20,7 @@ pub fn appendDefaultShapeProperties(allocator: std.mem.Allocator, obj: *Object) for (default_shape_properties) |prop| try obj.properties.append(allocator, prop); } -/// Локальные границы линии: от (0,0) до end_point. +/// Локальные границы: от (0,0) до end_point. pub fn getLocalBounds(obj: *const Object) !Rect_f { try shape_mod.ensure(obj, .line); const ep = obj.getProperty(.end_point).?; diff --git a/src/models/shape/shape.zig b/src/models/shape/shape.zig index 3bb50b1..c612315 100644 --- a/src/models/shape/shape.zig +++ b/src/models/shape/shape.zig @@ -11,7 +11,7 @@ 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); @@ -35,7 +35,7 @@ fn createWithCommonProperties(allocator: std.mem.Allocator, shape_kind: Object.S }; } -/// Проверяет, что объект имеет ожидаемый тип и все требуемые для этого типа свойства. +/// Проверяет тип объекта и наличие обязательных свойств. pub fn ensure(obj: *const Object, expected_kind: Object.ShapeKind) !void { if (obj.shape != expected_kind) return error.WrongShapeKind; const tags = requiredTagsFor(expected_kind); @@ -53,7 +53,7 @@ fn requiredTagsFor(kind: Object.ShapeKind) []const std.meta.Tag(PropertyData) { }; } -/// Локальные границы объекта (AABB в своих координатах). +/// Локальные границы (AABB). pub fn getLocalBounds(obj: *const Object) !Rect { return switch (obj.shape) { .line => line.getLocalBounds(obj), diff --git a/src/random_document.zig b/src/random_document.zig index f98834f..9320108 100644 --- a/src/random_document.zig +++ b/src/random_document.zig @@ -12,7 +12,6 @@ fn randFloat(rng: std.Random, min: f32, max: f32) f32 { return min + (max - min) * rng.float(f32); } -/// Цвет в u32, порядок 0xRRGGBBAA (R старший, A младший; чёрный = 0x000000FF). fn randRgba(rng: std.Random) u32 { const r = rng.int(u8); const g = rng.int(u8); @@ -26,7 +25,6 @@ fn randomShapeKind(rng: std.Random) Object.ShapeKind { return shapes_implemented[rng.intRangeLessThan(usize, 0, shapes_implemented.len)]; } -/// Случайно заполняет все доступные свойства объекта; позиция — в пределах документа. fn randomizeObjectProperties(allocator: std.mem.Allocator, doc_size: *const Size_f, obj: *Object, rng: std.Random) !void { const margin: f32 = 8; const max_x = @max(0, doc_size.w - margin); @@ -91,9 +89,7 @@ fn randomizeObjectProperties(allocator: std.mem.Allocator, doc_size: *const Size } } -/// Создаёт в документе случайное количество фигур (в т.ч. вложенных). -/// У каждой фигуры все доступные свойства задаются случайно; позиция — в пределах документа. -/// Реализованные типы: line, ellipse, broken. +/// Создаёт в документе случайные фигуры (line, ellipse, broken). pub fn addRandomShapes(doc: *Document, rng: std.Random) !void { const max_total: usize = 80; var total_count: usize = 0; diff --git a/src/render/CpuRenderEngine.zig b/src/render/CpuRenderEngine.zig index a195c6e..5ba537c 100644 --- a/src/render/CpuRenderEngine.zig +++ b/src/render/CpuRenderEngine.zig @@ -174,7 +174,7 @@ pub fn renderEngine(self: *CpuRenderEngine) RenderEngine { return .{ .cpu = self }; } -/// Растеризует документ: фон + рекурсивная отрисовка фигур через конвейер (трансформ, прозрачность, наложение). +/// Растеризует документ в текстуру (фон + фигуры через конвейер). pub fn renderDocument(self: *CpuRenderEngine, document: *const Document, canvas_size: Size_i, visible_rect: Rect_i) !?dvui.Texture { const width = visible_rect.w; const height = visible_rect.h; diff --git a/src/render/RenderEngine.zig b/src/render/RenderEngine.zig index 5bfc29f..12c1df4 100644 --- a/src/render/RenderEngine.zig +++ b/src/render/RenderEngine.zig @@ -18,7 +18,7 @@ pub const RenderEngine = union(enum) { }; } - /// Растеризует документ в текстуру (размер и видимая область в пикселях холста). + /// Растеризует документ в текстуру. pub fn render(self: RenderEngine, document: *const Document, canvas_size: basic_models.Size_i, visible_rect: basic_models.Rect_i) !?dvui.Texture { return switch (self) { .cpu => |cpu_r| cpu_r.renderDocument(document, canvas_size, visible_rect), diff --git a/src/render/cpu/arc.zig b/src/render/cpu/arc.zig index e15a25b..b931619 100644 --- a/src/render/cpu/arc.zig +++ b/src/render/cpu/arc.zig @@ -4,5 +4,5 @@ const DrawContext = pipeline.DrawContext; const Object = Document.Object; -/// Рисует дугу (заглушка: пока не реализовано). +/// Дуга (не реализовано). pub fn draw(_: *DrawContext, _: *const Object) void {} diff --git a/src/render/cpu/broken.zig b/src/render/cpu/broken.zig index 70491cb..d2661db 100644 --- a/src/render/cpu/broken.zig +++ b/src/render/cpu/broken.zig @@ -9,7 +9,7 @@ const Object = Document.Object; const default_stroke: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 }; const default_thickness: f32 = 2.0; -/// Рисует ломаную по точкам в локальных координатах. Обводка по stroke_rgba. +/// Ломаная по точкам, обводка stroke_rgba. pub fn draw(ctx: *DrawContext, obj: *const Object) void { const p_prop = obj.getProperty(.points) orelse return; const pts = p_prop.points.items; diff --git a/src/render/cpu/draw.zig b/src/render/cpu/draw.zig index 7b2a506..bac0845 100644 --- a/src/render/cpu/draw.zig +++ b/src/render/cpu/draw.zig @@ -48,7 +48,7 @@ fn drawObject(ctx: *DrawContext, obj: *const Object, parent_transform: Transform } } -/// Рекурсивно рисует документ в буфер: сначала корневые объекты по порядку, затем их потомков (каждый следующий поверх предыдущего). +/// Рекурсивно рисует документ в буфер (объекты и потомки по порядку). pub fn drawDocument( pixels: []@import("dvui").Color.PMA, buf_width: u32, @@ -68,8 +68,6 @@ pub fn drawDocument( .scale_x = scale_x, .scale_y = scale_y, }; - // вывести visible_rect - std.debug.print("visible_rect: {{ x: {}, y: {}, w: {}, h: {} }}\n", .{ visible_rect.x, visible_rect.y, visible_rect.w, visible_rect.h }); const identity = Transform{}; for (document.objects.items) |*obj| { drawObject(&ctx, obj, identity); diff --git a/src/render/cpu/ellipse.zig b/src/render/cpu/ellipse.zig index 937a535..fcb12be 100644 --- a/src/render/cpu/ellipse.zig +++ b/src/render/cpu/ellipse.zig @@ -7,7 +7,7 @@ const Color = @import("dvui").Color; const Object = Document.Object; const default_stroke: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 }; -/// Рисует эллипс: центр (0,0), полуоси radii. Растеризация в координатах буфера (обводка кольцом). +/// Эллипс с центром (0,0) и полуосями radii (обводка). pub fn draw(ctx: *DrawContext, obj: *const Object) void { const r_prop = obj.getProperty(.radii) orelse return; const rx = r_prop.radii.x; diff --git a/src/render/cpu/line.zig b/src/render/cpu/line.zig index 05a5f59..e1a6cf7 100644 --- a/src/render/cpu/line.zig +++ b/src/render/cpu/line.zig @@ -7,7 +7,7 @@ const Object = Document.Object; const default_stroke: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 }; const default_thickness: f32 = 2.0; -/// Рисует линию в локальных координатах: от (0,0) до end_point. Растеризация в координатах буфера (без пробелов при зуме). +/// Линия от (0,0) до end_point. pub fn draw(ctx: *DrawContext, obj: *const Object) void { const ep_prop = obj.getProperty(.end_point) orelse return; const end_x = ep_prop.end_point.x; @@ -17,7 +17,7 @@ pub fn draw(ctx: *DrawContext, obj: *const Object) void { drawLine(ctx, 0, 0, end_x, end_y, stroke, thickness); } -/// Линия по локальным координатам фигуры: переводит концы в буфер и рисует в пикселях буфера. +/// Рисует отрезок по локальным концам (перевод в буфер внутри). pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Color.PMA, thickness: f32) void { const w0 = ctx.localToWorld(x0, y0); const w1 = ctx.localToWorld(x1, y1); @@ -26,7 +26,6 @@ pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Co drawLineInBuffer(ctx, b0.x, b0.y, b1.x, b1.y, color, thickness); } -/// Брезенхем в координатах буфера; пиксели вне [0, buf_width) x [0, buf_height) пропускаются. fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, color: Color.PMA, thickness: f32) void { const bw = ctx.buf_width; const bh = ctx.buf_height; diff --git a/src/render/cpu/pipeline.zig b/src/render/cpu/pipeline.zig index 44817a4..71a8fd7 100644 --- a/src/render/cpu/pipeline.zig +++ b/src/render/cpu/pipeline.zig @@ -7,14 +7,14 @@ const Scale2_f = basic_models.Scale2_f; const Rect_i = basic_models.Rect_i; const Color = dvui.Color; -/// Трансформ объекта в мировых координатах документа (позиция, угол, масштаб, непрозрачность). +/// Трансформ объекта: позиция, угол, масштаб, непрозрачность. pub const Transform = struct { position: Point2_f = .{}, angle: f32 = 0, scale: Scale2_f = .{}, opacity: f32 = 1.0, - /// Композиция: мировой трансформ = parent * local (local в пространстве родителя). + /// Композиция: world = parent * local. pub fn compose(parent: Transform, local: Transform) Transform { const cos_a = std.math.cos(parent.angle); const sin_a = std.math.sin(parent.angle); @@ -36,8 +36,7 @@ pub const Transform = struct { } }; -/// Единый конвейер: принимает позицию в локальных координатах фигуры и цвет пикселя, -/// применяет трансформ (вращение, масштаб, перенос) и непрозрачность, накладывает на буфер. +/// Конвейер отрисовки: локальные координаты -> трансформ -> буфер. pub const DrawContext = struct { pixels: []Color.PMA, buf_width: u32, @@ -51,7 +50,7 @@ pub const DrawContext = struct { self.transform = t; } - /// Локальные координаты фигуры -> мировые (документ). + /// Локальные -> мировые. pub fn localToWorld(self: *const DrawContext, local_x: f32, local_y: f32) Point2_f { const t = &self.transform; const cos_a = std.math.cos(t.angle); @@ -62,7 +61,7 @@ pub const DrawContext = struct { }; } - /// Мировые координаты документа -> координаты в буфере (float; могут быть вне [0, buf_w] x [0, buf_h]). + /// Мировые -> буфер (float). pub fn worldToBufferF(self: *const DrawContext, wx: f32, wy: f32) Point2_f { const canvas_x = wx * self.scale_x; const canvas_y = wy * self.scale_y; @@ -74,7 +73,7 @@ pub const DrawContext = struct { }; } - /// Мировые координаты документа -> целочисленные координаты в буфере (округление до ближайшего пикселя). + /// Мировые -> буфер (целые). pub fn worldToBuffer(self: *const DrawContext, wx: f32, wy: f32) Point2_i { const b = self.worldToBufferF(wx, wy); return .{ @@ -83,7 +82,7 @@ pub const DrawContext = struct { }; } - /// Координаты буфера -> мировые (документ). scale_x/scale_y не должны быть 0. + /// Буфер -> мировые. pub fn bufferToWorld(self: *const DrawContext, buf_x: f32, buf_y: f32) Point2_f { const vx = @as(f32, @floatFromInt(self.visible_rect.x)); const vy = @as(f32, @floatFromInt(self.visible_rect.y)); @@ -97,7 +96,7 @@ pub const DrawContext = struct { }; } - /// Мировые координаты -> локальные фигуры (обратное к localToWorld). + /// Мировые -> локальные. pub fn worldToLocal(self: *const DrawContext, wx: f32, wy: f32) Point2_f { const t = &self.transform; const dx = wx - t.position.x; @@ -112,14 +111,13 @@ pub const DrawContext = struct { }; } - /// Смешивает цвет в пикселе буфера (bx, by). color уже в PMA; opacity трансформа применяется к альфе и к RGB. + /// Смешивает цвет в пикселе буфера с учётом opacity трансформа. pub fn blendPixelAtBuffer(self: *DrawContext, bx: u32, by: u32, color: Color.PMA) void { if (bx >= self.buf_width or by >= self.buf_height) return; const t = &self.transform; const idx = by * self.buf_width + bx; const dst = &self.pixels[idx]; const a = @as(f32, @floatFromInt(color.a)) / 255.0 * t.opacity; - // PMA: color.r/g/b уже помножены на color.a; при opacity только масштабируем их на t.opacity const src_r = @as(f32, @floatFromInt(color.r)) * t.opacity; const src_g = @as(f32, @floatFromInt(color.g)) * t.opacity; const src_b = @as(f32, @floatFromInt(color.b)) * t.opacity; @@ -130,7 +128,7 @@ pub const DrawContext = struct { dst.a = @intFromFloat(std.math.clamp(a * 255 + inv_a * @as(f32, @floatFromInt(dst.a)), 0, 255)); } - /// Записывает пиксель в локальных координатах фигуры с учётом трансформа и прозрачности (PMA blend). + /// Пиксель в локальных координатах (трансформ + PMA). pub fn blendPixelLocal(self: *DrawContext, local_x: f32, local_y: f32, color: Color.PMA) void { const w = self.localToWorld(local_x, local_y); const b = self.worldToBufferF(w.x, w.y); @@ -143,7 +141,7 @@ pub const DrawContext = struct { } }; -/// u32 в порядке 0xRRGGBBAA: старший байт R, младший A (чёрный = 0x000000FF). +/// Конвертирует u32 0xRRGGBBAA в Color.PMA. pub fn rgbaToPma(rgba: u32) Color.PMA { const r: u8 = @intCast((rgba >> 24) & 0xFF); const g: u8 = @intCast((rgba >> 16) & 0xFF);