Толщина линии
This commit is contained in:
@@ -50,7 +50,7 @@ fn randomizeObjectProperties(allocator: std.mem.Allocator, doc_size: *const Size
|
|||||||
const stroke = randRgba(rng);
|
const stroke = randRgba(rng);
|
||||||
try obj.setProperty(allocator, .{ .data = .{ .stroke_rgba = stroke } });
|
try obj.setProperty(allocator, .{ .data = .{ .stroke_rgba = stroke } });
|
||||||
obj.setProperty(allocator, .{ .data = .{ .fill_rgba = randRgba(rng) } }) catch {};
|
obj.setProperty(allocator, .{ .data = .{ .fill_rgba = randRgba(rng) } }) catch {};
|
||||||
const thickness = randFloat(rng, max_x * 0.01, max_x * 0.1);
|
const thickness = randFloat(rng, max_x * 0.001, max_x * 0.01);
|
||||||
try obj.setProperty(allocator, .{ .data = .{ .thickness = thickness } });
|
try obj.setProperty(allocator, .{ .data = .{ .thickness = thickness } });
|
||||||
|
|
||||||
switch (obj.shape) {
|
switch (obj.shape) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const std = @import("std");
|
||||||
const Document = @import("../../models/Document.zig");
|
const Document = @import("../../models/Document.zig");
|
||||||
const pipeline = @import("pipeline.zig");
|
const pipeline = @import("pipeline.zig");
|
||||||
const DrawContext = pipeline.DrawContext;
|
const DrawContext = pipeline.DrawContext;
|
||||||
@@ -23,30 +24,84 @@ pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Co
|
|||||||
const w1 = ctx.localToWorld(x1, y1);
|
const w1 = ctx.localToWorld(x1, y1);
|
||||||
const b0 = ctx.worldToBuffer(w0.x, w0.y);
|
const b0 = ctx.worldToBuffer(w0.x, w0.y);
|
||||||
const b1 = ctx.worldToBuffer(w1.x, w1.y);
|
const b1 = ctx.worldToBuffer(w1.x, w1.y);
|
||||||
drawLineInBuffer(ctx, b0.x, b0.y, b1.x, b1.y, color, thickness);
|
const t = &ctx.transform;
|
||||||
|
const scale = @sqrt(t.scale.scale_x * ctx.scale_x * t.scale.scale_y * ctx.scale_y);
|
||||||
|
const thickness_px: u32 = @max(@as(u32, 1), @as(u32, @intFromFloat(std.math.round(thickness * scale))));
|
||||||
|
drawLineInBuffer(ctx, b0.x, b0.y, b1.x, b1.y, color, thickness_px);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, color: Color.PMA, thickness: f32) void {
|
/// Точка (px, py) лежит внутри/на круге с центром (cx, cy) и радиусом в квадрате r_sq.
|
||||||
|
fn insideCircle(px: i32, py: i32, cx: i32, cy: i32, r_sq: i32) bool {
|
||||||
|
const dx = px - cx;
|
||||||
|
const dy = py - cy;
|
||||||
|
return dx * dx + dy * dy <= r_sq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Заливает круг в буфере (для скруглённых концов отрезка).
|
||||||
|
fn fillCircleAtBuffer(ctx: *DrawContext, cx: i32, cy: i32, radius: i32, color: Color.PMA) void {
|
||||||
|
const bw: i32 = @intCast(ctx.buf_width);
|
||||||
|
const bh: i32 = @intCast(ctx.buf_height);
|
||||||
|
const r_sq = radius * radius;
|
||||||
|
var dy: i32 = -radius;
|
||||||
|
while (dy <= radius) : (dy += 1) {
|
||||||
|
var dx: i32 = -radius;
|
||||||
|
while (dx <= radius) : (dx += 1) {
|
||||||
|
const px = cx + dx;
|
||||||
|
const py = cy + dy;
|
||||||
|
if (!insideCircle(px, py, cx, cy, r_sq)) continue;
|
||||||
|
if (px >= 0 and px < bw and py >= 0 and py < bh) {
|
||||||
|
ctx.blendPixelAtBuffer(@intCast(px), @intCast(py), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, color: Color.PMA, thickness_px: u32) void {
|
||||||
const bw = ctx.buf_width;
|
const bw = ctx.buf_width;
|
||||||
const bh = ctx.buf_height;
|
const bh = ctx.buf_height;
|
||||||
const dx: i32 = @intCast(@abs(bx1 - bx0));
|
const dx: i32 = @intCast(@abs(bx1 - bx0));
|
||||||
const dy: i32 = -@as(i32, @intCast(@abs(by1 - by0)));
|
const dy: i32 = @intCast(@abs(by1 - by0));
|
||||||
const sx: i32 = if (bx0 < bx1) 1 else -1;
|
const sx: i32 = if (bx0 < bx1) 1 else -1;
|
||||||
const sy: i32 = if (by0 < by1) 1 else -1;
|
const sy: i32 = if (by0 < by1) 1 else -1;
|
||||||
var err = dx + dy;
|
var err = dx - dy;
|
||||||
var x = bx0;
|
var x = bx0;
|
||||||
var y = by0;
|
var y = by0;
|
||||||
|
const half: i32 = @intCast(thickness_px / 2);
|
||||||
|
const half_sq = half * half;
|
||||||
|
const thickness_i: i32 = @intCast(thickness_px);
|
||||||
|
|
||||||
_ = thickness;
|
// Брезенхем + штамп по доминирующей оси: горизонтальная линия — вертикальный штамп, вертикальная — горизонтальный.
|
||||||
|
const more_horizontal = dx >= dy;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (x >= 0 and x < bw and y >= 0 and y < bh) {
|
const near_start = @abs(x - bx0) + @abs(y - by0) <= thickness_i;
|
||||||
ctx.blendPixelAtBuffer(@intCast(x), @intCast(y), color);
|
const near_end = @abs(x - bx1) + @abs(y - by1) <= thickness_i;
|
||||||
|
|
||||||
|
if (more_horizontal) {
|
||||||
|
var yo: i32 = -half;
|
||||||
|
while (yo <= half) : (yo += 1) {
|
||||||
|
const py = y + yo;
|
||||||
|
if (near_start and insideCircle(x, py, bx0, by0, half_sq)) continue;
|
||||||
|
if (near_end and insideCircle(x, py, bx1, by1, half_sq)) continue;
|
||||||
|
if (x >= 0 and x < @as(i32, @intCast(bw)) and py >= 0 and py < @as(i32, @intCast(bh))) {
|
||||||
|
ctx.blendPixelAtBuffer(@intCast(x), @intCast(py), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var xo: i32 = -half;
|
||||||
|
while (xo <= half) : (xo += 1) {
|
||||||
|
const px = x + xo;
|
||||||
|
if (near_start and insideCircle(px, y, bx0, by0, half_sq)) continue;
|
||||||
|
if (near_end and insideCircle(px, y, bx1, by1, half_sq)) continue;
|
||||||
|
if (px >= 0 and px < @as(i32, @intCast(bw)) and y >= 0 and y < @as(i32, @intCast(bh))) {
|
||||||
|
ctx.blendPixelAtBuffer(@intCast(px), @intCast(y), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (x == bx1 and y == by1) break;
|
if (x == bx1 and y == by1) break;
|
||||||
const e2 = 2 * err;
|
const e2 = 2 * err;
|
||||||
if (e2 >= dy) {
|
if (e2 >= -dy) {
|
||||||
err += dy;
|
err -= dy;
|
||||||
x += sx;
|
x += sx;
|
||||||
}
|
}
|
||||||
if (e2 <= dx) {
|
if (e2 <= dx) {
|
||||||
@@ -54,4 +109,10 @@ fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, c
|
|||||||
y += sy;
|
y += sy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Скруглённые концы: круги в крайних точках.
|
||||||
|
if (half > 0) {
|
||||||
|
fillCircleAtBuffer(ctx, bx0, by0, half, color);
|
||||||
|
fillCircleAtBuffer(ctx, bx1, by1, half, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user