draw octants directly rather than relying on font (#5433)

* implement `yQuads()` and `draw_octant()`, pretty obvious extensions of
existing code. to allocate up to 3 potential remainder lines, consider
that octants will often appear in a rectangular subset of the terminal.
we want the distributed excess uniformly distributed across such a
region. so:
* one excess row: break symmetry in any direction (pick an arbitrary
tetrad and use it everywhere)
  * two excess rows: go to alternating tetrads
* three excess rows: break symmetry, do not use three contiguous tetrads
* our `Octant`s are octary arrays of `bool`, provided as a somewhat
opaque constant table
* the 8-line copy-and-paste draw based on the `Octant` is not the
prettiest thing in the known universe
* we could generalize `draw_sextant()` and `draw_octant()` like
notcurses did, almost certainly
  * oh bird thou never wert
 

with that said, i don't think `draw_octant()` is actually being called
lol, so let's not merge this yet. happy to hear early feedback, though.
This commit is contained in:
Mitchell Hashimoto
2025-02-13 09:21:11 -08:00
committed by GitHub
4 changed files with 330 additions and 1 deletions

View File

@ -184,6 +184,10 @@ const SmoothMosaic = packed struct(u10) {
}
};
// Octant range, inclusive
const octant_min = 0x1cd00;
const octant_max = 0x1cde5;
// Utility names for common fractions
const one_eighth: f64 = 0.125;
const one_quarter: f64 = 0.25;
@ -581,6 +585,8 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void
0x1fb00...0x1fb3b => self.draw_sextant(canvas, cp),
octant_min...octant_max => self.draw_octant(canvas, cp),
// '🬼'
0x1fb3c => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from(
\\...
@ -2484,6 +2490,65 @@ fn draw_sextant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
if (sex.br) self.rect(canvas, x_halfs[1], y_thirds[1], self.metrics.cell_width, self.metrics.cell_height);
}
fn draw_octant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
assert(cp >= octant_min and cp <= octant_max);
// Octant representation. We use the funny numeric string keys
// so its easier to parse the actual name used in the Symbols for
// Legacy Computing spec.
const Octant = packed struct(u8) {
@"1": bool = false,
@"2": bool = false,
@"3": bool = false,
@"4": bool = false,
@"5": bool = false,
@"6": bool = false,
@"7": bool = false,
@"8": bool = false,
};
// Parse the octant data. This is all done at comptime so this is
// static data that is embedded in the binary.
const octants_len = octant_max - octant_min + 1;
const octants: [octants_len]Octant = comptime octants: {
@setEvalBranchQuota(10_000);
var result: [octants_len]Octant = .{.{}} ** octants_len;
var i: usize = 0;
const data = @embedFile("octants.txt");
var it = std.mem.splitScalar(u8, data, '\n');
while (it.next()) |line| {
// Skip comments
if (line.len == 0 or line[0] == '#') continue;
const current = &result[i];
i += 1;
// Octants are in the format "BLOCK OCTANT-1235". The numbers
// at the end are keys into our packed struct. Since we're
// at comptime we can metaprogram it all.
const idx = std.mem.indexOfScalar(u8, line, '-').?;
for (line[idx + 1 ..]) |c| @field(current, &.{c}) = true;
}
assert(i == octants_len);
break :octants result;
};
const x_halfs = self.xHalfs();
const y_quads = self.yQuads();
const oct = octants[cp - octant_min];
if (oct.@"1") self.rect(canvas, 0, 0, x_halfs[0], y_quads[0]);
if (oct.@"2") self.rect(canvas, x_halfs[1], 0, self.metrics.cell_width, y_quads[0]);
if (oct.@"3") self.rect(canvas, 0, y_quads[0], x_halfs[0], y_quads[1]);
if (oct.@"4") self.rect(canvas, x_halfs[1], y_quads[0], self.metrics.cell_width, y_quads[1]);
if (oct.@"5") self.rect(canvas, 0, y_quads[1], x_halfs[0], y_quads[2]);
if (oct.@"6") self.rect(canvas, x_halfs[1], y_quads[1], self.metrics.cell_width, y_quads[2]);
if (oct.@"7") self.rect(canvas, 0, y_quads[2], x_halfs[0], self.metrics.cell_height);
if (oct.@"8") self.rect(canvas, x_halfs[1], y_quads[2], self.metrics.cell_width, self.metrics.cell_height);
}
fn xHalfs(self: Box) [2]u32 {
return .{
@as(u32, @intFromFloat(@round(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2))),
@ -2500,6 +2565,21 @@ fn yThirds(self: Box) [2]u32 {
};
}
// assume octants might be striped across multiple rows of cells. to maximize
// distance between excess pixellines, we want (1) an arbitrary region (there
// will be a pattern of 1'-3-1'-3-1'-3 no matter what), (2) discontiguous
// regions (0 and 2 or 1 and 3), and (3) an arbitrary three regions (there will
// be a pattern of 3-1-3-1-3-1 no matter what).
fn yQuads(self: Box) [3]u32 {
return switch (@mod(self.metrics.cell_height, 4)) {
0 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 },
1 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 },
2 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 + 1 },
3 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 },
else => unreachable,
};
}
fn draw_smooth_mosaic(
self: Box,
canvas: *font.sprite.Canvas,
@ -3064,7 +3144,7 @@ fn testRenderAll(self: Box, alloc: Allocator, atlas: *font.Atlas) !void {
);
}
// Symbols for Legacy Computing Supplement.
// Symbols for Legacy Computing Supplement: Quadrants
// 𜰡 𜰢 𜰣 𜰤 𜰥 𜰦 𜰧 𜰨 𜰩 𜰪 𜰫 𜰬 𜰭 𜰮 𜰯
cp = 0x1cc21;
while (cp <= 0x1cc2f) : (cp += 1) {
@ -3077,6 +3157,19 @@ fn testRenderAll(self: Box, alloc: Allocator, atlas: *font.Atlas) !void {
else => {},
}
}
// Symbols for Legacy Computing Supplement: Octants
cp = 0x1CD00;
while (cp <= 0x1CDE5) : (cp += 1) {
switch (cp) {
0x1CD00...0x1CDE5 => _ = try self.renderGlyph(
alloc,
atlas,
cp,
),
else => {},
}
}
}
test "render all sprites" {

View File

@ -236,6 +236,8 @@ const Kind = enum {
// (Geometric Shapes)
// 🯠 🯡 🯢 🯣 🯤 🯥 🯦 🯧 🯨 🯩 🯪 🯫 🯬 🯭 🯮 🯯
0x1FBCE...0x1FBEF,
// (Octants)
0x1CD00...0x1CDE5,
=> .box,
// Branch drawing character set, used for drawing git-like

234
src/font/sprite/octants.txt Normal file
View File

@ -0,0 +1,234 @@
# This is the list of all the octants for the Symbols for Legacy
# Computing block. It is used at comptime to generate the lookup
# table for drawing them since we weren't able to discern a
# mathematical pattern for them.
BLOCK OCTANT-3
BLOCK OCTANT-23
BLOCK OCTANT-123
BLOCK OCTANT-4
BLOCK OCTANT-14
BLOCK OCTANT-124
BLOCK OCTANT-34
BLOCK OCTANT-134
BLOCK OCTANT-234
BLOCK OCTANT-5
BLOCK OCTANT-15
BLOCK OCTANT-25
BLOCK OCTANT-125
BLOCK OCTANT-135
BLOCK OCTANT-235
BLOCK OCTANT-1235
BLOCK OCTANT-45
BLOCK OCTANT-145
BLOCK OCTANT-245
BLOCK OCTANT-1245
BLOCK OCTANT-345
BLOCK OCTANT-1345
BLOCK OCTANT-2345
BLOCK OCTANT-12345
BLOCK OCTANT-6
BLOCK OCTANT-16
BLOCK OCTANT-26
BLOCK OCTANT-126
BLOCK OCTANT-36
BLOCK OCTANT-136
BLOCK OCTANT-236
BLOCK OCTANT-1236
BLOCK OCTANT-146
BLOCK OCTANT-246
BLOCK OCTANT-1246
BLOCK OCTANT-346
BLOCK OCTANT-1346
BLOCK OCTANT-2346
BLOCK OCTANT-12346
BLOCK OCTANT-56
BLOCK OCTANT-156
BLOCK OCTANT-256
BLOCK OCTANT-1256
BLOCK OCTANT-356
BLOCK OCTANT-1356
BLOCK OCTANT-2356
BLOCK OCTANT-12356
BLOCK OCTANT-456
BLOCK OCTANT-1456
BLOCK OCTANT-2456
BLOCK OCTANT-12456
BLOCK OCTANT-3456
BLOCK OCTANT-13456
BLOCK OCTANT-23456
BLOCK OCTANT-17
BLOCK OCTANT-27
BLOCK OCTANT-127
BLOCK OCTANT-37
BLOCK OCTANT-137
BLOCK OCTANT-237
BLOCK OCTANT-1237
BLOCK OCTANT-47
BLOCK OCTANT-147
BLOCK OCTANT-247
BLOCK OCTANT-1247
BLOCK OCTANT-347
BLOCK OCTANT-1347
BLOCK OCTANT-2347
BLOCK OCTANT-12347
BLOCK OCTANT-157
BLOCK OCTANT-257
BLOCK OCTANT-1257
BLOCK OCTANT-357
BLOCK OCTANT-2357
BLOCK OCTANT-12357
BLOCK OCTANT-457
BLOCK OCTANT-1457
BLOCK OCTANT-12457
BLOCK OCTANT-3457
BLOCK OCTANT-13457
BLOCK OCTANT-23457
BLOCK OCTANT-67
BLOCK OCTANT-167
BLOCK OCTANT-267
BLOCK OCTANT-1267
BLOCK OCTANT-367
BLOCK OCTANT-1367
BLOCK OCTANT-2367
BLOCK OCTANT-12367
BLOCK OCTANT-467
BLOCK OCTANT-1467
BLOCK OCTANT-2467
BLOCK OCTANT-12467
BLOCK OCTANT-3467
BLOCK OCTANT-13467
BLOCK OCTANT-23467
BLOCK OCTANT-123467
BLOCK OCTANT-567
BLOCK OCTANT-1567
BLOCK OCTANT-2567
BLOCK OCTANT-12567
BLOCK OCTANT-3567
BLOCK OCTANT-13567
BLOCK OCTANT-23567
BLOCK OCTANT-123567
BLOCK OCTANT-4567
BLOCK OCTANT-14567
BLOCK OCTANT-24567
BLOCK OCTANT-124567
BLOCK OCTANT-34567
BLOCK OCTANT-134567
BLOCK OCTANT-234567
BLOCK OCTANT-1234567
BLOCK OCTANT-18
BLOCK OCTANT-28
BLOCK OCTANT-128
BLOCK OCTANT-38
BLOCK OCTANT-138
BLOCK OCTANT-238
BLOCK OCTANT-1238
BLOCK OCTANT-48
BLOCK OCTANT-148
BLOCK OCTANT-248
BLOCK OCTANT-1248
BLOCK OCTANT-348
BLOCK OCTANT-1348
BLOCK OCTANT-2348
BLOCK OCTANT-12348
BLOCK OCTANT-58
BLOCK OCTANT-158
BLOCK OCTANT-258
BLOCK OCTANT-1258
BLOCK OCTANT-358
BLOCK OCTANT-1358
BLOCK OCTANT-2358
BLOCK OCTANT-12358
BLOCK OCTANT-458
BLOCK OCTANT-1458
BLOCK OCTANT-2458
BLOCK OCTANT-12458
BLOCK OCTANT-3458
BLOCK OCTANT-13458
BLOCK OCTANT-23458
BLOCK OCTANT-123458
BLOCK OCTANT-168
BLOCK OCTANT-268
BLOCK OCTANT-1268
BLOCK OCTANT-368
BLOCK OCTANT-2368
BLOCK OCTANT-12368
BLOCK OCTANT-468
BLOCK OCTANT-1468
BLOCK OCTANT-12468
BLOCK OCTANT-3468
BLOCK OCTANT-13468
BLOCK OCTANT-23468
BLOCK OCTANT-568
BLOCK OCTANT-1568
BLOCK OCTANT-2568
BLOCK OCTANT-12568
BLOCK OCTANT-3568
BLOCK OCTANT-13568
BLOCK OCTANT-23568
BLOCK OCTANT-123568
BLOCK OCTANT-4568
BLOCK OCTANT-14568
BLOCK OCTANT-24568
BLOCK OCTANT-124568
BLOCK OCTANT-34568
BLOCK OCTANT-134568
BLOCK OCTANT-234568
BLOCK OCTANT-1234568
BLOCK OCTANT-178
BLOCK OCTANT-278
BLOCK OCTANT-1278
BLOCK OCTANT-378
BLOCK OCTANT-1378
BLOCK OCTANT-2378
BLOCK OCTANT-12378
BLOCK OCTANT-478
BLOCK OCTANT-1478
BLOCK OCTANT-2478
BLOCK OCTANT-12478
BLOCK OCTANT-3478
BLOCK OCTANT-13478
BLOCK OCTANT-23478
BLOCK OCTANT-123478
BLOCK OCTANT-578
BLOCK OCTANT-1578
BLOCK OCTANT-2578
BLOCK OCTANT-12578
BLOCK OCTANT-3578
BLOCK OCTANT-13578
BLOCK OCTANT-23578
BLOCK OCTANT-123578
BLOCK OCTANT-4578
BLOCK OCTANT-14578
BLOCK OCTANT-24578
BLOCK OCTANT-124578
BLOCK OCTANT-34578
BLOCK OCTANT-134578
BLOCK OCTANT-234578
BLOCK OCTANT-1234578
BLOCK OCTANT-678
BLOCK OCTANT-1678
BLOCK OCTANT-2678
BLOCK OCTANT-12678
BLOCK OCTANT-3678
BLOCK OCTANT-13678
BLOCK OCTANT-23678
BLOCK OCTANT-123678
BLOCK OCTANT-4678
BLOCK OCTANT-14678
BLOCK OCTANT-24678
BLOCK OCTANT-124678
BLOCK OCTANT-34678
BLOCK OCTANT-134678
BLOCK OCTANT-234678
BLOCK OCTANT-1234678
BLOCK OCTANT-15678
BLOCK OCTANT-25678
BLOCK OCTANT-125678
BLOCK OCTANT-35678
BLOCK OCTANT-235678
BLOCK OCTANT-1235678
BLOCK OCTANT-45678
BLOCK OCTANT-145678
BLOCK OCTANT-1245678
BLOCK OCTANT-1345678
BLOCK OCTANT-2345678

Binary file not shown.