586 lines
23 KiB
Zig
586 lines
23 KiB
Zig
const std = @import("std");
|
|
|
|
const board_input = @import("board_input.zig");
|
|
const bitboard = @import("chess/bitboard.zig");
|
|
const chess_board = @import("chess/board.zig");
|
|
const geometry = @import("geometry.zig");
|
|
const piece_render = @import("piece_render.zig");
|
|
|
|
const Glyph = [7]u5;
|
|
|
|
pub const TextStyle = struct {
|
|
pixel_size: f32,
|
|
pixel_aspect: f32 = 1.0,
|
|
color: [4]f32,
|
|
};
|
|
|
|
fn glyphForChar(ch: u8) ?Glyph {
|
|
return switch (ch) {
|
|
'0' => .{ 0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110 },
|
|
'1' => .{ 0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110 },
|
|
'2' => .{ 0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111 },
|
|
'3' => .{ 0b11110, 0b00001, 0b00001, 0b01110, 0b00001, 0b00001, 0b11110 },
|
|
'4' => .{ 0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010 },
|
|
'5' => .{ 0b11111, 0b10000, 0b10000, 0b11110, 0b00001, 0b00001, 0b11110 },
|
|
'6' => .{ 0b01110, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110 },
|
|
'7' => .{ 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000 },
|
|
'8' => .{ 0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110 },
|
|
'9' => .{ 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00001, 0b01110 },
|
|
|
|
'a' => .{ 0b00000, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111 },
|
|
'b' => .{ 0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b11110 },
|
|
'c' => .{ 0b00000, 0b00000, 0b01111, 0b10000, 0b10000, 0b10000, 0b01111 },
|
|
'd' => .{ 0b00001, 0b00001, 0b01101, 0b10011, 0b10001, 0b10001, 0b01111 },
|
|
'e' => .{ 0b00000, 0b00000, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110 },
|
|
'f' => .{ 0b00110, 0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01000 },
|
|
'g' => .{ 0b00000, 0b01111, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110 },
|
|
'i' => .{ 0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b00100, 0b01110 },
|
|
'h' => .{ 0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001 },
|
|
'k' => .{ 0b10000, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001 },
|
|
'l' => .{ 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110 },
|
|
'n' => .{ 0b00000, 0b00000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001 },
|
|
'p' => .{ 0b00000, 0b00000, 0b11110, 0b10001, 0b11110, 0b10000, 0b10000 },
|
|
'q' => .{ 0b00000, 0b00000, 0b01101, 0b10011, 0b01111, 0b00001, 0b00001 },
|
|
'r' => .{ 0b00000, 0b00000, 0b10110, 0b11001, 0b10000, 0b10000, 0b10000 },
|
|
't' => .{ 0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01001, 0b00110 },
|
|
'w' => .{ 0b00000, 0b00000, 0b10001, 0b10001, 0b10101, 0b10101, 0b01010 },
|
|
'y' => .{ 0b00000, 0b10001, 0b10001, 0b01111, 0b00001, 0b00001, 0b01110 },
|
|
|
|
'A' => .{ 0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001 },
|
|
'B' => .{ 0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110 },
|
|
'D' => .{ 0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110 },
|
|
'E' => .{ 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111 },
|
|
'I' => .{ 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b11111 },
|
|
'K' => .{ 0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001 },
|
|
'L' => .{ 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111 },
|
|
'N' => .{ 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001, 0b10001 },
|
|
'P' => .{ 0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000 },
|
|
'Q' => .{ 0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101 },
|
|
'R' => .{ 0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001 },
|
|
'S' => .{ 0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110 },
|
|
'T' => .{ 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100 },
|
|
'Y' => .{ 0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100 },
|
|
|
|
'#' => .{ 0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010 },
|
|
'-' => .{ 0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000 },
|
|
'/' => .{ 0b00001, 0b00010, 0b00010, 0b00100, 0b01000, 0b01000, 0b10000 },
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
fn appendGlyph(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
glyph: Glyph,
|
|
x: f32,
|
|
y: f32,
|
|
style: TextStyle,
|
|
) !void {
|
|
for (glyph, 0..) |row_bits, row| {
|
|
for (0..5) |col| {
|
|
const shift: u3 = @intCast(4 - col);
|
|
if (((row_bits >> shift) & 1) == 0) continue;
|
|
|
|
const pixel_width = style.pixel_size * style.pixel_aspect;
|
|
const x0 = x + (@as(f32, @floatFromInt(col)) * pixel_width);
|
|
const y0 = y + (@as(f32, @floatFromInt(6 - row)) * style.pixel_size);
|
|
const x1 = x0 + pixel_width;
|
|
const y1 = y0 + style.pixel_size;
|
|
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ x0, y0 },
|
|
.{ x1, y0 },
|
|
.{ x1, y1 },
|
|
.{ x0, y1 },
|
|
style.color,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn appendText(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
text: []const u8,
|
|
x: f32,
|
|
y: f32,
|
|
style: TextStyle,
|
|
) !void {
|
|
var cursor_x = x;
|
|
const advance = style.pixel_size * style.pixel_aspect * 6.0;
|
|
|
|
for (text) |ch| {
|
|
if (ch == ' ') {
|
|
cursor_x += advance;
|
|
continue;
|
|
}
|
|
|
|
if (glyphForChar(ch)) |glyph| {
|
|
try appendGlyph(vertices, allocator, glyph, cursor_x, y, style);
|
|
}
|
|
|
|
cursor_x += advance;
|
|
}
|
|
}
|
|
|
|
const SelectedHighlightColor = [4]f32{ 0.12, 0.55, 0.12, 0.48 };
|
|
const HoverHighlightColor = [4]f32{ 0.45, 0.90, 0.45, 0.38 };
|
|
|
|
fn appendSquareOverlay(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
square: board_input.SquareCoord,
|
|
color: [4]f32,
|
|
) !void {
|
|
const file: f32 = @floatFromInt(square.file);
|
|
const rank: f32 = @floatFromInt(square.rank);
|
|
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
geometry.boardToNdc(board_rect, file, rank),
|
|
geometry.boardToNdc(board_rect, file + 1.0, rank),
|
|
geometry.boardToNdc(board_rect, file + 1.0, rank + 1.0),
|
|
geometry.boardToNdc(board_rect, file, rank + 1.0),
|
|
color,
|
|
);
|
|
}
|
|
|
|
pub fn appendSelectedSquareHighlight(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
selected: ?board_input.SquareCoord,
|
|
) !void {
|
|
const square = selected orelse return;
|
|
try appendSquareOverlay(vertices, allocator, board_rect, square, SelectedHighlightColor);
|
|
}
|
|
|
|
pub fn appendPalettePieceHighlight(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
selected_piece: ?u4,
|
|
) !void {
|
|
const encoded = selected_piece orelse return;
|
|
const index = piece_render.paletteIndexForEncoded(encoded) orelse return;
|
|
const palette_rect = piece_render.paletteRectForBoard(board_rect);
|
|
const cell_height = palette_rect.height / @as(f32, @floatFromInt(piece_render.palette_entries.len));
|
|
const y = palette_rect.bottom + (@as(f32, @floatFromInt(piece_render.palette_entries.len - 1 - index)) * cell_height);
|
|
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ palette_rect.left, y },
|
|
.{ palette_rect.left + palette_rect.width, y },
|
|
.{ palette_rect.left + palette_rect.width, y + cell_height },
|
|
.{ palette_rect.left, y + cell_height },
|
|
.{ 0.45, 0.90, 0.45, 0.38 },
|
|
);
|
|
}
|
|
|
|
pub fn appendHoveredSquareHighlight(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
hovered: ?board_input.SquareCoord,
|
|
) !void {
|
|
const square = hovered orelse return;
|
|
try appendSquareOverlay(vertices, allocator, board_rect, square, HoverHighlightColor);
|
|
}
|
|
|
|
pub fn appendCheckBorder(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
checked: ?board_input.SquareCoord,
|
|
) !void {
|
|
const square = checked orelse return;
|
|
const file: f32 = @floatFromInt(square.file);
|
|
const rank: f32 = @floatFromInt(square.rank);
|
|
const thickness: f32 = 0.08;
|
|
const color = [4]f32{ 1.0, 0.05, 0.05, 0.85 };
|
|
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, file, rank), geometry.boardToNdc(board_rect, file + 1.0, rank), geometry.boardToNdc(board_rect, file + 1.0, rank + thickness), geometry.boardToNdc(board_rect, file, rank + thickness), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, file, rank + 1.0 - thickness), geometry.boardToNdc(board_rect, file + 1.0, rank + 1.0 - thickness), geometry.boardToNdc(board_rect, file + 1.0, rank + 1.0), geometry.boardToNdc(board_rect, file, rank + 1.0), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, file, rank), geometry.boardToNdc(board_rect, file + thickness, rank), geometry.boardToNdc(board_rect, file + thickness, rank + 1.0), geometry.boardToNdc(board_rect, file, rank + 1.0), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, file + 1.0 - thickness, rank), geometry.boardToNdc(board_rect, file + 1.0, rank), geometry.boardToNdc(board_rect, file + 1.0, rank + 1.0), geometry.boardToNdc(board_rect, file + 1.0 - thickness, rank + 1.0), color);
|
|
}
|
|
|
|
pub fn appendCheckmateMarker(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
winning_king: ?board_input.SquareCoord,
|
|
) !void {
|
|
const square = winning_king orelse return;
|
|
const square_w = board_rect.width / 8.0;
|
|
const square_h = board_rect.height / 8.0;
|
|
const pixel_size = @min(square_w, square_h) * 0.045;
|
|
const glyph_w = pixel_size * 5.0;
|
|
const glyph_h = pixel_size * 7.0;
|
|
const padding_x = square_w * 0.10;
|
|
const padding_y = square_h * 0.10;
|
|
const top_right = geometry.boardToNdc(
|
|
board_rect,
|
|
@as(f32, @floatFromInt(square.file)) + 1.0,
|
|
@as(f32, @floatFromInt(square.rank)) + 1.0,
|
|
);
|
|
|
|
try appendText(
|
|
vertices,
|
|
allocator,
|
|
"#",
|
|
top_right[0] - padding_x - glyph_w,
|
|
top_right[1] - padding_y - glyph_h,
|
|
.{ .pixel_size = pixel_size, .color = .{ 1.0, 0.92, 0.18, 1.0 } },
|
|
);
|
|
}
|
|
|
|
pub fn appendValidMoveDots(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
state: chess_board.BoardState,
|
|
valid_moves: bitboard.Bitboard,
|
|
) !void {
|
|
const square_w = board_rect.width / 8.0;
|
|
const square_h = board_rect.height / 8.0;
|
|
const radius_x = square_w * 0.11;
|
|
const radius_y = square_h * 0.11;
|
|
const color = SelectedHighlightColor;
|
|
const segments = 48;
|
|
|
|
var moves = valid_moves;
|
|
while (moves != 0) {
|
|
const move_square: bitboard.Square = @intCast(@ctz(moves));
|
|
moves &= moves - 1;
|
|
const file: u3 = @intCast(move_square % 8);
|
|
const rank: u3 = @intCast(move_square / 8);
|
|
if (state.getSquare(@intCast((@as(u6, rank) * 8) + @as(u6, file))) != 0) {
|
|
const x0 = @as(f32, @floatFromInt(file));
|
|
const x1 = x0 + 1.0;
|
|
const y0 = @as(f32, @floatFromInt(rank));
|
|
const y1 = y0 + 1.0;
|
|
const thickness: f32 = 0.07;
|
|
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, x0, y0), geometry.boardToNdc(board_rect, x1, y0), geometry.boardToNdc(board_rect, x1, y0 + thickness), geometry.boardToNdc(board_rect, x0, y0 + thickness), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, x0, y1 - thickness), geometry.boardToNdc(board_rect, x1, y1 - thickness), geometry.boardToNdc(board_rect, x1, y1), geometry.boardToNdc(board_rect, x0, y1), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, x0, y0), geometry.boardToNdc(board_rect, x0 + thickness, y0), geometry.boardToNdc(board_rect, x0 + thickness, y1), geometry.boardToNdc(board_rect, x0, y1), color);
|
|
try geometry.appendQuad(vertices, allocator, geometry.boardToNdc(board_rect, x1 - thickness, y0), geometry.boardToNdc(board_rect, x1, y0), geometry.boardToNdc(board_rect, x1, y1), geometry.boardToNdc(board_rect, x1 - thickness, y1), color);
|
|
} else {
|
|
const center = geometry.boardToNdc(
|
|
board_rect,
|
|
@as(f32, @floatFromInt(file)) + 0.5,
|
|
@as(f32, @floatFromInt(rank)) + 0.5,
|
|
);
|
|
|
|
var i: usize = 0;
|
|
while (i < segments) : (i += 1) {
|
|
const angle0 = (@as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(segments))) * std.math.tau;
|
|
const angle1 = (@as(f32, @floatFromInt(i + 1)) / @as(f32, @floatFromInt(segments))) * std.math.tau;
|
|
const p0 = [2]f32{ center[0] + (@cos(angle0) * radius_x), center[1] + (@sin(angle0) * radius_y) };
|
|
const p1 = [2]f32{ center[0] + (@cos(angle1) * radius_x), center[1] + (@sin(angle1) * radius_y) };
|
|
|
|
try vertices.append(allocator, .{ .position = center, .color = color, .uv = .{ 0.0, 0.0 } });
|
|
try vertices.append(allocator, .{ .position = p0, .color = color, .uv = .{ 0.0, 0.0 } });
|
|
try vertices.append(allocator, .{ .position = p1, .color = color, .uv = .{ 0.0, 0.0 } });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn appendRectBorder(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
rect: geometry.BoardRect,
|
|
thickness: f32,
|
|
color: [4]f32,
|
|
) !void {
|
|
const x0 = rect.left;
|
|
const x1 = rect.left + rect.width;
|
|
const y0 = rect.bottom;
|
|
const y1 = rect.bottom + rect.height;
|
|
|
|
try geometry.appendQuad(vertices, allocator, .{ x0, y0 }, .{ x1, y0 }, .{ x1, y0 + thickness }, .{ x0, y0 + thickness }, color);
|
|
try geometry.appendQuad(vertices, allocator, .{ x0, y1 - thickness }, .{ x1, y1 - thickness }, .{ x1, y1 }, .{ x0, y1 }, color);
|
|
try geometry.appendQuad(vertices, allocator, .{ x0, y0 }, .{ x0 + thickness, y0 }, .{ x0 + thickness, y1 }, .{ x0, y1 }, color);
|
|
try geometry.appendQuad(vertices, allocator, .{ x1 - thickness, y0 }, .{ x1, y0 }, .{ x1, y1 }, .{ x1 - thickness, y1 }, color);
|
|
}
|
|
|
|
pub fn appendPromotionPopup(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
square: bitboard.Square,
|
|
) !void {
|
|
const popup_rect = piece_render.promotionPopupRectForSquare(board_rect, square);
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ popup_rect.left, popup_rect.bottom },
|
|
.{ popup_rect.left + popup_rect.width, popup_rect.bottom },
|
|
.{ popup_rect.left + popup_rect.width, popup_rect.bottom + popup_rect.height },
|
|
.{ popup_rect.left, popup_rect.bottom + popup_rect.height },
|
|
.{ 0.04, 0.04, 0.04, 0.98 },
|
|
);
|
|
|
|
for (piece_render.promotion_piece_types, 0..) |_, i| {
|
|
const cell = piece_render.promotionChoiceRectForIndex(board_rect, square, i);
|
|
const border_color = geometry.White;
|
|
const thickness = board_rect.width / 220.0;
|
|
try appendRectBorder(vertices, allocator, cell, thickness, border_color);
|
|
}
|
|
|
|
}
|
|
|
|
pub fn appendModeMenu(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
edit_mode: bool,
|
|
) !void {
|
|
const button_w: f32 = 0.16;
|
|
const button_h: f32 = 0.08;
|
|
const reset_button_w: f32 = 0.22;
|
|
const gap: f32 = 0.025;
|
|
const top: f32 = 0.96;
|
|
const bottom = top - button_h;
|
|
const play_left: f32 = -0.95;
|
|
const edit_left = play_left + button_w + gap;
|
|
const reset_left = edit_left + button_w + gap;
|
|
const inactive = [4]f32{ 0.18, 0.18, 0.18, 1.0 };
|
|
const active = [4]f32{ 0.25, 0.45, 0.25, 1.0 };
|
|
const reset_color = [4]f32{ 0.25, 0.25, 0.35, 1.0 };
|
|
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ play_left, bottom },
|
|
.{ play_left + button_w, bottom },
|
|
.{ play_left + button_w, top },
|
|
.{ play_left, top },
|
|
if (edit_mode) inactive else active,
|
|
);
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ edit_left, bottom },
|
|
.{ edit_left + button_w, bottom },
|
|
.{ edit_left + button_w, top },
|
|
.{ edit_left, top },
|
|
if (edit_mode) active else inactive,
|
|
);
|
|
try geometry.appendQuad(
|
|
vertices,
|
|
allocator,
|
|
.{ reset_left, bottom },
|
|
.{ reset_left + reset_button_w, bottom },
|
|
.{ reset_left + reset_button_w, top },
|
|
.{ reset_left, top },
|
|
reset_color,
|
|
);
|
|
|
|
const pixel_size: f32 = button_h / 13.0;
|
|
const y = bottom + (button_h - pixel_size * 7.0) / 2.0;
|
|
try appendText(vertices, allocator, "PLAY", play_left + 0.018, y, .{ .pixel_size = pixel_size, .color = geometry.White });
|
|
try appendText(vertices, allocator, "EDIT", edit_left + 0.018, y, .{ .pixel_size = pixel_size, .color = geometry.White });
|
|
try appendText(vertices, allocator, "RESET", reset_left + 0.018, y, .{ .pixel_size = pixel_size, .color = geometry.White });
|
|
}
|
|
|
|
pub fn appendBoardCoordinateLabels(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
) !void {
|
|
const square_w = board_rect.width / 8.0;
|
|
const square_h = board_rect.height / 8.0;
|
|
const square_size = if (square_w < square_h) square_w else square_h;
|
|
const pixel_size = square_size / 36.0;
|
|
const padding = square_size / 24.0;
|
|
const glyph_w = pixel_size * 5.0;
|
|
const glyph_h = pixel_size * 7.0;
|
|
|
|
const files = "abcdefgh";
|
|
for (files, 0..) |file_ch, file| {
|
|
const is_dark = (file % 2) == 0;
|
|
const color = if (is_dark) geometry.Light else geometry.Dark;
|
|
const x = board_rect.left + (@as(f32, @floatFromInt(file + 1)) * square_w) - padding - glyph_w;
|
|
const y = board_rect.bottom + padding;
|
|
|
|
try appendText(vertices, allocator, files[file .. file + 1], x, y, .{
|
|
.pixel_size = pixel_size,
|
|
.color = color,
|
|
});
|
|
|
|
_ = file_ch;
|
|
}
|
|
|
|
const ranks = "12345678";
|
|
for (ranks, 0..) |rank_ch, rank| {
|
|
const is_dark = (rank % 2) == 0;
|
|
const color = if (is_dark) geometry.Light else geometry.Dark;
|
|
const x = board_rect.left + padding;
|
|
const y = board_rect.bottom + (@as(f32, @floatFromInt(rank + 1)) * square_h) - padding - glyph_h;
|
|
|
|
try appendText(vertices, allocator, ranks[rank .. rank + 1], x, y, .{
|
|
.pixel_size = pixel_size,
|
|
.color = color,
|
|
});
|
|
|
|
_ = rank_ch;
|
|
}
|
|
}
|
|
|
|
pub fn appendFenText(
|
|
vertices: *std.ArrayList(geometry.Vertex),
|
|
allocator: std.mem.Allocator,
|
|
board_rect: geometry.BoardRect,
|
|
fen_text: []const u8,
|
|
) !void {
|
|
const max_text_width = board_rect.width;
|
|
const pixel_aspect = board_rect.width / board_rect.height;
|
|
const natural_pixel_size = board_rect.height / 95.0;
|
|
const fit_pixel_size = max_text_width / (@as(f32, @floatFromInt(fen_text.len)) * 6.0 * pixel_aspect);
|
|
const pixel_size = if (fit_pixel_size < natural_pixel_size) fit_pixel_size else natural_pixel_size;
|
|
const gap = board_rect.height / 18.0;
|
|
const y = board_rect.bottom - gap - (pixel_size * 7.0);
|
|
|
|
try appendText(vertices, allocator, fen_text, board_rect.left, y, .{
|
|
.pixel_size = pixel_size,
|
|
.pixel_aspect = pixel_aspect,
|
|
.color = geometry.White,
|
|
});
|
|
}
|
|
|
|
test "appendText appends vertices for supported glyph pixels" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendText(&vertices, std.testing.allocator, "1", 0.0, 0.0, .{
|
|
.pixel_size = 0.01,
|
|
.color = geometry.White,
|
|
});
|
|
|
|
try std.testing.expect(vertices.items.len > 0);
|
|
try std.testing.expectEqual(@as(usize, 0), vertices.items.len % 6);
|
|
}
|
|
|
|
test "appendSelectedSquareHighlight appends one quad when selected" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendSelectedSquareHighlight(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
.{ .file = 4, .rank = 1 },
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 6), vertices.items.len);
|
|
}
|
|
|
|
test "appendSelectedSquareHighlight appends nothing without selection" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendSelectedSquareHighlight(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
null,
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 0), vertices.items.len);
|
|
}
|
|
|
|
test "appendHoveredSquareHighlight appends one quad when hovered" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendHoveredSquareHighlight(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
.{ .file = 2, .rank = 2 },
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 6), vertices.items.len);
|
|
}
|
|
|
|
test "appendCheckBorder appends four border quads when checked" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendCheckBorder(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
.{ .file = 4, .rank = 0 },
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 4 * 6), vertices.items.len);
|
|
}
|
|
|
|
test "appendCheckBorder appends nothing without checked square" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendCheckBorder(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
null,
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 0), vertices.items.len);
|
|
}
|
|
|
|
test "appendCheckmateMarker appends hash glyph when winner is present" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendCheckmateMarker(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
.{ .file = 5, .rank = 5 },
|
|
);
|
|
|
|
try std.testing.expect(vertices.items.len > 0);
|
|
}
|
|
|
|
test "appendCheckmateMarker appends nothing without winner" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
try appendCheckmateMarker(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
null,
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 0), vertices.items.len);
|
|
}
|
|
|
|
test "appendValidMoveDots appends one circular triangle fan per move" {
|
|
var vertices: std.ArrayList(geometry.Vertex) = .empty;
|
|
defer vertices.deinit(std.testing.allocator);
|
|
|
|
const state = chess_board.BoardState.empty();
|
|
const moves = bitboard.bit(20) | bitboard.bit(28);
|
|
|
|
try appendValidMoveDots(
|
|
&vertices,
|
|
std.testing.allocator,
|
|
geometry.boardRectForExtent(800, 600),
|
|
state,
|
|
moves,
|
|
);
|
|
|
|
try std.testing.expectEqual(@as(usize, 2 * 48 * 3), vertices.items.len);
|
|
}
|