Skip to documentation
SLOP

tiny.gui.paint.synth

Reference tiny.gui paint synth

Defined in paint.

API (3)

Actions

Public operations.

No direct callersNo direct callspaintsynth
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallstest sourcelib.gui.src.paint.synthtest: covered spans the terminal voca...private sourcelib.gui.src.paint.textappendLookupOrderedFaceSegmentprivate sourcelib.gui.src.paint.textsynthCodepointprivate sourcelib.gui.src.paint.synthboxSegmentspaint.synthcovered
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.synthtest: box drawing tee composes three ...test sourcelib.gui.src.paint.synthtest: braille dot count follows the b...test sourcelib.gui.src.paint.synthtest: chevron and star emit stepped f...test sourcelib.gui.src.paint.synthtest: circled operators draw a ring w...test sourcelib.gui.src.paint.synthtest: full block covers the cell and ...+3 moreprivate sourcelib.gui.src.paint.synthboxSegmentsprivate sourcelib.gui.src.paint.synthdrawBlockprivate sourcelib.gui.src.paint.synthdrawBoxprivate sourcelib.gui.src.paint.synthdrawBrailleprivate sourcelib.gui.src.paint.synthdrawChevron+3 morepaint.synthdraw
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.synthtest: notdef draws a closed box witho...private sourcelib.gui.src.paint.textdrawSyntheticOrNotdefprivate sourcelib.gui.src.paint.synthfillprivate sourcelib.gui.src.paint.synthsnapprivate sourcelib.gui.src.paint.synthstrokeThicknesspaint.synthdrawNotdef
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/gui/src/paint/root.zig:11

zig
pub const synth = @import("synth.zig");

Source: lib/gui/src/paint/synth.zig

