font/sprite: rework `yQuads` and friends for better alignment with `draw_block` (#7488)
This improves "outer edge" alignment of octants and other elements drawn using `yQuads` and friends with blocks drawn with `draw_block` -- this should guarantee alignment along a continuous edge, but may result in a 1px overlap of opposing edges (such as a top half block followed by a bottom half block with an odd cell height, they will both have the center row filled). This is very necessary since several block elements are needed to complete the set of octants, since dedicated octant characters aren't included when they would be redundant. Fixes #7479 <details> <summary><b><code>Box.ppm</code> diff</b></summary>  </details> > [!NOTE] > In the future I think we should have a unified single source of truth for grid positions (divisions of the cell) to ensure this type of thing can't happen with other characters, and also it would make a lot of the code cleaner. For now this works though.pull/7498/head
commit
6e69893f29
|
|
@ -2488,10 +2488,10 @@ fn draw_sextant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
|
||||||
|
|
||||||
if (sex.tl) self.rect(canvas, 0, 0, x_halfs[0], y_thirds[0]);
|
if (sex.tl) self.rect(canvas, 0, 0, x_halfs[0], y_thirds[0]);
|
||||||
if (sex.tr) self.rect(canvas, x_halfs[1], 0, self.metrics.cell_width, y_thirds[0]);
|
if (sex.tr) self.rect(canvas, x_halfs[1], 0, self.metrics.cell_width, y_thirds[0]);
|
||||||
if (sex.ml) self.rect(canvas, 0, y_thirds[0], x_halfs[0], y_thirds[1]);
|
if (sex.ml) self.rect(canvas, 0, y_thirds[1], x_halfs[0], y_thirds[2]);
|
||||||
if (sex.mr) self.rect(canvas, x_halfs[1], y_thirds[0], self.metrics.cell_width, y_thirds[1]);
|
if (sex.mr) self.rect(canvas, x_halfs[1], y_thirds[1], self.metrics.cell_width, y_thirds[2]);
|
||||||
if (sex.bl) self.rect(canvas, 0, y_thirds[1], x_halfs[0], self.metrics.cell_height);
|
if (sex.bl) self.rect(canvas, 0, y_thirds[3], x_halfs[0], self.metrics.cell_height);
|
||||||
if (sex.br) self.rect(canvas, x_halfs[1], y_thirds[1], self.metrics.cell_width, self.metrics.cell_height);
|
if (sex.br) self.rect(canvas, x_halfs[1], y_thirds[3], self.metrics.cell_width, self.metrics.cell_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_octant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
|
fn draw_octant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
|
||||||
|
|
@ -2545,42 +2545,58 @@ fn draw_octant(self: Box, canvas: *font.sprite.Canvas, cp: u32) void {
|
||||||
const oct = octants[cp - octant_min];
|
const oct = octants[cp - octant_min];
|
||||||
if (oct.@"1") self.rect(canvas, 0, 0, x_halfs[0], y_quads[0]);
|
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.@"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.@"3") self.rect(canvas, 0, y_quads[1], x_halfs[0], y_quads[2]);
|
||||||
if (oct.@"4") self.rect(canvas, x_halfs[1], y_quads[0], self.metrics.cell_width, y_quads[1]);
|
if (oct.@"4") self.rect(canvas, x_halfs[1], y_quads[1], self.metrics.cell_width, y_quads[2]);
|
||||||
if (oct.@"5") self.rect(canvas, 0, y_quads[1], x_halfs[0], y_quads[2]);
|
if (oct.@"5") self.rect(canvas, 0, y_quads[3], x_halfs[0], y_quads[4]);
|
||||||
if (oct.@"6") self.rect(canvas, x_halfs[1], y_quads[1], self.metrics.cell_width, y_quads[2]);
|
if (oct.@"6") self.rect(canvas, x_halfs[1], y_quads[3], self.metrics.cell_width, y_quads[4]);
|
||||||
if (oct.@"7") self.rect(canvas, 0, y_quads[2], x_halfs[0], self.metrics.cell_height);
|
if (oct.@"7") self.rect(canvas, 0, y_quads[5], 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);
|
if (oct.@"8") self.rect(canvas, x_halfs[1], y_quads[5], self.metrics.cell_width, self.metrics.cell_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// xHalfs[0] should be used as the right edge of a left-aligned half.
|
||||||
|
/// xHalfs[1] should be used as the left edge of a right-aligned half.
|
||||||
fn xHalfs(self: Box) [2]u32 {
|
fn xHalfs(self: Box) [2]u32 {
|
||||||
|
const float_width: f64 = @floatFromInt(self.metrics.cell_width);
|
||||||
|
const half_width: u32 = @intFromFloat(@round(0.5 * float_width));
|
||||||
|
return .{ half_width, self.metrics.cell_width - half_width };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use these values as such:
|
||||||
|
/// yThirds[0] bottom edge of the first third.
|
||||||
|
/// yThirds[1] top edge of the second third.
|
||||||
|
/// yThirds[2] bottom edge of the second third.
|
||||||
|
/// yThirds[3] top edge of the final third.
|
||||||
|
fn yThirds(self: Box) [4]u32 {
|
||||||
|
const float_height: f64 = @floatFromInt(self.metrics.cell_height);
|
||||||
|
const one_third_height: u32 = @intFromFloat(@round(one_third * float_height));
|
||||||
|
const two_thirds_height: u32 = @intFromFloat(@round(two_thirds * float_height));
|
||||||
return .{
|
return .{
|
||||||
@as(u32, @intFromFloat(@round(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2))),
|
one_third_height,
|
||||||
@as(u32, @intFromFloat(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2)),
|
self.metrics.cell_height - two_thirds_height,
|
||||||
|
two_thirds_height,
|
||||||
|
self.metrics.cell_height - one_third_height,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn yThirds(self: Box) [2]u32 {
|
/// Use these values as such:
|
||||||
return switch (@mod(self.metrics.cell_height, 3)) {
|
/// yQuads[0] bottom edge of first quarter.
|
||||||
0 => .{ self.metrics.cell_height / 3, 2 * self.metrics.cell_height / 3 },
|
/// yQuads[1] top edge of second quarter.
|
||||||
1 => .{ self.metrics.cell_height / 3, 2 * self.metrics.cell_height / 3 + 1 },
|
/// yQuads[2] bottom edge of second quarter.
|
||||||
2 => .{ self.metrics.cell_height / 3 + 1, 2 * self.metrics.cell_height / 3 },
|
/// yQuads[3] top edge of third quarter.
|
||||||
else => unreachable,
|
/// yQuads[4] bottom edge of third quarter
|
||||||
};
|
/// yQuads[5] top edge of fourth quarter.
|
||||||
}
|
fn yQuads(self: Box) [6]u32 {
|
||||||
|
const float_height: f64 = @floatFromInt(self.metrics.cell_height);
|
||||||
// assume octants might be striped across multiple rows of cells. to maximize
|
const quarter_height: u32 = @intFromFloat(@round(0.25 * float_height));
|
||||||
// distance between excess pixellines, we want (1) an arbitrary region (there
|
const half_height: u32 = @intFromFloat(@round(0.50 * float_height));
|
||||||
// will be a pattern of 1'-3-1'-3-1'-3 no matter what), (2) discontiguous
|
const three_quarters_height: u32 = @intFromFloat(@round(0.75 * float_height));
|
||||||
// regions (0 and 2 or 1 and 3), and (3) an arbitrary three regions (there will
|
return .{
|
||||||
// be a pattern of 3-1-3-1-3-1 no matter what).
|
quarter_height,
|
||||||
fn yQuads(self: Box) [3]u32 {
|
self.metrics.cell_height - three_quarters_height,
|
||||||
return switch (@mod(self.metrics.cell_height, 4)) {
|
half_height,
|
||||||
0 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 },
|
self.metrics.cell_height - half_height,
|
||||||
1 => .{ self.metrics.cell_height / 4, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 },
|
three_quarters_height,
|
||||||
2 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4, 3 * self.metrics.cell_height / 4 + 1 },
|
self.metrics.cell_height - quarter_height,
|
||||||
3 => .{ self.metrics.cell_height / 4 + 1, 2 * self.metrics.cell_height / 4 + 1, 3 * self.metrics.cell_height / 4 },
|
|
||||||
else => unreachable,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2591,8 +2607,12 @@ fn draw_smooth_mosaic(
|
||||||
) !void {
|
) !void {
|
||||||
const y_thirds = self.yThirds();
|
const y_thirds = self.yThirds();
|
||||||
const top: f64 = 0.0;
|
const top: f64 = 0.0;
|
||||||
const upper: f64 = @floatFromInt(y_thirds[0]);
|
// We average the edge positions for the y_thirds boundaries here
|
||||||
const lower: f64 = @floatFromInt(y_thirds[1]);
|
// rather than having to deal with varying alignments depending on
|
||||||
|
// the surrounding pieces. The most this will be off by is half of
|
||||||
|
// a pixel, so hopefully it's not noticeable.
|
||||||
|
const upper: f64 = 0.5 * (@as(f64, @floatFromInt(y_thirds[0])) + @as(f64, @floatFromInt(y_thirds[1])));
|
||||||
|
const lower: f64 = 0.5 * (@as(f64, @floatFromInt(y_thirds[2])) + @as(f64, @floatFromInt(y_thirds[3])));
|
||||||
const bottom: f64 = @floatFromInt(self.metrics.cell_height);
|
const bottom: f64 = @floatFromInt(self.metrics.cell_height);
|
||||||
const left: f64 = 0.0;
|
const left: f64 = 0.0;
|
||||||
const center: f64 = @round(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2);
|
const center: f64 = @round(@as(f64, @floatFromInt(self.metrics.cell_width)) / 2);
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue