nes-tetris-components
Version:
A set of components and front-end functionality of NES Tetris
129 lines (128 loc) • 5.46 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.setUpPasting = exports.PasteHandler = void 0;
const nes_tetris_representation_1 = require("nes-tetris-representation");
/*
* Heavily drawn from https://github.com/GregoryCannon/TetrisTrainer/blob/master/src/board_loader.js by GregoryCannon,
* used with permission.
*
* Thank you Greg!
*
* I'll admit, I took most of this code without really refactoring it, just turning it into Typescript and "pure-ing it up a bit".
* As a result, it looks a fair bit different to the rest of the codebase. Maybe one day I'll refactor it. But today is not that day.
*
* This class allows you to paste an image from a clipboard and convert that into a Tetris grid.
*/
class PasteHandler {
constructor(pasteAreaElement, pastedImageElement, dummyCanvas, callback) {
this.loadedStateFromImage = false;
this.pasteAreaElement = pasteAreaElement;
this.pastedImageElement = pastedImageElement;
this.dummyCanvas = dummyCanvas;
this.callback = callback;
this.setup();
}
setup() {
const that = this;
// When an image is pasted, get the board state from it
this.pasteAreaElement.onpaste = (event) => {
// use event.originalEvent.clipboard for newer chrome versions
const { items } = event.clipboardData;
// find pasted image among pasted items
let blob = null;
// tslint:disable-next-line prefer-for-of
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') === 0) {
blob = items[i].getAsFile();
}
}
// load image if there is a pasted image
if (blob !== null) {
const reader = new FileReader();
reader.onload = (onLoadEvent) => {
that.pastedImageElement.onload = () => {
that.callback(that.getBoardStateFromImage(that.pastedImageElement));
};
that.pastedImageElement.src = onLoadEvent.target.result;
};
reader.readAsDataURL(blob);
}
};
}
getBoardStateFromImage(img) {
const board = [];
const context = this.dummyCanvas.getContext('2d');
this.dummyCanvas.width = img.width;
this.dummyCanvas.height = img.height;
context.drawImage(img, 0, 0);
const cropOffset = -0.3;
const SQ = (img.height / 20 + img.width / 10) / 2 + cropOffset;
const rgbEmptyThreshold = 60; // If all three channels are <60/255, then the cell is "empty"
// Iterate over the image and read the square colors into the board
for (let c = 0; c < 10; c++) {
for (let r = 0; r < 20; r++) {
// First two invisible rows should be empty
if (!board[r]) {
board.push([]);
}
const x = Math.round((c + 0.5) * SQ);
const y = Math.round((r + 0.5) * SQ);
const pixelData = context.getImageData(x, y, 1, 1).data;
if (Math.max(pixelData[0], pixelData[1], pixelData[2]) > rgbEmptyThreshold) {
let blockValue;
const randomValue = Math.random();
if (randomValue > 5 / 7) {
blockValue = nes_tetris_representation_1.BlockValue.ACTIVE_1;
}
else if (randomValue > 3 / 7) {
blockValue = nes_tetris_representation_1.BlockValue.ACTIVE_2;
}
else {
blockValue = nes_tetris_representation_1.BlockValue.ACTIVE_3;
}
board[r][c] = blockValue;
}
else {
board[r][c] = nes_tetris_representation_1.BlockValue.EMPTY;
}
}
}
this.clearFloatingPiece(board);
const extraRowOne = [];
for (let c = 0; c < 10; c++) {
extraRowOne.push(0);
}
const extraRowTwo = [];
for (let c = 0; c < 10; c++) {
extraRowTwo.push(0);
}
board.unshift(extraRowOne);
board.unshift(extraRowTwo);
return board;
}
clearFloatingPiece(board) {
// Start from the bottom, look for an empty row, and then clear all rows above that
let startedClearing = false;
for (let r = 19; r >= 0; r--) {
if (startedClearing) {
for (let c = 0; c < 10; c++) {
board[r][c] = 0;
}
}
else {
let rowEmpty = true;
for (let c = 0; c < 10; c++) {
if (board[r][c] !== 0) {
rowEmpty = false;
break;
}
}
if (rowEmpty) {
startedClearing = true;
}
}
}
}
}
exports.PasteHandler = PasteHandler;
exports.setUpPasting = (pasteAreaElement, pastedImageElement, dummyCanvas, callback) => new PasteHandler(pasteAreaElement, pastedImageElement, dummyCanvas, callback);