import Cartographer from './cartographer.js'; import * as funky from './funky'; import {rangeInclusive, invSqrt2, sqrt2} from './utils.js'; import Square from './square.js'; import Point from './point.js'; const tilePointToSquare = ({tilePoint, pixelPoint}) => ({tilePoint: new Square(tilePoint), pixelPoint}); export default class CartographerPointyXY extends Cartographer { constructor(settings) { super(settings); [ 'tileHeight', 'tileWidth', 'maxWidth', 'minWidth', 'horizontalOverhang', 'verticalOverhang', 'horizontalDistance', 'verticalDistance', 'calculateHorizontalScale', 'calculateVerticalScale', 'tileToPixel', '_pixelToTile', 'teleport', 'inBounds', 'enforceBoundries', 'boundingBox', ].map(method => this[method] = this[method].bind(this)); } tileHeight() { return this.maxWidth(); } tileWidth() { return this.maxWidth(); } maxWidth() { return this.scale * 2; } minWidth() { return this.maxWidth() * invSqrt2; } horizontalOverhang() { return this.maxWidth() * 0.5; } verticalOverhang() { return this.maxWidth() * 0.5; } horizontalDistance() { return this.maxWidth() / 2; } verticalDistance() { return this.maxWidth() / 2; } calculateHorizontalScale(pixels, tiles) { return pixels / (tiles / 2) / 2; } calculateVerticalScale(pixels, tiles) { return pixels / (tiles / 2) / 2; } tileToPixel(square) { square = square instanceof Square ? square : new Square(...arguments); const x = square.getX(); const y = square.getY(); // (above/below axis) * (distance from axis) / (size) let pixelX = (x < y ? -1 : 1) * (Math.abs(y - x) / sqrt2) * this.minWidth(); let pixelY = (-x < y ? 1 : -1) * (Math.abs(x + y) / sqrt2) * this.minWidth(); return new Point(pixelX + this.originX, this.originY - pixelY); } _pixelToTile (point) { point = point instanceof Point ? point : new Point(...arguments); const pixelX = point.getX() - this.originX; const pixelY = this.originY - point.getY(); // (above/below axis) * (distance from axis) / (size) const x = (-pixelX < pixelY ? 1 : -1) * (Math.abs(pixelX + pixelY) / sqrt2) / this.minWidth(); const y = (pixelX < pixelY ? 1 : -1) * (Math.abs(pixelY - pixelX) / sqrt2) / this.minWidth(); return new Square(x, y); } teleport ({x, y}) { x = x % this.width; y = y % this.height; x = x < 0 ? this.width + x : x; y = y < 0 ? this.height + y : y; return new Point(x, y); } inBounds ({x, y}) { if (this.negativeTiles) { return (!this.width || Math.abs(x) <= Math.floor(this.width / 2)) && (!this.height || Math.abs(y) <= Math.floor(this.height / 2)); } else { return (!this.width || (x >= 0 && x < this.width)) && (!this.height || (y >= 0 && y < this.height)); } } enforceBoundries ({tilePoint, pixelPoint}) { return this.wrap ? ({tilePoint: this.teleport(tilePoint), pixelPoint}) : this.inBounds(tilePoint) ? ({tilePoint, pixelPoint}) : null; } boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { const upperLeftTile = this._pixelToTile(upperLeftPoint); const lowerRightTile = this._pixelToTile(lowerRightPoint); const upperRightTile = this._pixelToTile(upperRightPoint); const lowerLeftTile = this._pixelToTile(lowerLeftPoint); const columns = rangeInclusive(lowerLeftTile.getX(), upperRightTile.getX()); const upperLeftIntercept = upperLeftTile.getY() - upperLeftTile.getX(); const upperRightIntercept = upperLeftTile.getY() + upperLeftTile.getX(); const lowerLeftIntercept = lowerRightTile.getY() - lowerRightTile.getX(); const lowerRightIntercept = lowerRightTile.getY() + lowerRightTile.getX(); const aboutHalf = Math.floor(columns.length / 2); const midway = columns.length % 2 ? columns[aboutHalf] : (columns[aboutHalf - 1] + columns[aboutHalf]) / 2; const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)}); return funky.chain(columns) .map(x => { let top = x < midway ? upperLeftIntercept + x : upperRightIntercept - x; let bottom = x < midway ? lowerRightIntercept - x : lowerLeftIntercept + x; bottom = Math.min(bottom, top); top = Math.max(bottom, top); // push out by 1 on either end to account for interlocking tiles const rows = rangeInclusive(bottom - 1, top + 1); const makeAPoint = y => ({x, y}); return funky.chain(rows) .map(makeAPoint) .map(makeAPointPair) .map(this.enforceBoundries) .compact() .map(tilePointToSquare) .value(); }) .flatten() .value(); } }