mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 11:43:10 +02:00
Frontend (React 19 + Vite 6 + TypeScript strict): - DropZone, ModeSelect, GameCanvas, PuzzleCanvas, ScoreScreen, PuzzleResult - File-drop game with AABB collision; download (JSZip) and upload (NestJS) modes - Puzzle mode: NxN image slice via OffscreenCanvas; Union-Find spatial clustering guarantees 100% catch rate is always achievable regardless of piece speeds - ESLint typescript-eslint strict-type-checked (zero errors) - 145 Vitest tests; 100% coverage on statements/branches/functions/lines Backend (NestJS 11): - POST /files/upload (multer disk storage) and GET /health Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZ8QTmreFcaqrsvVb38Grd
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import type { PuzzlePiece } from "../types";
|
||
|
||
/** Slice an image file into a gridSize×gridSize grid of data-URL pieces. */
|
||
export function sliceImage(file: File, gridSize: number): Promise<PuzzlePiece[]> {
|
||
return new Promise<PuzzlePiece[]>((resolve, reject) => {
|
||
const img = new Image();
|
||
const url = URL.createObjectURL(file);
|
||
|
||
img.onload = () => {
|
||
const pieceWidth = Math.floor(img.width / gridSize);
|
||
const pieceHeight = Math.floor(img.height / gridSize);
|
||
const pieces: PuzzlePiece[] = [];
|
||
|
||
for (let row = 0; row < gridSize; row++) {
|
||
for (let col = 0; col < gridSize; col++) {
|
||
const offscreen = document.createElement("canvas");
|
||
offscreen.width = pieceWidth;
|
||
offscreen.height = pieceHeight;
|
||
const ctx = offscreen.getContext("2d");
|
||
/* istanbul ignore next */
|
||
if (!ctx) {
|
||
URL.revokeObjectURL(url);
|
||
reject(new Error("Canvas 2D context not available"));
|
||
return;
|
||
}
|
||
ctx.drawImage(
|
||
img,
|
||
col * pieceWidth,
|
||
row * pieceHeight,
|
||
pieceWidth,
|
||
pieceHeight,
|
||
0,
|
||
0,
|
||
pieceWidth,
|
||
pieceHeight,
|
||
);
|
||
pieces.push({
|
||
row,
|
||
col,
|
||
gridSize,
|
||
imageUrl: offscreen.toDataURL(),
|
||
pieceWidth,
|
||
pieceHeight,
|
||
});
|
||
}
|
||
}
|
||
|
||
URL.revokeObjectURL(url);
|
||
resolve(pieces);
|
||
};
|
||
|
||
img.onerror = () => {
|
||
URL.revokeObjectURL(url);
|
||
reject(new Error("Failed to load image for slicing"));
|
||
};
|
||
|
||
img.src = url;
|
||
});
|
||
}
|