From 140ad0f22c85f49fdd8f015b2cf62d00f859ab11 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 15 May 2023 22:20:40 -0400 Subject: [PATCH] Render ghost stones --- kifu/core-types/Makefile | 4 +- kifu/core-types/package.json | 3 +- kifu/pwa/src/components/Board.ts | 172 ++++++++++++++++++++----------- package-lock.json | 1 + 4 files changed, 118 insertions(+), 62 deletions(-) diff --git a/kifu/core-types/Makefile b/kifu/core-types/Makefile index d42b5a0..9806904 100644 --- a/kifu/core-types/Makefile +++ b/kifu/core-types/Makefile @@ -1,6 +1,6 @@ SOURCES = $(shell find ../core -name "*.rs") -dist/core.d.ts: $(SOURCES) +dist/index.ts: $(SOURCES) mkdir -p dist - typeshare ../core --lang=typescript --output-file=dist/core.d.ts + typeshare ../core --lang=typescript --output-file=dist/index.ts diff --git a/kifu/core-types/package.json b/kifu/core-types/package.json index 4691e70..19766f8 100644 --- a/kifu/core-types/package.json +++ b/kifu/core-types/package.json @@ -2,7 +2,8 @@ "name": "core-types", "version": "0.0.1", "description": "", - "types": "dist/core.d.ts", + "types": "dist/index.ts", + "main": "dist/index.ts", "scripts": { "build": "make", "test": "echo \"Error: no test specified\" && exit 1" diff --git a/kifu/pwa/src/components/Board.ts b/kifu/pwa/src/components/Board.ts index 1bef6a4..7ddb849 100644 --- a/kifu/pwa/src/components/Board.ts +++ b/kifu/pwa/src/components/Board.ts @@ -1,10 +1,16 @@ import { BoardElement, Color } from "core-types"; +const MARGIN = 20; const BOARD_WIDTH = 800; const BOARD_HEIGHT = 800; +type Pixel = { x: number; y: number }; +type Address = { column: number; row: number }; + export class GoBoard { private board: BoardElement; + private pen: Pen; + private cursorLocation: Address | null; canvas: HTMLCanvasElement; constructor(board: BoardElement) { @@ -12,10 +18,43 @@ export class GoBoard { this.canvas = document.createElement("canvas"); this.canvas.width = BOARD_WIDTH; this.canvas.height = BOARD_HEIGHT; + + this.pen = new Pen( + this.canvas.width, + this.canvas.height, + MARGIN, + this.board.size.width, + this.board.size.height + ); + + this.cursorLocation = null; + + this.canvas.onmousemove = (event) => { + const bounds = this.canvas.getBoundingClientRect(); + const coordinate = { + x: event.clientX - bounds.x, + y: event.clientY - bounds.y, + }; + + let address = this.pen.address(coordinate); + + if (this.cursorLocation != address) { + this.cursorLocation = this.pen.address(coordinate); + this.renderBoard(); + } + }; } setBoard(board: BoardElement) { this.board = board; + + this.pen = new Pen( + this.canvas.width, + this.canvas.height, + MARGIN, + this.board.size.width, + this.board.size.height + ); } renderBoard() { @@ -25,97 +64,112 @@ export class GoBoard { return null; } - let hspaceBetween = (this.canvas.width - 40) / (this.board.size.width - 1); - let vspaceBetween = - (this.canvas.height - 40) / (this.board.size.height - 1); + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ctx.lineWidth = 2; ctx.strokeStyle = "black"; ctx.beginPath(); for (var col = 0; col < this.board.size.width; col++) { - ctx.moveTo(20 + col * hspaceBetween, 20); - ctx.lineTo(20 + col * hspaceBetween, this.canvas.height - 20); + ctx.moveTo(MARGIN + col * this.pen.hspaceBetween, MARGIN); + ctx.lineTo( + MARGIN + col * this.pen.hspaceBetween, + MARGIN + (this.board.size.height - 1) * this.pen.vspaceBetween + ); } for (var row = 0; row < this.board.size.height; row++) { - ctx.moveTo(20, 20 + row * vspaceBetween); - ctx.lineTo(this.canvas.width - 20, 20 + row * vspaceBetween); + ctx.moveTo(MARGIN, MARGIN + row * this.pen.vspaceBetween); + ctx.lineTo( + MARGIN + (this.board.size.width - 1) * this.pen.hspaceBetween, + MARGIN + row * this.pen.vspaceBetween + ); } ctx.closePath(); ctx.stroke(); - const pen = new Pen(20, 20, hspaceBetween, vspaceBetween); - pen.starPoint(ctx, 3, 3); - pen.starPoint(ctx, 3, 9); - pen.starPoint(ctx, 3, 15); - pen.starPoint(ctx, 9, 3); - pen.starPoint(ctx, 9, 9); - pen.starPoint(ctx, 9, 15); - pen.starPoint(ctx, 15, 3); - pen.starPoint(ctx, 15, 9); - pen.starPoint(ctx, 15, 15); + this.pen.starPoint(ctx, { column: 3, row: 3 }); + this.pen.starPoint(ctx, { column: 3, row: 9 }); + this.pen.starPoint(ctx, { column: 3, row: 15 }); + this.pen.starPoint(ctx, { column: 9, row: 3 }); + this.pen.starPoint(ctx, { column: 9, row: 9 }); + this.pen.starPoint(ctx, { column: 9, row: 15 }); + this.pen.starPoint(ctx, { column: 15, row: 3 }); + this.pen.starPoint(ctx, { column: 15, row: 9 }); + this.pen.starPoint(ctx, { column: 15, row: 15 }); + + if (this.cursorLocation) { + this.pen.ghostStone(ctx, this.cursorLocation, Color.White); + } } } class Pen { - xOffset: number; - yOffset: number; + margin: number; hspaceBetween: number; vspaceBetween: number; constructor( - xOffset: number, - yOffset: number, - hspaceBetween: number, - vspaceBetween: number + width: number, + height: number, + margin: number, + columns: number, + rows: number ) { - this.xOffset = xOffset; - this.yOffset = yOffset; - this.hspaceBetween = hspaceBetween; - this.vspaceBetween = vspaceBetween; + this.margin = margin; + this.hspaceBetween = (width - margin * 2) / (columns - 1); + this.vspaceBetween = (height - margin * 2) / (rows - 1); } - starPoint(ctx: CanvasRenderingContext2D, row: number, col: number) { - ctx.moveTo( - this.xOffset + col * this.hspaceBetween, - this.yOffset + row * this.vspaceBetween - ); - ctx.arc( - this.xOffset + col * this.hspaceBetween, - this.yOffset + row * this.vspaceBetween, - 5, - 0, - 2 * Math.PI - ); + starPoint(ctx: CanvasRenderingContext2D, addr: Address) { + ctx.fillStyle = "rgba(0, 0, 0, 1.0);"; + const pixel = this.position(addr); + ctx.moveTo(pixel.x, pixel.y); + ctx.arc(pixel.x, pixel.y, 5, 0, 2 * Math.PI); ctx.fill(); } - ghostStone( - ctx: CanvasRenderingContext2D, - row: number, - col: number, - color: Color - ) { + ghostStone(ctx: CanvasRenderingContext2D, addr: Address, color: Color) { switch (color) { - case "White": - ctx.fillStyle = "white"; - case "Black": - ctx.fillStyle = "black"; + case Color.White: + ctx.fillStyle = "rgba(230, 230, 230, 0.5)"; + break; + case Color.Black: + ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; + break; } - this.drawStone(ctx, row, col); + this.drawStone(ctx, addr); } - drawStone(ctx: CanvasRenderingContext2D, row: number, col: number) { - let radius = this.hspaceBetween / 2 - 2; - let [xLoc, yLoc] = this.stoneLocation(row, col); - ctx.arc(xLoc, yLoc, radius, 0, 2.0 * Math.PI); + drawStone(ctx: CanvasRenderingContext2D, addr: Address) { + const radius = this.hspaceBetween / 2 - 2; + const pixel = this.position(addr); + ctx.moveTo(pixel.x, pixel.y); + ctx.arc(pixel.x, pixel.y, radius, 0, 2.0 * Math.PI); ctx.fill(); } - stoneLocation(row: number, col: number): [number, number] { - return [ - this.xOffset + col * this.hspaceBetween, - this.yOffset + col * this.vspaceBetween, - ]; + position(addr: Address): Pixel { + return { + x: this.margin + addr.column * this.hspaceBetween, + y: this.margin + addr.row * this.vspaceBetween, + }; + } + + address(pixel: Pixel): Address | null { + if ( + Math.round(pixel.x) < this.margin || + Math.round(pixel.y) < this.margin + ) { + return null; + } else { + return { + column: Math.round( + (Math.round(pixel.x) - this.margin) / this.hspaceBetween + ), + row: Math.round( + (Math.round(pixel.y) - this.margin) / this.vspaceBetween + ), + }; + } } } diff --git a/package-lock.json b/package-lock.json index 753687b..32d874d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ } }, "kifu/pwa": { + "name": "kifu-pwa", "version": "1.0.0", "license": "GPL-3.0-or-later", "dependencies": {