zig
const std = @import("std");const gui = @import("../root.zig");const Color = gui.model.UiColor;const Rect = gui.layout.Rect;pub fn covered(codepoint: u21) bool {    return switch (codepoint) {        0x2500...0x257F => boxSegments(codepoint) != null,        0x2580...0x259F => true,        0x2800...0x28FF => true,        0x2295...0x2299 => true,        0x22EF, 0x276F, 0x2726 => true,        else => false,    };}pub fn draw(writer: anytype, codepoint: u21, cell: Rect, color: Color) bool {    switch (codepoint) {        0x2500...0x257F => {            const segments = boxSegments(codepoint) orelse return false;            drawBox(writer, segments, cell, color);            return true;        },        0x2580...0x259F => {            drawBlock(writer, codepoint, cell, color);            return true;        },        0x2800...0x28FF => {            drawBraille(writer, codepoint, cell, color);            return true;        },        0x2295...0x2299 => {            drawCircled(writer, codepoint, cell, color);            return true;        },        0x22EF => {            drawMidlineEllipsis(writer, cell, color);            return true;        },        0x276F => {            drawChevron(writer, cell, color);            return true;        },        0x2726 => {            drawStar(writer, cell, color);            return true;        },        else => return false,    }}pub fn drawNotdef(writer: anytype, cell: Rect, color: Color) void {    const thickness = strokeThickness(cell);    const x = snap(cell.x + thickness);    const y = snap(cell.y + thickness);    const width = @max(thickness, snap(cell.width - thickness * 2));    const height = @max(thickness, snap(cell.height - thickness * 2));    fill(writer, .{ .x = x, .y = y, .width = width, .height = thickness }, color);    fill(writer, .{ .x = x, .y = y + height - thickness, .width = width, .height = thickness }, color);    fill(writer, .{ .x = x, .y = y, .width = thickness, .height = height }, color);    fill(writer, .{ .x = x + width - thickness, .y = y, .width = thickness, .height = height }, color);}fn drawCircled(writer: anytype, codepoint: u21, cell: Rect, color: Color) void {    const t = strokeThickness(cell);    const side = @min(cell.width, cell.height * 0.72);    const x0 = snap(cell.x + (cell.width - side) / 2);    const y0 = snap(cell.y + (cell.height - side) / 2);    const x1 = x0 + side;    const y1 = y0 + side;    const corner = side / 4;    fill(writer, .{ .x = x0 + corner, .y = y0, .width = side - corner * 2, .height = t }, color);    fill(writer, .{ .x = x0 + corner, .y = y1 - t, .width = side - corner * 2, .height = t }, color);    fill(writer, .{ .x = x0, .y = y0 + corner, .width = t, .height = side - corner * 2 }, color);    fill(writer, .{ .x = x1 - t, .y = y0 + corner, .width = t, .height = side - corner * 2 }, color);    fill(writer, .{ .x = x0 + corner / 2, .y = y0 + corner / 2, .width = t, .height = t }, color);    fill(writer, .{ .x = x1 - corner / 2 - t, .y = y0 + corner / 2, .width = t, .height = t }, color);    fill(writer, .{ .x = x0 + corner / 2, .y = y1 - corner / 2 - t, .width = t, .height = t }, color);    fill(writer, .{ .x = x1 - corner / 2 - t, .y = y1 - corner / 2 - t, .width = t, .height = t }, color);    const cx = x0 + side / 2;    const cy = y0 + side / 2;    const reach = side / 2 - corner / 2 - t;    switch (codepoint) {        0x2295 => {            fill(writer, .{ .x = cx - reach, .y = cy - t / 2, .width = reach * 2, .height = t }, color);            fill(writer, .{ .x = cx - t / 2, .y = cy - reach, .width = t, .height = reach * 2 }, color);        },        0x2296 => fill(writer, .{ .x = cx - reach, .y = cy - t / 2, .width = reach * 2, .height = t }, color),        0x2297 => {            drawDiagonal(writer, cx, cy, reach, t, color, .both);        },        0x2298 => drawDiagonal(writer, cx, cy, reach, t, color, .rising),        0x2299 => fill(writer, .{ .x = cx - t, .y = cy - t, .width = t * 2, .height = t * 2 }, color),        else => unreachable,    }}const DiagonalArms = enum { rising, both };fn drawDiagonal(writer: anytype, cx: f32, cy: f32, reach: f32, t: f32, color: Color, arms: DiagonalArms) void {    const steps: usize = 3;    const step = reach / @as(f32, @floatFromInt(steps));    for (0..steps) |index| {        const offset = step * @as(f32, @floatFromInt(index)) + step / 2;        fill(writer, .{ .x = cx + offset - t / 2, .y = cy - offset - t / 2, .width = t, .height = t }, color);        fill(writer, .{ .x = cx - offset - t / 2, .y = cy + offset - t / 2, .width = t, .height = t }, color);        if (arms == .both) {            fill(writer, .{ .x = cx - offset - t / 2, .y = cy - offset - t / 2, .width = t, .height = t }, color);            fill(writer, .{ .x = cx + offset - t / 2, .y = cy + offset - t / 2, .width = t, .height = t }, color);        }    }}fn drawMidlineEllipsis(writer: anytype, cell: Rect, color: Color) void {    const t = strokeThickness(cell);    const cy = cell.y + (cell.height - t) / 2;    const spacing = cell.width / 4;    for (0..3) |index| {        const offset: f32 = @floatFromInt(index + 1);        fill(writer, .{ .x = cell.x + spacing * offset - t / 2, .y = cy, .width = t, .height = t }, color);    }}const BoxSegments = struct {    left: bool = false,    right: bool = false,    up: bool = false,    down: bool = false,};fn boxSegments(codepoint: u21) ?BoxSegments {    return switch (codepoint) {        0x2500, 0x2501 => .{ .left = true, .right = true },        0x2502, 0x2503 => .{ .up = true, .down = true },        0x250C, 0x250F, 0x256D => .{ .right = true, .down = true },        0x2510, 0x2513, 0x256E => .{ .left = true, .down = true },        0x2514, 0x2517, 0x2570 => .{ .right = true, .up = true },        0x2518, 0x251B, 0x256F => .{ .left = true, .up = true },        0x251C => .{ .up = true, .down = true, .right = true },        0x2524 => .{ .up = true, .down = true, .left = true },        0x252C => .{ .left = true, .right = true, .down = true },        0x2534 => .{ .left = true, .right = true, .up = true },        0x253C => .{ .left = true, .right = true, .up = true, .down = true },        0x2574 => .{ .left = true },        0x2575 => .{ .up = true },        0x2576 => .{ .right = true },        0x2577 => .{ .down = true },        else => null,    };}fn drawBox(writer: anytype, segments: BoxSegments, cell: Rect, color: Color) void {    const t = strokeThickness(cell);    const cx = snap(cell.x + (cell.width - t) / 2);    const cy = snap(cell.y + (cell.height - t) / 2);    if (segments.left) fill(writer, .{ .x = cell.x, .y = cy, .width = cx - cell.x + t, .height = t }, color);    if (segments.right) fill(writer, .{ .x = cx, .y = cy, .width = cell.x + cell.width - cx, .height = t }, color);    if (segments.up) fill(writer, .{ .x = cx, .y = cell.y, .width = t, .height = cy - cell.y + t }, color);    if (segments.down) fill(writer, .{ .x = cx, .y = cy, .width = t, .height = cell.y + cell.height - cy }, color);}fn drawBlock(writer: anytype, codepoint: u21, cell: Rect, color: Color) void {    switch (codepoint) {        0x2580 => fillFraction(writer, cell, color, 0, 0, 8, 4),        0x2581...0x2588 => {            const eighths: f32 = @floatFromInt(codepoint - 0x2580);            const height = cell.height * eighths / 8;            fill(writer, .{ .x = cell.x, .y = cell.y + cell.height - height, .width = cell.width, .height = height }, color);        },        0x2589...0x258F => {            const eighths: f32 = @floatFromInt(8 - (codepoint - 0x2588));            fill(writer, .{ .x = cell.x, .y = cell.y, .width = cell.width * eighths / 8, .height = cell.height }, color);        },        0x2590 => fillFraction(writer, cell, color, 4, 0, 8, 8),        0x2591 => fill(writer, cell, shaded(color, 64)),        0x2592 => fill(writer, cell, shaded(color, 128)),        0x2593 => fill(writer, cell, shaded(color, 192)),        0x2594 => fill(writer, .{ .x = cell.x, .y = cell.y, .width = cell.width, .height = cell.height / 8 }, color),        0x2595 => fill(writer, .{ .x = cell.x + cell.width * 7 / 8, .y = cell.y, .width = cell.width / 8, .height = cell.height }, color),        0x2596...0x259F => {            const quads = quadrants(codepoint);            if (quads.upper_left) fillFraction(writer, cell, color, 0, 0, 4, 4);            if (quads.upper_right) fillFraction(writer, cell, color, 4, 0, 8, 4);            if (quads.lower_left) fillFraction(writer, cell, color, 0, 4, 4, 8);            if (quads.lower_right) fillFraction(writer, cell, color, 4, 4, 8, 8);        },        else => unreachable,    }}const Quadrants = struct {    upper_left: bool = false,    upper_right: bool = false,    lower_left: bool = false,    lower_right: bool = false,};fn quadrants(codepoint: u21) Quadrants {    return switch (codepoint) {        0x2596 => .{ .lower_left = true },        0x2597 => .{ .lower_right = true },        0x2598 => .{ .upper_left = true },        0x2599 => .{ .upper_left = true, .lower_left = true, .lower_right = true },        0x259A => .{ .upper_left = true, .lower_right = true },        0x259B => .{ .upper_left = true, .upper_right = true, .lower_left = true },        0x259C => .{ .upper_left = true, .upper_right = true, .lower_right = true },        0x259D => .{ .upper_right = true },        0x259E => .{ .upper_right = true, .lower_left = true },        0x259F => .{ .upper_right = true, .lower_left = true, .lower_right = true },        else => .{},    };}fn drawBraille(writer: anytype, codepoint: u21, cell: Rect, color: Color) void {    const bits: u8 = @intCast(codepoint - 0x2800);    const dot_width = @max(1, snap(cell.width / 4));    const dot_height = @max(1, snap(cell.height / 8));    const columns = [2]f32{ cell.x + cell.width * 0.18, cell.x + cell.width * 0.58 };    const rows = [4]f32{        cell.y + cell.height * 0.12,        cell.y + cell.height * 0.34,        cell.y + cell.height * 0.56,        cell.y + cell.height * 0.78,    };    const dots = [8]struct { column: usize, row: usize }{        .{ .column = 0, .row = 0 },        .{ .column = 0, .row = 1 },        .{ .column = 0, .row = 2 },        .{ .column = 1, .row = 0 },        .{ .column = 1, .row = 1 },        .{ .column = 1, .row = 2 },        .{ .column = 0, .row = 3 },        .{ .column = 1, .row = 3 },    };    for (dots, 0..) |dot, bit| {        if ((bits >> @intCast(bit)) & 1 == 0) continue;        fill(writer, .{            .x = snap(columns[dot.column]),            .y = snap(rows[dot.row]),            .width = dot_width,            .height = dot_height,        }, color);    }}fn drawChevron(writer: anytype, cell: Rect, color: Color) void {    const steps: usize = 4;    const inset_x = cell.width * 0.2;    const inset_y = cell.height * 0.22;    const span_x = cell.width - inset_x * 2;    const half_y = cell.height / 2 - inset_y;    const step_x = span_x / @as(f32, @floatFromInt(steps));    const step_y = half_y / @as(f32, @floatFromInt(steps));    const thickness = @max(step_y, strokeThickness(cell));    for (0..steps) |index| {        const offset: f32 = @floatFromInt(index);        fill(writer, .{            .x = snap(cell.x + inset_x + step_x * offset),            .y = snap(cell.y + inset_y + step_y * offset),            .width = @max(1, snap(step_x + 1)),            .height = @max(1, snap(thickness)),        }, color);        fill(writer, .{            .x = snap(cell.x + inset_x + step_x * offset),            .y = snap(cell.y + cell.height - inset_y - step_y * (offset + 1)),            .width = @max(1, snap(step_x + 1)),            .height = @max(1, snap(thickness)),        }, color);    }}fn drawStar(writer: anytype, cell: Rect, color: Color) void {    const rows = [5]f32{ 1, 3, 5, 3, 1 };    const unit_height = cell.height / 7;    const unit_width = cell.width / 6;    for (rows, 0..) |width_units, index| {        const row_width = unit_width * width_units;        const row_index: f32 = @floatFromInt(index);        fill(writer, .{            .x = snap(cell.x + (cell.width - row_width) / 2),            .y = snap(cell.y + unit_height * (row_index + 1)),            .width = @max(1, snap(row_width)),            .height = @max(1, snap(unit_height)),        }, color);    }}fn fillFraction(writer: anytype, cell: Rect, color: Color, x0: f32, y0: f32, x1: f32, y1: f32) void {    fill(writer, .{        .x = cell.x + cell.width * x0 / 8,        .y = cell.y + cell.height * y0 / 8,        .width = cell.width * (x1 - x0) / 8,        .height = cell.height * (y1 - y0) / 8,    }, color);}fn fill(writer: anytype, rect: Rect, color: Color) void {    writer.fillRect(.{        .x = snap(rect.x),        .y = snap(rect.y),        .width = @max(1, snap(rect.width)),        .height = @max(1, snap(rect.height)),    }, color);}fn shaded(color: Color, alpha: u8) Color {    return .{ .r = color.r, .g = color.g, .b = color.b, .a = @intCast(@as(u16, color.a) * alpha / 255) };}fn strokeThickness(cell: Rect) f32 {    return @max(1, snap(cell.height / 12));}fn snap(value: f32) f32 {    return @round(value);}const RecordingWriter = struct {    rects: std.ArrayListUnmanaged(Rect) = .empty,    colors: std.ArrayListUnmanaged(Color) = .empty,    allocator: std.mem.Allocator,    fn fillRect(self: *RecordingWriter, rect: Rect, color: Color) void {        self.rects.append(self.allocator, rect) catch @panic("synth test allocation failed");        self.colors.append(self.allocator, color) catch @panic("synth test allocation failed");    }    fn deinit(self: *RecordingWriter) void {        self.rects.deinit(self.allocator);        self.colors.deinit(self.allocator);    }};const test_cell = Rect{ .x = 10, .y = 20, .width = 10, .height = 24 };const test_color = Color{ .r = 200, .g = 100, .b = 50, .a = 255 };test "covered spans the terminal vocabulary and rejects letters" {    try std.testing.expect(covered(0x2502));    try std.testing.expect(covered(0x258F));    try std.testing.expect(covered(0x280B));    try std.testing.expect(covered(0x276F));    try std.testing.expect(covered(0x2726));    try std.testing.expect(covered(0x2297));    try std.testing.expect(covered(0x22EF));    try std.testing.expect(!covered('a'));    try std.testing.expect(!covered(0x2560));}test "notdef draws a closed box without a font glyph" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    drawNotdef(&writer, test_cell, test_color);    try std.testing.expectEqual(@as(usize, 4), writer.rects.items.len);    try std.testing.expectEqual(writer.rects.items[0].y, writer.rects.items[2].y);    try std.testing.expectEqual(writer.rects.items[1].y + writer.rects.items[1].height, writer.rects.items[2].y + writer.rects.items[2].height);}test "circled operators draw a ring with an inner mark" {    var ring_only = RecordingWriter{ .allocator = std.testing.allocator };    defer ring_only.deinit();    try std.testing.expect(draw(&ring_only, 0x2299, test_cell, test_color));    const dot_rects = ring_only.rects.items.len;    try std.testing.expectEqual(@as(usize, 9), dot_rects);    var crossed = RecordingWriter{ .allocator = std.testing.allocator };    defer crossed.deinit();    try std.testing.expect(draw(&crossed, 0x2297, test_cell, test_color));    try std.testing.expect(crossed.rects.items.len > dot_rects);}test "midline ellipsis draws three centered dots" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x22EF, test_cell, test_color));    try std.testing.expectEqual(@as(usize, 3), writer.rects.items.len);    for (writer.rects.items) |rect| {        try std.testing.expect(rect.y > test_cell.y + test_cell.height / 4);        try std.testing.expect(rect.y < test_cell.y + test_cell.height * 3 / 4);    }}test "left one eighth block fills a narrow left bar" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x258F, test_cell, test_color));    try std.testing.expectEqual(@as(usize, 1), writer.rects.items.len);    const rect = writer.rects.items[0];    try std.testing.expectEqual(test_cell.x, rect.x);    try std.testing.expectEqual(test_cell.y, rect.y);    try std.testing.expectApproxEqAbs(test_cell.width / 8, rect.width, 1.0);    try std.testing.expectEqual(test_cell.height, rect.height);}test "full block covers the cell and shades keep the hue at reduced alpha" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x2588, test_cell, test_color));    try std.testing.expectEqual(test_cell.width, writer.rects.items[0].width);    try std.testing.expectEqual(test_cell.height, writer.rects.items[0].height);    try std.testing.expect(draw(&writer, 0x2592, test_cell, test_color));    try std.testing.expectEqual(@as(u8, 128), writer.colors.items[1].a);    try std.testing.expectEqual(test_color.r, writer.colors.items[1].r);}test "braille dot count follows the bit pattern" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x280B, test_cell, test_color));    try std.testing.expectEqual(@as(usize, 3), writer.rects.items.len);    var full = RecordingWriter{ .allocator = std.testing.allocator };    defer full.deinit();    try std.testing.expect(draw(&full, 0x28FF, test_cell, test_color));    try std.testing.expectEqual(@as(usize, 8), full.rects.items.len);}test "box drawing tee composes three segments" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x251C, test_cell, test_color));    try std.testing.expectEqual(@as(usize, 3), writer.rects.items.len);}test "chevron and star emit stepped fills inside the cell" {    var writer = RecordingWriter{ .allocator = std.testing.allocator };    defer writer.deinit();    try std.testing.expect(draw(&writer, 0x276F, test_cell, test_color));    try std.testing.expect(draw(&writer, 0x2726, test_cell, test_color));    for (writer.rects.items) |rect| {        try std.testing.expect(rect.x >= test_cell.x - 1);        try std.testing.expect(rect.x + rect.width <= test_cell.x + test_cell.width + 2);        try std.testing.expect(rect.y >= test_cell.y - 1);        try std.testing.expect(rect.y + rect.height <= test_cell.y + test_cell.height + 2);    }}

Complete caller list for paint.synth.draw

8 direct callers.

Complete call list for paint.synth.draw

8 direct calls.

Audit

Definitions4
Public names4
Members0
Version26.7.0
Revisiondaab053ee433