From 935f99c22dc7b81a8703e46c1e43144f0d99a47e Mon Sep 17 00:00:00 2001 From: sfja Date: Wed, 15 Oct 2025 00:34:45 +0200 Subject: [PATCH] add rotate and stuff --- src/gamelib.js | 118 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/src/gamelib.js b/src/gamelib.js index eb20d3e..6204cec 100644 --- a/src/gamelib.js +++ b/src/gamelib.js @@ -106,19 +106,15 @@ export class Gamelib { ev.preventDefault(); } - drawSprite(x, y, width, height, name) { - const cx = this.cx; + #loadSprite(width, height, name) { const spriteId = `${name}_${width}x${height}`; - if (this.spriteCache.has(spriteId)) { - const sprite = this.spriteCache.get(spriteId); - cx.drawImage(sprite, x, y); - return; + return this.spriteCache.get(spriteId); } // start by caching an empty canvas - const canvas = new OffscreenCanvas(width, height); - this.spriteCache.set(spriteId, canvas); + const sprite = new OffscreenCanvas(width, height); + this.spriteCache.set(spriteId, sprite); const image = new Image(); image.src = this.assetProvider.url(name); @@ -127,8 +123,60 @@ export class Gamelib { const spriteCx = sprite.getContext("2d"); spriteCx.drawImage(image, 0, 0); - // does it make sense to draw post fectum? + const invalidatedKeys = this.spriteCache.keys() + .filter((key) => key.startsWith(`${spriteId}r`)); + for (const key of invalidatedKeys) { + this.spriteCache.delete(key); + } }; + + return sprite; + } + + drawSprite(x, y, width, height, name) { + const cx = this.cx; + + const sprite = this.#loadSprite(width, height, name); + cx.drawImage(sprite, x, y); + } + + drawSpriteRotated(x, y, width, height, name, angle) { + const cx = this.cx; + + const angleIncrement = Math.PI * 2 / 360; + const angleNormalized = Math.floor((angle % (Math.PI * 2)) / angleIncrement) * + angleIncrement; + + const rotatedSpriteId = `${name}_${width}x${height}r${angleNormalized}`; + if (this.spriteCache.has(rotatedSpriteId)) { + const sprite = this.spriteCache.get(rotatedSpriteId); + cx.drawImage(sprite, x, y); + return; + } + + const sprite = this.#loadSprite(width, height, name); + + const newSprite = new OffscreenCanvas(sprite.width, sprite.height); + const newSpriteCx = newSprite.getContext("2d"); + + newSpriteCx.imageSmoothingEnabled = false; + newSpriteCx.save(); + newSpriteCx.translate(sprite.width / 2, sprite.height / 2); + newSpriteCx.rotate(angleNormalized); + + newSpriteCx.drawImage( + sprite, + -sprite.width / 2, + -sprite.height / 2, + sprite.width, + sprite.height, + ); + + newSpriteCx.restore(); + + cx.drawImage(newSprite, x, y); + + this.spriteCache.set(rotatedSpriteId, newSprite); } clear(color) { @@ -145,6 +193,42 @@ export class Gamelib { cx.fillRect(x, y, width, height); } + drawLine(x0, y0, x1, y1, thickness, color) { + const cx = this.cx; + + cx.strokeStyle = color; + cx.lineWidth = thickness; + cx.beginPath(); + cx.moveTo(x0, y0); + cx.lineTo(x1, y1); + cx.stroke(); + } + + drawPath(path, color) { + const cx = this.cx; + + cx.fillStyle = color; + cx.beginPath(); + cx.moveTo(path[0][0], path[0][1]); + for (const [x, y] of path.slice(1)) { + cx.lineTo(x, y); + } + cx.fill(); + } + + drawPathLine(path, thickness, color) { + const cx = this.cx; + + cx.strokeStyle = color; + cx.lineWidth = thickness; + cx.beginPath(); + cx.moveTo(path[0][0], path[0][1]); + for (const [x, y] of path.slice(1)) { + cx.lineTo(x, y); + } + cx.stroke(); + } + drawText(x, y, text, style = {}) { const cx = this.cx; @@ -239,6 +323,10 @@ export class GamelibAdapter { this.gamelib.drawSprite(x, y, width, height, name); } + drawSpriteRotated(x, y, width, height, name, angle) { + this.gamelib.drawSpriteRotated(x, y, width, height, name, angle); + } + rgb(red, green, blue) { return `rgb(${red}, ${green}, ${blue})`; } @@ -251,6 +339,18 @@ export class GamelibAdapter { this.gamelib.drawRect(x, y, width, height, color); } + drawLine(x0, y0, x1, y1, thickness, color) { + this.gamelib.drawLine(x0, y0, x1, y1, thickness, color); + } + + drawPath(path, color) { + this.gamelib.drawPath(path, color); + } + + drawPathLine(path, thickness, color) { + this.gamelib.drawPathLine(path, thickness, color); + } + drawText(x, y, text, style = {}) { this.gamelib.drawText(x, y, text, style); }