More Sprite Glyphs (#7755)
Follow-up to #7732, but even more to come, these were just some low hanging fruit which it would be nice to merge early. Adds these characters from the Symbols for Legacy Computing Supplement block: ``` ``` How this looks in Ghostty: |Main (via font)|This PR (via sprites)| |-|-| |<img width="256" alt="image" src="https://github.com/user-attachments/assets/f6000984-7e4a-4ec0-b282-9d0905bd54ed" />|<img width="256" alt="image" src="https://github.com/user-attachments/assets/0d880458-3025-4e42-b2ba-cf84f540d503" />| This PR also adjusts the way block quadrants are drawn for better alignment with other block elements, this matches how we handle sextants already. ### Diffs |Range||||| |-|-|-|-|-| |U+1CC00...U+1CCFF||||| |U+1CE00..U+1CEFF||||| |U+2500...U+25FF|||||pull/7757/head
|
|
@ -15,6 +15,8 @@ const common = @import("common.zig");
|
|||
const Shade = common.Shade;
|
||||
const Quads = common.Quads;
|
||||
const Alignment = common.Alignment;
|
||||
const xHalfs = common.xHalfs;
|
||||
const yHalfs = common.yHalfs;
|
||||
const rect = common.rect;
|
||||
|
||||
const font = @import("../../main.zig");
|
||||
|
|
@ -174,11 +176,11 @@ fn quadrant(
|
|||
canvas: *font.sprite.Canvas,
|
||||
comptime quads: Quads,
|
||||
) void {
|
||||
const center_x = metrics.cell_width / 2 + metrics.cell_width % 2;
|
||||
const center_y = metrics.cell_height / 2 + metrics.cell_height % 2;
|
||||
const x_halfs = xHalfs(metrics);
|
||||
const y_halfs = yHalfs(metrics);
|
||||
|
||||
if (quads.tl) rect(metrics, canvas, 0, 0, center_x, center_y);
|
||||
if (quads.tr) rect(metrics, canvas, center_x, 0, metrics.cell_width, center_y);
|
||||
if (quads.bl) rect(metrics, canvas, 0, center_y, center_x, metrics.cell_height);
|
||||
if (quads.br) rect(metrics, canvas, center_x, center_y, metrics.cell_width, metrics.cell_height);
|
||||
if (quads.tl) rect(metrics, canvas, 0, 0, x_halfs[0], y_halfs[0]);
|
||||
if (quads.tr) rect(metrics, canvas, x_halfs[1], 0, metrics.cell_width, y_halfs[0]);
|
||||
if (quads.bl) rect(metrics, canvas, 0, y_halfs[1], x_halfs[0], metrics.cell_height);
|
||||
if (quads.br) rect(metrics, canvas, x_halfs[1], y_halfs[1], metrics.cell_width, metrics.cell_height);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,14 @@ pub fn xHalfs(metrics: font.Metrics) [2]u32 {
|
|||
return .{ half_width, metrics.cell_width - half_width };
|
||||
}
|
||||
|
||||
/// yHalfs[0] should be used as the bottom edge of a top-aligned half.
|
||||
/// yHalfs[1] should be used as the top edge of a bottom-aligned half.
|
||||
pub fn yHalfs(metrics: font.Metrics) [2]u32 {
|
||||
const float_height: f64 = @floatFromInt(metrics.cell_height);
|
||||
const half_height: u32 = @intFromFloat(@round(0.5 * float_height));
|
||||
return .{ half_height, metrics.cell_height - half_height };
|
||||
}
|
||||
|
||||
/// Use these values as such:
|
||||
/// yThirds[0] bottom edge of the first third.
|
||||
/// yThirds[1] top edge of the second third.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ const xHalfs = common.xHalfs;
|
|||
const yQuads = common.yQuads;
|
||||
const rect = common.rect;
|
||||
|
||||
const box = @import("box.zig");
|
||||
|
||||
const font = @import("../../main.zig");
|
||||
|
||||
const octant_min = 0x1cd00;
|
||||
|
|
@ -192,6 +194,135 @@ pub fn draw1CC21_1CC2F(
|
|||
);
|
||||
}
|
||||
|
||||
/// Twelfth and Quarter circle pieces.
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
/// These are actually ellipses, sized to touch
|
||||
/// the edge of their enclosing set of cells.
|
||||
pub fn draw1CC30_1CC3F(
|
||||
cp: u32,
|
||||
canvas: *font.sprite.Canvas,
|
||||
width: u32,
|
||||
height: u32,
|
||||
metrics: font.Metrics,
|
||||
) !void {
|
||||
switch (cp) {
|
||||
// UPPER LEFT TWELFTH CIRCLE
|
||||
0x1CC30 => try circlePiece(canvas, width, height, metrics, 0, 0, 2, 2),
|
||||
// UPPER CENTRE LEFT TWELFTH CIRCLE
|
||||
0x1CC31 => try circlePiece(canvas, width, height, metrics, 1, 0, 2, 2),
|
||||
// UPPER CENTRE RIGHT TWELFTH CIRCLE
|
||||
0x1CC32 => try circlePiece(canvas, width, height, metrics, 2, 0, 2, 2),
|
||||
// UPPER RIGHT TWELFTH CIRCLE
|
||||
0x1CC33 => try circlePiece(canvas, width, height, metrics, 3, 0, 2, 2),
|
||||
// UPPER MIDDLE LEFT TWELFTH CIRCLE
|
||||
0x1CC34 => try circlePiece(canvas, width, height, metrics, 0, 1, 2, 2),
|
||||
// UPPER LEFT QUARTER CIRCLE
|
||||
0x1CC35 => try circlePiece(canvas, width, height, metrics, 0, 0, 1, 1),
|
||||
// UPPER RIGHT QUARTER CIRCLE
|
||||
0x1CC36 => try circlePiece(canvas, width, height, metrics, 1, 0, 1, 1),
|
||||
// UPPER MIDDLE RIGHT TWELFTH CIRCLE
|
||||
0x1CC37 => try circlePiece(canvas, width, height, metrics, 3, 1, 2, 2),
|
||||
// LOWER MIDDLE LEFT TWELFTH CIRCLE
|
||||
0x1CC38 => try circlePiece(canvas, width, height, metrics, 0, 2, 2, 2),
|
||||
// LOWER LEFT QUARTER CIRCLE
|
||||
0x1CC39 => try circlePiece(canvas, width, height, metrics, 0, 1, 1, 1),
|
||||
// LOWER RIGHT QUARTER CIRCLE
|
||||
0x1CC3A => try circlePiece(canvas, width, height, metrics, 1, 1, 1, 1),
|
||||
// LOWER MIDDLE RIGHT TWELFTH CIRCLE
|
||||
0x1CC3B => try circlePiece(canvas, width, height, metrics, 3, 2, 2, 2),
|
||||
// LOWER LEFT TWELFTH CIRCLE
|
||||
0x1CC3C => try circlePiece(canvas, width, height, metrics, 0, 3, 2, 2),
|
||||
// LOWER CENTRE LEFT TWELFTH CIRCLE
|
||||
0x1CC3D => try circlePiece(canvas, width, height, metrics, 1, 3, 2, 2),
|
||||
// LOWER CENTRE RIGHT TWELFTH CIRCLE
|
||||
0x1CC3E => try circlePiece(canvas, width, height, metrics, 2, 3, 2, 2),
|
||||
// LOWER RIGHT TWELFTH CIRCLE
|
||||
0x1CC3F => try circlePiece(canvas, width, height, metrics, 3, 3, 2, 2),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: These two characters should be easy, but it's not clear how they're
|
||||
/// meant to align with adjacent cells, what characters they're meant to
|
||||
/// be used with:
|
||||
/// - 1CC1F BOX DRAWINGS DOUBLE DIAGONAL UPPER RIGHT TO LOWER LEFT
|
||||
/// - 1CC20 BOX DRAWINGS DOUBLE DIAGONAL UPPER LEFT TO LOWER RIGHT
|
||||
pub fn draw1CC1B_1CC1E(
|
||||
cp: u32,
|
||||
canvas: *font.sprite.Canvas,
|
||||
width: u32,
|
||||
height: u32,
|
||||
metrics: font.Metrics,
|
||||
) !void {
|
||||
const w: i32 = @intCast(width);
|
||||
const h: i32 = @intCast(height);
|
||||
const t: i32 = @intCast(metrics.box_thickness);
|
||||
switch (cp) {
|
||||
// BOX DRAWINGS LIGHT HORIZONTAL AND UPPER RIGHT
|
||||
0x1CC1B => {
|
||||
box.linesChar(metrics, canvas, .{ .left = .light, .right = .light });
|
||||
canvas.box(w - t, 0, w, @divFloor(h, 2), .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT HORIZONTAL AND LOWER RIGHT
|
||||
0x1CC1C => {
|
||||
box.linesChar(metrics, canvas, .{ .left = .light, .right = .light });
|
||||
canvas.box(w - t, @divFloor(h, 2), w, h, .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT TOP AND UPPER LEFT
|
||||
0x1CC1D => {
|
||||
canvas.box(0, 0, w, t, .on);
|
||||
canvas.box(0, 0, t, @divFloor(h, 2), .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT BOTTOM AND LOWER LEFT
|
||||
0x1CC1E => {
|
||||
canvas.box(0, h - t, w, h, .on);
|
||||
canvas.box(0, @divFloor(h, 2), t, h, .on);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw1CE16_1CE19(
|
||||
cp: u32,
|
||||
canvas: *font.sprite.Canvas,
|
||||
width: u32,
|
||||
height: u32,
|
||||
metrics: font.Metrics,
|
||||
) !void {
|
||||
const w: i32 = @intCast(width);
|
||||
const h: i32 = @intCast(height);
|
||||
const t: i32 = @intCast(metrics.box_thickness);
|
||||
switch (cp) {
|
||||
// BOX DRAWINGS LIGHT VERTICAL AND TOP RIGHT
|
||||
0x1CE16 => {
|
||||
box.linesChar(metrics, canvas, .{ .up = .light, .down = .light });
|
||||
canvas.box(@divFloor(w, 2), 0, w, t, .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT VERTICAL AND BOTTOM RIGHT
|
||||
0x1CE17 => {
|
||||
box.linesChar(metrics, canvas, .{ .up = .light, .down = .light });
|
||||
canvas.box(@divFloor(w, 2), h - t, w, h, .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT VERTICAL AND TOP LEFT
|
||||
0x1CE18 => {
|
||||
box.linesChar(metrics, canvas, .{ .up = .light, .down = .light });
|
||||
canvas.box(0, 0, @divFloor(w, 2), t, .on);
|
||||
},
|
||||
// BOX DRAWINGS LIGHT VERTICAL AND BOTTOM LEFT
|
||||
0x1CE19 => {
|
||||
box.linesChar(metrics, canvas, .{ .up = .light, .down = .light });
|
||||
canvas.box(0, h - t, @divFloor(w, 2), h, .on);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Separated Block Sextants
|
||||
pub fn draw1CE51_1CE8F(
|
||||
cp: u32,
|
||||
|
|
@ -271,3 +402,93 @@ pub fn draw1CE51_1CE8F(
|
|||
.on,
|
||||
);
|
||||
}
|
||||
|
||||
fn circlePiece(
|
||||
canvas: *font.sprite.Canvas,
|
||||
width: u32,
|
||||
height: u32,
|
||||
metrics: font.Metrics,
|
||||
x: f64,
|
||||
y: f64,
|
||||
w: f64,
|
||||
h: f64,
|
||||
) !void {
|
||||
// Radius in pixels of the arc we need to draw.
|
||||
const wdth: f64 = @as(f64, @floatFromInt(width)) * w;
|
||||
const hght: f64 = @as(f64, @floatFromInt(height)) * h;
|
||||
|
||||
// Position in pixels (rather than cells) for x/y
|
||||
const xp: f64 = @as(f64, @floatFromInt(width)) * x;
|
||||
const yp: f64 = @as(f64, @floatFromInt(height)) * y;
|
||||
|
||||
// Set the clip so we don't include anything outside of the cell.
|
||||
canvas.clip_left = canvas.padding_x;
|
||||
canvas.clip_right = canvas.padding_x;
|
||||
canvas.clip_top = canvas.padding_y;
|
||||
canvas.clip_bottom = canvas.padding_y;
|
||||
|
||||
// Coefficient for approximating a circular arc.
|
||||
const c: f64 = (std.math.sqrt2 - 1.0) * 4.0 / 3.0;
|
||||
const cw = c * wdth;
|
||||
const ch = c * hght;
|
||||
|
||||
const thick: f64 = @floatFromInt(metrics.box_thickness);
|
||||
const ht = thick * 0.5;
|
||||
|
||||
var path = canvas.staticPath(2);
|
||||
|
||||
if (xp < wdth) {
|
||||
if (yp < hght) {
|
||||
// Upper left arc.
|
||||
path.moveTo(wdth - xp, ht - yp);
|
||||
path.curveTo(
|
||||
wdth - cw - xp,
|
||||
ht - yp,
|
||||
ht - xp,
|
||||
hght - ch - yp,
|
||||
ht - xp,
|
||||
hght - yp,
|
||||
);
|
||||
} else {
|
||||
// Lower left arc.
|
||||
path.moveTo(ht - xp, hght - yp);
|
||||
path.curveTo(
|
||||
ht - xp,
|
||||
hght + ch - yp,
|
||||
wdth - cw - xp,
|
||||
hght * 2 - ht - yp,
|
||||
wdth - xp,
|
||||
hght * 2 - ht - yp,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (yp < hght) {
|
||||
// Upper right arc.
|
||||
path.moveTo(wdth - xp, ht - yp);
|
||||
path.curveTo(
|
||||
wdth + cw - xp,
|
||||
ht - yp,
|
||||
wdth * 2 - ht - xp,
|
||||
hght - ch - yp,
|
||||
wdth * 2 - ht - xp,
|
||||
hght - yp,
|
||||
);
|
||||
} else {
|
||||
// Lower right arc.
|
||||
path.moveTo(wdth * 2 - ht - xp, hght - yp);
|
||||
path.curveTo(
|
||||
wdth * 2 - ht - xp,
|
||||
hght + ch - yp,
|
||||
wdth + cw - xp,
|
||||
hght * 2 - ht - yp,
|
||||
wdth - xp,
|
||||
hght * 2 - ht - yp,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try canvas.strokePath(path.wrapped_path, .{
|
||||
.line_cap_mode = .butt,
|
||||
.line_width = @floatFromInt(metrics.box_thickness),
|
||||
}, .on);
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 316 B After Width: | Height: | Size: 794 B |
|
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 632 B |
|
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 819 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 443 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |