zig-chess/src/geometry.zig

251 lines
6.9 KiB
Zig

const std = @import("std");
const board = @import("chess/board.zig");
const piece = @import("chess/piece.zig");
pub const Vertex = extern struct {
position: [2]f32,
color: [4]f32,
uv: [2]f32,
};
pub const BoardRect = struct {
left: f32,
bottom: f32,
width: f32,
height: f32,
};
pub const Light = [4]f32{ 0.85, 0.75, 0.60, 1.0 };
pub const Dark = [4]f32{ 0.35, 0.20, 0.10, 1.0 };
pub const White = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
pub fn boardRectForExtent(width: u32, height: u32) BoardRect {
return boardRectForExtentWithPalette(width, height, true);
}
pub fn boardRectForExtentWithPalette(width: u32, height: u32, show_palette: bool) BoardRect {
const window_w: f32 = @floatFromInt(width);
const window_h: f32 = @floatFromInt(height);
const margin_px: f32 = 32.0;
const palette_gap_px: f32 = if (show_palette) 16.0 else 0.0;
const palette_width_ratio: f32 = if (show_palette) 1.0 / 12.0 else 0.0;
const menu_area_px: f32 = 80.0;
const fen_gap_ratio: f32 = 1.0 / 18.0;
const fen_glyph_height_ratio: f32 = 7.0 / 95.0;
const fen_area_ratio = fen_gap_ratio + fen_glyph_height_ratio;
const available_w = @max(1.0, window_w - (2.0 * margin_px) - palette_gap_px);
const available_h = @max(1.0, window_h - (2.0 * margin_px) - menu_area_px);
const board_from_width = available_w / (1.0 + palette_width_ratio);
const board_from_height = available_h / (1.0 + fen_area_ratio);
const board_size_px = @min(board_from_width, board_from_height);
const palette_width_px = board_size_px * palette_width_ratio;
const group_width_px = board_size_px + palette_gap_px + palette_width_px;
const board_left_px = (window_w - group_width_px) / 2.0;
const board_bottom_px = margin_px + (board_size_px * fen_area_ratio);
const max_board_top_px = window_h - margin_px - menu_area_px;
const board_top_px = @min(board_bottom_px + board_size_px, max_board_top_px);
const adjusted_board_size_px = @max(1.0, board_top_px - board_bottom_px);
return .{
.left = (board_left_px / window_w) * 2.0 - 1.0,
.bottom = (board_bottom_px / window_h) * 2.0 - 1.0,
.width = (adjusted_board_size_px / window_w) * 2.0,
.height = (adjusted_board_size_px / window_h) * 2.0,
};
}
pub fn appendQuad(
vertices: *std.ArrayList(Vertex),
allocator: std.mem.Allocator,
bottom_left: [2]f32,
bottom_right: [2]f32,
top_right: [2]f32,
top_left: [2]f32,
color: [4]f32,
) !void {
try appendTexturedQuad(
vertices,
allocator,
bottom_left,
bottom_right,
top_right,
top_left,
color,
.{ 0.0, 0.0 },
.{ 0.0, 0.0 },
.{ 0.0, 0.0 },
.{ 0.0, 0.0 },
);
}
pub fn appendTexturedQuad(
vertices: *std.ArrayList(Vertex),
allocator: std.mem.Allocator,
bottom_left: [2]f32,
bottom_right: [2]f32,
top_right: [2]f32,
top_left: [2]f32,
color: [4]f32,
bottom_left_uv: [2]f32,
bottom_right_uv: [2]f32,
top_right_uv: [2]f32,
top_left_uv: [2]f32,
) !void {
try vertices.append(allocator, .{
.position = bottom_left,
.color = color,
.uv = bottom_left_uv,
});
try vertices.append(allocator, .{
.position = bottom_right,
.color = color,
.uv = bottom_right_uv,
});
try vertices.append(allocator, .{
.position = top_right,
.color = color,
.uv = top_right_uv,
});
try vertices.append(allocator, .{
.position = bottom_left,
.color = color,
.uv = bottom_left_uv,
});
try vertices.append(allocator, .{
.position = top_right,
.color = color,
.uv = top_right_uv,
});
try vertices.append(allocator, .{
.position = top_left,
.color = color,
.uv = top_left_uv,
});
}
pub fn appendChessboard(
vertices: *std.ArrayList(Vertex),
board_rect: BoardRect,
allocator: std.mem.Allocator,
) !void {
var rank: usize = 0;
while (rank < 8) : (rank += 1) {
var file: usize = 0;
while (file < 8) : (file += 1) {
const color = if ((rank + file) % 2 == 0) Dark else Light;
const x0: f32 = @floatFromInt(file);
const x1: f32 = @floatFromInt(file + 1);
const y0: f32 = @floatFromInt(rank);
const y1: f32 = @floatFromInt(rank + 1);
try appendQuad(
vertices,
allocator,
boardToNdc(board_rect, x0, y0),
boardToNdc(board_rect, x1, y0),
boardToNdc(board_rect, x1, y1),
boardToNdc(board_rect, x0, y1),
color,
);
}
}
}
pub fn appendPieceQuad(
vertices: *std.ArrayList(Vertex),
board_rect: BoardRect,
allocator: std.mem.Allocator,
file: f32,
rank: f32,
) !void {
try appendPieceQuadWithColor(vertices, board_rect, allocator, file, rank, White);
}
pub fn appendPieceQuadWithColor(
vertices: *std.ArrayList(Vertex),
board_rect: BoardRect,
allocator: std.mem.Allocator,
file: f32,
rank: f32,
color: [4]f32,
) !void {
const inset: f32 = 0.08;
const x0 = file + inset;
const x1 = file + 1.0 - inset;
const y0 = rank + inset;
const y1 = rank + 1.0 - inset;
try appendTexturedQuad(
vertices,
allocator,
boardToNdc(board_rect, x0, y0),
boardToNdc(board_rect, x1, y0),
boardToNdc(board_rect, x1, y1),
boardToNdc(board_rect, x0, y1),
color,
.{ 0.0, 1.0 },
.{ 1.0, 1.0 },
.{ 1.0, 0.0 },
.{ 0.0, 0.0 },
);
}
pub fn appendPieceQuadCenteredAtNdc(
vertices: *std.ArrayList(Vertex),
allocator: std.mem.Allocator,
center: [2]f32,
size: [2]f32,
color: [4]f32,
) !void {
const half_w = size[0] / 2.0;
const half_h = size[1] / 2.0;
const x0 = center[0] - half_w;
const x1 = center[0] + half_w;
const y0 = center[1] - half_h;
const y1 = center[1] + half_h;
try appendTexturedQuad(
vertices,
allocator,
.{ x0, y0 },
.{ x1, y0 },
.{ x1, y1 },
.{ x0, y1 },
color,
.{ 0.0, 1.0 },
.{ 1.0, 1.0 },
.{ 1.0, 0.0 },
.{ 0.0, 0.0 },
);
}
pub fn appendPiecesFromBoard(
vertices: *std.ArrayList(Vertex),
board_rect: BoardRect,
allocator: std.mem.Allocator,
state: board.BoardState,
) !void {
for (0..8) |rank| {
for (0..8) |file| {
const p = state.getSquare(@intCast((rank * 8) + file));
if (piece.typeOf(p) != piece.PieceType.none) {
try appendPieceQuad(vertices, board_rect, allocator, @floatFromInt(file), @floatFromInt(rank));
}
}
}
}
pub fn boardToNdc(rect: BoardRect, x: f32, y: f32) [2]f32 {
return .{
rect.left + (x / 8.0) * rect.width,
rect.bottom + (y / 8.0) * rect.height,
};
}