WIP script to generate PNG images from board configs

This commit is contained in:
Maciej Caderek
2022-04-30 07:23:55 +02:00
parent dd013b139f
commit d625ff4610
97 changed files with 1472 additions and 64 deletions

View File

@@ -1,5 +1,13 @@
import { BoardConfig, Header, Position } from "./../types";
import { Style, BoardStyle } from "../types";
import {
BoardConfig,
CreateCanvas,
Header,
LoadImage,
Position,
Style,
BoardStyle,
} from "./../types";
// @ts-ignore
import drawRectangle from "./layers/drawRectangle";
import drawCoords from "./layers/drawCoords";
import drawMoveIndicators from "./layers/drawMoveIndicators";
@@ -9,6 +17,7 @@ import drawExtraInfo from "./layers/drawExtraInfo";
import boards from "./styles-board/boardStyles";
import isLink from "../utils/isLink";
import { PiecesStyle } from "./styles-pieces/piecesStyles";
import loadImageBrowser from "./loaders/loadImage";
const defaultConfig: BoardConfig = {
size: 720,
@@ -62,13 +71,19 @@ class Board {
private ctx: CanvasRenderingContext2D;
private tempCtx: CanvasRenderingContext2D;
private tempCanvas: HTMLCanvasElement = document.createElement("canvas");
public canvas: HTMLCanvasElement = document.createElement("canvas");
private tempCanvas: HTMLCanvasElement;
public canvas: HTMLCanvasElement;
constructor(
config: Partial<BoardConfig> = {},
private loadImage: LoadImage = loadImageBrowser,
private createCanvas: CreateCanvas = () => document.createElement("canvas")
) {
this.canvas = this.createCanvas();
this.tempCanvas = this.createCanvas();
constructor(config: Partial<BoardConfig> = {}) {
const ctx = this.canvas.getContext("2d");
const tempCtx = this.tempCanvas.getContext("2d");
this.canvas.classList.add("board");
if (ctx === null || tempCtx === null) {
throw new Error("Cannot create canvas 2D context");
@@ -94,7 +109,7 @@ class Board {
this.cfg = cfg;
this.setSize(cfg.size);
this.setStyle(cfg.boardStyle);
this.setStyle(cfg.boardStyle, refresh);
if (refresh) {
await this.refresh();
@@ -151,10 +166,12 @@ class Board {
this.borderScale = scale;
}
setStyle(style: BoardStyle) {
setStyle(style: BoardStyle, refresh: boolean = true) {
this.style = boards[style];
this.cfg.boardStyle = style;
this.refresh();
if (refresh) {
this.refresh();
}
return this;
}
@@ -252,12 +269,13 @@ class Board {
this.scale,
this.margin,
this.style,
this.getFinalHeader()
this.getFinalHeader(),
this.loadImage
);
}
async renderBackground() {
const canvas = document.createElement("canvas");
const canvas = this.createCanvas();
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
canvas.width = this.size;
@@ -265,7 +283,19 @@ class Board {
const { background, dark, light, border, coords } = this.style;
await drawRectangle(ctx, this.width, this.height, 0, 0, border);
ctx.clearRect(0, 0, this.size, this.size);
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, this.size, this.size);
await drawRectangle(
ctx,
this.width,
this.height,
0,
0,
border,
this.loadImage
);
await drawRectangle(
ctx,
@@ -274,6 +304,7 @@ class Board {
this.cfg.showBorder ? this.borderWidth : 0,
(this.cfg.showBorder ? this.borderWidth : 0) + this.margin,
background,
this.loadImage,
this.cfg.tiles
);
@@ -288,7 +319,15 @@ class Board {
const x = file * this.squareSize + this.borderWidth;
const y = rank * this.squareSize + this.borderWidth + this.margin;
await drawRectangle(ctx, this.squareSize, this.squareSize, x, y, style);
await drawRectangle(
ctx,
this.squareSize,
this.squareSize,
x,
y,
style,
this.loadImage
);
}
}
@@ -306,6 +345,7 @@ class Board {
);
}
console.log("Background rendered!");
this.background = canvas;
}
@@ -331,7 +371,8 @@ class Board {
this.borderWidth,
this.cfg.tiles,
this.cfg.flipped,
this.margin
this.margin,
this.loadImage
);
}
@@ -361,7 +402,8 @@ class Board {
this.cfg.flipped,
this.margin,
this.cfg.piecesStyle,
this.cfg.showShadows
this.cfg.showShadows,
this.loadImage
);
if (this.cfg.showExtraInfo && header) {
@@ -384,6 +426,55 @@ class Board {
this.ctx.drawImage(this.tempCanvas, 0, 0);
}
async renderStatic() {
this.ctx.clearRect(0, 0, this.size, this.size);
const { background, dark, light, border } = this.style;
drawRectangle(
this.ctx,
this.width,
this.height,
0,
0,
border,
this.loadImage
);
drawRectangle(
this.ctx,
this.innerSize,
this.innerSize,
this.cfg.showBorder ? this.borderWidth : 0,
(this.cfg.showBorder ? this.borderWidth : 0) + this.margin,
background,
this.loadImage,
this.cfg.tiles
);
for (let rank = 0; rank < this.cfg.tiles; rank++) {
for (let file = 0; file < this.cfg.tiles; file++) {
const style =
(file % 2 === 0 && rank % 2 === 0) ||
(file % 2 !== 0 && rank % 2 !== 0)
? light
: dark;
const x = file * this.squareSize + this.borderWidth;
const y = rank * this.squareSize + this.borderWidth + this.margin;
drawRectangle(
this.ctx,
this.squareSize,
this.squareSize,
x,
y,
style,
this.loadImage
);
}
}
}
toImgUrl() {
return this.canvas.toDataURL();
}

View File

@@ -1,7 +1,6 @@
import { Header, Style } from "../../types";
import { Header, LoadImage, Style } from "../../types";
import drawRectangle from "./drawRectangle";
import drawText from "./drawText";
import loadImage from "../loaders/loadImage";
const drawHeader = async (
ctx: CanvasRenderingContext2D,
@@ -9,10 +8,19 @@ const drawHeader = async (
scale: number,
margin: number,
style: Style,
data: Header
data: Header,
loadImage: LoadImage
) => {
ctx.clearRect(0, 0, size, size);
await drawRectangle(ctx, size, size + margin * 2, 0, 0, style.border);
await drawRectangle(
ctx,
size,
size + margin * 2,
0,
0,
style.border,
loadImage
);
const font = "Ubuntu";

View File

@@ -1,4 +1,4 @@
import { Solid } from "./../../types";
import { Solid, LoadImage } from "./../../types";
import { Move } from "chess.js";
import { Style, SquareStyle } from "../../types";
import drawRectangle from "./drawRectangle";
@@ -21,7 +21,8 @@ const drawMoveIndicators = async (
borderWidth: number,
tiles: number,
flipped: boolean,
margin: number
margin: number,
loadImage: LoadImage
) => {
const [x0, y0] = notationToXY(move.from, flipped, tiles);
const [x1, y1] = notationToXY(move.to, flipped, tiles);
@@ -62,8 +63,24 @@ const drawMoveIndicators = async (
toStyle = fromStyle;
}
drawRectangle(ctx, squareSize, squareSize, fromX, fromY + margin, fromStyle);
drawRectangle(ctx, squareSize, squareSize, toX, toY + margin, toStyle);
drawRectangle(
ctx,
squareSize,
squareSize,
fromX,
fromY + margin,
fromStyle,
loadImage
);
drawRectangle(
ctx,
squareSize,
squareSize,
toX,
toY + margin,
toStyle,
loadImage
);
};
export default drawMoveIndicators;

View File

@@ -1,6 +1,5 @@
import { state } from "../../state";
import { Position } from "../../types";
import ImagesCache from "../loaders/PiecesCache";
import { LoadImage, Position } from "../../types";
import PiecesCache from "../loaders/PiecesCache";
import { PiecesStyle } from "../styles-pieces/piecesStyles";
const drawPieces = async (
@@ -11,7 +10,8 @@ const drawPieces = async (
flipped: boolean,
margin: number,
piecesStyle: PiecesStyle,
shadow: boolean = true
shadow: boolean = true,
loadImage: LoadImage
) => {
const { placement, check, mate, turn } = position;
ctx.shadowColor = "rgba(0, 0, 0, 0)";
@@ -20,7 +20,7 @@ const drawPieces = async (
ctx.shadowOffsetY = 0;
for (const { x, y, type, color } of placement) {
const img = await ImagesCache.get(piecesStyle, type, color);
const img = await PiecesCache.get(piecesStyle, type, color, loadImage);
const rank = flipped ? 8 - 1 - y : y;
const file = flipped ? 8 - 1 - x : x;
@@ -28,7 +28,7 @@ const drawPieces = async (
const hex = mate ? "#ff002f" : "#ffa600";
ctx.shadowColor = hex;
ctx.shadowBlur = squareSize * (state.mobile ? 0.15 : 0.15);
ctx.shadowBlur = squareSize * 0.15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.drawImage(

View File

@@ -1,6 +1,5 @@
import { SquareStyle } from "../../types";
import { LoadImage, SquareStyle } from "../../types";
import createGradient from "../fill/createGradient";
import loadImage from "../loaders/loadImage";
const drawRectangle = async (
ctx: CanvasRenderingContext2D,
@@ -9,6 +8,7 @@ const drawRectangle = async (
x: number,
y: number,
squareStyle: SquareStyle,
loadImage: LoadImage,
tiles: number = 8
) => {
if (squareStyle.type === "image") {

View File

@@ -1,16 +1,15 @@
import { PieceType, PieceColor } from "../../types";
import { PieceType, PieceColor, LoadImage } from "../../types";
import {
PiecesStyle,
pieceNames,
PieceName,
} from "../styles-pieces/piecesStyles";
import loadImage from "./loadImage";
let style: PiecesStyle | null = null;
let piecesImages: Map<string, HTMLImageElement> = new Map();
const PiecesCache = {
async load(piecesSetName: PiecesStyle) {
async load(piecesSetName: PiecesStyle, loadImage: LoadImage) {
await Promise.all(
pieceNames.map((key) => {
const src = `/pieces/${piecesSetName}/${key}.svg`;
@@ -24,10 +23,11 @@ const PiecesCache = {
async get(
piecesSetName: PiecesStyle,
pieceName: PieceType,
pieceColor: PieceColor
pieceColor: PieceColor,
loadImage: LoadImage
) {
if (style !== piecesSetName) {
await this.load(piecesSetName);
await this.load(piecesSetName, loadImage);
style = piecesSetName;
}
@@ -35,23 +35,6 @@ const PiecesCache = {
return piecesImages.get(piece) as HTMLImageElement;
},
async getDataURLs() {
return Promise.all(
[...piecesImages.entries()].map(
async ([key, img]: [string, HTMLImageElement]) => {
let blob = await fetch(img.src).then((r) => r.blob());
let dataUrl = await new Promise((resolve) => {
let reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
return [key, dataUrl];
}
)
);
},
};
export default PiecesCache;

View File

@@ -11,6 +11,7 @@ const piecesStyles = [
"cardinal",
"cases",
"cburnett",
"checkers",
"chess7",
"chessnut",
"companion",

View File

@@ -210,7 +210,7 @@ class Player {
}
if (this.ply > 0 && state.siteConfig.sounds) {
state.boardConfig.piecesStyle.includes("anarc")
state.boardConfig.piecesStyle.includes("anarchy")
? this.playAnarchySFX(position)
: this.playSFX(position);
}

View File

@@ -205,3 +205,7 @@ export type Header = {
Site: string | null;
Result: string | null;
};
export type LoadImage = (src: string) => Promise<HTMLImageElement>;
export type CreateCanvas = () => HTMLCanvasElement;