Определение позиции дочернего объекта
This commit is contained in:
@@ -13,19 +13,6 @@ const Object = Document.Object;
|
||||
const DrawContext = pipeline.DrawContext;
|
||||
const Transform = pipeline.Transform;
|
||||
|
||||
fn getLocalTransform(obj: *const Object) Transform {
|
||||
const pos = if (obj.getProperty(.position)) |p| p.position else basic_models.Point2_f{ .x = 0, .y = 0 };
|
||||
const angle = if (obj.getProperty(.angle)) |p| p.angle else 0;
|
||||
const scale = if (obj.getProperty(.scale)) |p| p.scale else basic_models.Scale2_f{ .scale_x = 1, .scale_y = 1 };
|
||||
const opacity = if (obj.getProperty(.opacity)) |p| p.opacity else 1.0;
|
||||
return .{
|
||||
.position = pos,
|
||||
.angle = angle,
|
||||
.scale = scale,
|
||||
.opacity = opacity,
|
||||
};
|
||||
}
|
||||
|
||||
fn isVisible(obj: *const Object) bool {
|
||||
return if (obj.getProperty(.visible)) |p| p.visible else true;
|
||||
}
|
||||
@@ -37,7 +24,7 @@ fn drawObject(
|
||||
allocator: std.mem.Allocator,
|
||||
) !void {
|
||||
if (!isVisible(obj)) return;
|
||||
const local = getLocalTransform(obj);
|
||||
const local = Transform.init(obj);
|
||||
const world = Transform.compose(parent_transform, local);
|
||||
ctx.setTransform(world);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const dvui = @import("dvui");
|
||||
const basic_models = @import("../../models/basic_models.zig");
|
||||
const Document = @import("../../models/Document.zig");
|
||||
const Point2_f = basic_models.Point2_f;
|
||||
const Point2_i = basic_models.Point2_i;
|
||||
const Scale2_f = basic_models.Scale2_f;
|
||||
@@ -14,6 +15,19 @@ pub const Transform = struct {
|
||||
scale: Scale2_f = .{},
|
||||
opacity: f32 = 1.0,
|
||||
|
||||
pub fn init(obj: *const Document.Object) Transform {
|
||||
const pos = if (obj.getProperty(.position)) |p| p.position else Point2_f{ .x = 0, .y = 0 };
|
||||
const angle = if (obj.getProperty(.angle)) |p| p.angle else 0;
|
||||
const scale = if (obj.getProperty(.scale)) |p| p.scale else Scale2_f{ .scale_x = 1, .scale_y = 1 };
|
||||
const opacity = if (obj.getProperty(.opacity)) |p| p.opacity else 1.0;
|
||||
return .{
|
||||
.position = pos,
|
||||
.angle = angle,
|
||||
.scale = scale,
|
||||
.opacity = opacity,
|
||||
};
|
||||
}
|
||||
|
||||
/// Композиция: world = parent * local.
|
||||
pub fn compose(parent: Transform, local: Transform) Transform {
|
||||
const cos_a = std.math.cos(parent.angle);
|
||||
@@ -36,6 +50,20 @@ pub const Transform = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Мировые -> локальные для заданного трансформа.
|
||||
pub fn worldToLocalTransform(t: Transform, wx: f32, wy: f32) Point2_f {
|
||||
const dx = wx - t.position.x;
|
||||
const dy = wy - t.position.y;
|
||||
const cos_a = std.math.cos(-t.angle);
|
||||
const sin_a = std.math.sin(-t.angle);
|
||||
const sx = if (t.scale.scale_x != 0) t.scale.scale_x else 1.0;
|
||||
const sy = if (t.scale.scale_y != 0) t.scale.scale_y else 1.0;
|
||||
return .{
|
||||
.x = (dx * cos_a - dy * sin_a) / sx,
|
||||
.y = (dx * sin_a + dy * cos_a) / sy,
|
||||
};
|
||||
}
|
||||
|
||||
/// Конвейер отрисовки: локальные координаты -> трансформ -> буфер.
|
||||
pub const DrawContext = struct {
|
||||
pixels: []Color.PMA,
|
||||
@@ -100,17 +128,7 @@ pub const DrawContext = struct {
|
||||
|
||||
/// Мировые -> локальные.
|
||||
pub fn worldToLocal(self: *const DrawContext, wx: f32, wy: f32) Point2_f {
|
||||
const t = &self.transform;
|
||||
const dx = wx - t.position.x;
|
||||
const dy = wy - t.position.y;
|
||||
const cos_a = std.math.cos(-t.angle);
|
||||
const sin_a = std.math.sin(-t.angle);
|
||||
const sx = if (t.scale.scale_x != 0) t.scale.scale_x else 1.0;
|
||||
const sy = if (t.scale.scale_y != 0) t.scale.scale_y else 1.0;
|
||||
return .{
|
||||
.x = (dx * cos_a - dy * sin_a) / sx,
|
||||
.y = (dx * sin_a + dy * cos_a) / sy,
|
||||
};
|
||||
return worldToLocalTransform(self.transform, wx, wy);
|
||||
}
|
||||
|
||||
/// Смешивает цвет в пикселе буфера с учётом opacity трансформа. В replace_mode просто перезаписывает пиксель.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const Point2_f = @import("../models/basic_models.zig").Point2_f;
|
||||
const basic_models = @import("../models/basic_models.zig");
|
||||
const Point2_f = basic_models.Point2_f;
|
||||
const Canvas = @import("../Canvas.zig");
|
||||
const Document = @import("../models/Document.zig");
|
||||
const pipeline = @import("../render/cpu/pipeline.zig");
|
||||
const Transform = pipeline.Transform;
|
||||
|
||||
pub const ToolContext = struct {
|
||||
canvas: *Canvas,
|
||||
@@ -8,11 +11,41 @@ pub const ToolContext = struct {
|
||||
selected_object_id: ?u64,
|
||||
|
||||
pub fn addObject(self: *const ToolContext, template: Document.Object) !void {
|
||||
try self.canvas.document.addObjectUnderParentId(self.selected_object_id, template);
|
||||
var obj = template;
|
||||
const local_pos = self.computeLocalPosition();
|
||||
try obj.setProperty(self.canvas.document.allocator, .{ .data = .{ .position = local_pos } });
|
||||
try self.canvas.document.addObjectUnderParentId(self.selected_object_id, obj);
|
||||
self.canvas.requestRedraw();
|
||||
}
|
||||
|
||||
fn computeLocalPosition(self: *const ToolContext) Point2_f {
|
||||
if (self.selected_object_id) |parent_id| {
|
||||
if (findWorldTransformById(self.canvas.document, parent_id)) |parent_world| {
|
||||
return pipeline.worldToLocalTransform(parent_world, self.document_point.x, self.document_point.y);
|
||||
}
|
||||
}
|
||||
return self.document_point;
|
||||
}
|
||||
};
|
||||
|
||||
fn findWorldTransformById(doc: *Document, target_id: u64) ?Transform {
|
||||
const identity = Transform{};
|
||||
for (doc.objects.items) |*obj| {
|
||||
if (findWorldTransformInTree(obj, identity, target_id)) |t| return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn findWorldTransformInTree(obj: *const Document.Object, parent: Transform, target_id: u64) ?Transform {
|
||||
const local = Transform.init(obj);
|
||||
const world = Transform.compose(parent, local);
|
||||
if (obj.id == target_id) return world;
|
||||
for (obj.children.items) |*child| {
|
||||
if (findWorldTransformInTree(child, world, target_id)) |t| return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const Tool = struct {
|
||||
onCanvasClick: *const fn (*const ToolContext) anyerror!void,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ fn onCanvasClick(ctx: *const Tool.ToolContext) !void {
|
||||
const canvas = ctx.canvas;
|
||||
var obj = shape.createObject(canvas.document.allocator, .arc) catch return;
|
||||
defer obj.deinit(canvas.allocator);
|
||||
try obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = ctx.document_point } });
|
||||
try ctx.addObject(obj);
|
||||
}
|
||||
pub const tool = Tool.Tool{ .onCanvasClick = onCanvasClick };
|
||||
|
||||
@@ -5,7 +5,6 @@ fn onCanvasClick(ctx: *const Tool.ToolContext) !void {
|
||||
const canvas = ctx.canvas;
|
||||
var obj = shape.createObject(canvas.document.allocator, .broken) catch return;
|
||||
defer obj.deinit(canvas.allocator);
|
||||
try obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = ctx.document_point } });
|
||||
try ctx.addObject(obj);
|
||||
}
|
||||
pub const tool = Tool.Tool{ .onCanvasClick = onCanvasClick };
|
||||
|
||||
@@ -5,7 +5,6 @@ fn onCanvasClick(ctx: *const Tool.ToolContext) !void {
|
||||
const canvas = ctx.canvas;
|
||||
var obj = shape.createObject(canvas.document.allocator, .ellipse) catch return;
|
||||
defer obj.deinit(canvas.allocator);
|
||||
try obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = ctx.document_point } });
|
||||
try ctx.addObject(obj);
|
||||
}
|
||||
pub const tool = Tool.Tool{ .onCanvasClick = onCanvasClick };
|
||||
|
||||
@@ -7,7 +7,6 @@ fn onCanvasClick(ctx: *const Tool.ToolContext) !void {
|
||||
const canvas = ctx.canvas;
|
||||
var obj = shape.createObject(canvas.document.allocator, .line) catch return;
|
||||
defer obj.deinit(canvas.allocator);
|
||||
try obj.setProperty(canvas.document.allocator, .{ .data = .{ .position = ctx.document_point } });
|
||||
try ctx.addObject(obj);
|
||||
}
|
||||
pub const tool = Tool.Tool{ .onCanvasClick = onCanvasClick };
|
||||
|
||||
@@ -192,8 +192,8 @@ fn handleCanvasZoom(canvas: *Canvas, scroll: anytype) void {
|
||||
};
|
||||
const doc_pt = canvas.contentPointToDocument(content_pt, natural_scale);
|
||||
|
||||
// canvas.addZoom(y / 1000);
|
||||
canvas.multZoom(1 + y / 3000);
|
||||
canvas.addZoom(y / 1000);
|
||||
// canvas.multZoom(1 + y / 3000);
|
||||
canvas.requestRedraw();
|
||||
|
||||
// Сдвигаем viewport так, чтобы точка под курсором (даже вне холста) не уезжала
|
||||
|
||||
Reference in New Issue
Block a user