import * as I from 'immutable'; import {compose} from '@avocado/core'; import {Rectangle, Vector} from '@avocado/math'; import {EventEmitter} from '@avocado/mixins'; import {Synchronized} from '@avocado/state'; const decorate = compose( EventEmitter, Vector.Mixin('size', 'width', 'height', { default: [0, 0], }), Synchronized, ); export class Tiles extends decorate(class {}) { constructor() { super(); this.data = I.List(); } forEachTile(fn) { let [x, y] = [0, 0]; let [width, height] = this.size; let i = 0; for (let k = 0; k < height; ++k) { for (let j = 0; j < width; ++j) { fn(this.data.get(i), x, y, i); ++i; ++x; } x = 0; ++y; } } fromJSON(json) { if (json.size) { this.size = json.size; } if (json.data) { this.data = I.fromJS(json.data); } return this; } patchStateStep(key, step) { if ('data' === key) { const oldData = this.data; for (const i in step.value) { const index = parseInt(i); this.data = this.data.set(index, step.value[i]); } if (oldData !== this.data) { this.emit('dataChanged'); } return; } super.patchStateStep(key, step); } get rectangle() { return Rectangle.compose([0, 0], this.size); } setTileAt(x, y, tile) { const oldTile = this.tileAt(x, y); if (oldTile === tile) { return; } const index = y * this.width + x; if (index < 0 || index >= this.data.size) { return; } this.data = this.data.set(index, tile); this.emit('dataChanged'); } slice(rectangle) { const tilesRectangle = this.rectangle; // Get intersection. if (!Rectangle.intersects(rectangle, tilesRectangle)) { return []; } let [x, y, sliceWidth, sliceHeight] = Rectangle.intersection( rectangle, tilesRectangle, ); // No muls in the loop. let sliceRow = y * sliceWidth; const dataWidth = this.width; let dataRow = y * dataWidth; // Copy slice. const slice = new Array(sliceWidth * sliceHeight); for (let j = 0; j < sliceHeight; ++j) { for (let i = 0; i < sliceWidth; ++i) { slice[sliceRow + x] = this.data.get(dataRow + x); x++; } sliceRow += sliceWidth; dataRow += dataWidth; x -= sliceWidth; } return slice; } synchronizedChildren() { return [ 'width', 'height', 'data', ]; } tileAt(x, y) { return this.data.get(y * this.width + x); } toJSON() { return { size: [...this.size], data: [...this.data.toJS()], }; } }