Files
chess/frontend/visuals.js

176 lines
4.6 KiB
JavaScript

const BOX_SIDE = 80;
const INDEX_MARGIN = BOX_SIDE / 2;
const INDEX_LETTERS = "abcdefgh";
export class Canvas {
constructor() {
this._canvas = document.createElement("canvas");
this._ctx = this._canvas.getContext("2d");
let boardSide = BOX_SIDE * 8 + INDEX_MARGIN;
this._canvas.width = boardSide;
this._canvas.height = boardSide;
document.body.appendChild(this._canvas);
this.drawChessBoard();
}
decideColor(r, c) {
if ((r + c) % 2) return "#c0c0c0";
else return "#ffffff";
}
drawSquareColor(color, r, c) {
const crtStyle = this._ctx.fillStyle;
this._ctx.fillStyle = color;
this._ctx.fillRect(
c * BOX_SIDE + INDEX_MARGIN,
r * BOX_SIDE + INDEX_MARGIN,
BOX_SIDE,
BOX_SIDE
);
this._ctx.fillStyle = crtStyle;
}
drawChessBoard() {
// Draw squares
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
this.drawSquareColor(this.decideColor(r, c), r, c);
}
}
// Draw letters
this._ctx.fillStyle = "black";
let fontsize = INDEX_MARGIN / 2;
this._ctx.font = `${fontsize}px Monospace`;
for (let idx = 0; idx < 8; idx++) {
this._ctx.fillText(
INDEX_LETTERS[idx],
BOX_SIDE * idx + BOX_SIDE / 2 + INDEX_MARGIN - fontsize / 4,
INDEX_MARGIN / 2
);
this._ctx.fillText(
(8 - idx).toString(),
INDEX_MARGIN / 2 - fontsize / 4,
BOX_SIDE * idx + BOX_SIDE / 2 + INDEX_MARGIN + fontsize / 4
);
}
}
screenCoordsToRowCol(cx, cy) {
let canvasRect = this._canvas.getBoundingClientRect();
if (cx > canvasRect.right || cy > canvasRect.bottom) {
return null;
}
let canvasX = cx - canvasRect.left;
let canvasY = cy - canvasRect.top;
let row = Math.floor((canvasY - INDEX_MARGIN) / BOX_SIDE);
let col = Math.floor((canvasX - INDEX_MARGIN) / BOX_SIDE);
return [row, col];
}
rowColToScreenCoords(row, col) {
let canvasRect = this._canvas.getBoundingClientRect();
let topPx =
canvasRect.top + INDEX_MARGIN + row * BOX_SIDE + window.pageYOffset;
let leftPx =
canvasRect.left + INDEX_MARGIN + col * BOX_SIDE + window.pageXOffset;
return [topPx, leftPx];
}
}
function boardIndexToRowCol(boardIndex) {
let [colLetter, rowDigit] = boardIndex;
let row = 8 - parseInt(rowDigit);
let col = INDEX_LETTERS.indexOf(colLetter);
return [row, col];
}
export function rowColToBoardIndex(row, col) {
let rowDigit = 8 - row;
let colLetter = INDEX_LETTERS[col];
return `${colLetter}${rowDigit}`;
}
export class ActiveSquares {
constructor() {
this.selectedSquare = null;
}
selectSquare(canvas, position) {
let [r, c] = boardIndexToRowCol(position);
canvas.drawSquareColor("#c0c0ff", r, c);
this.selectedSquare = position;
}
unselectSquare(canvas) {
if (this.selectedSquare === null) return;
let [r, c] = boardIndexToRowCol(this.selectedSquare);
canvas.drawSquareColor(canvas.decideColor(r, c), r, c);
this.selectedSquare = null;
}
}
class PieceVis {
constructor(piece) {
this.piece = piece;
this.im = null;
}
get ["icon"]() {
return `./icons/${this.piece.piece_type}_${this.piece.color}.svg`;
}
draw(canvas, position) {
let [topPx, leftPx] = canvas.rowColToScreenCoords(
...boardIndexToRowCol(position),
canvas
);
if (this.im === null) {
let im = new Image(BOX_SIDE, BOX_SIDE);
im.src = this.icon;
im.onload = () => {
im.style.position = "absolute";
im.style.top = `${topPx}px`;
im.style.left = `${leftPx}px`;
};
document.body.appendChild(im);
this.im = im;
} else {
this.im.style.top = `${topPx}px`;
this.im.style.left = `${leftPx}px`;
}
}
undraw() {
if (this.im !== null) {
this.im.remove();
this.im = null;
}
}
}
export class ConfigVis {
constructor(configuration) {
this.configuration = configuration;
this.piecesVis = new Map();
}
draw(canvas) {
for (let [position, piece] of this.configuration.board) {
if (this.piecesVis.has(position)) {
if (this.piecesVis.get(position).piece == piece) {
continue;
}
}
let pv = this.piecesVis.get(position);
if (pv !== undefined) {
pv.undraw();
}
pv = new PieceVis(piece);
pv.draw(canvas, position);
this.piecesVis.set(position, pv);
}
for (let [position, pv] of this.piecesVis) {
if (!this.configuration.board.has(position)) {
pv.undraw();
this.piecesVis.delete(position);
}
}
}
undraw() {
for (let pv of this.piecesVis.values()) {
pv.undraw();
}
this.piecesVis.clear();
}
}